Verilog代碼匯總


Verilog代碼匯總

自己整理的verilog文件,方便后續查看。

因個人技術力低下,可能會出現錯誤,可以通過郵箱和我交流:jiaming_li@cqu.edu.cn

使用vivado軟件進行編寫,更改文件編輯器為VSC安裝格式化插件verilog-format

xorgate

代碼傳送門:https://github.com/Geaming-CHN/Vivado/tree/main/_Digital logic/gates/xorgate

異或門的verilog實現:

​ 熟悉大概格式

xorgate.v

module xorgate
#(parameter WIDTH = 8)
    (
    input[(WIDTH-1):0] a,
    input[(WIDTH-1):0] b,
    output[(WIDTH-1):0] c
    );
    assign c = a^b;
endmodule

1位4選1多路選擇器

代碼傳送門:https://github.com/Geaming-CHN/Vivado/tree/main/_Digital logic/Project3

邏輯表達式

\(\large e=a\bar{S_2}\bar{S_1}+b\bar{S_2}{S_1}+c{S_2}\bar{S_1}+d{S_2}{S_1}\)

手繪電路圖如下:

image-20220421194041779

源代碼文件:

采用assign

module design_1
    (
    input [0:0] a,
    input [0:0] b,
    input [0:0] c,
    input [0:0] d,
    input [0:0] S1,
    input [0:0] S2,

    output [0:0] e
    );
    assign e = a&(~S2)&(~S1)|b&(~S2)&(S1)|c&(S2)&(~S1)|d&(S2)&(S1);
endmodule

采用always

module design_1
    (
    input [2:0]a,
    input [2:0]b,
    input [2:0]c,
    input [2:0]d,
    input [1:0]S,
    output reg[2:0]e
    );
    always @(a,b,c,d,S) 
    begin
        case(S[1:0])
            2'b00: e = a;      
            2'b01: e = b;
            2'b10: e = c;
            2'b11: e = d;
            default:e = 2'b00;  
        endcase
    end
endmodule

仿真文件:

module design_1_sim_2();
    parameter size = 10000;
    reg a          = 0;
    reg b          = 0;
    reg c          = 0;
    reg d          = 0;
    reg S1         = 0;
    reg S2         = 0;
    wire e;
    design_1 u(.a(a),.b(b),.c(c),.d(d),.S1(S1),.S2(S2),.e(e));
    initial begin
        a  = 'd0;
        b  = 'd0;
        c  = 'd0;
        d  = 'd0;
        S1 = 'd0;
        S2 = 'd0;
        repeat(size)
        begin
            #10
            a  = $random;
            b  = $random;
            c  = $random;
            d  = $random;
            S1 = $random;
            S2 = $random;
        end
    end
endmodule

數字鍾設計

代碼傳送門:https://github.com/Geaming-CHN/Vivado/tree/main/_Digital logic/project7

代碼當時沒有分模塊寫,所以一個代碼給完了。

內容

實現一個六十進制數字時鍾,秒到60則歸零重加,同時讓分加1,分加到60歸零重加,並讓小時加1,小時加到24歸零重加。

設計思路

首先將整個實驗分成三大部分,一是分頻模塊,二是計數模塊,三是顯示模塊

分頻:

  • 因為代碼中的時鍾信號clk頻率過高,與數字時鍾的要求不匹配。在代碼中采用設置parameter分頻獲得clk2,計數時以clk2為時鍾信號進行計算。

計數:

  • 計數模塊鍾分別設計一個60進制和24進制的計數器,用來計算時分秒及其進位。為方便顯示模塊使用,
  • 將秒拆分為Second_2,Second_1;
  • 將分拆分為Minute_2,Minute_1;
  • 將時拆分為Hour_2,Hour_1;
  • 則Second_1,Minute_1為十進制計數器,Second_2,Minute_2為六進制計數器,Hour_2為3進制計數器,Hour_1為10進制計數器(最后為5進制計數器,特殊情況:23:59:59)

顯示:

使用七段數碼管進行顯示。

其中低電平為有效。為了實現4個數碼管顯示不同內容,利用人眼的視覺暫留,對4個氣短數碼管進行分時復用。當數碼管的刷新頻率高於人眼的視覺暫留時,人眼就無法察覺數碼管信息的改變。

reg[10:0] display

使用11位的display對數碼管進行輸出,前4位為使能端,后七位與七段數碼管相對應。

  • Hour_1為10進制計數器(最后為5進制計數器,特殊情況:23:59:59)

