文章目录
Procedures
在本节开始之前先将常用语句进行划分,分为可综合性和不可综合性两种,简单来说,可综合性即是可以构成硬件电路的语句,而不可综合性即是只能用来仿真的语句。
类别 | 语句 | 可综合性 |
---|---|---|
过程语句 | initial | |
always | √ | |
语句块 | begin-end | |
fork-join | ||
赋值语句 | assign | √ |
= , <= | √ | |
条件语句 | if - else | √ |
case, casez, casex | √ | |
循环语句 | forever | |
repeat | ||
while | ||
for | √ | |
编译向导语句 | 'define | √ |
'include | √ | |
'ifdef, 'else, 'endif | √ |
Always blocks(combinaitonal)
-
Problem Statement
Build an AND gate using both an assign statement and a combinational always block.
-
Code Block
// 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 @(*) out_alwaysblock = a && b;//与assign有同样效果,但是信号要是寄存器类型 endmodule
-
Key Point
always语句是一直在运行的,只要满足语句后面的敏感时间,always语句块就会被执行,其语法格式为:
always@(<敏感事件列表>)
语句块;
其中敏感事件列表可以为:
@ (*) //模块任意信号发生改变
@ (a) //当信号a改变时
@ (a or b) //当信号a或b改变时
@(posedge clk) //clk信号上升沿触发
@(posedge clk or negedge clk) //clk信号上升沿或clk信号下降沿触发
在过程语句(initial和always)中,被赋值信号必须是“寄存器”类型,例如’reg’。
在连续赋值语句(assign)中,被赋值信号必须是“连线型”数据类型,例如’wire’。
在使用always语句时,必须将能触发always语句的敏感事件全部写入括号内。
Always blocks(clocked)
-
Problem Statement
Build an XOR gate three ways, using an assign statement, a combinational always block, and a clocked always block. Note that the clocked always block produces a different circuit from the other two: There is a flip-flop so the output is delayed.
-
Code 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 @(*) out_always_comb = a^b; always @(posedge clk) out_always_ff <= a^b; endmodule
-
Key Point
过程语句赋值有:
阻塞性过程赋值语句 “变量 = 表达式”;
非阻塞性过程赋值语句 “变量 <= 表达式”。
阻塞性赋值语句和非阻塞性赋值语句只有在语句块中能使用。
阻塞性赋值语句特点是:
① 在串行语句块中,按照代码排序顺序执行,在并行语句块则同时执行,没有顺序之分。
② 执行语句的顺序的是,先计算等式右边,然后立刻将计算的值赋予左边。
非阻塞性赋值语句特点是:
① 任何情况都是并行,没有先后顺序的关系
② 执行语句的顺序的是,先计算等式右边,等延迟时间结束后,才将计算值赋予左边变量。
只有在“行为级建模 + 串行语句块 + 阻塞性赋值”的情况下,代码才会串行进行
If statement
-
Problem Statement
Build a 2-to-1 mux that chooses between
a
andb
. Chooseb
if bothsel_b1
andsel_b2
are true. Otherwise, choosea
. Do the same twice, once usingassign
statements and once using a procedural if statement.
-
Code Block
// 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 ); always @(*) begin if (sel_b1 & sel_b2 ) begin out_always = b; end else begin out_always = a; end end assign out_assign = (sel_b1 && sel_b2)?b:a; endmodule
-
Key Point
串行语句块采用关键字“begin”和“end”,其中代码如果用阻塞性赋值语句“=”,则为串行执行。
if条件语句有三种表达式:
if(条件表达式) 语句块;
if(条件表达式) 语句块; else 语句块;
if(条件表达式1) 语句块; else if (条件表达式1) 语句块; ...... else 语句块;
if语句有优先级的划分,从上至下执行,如果前面已经满足条件,那么就会直接跳出整个if语句块,并不会把所有else if顺序读完。
if的语句块中推荐使用begin end语句块,那怕只有一行代码。
If statement latches
-
Problem Statement
The following code contains incorrect behaviour that creates a latch. Fix the bugs so that you will shut off the computer only if it’s really overheated, and stop driving if you’ve arrived at your destination or you need to refuel.
-
Code Block
// 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
-
Key Point
当我们在设计电路时,不能指望代码自动生成正确的电路,哪怕代码在编译的时候并没有报错,如下错误所示:
If (cpu_overheated) then shut_off_computer = 1; If (~arrived) then keep_driving = ~gas_tank_empty;
因为在verilog中,在不满足if条件的情况时,并没有写对应的else的情况,那么verilog会保持信号不变,那么就会产生错误的信号。
所以在我们写if或者case语句块时,需要考虑到电路的所有情况。
Case statement
-
Problem Statement
Case statements are more convenient than if statements if there are a large number of cases. So, in this exercise, create a 6-to-1 multiplexer. When
sel
is between 0 and 5, choose the corresponding data input. Otherwise, output 0. The data inputs and outputs are all 4 bits wide. -
Code Block
// 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 = 3'b000; end endcase end endmodule
-
Key Point
case是比if-else语句更为方便的多路分支选择控制语句,其语法表达式为:
case(控制表达式) 值A:语句块; 值B:语句块; ... 值N:语句块; default:语句块; endcase
值A~值N的之间必须各不相同,但是语句块可以是一样的。
case一旦判断到执行某值的语句后,case语句块就会跳出,不会全部遍历。
只有当列出了敏感表达式的所有可能指,不然就要写default选项。
各数值之间位宽必须相同
Priority encoder
-
Problem Statement
Build a 4-bit priority encoder(优先编码器,输出向量中第一个’1’的位置). For this problem, if none of the input bits are high (i.e., input is zero), output zero. Note that a 4-bit number has 16 possible combinations.
-
Code Block
// synthesis verilog_input_version verilog_2001 module top_module ( input [3:0] in, output reg [1:0] pos ); always@(*) begin case(in) 4'b0010, 4'b0110, 4'b1010, 4'b1110 : begin pos = 2'd1; end 4'b0100, 4'b1100: begin pos = 2'd2; end 4'b1000 : begin pos = 2'd3; end default: begin pos = 2'd0; end endcase end endmodule
-
Key Point
case之间若有连续排列的值执行的是同一语句块,可以用逗号将数值直接隔开。
case本质上就是一种数值的匹配,那么可以有另外一种思路,直接对向量各位置直接比较有没有’1’,如下所示:
module top_module ( input [3:0] in, output reg [1:0] pos ); always@(*) begin case(1) in[0] : begin pos = 2'd0; end in[1] : begin pos = 2'd1; end in[2] : begin pos = 2'd2; end in[3] : begin pos = 2'd3; end default : begin pos = 2'd0; end endcase end endmodule
注:不能如下面这么写。
// synthesis verilog_input_version verilog_2001 module top_module ( input [3:0] in, output reg [1:0] pos ); always@(*) begin case(1) in[1] : begin pos = 2'd1;//会错误地将4'b0011作为控制数值 end in[2] : begin pos = 2'd2;//会错误地将4'b0101作为控制数值 end in[3] : begin pos = 2'd3;//会错误地将4'b1001作为控制数值 end default : begin pos = 2'd0; end endcase end endmodule
Priority encoder with casez
-
Problem Statement
Build a priority encoder for 8-bit inputs. Given an 8-bit vector, the output should report the first bit in the vector that is
1
. Report zero if the input vector has no bits that are high. -
Code Block
// synthesis verilog_input_version verilog_2001 module top_module ( input [7:0] in, output reg [2:0] pos ); always@(*) begin casez (in) 8'bzzzzzzz1 : begin pos = 3'd0; end 8'bzzzzzz1z : begin pos = 3'd1; end 8'bzzzzz1zz : begin pos = 3'd2; end 8'bzzzz1zzz : begin pos = 3'd3; end 8'bzzz1zzzz : begin pos = 3'd4; end 8'bzz1zzzzz : begin pos = 3'd5; end 8'bz1zzzzzz : begin pos = 3'd6; end 8'b1zzzzzzz : begin pos = 3'd7; end default : begin pos = 3'd0; end endcase end endmodule
-
Key Point
当你不关心向量中其他位置的数值关系的时候,就可以使用casez,并且不关心的数值位置替换为z即可。
因为case是从上至下执行,一旦匹配就会退出,所以在写敏感表达式的数值时,要注意顺序,错误示范如下:
// synthesis verilog_input_version verilog_2001 module top_module ( input [7:0] in, output reg [2:0] pos ); always@(*) begin casez (in) 8'bzzzzzz1z : begin pos = 3'd1;//因为与8'bzzzzzzz1调换了位置,所以当出现8'bzzzzzz11时,将会匹配到这并直接退出,并不会匹配到下面。 end 8'bzzzzzzz1 : begin pos = 3'd0; end 8'bzzzzz1zz : begin pos = 3'd2; end 8'bzzzz1zzz : begin pos = 3'd3; end 8'bzzz1zzzz : begin pos = 3'd4; end 8'bzz1zzzzz : begin pos = 3'd5; end 8'bz1zzzzzz : begin pos = 3'd6; end 8'b1zzzzzzz : begin pos = 3'd7; end default : begin pos = 3'd0; end endcase end endmodule
Avoiding latches
-
Problems Statement
Suppose you’re building a circuit to process scancodes from a PS/2 keyboard for a game. Given the last two bytes of scancodes received, you need to indicate whether one of the arrow keys on the keyboard have been pressed. This involves a fairly simple mapping, which can be implemented as a case statement (or if-elseif) with four cases.
Your circuit has one 16-bit input, and four outputs. Build this circuit that recognizes these four scancodes and asserts the correct output.
问题示意图(转自HDLBits) -
Code Block
// synthesis verilog_input_version verilog_2001 module top_module ( input [15:0] scancode, output reg left, output reg down, output reg right, output reg up ); always@(*) begin up = 1'b0; down = 1'b0; left = 1'b0; right = 1'b0; case(scancode) 16'he06b : begin left = 1; end 16'he072 : begin down = 1; end 16'he074 : begin right = 1; end 16'he075 : begin up = 1; end /* default : begin left = 0; down = 0; right = 0; up = 0; end*/ endcase end endmodule
-
Key Point
当情况非常复杂时,仅有一个简单的default是不够的,我们必须在case item和default中为4个输出进行赋值,这会导致很多不必要的代码编写。
所以我们可以对输出先进行赋初值的操作,这种能确保在所有可能的情况下输出都被赋值,除非case语句覆盖了赋值,所以不再需要写default项。
整数的表达方式如下:
数值 基数符号 二进制 b或B 八进制 o或O 十进制 d或D 十六进制 h或H
More Verilog Features
Conditional ternary operator
-
Problem Statement
Given four unsigned numbers, find the minimum. Unsigned numbers can be compared with standard comparison operators (a < b). Use the conditional operator to make two-way min circuits, then compose a few of them to create a 4-way min circuit. You’ll probably want some wire vectors for the intermediate results.
-
Code Block
module top_module ( input [7:0] a, b, c, d, output [7:0] min);// // assign intermediate_result1 = compare? true: false; wire [7:0] temp_num1 , temp_num2; assign temp_num1 = (a>b ? b:a); assign temp_num2 = (c>d ? d:c); assign min = (temp_num1 > temp_num2 ? temp_num2 : temp_num1); endmodule
Reduction operators
-
Problem Statement
Parity checking is often used as a simple method of detecting errors when transmitting data through an imperfect channel. Create a circuit that will compute a parity bit for a 8-bit byte (which will add a 9th bit to the byte). We will use “even” parity, where the parity bit is just the XOR of all 8 data bits.
-
Code Block
module top_module ( input [7:0] in, output parity); assign parity = ^in;//奇偶校验 endmodule
-
Key Point
缩位运算符可以将向量各个位进行和,或和异或等运算,产生一位输出,如下所示:
& a[3:0] // 和运算: a[3]&a[2]&a[1]&a[0]。 相当于(a[3:0] == 4'hf) | b[3:0] // 或运算: b[3]|b[2]|b[1]|b[0]。 相当于(b[3:0] != 4'h0) ^ c[2:0] // 异或运算: c[2]^c[1]^c[0]。 可以进行奇偶检验
Reduction : Even wider gates
-
Problem Statement
Build a combinational circuit with 100 inputs,
in[99:0]
.There are 3 outputs:
- out_and: output of a 100-input AND gate.
- out_or: output of a 100-input OR gate.
- out_xor: output of a 100-input XOR gate.
-
Code Block
module top_module( input [99:0] in, output out_and, output out_or, output out_xor ); assign out_and = ∈ assign out_or = |in; assign out_xor = ^in; endmodule
Combinational for-loop: Vector reversal 2
-
Problem Statement
Given a 100-bit input vector [99:0], reverse its bit ordering.
-
Code Block
module top_module( input [99:0] in, output [99:0] out ); integer i; always @(*)begin for(i=0;i<100;i++) begin out[i] = in[99-i]; end end endmodule
-
Key Point
'for’条件循环的语法格式为:
integer 整数变量 for (循环变量初值; 循环结束条件; 循环变量增值)
Combinational for-loop: 255-bit population count
-
Problem Statement
A “population count” circuit counts the number of '1’s in an input vector. Build a population count circuit for a 255-bit input vector.
-
Code Block
module top_module( input [254:0] in, output [7:0] out ); integer i; always @(*) begin out = 8'd0; for(i=0; i<255; i++)begin out += in[i]; end end endmodule
Generate for-loop: 100-bit binary adder2
-
Problem Statement
Create a 100-bit binary ripple-carry adder by instantiating 100 full adder. The adder adds two 100-bit numbers and a carry-in to produce a 100-bit sum and carry out. To encourage you to actually instantiate full adders, also output the carry-out from each full adder in the ripple-carry adder. cout[99] is the final carry-out from the last full adder, and is the carry-out you usually see.
-
Code Block
module top_module( input [99:0] a, b, input cin, output [99:0] cout, output [99:0] sum ); genvar i; generate for(i=0; i<100; i++) begin : adder //命名 if(i == 0)begin assign {cout[0], sum[0]} = a[0] + b[0] + cin; end else begin assign {cout[i], sum[i]} = a[i] + b[i] + cout[i-1]; end end endgenerate endmodule
-
Key Point
generate语句的最主要功能就是对module、reg、assign、always、task等语句或者模块进行复制。
generate语句有generate for、generate if、generate case三种语句。
generate语句块注意事项:
generate不放在always块中,而是always块等包含在generate中; generate for中,要在begin后面给模块命名,这是一个强制规定,并且也是利用循环生成语句生成多个实例的时候分配名字所必须的; task不能放在generatefor中,要想实现同样的功能,用子模块; 循环生成中for语句使用的变量必须用genvar关键字定义,genvar关键字可以写在generate语句外面,也可以写在generate语句里面,只要先于for语句声明即可,且genvar关键字定义的变量只有generate语句块能使用; for语句的内容必须加begin-end,即使只有一条语句也不能省略。这也是一个强制规定,而且给循环起名字也离不开begin关键字;
Generate for-loop: 100-digit BCD adder
-
Problem statement
You are provided with a BCD one-digit adder named
bcd_fadd
that adds two BCD digits and carry-in, and produces a sum and carry-out.module bcd_fadd { input [3:0] a, input [3:0] b, input cin, output cout, output [3:0] sum );
Instantiate 100 copies of
bcd_fadd
to create a 100-digit BCD ripple-carry adder. Your adder should add two 100-digit BCD numbers (packed into 400-bit vectors) and a carry-in to produce a 100-digit sum and carry out. -
Code Block
module top_module( input [399:0] a, b, input cin, output cout, output [399:0] sum ); wire [99:0] cout_temp; genvar i; generate for(i=0;i<100;i++) begin:adder if(i == 0) begin bcd_fadd bcd_inst(a[3:0],b[3:0],cin,cout_temp[0],sum[3:0]); end else begin bcd_fadd bcd_inst(a[4*i+3:4*i],b[4*i+3:4*i],cout_temp[i-1],cout_temp[i],sum[4*i+3:4*i]); end end assign cout=cout_temp[99]; endgenerate endmodule