4. MESI 写命中验证
验证目标
构造一个缓存行处于 M(Modified) 状态的场景,执行一次本地写命中,检查两件事:
- 写操作只更新本地缓存数据,不产生任何总线事务(无 BusRd/BusRdX/BusUpgr/Flush 等)。
- 写完成后该行状态保持 M,数据被更新为新值。
状态与事件关系
最小可复现实现
用一个简化的 MESI 缓存模型:单核、单行、带总线事务计数器。先通过一次写未命中把行带入 M 状态,再执行本地写命中并断言。
#include <stdio.h>
#include <assert.h>
typedef enum { I, S, E, M } mesi_t;
typedef struct {
mesi_t state;
int data;
int bus_txn; /* 总线事务计数 */
} cache_t;
/* 写未命中:分配行并进入 M(简化:直接置 M,计一次总线事务) */
void write_miss(cache_t *c, int val) {
c->bus_txn++; /* BusRdX */
c->state = M;
c->data = val;
}
/* 本地写命中:M 状态下直接更新,不产生总线事务,状态保持 M */
void local_write_hit(cache_t *c, int val) {
assert(c->state == M); /* 前置条件:行处于 M */
c->data = val; /* 只改本地数据 */
/* 状态保持 M,不触发任何总线事务 */
}
int main(void) {
cache_t c = { .state = I, .data = 0, .bus_txn = 0 };
/* 步骤1:写未命中,进入 M */
write_miss(&c, 0x10);
assert(c.state == M);
int txn_after_miss = c.bus_txn; /* 应为 1 */
/* 步骤2:本地写命中 */
local_write_hit(&c, 0x20);
/* 验证1:状态保持 M */
assert(c.state == M);
/* 验证2:数据已更新 */
assert(c.data == 0x20);
/* 验证3:无新增总线事务 */
assert(c.bus_txn == txn_after_miss);
printf("PASS: state=M, data=0x%X, bus_txn=%d (no new txn)\n",
c.data, c.bus_txn);
return 0;
}
运行与判定
gcc -Wall -o mesi_m_hit mesi_m_hit.c && ./mesi_m_hit
通过条件:程序无断言失败,输出 PASS: state=M, data=0x20, bus_txn=1 (no new txn)。其中 bus_txn 在本地写命中前后保持不变,即证明 M 状态下的本地写不产生总线事务。
常见问题
- 若把
local_write_hit写成先判断状态再决定是否发总线请求,务必确认 M 分支不递增bus_txn,否则断言会失败。 - 真实 RTL 验证中,对应检查点是:写命中 M 时监视总线接口无新请求,且目录/一致性状态机不产生状态迁移。