You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

RAM读写顺序异常求助:写入与读取结果错位

Verilog RAM模块读写错位问题排查与修复

问题现象

向RAM的000地址写入'A'、001地址写入'B'……111地址写入'H',读取时000地址显示'B',001地址显示'C',后续结果依次错位。

原设计模块代码

module task (
  input [2:0] address_ram,
  input [7:0] data_in,
  output [(DATA_WIDTH-1):0] q_ram,
  input clk,
  input rst,
  input we
);

  parameter DATA_WIDTH = 8;
  parameter ADDR_WIDTH = 8;
  // RAM content
  reg [DATA_WIDTH-1:0] ram [2**ADDR_WIDTH-1:0];
  reg [ADDR_WIDTH-1:0] addr_r;

  always @(posedge clk or negedge rst) begin
    if (rst) begin
      // Reset condition
      addr_r <= 8'b0;
    end else begin
      // WRITE to RAM
      if (we) begin
        ram[addr_r] <= data_in;
      end

      // READ from RAM
      addr_r <= address_ram;
    end
  end
  assign   q_ram = ram[address_ram];
endmodule

原测试平台代码

// Code your testbench here
// or browse Examples
module task_tb;

  // Parameters
  parameter DATA_WIDTH = 8;
  parameter ADDR_WIDTH = 8;

  // Inputs and Outputs
  reg clk, rst, we;
  reg [DATA_WIDTH-1:0] data_in;
  reg [2:0] address_ram;
  wire [(DATA_WIDTH-1):0] q_ram_out;

  // Instantiate the home_task module
  home_task uut (
    .clk(clk),
    .rst(rst),
    .we(we),
    .address_ram(address_ram),
    .data_in(data_in),
    .q_ram(q_ram_out)
  );

  // Clock Generation
  always begin
    #5 clk = ~clk; // Toggle the clock every 5 time units
  end

  // Initial Block
  initial begin
    clk = 0;
    rst = 0; // Assert reset initially
    we = 0;
    data_in = 8'b01000001;
    address_ram = 3'b000;
    result = 1;

    // Apply reset for some time
    #10 rst = 0;
    #10;

    // Write data to RAM
    we = 1;
    address_ram = 3'b000;
    data_in = 8'b01000001; // 'A'
    #10;

    we = 1;
    address_ram = 3'b001;
    data_in = 8'b01000010; // 'B'
    #10;

    we = 1;
    address_ram = 3'b010;
    data_in = 8'b01000011; // 'C'
    #10;

    we = 1;
    address_ram = 3'b011;
    data_in = 8'b01000100; // 'D'
    #10;

    we = 1;
    address_ram = 3'b100;
    data_in = 8'b01000101; // 'E'
    #10;

    we = 1;
    address_ram = 3'b101;
    data_in = 8'b01000110; // 'F'
    #10;

    we = 1;
    address_ram = 3'b110;
    data_in = 8'b01000111; // 'G'
    #10;

    we = 1;
    address_ram = 3'b111;
    data_in = 8'b01001000; // 'H'
    #10;


     address_ram = 3'b000;
    repeat (2**3) begin
     
  
      $display("Read Data from RAM at address %b: %c", address_ram, q_ram_out);
      #10;
      address_ram = address_ram + 1;
    end
    // Add more stimulus as needed
    #100 $finish;
  end

endmodule

问题根源

  1. 写入地址错误:设计模块中写操作使用addr_r寄存器而非输入的address_ram。addr_r在时钟沿更新为当前address_ram,导致第二次写操作时,addr_r还保持上一个周期的地址,将当前数据写入了错误的地址(比如把'B'写入000地址,覆盖了之前的'A')。
  2. 冗余寄存器干扰:addr_r寄存器对异步读取逻辑完全无用,反而引入地址延迟,引发写入错位。
  3. 测试平台瑕疵:模块实例化名称不匹配(设计模块是task,测试平台用home_task)、未定义变量result、复位信号未有效触发。

修复后的代码

修复后的设计模块

module ram_task (
  input [2:0] address_ram,
  input [7:0] data_in,
  output [(DATA_WIDTH-1):0] q_ram,
  input clk,
  input rst_n,
  input we
);

  parameter DATA_WIDTH = 8;
  parameter ADDR_WIDTH = 8;
  reg [DATA_WIDTH-1:0] ram [2**ADDR_WIDTH-1:0];

  always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      // 可选:复位时初始化RAM,此处暂不做全量初始化
    end else begin
      // 直接使用输入地址作为写入目标
      if (we) begin
        ram[address_ram] <= data_in;
      end
    end
  end

  // 异步读取:直接输出对应地址的RAM内容
  assign q_ram = ram[address_ram];
endmodule

修复后的测试平台

module ram_task_tb;

  parameter DATA_WIDTH = 8;
  parameter ADDR_WIDTH = 8;

  reg clk, rst_n, we;
  reg [DATA_WIDTH-1:0] data_in;
  reg [2:0] address_ram;
  wire [(DATA_WIDTH-1):0] q_ram_out;

  ram_task uut (
    .clk(clk),
    .rst_n(rst_n),
    .we(we),
    .address_ram(address_ram),
    .data_in(data_in),
    .q_ram(q_ram_out)
  );

  // 时钟生成
  always begin
    #5 clk = ~clk;
  end

  initial begin
    clk = 0;
    rst_n = 0;
    we = 0;
    data_in = 8'b0;
    address_ram = 3'b000;

    // 释放复位
    #10 rst_n = 1;
    #10;

    // 写入数据
    we = 1;
    address_ram = 3'b000; data_in = 8'b01000001; #10; // 'A'
    address_ram = 3'b001; data_in = 8'b01000010; #10; // 'B'
    address_ram = 3'b010; data_in = 8'b01000011; #10; // 'C'
    address_ram = 3'b011; data_in = 8'b01000100; #10; // 'D'
    address_ram = 3'b100; data_in = 8'b01000101; #10; // 'E'
    address_ram = 3'b101; data_in = 8'b01000110; #10; // 'F'
    address_ram = 3'b110; data_in = 8'b01000111; #10; // 'G'
    address_ram = 3'b111; data_in = 8'b01001000; #10; // 'H'

    // 关闭写使能,开始读取
    we = 0;
    address_ram = 3'b000;
    repeat (2**3) begin
      $display("Read Data from RAM at address %b: %c", address_ram, q_ram_out);
      #10;
      address_ram = address_ram + 1;
    end

    #100 $finish;
  end

endmodule

关键修复点

  • 写入地址修正:直接使用输入的address_ram作为写入地址,确保数据写入指定位置。
  • 移除冗余逻辑:删除无用的addr_r寄存器,消除地址延迟干扰。
  • 测试平台修正:匹配模块实例化名称,添加有效复位,移除未定义变量,读取前关闭写使能。

内容的提问来源于stack exchange,提问作者Fatima Ismail

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 19:44:59