要实现只输出访存地址、指令 PC、缓存行中的偏移量、读写操作、实际访问数据长度、L1、L2 Cache 是否命中以及 L1、L2 Cache 的索引的 DebugFlag 输出信息,你可以在 BaseCache::accessBlock() 函数中添加相应的代码,并使用 DPRINTFN() 宏输出信息。下面是一个例子:
void
BaseCache::accessBlock(Addr addr, PacketPtr pkt, CacheBlk *&blk, bool is_secure)
{
// 输出访存地址和指令 PC
DPRINTFN("Accessing address %#x, PC %#x\n", addr, pkt->req->getPC());
// 计算缓存行偏移量
unsigned blk_offset = blkSize_ & (addr >> blkSizeBits_);
// 输出缓存行偏移量
DPRINTFN("Block offset: %d\n", blk_offset);
// 输出读写操作
const char *rw = pkt->isRead() ? "Read" : "Write";
DPRINTFN("%s operation\n", rw);
// 输出实际访问数据长度
int len = pkt->getSize();
DPRINTFN("Accessing %d bytes\n", len);
// 计算索引和标记
unsigned index_bits = extractBits(addr, tagOffset_, indexSize_);
unsigned tag_bits = extractBits(addr, 0, tagOffset_);
// 输出 L1 Cache 索引和标记
DPRINTFN("L1 Cache index: %d, tag: %#x\n", index_bits, tag_bits);
// 查找缓存块
blk = tags_[index_bits]->findBlock(addr, is_secure);
// 如果 L1 Cache 命中,则输出信息
if (blk && blk->isValid()) {
// 输出 L1 Cache 命中信息
DPRINTFN("L1 Cache hit\n");
// 输出 L1 Cache 中的状态
blk->print();
// 更新 LRU
tags_[index_bits]->touchBlock(blk);
// 如果有L2 Cache,则查找 L2 Cache
if (nextLevel_) {
PacketPtr new_pkt = new Packet(pkt, is_secure);
new_pkt->senderState = blk;
// 查找 L2 Cache
nextLevel_->access(new_pkt);
// 如果 L2 Cache 命中,则输出信息
if (new_pkt->isResponse()) {
// 输出 L2 Cache 命中信息
DPRINTFN("L2 Cache hit\n");
// 解析响应数据包,更新 L1 Cache
accessAndRespond(blk, new_pkt, true, is_secure);
// 如果有共享的 L2 Cache,则将更新广播给其他 L1 Cache
if (system->cacheLineMigrator) {
system->cacheLineMigrator->broadcastWrite(addr);
}
} else {
// 如果 L2 Cache 没有命中,则从主存中加载数据到 L1 Cache,并更新 LRU
DPRINTFN("L2 Cache miss\n");
blk = handleFill(new_pkt, blk, is_secure);
tags_[index_bits]->touchBlock(blk);
}
delete new_pkt;
}
} else {
// 如果 L1 Cache 没有命中,则从 L2 Cache 或主存中加载数据到 L1 Cache
DPRINTFN("L1 Cache miss\n");
blk = handleFill(pkt, blk, is_secure);
tags_[index_bits]->touchBlock(blk);
}
}
在 `accessBlock()` 中添加以上代码后,就可以输出访存地址、指令 PC、缓存行偏移量、读写操作、实际访问数据长度、L1、L2 Cache 是否命中以及 L1、L2 Cache 的索引等信息了。你可以根据需要修改输出信息的格式和内容。
接下来,我们需要修改 src/mem/cache/tags/base.cc 文件,以便将这些信息添加到 CacheBlk 中。
具体来说,我们需要在 CacheBlk 结构体中添加成员变量,如下所示:
struct CacheBlk
{
// ...
Addr addr;
bool valid;
bool dirty;
Tick tickInserted;
// 添加以下成员变量
uint64_t instPC;
uint64_t dataSize;
bool isRead;
bool l1Hit;
bool l2Hit;
int l1Index;
int l2Index;
int blkOffset;
};
然后,在 src/mem/cache/tags/base.cc 文件的 BaseSetAssoc::accessBlock() 函数中,我们需要修改代码,以便在访问块时填充新添加的 CacheBlk 成员变量。
void
Cache::printBlk(CacheBlk *blk) const
{
// ...
DPRINTF(Cache, "%#llx: ", blk->addr);
// 添加以下代码
DPRINTF(Cache, "instPC=%#llx ", blk->instPC);
DPRINTF(Cache, "dataSize=%d ", blk->dataSize);
DPRINTF(Cache, "isRead=%d ", blk->isRead);
DPRINTF(Cache, "l1Hit=%d ", blk->l1Hit);
DPRINTF(Cache, "l2Hit=%d ", blk->l2Hit);
DPRINTF(Cache, "l1Index=%d ", blk->l1Index);
DPRINTF(Cache, "l2Index=%d ", blk->l2Index);
DPRINTF(Cache, "blkOffset=%d ", blk->blkOffset);
DPRINTF(Cache, "%s%s%s%s\n",
blk->isValid() ? "valid " : "",
blk->isDirty() ? "dirty " : "",
blk->isReferenced() ? "ref " : "",
blk->isSecure() ? "sec " : "");
}
最后,我们需要重新编译 gem5 并使用新的 debugflag 来运行模拟。具体的步骤可以参考 gem5 的官方文档。
当模拟运行结束后,我们可以在输出信息中看到新添加的成员变量。例如,以下是一些样例输出:
...
<cache access> addr=0x1d3918 instPC=0x63bd2 dataSize=4 isRead