IE盒子

搜索
查看: 90|回复: 0

Dual-Port-RAM:双端口RAM实现笔记

[复制链接]

3

主题

5

帖子

10

积分

新手上路

Rank: 1

积分
10
发表于 2023-3-3 11:36:27 | 显示全部楼层 |阅读模式
FPGA工程实践中的RAM形式很多,在设计中常用的RAM有单口RAM:SPRAM(single-port RAM)。双口RAM:TPRAM(two-port RAM)和真双口RAM:(dual-port RAM)。在芯片设计中,具体使用哪一种RAM,要根据设计的需要而定,比如读写速度和面积。
以下RAM模型是简化的,实际使用中可能会有一些功耗控制的信号等。
单端口RAM只有一套读写线,读写时分复用,读的时候不能写,写的时候不能读。



SPRAM示意图

其中ena是读写使能信号,addr是RAM地址,data_in_a和data_out_a分别是写入RAM和由RAM读出的数据。
对于双端口RAM来说,读和写是分开的,分别有一套读地址线和写地址线,相对于单端口RAM来说效率更高。但是由于同时可以进行读写,所以要注意同一时间内读写地址不能冲突。我们接下来就主要介绍双端口RAM的功能和简单实现。
双端口RAM分很多种,我们在这里主要介绍伪双端口RAM——TPRAM(two-port RAM)和真双端口RAM——DPRAM(dual-port RAM)。伪双端口RAM是指有两个读写通道,其中一个用来读另一个用来写;真双端口 RAM 指的是有两个读写端口,每个端口都可以独立发起读或者写。
1.伪双端口RAM——TPRAM
该RAM读和写是分开的。分别有一套读地址线和写地址线。在读写TPRAM时,要注意读写地址不能冲突。示意图如下,信号含义参照SPRAM。



TPRAM示意图

其中wea是a通道的写使能信号,enb是b通道的读使能信号。
一个伪双端口RAM,一个通道读,一个通道写,写通道有写数据、写地址和写使能,读通道有读数据、读地址和读使能,再加上输入时钟,构成输入输出的端口。主要代码如下:
module TPRAM (
    input clk,

    input wea,                  //enable write signal of channel a
    input enb,                  //enable signal of channel b

    input [7:0] addra,          //address of channel a
    input [7:0] addrb,          //address of channle b

    input [15:0] data_i_a,      //data input of channel a
    output reg [15:0] data_o_b //data output of channel b
);
reg [15:0] RAM [255:0];         //DATAWIDTH = 16, DEPTH = 256 = 2^8

always @(posedge clk) begin     //write channel
    if(wea) begin
        RAM[addra] <= data_i_a;
    end
end

always @(posedge clk) begin    //read channel
    if(enb) begin
        data_o_b <= RAM[addrb];
    end
end
endmodule
RAM读写用两个过程语句块描述,一个通道读,一个通道写。如果是异步的时钟,即读写不是同一时钟的话,可以修改两个过程语句块的敏感事件列表,例如一个改成always@(posedge clkw),另一个改成always@(posedge clkr)即可。
2.真双端口RAM——DPRAM(dual-port RAM)
RAM是真双口RAM,有两套读写地址,可以分别做读写操作。示意图如下,信号含义参照SPRAM。



DPRAM示意图

真双端口RAM,每个通道都有通道使能、输入数据、输出数据、数据地址、写使能、通道时钟六个端口,所以真双端口RAM共有12个端口。主要代码如下:
module DPRAM (
    input clka,
    input clkb,

    input ena,                  //enable signal of channel a
    input enb,                  //enable signal of channel b

    input wea,                  //enable write signal of channel a
    input web,                  //enable write signal of channel b

    input [7:0] addra,          //address of channel a
    input [7:0] addrb,          //address of channle b

    input [15:0] data_i_a,      //data input of channel a
    input [15:0] data_i_b,      //data input of channel b

    output reg [15:0] data_o_a, //data output of channel a
    output reg [15:0] data_o_b //data output of channel b
);
reg [15:0] RAM [255:0];         //DATAWIDTH = 16, DEPTH = 256 = 2^8

always @(posedge clka) begin     
    if(ena) begin
        if(wea) begin
            RAM[addra] <= data_i_a;
        end
        data_o_a <= RAM[addra];
    end
end

always @(posedge clkb) begin     
    if(enb) begin
        if(wea) begin
            RAM[addrb] <= data_i_b;
        end
        data_o_b <= RAM[addrb];
    end
