[1]、数字芯片实验室(微信公众号)
因为最近参加Xilinx暑假计划比较忙,已经很久没写文章了,这篇博客我们将重点讲述Verilog中阻塞逻辑赋值‘=’、非阻塞逻辑赋值‘<=’。看到这里不免大家要嘲笑,这谁不知道,文章的内容咋么越写越倒退了。其实不是这样,绝大多数的同学理解阻塞赋值与非阻塞赋值是组合逻辑语句用阻塞赋值 ‘=’、时序逻辑用非阻塞逻辑赋值‘<=’。那么真的是这样吗? 答案是否定的,其实组合逻辑语句一定用阻塞赋值 ‘=’,时序逻辑也可以用阻塞逻辑赋值‘=’。
本次实验所用到的软硬件平台如下:
1、VIVADO 2019.1
2、Modelsim 10.7
其实阻塞逻辑赋值与非阻塞逻辑赋值最明显的区别就是:是否在整个代码块执行完毕再更新代码块中的数值。对于阻塞赋值 ‘=’被赋值的变量立刻发生变化,非阻塞逻辑赋值‘<=’会等到整个代码块运行结束后再进行变量的更新。接下来将以一道题目来解释于阻塞赋值 ‘=’出现在时序逻辑中的例子。
题目要求: 将一个串行执行的C语言算法转化为单拍完成的并行可综合verilog。
C语言源码如下:
unsigned charcal_table_high_first(unsignedcharvalue)
{
unsigned char i ;
unsigned char checksum = value ;
for (i=8;i>0;--i)
{
if (check_sum& 0x80)
{
check_sum = (check_sum<<1) ^ 0x31;
}
else
{
check_sum = (check_sum << 1);
}
}
return check_sum;
}
针对上面的函数输入相应的变量检测我们接下来代码实现的正确性:
value =0x0:check_sum=0x0
value =0x1:check_sum=0x31
value =0x2:check_sum=0x62
value =0x3:check_sum=0x53
value =0x4:check_sum=0xc4
value =0x5:check_sum=0xf5
value =0x6:check_sum=0xa6
value =0x7:check_sum=0x97
value =0x8:check_sum=0xb9
value =0x9:check_sum=0x88
value =0xa:check_sum=0xdb
value =0xb:check_sum=0xea
value =0xc:check_sum=0x7d
这里可以看出题意上的要求是单拍完成,这个题很想CRC校验的问题,整个CRC校验每个字节的输入也必须再单拍内完成,还记得当时的博客我们咋么进行实验的吗,就是将每一位输出表达式进行彻底展开,然后在一拍之后输出,不太明白的同学,可以查看这篇博客基于FPGA的千兆以太网的实现(3),但是在进行输出展开的时候特别耗费时间,而且还有可能出错,那么有没有更好的办法进行计算呢?接下来我们将充分利用组合逻辑来实现这题目。
在进行写代码时,首先进行以下分析:
该算法逻辑如下:输入一个8bit的数,首先判断最高位是否为1,如果为1则左移一位,并且和8‘b00110001异或;如果最高位不为1则左移一位。此过程执行8次。
我们可以看出**:任何数与0异或都等于它本身**,即0^x=x。所以我们可以把算法流程变换为:8’h31 = 8’b00110001, 8’h00 = 8’b00000000,设左移前最高位为M,可以将判断左移前最高位是否为1的过程省略,直接与8’b00MM000M异或,此时流程图可以简化为:
那么接下来我们将给出上面硬件逻辑的代码,从代码中我们便可以看出时序逻辑电路中阻塞逻辑赋值的重要性。
将上述的题目使用verilog实现,代码如下:
`timescale 1ns / 1ps
// *********************************************************************************
// Author : zhangningning
// Email : [email protected]
// Website : https://blog.csdn.net/zhangningning1996
// Module Name : test.v
// Create Time : 2020-08-05 20:49:16
// Revise : 2020-08-05 20:49:16
// Editor : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date By Version Change Description
// -----------------------------------------------------------------------
// XXXX zhangningning 1.0 Original
//
// *********************************************************************************
module test(
input sclk ,
input rst_n ,
input [ 7:0] data ,
input data_en ,
output reg [ 7:0] check_sum ,
output reg check_sum_en
);
//========================================================================================\
//**************Define Parameter and Internal Signals**********************************
//========================================================================================/
//========================================================================================\
//************** Main Code **********************************
//========================================================================================/
integer i ;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
check_sum = 8'd0;
else if(data_en == 1'b1)
for(i = 0 ; i < 8 ; i = i+1)
if(i == 0)
check_sum = {data[6:0],1'b0}^({8{data[7]}} & 8'h31);
else
check_sum = {check_sum[6:0],1'b0}^({8{check_sum[7]}} & 8'h31);
else
check_sum = 8'd0;
always @(posedge sclk)
check_sum_en <= data_en;
endmodule
同学们可以看到,代码是多么简单,但是注意这里一定要是阻塞赋值,否则就不对,下面的代码会说明这个问题。
大家可以注意这个代码风格很像VHDL,确实不太想我们平时写的代码,因为这样我们不能做到很好的时序把控。
tb模块:
`timescale 1ns / 1ps
// *********************************************************************************
// Author : zhangningning
// Email : [email protected]
// Website : https://blog.csdn.net/zhangningning1996
// Module Name : tb.v
// Create Time : 2020-08-05 21:07:15
// Revise : 2020-08-05 21:07:15
// Editor : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date By Version Change Description
// -----------------------------------------------------------------------
// XXXX zhangningning 1.0 Original
//
// *********************************************************************************
module tb();
reg sclk ;
reg rst_n ;
reg [ 7:0] data ;
reg data_en ;
wire [ 7:0] check_sum ;
wire check_sum_en ;
initial begin
sclk <= 1'b0;
rst_n = 1'b0;
data = 8'd0;
data_en = 1'b0;
#(100);
rst_n = 1'b1;
#(100);
data_en = 1'b1;
#(200);
data_en = 1'b0;
#(100);
$stop;
end
always #10 sclk <= ~sclk;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
data <= 8'd0;
else if(data_en == 1'b1)
data <= data + 1'b1;
else
data <= data;
test test_inst(
.sclk (sclk ),
.rst_n (rst_n ),
.data (data ),
.data_en (data_en ),
.check_sum (check_sum ),
.check_sum_en (check_sum_en )
);
endmodule
仿真结果如下:
将上面的结果与C语言的运行结果相对比,可以验证我们实验的正确性
前面我们使用的是阻塞赋值,如果我们将其换成非阻塞赋值,那么实验结果将会发生改变,详情如下。
test模块:
`timescale 1ns / 1ps
// *********************************************************************************
// Author : zhangningning
// Email : [email protected]
// Website : https://blog.csdn.net/zhangningning1996
// Module Name : test.v
// Create Time : 2020-08-05 20:49:16
// Revise : 2020-08-05 20:49:16
// Editor : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date By Version Change Description
// -----------------------------------------------------------------------
// XXXX zhangningning 1.0 Original
//
// *********************************************************************************
module test(
input sclk ,
input rst_n ,
input [ 7:0] data ,
input data_en ,
output reg [ 7:0] check_sum ,
output reg check_sum_en
);
//========================================================================================\
//**************Define Parameter and Internal Signals**********************************
//========================================================================================/
//========================================================================================\
//************** Main Code **********************************
//========================================================================================/
integer i ;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
check_sum <= 8'd0;
else if(data_en == 1'b1)
for(i = 0 ; i < 8 ; i = i+1)
if(i == 0)
check_sum <= {data[6:0],1'b0}^({8{data[7]}} & 8'h31);
else
check_sum <= {check_sum[6:0],1'b0}^({8{check_sum[7]}} & 8'h31);
else
check_sum <= 8'd0;
always @(posedge sclk)
check_sum_en <= data_en;
endmodule
测试模块的代码与上面的相同,这里不在给出。
我们将上面的代码进行仿真,结果如下:
从上面可以发现仿真结果与C语言的仿真不一致,所以通过这篇博客可以学会阻塞赋值与非阻塞赋值的重要性。
通过上面的描述,同学们应该知道时序逻辑中也是可以用阻塞赋值的,这在VHDL中是比较常用的,同时也会带来一些列的时序问题,所以不建议多用。
创作不易,认为文章有帮助的同学们可以关注、点赞、转发支持。(txt文件、图片文件在群中)对文章有什么看法或者需要更近一步交流的同学,可以加入下面的群: