Verilog實現RAM(7-異步雙口SRAM:原理、實現、仿真、分析)

在之前的工作中,我們對常見存儲器件進行了名詞掃盲,通過調用IP覈實現了簡單的單端口同步讀寫SRAM、通過Verilog實現了單端口同步讀寫SRAM、單端口同步寫,異步讀SRAM、單端口異步讀寫SRAM,雙端口同步讀寫SRAM,以及雙端口異步讀寫SRAM,這些工作見:

Verilog實現RAM

但是隨着深入學習發現,之前實現的異步RAM更像是一個時鐘無關的RAM;異步雙口SRAM應該是讀寫時鐘不同,才能叫異步,像之前寫的“異步”SRAM實際爲時鐘無關的RAM,都沒有時鐘,如何稱爲異步?因此,重新對異步雙口SRAM進行設計和驗證:

目錄

一、原理

二、代碼實現與仿真:

三、參考文獻:


一、原理

異步雙口SRAM與雙端口同步讀寫SRAM類似,唯一區別就在於兩組端口時鐘不同,分別在其控制信號的作用下(如:片選、使能、地址等)進行讀/寫操作;

輸入端口有:(異步應該有兩組時鐘)

    reg clk1;//端口1對應時鐘

    reg [3:0]a1;//輸入地址(RAM深度爲16,對應地址位寬爲4)

    reg we1;// write enable,寫使能時進行RAM寫操作

    reg oe1;// output enable,輸出使能時RAM讀取的結果才能輸出

    reg cs1;// 片選信號,選擇讀取哪一個RAM

//  第二套

    reg clk2;//端口2對應時鐘

    reg [3:0]a2;//輸入地址(RAM深度爲16,對應地址位寬爲4)

    reg we2;// write enable,寫使能時進行RAM寫操作

    reg oe2;// output enable,輸出使能時RAM讀取的結果才能輸出

    reg cs2;// 片選信號,選擇讀取哪一個RAM

輸入輸出端口有:
    wire [7:0]d1;//讀取RAM時數據輸出/寫入RAM時數據輸入   

    wire [7:0]d2;//讀取RAM時數據輸出/寫入RAM時數據輸入

 

工作過程:

兩組端口在各自時鐘驅動下,獨立的根據各自的控制信號cs/we/oe執行對應的操作;工作過程類似:

cs有效(爲1)、we爲1時,寫使能,將d輸入數據寫入a對應地址處;

cs有效(爲1)、we爲0時,讀使能,oe有效(爲1)時將a地址處的數據讀出到d上;

注意:

雙端口RAM同時讀寫時會出現衝突,如:同時對同一地址讀操作,此時兩個端口能直接同時讀出數據嗎?存在仲裁嗎?再如:同時對同一地址進行寫操作,此時寫地址的結果應該是啥?是報錯?是仲裁?寫的內容是哪個端口的內容?還是說內容雜糅在一起;這就想起來之前看過的一句話,編寫一個簡單的RAM很容易,但是要編寫一個穩定的,考慮全面的RAM絕非易事。這也是爲什麼推薦使用IP核的原因,因爲IP核是衆多工程師努力的結晶,相對來說更穩定,考慮的更全面。但是此處爲了瞭解RAM的工作流程,暫不考慮複雜的情況,只對RAM的簡單功能進行仿真實現。

 

二、代碼實現與仿真:

接着之前的工作----仍舊實現一個位寬8bit,深度16bit的雙口異步SRAM;

實現代碼:

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: guoliang CLL
// 
// Create Date: 2020/03/04 15:51:18
// Design Name: 
// Module Name: dsram
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////


module dsram
#(parameter DW = 8,AW = 4)
(
    input clk1,
    input [AW-1:0]a1,//address
    input cs1,// chip select
    input oe1,// output enable
    input we1,// write enable
    inout [DW-1:0]d1,// data
    // 
    input clk2,
    input [AW-1:0]a2,//address
    input cs2,// chip select
    input oe2,// output enable
    input we2,// write enable
    inout [DW-1:0]d2// data
    );

// 
parameter DP = 1 << AW;// depth 
reg [DW-1:0]mem[0:DP-1];
reg [DW-1:0]reg_d1;
// port2
reg [DW-1:0]reg_d2;
//initialization
// synopsys_translate_off
integer i;
initial begin
    for(i=0; i < DP; i = i + 1) begin
        mem[i] = 8'h00;
    end
end
// synopsys_translate_on

// read declaration
// port1
always@(posedge clk1)
begin
    if(cs1 & !we1 & oe1)
        begin
            reg_d1 <= mem[a1];
        end
    else
        begin
            reg_d1 <= reg_d1;
        end
end
// port2
always@(posedge clk2)
begin
    if(cs2 & !we2 & oe2)
        begin
            reg_d2 <= mem[a2];
        end
    else
        begin
            reg_d2 <= reg_d2;
        end
