Gem5 学习笔记(2) : Gem5 建模要点与基本思路
1. 核心建模框架┌─────────────────────────────────────────┐ │ 事件调度器 (Event Queue) │ │ 全局时间 优先级队列管理 │ └─────────────────────────────────────────┘ │ ┌───────────────┼───────────────┐ ▼ ▼ ▼ ┌────────┐ ┌────────┐ ┌──────────┐ │ CPU │◄──►│ Cache │◄──►│ Memory │ │ 模型 │ │ 模型 │ │ 控制器 │ └────────┘ └────────┘ └──────────┘ │ │ │ ▼ ▼ ▼ 产生事件 产生事件 产生事件 (执行完成) (命中/缺失) (数据返回)每个硬件模型的核心结构classHardwareComponent:def__init__(self,name,latency_params):self.namename self.stateIDLE# 状态机self.latencylatency_params# 延迟参数defprocess_event(self,event,current_time):处理输入事件返回新产生的事件列表new_events[]# 1. 根据当前状态和事件类型处理# 2. 计算完成时间当前时间 处理延迟# 3. 可能改变自身状态# 4. 生成下游事件returnnew_events2. 具体硬件建模详解 CPU / 处理器核心建模要点指令流水线、执行单元、分支预测classCPUCore:def__init__(self):self.pipeline[]# 流水线各级self.rob[]# Reorder Bufferself.pc0# 程序计数器self.stats{cycles:0,instructions:0}defprocess_event(self,event,current_time):new_events[]ifevent.typeFETCH:# 取指发送请求到I-Cachenew_events.append(Event(timecurrent_time,# 立即发送targetI-Cache,actionREAD,data{addr:self.pc,core_id:self.id}))self.pc4# 假设4字节指令elifevent.typeEXECUTE_COMPLETE:# 执行完成检查是否需要访存instrevent.data[instruction]ifinstr.is_load():# 发送读请求到D-Cachenew_events.append(Event(timecurrent_time,targetD-Cache,actionREAD,data{addr:instr.addr,core_id:self.id}))elifinstr.is_store():new_events.append(Event(timecurrent_time,targetD-Cache,actionWRITE,data{addr:instr.addr,data:instr.data}))else:# 普通ALU指令直接提交new_events.append(Event(timecurrent_time1,# 1周期提交targetself.id,actionCOMMIT))elifevent.typeCACHE_RESPONSE:# 缓存响应到达继续执行new_events.append(Event(timecurrent_time1,targetself.id,actionEXECUTE_RESUME,data{data:event.data[data]}))returnnew_events 缓存 (Cache)建模要点Tag阵列、Data阵列、MSHR (Miss Status Handling Register)、替换策略classCache:def__init__(self,size,ways,block_size,latency,next_levelNone):self.sizesize self.waysways self.block_sizeblock_size self.hit_latencylatency# 命中延迟周期或nsself.next_levelnext_level# 下一级缓存/内存self.tags{}# 地址 - CacheLineself.mshrs[]# 未完成的miss请求self.stats{hits:0,misses:0}defprocess_event(self,event,current_time):new_events[]addrevent.data[addr]block_addraddr//self.block_sizeifevent.typeREADorevent.typeWRITE:# 检查是否命中ifself.is_hit(block_addr):# 命中延迟后返回self.stats[hits]1new_events.append(Event(timecurrent_timeself.hit_latency,targetevent.data[core_id],actionCACHE_RESPONSE,data{data:self.read_data(block_addr),hit:True}))else:# 未命中检查MSHR可能需要发送到下一级self.stats[misses]1mshr_idself.allocate_mshr(addr,event)ifmshr_idisnotNone:# 新的missnew_events.append(Event(timecurrent_time,# 立即发送targetself.next_level,actionREAD,data{addr:addr,mshr_id:mshr_id,source:self.name}))# 否则合并到已有MSHR不发送新请求elifevent.typeFILL:# 从下级返回的数据# 更新缓存唤醒等待的MSHRself.fill_cache(event.data[addr],event.data[data])waiting_requestsself.clear_mshr(event.data[mshr_id])forreqinwaiting_requests:new_events.append(Event(timecurrent_timeself.hit_latency,targetreq.source,actionCACHE_RESPONSE,data{data:event.data[data],hit:False}))returnnew_eventsdefis_hit(self,block_addr):returnblock_addrinself.tagsdefallocate_mshr(self,addr,original_event):# 管理非阻塞缓存的未命中请求iflen(self.mshrs)self.max_mshrs:self.mshrs.append({addr:addr,waiting:[original_event]})returnlen(self.mshrs)-1returnNone# MSHR满需要阻塞 内存控制器 / DRAM建模要点Bank并行、行缓冲、刷新、命令调度FR-FCFSclassDRAMController:def__init__(self,config):self.banks[{row:-1,busy_until:0}for_inrange(config.banks)]self.row_buffer_hits0self.row_buffer_misses0# DRAM时序参数单位nsself.timing{tRCD:15,# RAS to CAS delaytCAS:15,# CAS latencytRP:15,# Row prechargetRAS:35,# Row active timetRC:50,# Row cycle timetBurst:4# Burst length}defprocess_event(self,event,current_time):new_events[]ifevent.typeREAD:addrevent.data[addr]bank_id,rowself.decode_addr(addr)bankself.banks[bank_id]# 计算实际完成时间考虑Bank状态completion_timecurrent_timeifbank[row]row:# 行缓冲命中直接读self.row_buffer_hits1completion_timeself.timing[tCAS]self.timing[tBurst]else:# 行缓冲未命中需要预充电 激活 读self.row_buffer_misses1ifbank[busy_until]current_time:completion_timebank[busy_until]# 等待Bank空闲completion_timeself.timing[tRP]self.timing[tRCD]\ self.timing[tCAS]self.timing[tBurst]bank[row]row# 更新行缓冲bank[busy_until]completion_time# 发送数据返回事件new_events.append(Event(timecompletion_time,targetevent.data[source],# 返回给请求的缓存actionFILL,data{addr:addr,data:memory_data,mshr_id:event.data.get(mshr_id)}))returnnew_eventsdefdecode_addr(self,addr):# 地址映射Bank选择、行选择bank_id(addr10)0x7# 假设3位Bank选择row(addr13)0x3FFF# 行地址returnbank_id,row 互联网络 (NoC / 总线)建模要点路由算法、链路带宽、缓冲区、仲裁classNetworkOnChip:def__init__(self,topologymesh,dim_x4,dim_y4):self.routers{}self.links{}self.setup_topology(topology,dim_x,dim_y)defprocess_event(self,event,current_time):new_events[]ifevent.typePACKET_INJECT:# 数据包进入网络packetevent.data[packet]srcevent.data[src]dstevent.data[dst]# 确定路由路径XY路由pathself.route_xy(src,dst)next_hoppath[1]# 下一跳路由器# 计算传输延迟链路延迟 路由器延迟link_delayself.links[(src,next_hop)][delay]router_delay2# 2周期路由决策new_events.append(Event(timecurrent_timelink_delayrouter_delay,targetfRouter-{next_hop},actionPACKET_ARRIVE,data{packet:packet,path:path[1:],dst:dst}))elifevent.typePACKET_ARRIVE:packetevent.data[packet]current_routerevent.targetifcurrent_routerevent.data[dst]:# 到达目的地向上层传递new_events.append(Event(timecurrent_time,targetpacket.destination_component,actionNETWORK_RESPONSE,data{packet:packet}))else:# 继续转发pathevent.data[path]next_hoppath[0]# ... 类似INJECT的处理returnnew_events I/O 设备 / 加速器建模要点DMA传输、中断、专用计算单元classDMAController:def__init__(self):self.channels[None]*4# 4个DMA通道self.pending_transfers[]defprocess_event(self,event,current_time):new_events[]ifevent.typeDMA_REQUEST:# 配置DMA传输channelself.allocate_channel()transfer{src:event.data[src_addr],dst:event.data[dst_addr],bytes:event.data[size],start_time:current_time,completion_time:current_timeself.calculate_time(event.data[size])}self.channels[channel]transfer# 生成完成事件new_events.append(Event(timetransfer[completion_time],targetCPU,actionDMA_COMPLETE,data{channel:channel,callback:event.data[callback]}))# 期间会生成多个内存访问事件与内存系统交互foriinrange(0,event.data[size],64):# 64字节块传输new_events.append(Event(timecurrent_timei/self.bandwidth,targetMemory,actionREAD,data{addr:event.data[src_addr]i,size:64}))returnnew_events3. 组件间交互示例一个完整的Load指令流程时间轴 → CPU: FETCH ─────────────────────────────────────────► │ (发送给I-Cache) I-Cache: READ ──────────► HIT ─────────────────────► (1ns) │ (返回指令) CPU: EXECUTE ──────────────────► │ (解析为Load发送给D-Cache) D-Cache: READ ─────► MISS ───────────► (2ns) │ (查询MSHR发送给Mem) Memory: READ ───────────────► (50ns) │ (Bank busy, 行缓冲未命中) Memory: DATA_READY ─► D-Cache: FILL ─► │ (更新缓存唤醒CPU) CPU: RESUME ─► (继续执行)4. 关键设计模式模式说明应用分层建模高层组件CPU不关心底层细节DRAM时序模块化、可替换延迟参数化所有延迟可配置支持不同技术节点DDR4 vs DDR5 vs HBM状态机每个组件维护状态IDLE/BUSY/WAITING精确建模资源冲突事件链一个事件触发一系列下游事件追踪完整操作路径统计聚合在事件处理中收集性能数据生成模拟报告这种建模方式的核心优势是解耦每个硬件只需定义收到什么事件、如何处理、产生什么新事件调度器负责全局时间推进。这使得添加新硬件类型如新型加速器只需实现其事件处理逻辑无需修改其他组件。