img

module clock(
    input clk,rst,second_select,
    output reg[10:0] display
    );
    reg[3:0] Hour_2,Hour_1,Minute_2,Minute_1,Second_2,Second_1;
    reg[26:0] count = 0;
    reg[15:0] count2 = 0;
    reg[2:0] sel = 0;
    parameter T = 10000000;
    parameter T2 = 50000;
    reg clk2 = 1'b0;
    //時間
    always @(posedge clk) begin
        if(rst)
            count<='b0;
        else if(count == T)
        begin
            clk2 <= ~clk2;
            count <= 0;
        end
        else count <= count + 'b1;
    end
    //Second_1 % 10
    always @(posedge clk2) begin
        //rst
        if(rst)
            Second_1 <= 4'b0000;
        //進位
        else if(Second_1==4'b1001)
            Second_1 <= 4'b0000;
        else
            Second_1 <= Second_1 + 4'b0001;
    end
    //Second_2 % 6
    always @(posedge clk2) begin
        //rst
        if(rst)
            Second_2 <= 4'b0000;
        //進位
        else if(Second_1==4'b1001)//Second_1進位
            begin
                if(Second_2 == 4'b0101)//Second_2==5
                    Second_2<=4'b0000;
                else
                    Second_2<=Second_2+4'b0001;
            end
    end
    //Minute_1 % 10
    always @(posedge clk2) begin
        if(rst)
            Minute_1 <= 4'b0000;
        else if(Second_2 == 4'b0101&&Second_1==4'b1001)
            begin
                if(Minute_1==4'b1001)
                    Minute_1 <= 4'b0000;
                else
                    Minute_1 <= Minute_1+4'b0001;
            end
    end
    //Minute_2 % 6
    always @(posedge clk2) begin
        if(rst)
            Minute_2 <= 4'b0000;
        else if(Second_2 == 4'b0101&&Second_1==4'b1001)
            begin
                if(Minute_1==4'b1001)
                begin
                    if(Minute_2==4'b0101)
                        Minute_2<=4'b0000;
                    else
                        Minute_2 <= Minute_2+4'b0001;
                end
            end
    end
    //Hour_1 % 10 最后為4
    always @(posedge clk2) begin
        if(rst)
            Hour_1 <= 4'b0000;
        else if(Second_2 == 4'b0101&&Second_1==4'b1001&&Minute_2==4'b0101&&Minute_1==4'b1001)
            begin
                if(Hour_2==4'b0010&&Hour_1==4'b0011)//23:59:59
                    Hour_1 <= 4'b0000;
                else if(Hour_1 == 4'b1001)
                    Hour_1 <= 4'b0000;
                else
                    Hour_1 <= Hour_1 + 4'b0001;
            end
    end
    //Hour_2 % 3
    always @(posedge clk2) begin
        if(rst)
            Hour_2 <= 4'b0000;
        else if(Second_2 == 4'b0101&&Second_1==4'b1001&&Minute_2==4'b0101&&Minute_1==4'b1001)
            begin
                if(Hour_2==4'b0010&&Hour_1==4'b0011)//23:59:59
                    Hour_2 <= 4'b0000;
                else if(Hour_1 == 4'b1001)
                    Hour_2 <= Hour_2+4'b0001;
            end
    end
    //顯示
    always @(posedge clk) begin
        if(rst)
            count2<='b0;
        else if(count2==T2)
            begin
                count2<=0;
                sel<=sel+1;
                if(sel==4)
                    sel<=0;
            end        
        else
            count2 <=count2+1;
    end
    always @(posedge clk) begin
        //顯示秒
        if(second_select)
            begin
                case(sel)
                //Second_1 0~9
                0,1: begin
                    case(Second_1)
                        4'b0000:display = 11'b1110_0000001;
                        4'b0001:display = 11'b1110_1001111;
                        4'b0010:display = 11'b1110_0010010;
                        4'b0011:display = 11'b1110_0000110;
                        4'b0100:display = 11'b1110_1001100;
                        4'b0101:display = 11'b1110_0100100;
                        4'b0110:display = 11'b1110_0100000;
                        4'b0111:display = 11'b1110_0001111;
                        4'b1000:display = 11'b1110_0000000;
                        4'b1001:display = 11'b1110_0000100;
                    endcase
                end
                //Second_2 0~5
                2,3:begin
                    case(Second_2)
                        4'b0000:display = 11'b1101_0000001;
                        4'b0001:display = 11'b1101_1001111;
                        4'b0010:display = 11'b1101_0010010;
                        4'b0011:display = 11'b1101_0000110;
                        4'b0100:display = 11'b1101_1001100;
                        4'b0101:display = 11'b1101_0100100;
                    endcase
                end
                endcase
            end
        //顯示小時和分鍾
        else
            begin
                case(sel)
                //Minute_1 0~9
                0:begin
                    case(Minute_1)
                        4'b0000:display = 11'b1110_0000001;
                        4'b0001:display = 11'b1110_1001111;
                        4'b0010:display = 11'b1110_0010010;
                        4'b0011:display = 11'b1110_0000110;
                        4'b0100:display = 11'b1110_1001100;
                        4'b0101:display = 11'b1110_0100100;
                        4'b0110:display = 11'b1110_0100000;
                        4'b0111:display = 11'b1110_0001111;
                        4'b1000:display = 11'b1110_0000000;
                        4'b1001:display = 11'b1110_0000100;
                    endcase
                end
                //Minute_2 0~5
                1:begin
                    case(Minute_2)
                        4'b0000:display = 11'b1101_0000001;
                        4'b0001:display = 11'b1101_1001111;
                        4'b0010:display = 11'b1101_0010010;
                        4'b0011:display = 11'b1101_0000110;
                        4'b0100:display = 11'b1101_1001100;
                        4'b0101:display = 11'b1101_0100100;
                    endcase
                end
                //Hour_1 0~9
                2:begin
                    case(Hour_1)
                        4'b0000:display = 11'b1011_0000001;
                        4'b0001:display = 11'b1011_1001111;
                        4'b0010:display = 11'b1011_0010010;
                        4'b0011:display = 11'b1011_0000110;
                        4'b0100:display = 11'b1011_1001100;
                        4'b0101:display = 11'b1011_0100100;
                        4'b0110:display = 11'b1011_0100000;
                        4'b0111:display = 11'b1011_0001111;
                        4'b1000:display = 11'b1011_0000000;
                        4'b1001:display = 11'b1011_0000100;
                    endcase
                end
                //Hour_2
                3:begin
                    case(Hour_2)
                        4'b0000:display = 11'b0111_0000001;
                        4'b0001:display = 11'b0111_1001111;
                        4'b0010:display = 11'b0111_0010010;
                    endcase
                end
                endcase
            end
    end
endmodule

單端口RAM

代碼傳送門:https://github.com/Geaming-CHN/Vivado/tree/main/_Digital logic/Project14

內容

利用BASYS3片內存儲器單元實現單端口RAM設計(帶異步讀和同步讀兩種模式),在時鍾(clk)上升沿,采集地址addr)、輸入數據data_in)、執行相關控制信息。當寫使能(we)有效,則執行寫操作,否則執行讀取操作。同步與異步設計僅針對讀操作:對於異步RAM而言,讀操作為異步,即地址信號有效時,控制器直接讀取RAM陣列;對於同步RAM而言,地址信號在時鍾上升沿被采集。並保存在寄存器中,然后使用該地址信號讀取RAM陣列。

設計思路

單端口RAM設計(帶異步讀和同步讀兩種模式),在時鍾(clk)上沿采集地址(addr)、輸入數據data_in)、執行相關控制信息。當寫使能we有效,則執行寫操作,否則執行讀取操作。同步與異步設計僅針對讀操作:對於異步RAM而言,讀操作為異步,即地址信號有效時,控制器直接讀取RAM陣列;對於同步RAM而言,地址信號在時鍾上升沿被采集並保存在寄存器中,然后使用該地址信號讀取RAM陣列。(額外添加了讀使能oe)

image-20220421220041632

單端口同步RAM

  • 頂層模塊

    image-20220421220150561

  • divider:為七段數碼管提供分頻后的clk;

  • Syn_SinglePortRAM:單端口同步RAM;

  • transformer:移位加三法,輸入至七段數碼管進行顯示功能的實現;

  • display7seg:七段數碼管顯示模塊;

top_Syn_SinglePortRAM

module top_Syn_SinglePortRAM#(parameter DATA_WIDTH = 4,parameter ADDR_DEPTH = 4)(
    input clk,rst,
    input [ADDR_DEPTH-1:0]addr,
    input [DATA_WIDTH-1:0]data_in,
    input we,oe,
    output wire[3:0]an,
    output wire[6:0]display
    );
    wire[DATA_WIDTH-1:0]data_out;
    wire clk_div;
    reg [25:0]target = 50000;
    wire [15:0]BCD;
    //divider
    divider d(.clk(clk),.rst(rst),.target(target),.clk_div(clk_div));
    //Syn_SinglePortRAM
    Syn_SinglePortRAM S(.clk(clk),.rst(rst),.addr(addr),.data_in(data_in),.we(we),.oe(oe),.data_out(data_out));
    //transformer
    transformer t(.data(data_out),.BCD(BCD));
    //display7seg
    display7seg dis(.clk(clk_div),.data3(BCD[15:12]),.data2(BCD[11:8]),.data1(BCD[7:4]),.data0(BCD[3:0]),.an(an),.display(display));
endmodule

divider

module divider(
    input clk,rst,
    input [15:0] target,
    output reg clk_div
    );
    reg [15:0] counter;
    always @(posedge clk) begin
        if(rst)
        begin
            counter <= 0;
            clk_div <= 0;
        end
        else if(counter==target)
        begin
            counter <= 0;
            clk_div <= ~clk_div;
        end
        else 
            counter <= counter+1;
    end
endmodule

Syn_SinglePortRAM

module Syn_SinglePortRAM#(parameter DATA_WIDTH = 4,parameter ADDR_DEPTH = 4)(
    input clk,rst,
    input [ADDR_DEPTH-1:0]addr,
    input [DATA_WIDTH-1:0]data_in,
    input we,
    input oe,
    output reg[DATA_WIDTH-1:0]data_out
    );
    reg [DATA_WIDTH-1:0] RAM[(1<<ADDR_DEPTH)-1:0];
    //write
    always @(posedge clk) 
    begin
        if(rst)
        begin:init_RAM
            integer i;//必須聲明在有名字的塊中,或寫在外面
            for(i=0;i<(1<<ADDR_DEPTH);i = i+1)
            begin
                RAM[i] <= 0;
            end
        end
        else if(we)
        begin
            RAM[addr] <= data_in;
        end
    end
    //syn_read
    always @(posedge clk) begin
        if(rst)
        begin
            data_out <= 0;
        end
        else if(!we && oe)
        begin 
            data_out <= RAM[addr];
        end
        else
            data_out <= 0;
    end
endmodule

transformer

module transformer(
    input [3:0] data,
    output [15:0]BCD//四位,方便輸入至數碼管
    );
    //移位加3,轉換成BCD
    reg [19:0] transfor_data;
    always @(*) 
    begin
        transfor_data = 16'b0;
        transfor_data[3:0] = data;
        repeat(4)
        begin
            if(transfor_data[19:16]>4)
                transfor_data[19:16] = transfor_data[19:16]+2'b11;
            if(transfor_data[15:12]>4)
                transfor_data[15:12] = transfor_data[15:12]+2'b11;
            if(transfor_data[11:8]>4)
                transfor_data[11:8] = transfor_data[11:8]+2'b11; 
            if(transfor_data[7:4]>4)     
                transfor_data[7:4] = transfor_data[7:4]+2'b11;      
            transfor_data[19:1] = transfor_data[18:0];
        end
    end
    assign BCD = transfor_data[19:4];
endmodule

display7seg

module display7seg(
    input clk,
    input [3:0]data3,data2,data1,data0,
    output reg[3:0]an,
    output reg[6:0]display
    );
    reg [1:0] count;
    always @(posedge clk) begin
        if(count == 'b11)
            count <= 0;
        else
            count <= count +'b1;
    end
    always @(posedge clk) begin
        case(count)
        2'b00: an <= 4'b1110;
        2'b01: an <= 4'b1101;
        2'b10: an <= 4'b1011;
        2'b11: an <= 4'b0111;
        endcase
    end
    always @(posedge clk) begin
        case(count)
            2'b00:
            case (data0)
                4'b0000:display = 7'b0000001;
                4'b0001:display = 7'b1001111;
                4'b0010:display = 7'b0010010;
                4'b0011:display = 7'b0000110;
                4'b0100:display = 7'b1001100;
                4'b0101:display = 7'b0100100;
                4'b0110:display = 7'b0100000;
                4'b0111:display = 7'b0001111;
                4'b1000:display = 7'b0000000;
                4'b1001:display = 7'b0000100;    
            endcase
            2'b01:
            case (data1)
                4'b0000:display = 7'b0000001;
                4'b0001:display = 7'b1001111;
                4'b0010:display = 7'b0010010;
                4'b0011:display = 7'b0000110;
                4'b0100:display = 7'b1001100;
                4'b0101:display = 7'b0100100;
                4'b0110:display = 7'b0100000;
                4'b0111:display = 7'b0001111;
                4'b1000:display = 7'b0000000;
                4'b1001:display = 7'b0000100;    
            endcase
            2'b10:
            case (data2)
                4'b0000:display = 7'b0000001;
                4'b0001:display = 7'b1001111;
                4'b0010:display = 7'b0010010;
                4'b0011:display = 7'b0000110;
                4'b0100:display = 7'b1001100;
                4'b0101:display = 7'b0100100;
                4'b0110:display = 7'b0100000;
                4'b0111:display = 7'b0001111;
                4'b1000:display = 7'b0000000;
                4'b1001:display = 7'b0000100;    
            endcase
            2'b11:
            case (data3)
                4'b0000:display = 7'b0000001;
                4'b0001:display = 7'b1001111;
                4'b0010:display = 7'b0010010;
                4'b0011:display = 7'b0000110;
                4'b0100:display = 7'b1001100;
                4'b0101:display = 7'b0100100;
                4'b0110:display = 7'b0100000;
                4'b0111:display = 7'b0001111;
                4'b1000:display = 7'b0000000;
                4'b1001:display = 7'b0000100;    
            endcase
        endcase
    end
endmodule

單端口異步RAM

  • 頂層模塊

image-20220421220247508

  • Asy_SinglePortRAM:單端口異步RAM;
  • 其余模塊功能同上;

top_Asy_SinglePortRAM

module top_Asy_SinglePortRAM#(parameter DATA_WIDTH = 4,
                              parameter ADDR_DEPTH = 4)
                            (input clk,
                              rst,
                              input [ADDR_DEPTH-1:0]addr,
                              input [DATA_WIDTH-1:0]data_in,
                              input we,
                              oe,
                              output wire[3:0]an,
                              output wire[6:0]display);
    wire[DATA_WIDTH-1:0]data_out;
    wire clk_div;
    reg [25:0]target = 50000;
    wire [15:0]BCD;
    //divider
    divider d(.clk(clk),.rst(rst),.target(target),.clk_div(clk_div));
    //Syn_SinglePortRAM
    Asy_SinglePortRAM S(.clk(clk),.rst(rst),.addr(addr),.data_in(data_in),.we(we),.oe(oe),.data_out(data_out));
    //transformer
    transformer t(.data(data_out),.BCD(BCD));
    //display7seg
    display7seg dis(.clk(clk_div),.data3(BCD[15:12]),.data2(BCD[11:8]),.data1(BCD[7:4]),.data0(BCD[3:0]),.an(an),.display(display));
endmodule

Asy_SinglePortRAM

module Asy_SinglePortRAM#(parameter DATA_WIDTH = 4,parameter ADDR_DEPTH = 4)(
    input clk,rst,
    input [ADDR_DEPTH-1:0]addr,
    input [DATA_WIDTH-1:0]data_in,
    input we,oe,
    output reg[DATA_WIDTH-1:0]data_out
    );
    reg [DATA_WIDTH-1:0] RAM[(1<<ADDR_DEPTH)-1:0];
    //write
    always @(posedge clk) 
    begin
        if(rst)
        begin:init_RAM
            integer i;//必須聲明在有名字的塊中,或寫在外面
            for(i=0;i<(1<<ADDR_DEPTH);i = i+1)
            begin
                RAM[i] <= 0;
            end
        end
        else if(we)
        begin
            RAM[addr] <= data_in;
        end
    end
    //read
    //asy
    always @(addr) 
    begin
        if(!we && oe)
            data_out = RAM[addr];
        else
        begin
            data_out = 0;
        end
    end
endmodule

雙端口RAM

代碼傳送門:https://github.com/Geaming-CHN/Vivado/tree/main/_Digital logic/Project14

內容

實現雙端口(同步與異步)RAM設計,相對於單端口RAM而言,雙端口RAM存在兩個存取端口,並且可獨立進行讀寫操作,具有自己的地址(addr_a、addr_b)、數據輸入din_a、din_b輸出端口(dout_a、dout_b以及控制信號。

設計思路

雙端口(同步與異步)RAM,相對於單端口RAM而言,雙端口RAM存在兩個存取端口,並且可獨立進行讀寫操作,具有自己的地址(addr_a、addr_b)、數據輸入(din_a、din_b)/輸出端口(dout_a、dout_b)以及控制信號。雙端口RAM常用於視頻/圖像處理設計中。(額外添加了讀使能oe_a,oe_b)

image-20220421220346608

雙端口同步RAM

  • 頂層模塊:

image-20220421220423865

  • Syn_DoublePortRAM:雙端口同步RAM;
  • 其余模塊功能同上

top_Syn_DoublePortRAM

module top_Syn_DoublePortRAM#(parameter DATA_WIDTH = 4,
                              parameter ADDR_DEPTH = 3)
                            (input clk,
                              rst,
                              input [ADDR_DEPTH-1:0]addr_a,
                              addr_b,
                              input [DATA_WIDTH-1:0]din_a,
                              din_b,
                              input we_a,
                              we_b,
                              input oe_a,
                              oe_b,
                              output wire[3:0]an,
                              output wire[6:0]display,
                              output wire error);
    wire[DATA_WIDTH-1:0]dout_a,dout_b;
    wire clk_div;
    reg [25:0]target = 50000;
    wire [15:0]BCD_a;
    wire [15:0]BCD_b;
    //divider
    divider d(.clk(clk),.rst(rst),.target(target),.clk_div(clk_div));
    //Syn_DoublePortRAM
    Syn_DoublePortRAM S(.clk(clk),.rst(rst),.addr_a(addr_a),.addr_b(addr_b),.din_a(din_a),.din_b(din_b),.we_a(we_a),.we_b(we_b),.oe_a(oe_a),.oe_b(oe_b),.dout_a(dout_a),.dout_b(dout_b),.error(error));
    //transformer
    transformer t_a(.data(dout_a),.BCD(BCD_a));
    transformer t_b(.data(dout_b),.BCD(BCD_b));
    //display7seg
    display7seg dis(.clk(clk_div),.data3(BCD_a[7:4]),.data2(BCD_a[3:0]),.data1(BCD_b[7:4]),.data0(BCD_b[3:0]),.an(an),.display(display));
endmodule

Syn_DoublePortRAM

module Syn_DoublePortRAM#(parameter DATA_WIDTH = 4,
                          parameter ADDR_DEPTH = 3)
                        (input clk,
                          rst,
                          input [ADDR_DEPTH-1:0]addr_a,
                          addr_b,
                          input [DATA_WIDTH-1:0]din_a,
                          din_b,
                          input we_a,
                          we_b,
                          input oe_a,
                          oe_b,
                          output reg[DATA_WIDTH-1:0]dout_a,
                          dout_b,
                          output reg error);
    reg [DATA_WIDTH-1:0] RAM[(1<<ADDR_DEPTH)-1:0];
    //error
    always @(posedge clk)
    begin
        if (rst)
        begin
            error <= 0;
        end
        else if (!we_a&&!we_b&&(addr_a == addr_b))//地址相同時只能進行read
            error <= 0;
        else if (addr_a! = addr_b)//地址不同
            error <= 0;
        else
            error <= 1;//error指示燈亮
    end
    //write
    integer i;
    always @(posedge clk)
    begin
        if (rst)//init
        begin
            for(i = 0;i<(1<<ADDR_DEPTH);i = i+1)
            begin
                RAM[i] <= 0;
            end
        end
        else if (we_a&&!we_b&&(addr_a! = addr_b))
            RAM[addr_a] = din_a;
        else if (!we_a&&we_b&&(addr_a! = addr_b))
            RAM[addr_b] = din_b;
        else if (we_a&&we_b&&(addr_a! = addr_b))
        begin
            RAM[addr_a] = din_a;
            RAM[addr_b] = din_b;
        end
            end
            //read
            //syn_a
            always @(posedge clk)
            begin
                if (rst)
                begin
                    dout_a <= 0;
                end
                else if (!we_a && oe_a)
                begin
                    dout_a <= RAM[addr_a];
                end
                else
                    dout_a <= 0;
            end
            
            always @(posedge clk)
            begin
                if (rst)
                begin
                    dout_b <= 0;
                end
                else if (!we_b && oe_b)
                begin
                    dout_b <= RAM[addr_b];
                end
                else
                    dout_b <= 0;
            end
            endmodule

雙端口異步RAM

  • 頂層模塊:

    image-20220421220505617

  • Asy_DoublePortRAM:雙端口同步RAM;

  • 其余模塊功能同上

top_Asy_DoublePortRAM

module top_Asy_DoublePortRAM#(parameter DATA_WIDTH = 3,
                              parameter ADDR_DEPTH = 3)
                            (input clk,
                              rst,
                              input [ADDR_DEPTH-1:0]addr_a,
                              addr_b,
                              input [DATA_WIDTH-1:0]din_a,
                              din_b,
                              input we_a,
                              we_b,
                              input oe_a,
                              oe_b,
                              output wire[3:0]an,
                              output wire[6:0]display,
                              output wire error);
    
    wire[DATA_WIDTH-1:0]dout_a,dout_b;
    wire clk_div;
    reg [25:0]target = 50000;
    wire [15:0]BCD_a;
    wire [15:0]BCD_b;
    
    //divider
    divider d(.clk(clk),
    .rst(rst),
    .target(target),
    .clk_div(clk_div));
    
    //Syn_DoublePortRAM
    Asy_DoublePortRAM S(.clk(clk),
    .rst(rst),
    .addr_a(addr_a),
    .addr_b(addr_b),
    .din_a(din_a),
    .din_b(din_b),
    .we_a(we_a),
    .we_b(we_b),
    .oe_a(oe_a),
    .oe_b(oe_b),
    .dout_a(dout_a),
    .dout_b(dout_b),
    .error(error));
    
    //transformer
    transformer t_a(.data(dout_a),.BCD(BCD_a));
    transformer t_b(.data(dout_b),.BCD(BCD_b));
    //display7seg
    display7seg dis(.clk(clk_div),.data3(BCD_a[7:4]),.data2(BCD_a[3:0]),.data1(BCD_b[7:4]),.data0(BCD_b[3:0]),.an(an),.display(display));
