日本黄色一级经典视频|伊人久久精品视频|亚洲黄色色周成人视频九九九|av免费网址黄色小短片|黄色Av无码亚洲成年人|亚洲1区2区3区无码|真人黄片免费观看|无码一级小说欧美日免费三级|日韩中文字幕91在线看|精品久久久无码中文字幕边打电话

當前位置:首頁 > > ZYNQ


使用邏輯門和連續(xù)賦值對電路建模,是相對詳細的描述硬件的方法。使用過程塊可以從更高層次的角度描述一個系統(tǒng),稱作行為級建模(behavirol modeling)。

1. 過程賦值

阻塞賦值和非阻塞賦值的區(qū)別都很熟悉了。這里記錄兩個特性。

1.1 特性1

絕大多數(shù)情況下,非阻塞賦值都是一個時間點處最后執(zhí)行的賦值語句??聪旅娴氖纠a:

module test
(
 input clk,
 output reg a, b
);

always @ (posedge clk) begin
 a = 0;
 b = 1;
 a <= b;
 b <= a;
end

endmodule

非阻塞賦值可以認為包括兩步:
(1)求值和調(diào)度(evaluate and schedule),先得到非阻塞賦值等式右側(cè)的值,并將這次賦值安排在當前時間點的結(jié)束時刻。
(2)當前時間點結(jié)束時,更新左側(cè)的值。因此這段代碼的結(jié)果是 a = 1,b = 0。

1.2 特性2

如果過程塊內(nèi),有針對同一個變量的多個非阻塞賦值,那么這些非阻塞賦值會按順序執(zhí)行(但我認為不能簡單地說過程塊內(nèi)是“順序執(zhí)行的”,容易造成誤導(dǎo),應(yīng)該說具有一定的“順序性”特點)。

看下面的示例代碼:

module test
(
 input clk,
 output reg a, b
);

always @ (posedge clk) begin
 a <= 0;
 a <= 1;
end

endmodule

always塊內(nèi)有兩條對于變量a的賦值語句,但由于順序性特點,a的賦值結(jié)果應(yīng)該是1。利用這個特性,會經(jīng)常見到下面這種代碼寫法:

always @ (posedge clk) begin
 a <= 0;
 if (flag == 1)
 a <= 1;
end

只有當flag=1時,a才為1。

2. 過程連續(xù)賦值

這種賦值方式允許在過程塊中連續(xù)地驅(qū)動網(wǎng)絡(luò)或變量。但這種建模方法不可綜合,因此這里只簡單記錄一下兩種過程連續(xù)賦值方式的作用。

assign和deassign:assign連續(xù)賦值會優(yōu)先占用一個變量,讓其它對這個變量進行賦值的過程塊無效。deassign連續(xù)賦值會解除占用關(guān)系。

看下面的示例代碼:

