【ZYNQ Ultrascale+ MPSOC FPGA教程】第三章 Verilog基礎(chǔ)模塊介紹
資料介紹
作者: ALINX
* 本原創(chuàng)教程由芯驛電子科技(上海)有限公司(ALINX)創(chuàng)作,版權(quán)歸本公司所有,如需轉(zhuǎn)載,需授權(quán)并注明出處。
適用于板卡型號:
AXU2CGA/AXU2CGB/AXU3EG/AXU4EV-E/AXU4EV-P/AXU5EV-E/AXU5EV-P /AXU9EG/AXU15EG
簡介
本文主要介紹verilog基礎(chǔ)模塊,夯實基礎(chǔ),對深入學習FPGA會有很大幫助。
數(shù)據(jù)類型
常量
整數(shù):整數(shù)可以用二進制b或B,八進制o或O,十進制d或D,十六進制h或H表示,例如, 8’b00001111表示8位位寬的二進制整數(shù),4’ha表示4位位寬的十六進制整數(shù)。
X和Z:X代表不定值,z代表高阻值,例如,5’b00x11,第三位不定值,3’b00z表示最低位為高阻值。
下劃線:在位數(shù)過長時可以用來分割位數(shù),提高程序可讀性,如8’b0000_1111
參數(shù)parameter: parameter可以用標識符定義常量,運用時只使用標識符即可,提高可讀性及維護性,如定義parameter width = 8 ; 定義寄存器reg [width-1:0] a; 即定義了8位寬度的寄存器。
參數(shù)的傳遞:在一個模塊中如果有定義參數(shù),在其他模塊調(diào)用此模塊時可以傳遞參數(shù),并可以修改參數(shù),如下所示,在module后用#()表示。
例如定義模塊如下調(diào)用模塊
module rom #( parameter depth =15, parameter width =8 ) ( input[depth-1:0] addr , input[width-1:0] data , output result ); endmodule module top(); wire[31:0] addr ; wire[15:0] data ; wire result ; rom #( .depth(32), .width(16) ) r1 ( .addr(addr), .data(data), .result(result) ); endmodule
Parameter可以用于模塊間的參數(shù)傳遞,而localparam僅用于本模塊內(nèi)使用,不能用于參數(shù)傳遞。Localparam多用于狀態(tài)機狀態(tài)的定義。
變量
變量是指程序運行時可以改變其值的量,下面主要介紹幾個常用了變量類型
1.Wire 型
Wire 類型變量,也叫網(wǎng)絡(luò)類型變量,用于結(jié)構(gòu)實體之間的物理連接,如門與門之間,不能儲存值,用連續(xù)賦值語句assign賦值,定義為wire [n-1:0] a ; 其中n代表位寬,如定義wire a ; assign a = b ; 是將b的結(jié)點連接到連線a上。如下圖所示,兩個實體之間的連線即是wire類型變量。
2.Reg 型
Reg 類型變量,也稱為寄存器變量,可用來儲存值,必須在always語句里使用。其定義為
reg [n-1:0] a ; 表示n位位寬的寄存器,如reg [7:0] a; 表示定義8位位寬的寄存器a。如下所示定義了寄存器q,生成的電路為時序邏輯,右圖為其結(jié)構(gòu),為D觸發(fā)器。
module top(d, clk, q); input d ; input clk ; outputreg q ; always@(posedge clk) begin q <= d ; end endmodule
也可以生成組合邏輯,如數(shù)據(jù)選擇器,敏感信號沒有時鐘,定義了reg Mux,最終生成電路為組合邏輯。
module top(a, b, c, d, sel, Mux); input a ; input b ; input c ; input d ; input[1:0] sel ; outputreg Mux ; always@(sel or a or b or c or d) begin case(sel) 2'b00: Mux = a ; 2'b01: Mux = b ; 2'b10: Mux = c ; 2'b11: Mux = d ; endcase end endmodule
3.Memory型
可以用memory類型來定義RAM,ROM等存儲器,其結(jié)構(gòu)為reg [n-1:0] 存儲器名[m-1:0],意義為m個n位寬度的寄存器。例如,reg [7:0] ram [255:0]表示定義了256個8位寄存器,256也即是存儲器的深度,8為數(shù)據(jù)寬度。
運算符
運算符可分為以下幾類:
1. 算術(shù)運算符(+,-,*,/,%)
2. 賦值運算符(=,
3. 關(guān)系運算符(>,=,
4. 邏輯運算符(&&,||,?。?br>
5. 條件運算符(?:)
6. 位運算符(~,|,^,&,^~)
7. 移位運算符(>)
8. 拼接運算符({ })
算術(shù)運算符
“+”(加法運算符),”-“(減法運算符),”*”(乘法運算符),”/”(除法運算符,如7/3 =2),“%”(取模運算符,也即求余數(shù),如7%3=1,余數(shù)為1)
賦值運算符
“=”阻塞賦值,”
代碼如下:激勵文件如下
module top(din,a,b,c,clk); input din; input clk; outputreg a,b,c; always@(posedge clk) begin a = din; b = a; c = b; end endmodule `timescale1 ns/1 ns module top_tb(); reg din ; reg clk ; wire a,b,c ; initial begin din =0; clk =0; forever begin #({$random}%100) din =~din ; end end always#10 clk =~clk ; top t0(.din(din),.a(a),.b(b),.c(c),.clk(clk)); endmodule
可以從仿真結(jié)果看到,在clk的上升沿,a的值等于din,并立即賦給b,b的值賦給c。
如果改為非阻塞賦值,仿真結(jié)果如下,在clk上升沿,a的值沒有立即賦值給b,b為a原來的值,同樣,c為b原來的值
可以從兩者的RTL圖看出明顯不同:
阻塞賦值RTL圖非阻塞賦值RTL圖
一般情況下,在時序邏輯電路中使用非阻塞賦值,可避免仿真時出現(xiàn)競爭冒險現(xiàn)象;在組合邏輯中使用阻塞賦值,執(zhí)行賦值語句后立即改變;在assign語句中必須用阻塞賦值。
用于表示兩個操作數(shù)之間的關(guān)系,如a>b,a If (a>=b) q else q
“&&”(兩個操作數(shù)邏輯與),”||”(兩個操作數(shù)邏輯或),”!”(單個操作數(shù)邏輯非)例如:
If (a>b && c “?:”為條件判斷,類似于if else,例如assign a = (i>8)?1’b1:1’b0 ;判斷i的值是否大于8,如果大于8則a的值為1,否則為0。 “~”按位取反,”|”按位或,”^”按位異或,”&”按位與,”^”按位同或,除了”~”只需要一個操作數(shù)外,其他幾個都需要兩個操作數(shù),如a&b,a|b。具體應用在后面的組合邏輯一節(jié)中有講解。 “>”右移位運算符,如a>2,向右移兩位。 “{ }”拼接運算符,將多個信號按位拼接,如{a[3:0], b[1:0]},將a的低4位,b的低2位拼接成6位數(shù)據(jù)。另外,{n{a[3:0]}}表示將n個a[3:0]拼接,{n{1’b0}}表示n位的0拼接。如{8{1’b0}}表示為8’b0000_0000. 各種運算符的優(yōu)先級別如下:
組合邏輯
本節(jié)主要介紹組合邏輯,組合邏輯電路的特點是任意時刻的輸出僅僅取決于輸入信號,輸入信號變化,輸出立即變化,不依賴于時鐘。
在verilog中以“&”表示按位與,如c=a&b,真值表如下,在a和b都等于1時結(jié)果才為1,RTL表示如右圖
代碼實現(xiàn)如下:激勵文件如下:
module top(a, b, c); input a ; input b ; output c ; assign c = a & b ; endmodule `timescale1 ns/1 ns module top_tb(); reg a ; reg b ; wire c ; initial begin a =0; b =0; forever begin #({$random}%100) a =~a ; #({$random}%100) b =~b ; end end top t0(.a(a),.b(b),.c(c)); endmodule
仿真結(jié)果如下:
如果a和b的位寬大于1,例如定義input [3:0] a, input [3:0]b,那么a&b則指a與b的對應位相與。如a[0]&b[0],a[1]&b[1]。
在verilog中以“|”表示按位或,如c = a|b , 真值表如下,在a和b都為0時結(jié)果才為0。
代碼實現(xiàn)如下:激勵文件如下
module top(a, b, c); input a ; input b ; output c ; assign c = a | b ; endmodule `timescale1 ns/1 ns module top_tb(); reg a ; reg b ; wire c ; initial begin a =0; b =0; forever begin #({$random}%100) a =~a ; #({$random}%100) b =~b ; end end top t0(.a(a),.b(b),.c(c)); endmodule
仿真結(jié)果如下:
同理,位寬大于1,則是按位或。
在verilog中以“~”表示按位取反,如b=~a,真值表如下,b等于a的相反數(shù)。
代碼實現(xiàn)如下:激勵文件如下:
module top(a, b); input a ; output b ; assign b =~a ; endmodule `timescale1 ns/1 ns module top_tb(); reg a ; wire b ; initial begin a =0; forever begin #({$random}%100) a =~a ; end end top t0(.a(a),.b(b)); endmodule
仿真結(jié)果如如下:
在verilog中以“^”表示異或,如c= a^b ,真值表如下,當a和b相同時,輸出為0。
代碼實現(xiàn)如下:激勵文件如下:
module top(a, b, c); input a ; input b ; output c ; assign c = a ^ b ; endmodule `timescale1 ns/1 ns module top_tb(); reg a ; reg b ; wire c ; initial begin a =0; b =0; forever begin #({$random}%100) a =~a ; #({$random}%100) b =~b ; end end top t0(.a(a),.b(b),.c(c)); endmodule
仿真結(jié)果如下:
在verilog中以大于“>”,等于”==”,小于”=”,小于等于” b ;表示如果a大于b,那么c的值就為1,否則為0。真值表如下:
代碼實現(xiàn)如下:激勵文件如下:
module top(a, b, c); input a ; input b ; output c ; assign c = a > b ; endmodule `timescale1 ns/1 ns module top_tb(); reg a ; reg b ; wire c ; initial begin a =0; b =0; forever begin #({$random}%100) a =~a ; #({$random}%100) b =~b ; end end top t0(.a(a),.b(b),.c(c)); endmodule
仿真結(jié)果如下:
半加器和全加器是算術(shù)運算電路中的基本單元,由于半加器不考慮從低位來的進位,所以稱之為半加器,sum表示相加結(jié)果,count表示進位,真值表可表示如下:
可根據(jù)真值表寫出代碼如下:激勵文件如下:
module top(a, b, sum, count); input a ; input b ; output sum ; output count ; assign sum = a ^ b ; assign count = a & b ; endmodule `timescale1 ns/1 ns module top_tb(); reg a ; reg b ; wire sum ; wire count ; initial begin a =0; b =0; forever begin #({$random}%100) a =~a ; #({$random}%100) b =~b ; end end top t0(.a(a),.b(b), .sum(sum),.count(count)); endmodule
仿真結(jié)果如下:
而全加器需要加上低位來的進位信號cin,真值表如下:
代碼如下:激勵文件如下:
module top(cin, a, b, sum, count); input cin ; input a ; input b ; output sum ; output count ; assign{count,sum}= a + b + cin ; endmodule `timescale1 ns/1 ns module top_tb(); reg a ; reg b ; reg cin ; wire sum ; wire count ; initial begin a =0; b =0; cin =0; forever begin #({$random}%100) a =~a ; #({$random}%100) b =~b ; #({$random}%100) cin =~cin ; end end top t0(.cin(cin),.a(a),.b(b), .sum(sum),.count(count)); endmodule
仿真結(jié)果如下:
乘法的表示也很簡單,利用”*”即可,如a*b,舉例代碼如下:
module top(a, b, c); input[1:0] a ; input[1:0] b ; output[3:0] c ; assign c = a * b ; endmodule `timescale1 ns/1 ns module top_tb(); reg[1:0]a ; reg[1:0]b ; wire[3:0]c ; initial begin a =0; b =0; forever begin #({$random}%100) a =~a ; #({$random}%100) b =~b ; end end top t0(.a(a),.b(b),.c(c)); endmodule
仿真結(jié)果如下:
在verilog中經(jīng)常會用到數(shù)據(jù)選擇器,通過選擇信號,選擇不同的輸入信號輸出到輸出端,如下圖真值表,四選一數(shù)據(jù)選擇器,sel[1:0]為選擇信號,a,b,c,d為輸入信號,Mux為輸出信號。
代碼如下:激勵文件如下:
module top(a, b, c, d, sel, Mux); input a ; input b ; input c ; input d ; input[1:0] sel ; outputreg Mux ; always@(sel or a or b or c or d) begin case(sel) 2'b00: Mux = a ; 2'b01: Mux = b ; 2'b10: Mux = c ; 2'b11: Mux = d ; endcase end endmodule `timescale1 ns/1 ns module top_tb(); reg a ; reg b ; reg c ; reg d ; reg[1:0] sel ; wire Mux ; initial begin a =0; b =0; c =0; d =0; forever begin #({$random}%100) a ={$random}%3; #({$random}%100) b ={$random}%3; #({$random}%100) c ={$random}%3; #({$random}%100) d ={$random}%3; end end initial begin sel =2'b00; #2000 sel =2'b01; #2000 sel =2'b10; #2000 sel =2'b11; end top t0(.a(a),.b(b),.c(c),.d(d),.sel(sel), .Mux(Mux)); endmodule
仿真結(jié)果如下
3-8譯碼器是一個很常用的器件,其真值表如下所示,根據(jù)A2,A1,A0的值,得出不同的結(jié)果。
代碼如下:激勵文件如下:
module top(addr, decoder); input[2:0] addr ; outputreg[7:0] decoder ; always@(addr) begin case(addr) 3'b000: decoder =8'b1111_1110; 3'b001: decoder =8'b1111_1101; 3'b010: decoder =8'b1111_1011; 3'b011: decoder =8'b1111_0111; 3'b100: decoder =8'b1110_1111; 3'b101: decoder =8'b1101_1111; 3'b110: decoder =8'b1011_1111; 3'b111: decoder =8'b0111_1111; endcase end endmodule `timescale1 ns/1 ns module top_tb(); reg[2:0] addr ; wire[7:0] decoder ; initial begin addr =3'b000; #2000 addr =3'b001; #2000 addr =3'b010; #2000 addr =3'b011; #2000 addr =3'b100; #2000 addr =3'b101; #2000 addr =3'b110; #2000 addr =3'b111; end top t0(.addr(addr),.decoder(decoder)); endmodule
仿真結(jié)果如下:
在FPGA使用中,經(jīng)常會用到雙向IO,需要用到三態(tài)門,如bio = en? din: 1’bz ;其中en為使能信號,用于打開關(guān)閉三態(tài)門,下面的RTL圖即是實現(xiàn)了雙向IO,可參考代碼。激勵文件實現(xiàn)兩個雙向IO的對接。
module top(en, din, dout, bio); input din ; input en ; output dout ; inout bio ; assign bio = en? din :1'bz; assign dout = bio ; endmodule `timescale1 ns/1 ns module top_tb(); reg en0 ; reg din0 ; wire dout0 ; reg en1 ; reg din1 ; wire dout1 ; wire bio ; initial begin din0 =0; din1 =0; forever begin #({$random}%100) din0 =~din0 ; #({$random}%100) din1 =~din1 ; end end initial begin en0 =0; en1 =1; #100000 en0 =1; en1 =0; end top t0(.en(en0),.din(din0),.dout(dout0),.bi o(bio)); top t1(.en(en1),.din(din1),.dout(dout1),.bi o(bio)); endmodule
激勵文件結(jié)構(gòu)如下圖
仿真結(jié)果如下,en0為0,en1為1時,1通道打開,雙向IO bio就等于1通道的din1,1通道向外發(fā)送數(shù)據(jù),0通道接收數(shù)據(jù),dout0等于bio;當en0為1,en1為0時,0通道打開,雙向IO bio就等于0通道的din0,0通道向外發(fā)送數(shù)據(jù),1通道接收數(shù)據(jù),dout1等于bio
時序邏輯
組合邏輯電路在邏輯功能上特點是任意時刻的輸出僅僅取決于當前時刻的輸入,與電路原來的狀態(tài)無關(guān)。而時序邏輯在邏輯功能上的特點是任意時刻的輸出不僅僅取決于當前的輸入信號,而且還取決于電路原來的狀態(tài)。下面以典型的時序邏輯分析。
D觸發(fā)器在時鐘的上升沿或下降沿存儲數(shù)據(jù),輸出與時鐘跳變之前輸入信號的狀態(tài)相同。
代碼如下激勵文件如下
module top(d, clk, q); input d ; input clk ; outputreg q ; always@(posedge clk) begin q <= d ; end endmodule `timescale1 ns/1 ns module top_tb(); reg d ; reg clk ; wire q ; initial begin d =0; clk =0; forever begin #({$random}%100) d =~d ; end end always#10 clk =~clk ; top t0(.d(d),.clk(clk),.q(q)); endmodule
RTL圖表示如下
仿真結(jié)果如下,可以看到在t0時刻時,d的值為0,則q的值也為0;在t1時刻d發(fā)生了變化,值為1,那么q相應也發(fā)生了變化,值變?yōu)?。可以看到在t0-t1之間的一個時鐘周期內(nèi),無論輸入信號d的值如何變化,q的值是保持不變的,也就是有存儲的功能,保存的值為在時鐘的跳變沿時d的值。
軟件是按照兩級D觸發(fā)器的模型進行時序分析的,具體可以分析在同一時刻兩個D觸發(fā)器輸出的數(shù)據(jù)有何不同,其RTL圖如下:
代碼如下:激勵文件如下:
module top(d, clk, q, q1); input d ; input clk ; outputreg q ; outputreg q1 ; always@(posedge clk) begin q <= d ; end always@(posedge clk) begin q1 <= q ; end endmodule `timescale1 ns/1 ns module top_tb(); reg d ; reg clk ; wire q ; wire q1 ; initial begin d =0; clk =0; forever begin #({$random}%100) d =~d ; end end always#10 clk =~clk ; top t0(.d(d),.clk(clk),.q(q),.q1(q1)); endmodule
仿真結(jié)果如下,可以看到t0時刻,d為0,q輸出為0,t1時刻,q隨著d的數(shù)據(jù)變化而變化,而此時鐘跳變之前q的值仍為0,那么q1的值仍為0,t2時刻,時鐘跳變前q的值為1,則q1的值相應為1,q1相對于q落后一個周期。
異步復位是指獨立于時鐘,一旦異步復位信號有效,就觸發(fā)復位操作。這個功能在寫代碼時會經(jīng)常用到,用于給信號復位,初始化。其RTL圖如下:
代碼如下,注意要把異步復位信號放在敏感列表里,如果是低電平復位,即為negedge,如果是高電平復位,則是posedge
module top(d, rst, clk, q); input d ; input rst ; input clk ; outputreg q ; always@(posedge clk ornegedge rst) begin if(rst ==1'b0) q <=0; else q <= d ; end endmodule `timescale1 ns/1 ns module top_tb(); reg d ; reg rst ; reg clk ; wire q ; initial begin d =0; clk =0; forever begin #({$random}%100) d =~d ; end end initial begin rst =0; #200 rst =1; end always#10 clk =~clk ; top t0(.d(d),.rst(rst),.clk(clk),.q(q)); endmodule
仿真結(jié)果如下,可以看到在復位信號之前,雖然輸入信號d數(shù)據(jù)有變化,但由于正處于復位狀態(tài),輸入信號q始終為0,在復位之后q的值就正常了。
前面講到異步復位獨立于時鐘操作,而同步清零則是同步于時鐘信號下操作的,當然也不僅限于同步清零,也可以是其他的同步操作,其RTL圖如下:
代碼如下,不同于異步復位,同步操作不能把信號放到敏感列表里
module top(d, rst, clr, clk, q); input d ; input rst ; input clr ; input clk ; outputreg q ; always@(posedge clk ornegedge rst) begin if(rst ==1'b0) q <=0; elseif(clr ==1'b1) q <=0; else q <= d ; end endmodule `timescale1 ns/1 ns module top_tb(); reg d ; reg rst ; reg clr ; reg clk ; wire q ; initial begin d =0; clk =0; forever begin #({$random}%100) d =~d ; end end initial begin rst =0; clr =0; #200 rst =1; #200 clr =1; #100 clr =0; end always#10 clk =~clk ; top t0(.d(d),.rst(rst),.clr(clr),.clk(clk), .q(q)); endmodule
仿真結(jié)果如下,可以看到clr信號拉高后,q沒有立即清零,而是在下個clk上升沿之后執(zhí)行清零操作,也就是clr同步于clk。
移位寄存器是指在每個時鐘脈沖來時,向左或向右移動一位,由于D觸發(fā)器的特性,數(shù)據(jù)輸出同步于時鐘邊沿,其結(jié)構(gòu)如下,每個時鐘來臨,每個D觸發(fā)器的輸出q等于前一個D觸發(fā)器輸出的值,從而實現(xiàn)移位的功能。
代碼實現(xiàn):
module top(d, rst, clk, q); input d ; input rst ; input clk ; outputreg[7:0] q ; always@(posedge clk ornegedge rst) begin if(rst ==1'b0) q <=0; else q <={q[6:0], d};//向左移位 //q <= {d, q[7:1]} ; //向右移位 end endmodule 激勵文件: `timescale1 ns/1 ns module top_tb(); reg d ; reg rst ; reg clk ; wire[7:0] q ; initial begin d =0; clk =0; forever begin #({$random}%100) d =~d ; end end initial begin rst =0; #200 rst =1; end always#10 clk =~clk ; top t0(.d(d),.rst(rst),.clk(clk),.q(q)); endmodule
仿真結(jié)果如下,可以看到復位之后,每個clk上升沿左移一位
單口RAM的寫地址與讀地址共用一個地址,代碼如下,其中reg [7:0] ram [63:0]意思是定義了64個8位寬度的數(shù)據(jù)。其中定義了addr_reg,可以保持住讀地址,延遲一周期之后將數(shù)據(jù)送出。
module top ( input[7:0] data, input[5:0] addr, input wr, input clk, output[7:0] q ); reg[7:0] ram[63:0];//declare ram reg[5:0] addr_reg;//addr register always@(posedge clk) begin if(wr)//write ram[addr]<= data; addr_reg <= addr; end assign q = ram[addr_reg];//read data endmodule `timescale1 ns/1 ns module top_tb(); reg[7:0] data ; reg[5:0] addr ; reg wr ; reg clk ; wire[7:0] q ; initial begin data =0; addr =0; wr =1; clk =0; end always#10 clk =~clk ; always@(posedge clk) begin data <= data +1'b1; addr <= addr +1'b1; end top t0(.data(data), .addr(addr), .clk(clk), .wr(wr), .q(q)); endmodule
仿真結(jié)果如下,可以看到q的輸出與寫入的數(shù)據(jù)一致
偽雙口RAM的讀寫地址是獨立的,可以隨機選擇寫或讀地址,同時進行讀寫操作。代碼如下,在激勵文件中定義了en信號,在其有效時發(fā)送讀地址。
module top ( input[7:0] data, input[5:0] write_addr, input[5:0] read_addr, input wr, input rd, input clk, outputreg[7:0] q ); reg[7:0] ram[63:0];//declare ram reg[5:0] addr_reg;//addr register always@(posedge clk) begin if(wr)//write ram[write_addr]<= data; if(rd)//read q <= ram[read_addr]; end endmodule `timescale1 ns/1 ns module top_tb(); reg[7:0] data ; reg[5:0] write_addr ; reg[5:0] read_addr ; reg wr ; reg clk ; reg rd ; wire[7:0] q ; initial begin data =0; write_addr =0; read_addr =0; wr =0; rd =0; clk =0; #100 wr =1; #20 rd =1; end always#10 clk =~clk ; always@(posedge clk) begin if(wr) begin data <= data +1'b1; write_addr <= write_addr +1'b1; if(rd) read_addr <= read_addr +1'b1; end end top t0(.data(data), .write_addr(write_addr), .read_addr(read_addr), .clk(clk), .wr(wr), .rd(rd), .q(q)); endmodule
仿真結(jié)果如下,可以看到在rd有效時,對讀地址進行操作,讀出數(shù)據(jù)
真雙口RAM有兩套控制線,數(shù)據(jù)線,允許兩個系統(tǒng)對其進行讀寫操作,代碼如下:
module top ( input[7:0] data_a, data_b, input[5:0] addr_a, addr_b, input wr_a, wr_b, input rd_a, rd_b, input clk, outputreg[7:0] q_a, q_b ); reg[7:0] ram[63:0];//declare ram //Port A always@(posedge clk) begin if(wr_a)//write begin ram[addr_a]<= data_a; q_a <= data_a ; end if(rd_a) //read q_a <= ram[addr_a]; end //Port B always@(posedge clk) begin if(wr_b)//write begin ram[addr_b]<= data_b; q_b <= data_b ; end if(rd_b) //read q_b <= ram[addr_b]; end endmodule `timescale1 ns/1 ns module top_tb(); reg[7:0] data_a, data_b ; reg[5:0] addr_a, addr_b ; reg wr_a, wr_b ; reg rd_a, rd_b ; reg clk ; wire[7:0] q_a, q_b ; initial begin data_a =0; data_b =0; addr_a =0; addr_b =0; wr_a =0; wr_b =0; rd_a =0; rd_b =0; clk =0; #100 wr_a =1; #100 rd_b =1; end always#10 clk =~clk ; always@(posedge clk) begin if(wr_a) begin data_a <= data_a +1'b1; addr_a <= addr_a +1'b1; end else begin data_a <=0; addr_a <=0; end end always@(posedge clk) begin if(rd_b) begin addr_b <= addr_b +1'b1; end else addr_b <=0; end top t0(.data_a(data_a),.data_b(data_b), .addr_a(addr_a),.addr_b(addr_b ), .wr_a(wr_a),.wr_b(wr_b), .rd_a(rd_a),.rd_b(rd_b), .clk(clk), .q_a(q_a),.q_b(q_b)); endmodule
仿真結(jié)果如下
ROM是用來存儲數(shù)據(jù)的,可以按照下列代碼形式初始化ROM,但這種方法處理大容量的ROM就比較麻煩,建議用FPGA自帶的ROM IP核實現(xiàn),并添加初始化文件。
代碼實現(xiàn)
moduletop ( input[3:0] addr, input clk, outputreg[7:0] q ); always@(posedge clk) begin case(addr) 4'd0: q <=8'd15; 4'd1: q <=8'd24; 4'd2: q <=8'd100; 4'd3: q <=8'd78; 4'd4: q <=8'd98; 4'd5: q <=8'd105; 4'd6: q <=8'd86; 4'd7: q <=8'd254; 4'd8: q <=8'd76; 4'd9: q <=8'd35; 4'd10: q <=8'd120; 4'd11: q <=8'd85; 4'd12: q <=8'd37; 4'd13: q <=8'd19; 4'd14: q <=8'd22; 4'd15: q <=8'd67; default: q <=8'd0; endcase end endmodule `timescale1 ns/1 ns module top_tb(); reg[3:0] addr ; reg clk ; wire[7:0] q ; initial begin addr =0; clk =0; end always#10 clk =~clk ; always@(posedge clk) begin addr <= addr +1'b1; end top t0(.addr(addr), .clk(clk), .q(q)); endmodule
仿真結(jié)果如下
在verilog里經(jīng)常會用到有限狀態(tài)機,處理相對復雜的邏輯,設(shè)定好不同的狀態(tài),根據(jù)觸發(fā)條件跳轉(zhuǎn)到對應的狀態(tài),在不同的狀態(tài)下做相應的處理。有限狀態(tài)機主要用到always及case語句。下面以一個四狀態(tài)的有限狀態(tài)機舉例說明。
在程序中設(shè)計了8位的移位寄存器,在Idle狀態(tài)下,判斷shift_start信號是否為高,如果為高,進入Start狀態(tài),在Start狀態(tài)延遲100個周期,進入Run狀態(tài),進行移位處理,如果shift_stop信號有效了,進入Stop狀態(tài),在Stop狀態(tài),清零q的值,再跳轉(zhuǎn)到Idle狀態(tài)。
Mealy有限狀態(tài)機,輸出不僅與當前狀態(tài)有關(guān),也與輸入信號有關(guān),在RTL中會與輸入信號有連接。
module top ( input shift_start, input shift_stop, input rst, input clk, input d, outputreg[7:0] q ); parameter Idle =2'd0;//Idle state parameter Start =2'd1;//Start state parameter Run =2'd2;//Run state parameter Stop =2'd3;//Stop state reg[1:0] state ;//statement reg[4:0] delay_cnt ;//delay counter always@(posedge clk ornegedge rst) begin if(!rst) begin state <= Idle ; delay_cnt <=0; q <=0; end else case(state) Idle :begin if(shift_start) state <= Start ; end Start :begin if(delay_cnt ==5'd99) begin delay_cnt <=0; state <= Run ; end else delay_cnt <= delay_cnt +1'b1; end Run :begin if(shift_stop) state <= Stop ; else q <={q[6:0], d}; end Stop :begin q <=0; state <= Idle ; end default: state <= Idle ; endcase end endmodule
Moore有限狀態(tài)機,輸出只與當前狀態(tài)有關(guān),與輸入信號無關(guān),輸入信號只影響狀態(tài)的改變,不影響輸出,比如對delay_cnt和q的處理,只與state狀態(tài)有關(guān)。
module top ( input shift_start, input shift_stop, input rst, input clk, input d, outputreg[7:0] q ); parameter Idle =2'd0;//Idle state parameter Start =2'd1;//Start state parameter Run =2'd2;//Run state parameter Stop =2'd3;//Stop state reg[1:0] current_state ;//statement reg[1:0] next_state ; reg[4:0] delay_cnt ;//delay counter //First part: statement transition always@(posedge clk ornegedge rst) begin if(!rst) current_state <= Idle ; else current_state <= next_state ; end //Second part: combination logic, judge statement transition condition always@(*) begin case(current_state) Idle :begin if(shift_start) next_state <= Start ; else next_state <= Idle ; end Start :begin if(delay_cnt ==5'd99) next_state <= Run ; else next_state <= Start ; end Run :begin if(shift_stop) next_state <= Stop ; else next_state <= Run ; end Stop : next_state <= Idle ; default:next_state <= Idle ; endcase end //Last part: output data always@(posedge clk ornegedge rst) begin if(!rst) delay_cnt <=0; elseif(current_state == Start) delay_cnt <= delay_cnt +1'b1; else delay_cnt <=0; end always@(posedge clk ornegedge rst) begin if(!rst) q <=0; elseif(current_state == Run) q <={q[6:0], d}; else q <=0; end endmodule
在上面兩個程序中用到了兩種方式的寫法,第一種的Mealy狀態(tài)機,采用了一段式的寫法,只用了一個always語句,所有的狀態(tài)轉(zhuǎn)移,判斷狀態(tài)轉(zhuǎn)移條件,數(shù)據(jù)輸出都在一個always語句里,缺點是如果狀態(tài)太多,會使整段程序顯的冗長。第二個Moore狀態(tài)機,采用了三段式的寫法,狀態(tài)轉(zhuǎn)移用了一個always語句,判斷狀態(tài)轉(zhuǎn)移條件是組合邏輯,采用了一個always語句,數(shù)據(jù)輸出也是單獨的 always語句,這樣寫起來比較直觀清晰,狀態(tài)很多時也不會顯得繁瑣。
Mealy有限狀態(tài)機RTL圖
Moore有限狀態(tài)機RTL圖
激勵文件如下:
`timescale1 ns/1 ns module top_tb(); reg shift_start ; reg shift_stop ; reg rst ; reg clk ; reg d ; wire[7:0] q ; initial begin rst =0; clk =0; d =0; #200 rst =1; forever begin #({$random}%100) d =~d ; end end initial begin shift_start =0; shift_stop =0; #300 shift_start =1; #1000 shift_start =0; shift_stop =1; #50 shift_stop =0; end always#10 clk =~clk ; top t0 ( .shift_start(shift_start), .shift_stop(shift_stop), .rst(rst), .clk(clk), .d(d), .q(q) ); endmodule
仿真結(jié)果如下:
總結(jié)
本文檔介紹了組合邏輯以及時序邏輯中常用的模塊,其中有限狀態(tài)機較為復雜,但經(jīng)常用到,希望大家能夠深入理解,在代碼中多運用,多思考,有利于快速提升水平。
- 采用Zynq UltraScale+ MPSoC滿足汽車ESD和SEED要求
- Zynq UltraScale+ MPSoC數(shù)據(jù)手冊:DC和AC開關(guān)特性
- Zynq UltraScale+ MPSoC驗證數(shù)據(jù)手冊
- Zynq UltraScale+ MPSoC中的隔離方法
- Zynq UltraScale+ MPSoC的隔離設(shè)計示例
- 適用于Xilinx Zynq UltraScale+ MPSoC應用的電源參考設(shè)計
- 米爾電子zynq ultrascale+ mpsoc底板外設(shè)資源清單分享
- 第三章 555集成電路應用設(shè)計 0次下載
- 計算機網(wǎng)絡(luò)第三章數(shù)據(jù)鏈路層資源下載 0次下載
- 如何調(diào)試 Zynq UltraScale+ MPSoC VCU DDR 控制器?
- 【ZYNQ Ultrascale+ MPSOC FPGA教程】第一章 MPSoC芯片介紹
- 電力系統(tǒng)繼電保護第三章-電網(wǎng)的距離保護(課件).ppt 0次下載
- 數(shù)字信號處理 第三章 5次下載
- 現(xiàn)代電路理論第三章 0次下載
- 信號與系統(tǒng)第三章課件PPT 0次下載
- 針對UltraScale/UltraScale+芯片DFX應考慮的因素有哪些(1) 537次閱讀
- Xilinx ZYNQ UltraScale+系列產(chǎn)品介紹 2405次閱讀
- FPGAs,Zynq和Zynq MPSoC器件的特點 2275次閱讀
- 基于Xilinx Zynq UltraScale+ RFSoC ZCU216評估套件詳細內(nèi)容介紹 9162次閱讀
- 基于Xilinx Zynq ultraScale+ 系列FPGA的AXU2CGB 開發(fā)板評測 9051次閱讀
- 米爾科技Zynq UltraScale+ MPSoC技術(shù)參考手冊介紹 2975次閱讀
- 詳解Xilinx公司Zynq? UltraScale+?MPSoC產(chǎn)品 3184次閱讀
- Xilinx Zynq UltraScale MPSoC可擴展電源設(shè)計 1950次閱讀
- Xilinx Kintex UltraScale+ FPGA KCU116評估套件主要性能和優(yōu)勢 6734次閱讀
- Zynq UltraScale+ MPSoC ZCU102評估套件解決方案 8640次閱讀
- Enea OSE可實現(xiàn)對Xilinx UltraScale+ MPSoC的支持 2790次閱讀
- Xilinx基于ARM的Zynq-7000和Zynq UltraScale+ MPSoC及RFSoC器件是否存在安全漏洞 2620次閱讀
- Zynq UltraScale+ MPSoC 上的多個Linux UIO設(shè)計 3219次閱讀
- 用ZYNQ MPSoC玩DOOM! 2539次閱讀
- Ti推出面向Zynq UltraScale+ MPSoC的電源參考設(shè)計 3588次閱讀
下載排行
本周
- 1電子電路原理第七版PDF電子教材免費下載
- 0.00 MB | 1491次下載 | 免費
- 2單片機典型實例介紹
- 18.19 MB | 95次下載 | 1 積分
- 3S7-200PLC編程實例詳細資料
- 1.17 MB | 27次下載 | 1 積分
- 4筆記本電腦主板的元件識別和講解說明
- 4.28 MB | 18次下載 | 4 積分
- 5開關(guān)電源原理及各功能電路詳解
- 0.38 MB | 11次下載 | 免費
- 6100W短波放大電路圖
- 0.05 MB | 4次下載 | 3 積分
- 7基于單片機和 SG3525的程控開關(guān)電源設(shè)計
- 0.23 MB | 4次下載 | 免費
- 8基于AT89C2051/4051單片機編程器的實驗
- 0.11 MB | 4次下載 | 免費
本月
- 1OrCAD10.5下載OrCAD10.5中文版軟件
- 0.00 MB | 234313次下載 | 免費
- 2PADS 9.0 2009最新版 -下載
- 0.00 MB | 66304次下載 | 免費
- 3protel99下載protel99軟件下載(中文版)
- 0.00 MB | 51209次下載 | 免費
- 4LabView 8.0 專業(yè)版下載 (3CD完整版)
- 0.00 MB | 51043次下載 | 免費
- 5555集成電路應用800例(新編版)
- 0.00 MB | 33562次下載 | 免費
- 6接口電路圖大全
- 未知 | 30320次下載 | 免費
- 7Multisim 10下載Multisim 10 中文版
- 0.00 MB | 28588次下載 | 免費
- 8開關(guān)電源設(shè)計實例指南
- 未知 | 21539次下載 | 免費
總榜
- 1matlab軟件下載入口
- 未知 | 935053次下載 | 免費
- 2protel99se軟件下載(可英文版轉(zhuǎn)中文版)
- 78.1 MB | 537793次下載 | 免費
- 3MATLAB 7.1 下載 (含軟件介紹)
- 未知 | 420026次下載 | 免費
- 4OrCAD10.5下載OrCAD10.5中文版軟件
- 0.00 MB | 234313次下載 | 免費
- 5Altium DXP2002下載入口
- 未知 | 233046次下載 | 免費
- 6電路仿真軟件multisim 10.0免費下載
- 340992 | 191183次下載 | 免費
- 7十天學會AVR單片機與C語言視頻教程 下載
- 158M | 183277次下載 | 免費
- 8proe5.0野火版下載(中文版免費下載)
- 未知 | 138039次下載 | 免費
評論
查看更多