在之前的工作中,我們對常見存儲器件進行了名詞掃盲,通過調用IP覈實現了簡單的單端口同步讀寫SRAM、通過Verilog實現了單端口同步讀寫SRAM、單端口同步寫,異步讀SRAM、單端口異步讀寫SRAM,雙端口同步讀寫SRAM,以及雙端口異步讀寫SRAM,這些工作見:
但是隨着深入學習發現,之前實現的異步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電路如下: