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

ROM硬件实现双指针:如何通过case语句在单时钟周期读取两个值?

好问题!我来给你拆解一下怎么在单时钟周期内从case实现的ROM里读出两个值——核心是要让ROM支持并行访问,下面是几个实用的方案,你可以根据自己的场景来选:

方案一:复制ROM存储体(双端口ROM)

这是最直接的思路:做两个完全一样的ROM块,每个块对应一个读地址指针。同一个时钟周期里,两个指针分别访问各自的ROM,就能同时读出两个独立的值。

比如用Verilog实现的话,大概是这样:

module dual_port_rom #(
    parameter WIDTH = 8,
    parameter DEPTH = 256
)(
    input clk,
    input [7:0] addr1, addr2,
    output reg [WIDTH-1:0] data1, data2
);

// 定义两个完全相同的ROM存储数组
reg [WIDTH-1:0] rom_block1 [0:DEPTH-1];
reg [WIDTH-1:0] rom_block2 [0:DEPTH-1];

// 初始化ROM(假设用hex文件加载数据)
initial begin
    $readmemh("rom_data.hex", rom_block1);
    $readmemh("rom_data.hex", rom_block2);
end

always @(posedge clk) begin
    data1 <= rom_block1[addr1];
    data2 <= rom_block2[addr2];
end

endmodule

优缺点:逻辑简单,时序容易收敛;但会占用双倍的存储资源,适合FPGA/ASIC资源充足的场景。

方案二:使用宽位宽ROM,合并相邻/关联地址的数值

如果你的两个读地址是固定关联的(比如总是相邻,或者差一个固定偏移),可以把ROM的位宽翻倍,每个存储单元存两个连续的值。比如原来每个单元存8位,现在改成16位,存addr和addr+1的数值。这样读的时候,只需要给一个基地址,读出宽位数据后拆分出两个目标值就行。

示例代码:

module wide_rom #(
    parameter WIDTH = 8,
    parameter DEPTH = 128  // 因为每个单元存两个值,深度减半
)(
    input clk,
    input [6:0] base_addr,
    output reg [WIDTH-1:0] data_low, data_high
);

reg [2*WIDTH-1:0] rom_block [0:DEPTH-1];

initial begin
    // 注意数据文件里每一行是两个8位值拼接成的16位(高8位是addr+1,低8位是addr)
    $readmemh("wide_rom_data.hex", rom_block);
end

always @(posedge clk) begin
    {data_high, data_low} <= rom_block[base_addr];
end

endmodule

优缺点:能节省一半的存储资源,但只适用于地址有固定关联的场景,如果两个地址是完全随机的,这个方法就不适用了。

方案三:利用FPGA原生双端口ROM资源

现在主流FPGA(比如Xilinx、Intel的)都内置了双端口存储块(比如BRAM),这些硬件本身就支持同一个时钟周期内两个独立地址的读操作。如果你是在FPGA上实现,这是最优解。

你可以直接例化厂商提供的双端口ROM IP,或者用综合工具能识别的语法来描述,比如:

module fpga_dual_port_rom #(
    parameter WIDTH = 8,
    parameter DEPTH = 256
)(
    input clk,
    input [7:0] addr1, addr2,
    output reg [WIDTH-1:0] data1, data2
);

reg [WIDTH-1:0] rom_block [0:DEPTH-1];

initial begin
    $readmemh("rom_data.hex", rom_block);
end

// 两个独立的读操作,综合工具会自动映射到双端口BRAM
always @(posedge clk) begin
    data1 <= rom_block[addr1];
end

always @(posedge clk) begin
    data2 <= rom_block[addr2];
end

endmodule

优缺点:完全利用硬件原生资源,不浪费逻辑,时序表现最好;但要注意不同厂商的综合语法可能有细微差别,最好参考对应FPGA的综合手册。

避坑提醒:不要用双case组合逻辑实现

如果你想着直接写两个独立的case块来做双读,比如:

// 非常不推荐的组合逻辑ROM双读方式
module bad_dual_read_rom(
    input [7:0] addr1, addr2,
    output reg [7:0] data1, data2
);

always @(*) begin
    case(addr1)
        8'h00: data1 = 8'h11;
        8'h01: data1 = 8'h22;
        // ... 其他地址映射
        default: data1 = 8'h00;
    endcase
end

always @(*) begin
    case(addr2)
        8'h00: data2 = 8'h11;
        8'h01: data2 = 8'h22;
        // ... 其他地址映射
        default: data2 = 8'h00;
    endcase
end

endmodule

这种方式会生成大量的组合逻辑,资源占用极高,而且时序很难收敛,只适合极小容量的ROM(比如几个地址),绝对不要用于中等或大容量的场景。


内容的提问来源于stack exchange,提问作者Aren dg

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 09:46:47