endmodule

Asy_DoublePortRAM

module Asy_DoublePortRAM#(parameter DATA_WIDTH = 4,
                          parameter ADDR_DEPTH = 3)
                        (input clk,
                          rst,
                          input [ADDR_DEPTH-1:0]addr_a,
                          addr_b,
                          input [DATA_WIDTH-1:0]din_a,
                          din_b,
                          input we_a,
                          we_b,
                          input oe_a,
                          oe_b,
                          output reg[DATA_WIDTH-1:0]dout_a,
                          dout_b,
                          output reg error);
    
    reg [DATA_WIDTH-1:0] RAM[(1<<ADDR_DEPTH)-1:0];
    //error
    always @(posedge clk)
    begin
        if (rst)
        begin
            error <= 0;
        end
        else if (!we_a&&!we_b&&(addr_a == addr_b))//地址相同時只能進行read
            error <= 0;
        else if (addr_a! = addr_b)//地址不同
            error <= 0;
        else
            error <= 1;//error指示燈亮
    end
    //write
    integer i;
    always @(posedge clk)
    begin
        if (rst)//init
        begin
            for(i = 0;i<(1<<ADDR_DEPTH);i = i+1)
            begin
                RAM[i] <= 0;
            end
        end
        else if (we_a&&!we_b&&(addr_a! = addr_b))
            RAM[addr_a] = din_a;
        else if (!we_a&&we_b&&(addr_a! = addr_b))
            RAM[addr_b] = din_b;
        else if (we_a&&we_b&&(addr_a! = addr_b))
        begin
            RAM[addr_a] = din_a;
            RAM[addr_b] = din_b;
        end
            end
            //read
            //asy_a
            always @(addr_a)
            begin
                if (!we_a && oe_a)
                    dout_a <= RAM[addr_a];
                else
                    dout_a <= 0;
            end
            
            //asy_b
            always @(addr_b)
            begin
                if (!we_b && oe_b)
                    dout_b <= RAM[addr_b];
                else
                    dout_b <= 0;
            end
            endmodule

