怎么給幼兒園做網(wǎng)站seo專業(yè)培訓機構(gòu)
??對于初學者,一般會遇到這種情況,明明寫的時序邏輯,結(jié)果仿真結(jié)果卻是組合邏輯,然后看遍設(shè)計代碼,始終找不到原因,交流群、知乎這種問題隨處可見。但不要懷疑軟件問題,modelsim這些專用軟件基本不會遇見軟件自身問題,原因其實很簡單,因為多數(shù)人只關(guān)注設(shè)計文件不會關(guān)注TestBentch的合理性,導致找不到問題原因,后文分析原因并給出避免這種問題?的方法。
??在仿真時經(jīng)常會使用“#”和“@(posedge clk)”來實現(xiàn)延遲,“#”后面跟數(shù)字,表示延遲數(shù)字對應(yīng)最小的時間單位,而“@(posedge clk)”則用來檢測clk信號上升沿,如果CYCLE表示始終周期對應(yīng)的時間長度,那么“#(CYCLE)”表示延遲一個時鐘周期長度的時間。在時鐘上升沿輸出數(shù)據(jù)后,使用“@(posedge clk)”,會延遲到下個時鐘上升沿,同樣也可以表示延遲一個時鐘周期,那有沒有區(qū)別?
??分析以下代碼,輸出dout就是兩個輸入dataa與datab相加,由于是時序邏輯,dout會延遲dataa或datab變化后的一個時鐘周期。
module add( input clk ,//系統(tǒng)時鐘; input rst_n ,//系統(tǒng)復位,低電平有效; input [3 : 0] data_a ,//加數(shù)dataa; input [3 : 0] data_b ,//加數(shù)datab; output reg [4 : 0] dout );always@(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin//初始值為0; dout <= 4'd0; end else begin dout <= data_a + data_b; end endendmodule
??Testbench如下所示:
`timescale 1 ns/1 ns
module test(); parameter CYCLE = 10 ;//系統(tǒng)時鐘周期,單位ns,默認10ns; parameter RST_TIME = 5 ;//系統(tǒng)復位持續(xù)時間; parameter STOP_TIME = 100 ;//仿真運行時間,復位完成后運行100個系統(tǒng)時鐘后停止; reg clk ;//系統(tǒng)時鐘,默認100MHz; reg rst_n ;//系統(tǒng)復位,默認低電平有效; reg [3 : 0] data_a; reg [3 : 0] data_b;wire [4 : 0] dout ; add u_add ( .clk ( clk ), .rst_n ( rst_n ), .data_a ( data_a ), .data_b ( data_b ), .dout ( dout ) ); //生成周期為CYCLE數(shù)值的系統(tǒng)時鐘; initial begin clk = 1; forever #(CYCLE/2) clk=~clk; end//生成復位信號; initial begin rst_n = 1; #2; rst_n = 0;//開始時復位10個時鐘; #(RST_TIME*CYCLE); rst_n = 1; end //生成輸入信號din; initial begin data_a = 0;data_b=0;//輸入數(shù)據(jù)初始化為0; #(10*CYCLE);//延遲10個時鐘周期; repeat(STOP_TIME)begin//循環(huán)STOP_TIME次;#(CYCLE); data_a = {$random} % 16; data_b = {$random} % 16; end $stop;//停止仿真; endendmodule
??使用modelsim仿真如下圖所示,奇怪的是為什么時序邏輯仿真成組合邏輯了?
??分析:加法器代碼肯定是沒有問題的,modelsim軟件也是經(jīng)過fpga設(shè)計以及IC設(shè)計人員多年使用,是最常用的仿真工具,也不可能出現(xiàn)這樣的低級bug。如果你把代碼下載到開發(fā)板上,使用在線邏輯分析儀抓取數(shù)據(jù),能夠得到正確的運行結(jié)果,但是仿真就是錯誤的。這是為什么?那就只剩下寫的testbench文件了,來看下testbench與輸出相關(guān)的信號,首先時鐘和復位信號是沒有問題的,那就只剩下dataa與datab的產(chǎn)生模塊了,如下所示:
//生成輸入信號din;
initial begin data_a = 0;data_b=0;//輸入數(shù)據(jù)初始化為0; #(10*CYCLE);//延遲10個時鐘周期; repeat(STOP_TIME)begin//循環(huán)STOP_TIME次;#(CYCLE); data_a = {$random} % 16; data_b = {$random} % 16; end $stop;//停止仿真;
end
??開始仿真時兩個輸入信號都被賦值為0,經(jīng)10個時鐘延遲之后進入forever循環(huán)內(nèi),每次循環(huán)之前都會把數(shù)據(jù)延遲一個時鐘周期,然后在對兩個輸入信號賦一個0~15的隨機值。邏輯上其實沒有問題,但是注意一個問題,每次給dataa和datab賦值時間與時鐘clk上升沿是對齊的,導致D觸發(fā)器的輸入信號在時鐘上升沿時發(fā)生變化,由此導致D觸發(fā)器數(shù)據(jù)采集錯誤,最終導致D觸發(fā)器輸出信號dout提前更新數(shù)據(jù)。這里實際上與保持時間違例有點類似,D觸發(fā)器的下一個輸入數(shù)據(jù)來得過快,影響了上一個數(shù)據(jù)的采集。
??解決方法很簡單,因為是數(shù)據(jù)剛好在時鐘上升沿時發(fā)生更新導致D觸發(fā)器數(shù)據(jù)采集錯誤,那么把兩個輸入數(shù)據(jù)全部延遲一點不就行了,修改如下,將兩個輸入數(shù)據(jù)的所有變化均延遲1ns,與時鐘上升沿錯開。
//生成輸入信號din;
initial begin #1; data_a = 0;data_b=0;//輸入數(shù)據(jù)初始化為0; #(10*CYCLE);//延遲10個時鐘周期; repeat(STOP_TIME)begin//循環(huán)STOP_TIME次; #(CYCLE); data_a = {$random} % 16; data_b = {$random} % 16; end $stop;//停止仿真;
end
??修改后仿真結(jié)果如下:
??從上面仿真結(jié)果就可以看到,dataa與datab的變化都延遲時鐘上升沿1ns之后,就沒有在影響仿真結(jié)果了。這也是為什么很多代碼在對信號賦值之前會延遲1ns的原因,就是為了數(shù)據(jù)變化與時鐘上升沿錯開,避免發(fā)生上面這種由于testbench書寫問題所引發(fā)的離奇結(jié)果。
??使用“#”會引發(fā)上面問題,那如果過使用“@(posedge clk)”這種寫法還會出現(xiàn)那樣的仿真結(jié)果?
??先給答案,不會出現(xiàn)類似問題,因為“@(posedge clk)”表示已經(jīng)檢測到時鐘上升沿了,那么在這之后更新的數(shù)據(jù)自然與時鐘上升沿就是錯開的了。
??同樣的案例,只是把dataa和datab賦值的部分改成如下代碼。
//生成輸入信號din;
initial begin data_a = 0;data_b=0;//輸入數(shù)據(jù)初始化為0; #(10*CYCLE);//延遲10個時鐘周期; repeat(STOP_TIME) @(posedge clk)begin//循環(huán)STOP_TIME次; data_a = {$random} % 16; data_b = {$random} % 16; end $stop;//停止仿真;
end
??上面代碼表達結(jié)果與下面代碼一致。
//生成輸入信號din;
initial begin data_a = 0;data_b=0;//輸入數(shù)據(jù)初始化為0; #(10*CYCLE);//延遲10個時鐘周期; repeat(STOP_TIME)begin//循環(huán)STOP_TIME次; @(posedge clk); data_a = {$random} % 16; data_b = {$random} % 16; end $stop;//停止仿真;
end
??仿真結(jié)果如下,沒有出現(xiàn)任何問題,數(shù)據(jù)變化近似與時鐘上升沿對齊,但是輸出數(shù)據(jù)dout沒有受到影響,這就是“@(posedge clk)”的效果。
??“#”和“@(posedge clk)”雖然都可以寫成延時幾個時鐘周期的形式,但是他們是有區(qū)別的,區(qū)別在與“#”延遲與時鐘其實沒有關(guān)系,就有可能和時鐘上升沿重合,這是使用是需要注意的,建議在仿真開始時就對數(shù)據(jù)延遲1ns然后在賦值,與時鐘信號變化錯位。而“@(posedge clk)”本質(zhì)是檢測時鐘上升沿,在時鐘上升沿之后才會去執(zhí)行后面的語句,所以數(shù)據(jù)變化與時鐘上升沿變化是錯位的,不會出現(xiàn)“#”那種問題。
??上述從效果看肯定是使用“@(posedge clk)”,作為延遲更好,使用“#(CYCLE)”延遲一個時鐘更號理解,但是要注意可能遇到的問題,一般使用模板時,賦值語句開頭會帶有“#1;”,或者在每次賦值前用“@(posedge clk)”作為延遲,如下我常用賦值模板。
//生成輸入信號din;
initial begin #1;din = 0;//輸入數(shù)據(jù)初始化為0; #(10*CYCLE);//延遲10個時鐘周期; end