end

// wrirte declaration
// port1
always@(posedge clk1)
begin
    if(cs1 & we1)//port1 higher priority
        begin
            mem[a1] <= d1;
        end
    else
        begin
            mem[a1] <= mem[a1];
        end    
end
//port2
always@(posedge clk2)
begin
    if(cs2 & we2)
        begin
            mem[a2] <= d2;
        end
    else
        begin
            mem[a2] <= mem[a2];
        end    
end

// 三態邏輯
assign d1 = (cs1 & !we1 & oe1) ? reg_d1 : {DW{1'bz}};
assign d2 = (cs2 & !we2 & oe2) ? reg_d2 : {DW{1'bz}};
endmodule

測試文件:

測試設計:port1對應快時鐘clk1,port2對應慢時鐘clk2;

1、port1在clk1驅動下從地址0-15寫數據1-16;port2同時在clk2驅動下從地址0-15讀數據;

2、port1在clk1驅動下從地址15-0讀數據;port2同時在clk2驅動下從地址15-0寫數據oxff;

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: CLL
// 
// Create Date: 2020/03/04 16:42:17
// Design Name: 
// Module Name: dsram_tsb
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////


module dsram_tsb(

    );
// port declaration
reg clk1;
reg clk2;
// port1
reg [3:0]a1;//address
reg cs1;
reg oe1;
reg we1;// write enable
wire [7:0]d1;//datain/out 
// port2
reg [3:0]a2;//address
reg cs2;
reg oe2;
reg we2;// write enable
wire [7:0]d2;//datain/out 
// reg declaration
reg [7:0]din1;
reg [7:0]din2;
// clk1
initial
begin
    clk1 = 1'b0;
    forever #10 clk1 = ~clk1;//period = 20
end
// clk2
initial
begin
    clk2 = 1'b0;
    forever #25 clk2 = ~clk2;//period = 50
end
//
assign d1 = (cs1 & we1)?din1:8'bzzzz_zzzz;
assign d2 = (cs2 & we2)?din2:8'bzzzz_zzzz;
//port1
initial
begin
///////////////////////////////
    fork// port1寫,port2讀
        begin
            a1 = 4'b0000;
            din1 = 8'd1;
            we1 = 1'b1;
            oe1 = 1'b0;
            cs1 = 1'b1;
            repeat(15) begin//port1寫1-16於地址0-15
            #20 a1 = a1+1'b1;
            din1 = din1+1'b1;
            end
        end
        begin
            a2 = 4'b0000;
            din2 = 8'd0;
            we2 = 1'b0;
            oe2 = 1'b1;
            cs2 = 1'b1;
            repeat(15) begin//port2讀地址0-15
            #50 a2 = a2+1'b1;
            end
        end
    join
///////////////////////////////////////////////////
    #50//執行完後,port1讀,port2寫
    fork// port1寫,port2讀
        begin
            we1 = 1'b0;
            oe1 = 1'b1;
            cs1 = 1'b1;
            repeat(15) begin//port1讀於地址15-0
            #20 a1 = a1-1'b1;
            end
        end
        begin
            din2 = 8'b1111_1111;
            we2 = 1'b1;
            oe2 = 1'b0;
            cs2 = 1'b1;
            repeat(15) begin//port2寫11111111於地址15-0
            #50 a2 = a2-1'b1;
            end
        end
    join
end
dsram inst (
  .clk1(clk1),
  .a1(a1),      // input wire [3 : 0] a
  .d1(d1),      // input wire [7 : 0] d
  .we1(we1),    // input wire we
  .cs1(cs1), 
  .oe1(oe1),
  .clk2(clk2),  
  .a2(a2),      // input wire [3 : 0] a
  .d2(d2),      // input wire [7 : 0] d
  .we2(we2),    // input wire we
  .cs2(cs2), 
  .oe2(oe2)  
);
endmodule

仿真結果如下:

1、port1在clk1驅動下從地址0-15寫數據1-16;port2同時在clk2驅動下從地址0-15讀數據;如下:

可以看出,讀寫分別對應各自的clk上升沿,port1在clk1驅動下將數據1-16寫入了mem的[0-15],port2在clk2驅動下以一個較慢的速度從地址0-15讀出數據1-16,與設計一致;


2、port1在clk1驅動下從地址15-0讀數據;port2同時在clk2驅動下從地址15-0寫數據oxff;如下:

可以看出,讀寫分別對應各自的clk上升沿,port1在clk1驅動下快速從地址15-0讀出之前寫進去的數據16-1,讀數據較快不受port2新寫入的oxff影響,port2在clk2驅動下以一個較慢的速度向地址15-0寫數據oxff,與設計一致;

RTL電路如下:

三、參考文獻:

verilog怎麼寫能綜合出異步的RAM

verilog中的fork...join用法

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章