RTL编码之“基于简单双端口RAM的掩码操作方法“

背景:

通常RAM存储的数据表示一组特有的信息,但是存在这样的情况,如下:

一组信息包含多个相同含义的信息,例如RAM存储位宽是8bit,每bit表示一个port端口的link状态(1:linkup;0:linkdonw)。这样每8个端口用一个地址将端口link状态存储在RAM中。

反过来考虑,要是每个端口占用一个地址,仅仅存储端口的link状态,这样会非常浪费资源。

代码实现:

 module sp_ram_mask #(
    parameter  U_DLY        = 1     ,
    parameter  ADDR_WIDTH   = 3     ,
    parameter  DATA_WIDTH   = 8     ,
    parameter  BIT_SIZE     = 1     ,
    parameter  MASK_WIDTH   = DATA_WIDTH / BIT_SIZE
 )(
    input                           clk         ,
    input                           rst_n       ,
    input                           wr_en       ,
    input       [DATA_WIDTH-1:0]    wr_data     ,
    input       [MASK_WIDTH-1:0]    mask        ,
    input                           rd_en       ,
    input       [ADDR_WIDTH-1:0]    addr        ,
    output  reg [DATA_WIDTH-1:0]    rd_data
);

// 定义本地参数
localparam DEPTH = (1 << ADDR_WIDTH);

// 定义信号量
reg     [DATA_WIDTH-1:0]    memory [DEPTH-1:0];
reg     [DATA_WIDTH-1:0]    currt_data;
reg     [DATA_WIDTH-1:0]    wr_data_real;

// 获取写数据时当前的RAM中的数据
always @ ( * ) begin
    if (wr_en == 1'b1) begin
        currt_data = memory[addr];
    end
    else begin
        currt_data = {DATA_WIDTH{1'b0}};
    end
end

//构造掩码位有效的写数据,其它位不变
always @ ( * ) begin : MASK_WRITE_DATA
    integer i;
    if (wr_en == 1'b1) begin
        for (i = 0; i < MASK_WIDTH; i = i + 1) begin
            if (mask[i] == 1'b1) begin
                wr_data_real[i * BIT_SIZE +: BIT_SIZE] = wr_data[i * BIT_SIZE +: BIT_SIZE];
            end
            else begin
                wr_data_real[i * BIT_SIZE +: BIT_SIZE] = currt_data[i * BIT_SIZE +: BIT_SIZE];
            end
        end
    end
    else begin
        wr_data_real = {DATA_WIDTH{1'b0}};
    end
end

always @ (posedge clk or negedge rst_n) begin : WRITE_DATA
    integer i;
    if (!rst_n) begin
        for (i = 0; i < DEPTH; i = i + 1) begin
            memory[i] <= #U_DLY {DATA_WIDTH{1'b0}};
        end
    end
    else if (wr_en == 1'b1) begin
        memory[addr] <= #U_DLY wr_data_real;
    end
    else;
end

always @ (posedge clk or negedge rst_n) begin : READ_DATA
    if (!rst_n) begin
        rd_data <= #U_DLY {DATA_WIDTH{1'b0}};
    end
    else if (rd_en == 1'b1) begin
        rd_data <= #U_DLY memory[addr];
    end
    else;
end

endmodule

仿真代码:

`timescale 1ns/1ps

module tb_sp_ram_mask #(
    parameter  ADDR_WIDTH   = 3     ,
    parameter  DATA_WIDTH   = 8     ,
    parameter  BIT_SIZE     = 1     ,
    parameter  MASK_WIDTH   = DATA_WIDTH / BIT_SIZE
)();

reg                         clk         ;
reg                         rst_n       ;
reg                         wr_en       ;
reg     [DATA_WIDTH-1:0]    wr_data     ;
reg     [MASK_WIDTH-1:0]    mask        ;
reg                         rd_en       ;
reg     [ADDR_WIDTH-1:0]    addr        ;
wire    [DATA_WIDTH-1:0]    rd_data     ;

`ifdef FSDB_ON
initial begin
    $fsdbAutoSwitchDumpfile(1000,"wave.fsdb",20,"fsdb_dump.log");
    //$fsdbDumpfile("tb_sp_ram_mask.fsdb"); //上面一行可用这个简单的系统任务
    $fsdbDumpvars(0); //0表示所有层次都dump 从xxx_top开始dump
    $fsdbDumpMDA(); //dump 二维数组,vcs 编译时加上 -debug_all 才行
    $fsdbDumpon;            //开始dump时间 
    $fsdbDumpflush;    //结束dump时间 ,如果没有开始和结束时间默认从仿真开始到结束
end
`endif

initial begin
    clk = 1;
    forever #0.5 clk = ~clk;
end

initial begin
    rst_n = 0;
    wr_en = 1'b0;
    rd_en = 1'b0;
    wr_data = 0;
    mask = 0;
    addr = 0;
    #9 rst_n = 1;
    #1 wr_en = 1'b1; wr_data = 8'b1111_1111; mask = 8'b1111_1111; addr = 3'd2;
    #1 wr_en = 1'b0;
    #1 rd_en = 1'b1; addr = 3'd2;
    #1 rd_en = 1'b0;
    #1 wr_en = 1'b1; wr_data = 8'b0010_0010; mask = 8'b0001_0001; addr = 3'd2;
    #1 wr_en = 1'b0;
    #1 rd_en = 1'b1; addr = 3'd2;
    #1 rd_en = 1'b0;
    #1 wr_en = 1'b1; wr_data = 8'b0111_1101; mask = 8'b1000_0010; addr = 3'd2;
    #1 wr_en = 1'b0;
    #1 rd_en = 1'b1; addr = 3'd2;
    #1 rd_en = 1'b0;
    #10000 $finish;
end


sp_ram_mask #(
    .ADDR_WIDTH ( ADDR_WIDTH    ),
    .DATA_WIDTH ( DATA_WIDTH    ),
    .BIT_SIZE   ( BIT_SIZE      ),
    .MASK_WIDTH ( MASK_WIDTH    )
)
u_sp_ram_mask (
    .clk        ( clk       ), // i
    .rst_n      ( rst_n     ), // i
    .wr_en      ( wr_en     ), // i
    .wr_data    ( wr_data   ), // i
    .mask       ( mask      ), // i
    .rd_en      ( rd_en     ), // i
    .addr       ( addr      ), // i
    .rd_data    ( rd_data   )  // o
);
endmodule

用例过程:

步骤1:地址2所有端口状态置为UP(1),然后读出结果,预期结果0xff;

步骤2:地址2的0和4端口状态置为DOWN(0),然后读出结果,预期结果0xee;

步骤3:地址2的1和7端口状态置为DOWN(0),然后读出结果,预期结果0x6c。

仿真结果:

从波形图看,结果完全符合预期。

需要读取端口的状态时,只需将端口换算成对应的地址,然后取对应bit位的值。

代码实现已经参数化,容易扩展。例如:8bit数据存储4个端口的信息,每2bit是一组,掩码位宽是4。每2bit中,高位表示是否由光模块,低位表示link状态。

若有不正确的地方,欢迎大家指正。

  • 10
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值