FIFO

代碼傳送門:https://github.com/Geaming-CHN/Vivado/tree/main/_Digital logic/Project14

內容

實現FIFO設計,FIFO由存儲單元隊列或陣列構成,和RAM不同的是FIFO沒有地址,第一個被寫入隊列的數據也是第一個從隊列中讀出的數據。

設計思路

FIFO 是一個先入先出的存儲隊列,和RAM 不同的是FIFO 沒有地址,第一個被寫入隊列的數據也是第一個從隊列中讀出的數據。FIFO 可以在輸入輸出速率不匹配時,作為臨時存儲單元;可用於不同時鍾域中間的同步;輸入數據路徑和輸出數據路徑之間數據寬度不匹配時,可用於數據寬度調整電路。

image-20220421220605129

  • 頂層模塊

    image-20220421220624346

  • debkey:消抖模塊;

  • FIFO:實現FIFO;

top_FIFO

module top_FIFO#(parameter DATA_WIDTH = 4,parameter ADDR_DEPTH = 4)(
    input button,clk,rst,wr_en,rd_en,
    input [DATA_WIDTH-1:0]data_in,
    output empty,full, 
    output wire[3:0]an,
    output wire[6:0]display
    );
    wire button_deb;
    wire[DATA_WIDTH-1:0]data_out;
    wire clk_div;
    reg [25:0]target = 50000;
    wire [15:0]BCD;
    //divider
    divider d(.clk(clk),.rst(rst),.target(target),.clk_div(clk_div));
    //debkey
    debkey deb(.clk(clk),.rst(rst),.key_in(button),.key_out(button_deb));
    //FIFO
    FIFO F(.clk(button_deb),.rst(rst),.wr_en(wr_en),.rd_en(rd_en),.data_in(data_in),.empty(empty),.full(full),.data_out(data_out));
    //transformer
    transformer t(.data(data_out),.BCD(BCD));
    //display7seg
    display7seg dis(.clk(clk_div),.data3(BCD[15:12]),.data2(BCD[11:8]),.data1(BCD[7:4]),.data0(BCD[3:0]),.an(an),.display(display));    
