A load to an Invalid line issues BusRd. With no other sharer the line arrives Exclusive; with a sharer it arrives Shared.
A read aimed at a Modified copy forces a writeback first. The owner supplies the data, memory is updated, and both ends settle in S.
Miss under | Request | Data source | End state (self, other) |
|---|---|---|---|
Other cache is I | BusRd | Memory | E, other stays I |
Other cache is S or E | BusRd | Memory or sharer | S, other becomes S |
Other cache is M | BusRd | Owner after writeback | S, other becomes S |
A store to an Invalid line issues BusRdX. It invalidates every copy, takes ownership, and lands in M.
A store to a Shared line issues BusUpgr. The writer waits for one acknowledgement per sharer, then enters M.
Store under | Request | Invalidations | End state |
|---|---|---|---|
E | None; silent upgrade | 0 | M |
S | BusUpgr, wait for all acks | One per sharer | M |
I | BusRdX, wait for all acks | Every valid copy | M |
M | None; already owner | 0 | M |
Acknowledgement counting is the safety mechanism. Entering M with one ack missing leaves a stale sharer behind.
Evicting M writes the line back and leaves I. Evicting E or S just leaves I with no bus action.
A remote read downgrades M to S after the writeback. Ownership moves without any eviction.
The model below implements two caches, one line, and a serializing bus. Its checker asserts single-writer after every step.
module tb_mesi_2core;
localparam [1:0] I = 2'b00, S = 2'b01, E = 2'b10, M = 2'b11;
logic [1:0] st0;
logic [1:0] st1;
logic [7:0] mem_data;
logic [7:0] d0;
logic [7:0] d1;
int bad;
int inval;
// Single writer: M or E in one cache forces I in the other.
task check_swmr;
begin
if ((st0 == M || st0 == E) && (st1 != I)) bad = bad + 1;
if ((st1 == M || st1 == E) && (st0 != I)) bad = bad + 1;
end
endtask
// Read miss path (BusRd). Clean line grants E, shared line grants S.
task core_rd(input int c);
begin
if (c == 0) begin
if (st0 == I) begin
if (st1 == I) begin
st0 = E; d0 = mem_data;
end else begin
if (st1 == M) begin
mem_data = d1;
st1 = S;
end
if (st1 == E) st1 = S;
st0 = S; d0 = mem_data;
end
end
end else begin
if (st1 == I) begin
if (st0 == I) begin
st1 = E; d1 = mem_data;
end else begin
if (st0 == M) begin
mem_data = d0;
st0 = S;
end
if (st0 == E) st0 = S;
st1 = S; d1 = mem_data;
end
end
end
check_swmr;
end
endtask
// Write path (BusRdX on miss, BusUpgr on shared hit, silent on E or M).
task core_wr(input int c, input logic [7:0] wdata);
begin
if (c == 0) begin
if (st0 == E) begin
st0 = M; d0 = wdata;
end else if (st0 == S) begin
st1 = I; inval = inval + 1;
st0 = M; d0 = wdata;
end else if (st0 == I) begin
if (st1 == M) mem_data = d1;
if (st1 != I) inval = inval + 1;
st1 = I;
st0 = M; d0 = wdata;
end else begin
d0 = wdata;
end
end else begin
if (st1 == E) begin
st1 = M; d1 = wdata;
end else if (st1 == S) begin
st0 = I; inval = inval + 1;
st1 = M; d1 = wdata;
end else if (st1 == I) begin
if (st0 == M) mem_data = d0;
if (st0 != I) inval = inval + 1;
st0 = I;
st1 = M; d1 = wdata;
end else begin
d1 = wdata;
end
end
check_swmr;
end
endtask
// Eviction path. Only M writes back.
task core_evict(input int c);
begin
if (c == 0) begin
if (st0 == M) mem_data = d0;
st0 = I;
end else begin
if (st1 == M) mem_data = d1;
st1 = I;
end
check_swmr;
end
endtask
initial begin
st0 = I; st1 = I;
mem_data = 8'h00; d0 = 8'h00; d1 = 8'h00;
bad = 0; inval = 0;
core_rd(0); // I->E, line clean
if (st0 != E) bad = bad + 1;
core_rd(1); // I->S beside E, E downgrades to S
if (st0 != S || st1 != S) bad = bad + 1;
core_wr(0, 8'hA1); // S->M upgrade, 1 invalidation
if (st0 != M || st1 != I) bad = bad + 1;
core_rd(1); // read from M: writeback, both S
if (st0 != S || st1 != S) bad = bad + 1;
if (mem_data != 8'hA1) bad = bad + 1;
core_wr(1, 8'hB2); // S->M upgrade, 1 invalidation
if (st1 != M || st0 != I) bad = bad + 1;
core_evict(1); // M->I with writeback
if (st1 != I) bad = bad + 1;
if (mem_data != 8'hB2) bad = bad + 1;
core_rd(1); // I->E again
if (st1 != E) bad = bad + 1;
core_wr(1, 8'hC3); // E->M silent, count holds
if (st1 != M) bad = bad + 1;
core_wr(0, 8'hD4); // I->M takes ownership from M
if (st0 != M || st1 != I) bad = bad + 1;
if (inval != 3) bad = bad + 1; // 4 writes minus 1 silent upgrade
if (mem_data != 8'hC3) bad = bad + 1; // C3 wrote back during handoff; D4 lives only in M
if (bad == 0) $display("PASS: 9-op MESI script holds SWMR with 3 invalidations");
else $display("FAIL: %0d checks wrong", bad);
$finish;
end
endmoduleThe script covers I-to-E, I-to-S, both upgrade kinds, one silent upgrade, writeback, and eviction. It asserts 3 invalidations for 4 writes, which equals writes minus silent upgrades.
The store to the E line. Exclusivity was already proven at read-miss time, so the E-to-M upgrade is silent.
No. Memory is allowed to be stale while some cache holds M; the C3 writeback during the handoff plus the pending writeback on eviction keep the chain exact.