FPGA——(假)双端口RAM

假双端口RAM是虽然有两个端口,但其中一个端口只负责写,一个端口只负责读。

两个端口可以同时执行但是,不能发生读写冲突(同一时刻对于同一个地址读、写是不允许的)

(根据正点原子视频学习,图片来自于正点原子讲解视频)

一、实验任务

        对0-63地址使用 A(只写)端口进行写入0-63的数据,并使用B端口(只读)读出这些数据。

二、实验思路

        需要三个模块,1用于调用编写读取模块,2用于编写写入模块,3用于调用1.2模块以及IP核模块,即1,2为子模块,3为顶层模块。机构图如下:

三、实验步骤

        1.配置RAM IP核

        首先在左侧IP catalog中搜索RAM,双击block memory generator 进行配置,将RAM的类型调整为简单双核RAM(Simple Dual Port Ram)

再分别配置port A 核port B的数据宽度和深度(这里我们配置8,64)

        确定后即可完成配置。

        2.波形图分析

        写模块

        输入:

        clk:系统时钟

        rst:系统复位

        输出:

        ram_wr_en:ram使能信号

        ram_wr_wr: 写优先信号,但在简单双端口RAM中,该信号为1即写,为0不工作

        ram_wr_addr: 地址

        ram_wr_data: 数据

        rd_flag: 读信号,为防止读写冲突,我们这里让写到31位时候开始读操作

        读模块

        输入:

        clk:系统时钟

        rst:系统复位

        rd_flag: 读信号,为防止读写冲突,我们这里让写到31位时候开始读操作

        输出:

        ram_rd_en: 读使能信号

        ram_rd_addr: 读出数据的地址

        ram_rd_data: 读出的数据值

        3.代码编写

        根据波形图对读写模块进行编程

        写模块

module  ip_2portram_write(
    input              clk,
    input              rst,
    output    reg      ram_wr_en,//使能
    output    reg      ram_wr_we,//1写0不动
    output    reg[5:0] ram_wr_addr,
    output    reg[7:0] ram_wr_data,
    output    reg      rd_flag
); 
//ram_wr_en
always @(posedge clk or negedge rst) begin
    if (rst == 1'b0)
        ram_wr_en <= 1'b0;
    else
        ram_wr_en <= 1'b1;
end

//ram_wr_we
always @(posedge clk or negedge rst) begin
    if(rst == 1'b0)
        ram_wr_we <= 1'b0;
    else 
        ram_wr_we <= 1'b1;
end

//ram_wr_addr
always @(posedge clk or negedge rst ) begin
    if(rst == 1'b0)
        ram_wr_addr <= 6'b0;
    else if (ram_wr_we==1'b1 && ram_wr_addr != 6'd63)
        ram_wr_addr <= ram_wr_addr + 6'b1;
    else 
        ram_wr_addr <= 6'b0;
end
//ram_wr_data
always @(posedge clk or negedge rst ) begin
    if(rst == 1'b0)
        ram_wr_data <= 8'b0;
    else if (ram_wr_data != 8'd63 && ram_wr_we == 1'b1)
        ram_wr_data <= ram_wr_data + 8'b1;
    else 
        ram_wr_data <= 8'b0;
end
//rd_flag 
always @(posedge clk or negedge rst ) begin
    if(rst == 1'b0)
        rd_flag <= 1'b0;
    else if (ram_wr_en == 1'b1 && ram_wr_addr == 8'd31)//?
        rd_flag <= 1'b1;
    else    
        rd_flag <= rd_flag;
end


endmodule

        读模块

module ip_2portram_rd(
    input            clk,
    input            rst,
    input            rd_flag,
    input      [7:0] ram_rd_data,
    output reg [5:0] ram_rd_addr,
    output reg       ram_rd_en
 
);
//ram_rd_en
always @(posedge clk or negedge rst) begin
    if(rst == 1'b0)
        ram_rd_en <= 1'b0;
    else if (rd_flag == 1'b1)
        ram_rd_en <= 1'b1;
    else 
        ram_rd_en <= 1'b0;
end
//ram_rd_addr
always @(posedge clk or negedge rst) begin
    if(rst == 1'b0)
        ram_rd_addr <= 6'b0;
    else if (ram_rd_en == 1'b1 && ram_rd_addr !=  6'd63 )
        ram_rd_addr <= ram_rd_addr + 6'b1;
    else
        ram_rd_addr  <= 6'b0;
end
endmodule

        顶层调用封装模块

//顶层模块,调用 read write ip_ram

module ip_ram(
    input      sys_clk,
    input      sys_rst
);
    wire         rd_flag       ;
    wire   [7:0] ram_rd_data   ;
    wire   [5:0] ram_rd_addr   ;
    wire         ram_rd_en     ;
    wire         ram_wr_en     ;
    wire         ram_wr_we     ;
    wire   [5:0] ram_wr_addr   ;
    wire   [7:0] ram_wr_data   ;
//read
ip_2portram_rd u_ip_2portram_rd(
   .clk            (sys_clk         ),
   .rst            (sys_rst         ),
   .rd_flag        (rd_flag         ),
   .ram_rd_data    (ram_rd_data     ),
   .ram_rd_addr    (ram_rd_addr     ),
   .ram_rd_en      (ram_rd_en       )
);
//write  
ip_2portram_write u_ip_2portram_write(
   .clk               (   sys_clk         ),
   .rst               (   sys_rst         ),
   .ram_wr_en         (   ram_wr_en       ),
   .ram_wr_we         (   ram_wr_we       ),
   .ram_wr_addr       (   ram_wr_addr     ),
   .ram_wr_data       (   ram_wr_data     ),
   .rd_flag           (   rd_flag         )

);

//----------- Begin Cut here for INSTANTIATION Template ---// INST_TAG
blk_mem_gen_0 u_blk_mem_gen_0 (
   .clka         (sys_clk      ),    // input wire clka
   .ena          (ram_wr_en    ),      // input wire ena
   .wea          (ram_wr_we    ),      // input wire [0 : 0] wea
   .addra        (ram_wr_addr  ),  // input wire [5 : 0] addra
   .dina         (ram_wr_data  ),    // input wire [7 : 0] dina
   .clkb         (sys_clk      ),    // input wire clkb
   .enb          (ram_rd_en    ),      // input wire enb
   .addrb        (ram_rd_addr  ),  // input wire [5 : 0] addrb
   .doutb        (ram_rd_data  )  // output wire [7 : 0] doutb
);

endmodule

        4.仿真分析

为检查结果我们采用仿真分析,因此需要编写仿真模块激励代码,由于仅有clk和rst输入,因此仿真模块代码也比较简单。

`timescale    1ns/1ns

module   tb_ip_2port_ram();

parameter clk_period = 20;

reg sys_clk;
reg sys_rst;
//初始化复位
initial begin 
    sys_clk <= 1'b0;
    sys_rst <= 1'b0;
    #200
    sys_rst <= 1'b1;
end

//初始化时钟
always #(clk_period/2) sys_clk =~sys_clk;

ip_ram u_ip_ram(
    .sys_clk       (sys_clk),
    .sys_rst       (sys_rst)
);


endmodule

        进行仿真后波形图

        写入数据成功

        读取数据成功。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
FPGA端口RAM是一种常见的存储器组件,具有两个独立的读写端口,以支持多个并行访问。每个端口都有自己的地址和数据输入/输出。 使用端口RAM可以实现一些特定的应用场景,比如并行计算、数据缓存和通信协议处理等。下面是使用FPGA端口RAM的一般步骤: 1. 首先,在FPGA开发环境中创建一个新的项目,并将所需的端口RAM模块添加到设计中。这可以通过使用硬件描述语言(如Verilog或VHDL)来完成。 2. 在设计中定义每个端口的地址输入、数据输入和数据输出信号。通常情况下,每个端口都有一个地址输入信号(用于选择要访问的存储单元)、一个数据输入信号(用于写入数据)和一个数据输出信号(用于读取数据)。 3. 根据应用需求,为每个端口定义读写操作的时序和控制逻辑。这包括读写使能信号、读写时钟信号和读写操作的控制逻辑。 4. 在FPGA开发环境中生成RTL综合文件,并对其进行综合以生成逻辑网表。 5. 将逻辑网表映射到目标FPGA设备,并生成比特流文件。 6. 将比特流文件下载到FPGA设备中,并对其进行配置。 7. 在FPGA设备上进行仿真或验证,确保端口RAM在设计中的功能和性能都符合预期。 需要注意的是,具体的实现步骤可能会因使用的FPGA开发环境和目标设备而有所不同。因此,在实际应用中,建议参考相关的FPGA开发文档和手册,以确保正确地使用端口RAM

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值