endmodule

FIFO

module FIFO#(parameter DATA_WIDTH = 4,parameter ADDR_DEPTH = 4)(
    input clk,rst,wr_en,rd_en,
    input [DATA_WIDTH-1:0] data_in,
    output reg empty,full,    
    output reg[DATA_WIDTH-1:0] data_out
    );
    reg [DATA_WIDTH-1:0] FIFO[(1<<ADDR_DEPTH) - 1:0]; 
    reg [ADDR_DEPTH-1:0]head;
    reg [ADDR_DEPTH-1:0]rear;
    reg [ADDR_DEPTH:0]NUM;
    //empty
    always @(*) begin
        if(NUM==0)
            empty<=1;
        else
            empty<=0;
    end
    //full
    always @(*) begin
        if(NUM==(1<<ADDR_DEPTH))
            full<=1;
        else
            full<=0;
    end
    //NUM
    always @(posedge clk or posedge rst) begin
        if(rst)
            NUM<=0;
        else if(!wr_en&&!rd_en)//no write no read
            NUM<=NUM;
        else if(wr_en&&!rd_en&&(NUM<(1<<ADDR_DEPTH)))//wirte no read
            NUM<=NUM+'b1;
        else if(!wr_en&&rd_en&&(NUM>0))//read no write
            NUM<=NUM-'b1; 
        else if(wr_en&&rd_en)
            NUM<=NUM;
    end
    //write
    integer i;
    always @(posedge clk or posedge rst) begin
        if(rst)
        begin
            rear<='b0;
            for(i=0;i<(1<<ADDR_DEPTH);i=i+1)
                FIFO[i]<=0;
        end
        else if(wr_en&&(NUM<(1<<ADDR_DEPTH)))//not full
        begin
            FIFO[rear]<=data_in;
            rear<=(rear+1)%(1<<ADDR_DEPTH);
        end
        else if(wr_en&&(NUM>(1<<ADDR_DEPTH)-1))//full
            rear<=rear;

    end
    //read
    always @(posedge clk or posedge rst) begin
        if(rst)
        begin
            head<='b0;
            data_out<=0;
        end
            
        else if(rd_en&&(NUM!=0))
        begin
            data_out<=FIFO[head];
            head<=(head+1)%(1<<ADDR_DEPTH);
        end
        else if(NUM==0)
            data_out<=0;
    end
endmodule

debkey

module debkey(
    input clk,
    input rst,
    input key_in,
    output key_out
    );
    parameter T100Hz = 249999;
    integer cnt_100Hz;
    reg clk_100Hz;
    always @(posedge clk) begin
        if(rst)
            cnt_100Hz<=32'b0;
        else
        begin
            cnt_100Hz<=cnt_100Hz+1'b1;
            if(cnt_100Hz==T100Hz)
            begin
                cnt_100Hz<=32'b0;
                clk_100Hz<=~clk_100Hz;
            end
        end
    end

    reg[2:0]key_rrr,key_rr,key_r;
    always @(posedge clk_100Hz) begin
        if(rst)
            begin
                key_rrr<=1'b1;
                key_rr<=1'b1;
                key_r<=1'b1;
            end
        else
        begin
            key_rrr<=key_rr;
            key_rr<=key_r;
            key_r<=key_in;
        end
    end
    assign key_out = key_rrr&key_rr&key_r;

endmodule


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM