Verilog2——赋值语句、条件分支与循环语句、块语句与生成语句
前言:本文结合练习题目理解总结——赋值语句中阻塞赋值与非阻塞赋值的区别,条件分支与循环语句的使用,块语句和生成语句的语法
一、阻塞赋值与非阻塞赋值
语法理解:
过程赋值与连续赋值
**1-过程赋值(procedure assign)**是在 initial 或 always 语句块里面的赋值,赋值对象是寄存器、整数、实数等类型。
这些变量在被赋值后,其值将保持不变,直到重新被赋予新值。
1)语法上,没有关键词“assign”;2)过程赋值语句只能出现在过程块(procedure block)中;3)左侧被赋值的数据类型必须是寄存器类型的变量(reg);
2-连续赋值(continuous assign) 是在initial 或 always 语句块外面的赋值,总是处于激活状态,任何操作数的改变都会影响表达式的结果;
1)语法上,有关键词“assign”来标识;2)左侧被赋值的数据类型必须是线网型数据(wire);
3)连续赋值语句不能出现在过程快中(initial/always);4)连续赋值语句产生作用后,赋值表达式中信号的任何变化都将立即被反映到赋值线网型数据的取值上;
过程赋值只有在语句执行的时候,才会起作用。这是连续性赋值与过程性赋值的区别。
Verilog 过程赋值包括 2 种语句:阻塞赋值与非阻塞赋值。(另外一种是过程连续赋值,不可综合)
非阻塞赋值(Non-blocking)
[ b <= a LHS <= RHS ]
1-在语句块中,此语句所赋变量的值不能直接为后续语句使用;
2-当整个块结束,此条语句方可完成赋值操作,RHS方可得到值;
3-执行到非阻塞赋值语句时,完成/计算RHS、更新LHS/两个时间上分裂的操作;always块得到触发则开始计算RHS,always块结束,方可更新LHS;
4-所谓非阻塞,即该语句未完成赋值(LHS未得到值)时其他语句也可以开始计算RHS的操作,即计算RHS时,其中所涉及的变量也可以在别的Verilog语句中计算和更新。
5-非阻塞赋值操作只能对寄存器型变量赋值,只能用在initial块和always块中,不允许连续赋值;
6-常用于时序逻辑块中;
阻塞赋值(blocking)
[ b = a LHS = RHS ]
1-执行阻塞赋值语句,先计算RHS,计算时不允许其他语句的打断干扰,该语句完成后,其他赋值语句方可执行。
2-阻塞赋值只有一个完整的步骤----计算后即更新,所谓阻塞,即同一个always块中,后面的赋值语句要在前面的语句完成赋值后才可以开始赋值;
总结
1、时序逻辑建模、锁存器建模采用非阻塞赋值;
2、always块建立组合逻辑电路时,采用阻塞赋值;
3、同一个always块中,两者不能同时出现使用;
4、同一个always块建立时序和组合逻辑电路时,用非阻塞赋值;
5、不能在多个always块中为同一个变量赋值
举例:
`timescale 1ns/1ns
module Tff_2 (
input wire data, clk, rst,
output reg q
);
//*************code***********//
reg data_1;
always @ (posedge clk or negedge rst) begin
if (!rst)
data_1 <= 1'b0;
else
data_1 <= data ^ data_1 ;//TFF关系为 Q*=Q^T;
end
always @ (posedge clk or negedge rst) begin
if (!rst)
q <= 1'b0;
else
q <= data_1 ^ q;
end
//*************code***********//
endmodule
二、条件、分支语句与循环语句
Verilog的过程块提供了丰富的语句集,在过程块中,可以将多条语句合成一组,进行行为级描述。
条件语句(if_else)与分支语句(case)
`timescale 1ns/1ns
module multi_sel(
input [7:0]d ,
input clk,
input rst,
output reg input_grant,
output reg [10:0]out
);
//*************code***********//
reg [1:0]cnt;
reg [7:0]data_reg;
always@(posedge clk or negedge rst)begin
if(!rst) begin
cnt <= 0;
end
else if(cnt == 2'd3) begin
cnt <= 0;
end
else begin
cnt <= cnt + 1'd1;
end
end
always@(posedge clk or negedge rst)begin
if(!rst)begin
input_grant <= 0;
data_reg <= 0;
end
else if(cnt == 2'd0) begin//没有复位且cnt置0,说明计满或者开始计数
input_grant <= 1;
data_reg <= d;//读取输入数据
end
else begin
input_grant <= 0;
data_reg <= data_reg;//读取输入数据
end
end
always@(posedge clk or negedge rst)begin
if(!rst)
out <= 0;
else case(cnt)
2'd0: out <= d;
2'd1: out <= (data_reg<<2) - data_reg;//移位的优先级比加减法更低,前面加括号
2'd2:out <= (data_reg<<3)- data_reg;
2'd3: out <= (data_reg<<3) ;
default: out <= 0;
endcase
end
//*************code***********//
endmodule
-
题目描述2:
-
实现方法:
`timescale 1ns/1ns module data_cal( input clk, input rst, input [15:0]d, input [1:0]sel, output [4:0]out, output validout ); //*************code***********// reg [3:0]data3_0; reg [3:0]data7_4; reg [3:0]data11_8; reg [3:0]data15_12; reg [4:0]out_reg; always@(posedge clk or negedge rst)begin if(!rst) begin data3_0 <= 4'b0; data7_4 <= 4'b0; data11_8 <= 4'b0; data15_12 <= 4'b0; end else if(sel == 0)begin //这里必须要加上sel的判断信号作为赋值条件,题目要求只有在sel=0时输入有效,所以不能在所有上升沿都赋值 data3_0 <= d[3:0]; data7_4 <= d[7:4]; data11_8 <= d[11:8]; data15_12 <=d[15:12]; end end always@(posedge clk or negedge rst)begin if(!rst) begin out_reg <= 0; end else if(sel == 0)begin out_reg <= 0; end else case(sel) 2'd1: begin out_reg <= data3_0 + data7_4; end 2'd2: begin out_reg <= data11_8 + data3_0; end 2'd3: begin out_reg <= data3_0 + data15_12; end default : out_reg <= 0; endcase end assign validout = (sel ==0) ? 1'b0 : 1'b1; assign out = out_reg; //*************code***********// endmodule
循环语句(forever repeat while for )
forever语句
常用于产生周期性的波形,用来作为仿真测试信号。不能独立写在程序中,只能放在initial块里;
一旦执行便无限的执行下去,系统函数 $finish 可退出 forever。
//实现一个时钟
reg clk ;
initial begin
clk = 0 ;
forever begin
clk = ~clk ;
#5 ;
end
end
for语句
//初始化memory
initial begin
//读入两路的复乘系数coe和滤波系数
for ( x =0 ;x<2500 ;x=x+1 ) begin
Idata_m[x] <= 0;
Qdata_m[x] <= 0;
end
for ( y =0 ;y<33 ;y=y+1 ) begin
Qcoe_m[y] <= 0;
Icoe_m[y] <= 0;
end
for ( Z =0 ;Z<66 ;Z=Z+1 ) begin
filter_m[Z] <= 0;
end
end
while循环
while(满足条件继续执行否则退出)begin
end
//实现0-10计数
reg [3:0] counter ;
initial begin
counter = 'b0 ;
while (counter<=10) begin
#10 ;
counter = counter + 1'b1 ;
end
end
//stop the simulation
always begin
#10 ; if ($time >= 1000) $finish ;
end
endmodule
repeat循环
repeat 的功能是执行固定次数的循环,它不能像 while 循环那样用一个逻辑表达式来确定循环是否继续执行。
repeat 循环的次数必须是一个常量、变量或信号。如果循环次数是变量信号,则循环次数是开始执行 repeat 循环时变量信号的值。即便执行期间,循环次数代表的变量信号值发生了变化,repeat 执行次数也不会改变。
//语法格式
repeat (loop_times)begin
end
//连续存储
always @(posedge clk or negedge rstn) begin
j = 0 ;
if (!rstn) begin
repeat (8) begin
buffer[j] <= 'b0 ; //没有延迟的赋值,即同时赋值为0
j = j + 1 ;
end
end
else if (enable) begin
repeat (8) begin
@(posedge clk) buffer[j] <= counter3 ; //在下一个clk的上升沿赋值
j = j + 1 ;
end
end
end
三、块语句
顺序块
顺序块用关键字 begin 和 end 来表示。
顺序块中的语句是一条条执行的。非阻塞赋值除外。
并行块
并行块有关键字 fork 和 join 来表示。
并行块中的语句是并行执行的,阻塞形式的赋值也是并行执行。
举例
`timescale 1ns/1ns
module test ;
reg [3:0] ai_sequen2, bi_sequen2 ;
reg [3:0] ai_paral2, bi_paral2 ;
//顺序块和并行块的嵌套使用
initial begin: block_out // 块语句命名,可以通过disable block_out禁用
ai_sequen2 = 4'd5 ; //at 0ns
//顺序块中每条语句的时延总是与其相邻的前面语句执行的时间相关。
fork: block_in
//并行块中每条语句的时延都是与块语句开始执行的时间相关。
#10 ai_paral2 = 4'd5 ; //at 10ns
#15 bi_paral2 = 4'd8 ; //at 15ns
join
//前一语句结束时为15ns
#20 bi_sequen2 = 4'd8 ; //at 35ns
end
endmodule
四、生成块
用途:
Verilog中的generate语句常用于编写可配置的、可综合的RTL的设计结构。它可用于创建模块的多个实例化,或者有条件的实例化代码块。
语法:
generate相关的有generate for——用来构造循环结构,用来多次实例化某个模块;
generate if, generate case——用来在多个块之间最多选择一个代码块;
generate block,genvar;
用法:
generate的用法还是很宽泛的,它和module可以说是一个等级的。
-
1.在generate中的要求和在module中很类似,因为generate就是生成一个电路,电路结构就是你在generate中表述的内容。
-
2.可以独立存在于generate块或者module的应当是变量声明,常量定义,assign赋值,门级语句,块声明,实例调用方法(I/O匹配表);像if-else,while,for,case这类的语句都是高级语句,是不能独立出现的,必须放在initial或always块中。
-
3.在generate block里面可以有:instance 实例化,assign 语句,always 模块。
举例:
generate for
//首先使用genvar声明循环中使用的临时索引变量,被用来判断generate循环是否继续;
//genvar声明在生成块内部外部均可,不同块使用相同的变量名亦可只要不嵌套,仿真时该变量不存在;
//在“展开”生成循环的每个实例中,将创建一个隐式localparam,其名称和类型与循环索引变量相同。它的值是“展开”循环的特定实例的“索引”。可以从RTL引用此localparam以控制生成的代码,甚至可以由分层引用来引用。(这里不太理解)
module alu ( inputa, b,
output sum,cout);
assign sum = a ^ b;
assign cout = a & b;
endmodule
module my_design
#(parameter N=4)
input [N-1:0] a, b,
output [N-1:0]sum, cout);
genvar i;
// Generate for loop to instantiate N times
generate (optional)
for (i =; i< N; i++) begin: gen
alu alu_inst (a[i],b[i],sum[i], cout[i]);
end
endgenerate
endmodule
case generate & if generate
五、参考资料
1-菜鸟教程
2-Verilog数字系统设计教程讲解