Digital VLSI Design Lecture 2 Verilog
课程主页:
http://www.eng.biu.ac.il/temanad/digital-vlsi-design/
课程视频:
https://www.youtube.com/watch?reload=9&v=RbZ3BXbd6_k&list=PLZU5hLL_713x0_AV_rVbay0pWmED7992G
https://www.bilibili.com/video/BV1EA411n7VQ?from=search&seid=14545899898143497364
这次回顾第二讲,这一讲介绍了Verilog。
Verilog语法
基本结构
原语:
- not,and,or,etc
信号:
$4$种状态:$\text{0,1,X,Z}$
电线:不保持状态
寄存器:保持状态
可以表示总线或一组信号
```vhdl
wire in1,in2;
reg out;
wire [7:0] data;
reg [31:0] mem [0:7];//width (bits)=32,depth (words)=8- 操作符 - 类似原语: - $\&, \sim, \& \&,| \mid, \text { etc }$ - ![](https://github.com/Doraemonzzz/md-photo/blob/master/Digital%20VLSI%20Design/Lecture%202/2020082302.jpg?raw=true) - 常量: - 标准形式:$\text{W'Bval}$ - 例子: - 1'b0 - 8'hff - 8'd255 #### 过程块 - Initial块 - 将只执行一次,第一次调用该单元(仅在测试中) - ```vhdl initial begin a = 1’b0; b = 1’b0; end
always块
当敏感列表发生更改时,将对语句进行执行
同步复位与异步复位:
```vhdl
//同步复位
always @(posedge clock)if (!nreset) q <= 1’b0; else q <= d;
//异步复位
always @(posedge clock or negedge nreset)if (!nreset) q <= 1’b0; else if (load_enable) q <= d;
- always块有两种 - 时序 - 由灵敏度列表中的时钟确定。 - ```vhdl always @(posedge clock or negedge nreset) if (!nreset) q <= 1’b0; else if (load_enable) q <= d;
组合
描述纯组合逻辑,敏感列表没有时钟信号。
```vhdl
always @(a or b or c)out = a & b & c
#### 赋值 Verilog有三种类型的赋值: - 连续赋值 - 在always块之外。 - ```vhdl assign muxout = (sel&in1) l (~sel&ine); assign muxout = sel ? in1 : ine;
阻塞过程赋值”=”
时序执行。
```vhdl
// assume initially a=1;
a = 2;
b = a;
// a=2; b=2- 非阻塞过程赋值"<=" - 并行执行。 - ```vhdl // assume initially a=1; a <= 2; b <= a; // a=2; b=2
- 要消除错误,请遵循以下规则:
- 组合always块:使用阻塞赋值(=)
- 时序always块:使用非阻止赋值(<=)
- 不要将阻塞和非阻塞混合在同一always块
- 不要在多个always块中为同一个变量赋值
层次化
模块
用来定义硬件模块
实例
引用不同级别的块
```vhdl
mux4 M0 (.out(outa),.in(a),.sel(sel));
mux4 M1 (.out(outb),.in(b),.sel(sel));#### 系统任务 - 系统任务用于提供模拟数据的接口 - 以$\$\text{name}$语法标识 - 打印任务: - $\text{\$display, \$strobe}$:语句执行后立即打印 - $\text{\$monitor}$:每当其中一个参数发生更改时打印一次 - 全部采用c语言的printf格式 - ```vhdl $display(At %t Value of out is %b \n”,$time,out);
简单例子
Hello World
module main;
initial
begin
$display("Hello world !”);
$finish;
end
endmodule
组合逻辑
三种使用MUX的方法
使用assign:
- ```vhdl
wire out;
assign out = sel ? a : b;- 使用always块: - ```vhdl reg out; always (a or b or sel) if (sel) out=a; else out=b;
- ```vhdl
使用case语句:
- ```vhdl
reg out;
always @ (a or b or sel)begin case (sel) 1'b0: out=b; 1'b1: out=a; endcase end
#### 时序逻辑 - D-Flip Flop: - ```vhdl reg q; always @(posedge clk) q<= d;
- ```vhdl
异步D-Flip Flop
- ```vhdl
reg q;
always @(posedge clk or negedge reset_)if(~reset_) q<= 0; else q<= d;
#### 算数运算 - Verilog支持标准算数运算: - $+,-, *,<<($ shift left $),>>($ shift right $),$ etc - 使用$\{,\}$连接向量 - ```vhdl assign a = 4'b1100; assign b = 4'b1010; assign c = {a,b}; //c=8'b11001010
- ```vhdl
但是
- Verilog将所有向量视为无符号二进制数
为了做带符号操作,需要将reg/wire声明为signed:
- ```vhdl
wire signed [9:0] a,b;
wire signed [19:0] result = a*b;- 为了使用带符号常熟,需要增加s: #### reg vs wire 1.always块内部(时序和组合)只有reg可以用作LHS。 2.对于assign语句,只能使用wire作为LHS。 3.在initial块(Testbench)中,LHS上只能使用reg。 4.实例化模块的输出只能连接到wire 5.模块输入不能为reg 正确案例: ```vhdl reg r; always @* r = a & b;
- ```vhdl
wire w;
assign w = a & b;
reg r;
initial
begin
r=1'bo;
#1
r =1'b1;
end
错误案例:
module m1 (out)
output out;
endmodule
reg r
m1_m1_instance(.out(r))
module m2(in)
input in;
reg in,
endmodule
Testbench构造
定义clock:
`define CLK_PERIOD 10 initial begin //begins executing at time e clk = 0; end always //begins executing at time 0 and never stops #(CLK_PERIOD/2) clk = ~clk;
Verilog FSM(有限状态自动机)实现
FSM
4比特计数器
- 4个输入:
- clk:系统时钟
- rst_n:低有效复位
- act:激活信号
- up_dwn_n:计数上移(正)或计数下移(负数)
- 两个输出信号:
- count:当前计数值
- ovflw:溢出信号
- 4个输入:
状态机的图示:
定义每个状态:
localparam IDLE = 4'b0001; localparam CNTUP = 4'b0010; localparam CNTDN = 4'b0100; localparam OVFLW = 4'b1000;
组合块
- 计算下个状态:
always @* case (state) IDLE: begin if(act) if(up_dwn_n) next_state = CNTUP; else next_state = CNTDN; else next_state = IDLE; end
CNTUP: begin if (act) if(up_dwn_n) if (count==(1<<COUNTER_WIDTH)-1) next_state = OVFLW; else next_state = CNTUP; else if (count=='b0) next_state=OVFLW; else next_state=CNTDN; else next_state=IDLE; end
时序块:
定义状态寄存器
定义计数寄存器
最后给输出赋值
Testbench
定义信号和参数:
实例化状态机:
设置初始值、值监视和时序重置:
定义时钟:
设置stimuli:
RTL编程风格
组织代码
每个模块应位于单独的文件中
- 将文件命名为$<\text{modulename}>.\text{v}$
- 始终按名称(.dot()形式)连接模块。
将每个输入/输出写在单独的一行上
注释每个信号的作用。
分开时序和组合逻辑
赋值
- 在组合块($\text{always@}^\star$):
- 始终使用阻塞(=)赋值。
- 建议在敏感性列表中使用$\star$。
- 在时序块($\text{always@posedge}$):
- 始终使用非阻塞(<=)
- 最好将每个触发器放在单独的always块中。
- 永远不要从多个块中对同一个信号( LHS )赋值。
注意不要引起闩锁(latch)
每个if后要跟else
错误案例:
module mux4to1 (out, a, b, c, d, sel); output out; input &b, c, d; input [1:0] sel; reg out; always @(sel or a ob or c or d) case (sel) 2'b00: out = a; 2'b01: out = b; 2'b10: out = d; endcase endmodule
坚持使用一种重置类型
重置的目的是使您的设计进入一个众所周知的状态。
无论是否需要,都希望每个触发器都可重置。
我们通常使用异步重置:
always @(posedge clock or negedge rst_) if (!nreset) state <= idle; else state <= next_state;
但同步重置也没关系:
always @(posedge clock) if (!nreset) state <= idle; else state <= next_state;
只要确保不要在设计中混合使用
在复位信号中不要使用逻辑(这一点没有完全理解)
- 反例
assignsomething = a && reset ; always@* case (state) 1'b1011. if(b ll reset) next_state = idle;
参数化设计
- “Pretty code”是完全参数化的代码
- 参数化的两种方法:’define, ‘include, and ‘ifdef
- 编译器指令:
- 将所有`define语句放在外部定义文件中。
- 参数或局部参数
- 参数可以通过实例化来重写
- 局部参数更适合常量,例如FSM编码
- 编译器指令:
- 也可以使用generate语句,但要注意这些语句。
- 始终使用硬编码值对FSM状态进行编码
- 可以选择各种方法,如二进制码、灰度码、独热码等。
编写可读代码
- 始终使用缩进!!!
- 命名规范:
- 常用样式:
- C样式:带下划线的单词,如packet_addr、data_in
- Pascal样式:每个单词大写,例如PacketAddr、DataIn
- Modula样式:除第一个单词外,其他单词都大写,如packetAddr , dataIn
- 没有一个正确答案,但有两种推荐的样式
- NetFPGA VerilogCodingGuidelines
https://github.com/NetFPGA/netfpga/wiki/VerilogCodingGuidelines - ETH Zurich VHDL naming conventions (with emacs highlighting!):
https://www.dz.ee.ethz.ch/en/information/hdl-help/vhdl-naming-conventions.html
- NetFPGA VerilogCodingGuidelines
一些有用的文档和参考资料
- Chris Fletcher “Verilog: wire vs. reg
- Greg Tumbush “Signed Arithmetic in Verilog 2001 Opportunities and Hazards”
- NetFPGA wiki VerilogCodingGuidelines
- MIT 6.111 Lectures http://web.mit.edu/6.111/www/f2007/
- Stuart Sutherland, Don Mills “Standard Gotchas : Subtleties in the Verilog and SystemVerilog Standards That Every Engineer Should Know”