写回 + 写分配:DCache的完整状态机
数据Cache比指令Cache复杂得多,因为它需要处理写操作。两个关键策略决定了DCache的行为:
| 策略组合 | 读命中 | 写命中 | 读未命中 | 写未命中 |
|---|---|---|---|---|
| Write-Through + No-WA | Cache | Cache+主存 | Refill | 仅主存 |
| Write-Back + WA | Cache | 仅Cache | Refill | Refill+写 |
DCache需要3个状态来处理写回和Refill:
// Lesson 37: DCache — write-back, write-allocate, 4-way 16-set
module dcache #(
parameter ADDR_W=32, INDEX_W=4, OFFSET_W=4,
parameter TAG_W = ADDR_W - INDEX_W - OFFSET_W,
parameter WAYS=4, LINE_SIZE=16,
parameter LINE_W = LINE_SIZE * 8
)(
input wire clk, rst_n,
input wire [ADDR_W-1:0] cpu_addr_i,
input wire [31:0] cpu_wdata_i,
input wire cpu_we_i,
input wire cpu_req_i,
input wire [1:0] cpu_size_i,
output reg cpu_hit_o,
output reg [31:0] cpu_rdata_o,
output reg cpu_valid_o,
// Memory interface
input wire [LINE_W-1:0] mem_rdata_i,
input wire mem_valid_i,
output reg [ADDR_W-1:0] mem_addr_o,
output reg [LINE_W-1:0] mem_wdata_o,
output reg mem_req_o,
output reg mem_we_o
);
localparam SETS = 1 << INDEX_W;
reg valid[0:WAYS-1][0:SETS-1];
reg dirty[0:WAYS-1][0:SETS-1];
reg [TAG_W-1:0] tag[0:WAYS-1][0:SETS-1];
reg [LINE_W-1:0] data[0:WAYS-1][0:SETS-1];
reg [$clog2(WAYS)-1:0] lru[0:SETS-1];
// Tag比较 + 命中检测 (同ICache)
// ... 省略,同ICache结构
// 状态机: IDLE → WRITEBACK → REFILL → IDLE
localparam S_IDLE=0, S_WB=1, S_REFILL=2;
reg [1:0] state;
reg [$clog2(WAYS)-1:0] evict_way;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
state <= S_IDLE; /* ... reset all ... */
end else case (state)
S_IDLE: begin
if (cpu_req_i && is_hit) begin
if (cpu_we_i) begin
dirty[hit_way][idx] <= 1; // 写命中→脏
// 更新行内对应word
case (off[3:2])
0: data[hit_way][idx][31:0] <= cpu_wdata_i;
1: data[hit_way][idx][63:32] <= cpu_wdata_i;
2: data[hit_way][idx][95:64] <= cpu_wdata_i;
3: data[hit_way][idx][127:96]<= cpu_wdata_i;
endcase
end
lru[idx] <= hit_way + 1;
end else if (cpu_req_i && !is_hit) begin
evict_way <= lru[idx];
if (dirty[lru[idx]][idx]) begin
state <= S_WB; // 脏→先写回
mem_we_o <= 1; mem_req_o <= 1;
mem_addr_o <= {tag[lru[idx]][idx],idx,{OFFSET_W{1'b0}}};
mem_wdata_o <= data[lru[idx]][idx];
end else begin
state <= S_REFILL; // 不脏→直接Refill
mem_req_o <= 1; mem_we_o <= 0;
mem_addr_o <= {rtag,idx,{OFFSET_W{1'b0}}};
end
end
end
S_WB: if (mem_valid_i) begin
state <= S_REFILL;
mem_req_o <= 1; mem_we_o <= 0;
mem_addr_o <= {rtag,idx,{OFFSET_W{1'b0}}};
end
S_REFILL: if (mem_valid_i) begin
valid[evict_way][idx] <= 1;
dirty[evict_way][idx] <= 0;
tag[evict_way][idx] <= rtag;
data[evict_way][idx] <= mem_rdata_i;
lru[idx] <= evict_way + 1;
state <= S_IDLE; mem_req_o <= 0;
end
endcase
end
endmodule
| 指标 | Write-Through | Write-Back |
|---|---|---|
| 写延迟 | 高(每次写等主存) | 低(只写Cache) |
| 写带宽 | 高(每写必传主存) | 低(只在替换时写回) |
| 一致性 | 简单(Cache=主存) | 复杂(需要脏位+写回) |
| 功耗 | 高 | 低 |
| 适用场景 | I/O设备、调试 | 通用处理器 |