`timescale 1ns / 1ps

module sim();

reg clk = 0, rst_n = 0, d = 1;
reg q;

//test i1
//(
//    .clk (clk),
//    .rst_n(rst),
//    .q(q),
//    .d(d)
//);

always @ (rst_n)
 if (!rst_n) assign q = 0;
 else deassign q;

always @ (posedge clk)
 q <= d;

always #5 clk <= ~clk;

initial begin
 #50 rst_n = 1;
end

endmodule

當rst_n=0時,asssign連續(xù)賦值占用了q,q的值恒為0;當rst_n=1時,deassign解除了占用,q的值由其它過程賦值決定,在clk上升沿隨d變化而變化。

force和release:功能和assign、deassign相同,只是賦值對象可以是變量也可以是網(wǎng)絡(luò)。force過程賦值的對象為網(wǎng)絡(luò)時,會使其它所有對該網(wǎng)絡(luò)的驅(qū)動無效。

3. case語句

case語句的default分支不是必須的,只要設(shè)計者清楚設(shè)計意圖即可。記錄一下case兩個比較少見但有時候特別有用的用法。

3.1 do-not-cares

包括兩種:
?  casez表示不關(guān)心高阻狀態(tài)(z);
?  casex表示不關(guān)心高祖狀態(tài)(z)和未知狀態(tài)(x)。

在不關(guān)心的bit位上使用“?”表示要更加方便。casex和casez完全是可以綜合的,例如下面的代碼可以實現(xiàn)優(yōu)先編碼器:

module test
(
 input clk,
 input [3:0] d,
 output reg [15:0] q
);

always @ (posedge clk)
 casez (d)
 4'b1??? : q <= 1;
 4'b01?? : q <= 2;
 4'b001? : q <= 3;
 4'b0001 : q <= 4;
 endcase

endmodule

如果想使用casex和casez,還是要從“設(shè)計上”能否綜合的角度考慮一下,并且做好綜合后的仿真。

比如上面的代碼,使用case語句+16條分支可以實現(xiàn)同樣的效果,這個設(shè)計完全是可以綜合實現(xiàn)的。使用casez更多還是起到簡化代碼設(shè)計的作用。

3.2 常數(shù)case

case語句中可以使用常數(shù)表達式,這個常數(shù)會和每個分支中的表達式進行比較。如下面的代碼:

module test
(
 input clk,
 input [3:0] d,
 output reg [15:0] q
);

always @ (posedge clk)
 casez (1)
 d[0] : q <= 1;
 d[1] : q <= 2;
 d[2] : q <= 3;
 d[3] : q <= 4;
 endcase

endmodule

可以實現(xiàn),根據(jù)d中的哪個bit為1,執(zhí)行相應(yīng)的代碼。如果d中多個bit同時為1,此時多條分支同時滿足,會執(zhí)行順序最前面的一條。總之還是要清楚設(shè)計意圖,做好仿真工作。

4. 循環(huán)語句

forever 和 repeat 是完全不可綜合的,只用于仿真文件的設(shè)計。

循環(huán)語句 for 和 while 并不是完全不能綜合的,但因為Verilog是對硬件進行建模,for和while的使用肯定不像在軟件編程語言中使用一樣靈活。還是上面的老話,要從“設(shè)計上”能否綜合的角度進行考慮。

如果循環(huán)語句的使用出現(xiàn)問題,綜合工具會給出提示,如Vivado的提示信息如下:

[Synth 8-3380] loop condition does not converge after 2000 iterations

用好循環(huán),可以簡化代碼設(shè)計。一個寄存器鏈的示例如下(使用for也能達到同樣的效果):

module test
(
 input clk, rst_n,
 input [15:0] d,
 output reg [15:0] q
);

integer i;
(* keep = "true" *)reg [15:0] mem [7:0];
always @ (posedge clk or negedge rst_n) begin
 if (!rst_n) begin
 i = 0;
 while(i < 8) begin
 mem[i] <= 0;
 i = i + 1'b1;
 end
 end
 else begin
 i = 0;
 mem[0] <= d;
 while(i < 7) begin
 mem[i+1] <= mem[i];
 i = i + 1'b1;
 end
 end
 q <= mem[7];
end

endmodule

相應(yīng)的RTL原理圖如下 :

按軟件編程思維考慮,循環(huán)語句是一條一條執(zhí)行的一個過程。而從上面的設(shè)計結(jié)果來說,顯然while循環(huán)中的所有語句是“同時”執(zhí)行的,代碼只是將很多具有重復(fù)性特點的賦值語句改用 while/for 的形式來編寫。

5. 過程塊

過程塊(procedure)包括四種:initial結(jié)構(gòu)、always結(jié)構(gòu)、任務(wù)(task)、函數(shù)(function)。這里只記錄兩個不太熟悉的特性。

5.1 零延遲無限循環(huán)

always塊在仿真文件中,都要與一些時序控制配合使用。如果always塊中沒有任何推動仿真時間的控制,仿真會卡在一個時間點。比如經(jīng)常用如下語句創(chuàng)建時鐘信號:

always #10 clk = ~clk;

如果寫成了如下的形式:

always clk = ~clk;

相當于形成了一個零延遲的無限循環(huán),仿真時間會卡在0s無法前進。如果運行這個代碼,輕則程序卡死,重則系統(tǒng)奔潰只能重啟。

5.2 initial用于初始化

initial塊是可以綜合的,只不過不能添加時序控制語句,因此作用有限,一般用于變量的初始化。如下面代碼:

reg [15:0] mem [7:0];
integer i;
initial begin
 for (i = 0; i < 8; i=i+1)
 mem[i] = i;
end

6. 過程塊時序控制

此特性主要用于仿真文件中,部分在硬件設(shè)計中也會涉及。Verilog有兩種明確的時序控制類型:延時控制和事件表達式。仿真時間正是靠過程塊中的延時控制、事件控制以及wait語句來推動的。

6.1 延時控制

用于控制語句的執(zhí)行時間,比如描述激勵的波形。延遲值可以是表達式,比如“#d rega = regb;”,這條賦值語句會在延遲d個時間單位后執(zhí)行。
(1)如果d的計算結(jié)果是高阻(z)或未知(x),則當作0處理;
(2)如果d的計算結(jié)果為負數(shù),也會將其視作無符號數(shù)來看待,如下面的代碼:

parameter [7:0] delay = -50;

initial begin
 rst_n = 0;
 #(-delay) rst_n = 1;
 #delay rst_n = 0;
end

rst_n先延遲50個時鐘后變?yōu)?;由于8bit -50的二進制補碼當作無符號數(shù)看時值為206,因此在延時206個時鐘后,rst_n值又變?yōu)?。

6.2 事件表達式

直到某些仿真事件發(fā)生時,語句才會只執(zhí)行。網(wǎng)絡(luò)或變量的值發(fā)生變化,稱作隱式事件(implict event);設(shè)計者設(shè)置一些命名事件,可能會由其它過程塊觸發(fā),稱作顯式事件(explicit event)。

值的變化、或變化的方向(上升沿posedge或下降沿negedge)都是隱式事件。雖然在硬件設(shè)計中經(jīng)常和always配合使用(比如 always @ (posedge clk) ),但在仿真文件中有更多靈活的使用方法??聪旅娴拇a示例:

// example1:clk上升沿,語句執(zhí)行
reg [7:0] delay = 0;
initial begin
 forever @(posedge clk) delay = delay + 1'b1;
end

// example2:clk的值發(fā)生變化,語句執(zhí)行
reg [7:0] delay = 0;
always begin
 @(clk) delay = delay + 1'b1;
end

如果 posedge 和 negedge 檢測的對象是一個表達式或多位寬的數(shù)據(jù),則只會檢測LSB上的邊沿變化。如下:

// example3
reg [2:0] cnt = 0;
always @ (posedge clk)
 cnt <= cnt + 1'b1;

reg [7:0] delay = 0;
always begin
 @(posedge cnt) delay = delay + 1'b1;
end

檢測3bit變量cnt的上升沿,相當于檢測cnt[0]的邊沿事件。

事件(event) 是除了變量和網(wǎng)絡(luò)外Verilog中的另一種數(shù)據(jù)類型,如果一個標識符被申明為事件類型,則稱作“命名事件”,需要顯示地觸發(fā)。雖然事件是一種數(shù)據(jù)類型,但它本身又沒有任何“數(shù)據(jù)”。如下面的示例:

event trig;        // 命名事件申明
reg [2:0] cnt = 0;
always @ (posedge clk) begin
 cnt <= cnt + 1'b1;
 if (cnt == 7) -> trig;    // 事件顯示觸發(fā)
end

reg [7:0] delay = 0;
always begin
 @(trig) delay = delay + 1'b1;   // 事件捕獲
end

使用命名事件可以有效的實現(xiàn)多個過程塊之間的通信和同步。

如果過程塊語句的執(zhí)行同時對多個事件敏感,可以使用事件的邏輯或特性。在事件敏感列表中使用 “or” 或 “,”(這兩個符號含義等價),如“always @ (posedge clka or posedge clkb, trig)”。

還有一個特性稱作隱式事件表達式,符號為“@*”,會把過程時序控制語句中所有讀取的變量和網(wǎng)絡(luò)添加到事件表達式中

6.3 wait語句

上面的事件控制方法都是邊沿敏感型的。還可以使用wait語句控制過程塊的時序,直到某項條件為true時才執(zhí)行相應(yīng)語句,這種方法稱作電平敏感型。

如果wait中的條件為false,則過程塊會一直阻塞,直到條件變?yōu)檎鏁r,才會執(zhí)行后面的語句。比如下面的代碼:

reg [7:0] cnta = 0, cntb = 0;
initial begin
 wait(en) #10 cnta <= 60;
 #10 cntb <= 70;
end

對于begin…end(順序塊) 而言,wait會阻止順序塊的執(zhí)行,直到en為1時,cnta和cntb的兩條賦值語句才會執(zhí)行。如果使用fork…join(并行塊),則上述代碼中的wait只會對cnta的賦值語句有效,此時最好也為wait語句加上塊聲明(begin…end或fork…join)。

6.4 賦值間(Intra-assignment)時序控制

賦值間延遲和事件控制是另一種時序控制方法,如

 a = #5 b;

與“ #5 a = b; ”不同,賦值語句右邊的表達式會馬上求值,延遲和事件只是控制這個值賦值給賦值語句左邊的時間。比如上面的代碼等效于:

begin
 temp = b;
 #5 a = temp;
end	

利用賦值間時序控制的特性,可以巧妙地完成一些行為建模。比如下面的代碼可以避免賦值語句間的“競爭”,達到數(shù)據(jù)交換的效果:

fork       // 并行塊,存在競爭
 #5 a = b;
 #5 b = a;
join

fork       // 數(shù)據(jù)交換
 a = #5 b;
 b = #5 a;
join

賦值間延遲之前會先求等式右邊的值,延遲后才會把這個值賦到左邊,因此上面代碼相當于交換了a和b的值。很多工具在實現(xiàn)Verilog的賦值間時序控制這個特性時,都會使用臨時存儲來存放右邊表達式的值。

也可以用事件控制:

a = @(posedge clk) b;

//等效于
begin
 temp = b;
 @(posedge clk) a = temp;
end

賦值間時序控制還有一個特點是可以用repeat來控制延遲或事件執(zhí)行的次數(shù),如:

a = repeat(3) @(posedge clk) b;

//等效于
begin
 temp = b;
 @(posedge clk);
 @(posedge clk);
 @(posedge clk); a = temp;
end

要注意如果采用變量的形式 “ repeat (num) ”:

?   若num是無符號數(shù):當num為負數(shù)時,相當于二進制補碼對應(yīng)的無符號數(shù)。比如num = -1,repeat(num) 相當于 repeat(7) 。

?   若num是帶符號數(shù):當num為0或負數(shù)時,這條語句將永遠不會被執(zhí)行。

7. 塊(block)

塊(block)是一些賦值語句的組合,包括:

?   順序塊begin-end:塊中語句按照給定的順序執(zhí)行,因此塊中的延遲、事件控制相當于起到了隔斷的作用。順序塊的開始時間是第一條語句開始執(zhí)行的時刻,結(jié)束時間是最后一條語句執(zhí)行完的時刻。

?   并行塊fork-join:塊中語句同時執(zhí)行,即所有語句的開始時間相同。并行塊的結(jié)束時間是所有語句都執(zhí)行完的時刻。

7.1 嵌套塊

通常要使用多個塊的嵌套實現(xiàn)更復(fù)雜的控制邏輯,因此最好要理解各個塊的開始時間和結(jié)束時間。下面給出幾個例子:

// Example1
begin
 fork
 @Aevent;
 @Bevent;
 join
 areg = breg;
end

由于fork-join的并行性,A和B兩個事件可以以任意的順序出現(xiàn),fork-join塊結(jié)束后執(zhí)行賦值語句 areg = breg。

// Example2
begin
 begin
 @Aevent;
 @Bevent;
 end
 areg = breg;
end

如果換成begin-end,事件的觸發(fā)必須按照給定的順序。如果B事件先出現(xiàn),再出現(xiàn)A,那么內(nèi)部嵌套的begin-end還要再等待B事件的發(fā)生。

// Example3
fork
 @Aevent;
 begin #ta wa = 0; #ta wa = 1; end
 
 @Bevent;
 begin #tb wb = 1; #tb wb = 0; end
join

fork-join中的兩個順序塊的執(zhí)行分別受到兩個事件的控制。由于fork-join的并行性,兩個begin-end的觸發(fā)和執(zhí)行同樣也是并行的。

7.2 命名塊

每個塊都可以在begin和fork后面為其附加名字,稱為命名塊。其它語句可以通過這個名字來引用命名塊,最常見的是“命名塊+disable”的用法。

disable語句可以終止命名塊的運行,一般用于處理異常情況,比如下面的代碼:

begin : block_name
 ...
 if (a == 0)
 disable block_name;
 ...
end

當滿足a == 0時,begin-end塊會終止運行。disable會終止整個命名塊的運行,包括命名塊中的其它所有塊和已調(diào)用的任務(wù)。利用這個特性可以實現(xiàn)兩個功能:
?   中止一個循環(huán)語句(相當于C語言中的break)
?   跳過循環(huán)中的某些狀態(tài)(相當于C語言中的continue)

雖然Verilog沒有直接提供類似于C語言中break和continue的關(guān)鍵詞,但可以使用“命名塊+disable”來實現(xiàn)此特性。看下面的示例代碼:

reg [7:0] cnt = 0;
always @ (posedge clk) cnt <= cnt + 1'b1;

reg [7:0] data;
integer i;
initial begin : break
 for (i = 0; i < 100; i = i + 1) begin : continue
 @(posedge clk)
 if (cnt == 5)
 disable break;
 data <= cnt;
 end
end

for循環(huán)中,當滿足一定條件時," disable break; "會終止initial之后的begin-end塊的執(zhí)行,整個循環(huán)也就終止了。

如果改成" disable continue; ",當滿足條件時,會終止for之后的begin-end塊的執(zhí)行,這樣只會終止當前的循環(huán)狀態(tài),而不會影響循環(huán)的下一次迭代。

本站聲明: 本文章由作者或相關(guān)機構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時聯(lián)系本站刪除。
關(guān)閉