/ 在不同的消息类别上对不同的一致性消息类型进行建模。
//
// GarnetSyntheticTraffic 采用 Garnet_standalone 一致性协议
// 它对三个消息类/虚拟网络进行建模。
// 它们是:请求、转发、响应。
// 请求和转发是“控制”数据包(通常为 8 字节),
// 而响应是“数据”包(通常为 72 字节)。
//
// 数据包从测试仪进入网络的生命周期:
// (1) 该函数generatePkt()生成其中之一的数据包
// 以下 3 种类型(随机):ReadReq、INST_FETCH、WriteReq
// (2) mem/ruby/system/RubyPort.cc 将它们转换为 RubyRequestType_LD,
// 分别为 RubyRequestType_IFETCH、RubyRequestType_ST
// (3) mem/ruby/system/Sequencer.cc 将这些发送到缓存控制器
// 在一致性协议中。
// (4) Network_test-cache.sm 标签 RubyRequestType:LD,
// RubyRequestType: IFETCH 和 RubyRequestType: ST as
// 分别为请求、转发和响应事件;
// 并将它们分别注入到虚拟网络0、1和2中。
// 它立即回调定序器。
// (5) 数据包遍历网络(simple/garnet)并到达其
// 目的地(目录)和网络统计信息已更新。
// (6) Network_test-dir.sm 只是丢弃数据包。
先启动docker,然后cd 进gem5文件夹
按照官网,编译 garnet standaalone.
使用的命令行实例
这个config python文件,cpu类型是 GarnetSyntheticTraffic:
src/cpu/testers/garnet_synthetic_traffic/GarnetSyntheticTraffic.py 下,pybind了cpp的代码
garnet_synth_traffic.py中的 一大串例如 num_packets_max=args.num_packets_max, 会作为 const Params &p传递给cpp,而src/cpu/testers/garnet_synthetic_traffic/GarnetSyntheticTraffic.cc中,创建时就对成员变量初始化:
在 C++ 中,冒号 ( : )在构造函数中用于初始化成员变量和基类。这种语法称为初始化列表。初始化列表紧跟在构造函数声明的后面,并在函数体执行之前初始化类的成员。提供的代码中,GarnetSyntheticTraffic 类的构造函数使用初始化列表来初始化其成员变量和基类。
-
代码示例解释:
-
ClockedObject§:这是对基类 ClockedObject 的构造函数的调用。它使用参数 p(一个 Params 结构体)来初始化基类部分的 GarnetSyntheticTraffic 对象。
后续的每一行(例如,tickEvent([this]{ tick(); }, “GarnetSyntheticTraffic tick”, false, Event::CPU_Tick_Pri))都是成员变量的初始化。每个成员变量都使用特定的值或表达式进行初始化。例如:
tickEvent 成员使用一个 lambda 函数、一个字符串和两个布尔值进行初始化。
cachePort 使用字符串和 this 指针(指向当前对象)进行初始化。
numPacketsMax是一个int值,具体的数字初始化为p.num_packets_max.
最后一个成员变量 requestorId 是使用 p.system->getRequestorId(this) 的返回值进行初始化。
命令行有一些args 例如–injectionrate=0.01,还有 --synthetic=uniform_random ,传递给ruby createsystem.
Ruby.create_system(args, False, system)
比如 uniform_random,会(通过某种方式,目前还没解读到)传递到 src/cpu/testers/garnet_synthetic_traffic/GarnetSyntheticTraffic.cc中,
GarnetSyntheticTraffic.cc中的代码:
else if (traffic == UNIFORM_RANDOM_) {
destination = random_mt.random(0, num_destinations - 1);
.
void
GarnetSyntheticTraffic::generatePkt() 根据vnet不同,创建不同的 req .
req打包变成 PacketPtr pkt = new Packet(req, requestType);
sendPkt(pkt); 发送出去.
!cachePort.sendTimingReq(pkt) 用了 sendTimingReq.
这个函数细节在 src/mem/port.hh里:
s这里的 rc/mem/port.hh中的RequestPort::sendTimingReq 使用的 _responsePort传递进 TimingRequestProtocol::sendReq函数里,作为 *peer.
这里,从port.hh的 sendTimingReq,到下一步我们要看到 src/mem/protocol/timing.cc 中的 sendReq.
src/mem/protocol/timing.cc里, sendReq 被使用了,而 sendReq内部,则是使用了 peer->recvTimingReq(pkt).
这里TimingRequestProtocol的peer是 src/mem/protocol/timing.hh中 class TimingResponseProtocol类 , 这个类的官方注释里写了,
src/mem/ruby/system/RubyPort.cc 中会用一个 makeRequest
src/mem/ruby/system/Sequencer.cc 中 ,Sequencer 会有一个 issueRequest 的操作
``cpp
//创建一个ruby request
std::shared_ptr msg;
msg = pkt.各种操作//将pkt变成ruby request
m_mandatory_q_ptr->enqueue(msg, clockEdge(), latency);//插入 m_mandatory_q_ptr-
到这里,一个pkt 就变成msg,存进message buffer,然后变成了flits,进入了noc 网络.
其他的相近代码 没删,只是为了备用
我们点击vccode中的gotodefination
src/systemc/tlm_bridge/gem5_to_tlm.hh
或者
util/tlm/src/sc_slave_port.cc
src/mem/port.cc有bind的函数.
src/mem/tport.cc 有一串代码,本质是 schedTimingResp.
src/mem/bridge.hh
GarnetSyntheticTraffic 会打包好packet,一个requst packet准备好了后, GarnetSyntheticTraffic::sendPkt 会调用 cachePort.sendTimingReq(pkt). 这个port.sendTimingReq会调用port内部函数 TimingRequestProtocol::sendReq函数.
TimingRequestProtocol::sendReq 里会把传入的pkt 和_responsePort 一起读进来,调用 _responsePort的函数recvTimingReq,也就是这里执行的 peer->recvTimingReq(pkt),其实是 _responsePort->recvTimingReq(pkt).
这个 _responsePort每次都是会变的,取决于何时bind.
而这里,request发出的包是直接相连,或者说 "虚空连接"到response的,并没有经过network. 这里用的函数也都是port.hh或者tport.hh.
还是从 void
GarnetSyntheticTraffic::sendPkt(PacketPtr pkt) 中使用的 cachePort.sendTimingReq(pkt)开始.
只不过,这次的 cachePort 是 RubyPort了.
发req还是用protocol里的 TimingRequestProtocol::sendReq.
之前我们看的是 src/mem/tport.cc 里的
SimpleTimingPort::recvTimingReq(PacketPtr pkt)
rubyport里则有两种