在之前的工作中,我们对常见存储器件进行了名词扫盲,通过调用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电路如下:
三、参考文献: