问题28 Always blocks(combinational) (Alwaysblock1)
由于数字电路是由用导线连接的逻辑门组成的,因此任何电路都可以表示为模块和赋值语句的某种组合。然而,有时这并不是描述电路的最方便的方式。Procedures (of which always blocks are one example) provide an alternative syntax for describing circuits.
对于综合硬件,两种类型的 always 块是相关的:
Combinational: always @(*)
Clocked: always @(posedge clk)
组合always blocks 等同于赋值语句,因此总有一种方法可以双向表达组合电路。选择使用哪个主要是哪个语法更方便的问题。程序块内部代码的语法与外部代码不同。 程序块有更丰富的语句集(例如,if-then、case),不能包含连续赋值,但也引入了许多新的非直观的出错方式。(Procedural continuous assignments 确实存在,但与连续赋值有些不同,并且不可综合。)
例如,assign和combinational always块描述相同的电路。两者都创建了相同的组合逻辑块。无论何时任何输入(右侧)改变值,两者都将重新计算输出。
assign out1 = a & b | c ^ d; always @(*) out2 = a & b | c ^ d; • 1 • 2
对于combinational always块,始终使用 (*)的敏感度列表。显式列出信号容易出错(如果您错过了一个信号),在硬件合成中会被忽略。如果显式指定灵敏度列表并错过信号,则合成硬件的行为仍将如同指定了(*),但模拟将与硬件的行为不匹配。(在 SystemVerilog 中,使用 always_comb。)关于 wire 与 reg 的注意事项:assign 语句的左侧必须是 net 类型(例如,wire),而过程赋值(在 always block中)的左侧必须是变量类型(例如,reg)
A bit of practice
使用 assign 语句和组合 always block构建 AND
门。
// synthesis verilog_input_version verilog_2001 module top_module( input a, input b, output wire out_assign, output reg out_alwaysblock ); assign out_assign = a & b; always @(*) begin out_alwaysblock = a & b; end endmodule
问题29: Always blocks(clocked) (Alwaysblock2)
对于硬件综合,有两种相关的 always block:
组合型: always @(*)
时序型: always @(posedge clk)
Clocked always blocks总是创建了一个组合逻辑块,而且还会在组合逻辑块的输出处创建一组触发器(或“寄存器”)。不是立即可见逻辑块的输出,而是仅在下一个(posedge clk)之后立即可见输出。
Blocking vs. Non-Blocking Assignment
Verilog 中有三种类型的赋值:
Continuous assignments (assign x = y;). Can only be used when not inside a procedure (“always block”).
Procedural blocking assignment: (x = y;). Can only be used inside a procedure.
Procedural non-blocking assignment: (x <= y;). Can only be used inside a procedure.
在combinational always block,使用blocking assignments. 在clocked always block中,使用non-blocking assignments.
A bit of practice
以三种方式构建XOR gate,使用assign statement、combinational always block和clocked always block。请注意,the clocked always block产生的电路与其他两个不同:有一个触发器,因此输出被延迟
// synthesis verilog_input_version verilog_2001 module top_module( input clk, input a, input b, output wire out_assign, output reg out_always_comb, output reg out_always_ff ); assign out_assign = a ^ b; always@(*)begin out_always_comb = a ^ b; end always@(posedge clk)begin out_always_ff <= a ^ b; end endmodule
问题30: If statement(Always if)
if
语句通常创建一个 2
对 1
多路复用器,如果条件为真则选择一个输入,如果条件为假则选择另一个输入。
always @(*) begin if (condition) begin out = x; end else begin out = y; end end
这等效于使用带有条件运算符的连续赋值:
assign out = (condition) ? x : y; • 1
但是,程序 if 语句提供了一种新的出错方式。仅当总是为 out
分配一个值时,该电路才是组合电路。
A bit of practice
构建一个在 a
和 b
之间进行选择的 2
对 1
多路复用器。如果 sel_b1
和 sel_b2
都为真,则选择 b
。否则,选择a
。做同样的事情两次,一次使用分配语句,一次使用过程 if 语句。
// synthesis verilog_input_version verilog_2001 module top_module( input a, input b, input sel_b1, input sel_b2, output wire out_assign, output reg out_always ); assign out_assign = (sel_b2 & sel_b1) ? b : a; always@(*)begin if(sel_b2 & sel_b1)begin out_always = b; end else begin out_always = a; end end endmodule
问题31If statement latches(Always if2)
常见的错误来源:如何避免锁存。
设计电路时,首先要从电路方面考虑:
I want this logic gate
I want a combinational blob of logic that has these inputs and produces these outputs
I want a combinational blob of logic followed by a set of flip-flops
你不能做的是先写代码,然后希望它生成一个合适的电路。
If (cpu_overheated) then shut_off_computer = 1;
If (~arrived) then keep_driving = ~gas_tank_empty;
语法正确的代码不一定会产生合理的电路(组合逻辑 + 触发器)。通常的原因是:“在您指定的情况以外的情况下会发生什么?”。 Verilog 的回答是:保持输出不变。
这种“保持输出不变”的行为意味着需要记住当前状态,从而产生一个锁存器。组合逻辑(例如逻辑门)不能记住任何状态。在所有条件下,组合电路必须为所有输出分配一个值。这通常意味着您总是需要 else 子句或分配给输出的默认值。
// synthesis verilog_input_version verilog_2001 module top_module ( input cpu_overheated, output reg shut_off_computer, input arrived, input gas_tank_empty, output reg keep_driving ); // always @(*) begin if (cpu_overheated) begin shut_off_computer = 1; end else begin shut_off_computer = 0; end end always @(*) begin if (~arrived) begin keep_driving = ~gas_tank_empty; end else begin keep_driving = 0; end end endmodule
问题32: Case statement(Always case)
Verilog
中的 case
语句几乎等同于将一个表达式与其他表达式进行比较的 if-elseif-else
序列。它的语法和功能不同于 C
中的 switch
语句。
always @(*) begin // This is a combinational circuit case (in) 1'b1: begin out = 1'b1; // begin-end if >1 statement end 1'b0: out = 1'b0; default: out = 1'bx; endcase end
case 语句以 case 开头,每个case item以冒号结尾。没有switch。
每个案例项只能执行一个语句。这使得 C 中使用的break变得不必要。但这意味着,如果您需要多个语句,则必须使用 begin ... end。
允许重复(和部分重叠)案例项目。使用第一个匹配的。 C语言不允许重复的案例项目。
A bit of practice
如果有大量 case
,case
语句比 if
语句更方便。因此,在本练习中,创建一个 6
对 1
多路复用器。当 sel
在 0
到 5
之间时,选择对应的数据输入。否则,输出 0
。数据输入和输出均为 4
位宽。
// synthesis verilog_input_version verilog_2001 module top_module ( input [2:0] sel, input [3:0] data0, input [3:0] data1, input [3:0] data2, input [3:0] data3, input [3:0] data4, input [3:0] data5, output reg [3:0] out );// always@(*) begin // This is a combinational circuit case(sel) 3'b000: begin out = data0; end 3'b001: begin out = data1; end 3'b010: begin out = data2; end 3'b011: begin out = data3; end 3'b100: begin out = data4; end 3'b101: begin out = data5; end default: begin out = 4'b 0000; end endcase end endmodule
// synthesis verilog_input_version verilog_2001 module top_module ( input [2:0] sel, input [3:0] data0, input [3:0] data1, input [3:0] data2, input [3:0] data3, input [3:0] data4, input [3:0] data5, output reg [3:0] out );// always@(*) begin // This is a combinational circuit out = 4'b 0000; case(sel) 3'b000: begin out = data0; end 3'b001: begin out = data1; end 3'b010: begin out = data2; end 3'b011: begin out = data3; end 3'b100: begin out = data4; end 3'b101: begin out = data5; end endcase end endmodule