Advance every pipeline stage by one cycle. Stages execute in reverse order (WB -> MEM -> EX -> ID -> IF) so each stage can pass its results forward within the same cycle.
(&mut self, bus: &mut SystemBus)
| 255 | let snap_id_entry: StageEntry = self.if_id.as_ref().map(|r| { |
| 256 | let mnem = decode_insn(r.raw).map(|i| i.mnemonic()).unwrap_or("???"); |
| 257 | (r.pc, mnem) |
| 258 | }); |
| 259 | |
| 260 | let old_mem_wb = self.mem_wb.take(); |
| 261 | let old_ex_mem = self.ex_mem.take(); |
| 262 | let old_id_ex = self.id_ex.take(); |
| 263 | let old_if_id = self.if_id.take(); |
| 264 | |
| 265 | // ---- WB stage ------------------------------------------------------- |
| 266 | let wb_outcome = self.stage_wb(old_mem_wb.as_ref(), bus)?; |
| 267 | match wb_outcome { |
| 268 | TickOutcome::Halted(code) => return Ok(TickOutcome::Halted(code)), |
| 269 | TickOutcome::EcallSquash => { |
| 270 | self.flush_pipeline(); |
| 271 | return Ok(TickOutcome::Continue); |
| 272 | } |
| 273 | TickOutcome::Continue => {} |
| 274 | } |
| 275 | |
| 276 | if self.memory_wait_cycles > 0 { |
| 277 | self.memory_wait_cycles -= 1; |
| 278 | self.if_id = old_if_id; |
| 279 | self.id_ex = old_id_ex; |
| 280 | self.ex_mem = old_ex_mem; |
| 281 | if self.memory_wait_cycles == 0 { |
| 282 | self.mem_wb = self.pending_mem_wb.take(); |
| 283 | } |
| 284 | self.stats.stall_cycles += 1; |
| 285 | self.stats.memory_stall_cycles += 1; |
| 286 | self.last_cycle = CpuPipelineFeed { |
| 287 | stages: [ |
| 288 | snap_id_entry, |
| 289 | snap_id_entry, |
| 290 | snap_ex_entry, |
| 291 | snap_mem_entry, |
| 292 | snap_wb_entry, |
| 293 | ], |
| 294 | stalled: true, |
| 295 | flushed: false, |
| 296 | }; |
| 297 | return Ok(TickOutcome::Continue); |
| 298 | } |
| 299 | |
| 300 | // ---- Interrupt check (precise: after WB retires, before MEM executes) ---- |
| 301 | // In-flight instructions (old_ex_mem, old_id_ex, old_if_id) are squashed if |
| 302 | // an interrupt fires; their pipeline registers are already taken out of self. |
| 303 | if let Some((irq_cause, irq_tval, irq_pc)) = self.check_pending_interrupt() { |
| 304 | // old_ex_mem / old_id_ex / old_if_id drop here - no memory side-effects yet. |
| 305 | self.take_trap(irq_cause, irq_tval, irq_pc); |
| 306 | self.last_cycle = CpuPipelineFeed { |
| 307 | stages: [ |
| 308 | None, |
| 309 | snap_id_entry, |
| 310 | snap_ex_entry, |
| 311 | snap_mem_entry, |
| 312 | snap_wb_entry, |
| 313 | ], |
| 314 | stalled: false, |
no test coverage detected