使用邏輯門和連續(xù)賦值對(duì)電路建模,是相對(duì)詳細(xì)的描述硬件的方法。使用過程塊可以從更高層次的角度描述一個(gè)系統(tǒng),稱作行為級(jí)建模(behavirol modeling)。
1. 過程賦值
阻塞賦值和非阻塞賦值的區(qū)別都很熟悉了。這里記錄兩個(gè)特性。
1.1 特性1
絕大多數(shù)情況下,非阻塞賦值都是一個(gè)時(shí)間點(diǎn)處最后執(zhí)行的賦值語句??聪旅娴氖纠a:
module test
(
input clk,
output reg a, b
);
always @ (posedge clk) begin
a = 0;
b = 1;
a <= b;
b <= a;
end
endmodule
非阻塞賦值可以認(rèn)為包括兩步:
(1)求值和調(diào)度(evaluate and schedule),先得到非阻塞賦值等式右側(cè)的值,并將這次賦值安排在當(dāng)前時(shí)間點(diǎn)的結(jié)束時(shí)刻。
(2)當(dāng)前時(shí)間點(diǎn)結(jié)束時(shí),更新左側(cè)的值。因此這段代碼的結(jié)果是 a = 1,b = 0。
1.2 特性2
如果過程塊內(nèi),有針對(duì)同一個(gè)變量的多個(gè)非阻塞賦值,那么這些非阻塞賦值會(huì)按順序執(zhí)行(但我認(rèn)為不能簡(jiǎn)單地說過程塊內(nèi)是“順序執(zhí)行的”,容易造成誤導(dǎo),應(yīng)該說具有一定的“順序性”特點(diǎn))。
看下面的示例代碼:
module test
(
input clk,
output reg a, b
);
always @ (posedge clk) begin
a <= 0;
a <= 1;
end
endmodule
always塊內(nèi)有兩條對(duì)于變量a的賦值語句,但由于順序性特點(diǎn),a的賦值結(jié)果應(yīng)該是1。利用這個(gè)特性,會(huì)經(jīng)常見到下面這種代碼寫法:
always @ (posedge clk) begin
a <= 0;
if (flag == 1)
a <= 1;
end
只有當(dāng)flag=1時(shí),a才為1。
2. 過程連續(xù)賦值
這種賦值方式允許在過程塊中連續(xù)地驅(qū)動(dòng)網(wǎng)絡(luò)或變量。但這種建模方法不可綜合,因此這里只簡(jiǎn)單記錄一下兩種過程連續(xù)賦值方式的作用。
assign和deassign:assign連續(xù)賦值會(huì)優(yōu)先占用一個(gè)變量,讓其它對(duì)這個(gè)變量進(jìn)行賦值的過程塊無效。deassign連續(xù)賦值會(huì)解除占用關(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
當(dāng)rst_n=0時(shí),asssign連續(xù)賦值占用了q,q的值恒為0;當(dāng)rst_n=1時(shí),deassign解除了占用,q的值由其它過程賦值決定,在clk上升沿隨d變化而變化。
force和release:功能和assign、deassign相同,只是賦值對(duì)象可以是變量也可以是網(wǎng)絡(luò)。force過程賦值的對(duì)象為網(wǎng)絡(luò)時(shí),會(huì)使其它所有對(duì)該網(wǎng)絡(luò)的驅(qū)動(dòng)無效。
3. case語句
case語句的default分支不是必須的,只要設(shè)計(jì)者清楚設(shè)計(jì)意圖即可。記錄一下case兩個(gè)比較少見但有時(shí)候特別有用的用法。
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完全是可以綜合的,例如下面的代碼可以實(shí)現(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è)計(jì)上”能否綜合的角度考慮一下,并且做好綜合后的仿真。
比如上面的代碼,使用case語句+16條分支可以實(shí)現(xiàn)同樣的效果,這個(gè)設(shè)計(jì)完全是可以綜合實(shí)現(xiàn)的。使用casez更多還是起到簡(jiǎn)化代碼設(shè)計(jì)的作用。
3.2 常數(shù)case
case語句中可以使用常數(shù)表達(dá)式,這個(gè)常數(shù)會(huì)和每個(gè)分支中的表達(dá)式進(jìn)行比較。如下面的代碼:
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
可以實(shí)現(xiàn),根據(jù)d中的哪個(gè)bit為1,執(zhí)行相應(yīng)的代碼。如果d中多個(gè)bit同時(shí)為1,此時(shí)多條分支同時(shí)滿足,會(huì)執(zhí)行順序最前面的一條??傊€是要清楚設(shè)計(jì)意圖,做好仿真工作。
4. 循環(huán)語句
forever 和 repeat 是完全不可綜合的,只用于仿真文件的設(shè)計(jì)。
循環(huán)語句 for 和 while 并不是完全不能綜合的,但因?yàn)?span style="text-decoration:underline;">Verilog是對(duì)硬件進(jìn)行建模,for和while的使用肯定不像在軟件編程語言中使用一樣靈活。還是上面的老話,要從“設(shè)計(jì)上”能否綜合的角度進(jìn)行考慮。
如果循環(huán)語句的使用出現(xiàn)問題,綜合工具會(huì)給出提示,如Vivado的提示信息如下:
[Synth 8-3380] loop condition does not converge after 2000 iterations
用好循環(huán),可以簡(jiǎn)化代碼設(shè)計(jì)。一個(gè)寄存器鏈的示例如下(使用for也能達(dá)到同樣的效果):
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í)行的一個(gè)過程。而從上面的設(shè)計(jì)結(jié)果來說,顯然while循環(huán)中的所有語句是“同時(shí)”執(zhí)行的,代碼只是將很多具有重復(fù)性特點(diǎn)的賦值語句改用 while/for 的形式來編寫。
5. 過程塊
過程塊(procedure)包括四種:initial結(jié)構(gòu)、always結(jié)構(gòu)、任務(wù)(task)、函數(shù)(function)。這里只記錄兩個(gè)不太熟悉的特性。
5.1 零延遲無限循環(huán)
always塊在仿真文件中,都要與一些時(shí)序控制配合使用。如果always塊中沒有任何推動(dòng)仿真時(shí)間的控制,仿真會(huì)卡在一個(gè)時(shí)間點(diǎn)。比如經(jīng)常用如下語句創(chuàng)建時(shí)鐘信號(hào):
always #10 clk = ~clk;
如果寫成了如下的形式:
always clk = ~clk;
相當(dāng)于形成了一個(gè)零延遲的無限循環(huán),仿真時(shí)間會(huì)卡在0s無法前進(jìn)。如果運(yùn)行這個(gè)代碼,輕則程序卡死,重則系統(tǒng)奔潰只能重啟。
5.2 initial用于初始化
initial塊是可以綜合的,只不過不能添加時(shí)序控制語句,因此作用有限,一般用于變量的初始化。如下面代碼:
reg [15:0] mem [7:0];
integer i;
initial begin
for (i = 0; i < 8; i=i+1)
mem[i] = i;
end
6. 過程塊時(shí)序控制
此特性主要用于仿真文件中,部分在硬件設(shè)計(jì)中也會(huì)涉及。Verilog有兩種明確的時(shí)序控制類型:延時(shí)控制和事件表達(dá)式。仿真時(shí)間正是靠過程塊中的延時(shí)控制、事件控制以及wait語句來推動(dòng)的。
6.1 延時(shí)控制
用于控制語句的執(zhí)行時(shí)間,比如描述激勵(lì)的波形。延遲值可以是表達(dá)式,比如“#d rega = regb;”,這條賦值語句會(huì)在延遲d個(gè)時(shí)間單位后執(zhí)行。
(1)如果d的計(jì)算結(jié)果是高阻(z)或未知(x),則當(dāng)作0處理;
(2)如果d的計(jì)算結(jié)果為負(fù)數(shù),也會(huì)將其視作無符號(hào)數(shù)來看待,如下面的代碼:
parameter [7:0] delay = -50;
initial begin
rst_n = 0;
#(-delay) rst_n = 1;
#delay rst_n = 0;
end
rst_n先延遲50個(gè)時(shí)鐘后變?yōu)?;由于8bit -50的二進(jìn)制補(bǔ)碼當(dāng)作無符號(hào)數(shù)看時(shí)值為206,因此在延時(shí)206個(gè)時(shí)鐘后,rst_n值又變?yōu)?。
6.2 事件表達(dá)式
直到某些仿真事件發(fā)生時(shí),語句才會(huì)只執(zhí)行。網(wǎng)絡(luò)或變量的值發(fā)生變化,稱作隱式事件(implict event);設(shè)計(jì)者設(shè)置一些命名事件,可能會(huì)由其它過程塊觸發(fā),稱作顯式事件(explicit event)。
值的變化、或變化的方向(上升沿posedge或下降沿negedge)都是隱式事件。雖然在硬件設(shè)計(jì)中經(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檢測(cè)的對(duì)象是一個(gè)表達(dá)式或多位寬的數(shù)據(jù),則只會(huì)檢測(cè)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
檢測(cè)3bit變量cnt的上升沿,相當(dāng)于檢測(cè)cnt[0]的邊沿事件。
事件(event) 是除了變量和網(wǎng)絡(luò)外Verilog中的另一種數(shù)據(jù)類型,如果一個(gè)標(biāo)識(shí)符被申明為事件類型,則稱作“命名事件”,需要顯示地觸發(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
使用命名事件可以有效的實(shí)現(xiàn)多個(gè)過程塊之間的通信和同步。
如果過程塊語句的執(zhí)行同時(shí)對(duì)多個(gè)事件敏感,可以使用事件的邏輯或特性。在事件敏感列表中使用 “or” 或 “,”(這兩個(gè)符號(hào)含義等價(jià)),如“always @ (posedge clka or posedge clkb, trig)”。
還有一個(gè)特性稱作隱式事件表達(dá)式,符號(hào)為“@*”,會(huì)把過程時(shí)序控制語句中所有讀取的變量和網(wǎng)絡(luò)添加到事件表達(dá)式中
6.3 wait語句
上面的事件控制方法都是邊沿敏感型的。還可以使用wait語句控制過程塊的時(shí)序,直到某項(xiàng)條件為true時(shí)才執(zhí)行相應(yīng)語句,這種方法稱作電平敏感型。
如果wait中的條件為false,則過程塊會(huì)一直阻塞,直到條件變?yōu)檎鏁r(shí),才會(huì)執(zhí)行后面的語句。比如下面的代碼:
reg [7:0] cnta = 0, cntb = 0;
initial begin
wait(en) #10 cnta <= 60;
#10 cntb <= 70;
end
對(duì)于begin…end(順序塊) 而言,wait會(huì)阻止順序塊的執(zhí)行,直到en為1時(shí),cnta和cntb的兩條賦值語句才會(huì)執(zhí)行。如果使用fork…join(并行塊),則上述代碼中的wait只會(huì)對(duì)cnta的賦值語句有效,此時(shí)最好也為wait語句加上塊聲明(begin…end或fork…join)。
6.4 賦值間(Intra-assignment)時(shí)序控制
賦值間延遲和事件控制是另一種時(shí)序控制方法,如
a = #5 b;
與“ #5 a = b; ”不同,賦值語句右邊的表達(dá)式會(huì)馬上求值,延遲和事件只是控制這個(gè)值賦值給賦值語句左邊的時(shí)間。比如上面的代碼等效于:
begin
temp = b;
#5 a = temp;
end
利用賦值間時(shí)序控制的特性,可以巧妙地完成一些行為建模。比如下面的代碼可以避免賦值語句間的“競(jìng)爭(zhēng)”,達(dá)到數(shù)據(jù)交換的效果:
fork // 并行塊,存在競(jìng)爭(zhēng)
#5 a = b;
#5 b = a;
join
fork // 數(shù)據(jù)交換
a = #5 b;
b = #5 a;
join
賦值間延遲之前會(huì)先求等式右邊的值,延遲后才會(huì)把這個(gè)值賦到左邊,因此上面代碼相當(dāng)于交換了a和b的值。很多工具在實(shí)現(xiàn)Verilog的賦值間時(shí)序控制這個(gè)特性時(shí),都會(huì)使用臨時(shí)存儲(chǔ)來存放右邊表達(dá)式的值。
也可以用事件控制:
a = @(posedge clk) b;
//等效于
begin
temp = b;
@(posedge clk) a = temp;
end
賦值間時(shí)序控制還有一個(gè)特點(diǎn)是可以用repeat來控制延遲或事件執(zhí)行的次數(shù),如:
a = repeat(3) @(posedge clk) b;
//等效于
begin
temp = b;
@(posedge clk);
@(posedge clk);
@(posedge clk); a = temp;
end
要注意如果采用變量的形式 “ repeat (num) ”:
? 若num是無符號(hào)數(shù):當(dāng)num為負(fù)數(shù)時(shí),相當(dāng)于二進(jìn)制補(bǔ)碼對(duì)應(yīng)的無符號(hào)數(shù)。比如num = -1,repeat(num) 相當(dāng)于 repeat(7) 。
? 若num是帶符號(hào)數(shù):當(dāng)num為0或負(fù)數(shù)時(shí),這條語句將永遠(yuǎn)不會(huì)被執(zhí)行。
7. 塊(block)
塊(block)是一些賦值語句的組合,包括:
? 順序塊begin-end:塊中語句按照給定的順序執(zhí)行,因此塊中的延遲、事件控制相當(dāng)于起到了隔斷的作用。順序塊的開始時(shí)間是第一條語句開始執(zhí)行的時(shí)刻,結(jié)束時(shí)間是最后一條語句執(zhí)行完的時(shí)刻。
? 并行塊fork-join:塊中語句同時(shí)執(zhí)行,即所有語句的開始時(shí)間相同。并行塊的結(jié)束時(shí)間是所有語句都執(zhí)行完的時(shí)刻。
7.1 嵌套塊
通常要使用多個(gè)塊的嵌套實(shí)現(xiàn)更復(fù)雜的控制邏輯,因此最好要理解各個(gè)塊的開始時(shí)間和結(jié)束時(shí)間。下面給出幾個(gè)例子:
// Example1
begin
fork
@Aevent;
@Bevent;
join
areg = breg;
end
由于fork-join的并行性,A和B兩個(gè)事件可以以任意的順序出現(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中的兩個(gè)順序塊的執(zhí)行分別受到兩個(gè)事件的控制。由于fork-join的并行性,兩個(gè)begin-end的觸發(fā)和執(zhí)行同樣也是并行的。
7.2 命名塊
每個(gè)塊都可以在begin和fork后面為其附加名字,稱為命名塊。其它語句可以通過這個(gè)名字來引用命名塊,最常見的是“命名塊+disable”的用法。
disable語句可以終止命名塊的運(yùn)行,一般用于處理異常情況,比如下面的代碼:
begin : block_name
...
if (a == 0)
disable block_name;
...
end
當(dāng)滿足a == 0時(shí),begin-end塊會(huì)終止運(yùn)行。disable會(huì)終止整個(gè)命名塊的運(yùn)行,包括命名塊中的其它所有塊和已調(diào)用的任務(wù)。利用這個(gè)特性可以實(shí)現(xiàn)兩個(gè)功能:
? 中止一個(gè)循環(huán)語句(相當(dāng)于C語言中的break)
? 跳過循環(huán)中的某些狀態(tài)(相當(dāng)于C語言中的continue)
雖然Verilog沒有直接提供類似于C語言中break和continue的關(guān)鍵詞,但可以使用“命名塊+disable”來實(shí)現(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)中,當(dāng)滿足一定條件時(shí)," disable break; "會(huì)終止initial之后的begin-end塊的執(zhí)行,整個(gè)循環(huán)也就終止了。
如果改成" disable continue; ",當(dāng)滿足條件時(shí),會(huì)終止for之后的begin-end塊的執(zhí)行,這樣只會(huì)終止當(dāng)前的循環(huán)狀態(tài),而不會(huì)影響循環(huán)的下一次迭代。
![](https://file1.elecfans.com//web2/M00/99/D5/wKgaomTneJCAO1ZAAABiq3a-ogY629.jpg)
![](https://file1.elecfans.com//web2/M00/99/D5/wKgaomTneJCAUB5NAAACXWrmhKE766.png)
歡迎加入至芯科技FPGA微信學(xué)習(xí)交流群,這里有一群優(yōu)秀的FPGA工程師、學(xué)生、老師、這里FPGA技術(shù)交流學(xué)習(xí)氛圍濃厚、相互分享、相互幫助、叫上小伙伴一起加入吧!
點(diǎn)個(gè)在看你最好看
原文標(biāo)題:FPGA學(xué)習(xí)-使用邏輯門和連續(xù)賦值對(duì)電路建模
文章出處:【微信公眾號(hào):FPGA設(shè)計(jì)論壇】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
-
FPGA
+關(guān)注
關(guān)注
1630文章
21803瀏覽量
606450
原文標(biāo)題:FPGA學(xué)習(xí)-使用邏輯門和連續(xù)賦值對(duì)電路建模
文章出處:【微信號(hào):gh_9d70b445f494,微信公眾號(hào):FPGA設(shè)計(jì)論壇】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
TTL邏輯門的種類及應(yīng)用
FPGA門數(shù)的計(jì)算方法
![<b class='flag-5'>FPGA</b><b class='flag-5'>門</b>數(shù)的計(jì)算方法](https://file1.elecfans.com/web2/M00/0B/43/wKgZomcxY1uAHZIYAAAdPuPfZbQ026.png)
常用邏輯門芯片有哪些
FPGA算法工程師、邏輯工程師、原型驗(yàn)證工程師有什么區(qū)別?
數(shù)字邏輯怎么把邏輯圖畫成電路圖
常用的組合邏輯電路有哪些
數(shù)字系統(tǒng)的核心:邏輯門電路
![數(shù)字系統(tǒng)的核心:<b class='flag-5'>邏輯</b>門<b class='flag-5'>電路</b>](https://file1.elecfans.com/web2/M00/FC/B7/wKgZomaV09qAU3E8AAA_MdgxAtI622.png)
評(píng)論