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

如何在Verilog中实现无时钟的直接映射写回LRU Cache?

无时钟无FSM实现直接映射写回Cache的思路

核心思路:用握手信号替代时钟驱动状态更新

Cache的内部状态(标签、有效位、脏位、数据块)不需要时钟触发器,而是利用主存返回的mem_done信号作为状态更新的触发条件——只有当主存完成读写操作时,才更新Cache的内部状态,以此实现时序逻辑的效果。

具体实现步骤

1. 命中/Miss判断:纯组合逻辑实时计算

直接根据当前CPU地址的索引和标签,与Cache对应索引位置的标签、有效位对比,完全不需要时钟:

// 先定义Cache的参数(根据作业要求调整)
parameter BLOCK_SIZE = 4;          // 块大小4字节
parameter CACHE_SIZE = 1024;       // Cache总大小1KB
parameter OFFSET_WIDTH = $clog2(BLOCK_SIZE);
parameter INDEX_WIDTH = $clog2(CACHE_SIZE / BLOCK_SIZE);
parameter TAG_WIDTH = 32 - INDEX_WIDTH - OFFSET_WIDTH;

wire [TAG_WIDTH-1:0] curr_tag;
wire [INDEX_WIDTH-1:0] curr_index;

assign curr_index = addr[INDEX_WIDTH + OFFSET_WIDTH - 1 : OFFSET_WIDTH];
assign curr_tag = addr[31 : INDEX_WIDTH + OFFSET_WIDTH];

// 实时判断命中
always @(*) begin
    hit_miss = (cache_tag[curr_index] == curr_tag) && cache_valid[curr_index];
end

2. 内部状态存储:组合逻辑锁存器

用Verilog的组合逻辑块(always @(*))结合条件判断实现状态保持,只有在特定条件下(命中写、mem_done触发)才更新状态,未触发时保持原有值(相当于生成组合锁存器):

// 定义Cache内部存储
reg [TAG_WIDTH-1:0] cache_tag [0 : (CACHE_SIZE/BLOCK_SIZE)-1];
reg cache_valid [0 : (CACHE_SIZE/BLOCK_SIZE)-1];
reg cache_dirty [0 : (CACHE_SIZE/BLOCK_SIZE)-1];
reg [31:0] cache_data [0 : (CACHE_SIZE/BLOCK_SIZE)-1];

always @(*) begin
    // 命中写操作:直接更新Cache数据和脏位
    if (hit_miss && rw == 1'b1) begin
        cache_data[curr_index] = wdata;
        cache_dirty[curr_index] = 1'b1;
    end

    // 主存操作完成时更新Cache状态
    if (mem_done) begin
        if (mem_rw == 1'b0) begin
            // 主存读完成:加载新块到Cache
            cache_tag[curr_index] = curr_tag;
            cache_valid[curr_index] = 1'b1;
            cache_dirty[curr_index] = 1'b0;
            cache_data[curr_index] = mem_rdata;
        end else begin
            // 主存写回完成:清除脏位
            cache_dirty[curr_index] = 1'b0;
        end
    end
end

3. 主存控制逻辑:纯组合逻辑生成请求

根据当前的命中状态、脏位状态,直接生成主存的读写控制信号,完全不需要FSM:

always @(*) begin
    // 初始化所有输出,避免生成不必要的锁存器
    mem_rw = 1'b0;
    mem_addr = 32'b0;
    mem_wdata = 32'b0;
    rdata = 32'b0;

    if (hit_miss) begin
        if (rw == 1'b0) begin
            // 读命中:返回Cache数据给CPU
            rdata = cache_data[curr_index];
        end else begin
            // 写命中:CPU写数据已更新到Cache,可返回写数据(可选)
            rdata = wdata;
        end
    end else begin
        // Miss处理
        if (cache_dirty[curr_index]) begin
            // 当前块脏,先发起写回请求
            mem_rw = 1'b1;
            // 生成写回的主存地址(标签+索引+块起始偏移)
            mem_addr = {cache_tag[curr_index], curr_index, {OFFSET_WIDTH{1'b0}}};
            mem_wdata = cache_data[curr_index];
        end else begin
            // 当前块干净,直接发起读主存请求
            mem_rw = 1'b0;
            // 生成块对齐的主存地址
            mem_addr = {addr[31:OFFSET_WIDTH], {OFFSET_WIDTH{1'b0}}};
        end
    end
end

4. 关于LRU算法的说明

直接映射Cache每个索引仅对应一个块,发生Miss时只能替换该索引下的唯一块,因此LRU算法在这里没有实际意义(没有多个块可供选择),无需额外实现LRU相关逻辑,按题目要求直接替换当前块即可。

关键注意事项

  • 所有输出信号必须设置默认值,避免Verilog编译器生成不必要的锁存器,导致逻辑错误。
  • 利用mem_done的脉冲特性(主存完成操作时短暂拉高)来触发状态更新,替代时钟的边沿触发效果。
  • 组合逻辑锁存器的使用需要确保状态更新的条件互斥,避免出现冲突赋值。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 04:09:22