FPGA Simple Dual Port RAM - IP Core


foreword

Environment:
1. Quartus18.0
2. vscode
3. Board model: Atomic Brother Pioneer 2 (EP4CE10F17C8)
Requirements:
Use Altera RAM IP core to generate a simple dual-port RAM, then read and write to RAM, and use Modelsim software Carry out simulation and SignalTap software for online debugging.


1. Dual port RAM

From the previous single-port study, we know that the RAM IP core is divided into single-port RAM and dual-port RAM, that is, the number of read and write ports of the RAM IP core. For a single port, reading and writing share a pair of address lines, so data at different addresses cannot be read and written at the same time. For dual ports, the read and write address lines are separated, so they have simultaneous read and write capabilities .

1. Simple dual port and true dual port

Dual-port RAM is divided into simple dual-port RAM and true dual-port RAM. As the name implies, although simple dual-port RAM has two ports, one port can only be used for writing, and the other port can only be used for reading , so simple dual-port RAM RAM is also known as pseudo dual-port RAM. True dual-port RAM means that both ports can be used for writing or reading . It can be understood as a RAM with two independent single ports. A single-port RAM is more convenient to use.

  • In short, in the case of both reading and writing, if multiple writing and reading are required, choose true dual port.

2. Simple dual-port RAM block diagram

  • Module block diagram:
    insert image description here
  • Port block diagram:
    insert image description here
  • Port description:

data : RAM write data port;
wraddress : RAM write address port;
wren : write enable signal, active high;
byteena : byte enable control, this function shields the input data, so that only the specified byte in the data is written , bytes not written retain the previously written value. When the bit width of the written data is 16-bit, 18-bit, 32-bit and 36-bit, the M9K module will support the byte enable, the wren signal and the byte byteena signal together control the write operation of the RAM module. The byteena signal is optional during the creation of the RAM IP core, and it is optional to use the byte enable control function.
wr_addressstall : write address clock enable control, when the wr_addressstall signal is high level, the effective address clock enable will keep the previous address. The wr_addressstall signal is optional during the creation of the RAM IP core, and it is optional to use the address enable control function.
wrclock : write clock;
wrclocken : write clock enable signal, active high;
aclr : asynchronous reset signal, active high;
rdaddress : RAM read address port;
rden : read enable signal, active high;
q : Data read from RAM;
rd_addressstall: Read address clock enable control, when the rd_addressstall signal is high level, the effective address clock enable will keep the previous address. The rd_addressstall signal is optional during the creation of the RAM IP core, and it is optional to use the address enable control function.
rdclock : read clock;
rdclocken : read clock enable signal, active high;

2. IP core configuration

1. RAM dual-port IP core configuration

  • Find the RAM IP:
    insert image description here
  • Add the IP core path and name the file after double-clicking:
    insert image description here
  • Select port type and memory size:

insert image description here

The upper option is to choose simple dual-port or true dual-port; the lower one is the unit words or bits used to specify the memory size.

  • Set the storage depth and bit width of RAM:
    insert image description here
  • Check the independent clock and read enable signal:
    insert image description here

"What clocking method do you want to use?" : Used to set the clock of dual-port RAM, choose the second one here (Dualclock: use separate 'read' and 'write' clocks), that is, the write port and the read port are selected independently clock.
"Create a 'rden' read enable signal" : Select whether to check the read enable signal, if not checked, it will always be read.

  • Unregister output signal q:
    insert image description here

If selected here, the q read from RAM will be output with one more clock cycle delay.

  • Keep RAM initialized:
    insert image description here
  • Keep the default, next:
    insert image description here
  • Check ram_2_inst.v and ram_2_bb.v:
    insert image description here
  • After clicking finish, click YES to add the IP core to the project:
    insert image description here
  • Effect:
    insert image description here

2. PLL IP core configuration

Since the previous article has already introduced it in detail, I will not repeat it here:
Introduction to IP core and call of PLL_IP core

3. Source code

We configured IPs but we still need to write driver files to call them.

1. ram_wr (write module)

module ram_wr (
    input               clk         ,
    input               rst_n       ,

    output              ram_wr_en   ,//写使能
    output reg [4:0]    ram_wr_addr ,//写地址
    output reg [7:0]    ram_wr_data  //写数据
);
/*****************
该写模块中,在计数器从0累加到31时向ram写入数据,在到达63后计数器保持不变
该模块只在上电后写入一次
******************/
reg [5:0] wr_cnt;
//写使能在计数到0~31是为高电平
assign ram_wr_en = ((wr_cnt >= 6'd0) && (wr_cnt <= 6'd31) && rst_n) ? 1'b1 : 1'b0;

