來源:EETOP BLOG 作者:acgoal http://www./blog/html/46/553746-51456.html 下面有一段verilog代碼和仿真文件,,用的是VCS仿真和編譯工具。我們來研究一下不同的驅(qū)動(dòng)賦值方式對(duì)仿真結(jié)果的影響,。下面我把我做的例子和大家分享一下,。 設(shè)計(jì)源代碼如下:
`timescale 1ns/1ps module counter (data_out0, data_out1,clk,rst_n, data_in0, data_in1); output [3:0] data_out0; output [3:0] data_out1; input [3:0] data_in0; input [3:0] data_in1; input clk; input rst_n;
reg [3:0] data_out0; reg [3:0] data_out1; always @(posedge clk or negedge rst_n) begin if(!rst_n) begin data_out0 <= 4'd0; end else begin data_out0 <= data_in0; end end always @(posedge clk) begin if(!rst_n) begin data_out1 <= 4'd0; end else begin data_out1 <= data_in1; end end endmodule
這里有兩段,分別表示帶異步復(fù)位和帶同步復(fù)位的,。
測(cè)試程序如下:
`timescale 1ns/1ps module tb_top; reg clk; reg rst_n; wire [3:0] data_out0; wire [3:0] data_out1; reg [3:0] data_in0; reg [3:0] data_in1; counter u_counter0(.clk(clk), .rst_n(rst_n), .data_out0(data_out0), .data_out1(data_out1), .data_in0(data_in0), .data_in1(data_in1));
initial begin clk=1'b0; end always begin #3 clk=~clk; end initial begin rst_n = 1'b1; #15 rst_n = 1'b0; #180 rst_n = 1'b1; #200 $finish; end
initial begin data_in0 <= 0; wait(!rst_n); wait(rst_n); @(posedge clk); data_in0 <= 1; @(posedge clk); data_in0 <= 2; @(posedge clk); data_in0 <= 4; @(posedge clk); data_in0 <= 8; @(posedge clk); data_in0 <= 0; end
initial begin data_in1 <= 0; wait(!rst_n); wait(rst_n); @(posedge clk); data_in1 <= 1; @(posedge clk); data_in1 <= 2; @(posedge clk); data_in1 <= 4; @(posedge clk); data_in1 <= 8; @(posedge clk); data_in1 <= 0; end
initial begin $vcdpluson(); end endmodule
注意這里紅色的部分,,data0和data1的驅(qū)動(dòng)都用非阻塞賦值,。得到的仿真波形圖如下:
下面我把非阻塞賦值改成阻塞賦值,改動(dòng)部分如下:initial begin data_in0 = 0; wait(!rst_n); wait(rst_n); @(posedge clk); data_in0 = 1; @(posedge clk); data_in0 = 2; @(posedge clk); data_in0 = 4; @(posedge clk); data_in0 = 8; @(posedge clk); data_in0 = 0; end
initial begin data_in1 = 0; wait(!rst_n); wait(rst_n); @(posedge clk); data_in1 = 1; @(posedge clk); data_in1 = 2; @(posedge clk); data_in1 = 4; @(posedge clk); data_in1 = 8; @(posedge clk); data_in1 = 0; end
這樣的代碼,,仿真波形圖如下:
和上面的不一樣,,為什么呢,為什么這次的flop行為沒有了呢,?直接給結(jié)論吧,。 根據(jù)verilog/system verilog的standard。我這里以system verilog的standard為例,,STD 1800-2009版本,,第四章“Scheduling Semantics”,這一章關(guān)于仿真的調(diào)度描述,。 simulation的event大體分為:Active event -> Inactive event -> NBA event -> Overserved event -> Reactive -> Re-Inactive -> Re-NBA 一般的,,阻塞賦值發(fā)生在active/inactive event,而NBA( Non-blocking assignment update)從字面意思就能看出來是做非阻塞賦值的,。 再結(jié)合上面的例子,,如果data_in0是阻塞賦值出去的話,因?yàn)槔永锏膁ut是在時(shí)鐘上升延來的時(shí)候進(jìn)行非阻塞賦值(data_out0 <= data_in0),,這句話屬于NBA event,,而阻塞賦值data_in0 = 1 (或者2,4,,8)是Active event,,他比NBA要先執(zhí)行,所以我們看到data_out0在當(dāng)前的cycle就被采樣到了,。這就是后面一個(gè)波形圖的樣子,,看起來就像flop沒有用一樣。實(shí)際上是我們driver寫的有問題,。 如果data_in0在testbench里面用阻塞賦值就是 data_in0 <= 1 (或者2,,4,8,,見例子),,那么data_out0 <= data_in0, 和data_in0 <= 1同屬于NBA event。這樣子這兩句話同時(shí)執(zhí)行,,這樣就和實(shí)際電路中的flop行為一致了(就是并行執(zhí)行),。 結(jié)論:做驅(qū)動(dòng)的接口信號(hào)最好是非阻塞賦值出去。
|