关于Verilog中判断语句执行时序和modelsim时标取值的问题

文章讲述了Verilog中if语句的执行时机在仿真中的区别,解释了modelsim中的取样方式(右侧取样和左侧取样)如何影响信号变化,以及与寄存器时序的关系。
摘要由CSDN通过智能技术生成

开发平台:vivado 2020.1
仿真平台:modelsim 10.1d

前景提要

  本人FPGA菜鸟一枚,本文旨在记录自己在工程中遇到的困惑。

  我在Verilog代码中每次遇到 if语句就会想:if语句在T0时刻判断条件成功后,执行的语句是在T1时刻还是T0时刻立马执行?
通常在实际工程中无论是仿真还是逻辑分析仪抓信号结果都是:if语句在T0时刻判断条件成功后,执行的语句是在T1时刻。
modelsim时标取值是左侧取样还是右侧取样?
例1:

module test_ifelse(
	input	wire 		clk,
	output	reg	  [3:0]	data_out
	);

reg	[3:0]	data_in=0;

always@(posedge clk)begin
	data_in <= data_in + 1;
end

always @(posedge clk)begin
	if(data_in == 1)
		data_out<= 1;
	else if(data_in == 2)
		data_out<= 2;
	else if(data_in == 3)
		data_out<= 3;
	else if(data_in == 4)
		data_out<= 4;
	else if(data_in == 5)
		data_out <=5;
	else if(data_in == 6)
		data_out <=6;
	else 
		data_out<=0;
end
endmodule

modelsim仿真截图如下:
在这里插入图片描述

图1: 例1仿真图

此时modelsim的时标取值为右侧取样
  这也是平时在工程中所见的时序图。同时也是我产生了误解的原因之一,让我以为if语句的执行总是在判断条件成功后的下一拍。
其实不然,再看下一个例子。

例2:

module test_ifelse(
	input	wire 			clk,
	input	wire	[3:0]	data_in,
	output	reg	  	[3:0]	data_out
	);

always @(posedge clk)begin
	if(data_in == 1)
		data_out<= 1;
	else if(data_in == 2)
		data_out<= 2;
	else if(data_in == 3)
		data_out<= 3;
	else if(data_in == 4)
		data_out<= 4;
	else if(data_in == 5)
		data_out <=5;
	else if(data_in == 6)
		data_out <=6;
	else 
		data_out<=0;
end
endmodule

例2中将data_in改成外部输入的信号,data_in设置为0-6的伪随机数。testbench如下:

`timescale 1ns/1ns

module tb_test_ifelse();
reg 			clk;
reg 	[3:0]	a;
wire 	[3:0]	b;

initial begin
	a =0;
	clk =0;
end

always #20 a = {$random}%7;

always #10 clk = ~clk;

test_ifelse inst_test_ifelse(
	.clk(clk),
	.data_in(a),
	.data_out(b)
	);
endmodule

此时modelsim仿真截图如下:
在这里插入图片描述

图2:例2仿真图
此时modelsim取值为右侧取样。

从仿真图可以看到,在时钟上升沿采到数据后,data_out在同一时刻立马发生了变化。即结论:if语句是在当前时刻判断完成后,在当前时刻立马执行语句

为什么例1和例2的仿真结果看起来不一样呢?我们实际工程中又该如何区分呢?

问题分析

  例1和例2的区别在于data_in时钟域不同。在例1模块中data_in和data_out是在同一个时钟域。在例2模块中,data_in为外部输入信号,不一定是和data_out在同一时钟域(先不考虑时序违规的情况,时序违规请参考这篇FPGA时序分析与约束文章)。

  在FPGA中,寄存器是由触发器构成
寄存器模型

图3:寄存器模型图
  绝大部分设计中使用的寄存器都是时钟上升沿触发,即:当时钟上升沿来临时,寄存器q输出data_in的值。

  在理想的寄存器模型中,q的输出是0延时的,即:时钟上升沿来临的时,0延时输出q的值。例如:
寄存器时序理想时序图

图4:寄存器理想时序图
T0T1T2T3T4T5T6T7T8T9T10
clk上升沿上升沿上升沿上升沿上升沿上升沿上升沿上升沿上升沿上升沿上升沿
data_in(旧值)00110100110
q(旧值>>新值)0>>00>>00>>11>>11>>00>>11>>00>>00>>11>>11>>0

(PS:>>代表变化,例如0>>1表示 0变化到1)
  由于这是理想情况下,在时钟上升沿来临的时刻,寄存器将旧值(data_in)赋值给新值(q)是瞬时完成的。
实际上,即使随着现代技术的更迭,寄存器的输出速度越来越快,但仍然需要时间,实际上的寄存器时序图应该是:
在这里插入图片描述

图5:寄存器实际时序图
  寄存器旧值(data_in)在经过tco延迟后传输给q(新值)。

  早期EDA厂商基于这种实际捕获信号的逻辑值来同时判断时序变化是否违规,以及新旧值是否变化正确。在现代随着大规模集成电路发展,工程信号多而逐渐淘汰。自上世纪七十年代后,Synopsys公司以及EDA理论先驱卡佛尔.米德均提出:将逻辑功能分析和时间延迟分析分开 即:功能分析只看逻辑功能对不对的问题,时序分析再看时序是否违规,时序快不快的问题。因此在功能仿真中,现代EDA工具大多倾向于以0填充tco延迟来右侧逼近时钟上升沿
(参考:《现代数字电路设计与实践》-陆广)

结果分析

  现在回过头来看例1,画出例1的实际时序图:
在这里插入图片描述

图6:例1实际时序图
T0T1T2T3T4T5T6T7
clk上升沿上升沿上升沿上升沿上升沿上升沿上升沿上升沿
data_in(采样时刻值)01234567
data_in(采样时刻经过tco时刻后的变化)0>>11>>22>>33>>44>>55>>66>>77>>7
data_out(采样时刻值)00123456
data_out(采样时刻经过tco时刻后的变化)0>>00>>11>>22>>33>>44>>55>>66>>7

总上所述,现在EDA工具以0代替tco延迟,右侧逼近时钟沿,画出例子1功能仿真时序图:
在这里插入图片描述

图7:例1实际时序图中以0代替tco后的时序图

此时就和上面图1中moselsim显示的时序图一模一样了:
在这里插入图片描述

同理,画出例2实际时序图:
在这里插入图片描述

图8:例2实际时序图

再以0填充tco延迟,画出例2功能仿真时序图:
在这里插入图片描述

图9:例2实际时序图中以0代替tco后的时序图

此时就和上面图2中moselsim显示的时序图一模一样了:
在这里插入图片描述
以上都是modelsim中时标为右侧取样的例子,但实际工程中也有一些信号取样为左侧逼近取样。例如FIFO仿真测试程序如下:

module fifo_test(
	input	wire			clk,
	input	wire			rst,
	output	wire	[7:0]	fifo_data_out
    );
	
reg	[7:0]	fifo_data_in=8'd23;
reg			wr_en;
reg			rd_en;
reg [5:0]	wr_cnt;
reg [5:0]	rd_cnt;
wire[5:0]	wr_fifo_cnt;
wire[5:0]	rd_fifo_cnt;
wire		fifo_full;
wire		fifo_empty;

always@(posedge clk)begin
	if(rst == 1)begin
		fifo_data_in <= 8'd23;
		wr_en <= 1'b0;
		wr_cnt <= 6'd0;
	end
	else if(wr_cnt == 16) begin
	   wr_en <= 0;
	   fifo_data_in <= fifo_data_in;
	   wr_cnt <= 6'd16;
	end
	else begin
	   wr_en <= 1'b1;
	   fifo_data_in <= fifo_data_in + 10;
	   wr_cnt <= wr_cnt + 1;
	end
end

always @(posedge clk)begin
    if(rst == 1)begin
        rd_en <= 1'b0;
        rd_cnt <= 6'd0;
    end
    else if(rd_cnt == 16)begin
        rd_en <= 1'd0;
        rd_cnt <= 6'd16;
    end
    else if(rd_fifo_cnt == 15) begin
        rd_en <= 1'b1;
        rd_cnt <= rd_cnt +1;
    end
    else begin
        rd_en <= rd_en;
        rd_cnt <= rd_cnt;
    end
end

fifo1 inst_fifo1 (
  .rst(1'b0),                      	
  .wr_clk(clk),              
  .rd_clk(clk),              
  .din(fifo_data_in),                    
  .wr_en(wr_en),                
  .rd_en(rd_en),                
  .dout(fifo_data_out),                  
  .full(fifo_full),                  
  .empty(fifo_empty),                
  .rd_data_count(wr_fifo_cnt),
  .wr_data_count(rd_fifo_cnt) 
);
endmodule

测试程序如上,向FIFO写入16个数据后,再读16个数据出来。

testbench如下:

`timescale 1ns / 1ps

module tb_fifo_test();

reg				clk,rst;
wire	[7:0]	data_out;

initial begin
	clk = 0;
	rst = 1;
	#240;
	rst = 0;
end

always #10 clk = ~clk;

fifo_test inst_fifo_test(
	.clk(clk),
	.rst(rst),
	.fifo_data_out(data_out)
);
endmodule

打开modelsim仿真如下:
在这里插入图片描述
在250ns,时钟上升沿,fifo_data_in右侧取样为33,wr_en右侧取样为1’d1,wr_cnt右侧取样为6’d1

在这里插入图片描述
在270ns,时钟上升沿,fifo_data_in右侧取样为43,wr_en右侧取样为1’d1,wr_cnt右侧取样为6’d2

在这里插入图片描述
在290ns,时钟上升沿,fifo_data_in右侧取样为53,wr_en右侧取样为1’b1,wr_cnt右侧取样为6’d3,rd_fifo_cnt左侧取样为6’d0

在这里插入图片描述
在590ns,时钟上升沿,rd_en右侧取样为1’b1,wr_fifo_cnt左侧取样为6’d11,rd_cnt右侧取样为6’d1,rd_fifo_cnt左侧取样为6’d15

由此可见,在实际工程中也会遇到modelsim等仿真软件左侧取样和右侧取样同时出现情况。此时我们随便取一个左侧取样的信号,不断的放大时标来观察情况如下:

在这里插入图片描述
  在590ns,时钟上升沿,凡是左侧取样的信号,皆有0.1ns的延迟。此时modesim等仿真信号没有以0填充,必定有0.1ns的延迟,这可能就是寄存器翻转所带来的tco时间开销。

总结


verilog中if语句判断和执行是在同一时刻

凡是在modelsim功能仿真中,右侧取样的信号一定是在时钟沿时刻翻转,0延迟

凡是在modelsim功能仿真中,左侧取样的信号一定是在时钟沿右侧翻转,非0延迟

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱奔跑的虎子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值