双口RAM定义 及多个ram verilog rtl仿真

自己写的采用这种数组形式写的RAM块占用存储器容量的大小,是满足定义的最小的2的n次方。

reg [dw-1:0] mem [(1<<aw) -1:0] /* synthesis syn_ramstyle="block_ram" */;

如果129个数据,>128需要256的ram
所以mem为[7:0],8位地址线

比方写了一个有300K的存储器块,那实际会生成一个512K的存储块,因为256K不够。

reg [dw-1:0] mem [300*1024 -1:0] 

实际上

reg [dw-1:0] mem [512*1024 -1:0] 

如果想节约空间怎么办,写一个256K的存储器RAM之后再写一个64K的存储器RAM,两种通过选择器连接形成一个新的256K + 64K的RAM。

code

module ram(
    rclk    ,raddr,dout,

    //其他信号,举例dout
    wclk,waddr,wdin,wen
    );

    //参数定义
    parameter      ADDR_W =         18; //2^8-1个
    parameter      DATA_W =         8;
    //read输入信号定义
    input               rclk    ;
    input [ ADDR_W -1:0]   raddr  ; 
    //read输出信号定义
    output[DATA_W-1:0]  dout   ;

    //write输入信号定义
    input               wclk    ;
    input               wen    ; // write enable, active high
    input [ ADDR_W -1:0]   waddr  ;     
    input [ DATA_W -1:0]   wdin  ;     


    //中间信号ram定义
    reg[DATA_W -1:0] mem[(1<<ADDR_W)-1:0]  ;//block ram
    reg [ ADDR_W -1:0] ra_buffer;
    
    
    //read 
    always  @(posedge rclk )begin
         ra_buffer<=raddr;
    end
    assign dout=mem[ra_buffer];//数据在地址后的一个周期
    //write 
    always  @(posedge wclk )begin
        if(wen)
         mem[waddr]=wdin;
    end
    initial $readmemh("init_mem",mem);
  
 
    endmodule


在这里插入图片描述
在这里插入图片描述

RAMB36E1为bram,
BUFG两个时钟,
LUT2/3/6
IBUF
在这里插入图片描述
寄存器
在这里插入图片描述

对于640480一共需要640480个数据,640*480/1024=300K,所以为了省bram空间,拆成256K+64K,用addr[18]作为分界线

初始化mem文件
通过载入ram
initial $readmemh(“bmp_256K.mem”,mem);

module vga_dpram(
    input wr_clk ,
    input  [11:0] wr_din,
    input [18:0] wr_addr,
    input   wr,

    input rd_clk,
    input [18:0] rd_addr ,
    output   [11:0] rd_dout


);
    wire[11:0]  rd_dout_256K;
    wire[11:0]  rd_dout_64K;
    //地址延迟一拍出data
    reg addr18;
    always  @(posedge rd_clk  )begin 
        addr18<=rd_addr[18];
    end

    assign rd_dout=(addr18==0)?rd_dout_256K:rd_dout_64K;
    generic_dpram_256Kx12 ram_256Kx12(
            .rclk (    rd_clk     ),
            .raddr(    rd_addr[17:0] ),
            .dout (    rd_dout_256K    ),
            .wclk (     wr_clk    ),
            .waddr(     wr_addr[17:0]    ),
            .wdin (     wr_din     ),
            .wen  (     wr&~rd_addr[18]       )
    );
    generic_dpram_64Kx12 ram_64Kx12(
            .rclk (    rd_clk     ),
            .raddr(    rd_addr[17:0]),
            .dout (    rd_dout_64K    ),
            .wclk (     wr_clk    ),
            .waddr(     wr_addr[17:0]    ),
            .wdin (     wr_din     ),
            .wen  (     wr  &rd_addr[18]      )
    );
    endmodule
 

module generic_dpram_256Kx12(

        rclk    ,raddr,dout,

    //其他信号,举例dout
    wclk,waddr,wdin,wen
    );

    //参数定义
    parameter      ADDR_W =         18;  
    parameter      DATA_W =         12;
    //read输入信号定义
    input               rclk    ;
    input [ ADDR_W -1:0]   raddr  ; 
    //read输出信号定义
    output[DATA_W-1:0]  dout   ;

    //write输入信号定义
    input               wclk    ;
    input               wen    ; // write enable, active high
    input [ ADDR_W -1:0]   waddr  ;     
    input [ DATA_W -1:0]   wdin  ;     


    //中间信号bram定义
    reg[DATA_W -1:0] mem[(1<<ADDR_W)-1:0]  ;//block ram
    reg [ ADDR_W -1:0] ra_buffer;
    
    
    //read 
    always  @(posedge rclk )begin
         ra_buffer<=raddr;
    end
    assign dout=mem[ra_buffer];//数据在地址后的一个周期
    //write 
    always  @(posedge wclk )begin
        if(wen)
         mem[waddr]=wdin;
    end
    initial $readmemh("bmp_256k.mem",mem);
  
 
endmodule


module generic_dpram_64Kx12(

        rclk    ,raddr,dout,

    //其他信号,举例dout
    wclk,waddr,wdin,wen
    );

    //参数定义
    parameter      ADDR_W =         16;  
    parameter      DATA_W =         12;
    //read输入信号定义
    input               rclk    ;
    input [ ADDR_W -1:0]   raddr  ; 
    //read输出信号定义
    output[DATA_W-1:0]  dout   ;

    //write输入信号定义
    input               wclk    ;
    input               wen    ; // write enable, active high
    input [ ADDR_W -1:0]   waddr  ;     
    input [ DATA_W -1:0]   wdin  ;     


    //中间信号bram定义
    reg[DATA_W -1:0] mem[(1<<ADDR_W)-1:0]  ;//block ram
    reg [ ADDR_W -1:0] ra_buffer;
    
    
    //read 
    always  @(posedge rclk )begin
         ra_buffer<=raddr;
    end
    assign dout=mem[ra_buffer];//数据在地址后的一个周期
    //write 
    always  @(posedge wclk )begin
        if(wen)
         mem[waddr]=wdin;
    end
    initial $readmemh("bmp_64k.mem",mem);
  
 
endmodule

在这里插入图片描述
tb
按地址读出数据,测试是否初始化数据正确

module ram_dout_tb();

 
 
    parameter CNT_LEN=640*480;
    reg rst_n;

    //uut的输入信号
    reg[11:0]  wr_din  ;
    reg[18:0] wr_addr  ;
    reg wr;
    initial begin
         wr = 0;
         wr_din=0;
         wr_addr=0;
    end

    reg rd_clk;
    reg [18:0] rd_addr ;
    wire   [11:0] rd_dout;
    always  @(posedge rd_clk or negedge rst_n)begin
        if(rst_n==1'b0)begin
            rd_addr<=0;
        end
        else if(rd_addr==CNT_LEN-1)begin
            rd_addr<=0;
        end
        else begin
            rd_addr<=rd_addr+1;
        end
    end
 
   //时钟周期,单位为ns,可在此修改时钟周期。
   parameter CYCLE    = 40;

   //复位时间,此时表示复位2个时钟周期的时间。
   parameter RST_TIME = 2 ;


   vga_dpram uut(
    .  wr_clk (    rd_clk       ),
    .   wr_din(     wr_din      ),
    .   wr_addr(    wr_addr      ),
    .   wr      (   wr               ),
                          
   .  rd_clk  (   rd_clk         ),
   .  rd_addr (   rd_addr        ),
   .  rd_dout (   rd_dout        )


   );


     //生成本地时钟25M
     initial begin
         rd_clk = 0;
         forever
         #(CYCLE/2)
         rd_clk=~rd_clk;
     end

     //产生复位信号
     initial begin
         rst_n = 1;
         #2;
         rst_n = 0;
         #(CYCLE*RST_TIME);
         rst_n = 1;
     end
endmodule

数据应该在地址后一个时钟输出。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
抽样检测正确

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值