Skip to content

Commit f43637a

Browse files
committed
Merge branch 'refactor/pipeline-package'
# Conflicts: # crates/raven-engine/src/architectures/riscv32/falcon/pipeline/inspect.rs # crates/raven-engine/src/architectures/x86_64/pipeline.rs
2 parents a38b928 + f269812 commit f43637a

47 files changed

Lines changed: 10088 additions & 1914 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

crates/raven-engine/src/architecture.rs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::capability::{
22
AddressTranslation, CacheHierarchy, InstructionCodec, MemoryInspect, PipelineControl,
3-
PipelineInspect, RegisterFile,
3+
PipelineDynamicInspect, PipelineInspect, PipelineTuning, RegisterFile,
44
};
55
use std::collections::HashMap;
66
use std::fmt;
@@ -433,6 +433,30 @@ pub trait Machine: Send + std::any::Any {
433433
None
434434
}
435435

436+
/// The datapath's adjustable properties, for a settings screen.
437+
///
438+
/// A backend with a fixed datapath answers `None` and a host shows nothing;
439+
/// one that declares a shape can answer `Some` and let a user rewire the
440+
/// bypasses, change the branch predictor or add a functional unit, and then
441+
/// watch what it does to the Gantt view.
442+
fn pipeline_tuning(&self) -> Option<&dyn PipelineTuning> {
443+
None
444+
}
445+
446+
fn pipeline_tuning_mut(&mut self) -> Option<&mut dyn PipelineTuning> {
447+
None
448+
}
449+
450+
/// The structures a dynamically scheduled model runs on — reservation
451+
/// stations, the reorder buffer, the register alias table.
452+
///
453+
/// `None` from a backend running an in-order model, which is what tells a
454+
/// host to draw stages instead of a workbench. The answer changes with the
455+
/// model, so a host asks every frame rather than once.
456+
fn pipeline_dynamic(&self) -> Option<&dyn PipelineDynamicInspect> {
457+
None
458+
}
459+
436460
/// Advance one pipeline clock. The default retires one whole instruction.
437461
fn cycle(&mut self) -> Result<CycleResult, MachineError> {
438462
let address = self.snapshot().pc;

crates/raven-engine/src/architectures/riscv32/falcon/machine/mod.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,22 @@ pub trait JournaledPipeline {
7777
fn inspect(&self) -> Option<&dyn PipelineInspect> {
7878
None
7979
}
80+
81+
/// Optional control surface: switching the model on, resetting it for a
82+
/// fresh run, redirecting it after a jump.
83+
///
84+
/// A model that answers `inspect` should answer this too — a host that can
85+
/// see a pipeline but not turn it on has to reach past the trait for the
86+
/// concrete backend, which is the one thing the capability exists to avoid.
87+
fn control(&mut self) -> Option<&mut dyn crate::capability::PipelineControl> {
88+
None
89+
}
90+
91+
/// The structures a dynamically scheduled model runs on, when the model
92+
/// running has any. `None` under an in-order one.
93+
fn dynamic(&self) -> Option<&dyn crate::capability::PipelineDynamicInspect> {
94+
None
95+
}
8096
}
8197

8298
/// The "no pipeline" instantiation: a [`Machine`] that only ever single-steps
@@ -146,6 +162,14 @@ impl<P: JournaledPipeline> Machine<P> {
146162
self.pipeline.inspect()
147163
}
148164

165+
/// Pipeline controls through the engine-level contract.
166+
///
167+
/// Like [`Self::pipeline_mut`] this does not journal: these are between-step
168+
/// mutations, not execution.
169+
pub fn pipeline_controls(&mut self) -> Option<&mut dyn crate::capability::PipelineControl> {
170+
self.pipeline.control()
171+
}
172+
149173
/// Mutable pipeline access for physical configuration and explicit resets.
150174
/// Presentation state is not stored in the pipeline. This
151175
/// does **not** journal and deliberately does **not** clear the journal:

crates/raven-engine/src/architectures/riscv32/falcon/pipeline/forwarding.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -571,7 +571,7 @@ fn emit_forward_trace(
571571
);
572572
super::sim::push_trace(state, TraceKind::Forward, prod_idx, consumer_stage, detail);
573573
state.hazard_msgs.push((
574-
HazardType::Raw,
574+
HazardType::ReadAfterWrite,
575575
format!(
576576
"BYPASS: {} via {} into {}:{} [RAW covered]",
577577
super::sim::reg_name(p_rd),
@@ -621,7 +621,7 @@ fn emit_forward_trace_for_slot(
621621
);
622622
super::sim::push_trace(state, TraceKind::Forward, prod_idx, consumer_stage, detail);
623623
state.hazard_msgs.push((
624-
HazardType::Raw,
624+
HazardType::ReadAfterWrite,
625625
format!(
626626
"BYPASS: {} via {} into {}:{} [RAW covered]",
627627
super::sim::reg_name(p_rd),

crates/raven-engine/src/architectures/riscv32/falcon/pipeline/inspect.rs

Lines changed: 21 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,12 @@
11
//! Adapter from the RV32 pipeline simulator to the engine-level observer API.
22
33
use crate::capability::{
4-
PipelineEdge, PipelineEdgeKind, PipelineHazardKind, PipelineInspect, PipelineInstructionClass,
5-
PipelineSlotView, PipelineStageRole, PipelineStageView, PipelineStats, PipelineStatus,
6-
PipelineTimelineCell, PipelineTimelineRow, PipelineTimelineState, PipelineTraceKind,
7-
PipelineTraceView, PipelineUnitView,
4+
PipelineEdge, PipelineEdgeKind, PipelineInspect, PipelineSlotView, PipelineStageRole,
5+
PipelineStageView, PipelineStats, PipelineStatus, PipelineTimelineCell, PipelineTimelineRow,
6+
PipelineTimelineState, PipelineTraceView, PipelineUnitView,
87
};
98

10-
use super::{
11-
FuKind, GanttCell, HazardType, InstrClass, PipeSlot, PipelineSimState, Stage, TraceKind,
12-
};
13-
14-
/// The one place RV32's stage names are mapped to their roles; the stage view
15-
/// and the timeline both read it, so they cannot disagree.
16-
fn stage_role(stage: Stage) -> PipelineStageRole {
17-
match stage {
18-
Stage::IF => PipelineStageRole::Fetch,
19-
Stage::ID => PipelineStageRole::Decode,
20-
Stage::EX => PipelineStageRole::Execute,
21-
Stage::MEM => PipelineStageRole::Memory,
22-
Stage::WB => PipelineStageRole::Writeback,
23-
}
24-
}
25-
26-
fn instruction_class(class: InstrClass) -> PipelineInstructionClass {
27-
match class {
28-
InstrClass::Alu => PipelineInstructionClass::Alu,
29-
InstrClass::Mul => PipelineInstructionClass::Multiply,
30-
InstrClass::Div => PipelineInstructionClass::Divide,
31-
InstrClass::Load => PipelineInstructionClass::Load,
32-
InstrClass::Store => PipelineInstructionClass::Store,
33-
InstrClass::Branch => PipelineInstructionClass::Branch,
34-
InstrClass::Jump => PipelineInstructionClass::Jump,
35-
InstrClass::System => PipelineInstructionClass::System,
36-
InstrClass::Fp => PipelineInstructionClass::FloatingPoint,
37-
InstrClass::Unknown => PipelineInstructionClass::Unknown,
38-
}
39-
}
40-
41-
fn hazard_kind(hazard: HazardType) -> PipelineHazardKind {
42-
match hazard {
43-
HazardType::Raw => PipelineHazardKind::ReadAfterWrite,
44-
HazardType::LoadUse => PipelineHazardKind::LoadUse,
45-
HazardType::BranchFlush => PipelineHazardKind::BranchFlush,
46-
HazardType::FuBusy => PipelineHazardKind::FunctionalUnitBusy,
47-
HazardType::MemLatency => PipelineHazardKind::MemoryLatency,
48-
HazardType::Waw => PipelineHazardKind::WriteAfterWrite,
49-
HazardType::War => PipelineHazardKind::WriteAfterRead,
50-
}
51-
}
9+
use super::{FuKind, GanttCell, PipeSlot, PipelineSimState, Stage, TraceKind};
5210

5311
fn is_atomic(slot: &PipeSlot) -> bool {
5412
use crate::falcon::instruction::Instruction;
@@ -75,7 +33,7 @@ fn slot_view(slot: &PipeSlot) -> PipelineSlotView<'_> {
7533
PipelineSlotView {
7634
address: u64::from(slot.pc),
7735
disassembly: &slot.disasm,
78-
class: instruction_class(slot.class),
36+
class: slot.class,
7937
destination: slot.rd.map(super::sim::reg_name),
8038
sources: [
8139
slot.rs1.map(super::sim::reg_name),
@@ -84,26 +42,12 @@ fn slot_view(slot: &PipeSlot) -> PipelineSlotView<'_> {
8442
bubble: slot.is_bubble,
8543
speculative: slot.is_speculative,
8644
predicted_taken: slot.predicted_taken,
87-
hazard: slot.hazard.map(hazard_kind),
45+
hazard: slot.hazard,
8846
atomic: is_atomic(slot),
8947
cycles_remaining: slot.fu_cycles_left,
9048
}
9149
}
9250

93-
fn slot_belongs_to_unit(slot: &PipeSlot, unit: FuKind) -> bool {
94-
match unit {
95-
FuKind::Alu => matches!(
96-
slot.class,
97-
InstrClass::Alu | InstrClass::Branch | InstrClass::Jump
98-
),
99-
FuKind::Mul => slot.class == InstrClass::Mul,
100-
FuKind::Div => slot.class == InstrClass::Div,
101-
FuKind::Fpu => slot.class == InstrClass::Fp,
102-
FuKind::Lsu => matches!(slot.class, InstrClass::Load | InstrClass::Store),
103-
FuKind::Sys => slot.class == InstrClass::System,
104-
}
105-
}
106-
10751
fn row_is_atomic(disassembly: &str) -> bool {
10852
[
10953
"lr.w",
@@ -143,6 +87,10 @@ impl PipelineInspect for PipelineSimState {
14387
branch_stalls: branch,
14488
functional_unit_stalls: unit,
14589
memory_stalls: memory,
90+
// This datapath retires in order, so a name hazard never costs it
91+
// a cycle. Only a dynamically scheduled model can report these.
92+
waw_stalls: 0,
93+
war_stalls: 0,
14694
flushes: self.flush_count,
14795
branches: self.branches_executed,
14896
}
@@ -157,7 +105,7 @@ impl PipelineInspect for PipelineSimState {
157105
Some(PipelineStageView {
158106
name: stage.label(),
159107
slot: self.stages[index].as_ref().map(slot_view),
160-
role: stage_role(stage),
108+
role: stage.role(),
161109
})
162110
}
163111

@@ -194,7 +142,7 @@ impl PipelineInspect for PipelineSimState {
194142
ex.seq != 0
195143
&& ex.seq == slot.seq
196144
&& ex.pc == slot.pc
197-
&& slot_belongs_to_unit(ex, kind)
145+
&& kind.spec().handles(ex.class)
198146
});
199147
if is_mirrored {
200148
mirrored = Some(slot);
@@ -207,26 +155,14 @@ impl PipelineInspect for PipelineSimState {
207155
active += 1;
208156
first.get_or_insert_with(|| slot_view(slot));
209157
}
210-
let latency_class = match kind {
211-
FuKind::Alu => first.map_or(PipelineInstructionClass::Alu, |slot| slot.class),
212-
FuKind::Mul => PipelineInstructionClass::Multiply,
213-
FuKind::Div => PipelineInstructionClass::Divide,
214-
FuKind::Fpu => PipelineInstructionClass::FloatingPoint,
215-
FuKind::Lsu => first.map_or(PipelineInstructionClass::Load, |slot| {
216-
if slot.class == PipelineInstructionClass::Store {
217-
PipelineInstructionClass::Store
218-
} else {
219-
PipelineInstructionClass::Load
220-
}
221-
}),
222-
FuKind::Sys => PipelineInstructionClass::System,
223-
};
224158
Some(PipelineUnitView {
225159
name: kind.label(),
226160
capacity: usize::from(self.fu_capacity[kind.index()].max(1)),
227161
active,
228162
first,
229-
latency_class,
163+
// Idle, the unit shows the work it exists for; busy, it shows what
164+
// it is actually running — a store in the LSU rather than a load.
165+
latency_class: first.map_or(kind.spec().latency_class, |slot| slot.class),
230166
// RV32's per-class latencies are tunable and live in the caller's
231167
// `PipelineTiming`, which is handed to each step rather than kept
232168
// here — so the total belongs to whoever owns that table.
@@ -240,10 +176,6 @@ impl PipelineInspect for PipelineSimState {
240176

241177
fn trace(&self, index: usize) -> Option<PipelineTraceView<'_>> {
242178
let trace = self.hazard_traces.get(index)?;
243-
let kind = match trace.kind {
244-
TraceKind::Hazard(hazard) => PipelineTraceKind::Hazard(hazard_kind(hazard)),
245-
TraceKind::Forward => PipelineTraceKind::Forward,
246-
};
247179
let detail = if trace.detail.is_empty() {
248180
match trace.kind {
249181
TraceKind::Hazard(hazard) => self
@@ -257,7 +189,7 @@ impl PipelineInspect for PipelineSimState {
257189
trace.detail.as_str()
258190
};
259191
Some(PipelineTraceView {
260-
kind,
192+
kind: trace.kind,
261193
from_stage: trace.from_stage,
262194
to_stage: trace.to_stage,
263195
detail,
@@ -277,8 +209,9 @@ impl PipelineInspect for PipelineSimState {
277209
fn timeline_row(&self, index: usize) -> Option<PipelineTimelineRow<'_>> {
278210
let row = self.gantt.get(index)?;
279211
Some(PipelineTimelineRow {
212+
address: u64::from(row.pc),
280213
disassembly: &row.disasm,
281-
class: instruction_class(row.class),
214+
class: row.class,
282215
first_cycle: row.first_cycle,
283216
cells: row.cells.len(),
284217
atomic: row_is_atomic(&row.disasm),
@@ -292,7 +225,7 @@ impl PipelineInspect for PipelineSimState {
292225
GanttCell::InStage(stage) => (
293226
stage.label(),
294227
PipelineTimelineState::Active,
295-
stage_role(stage),
228+
stage.role(),
296229
),
297230
GanttCell::InFu(_) => (
298231
"EX",
@@ -302,7 +235,7 @@ impl PipelineInspect for PipelineSimState {
302235
GanttCell::Speculative(stage) => (
303236
stage.label(),
304237
PipelineTimelineState::Speculative,
305-
stage_role(stage),
238+
stage.role(),
306239
),
307240
GanttCell::SpeculativeFu(_) => (
308241
"EX",
@@ -379,7 +312,7 @@ mod tests {
379312
fn stage_views_widen_addresses_and_name_registers() {
380313
let mut pipeline = PipelineSimState::new();
381314
let mut slot = PipeSlot::from_word(0x1234, 0x0015_8513); // addi a0, a1, 1
382-
slot.hazard = Some(HazardType::Raw);
315+
slot.hazard = Some(HazardType::ReadAfterWrite);
383316
pipeline.stages[Stage::ID as usize] = Some(slot);
384317

385318
let stage = pipeline.stage(Stage::ID as usize).unwrap();

0 commit comments

Comments
 (0)