end
endmodule
对真双端口RAM的一个通道来说,当通道使能时,如果写使能,则把值写入到RAM的addra地址,并读出RAM位于addra地址的旧的值。如果写不使能,则读出RAM位于addra地址的数据。
以上就是两个模块的简单实现,我们再通过两个简单的testbench文件测试分别模块功能。这里由于用Mac仿真和查看波形,所以写了额外的波形生成文件代码,可参考如下链接:
1.TPRAM:
`timescale 1ns/100ps
`include "TPRAM.v"
module real_dual_port_ram_tb ();
reg          clk,wea,enb                    ;
reg   [7:0]  addra,addrb                    ;
reg   [15:0] data_i_a                       ;
wire   [15:0] data_o_b                       ;

always #5 clk = ~clk;
// instantiation
TPRAM inst_TPRAM(
    .clk(clk),
    .wea(wea),
    .enb(enb),
    .addra(addra),
    .addrb(addrb),
    .data_i_a(data_i_a),
    .data_o_b(data_o_b)
);
initial begin
    clk <= 0;
    #25
    wea<=1;enb<=1;addra<=8'd0;addrb<=8'd0;data_i_a<=16'd9;
    #20
    wea<=1;enb<=1;addra<=8'd1;addrb<=8'd1;data_i_a<=16'd2;
    #20
    wea<=1;enb<=1;addra<=8'd2;addrb<=8'd2;data_i_a<=16'd7;
    #20
    wea<=1;enb<=1;addra<=8'd3;addrb<=8'd3;data_i_a<=16'd7;
end

/*iverilog */
initial begin
    $dumpfile("waveform_TPRAM.vcd");//generate waveform file
    $dumpvars;//or $dumpvars;
    #200 //simulation time
    $finish;
end
/*iverilog */

endmodule
测试结果如下:



TPRAM仿真波形图

之所以开始输出数据会出现红线,是因为RAM的初始数值是不确定的,所以在每次切换地址后,读取出来的RAM对应地址的旧的值是不确定的,所以是红线。过一个周期后,改地址被写入新的值,新的值被读出。仿真结果是符合预期目标的。
2.DPRAM:
`timescale 1ns/100ps
`include "DPRAM.v"
module real_dual_port_ram_tb ();
reg          clka,clkb,ena,enb,wea,web     ;
reg   [7:0]  addra,addrb                   ;
reg   [15:0] data_i_a,data_i_b             ;
wire   [15:0] data_o_a,data_o_b             ;

always #5 clka = ~clka;
always #5 clkb = ~clkb;
// instantiation
DPRAM inst_DPRAM(
    .clka(clka),
    .clkb(clkb),
    .ena(ena),
    .enb(enb),
    .wea(wea),
    .web(web),
    .addra(addra),
    .addrb(addrb),
    .data_i_a(data_i_a),
    .data_i_b(data_i_b),
    .data_o_a(data_o_a),
    .data_o_b(data_o_b)
);
initial begin
    clka <= 0;
    clkb <= 0;
    #25
    ena<=1;enb<=1;wea<=1;web<=1;addra<=8'd0;addrb<=8'd1;data_i_a<=16'd9;data_i_b<=16'd10;
    #20
    ena<=1;enb<=1;wea<=1;web<=1;addra<=8'd2;addrb<=8'd3;data_i_a<=16'd7;data_i_b<=16'd7;
    #20
    ena<=1;enb<=1;wea<=1;web<=1;addra<=8'd4;addrb<=8'd5;data_i_a<=16'd6;data_i_b<=16'd8;
    #20
    ena<=1;enb<=1;wea<=1;web<=1;addra<=8'd2;addrb<=8'd3;data_i_a<=16'd0;data_i_b<=16'd0;
end

/*iverilog */
initial begin
    $dumpfile("waveform_DPRAM.vcd");//generate waveform file
    $dumpvars;//or $dumpvars;
    #200 //simulation time
    $finish;
end
/*iverilog */

endmodule
测试结果如下:



DPRAM仿真波形图

仿真结果也是符合预期目标的。
由此,我们介绍了简单的单双端口RAM定义、分类及伪双口RAM和真双口RAM的简单实现。当然,目前的vivado等verilog IDE中已经有了很多成熟的RAM IP可直接调用,上述代码可供初学者参考学习RAM的定义和功能。
参考链接:
https://www.bilibili.com/read/cv16270030/
https://blog.csdn.net/re_call/article/details/105225731
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表