電子發(fā)燒友App

硬聲App

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示
創(chuàng)作
電子發(fā)燒友網(wǎng)>電子資料下載>可編程邏輯>【ZYNQ Ultrascale+ MPSOC FPGA教程】第三章 Verilog基礎(chǔ)模塊介紹

【ZYNQ Ultrascale+ MPSOC FPGA教程】第三章 Verilog基礎(chǔ)模塊介紹

2021-01-22 | pdf | 589.52KB | 次下載 | 2積分

資料介紹

作者: 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語句中必須用阻塞賦值。

  • 關(guān)系運算符
  • 用于表示兩個操作數(shù)之間的關(guān)系,如a>b,a If (a>=b) q else q

  • 邏輯運算符
  • “&&”(兩個操作數(shù)邏輯與),”||”(兩個操作數(shù)邏輯或),”!”(單個操作數(shù)邏輯非)例如:

    If (a>b && c 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)先級別
  • 各種運算符的優(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é)果如下:

  • 數(shù)據(jù)選擇器
  • 在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譯碼器
  • 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é)果如下:

  • 三態(tài)門
  • 在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ā)器
  • 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ā)器的模型進行時序分析的,具體可以分析在同一時刻兩個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落后一個周期。

  • 帶異步復位的D觸發(fā)器
  • 異步復位是指獨立于時鐘,一旦異步復位信號有效,就觸發(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的值就正常了。

  • 帶異步復位同步清零的D觸發(fā)器
  • 前面講到異步復位獨立于時鐘操作,而同步清零則是同步于時鐘信號下操作的,當然也不僅限于同步清零,也可以是其他的同步操作,其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
  • 單口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
  • 偽雙口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
  • 真雙口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
  • 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é)果如下

  • 有限狀態(tài)機
  • 在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)常用到,希望大家能夠深入理解,在代碼中多運用,多思考,有利于快速提升水平。

    下載該資料的人也在下載 下載該資料的人還在閱讀
    更多 >

    評論

    查看更多

    下載排行

    本周

    1. 1電子電路原理第七版PDF電子教材免費下載
    2. 0.00 MB  |  1491次下載  |  免費
    3. 2單片機典型實例介紹
    4. 18.19 MB  |  95次下載  |  1 積分
    5. 3S7-200PLC編程實例詳細資料
    6. 1.17 MB  |  27次下載  |  1 積分
    7. 4筆記本電腦主板的元件識別和講解說明
    8. 4.28 MB  |  18次下載  |  4 積分
    9. 5開關(guān)電源原理及各功能電路詳解
    10. 0.38 MB  |  11次下載  |  免費
    11. 6100W短波放大電路圖
    12. 0.05 MB  |  4次下載  |  3 積分
    13. 7基于單片機和 SG3525的程控開關(guān)電源設(shè)計
    14. 0.23 MB  |  4次下載  |  免費
    15. 8基于AT89C2051/4051單片機編程器的實驗
    16. 0.11 MB  |  4次下載  |  免費

    本月

    1. 1OrCAD10.5下載OrCAD10.5中文版軟件
    2. 0.00 MB  |  234313次下載  |  免費
    3. 2PADS 9.0 2009最新版 -下載
    4. 0.00 MB  |  66304次下載  |  免費
    5. 3protel99下載protel99軟件下載(中文版)
    6. 0.00 MB  |  51209次下載  |  免費
    7. 4LabView 8.0 專業(yè)版下載 (3CD完整版)
    8. 0.00 MB  |  51043次下載  |  免費
    9. 5555集成電路應用800例(新編版)
    10. 0.00 MB  |  33562次下載  |  免費
    11. 6接口電路圖大全
    12. 未知  |  30320次下載  |  免費
    13. 7Multisim 10下載Multisim 10 中文版
    14. 0.00 MB  |  28588次下載  |  免費
    15. 8開關(guān)電源設(shè)計實例指南
    16. 未知  |  21539次下載  |  免費

    總榜

    1. 1matlab軟件下載入口
    2. 未知  |  935053次下載  |  免費
    3. 2protel99se軟件下載(可英文版轉(zhuǎn)中文版)
    4. 78.1 MB  |  537793次下載  |  免費
    5. 3MATLAB 7.1 下載 (含軟件介紹)
    6. 未知  |  420026次下載  |  免費
    7. 4OrCAD10.5下載OrCAD10.5中文版軟件
    8. 0.00 MB  |  234313次下載  |  免費
    9. 5Altium DXP2002下載入口
    10. 未知  |  233046次下載  |  免費
    11. 6電路仿真軟件multisim 10.0免費下載
    12. 340992  |  191183次下載  |  免費
    13. 7十天學會AVR單片機與C語言視頻教程 下載
    14. 158M  |  183277次下載  |  免費
    15. 8proe5.0野火版下載(中文版免費下載)
    16. 未知  |  138039次下載  |  免費