//计数器
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        wr_cnt <= 6'd0;
    end
    else if(wr_cnt == 6'd63)
        wr_cnt <=wr_cnt;
    else
        wr_cnt <= wr_cnt + 1'b1;
end

//产生写数据信号
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        ram_wr_data <= 8'd0;
    else if(wr_cnt >= 6'd0 && wr_cnt <= 6'd31)
        ram_wr_data <= ram_wr_data + 1'b1;
    else
        ram_wr_data <= 8'd0;
end

//写地址信号
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        ram_wr_addr <= 5'd0;
    else if(wr_cnt >= 6'd0 && wr_cnt <= 6'd31)
        ram_wr_addr <= ram_wr_addr + 1'b1;
    else
        ram_wr_addr <= 5'd0;
end
endmodule

2, ram_rd (read module)

module ram_rd(
    input               clk             ,
    input               rst_n           ,
    input   [7:0]       ram_rd_data     ,//读数据

    output              ram_rd_en       ,//读使能
    output  reg [4:0]   ram_rd_addr      //读地址
);

reg [5:0] rd_cnt;

assign ram_rd_en = ((rd_cnt >= 6'd0) && (rd_cnt <= 6'd31) && rst_n) ? 1'b1 :1'b0;

//读计数器
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        rd_cnt <= 6'd0;
    else if(rd_cnt == 6'd63)
        rd_cnt <= 6'd0;
    else
        rd_cnt <= rd_cnt + 1'b1;
end

//读地址范围
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        ram_rd_addr <= 5'd0;
    else if(rd_cnt >= 6'd0 && rd_cnt <= 6'd31)
        ram_rd_addr <= ram_rd_addr + 1'b1;
    else
        ram_rd_addr <= 5'd0;
end
endmodule

3. ip_2port_ram (top-level file)

module  ip_2port_ram(
    input     sys_clk   ,
    input     sys_rst_n

);

wire clk_50m;
wire clk_25m;
wire locked;
wire rst_n;

wire       ram_wr_en  ;
wire [4:0] ram_wr_addr;
wire [7:0] ram_wr_data;

wire       ram_rd_en  ;
wire [4:0] ram_rd_addr;
wire [7:0] ram_rd_data;

assign rst_n = sys_rst_n & locked;
//锁相环,用于产生两个时钟
pll_clk pll_clk_inst(
	.areset         (~sys_rst_n),
	.inclk0         (sys_clk),
	.c0             (clk_50m),
	.c1             (clk_25m),
	.locked         (locked)
);
//RAM写模块
ram_wr ram_wr_inst(
    .clk            (clk_50m),
    .rst_n          (rst_n),

    .ram_wr_en      (ram_wr_en  ),//写使能
    .ram_wr_addr    (ram_wr_addr),//写地址
    .ram_wr_data    (ram_wr_data) //写数据
);
//RAM读模块
ram_rd ram_rd_inst(
    .clk             (clk_25m),
    .rst_n           (rst_n),

    .ram_rd_en       (ram_rd_en  ),//读使能
    .ram_rd_addr     (ram_rd_addr), //读地址
    .ram_rd_data     (ram_rd_data)//读数据
);
//简单双端口RAM
ram_2 ram_2_inst(
	.data           (ram_wr_data),
	.rdaddress      (ram_rd_addr),
	.rdclock        (clk_25m),
	.rden           (ram_rd_en),
	.wraddress      (ram_wr_addr),
	.wrclock        (clk_50m),
	.wren           (ram_wr_en),
	.q              (ram_rd_data)
);
endmodule

4. Simulation

1. Simulation file

`timescale 1ns/1ns
module tb_ip_2port_ram();

parameter T = 20;

reg    sys_clk;
reg    sys_rst_n;

initial begin
    sys_clk = 1'b0;
    sys_rst_n = 1'b0;
    #(T+1)
    sys_rst_n = 1'b1;
    #(3000)
    $stop;
end

always #(T/2) sys_clk = ~sys_clk;

ip_2port_ram ip_2port_ram_inst(
    .sys_clk        (sys_clk),
    .sys_rst_n      (sys_rst_n)
);
endmodule

2. Waveform simulation

insert image description here

  • Waveform analysis:

The moment where the yellow line is located is both the time when the write is enabled and the time when the read is enabled, so it is both writing and reading at this time. When the ram address is 0, the written data is also 0; when the ram address is 1, the written data is also 1, we write a total of 32 data into the ram. The data ram_rd_data read in ram starts to output data after a delay of one clock cycle, and the output data is 0, 1, 2..., which is equal to the value we wrote

insert image description here

  • Waveform analysis:

Only write once at the beginning, then no longer write, but read every so often

5. SignalTap II Online Verification

  • read status:
    insert image description here
  • analyze:

After the ram_rd_en (read enable) signal is pulled high, ram_addr starts to increase from 0, that is to say, data is read from address 0 of ram; the data read from ram, ram_rd_data, starts to output data after a delay of one clock cycle, and the output The data is 0, 1, 2..., which is equal to the value we wrote. consistent with the simulation results. And read it every once in a while.

  • Write status:

Um, I don’t know why I can’t catch the waveform of the write status waveform here, because the write is only written once after power-on, and there is no need to write again later, so I set a rising edge as the trigger condition, but he still has no waveform. It should be that the use of SignalTap II is not very proficient, and I am not familiar with some more complicated operations.


6. Summary

Today's experiment is another content on top of yesterday's single port. A simple dual port can read and write at the same time, which is more abundant than a single port. Through this experiment, the configuration of the IP core has become more and more proficient, and it is improving every day!

7. References

The above information comes from the teaching video of punctual atom or the development tutorial of Pioneer 2: atom official

Guess you like

Origin blog.csdn.net/qq_52215423/article/details/131787290