Verilog实现RAM(7-异步双口SRAM:原理、实现、仿真、分析)

  • Post author:
  • Post category:其他


在之前的工作中,我们对常见存储器件进行了名词扫盲,通过调用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用法



版权声明:本文为CLL_caicai原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。