libero Simulate - ModelSim ME (三) 实战测试 双端口 RAM 读写
libero Simulate - ModelSim ME (一) 新建工程 - 所长 - 博客园
libero Simulate - ModelSim ME (二) 语法分析 - 所长 - 博客园
libero Simulate - ModelSim ME (三) 实战测试 双端口 RAM 读写
RAM 测试 和 之前的工程有区别的地方,不用新建工程 ,直接 在 libero 直接跳转到 仿真界面, 下面方法:

1、 IP 版本, RAM -Dual Port v2.2

2、IP 参数配置, 单位 8bit , 一个字节, 深度 512 ,表示 512字节的 RAM , 双端口 公用一个时钟 , 使能 仿真初始化 RAM 接口
- Pass-Through:写数据直接绕过存储阵列、立即出现在 DOUT(同周期),保证数据一致性、无旧数据残留
- Pass-Through:无输出寄存器、纯组合直通,地址 / 使能在时钟沿锁存后,数据经存储阵列直接输出,1 个时钟周期出数、零额外寄存器延迟
- Pipelined:写数据先到阵列、再经输出寄存器,第 2 个时钟周期才出最新写数据
- Pipelined:2 个时钟周期出数、多 1 级寄存器延迟,但可跑更高频率、时序更稳
- Previous DOUT:勾选,WMODE = 0 → RAM Write, Output Retained(输出保持模式);
- WMODE = 0 :写操作期间,DOUT 保持上一次读操作的有效数据,不更新为当前写入的 DIN 数据,双端口同时读写同一地址,需要避免写数据干扰读输出、保持读数据稳定
- Previous DOUT:不勾选,WMODE = 1 → Write Pass-Through(写直通模式)
- WMODE = 1 :写操作时,写入的 DIN 数据直接绕过存储阵列、立即出现在 DOUT(同周期更新),需写后立即读新数据、数据一致性要求高(如 FIFO、缓存、实时更新)
- 以上内容来自豆包

上图 我选择了 Pipelined 和 Previous DOUT , 写入和输出

官方手册里的读写时序图 Military ProASIC3/EL Low-Power Flash FPGAs Datasheet
Pipelined:读时序

Previous DOUT勾选, WMODE = 0:写时序

复位时序:

A3P250 最快读写速度:

A3P250 tAS 地址,使能,blk 建立时间;

编写 测试文件 步骤:
1、设置 时钟基准
3、 例化 RAM 模块
4、 编写 RAM 写入 时序
5、编写 RAM 读取时序
开始仿真:

读取端口使能,地址设为1 , 写入地址设为1 , 写入端口使能和写入数据0x78. 下个时钟周期 写入端口 使能关闭, 再过2个时钟周期后, 端口B 输出数据
/////////////////////////////////////////////////////////////////////////////////////////////////// // Company: <Name> // // File: testbench_ram.v // File history: // <Revision number>: <Date>: <Comments> // <Revision number>: <Date>: <Comments> // <Revision number>: <Date>: <Comments> // // Description: // // <Description here> // // Targeted device: <Family::ProASIC3> <Die::A3P1000> <Package::144 FBGA> // Author: <Name> // /////////////////////////////////////////////////////////////////////////////////////////////////// `timescale 1ns/100ps module testbench_ram(); // 设置时钟基准 parameter SYSCLK_PERIOD = 100;// 10MHZ // 定义 时钟信号和复位信号 reg SYSCLK; reg NSYSRESET; initial begin SYSCLK = 1'b0; NSYSRESET = 1'b0; end ////////////////////////////////////////////////////////////////////// // Reset Pulse ////////////////////////////////////////////////////////////////////// initial begin #(SYSCLK_PERIOD * 1 ) NSYSRESET = 1'b1; end ////////////////////////////////////////////////////////////////////// // Clock Driver ////////////////////////////////////////////////////////////////////// always @(SYSCLK) begin #(SYSCLK_PERIOD / 2.0) SYSCLK <= !SYSCLK; end // sysclk 计数 方便观察时序 reg [31:0] clk_cnt; always @(posedge SYSCLK or negedge NSYSRESET) begin if( !NSYSRESET ) begin clk_cnt <= 32'b0; end else begin clk_cnt <= clk_cnt + 32'b1; end end // RAM 读使能 对应 RW信号 高电平 parameter RAM_RW_READ_ENABLE = 1'b1; // RAM 写使能 对应 RW信号 低电平 parameter RAM_RW_WRITE_ENABLE = 1'b0; // BLK 信号使能 parameter RAM_BLK_ENABLE = 1'b0; parameter RAM_BLK_DISABLE = 1'b1; // 例化 双端口 模块 reg [8:0] ram_addr_a; reg [8:0] ram_addr_b; reg ram_blk_a; reg ram_blk_b; reg [7:0] ram_din_a; //wire [7:0] ram_dout_a; wire [7:0] ram_dout_b; ram_buff ram_0( // 512字节 读写同时钟 双端口 RAM .ADDRA(ram_addr_a), .ADDRB(ram_addr_b), .BLKA(ram_blk_a), .BLKB(ram_blk_b), .CLKAB(SYSCLK), .DINA(ram_din_a), .DINB(8'd0), .RESET(NSYSRESET), .RWA(RAM_RW_WRITE_ENABLE), .RWB(RAM_RW_READ_ENABLE), // Outputs // .DOUTA(ram_dout_a), .DOUTB(ram_dout_b) ); // 将 0x12345678 按顺序 写入 RAM // 写完成 读取 RAM reg [7:0] byte1 = 8'h78; reg [7:0] byte2 = 8'h56; reg [7:0] byte3 = 8'h34; reg [7:0] byte4 = 8'h12; reg [8:0] w_cnt = 0; reg write_done = 0; // 将数据写入到 RAM 中 always @(posedge SYSCLK or negedge NSYSRESET) begin if( !NSYSRESET ) begin w_cnt <= 9'b0; ram_blk_a <= RAM_BLK_DISABLE; ram_addr_a <= 9'b0; write_done <= 1'b0; end else begin if(w_cnt<4) begin w_cnt <= w_cnt + 9'b1; write_done <= 1'b0; ram_blk_a <= RAM_BLK_ENABLE; ram_addr_a <= w_cnt; case(w_cnt) 0: ram_din_a <= byte1; 1: ram_din_a <= byte2; 2: ram_din_a <= byte3; 3: ram_din_a <= byte4; default: ram_din_a <= 8'b0; endcase end else begin ram_blk_a <= RAM_BLK_DISABLE; write_done <= 1'b1; // w_cnt <= 9'b0; // 写完成后不再写入数据 end end end // 读取 RAM 中的数据 // 读取 RAM 会延迟2个时钟周期 reg [8:0] r_cnt = 0; reg [2:0] fsm_read; parameter FSM_READ_IDLE = 3'd0; parameter FSM_READ_START = 3'd1; parameter FSM_READ_DONE = 3'd2; always @(posedge SYSCLK or negedge NSYSRESET) begin if( !NSYSRESET ) begin r_cnt <= 9'b0; ram_blk_b <= RAM_BLK_DISABLE; ram_addr_b <= 9'b0; fsm_read <= FSM_READ_IDLE; end else begin case(fsm_read) FSM_READ_IDLE: begin ram_blk_b <= RAM_BLK_DISABLE; if(write_done) begin fsm_read <= FSM_READ_START; end end FSM_READ_START: begin if(r_cnt < 4) begin ram_blk_b <= RAM_BLK_ENABLE; ram_addr_b <= r_cnt; r_cnt <= r_cnt + 9'b1; end else begin fsm_read <= FSM_READ_DONE; end end FSM_READ_DONE: begin // 延迟两个时钟 关闭 BLK 信号 // 采用 WMODE = 0模式,输出数据要延迟2个时钟 fsm_read <= FSM_READ_IDLE; end endcase end end endmodule

总结: 通过时序仿真:
1、 写数据直接 blk_a拉低使能后,直接给地址和数据,就将数据写入到了对应地址中,
2、读数据 需要 blk_b 拉低后,给出地址后, 需要延迟 2个时钟周期 才能 流水线式 给出数据 。
浙公网安备 33010602011771号