forked from libre-chip/cpu
Compare commits
10 commits
3e08a282ec
...
225ceb0dfa
| Author | SHA1 | Date | |
|---|---|---|---|
| 225ceb0dfa | |||
| 67abfa2f5d | |||
| 5558763718 | |||
| 7151841af5 | |||
| a88009a303 | |||
| ce8519b2db | |||
| 151683fbda | |||
| e0dc5d486b | |||
| fdf1e97e10 | |||
| bf2cb688c7 |
14 changed files with 447880 additions and 97509 deletions
File diff suppressed because it is too large
Load diff
311
crates/cpu/src/rename_execute_retire/rename_table.rs
Normal file
311
crates/cpu/src/rename_execute_retire/rename_table.rs
Normal file
|
|
@ -0,0 +1,311 @@
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
// See Notices.txt for copyright information
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
config::{CpuConfig, CpuConfig2PowOutRegNumWidth, CpuConfigUnitCount, PhantomConstCpuConfig},
|
||||||
|
instruction::{L2RegNum, MOpRegNum, PRegNum, UnitNum, UnitOutRegNum},
|
||||||
|
rename_execute_retire::L2RegFileLen,
|
||||||
|
};
|
||||||
|
use fayalite::{int::UIntInRangeInclusiveType, prelude::*};
|
||||||
|
|
||||||
|
#[hdl(no_static)]
|
||||||
|
pub(crate) struct RenameTableEntry<C: PhantomConstGet<CpuConfig>> {
|
||||||
|
pub(crate) l1: HdlOption<PRegNum<C>>,
|
||||||
|
pub(crate) l2: HdlOption<L2RegNum>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: PhantomConstCpuConfig> RenameTableEntry<C> {
|
||||||
|
#[hdl]
|
||||||
|
pub(crate) fn const_zero(self) -> SimValue<Self> {
|
||||||
|
#[hdl(sim)]
|
||||||
|
Self {
|
||||||
|
l1: #[hdl(sim)]
|
||||||
|
(self.l1).HdlSome(self.l1.HdlSome.const_zero()),
|
||||||
|
l2: #[hdl(sim)]
|
||||||
|
HdlNone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// make arrays dynamically-sized to avoid putting large types on the stack
|
||||||
|
#[hdl(get(|c| 1 << MOpRegNum::WIDTH))]
|
||||||
|
type MOpRegCount<C: PhantomConstGet<CpuConfig>> = DynSize;
|
||||||
|
|
||||||
|
#[hdl(no_static)]
|
||||||
|
pub(crate) struct RenameTableDebugState<C: PhantomConstGet<CpuConfig>> {
|
||||||
|
entries: ArrayType<TraceAsString<RenameTableEntry<C>>, MOpRegCount<C>>,
|
||||||
|
l1_ref_counts: ArrayType<
|
||||||
|
ArrayType<
|
||||||
|
UIntInRangeInclusiveType<ConstUsize<0>, MOpRegCount<C>>,
|
||||||
|
CpuConfig2PowOutRegNumWidth<C>,
|
||||||
|
>,
|
||||||
|
CpuConfigUnitCount<C>,
|
||||||
|
>,
|
||||||
|
l2_ref_counts:
|
||||||
|
ArrayType<UIntInRangeInclusiveType<ConstUsize<0>, MOpRegCount<C>>, L2RegFileLen<C>>,
|
||||||
|
config: C,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct RenameTable<C: PhantomConstCpuConfig> {
|
||||||
|
entries: Box<[SimValue<TraceAsString<RenameTableEntry<C>>>; 1 << MOpRegNum::WIDTH]>,
|
||||||
|
l1_ref_counts: Box<[Box<[usize]>]>,
|
||||||
|
l2_ref_counts: Box<[usize]>,
|
||||||
|
config: C,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: PhantomConstCpuConfig> Clone for RenameTable<C> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
entries: self.entries.clone(),
|
||||||
|
config: self.config.clone(),
|
||||||
|
l1_ref_counts: self.l1_ref_counts.clone(),
|
||||||
|
l2_ref_counts: self.l2_ref_counts.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn clone_from(&mut self, source: &Self) {
|
||||||
|
let Self {
|
||||||
|
entries,
|
||||||
|
l1_ref_counts,
|
||||||
|
l2_ref_counts,
|
||||||
|
config,
|
||||||
|
} = self;
|
||||||
|
entries.clone_from(&source.entries);
|
||||||
|
l1_ref_counts.clone_from(&source.l1_ref_counts);
|
||||||
|
l2_ref_counts.clone_from(&source.l2_ref_counts);
|
||||||
|
*config = source.config;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub(crate) enum RenameTableUpdate<C: PhantomConstCpuConfig> {
|
||||||
|
Write {
|
||||||
|
unrenamed_reg_num: u32,
|
||||||
|
new: SimValue<TraceAsString<RenameTableEntry<C>>>,
|
||||||
|
},
|
||||||
|
UpdateForReadL2Reg {
|
||||||
|
dest: SimValue<PRegNum<C>>,
|
||||||
|
src: SimValue<L2RegNum>,
|
||||||
|
},
|
||||||
|
UpdateForWriteL2Reg {
|
||||||
|
dest: SimValue<L2RegNum>,
|
||||||
|
src: SimValue<PRegNum<C>>,
|
||||||
|
},
|
||||||
|
DropAllL2RegFileOutputs,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: PhantomConstCpuConfig> RenameTable<C> {
|
||||||
|
pub(crate) fn new(config: C) -> Self {
|
||||||
|
let entries: Box<[SimValue<TraceAsString<RenameTableEntry<C>>>; 1 << MOpRegNum::WIDTH]> =
|
||||||
|
vec![
|
||||||
|
RenameTableEntry[config].const_zero().into_trace_as_string();
|
||||||
|
1 << MOpRegNum::WIDTH
|
||||||
|
]
|
||||||
|
.try_into()
|
||||||
|
.expect("size is known to match");
|
||||||
|
Self {
|
||||||
|
entries,
|
||||||
|
l1_ref_counts: vec![
|
||||||
|
vec![0; CpuConfig2PowOutRegNumWidth[config]].into_boxed_slice();
|
||||||
|
CpuConfigUnitCount[config]
|
||||||
|
]
|
||||||
|
.into_boxed_slice(),
|
||||||
|
l2_ref_counts: vec![0; L2RegFileLen[config]].into_boxed_slice(),
|
||||||
|
config,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub(crate) fn entries(
|
||||||
|
&self,
|
||||||
|
) -> &[SimValue<TraceAsString<RenameTableEntry<C>>>; 1 << MOpRegNum::WIDTH] {
|
||||||
|
&self.entries
|
||||||
|
}
|
||||||
|
#[hdl]
|
||||||
|
pub(crate) fn to_debug_state(&self) -> SimValue<RenameTableDebugState<C>> {
|
||||||
|
let Self {
|
||||||
|
entries,
|
||||||
|
l1_ref_counts,
|
||||||
|
l2_ref_counts,
|
||||||
|
config,
|
||||||
|
} = self;
|
||||||
|
let ty = RenameTableDebugState[*config];
|
||||||
|
#[hdl(sim)]
|
||||||
|
RenameTableDebugState::<_> {
|
||||||
|
entries: entries.to_sim_value_with_type(ty.entries),
|
||||||
|
l1_ref_counts: l1_ref_counts.to_sim_value_with_type(ty.l1_ref_counts),
|
||||||
|
l2_ref_counts: l2_ref_counts.to_sim_value_with_type(ty.l2_ref_counts),
|
||||||
|
config,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[hdl]
|
||||||
|
pub(crate) fn l1_ref_counts(&self) -> &[Box<[usize]>] {
|
||||||
|
let mut expected = vec![
|
||||||
|
vec![0usize; CpuConfig2PowOutRegNumWidth[self.config]]
|
||||||
|
.into_boxed_slice();
|
||||||
|
CpuConfigUnitCount[self.config]
|
||||||
|
];
|
||||||
|
for entry in self.entries.iter() {
|
||||||
|
#[hdl(sim)]
|
||||||
|
let RenameTableEntry::<_> { l1, l2: _ } = entry.inner();
|
||||||
|
#[hdl(sim)]
|
||||||
|
if let HdlSome(l1) = l1 {
|
||||||
|
if let Some(unit_index) = UnitNum::index_sim(&l1.unit_num) {
|
||||||
|
expected[unit_index][UnitOutRegNum::value_sim(&l1.unit_out_reg)] += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert_eq!(*expected, *self.l1_ref_counts);
|
||||||
|
&self.l1_ref_counts
|
||||||
|
}
|
||||||
|
#[hdl]
|
||||||
|
pub(crate) fn l2_ref_counts(&self) -> &[usize] {
|
||||||
|
let mut expected = vec![0usize; L2RegNum.l2_reg_count()];
|
||||||
|
for entry in self.entries.iter() {
|
||||||
|
#[hdl(sim)]
|
||||||
|
let RenameTableEntry::<_> { l1: _, l2 } = entry.inner();
|
||||||
|
#[hdl(sim)]
|
||||||
|
if let HdlSome(l2) = l2 {
|
||||||
|
expected[L2RegNum::value_sim(l2)] += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert_eq!(*expected, *self.l2_ref_counts);
|
||||||
|
&self.l2_ref_counts
|
||||||
|
}
|
||||||
|
#[hdl]
|
||||||
|
pub(crate) fn update<'a>(&mut self, update: &RenameTableUpdate<C>, rename_table_name: &str) {
|
||||||
|
let mut update_entry =
|
||||||
|
|entry: &mut SimValue<TraceAsString<RenameTableEntry<C>>>,
|
||||||
|
new: SimValue<TraceAsString<RenameTableEntry<C>>>| {
|
||||||
|
#[hdl(sim)]
|
||||||
|
let RenameTableEntry::<_> { l1, l2 } = entry.inner();
|
||||||
|
#[hdl(sim)]
|
||||||
|
if let HdlSome(l1) = l1 {
|
||||||
|
if let Some(unit_index) = UnitNum::index_sim(&l1.unit_num) {
|
||||||
|
let ref_count = &mut self.l1_ref_counts[unit_index]
|
||||||
|
[UnitOutRegNum::value_sim(&l1.unit_out_reg)];
|
||||||
|
*ref_count = ref_count.checked_sub(1).unwrap_or_else(|| {
|
||||||
|
unreachable!("{rename_table_name}: l1 ref count went negative: {l1:?}")
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[hdl(sim)]
|
||||||
|
if let HdlSome(l2) = l2 {
|
||||||
|
let ref_count = &mut self.l2_ref_counts[L2RegNum::value_sim(l2)];
|
||||||
|
*ref_count = ref_count.checked_sub(1).unwrap_or_else(|| {
|
||||||
|
unreachable!("{rename_table_name}: l2 ref count went negative: {l2:?}")
|
||||||
|
});
|
||||||
|
}
|
||||||
|
#[hdl(sim)]
|
||||||
|
let RenameTableEntry::<_> { l1, l2 } = new.inner();
|
||||||
|
#[hdl(sim)]
|
||||||
|
if let HdlSome(l1) = l1 {
|
||||||
|
if let Some(unit_index) = UnitNum::index_sim(&l1.unit_num) {
|
||||||
|
let ref_count = &mut self.l1_ref_counts[unit_index]
|
||||||
|
[UnitOutRegNum::value_sim(&l1.unit_out_reg)];
|
||||||
|
*ref_count += 1;
|
||||||
|
assert!(
|
||||||
|
*ref_count <= 1 << MOpRegNum::WIDTH,
|
||||||
|
"{rename_table_name}: l1 ref count overflowed: {l1:?}",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[hdl(sim)]
|
||||||
|
if let HdlSome(l2) = l2 {
|
||||||
|
let ref_count = &mut self.l2_ref_counts[L2RegNum::value_sim(l2)];
|
||||||
|
*ref_count += 1;
|
||||||
|
assert!(
|
||||||
|
*ref_count <= 1 << MOpRegNum::WIDTH,
|
||||||
|
"{rename_table_name}: l2 ref count overflowed: {l2:?}",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
*entry = new;
|
||||||
|
};
|
||||||
|
match update {
|
||||||
|
RenameTableUpdate::Write {
|
||||||
|
unrenamed_reg_num,
|
||||||
|
new,
|
||||||
|
} => {
|
||||||
|
if *unrenamed_reg_num == MOpRegNum::CONST_ZERO_REG_NUM {
|
||||||
|
// writing to const zero reg does nothing
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
println!("{rename_table_name}: Write: {unrenamed_reg_num:#x} <- {new:?}");
|
||||||
|
update_entry(&mut self.entries[*unrenamed_reg_num as usize], new.clone());
|
||||||
|
}
|
||||||
|
RenameTableUpdate::UpdateForReadL2Reg { dest, src } => {
|
||||||
|
let new = #[hdl(sim)]
|
||||||
|
RenameTableEntry::<_> {
|
||||||
|
l1: #[hdl(sim)]
|
||||||
|
(HdlOption[dest.ty()]).HdlSome(dest),
|
||||||
|
l2: #[hdl(sim)]
|
||||||
|
HdlSome(src),
|
||||||
|
};
|
||||||
|
for (unrenamed_reg_num, entry) in self.entries.iter_mut().enumerate() {
|
||||||
|
#[hdl(sim)]
|
||||||
|
if let HdlSome(l2) = &entry.inner().l2 {
|
||||||
|
if L2RegNum::value_sim(l2) == L2RegNum::value_sim(src) {
|
||||||
|
println!(
|
||||||
|
"{rename_table_name}: UpdateForReadL2Reg: {unrenamed_reg_num:#x} \
|
||||||
|
updating from {entry:?} to {new:?}",
|
||||||
|
);
|
||||||
|
#[hdl(sim)]
|
||||||
|
if let HdlSome(_) = &entry.inner().l1 {
|
||||||
|
unreachable!("l1 should be HdlNone: {entry:?}");
|
||||||
|
}
|
||||||
|
update_entry(entry, new.to_trace_as_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RenameTableUpdate::UpdateForWriteL2Reg { dest, src } => {
|
||||||
|
let new = #[hdl(sim)]
|
||||||
|
RenameTableEntry::<_> {
|
||||||
|
l1: #[hdl(sim)]
|
||||||
|
(HdlOption[src.ty()]).HdlNone(),
|
||||||
|
l2: #[hdl(sim)]
|
||||||
|
HdlSome(dest),
|
||||||
|
};
|
||||||
|
for (unrenamed_reg_num, entry) in self.entries.iter_mut().enumerate() {
|
||||||
|
#[hdl(sim)]
|
||||||
|
if let HdlSome(l1) = &entry.inner().l1 {
|
||||||
|
if l1 == src {
|
||||||
|
println!(
|
||||||
|
"{rename_table_name}: UpdateForWriteL2Reg: {unrenamed_reg_num:#x} \
|
||||||
|
updating from {entry:?} to {new:?}",
|
||||||
|
);
|
||||||
|
#[hdl(sim)]
|
||||||
|
if let HdlSome(_) = &entry.inner().l2 {
|
||||||
|
unreachable!("l2 should be HdlNone: {entry:?}");
|
||||||
|
}
|
||||||
|
update_entry(entry, new.to_trace_as_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RenameTableUpdate::DropAllL2RegFileOutputs => {
|
||||||
|
for (unrenamed_reg_num, entry) in self.entries.iter_mut().enumerate() {
|
||||||
|
#[hdl(sim)]
|
||||||
|
if let HdlSome(_) = &entry.inner().l1 {
|
||||||
|
#[hdl(sim)]
|
||||||
|
if let HdlSome(_) = &entry.inner().l2 {
|
||||||
|
let mut new = entry.inner().clone();
|
||||||
|
new.l1 = #[hdl(sim)]
|
||||||
|
(new.l1.ty()).HdlNone();
|
||||||
|
println!(
|
||||||
|
"{rename_table_name}: DropAllL2RegFileOutputs: {unrenamed_reg_num:#x} \
|
||||||
|
updating from {entry:?} to {new:?}",
|
||||||
|
);
|
||||||
|
update_entry(entry, new.to_trace_as_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[hdl]
|
||||||
|
pub(crate) fn used_unit_out_reg_count(&self, unit_index: usize) -> usize {
|
||||||
|
self.l1_ref_counts()[unit_index]
|
||||||
|
.iter()
|
||||||
|
.filter(|ref_count| **ref_count != 0)
|
||||||
|
.count()
|
||||||
|
}
|
||||||
|
}
|
||||||
564
crates/cpu/src/rename_execute_retire/reorder_buffer.rs
Normal file
564
crates/cpu/src/rename_execute_retire/reorder_buffer.rs
Normal file
|
|
@ -0,0 +1,564 @@
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
// See Notices.txt for copyright information
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
config::{
|
||||||
|
CpuConfig, CpuConfig2PowOutRegNumWidth, CpuConfigRobSize, CpuConfigUnitCount,
|
||||||
|
PhantomConstCpuConfig,
|
||||||
|
},
|
||||||
|
instruction::{
|
||||||
|
COMMON_MOP_SRC_LEN, L2RegNum, L2RegisterFileMOp, MOp, MOpTrait, PRegNum, UnitNum,
|
||||||
|
UnitOutRegNum,
|
||||||
|
},
|
||||||
|
next_pc::SimValueDefault,
|
||||||
|
rename_execute_retire::{
|
||||||
|
L2RegFileLen, MOpId, MOpInUnitState, MOpInstance, NextPcPredictorOp, RenamedMOp,
|
||||||
|
UnitCausedCancel, rename_table::RenameTableUpdate, zeroed,
|
||||||
|
},
|
||||||
|
util::array_vec::ArrayVec,
|
||||||
|
};
|
||||||
|
use fayalite::{
|
||||||
|
int::{UIntInRangeInclusiveType, UIntInRangeType},
|
||||||
|
prelude::*,
|
||||||
|
ty::StaticType,
|
||||||
|
};
|
||||||
|
use std::{collections::VecDeque, mem};
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
type SimOnlyMOpInUnitState = SimOnly<MOpInUnitState>;
|
||||||
|
|
||||||
|
#[hdl(no_static)]
|
||||||
|
struct RobEntryDebugState<C: PhantomConstGet<CpuConfig>> {
|
||||||
|
/// See [`RobEntry::is_register_fence`]
|
||||||
|
is_register_fence: Bool,
|
||||||
|
/// See [`RobEntry::is_register_fence`]
|
||||||
|
done_waiting_for_register_fences: Bool,
|
||||||
|
mop: MOpInstance<RenamedMOp<C>>,
|
||||||
|
unit_index: UIntInRangeType<ConstUsize<0>, CpuConfigUnitCount<C>>,
|
||||||
|
mop_in_unit_state: SimOnlyMOpInUnitState,
|
||||||
|
is_speculative: Bool,
|
||||||
|
all_prior_mops_finished_and_or_caused_cancel: Bool,
|
||||||
|
output: HdlOption<NextPcPredictorOp<C>>,
|
||||||
|
caused_cancel: HdlOption<UnitCausedCancel<C>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: PhantomConstCpuConfig> SimValueDefault for RobEntryDebugState<C> {
|
||||||
|
#[hdl]
|
||||||
|
fn sim_value_default(self) -> SimValue<Self> {
|
||||||
|
let Self {
|
||||||
|
mop,
|
||||||
|
is_register_fence: _,
|
||||||
|
done_waiting_for_register_fences: _,
|
||||||
|
unit_index,
|
||||||
|
mop_in_unit_state: _,
|
||||||
|
is_speculative: _,
|
||||||
|
all_prior_mops_finished_and_or_caused_cancel: _,
|
||||||
|
output,
|
||||||
|
caused_cancel,
|
||||||
|
} = self;
|
||||||
|
#[hdl(sim)]
|
||||||
|
Self {
|
||||||
|
mop: zeroed(mop),
|
||||||
|
is_register_fence: false,
|
||||||
|
done_waiting_for_register_fences: false,
|
||||||
|
unit_index: zeroed(unit_index),
|
||||||
|
mop_in_unit_state: SimOnlyValue::default(),
|
||||||
|
is_speculative: false,
|
||||||
|
all_prior_mops_finished_and_or_caused_cancel: false,
|
||||||
|
output: #[hdl(sim)]
|
||||||
|
output.HdlNone(),
|
||||||
|
caused_cancel: #[hdl(sim)]
|
||||||
|
caused_cancel.HdlNone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct RobEntry<C: PhantomConstCpuConfig> {
|
||||||
|
/// Block this and all later µOps until all prior µOps are finished and didn't cause a cancel.
|
||||||
|
/// Not to be confused with memory fences.
|
||||||
|
pub(crate) is_register_fence: bool,
|
||||||
|
/// See [`Self::is_register_fence`]
|
||||||
|
pub(crate) done_waiting_for_register_fences: bool,
|
||||||
|
pub(crate) mop: SimValue<MOpInstance<RenamedMOp<C>>>,
|
||||||
|
pub(crate) unit_index: usize,
|
||||||
|
pub(crate) mop_in_unit_state: MOpInUnitState,
|
||||||
|
pub(crate) is_speculative: bool,
|
||||||
|
pub(crate) all_prior_mops_finished_and_or_caused_cancel: bool,
|
||||||
|
pub(crate) output: Option<SimValue<NextPcPredictorOp<C>>>,
|
||||||
|
pub(crate) caused_cancel: Option<SimValue<UnitCausedCancel<C>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: PhantomConstCpuConfig> RobEntry<C> {
|
||||||
|
pub(crate) fn new(
|
||||||
|
mop: SimValue<MOpInstance<RenamedMOp<C>>>,
|
||||||
|
unit_index: usize,
|
||||||
|
is_register_fence: bool,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
mop,
|
||||||
|
is_register_fence,
|
||||||
|
done_waiting_for_register_fences: false,
|
||||||
|
unit_index,
|
||||||
|
mop_in_unit_state: MOpInUnitState::NotYetEnqueued,
|
||||||
|
is_speculative: true,
|
||||||
|
all_prior_mops_finished_and_or_caused_cancel: false,
|
||||||
|
output: None,
|
||||||
|
caused_cancel: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn dest_reg(&self) -> Option<&SimValue<PRegNum<C>>> {
|
||||||
|
let dest_reg = MOpTrait::dest_reg_sim_ref(self.mop.mop.inner());
|
||||||
|
let unit_index = UnitNum::index_sim(&dest_reg.unit_num)?;
|
||||||
|
assert_eq!(unit_index, self.unit_index);
|
||||||
|
Some(dest_reg)
|
||||||
|
}
|
||||||
|
fn unit_out_reg(&self) -> Option<&SimValue<UnitOutRegNum<C>>> {
|
||||||
|
Some(&self.dest_reg()?.unit_out_reg)
|
||||||
|
}
|
||||||
|
pub(crate) fn unit_out_reg_index(&self) -> Option<usize> {
|
||||||
|
Some(UnitOutRegNum::value_sim(self.unit_out_reg()?))
|
||||||
|
}
|
||||||
|
#[hdl]
|
||||||
|
fn debug_state(&self, config: C) -> SimValue<RobEntryDebugState<C>> {
|
||||||
|
let Self {
|
||||||
|
is_register_fence,
|
||||||
|
done_waiting_for_register_fences,
|
||||||
|
mop,
|
||||||
|
unit_index,
|
||||||
|
mop_in_unit_state,
|
||||||
|
is_speculative,
|
||||||
|
all_prior_mops_finished_and_or_caused_cancel,
|
||||||
|
output,
|
||||||
|
caused_cancel,
|
||||||
|
} = self;
|
||||||
|
let ret_ty = RobEntryDebugState[config];
|
||||||
|
#[hdl(sim)]
|
||||||
|
RobEntryDebugState::<C> {
|
||||||
|
is_register_fence,
|
||||||
|
done_waiting_for_register_fences,
|
||||||
|
mop,
|
||||||
|
unit_index: unit_index.into_sim_value_with_type(ret_ty.unit_index),
|
||||||
|
mop_in_unit_state: SimOnlyValue::new(*mop_in_unit_state),
|
||||||
|
is_speculative,
|
||||||
|
all_prior_mops_finished_and_or_caused_cancel,
|
||||||
|
output: output.into_sim_value_with_type(ret_ty.output),
|
||||||
|
caused_cancel: caused_cancel.into_sim_value_with_type(ret_ty.caused_cancel),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[hdl]
|
||||||
|
fn for_each_reg<S>(
|
||||||
|
&self,
|
||||||
|
mut shared_state: S,
|
||||||
|
mut l1_reg: impl FnMut(&mut S, &SimValue<PRegNum<C>>),
|
||||||
|
mut l2_reg: impl FnMut(&mut S, &SimValue<L2RegNum>),
|
||||||
|
) {
|
||||||
|
l1_reg(
|
||||||
|
&mut shared_state,
|
||||||
|
MOpTrait::dest_reg_sim_ref(self.mop.mop.inner()),
|
||||||
|
);
|
||||||
|
MOpTrait::for_each_src_reg_sim_ref(self.mop.mop.inner(), &mut |l1, _| {
|
||||||
|
l1_reg(&mut shared_state, l1);
|
||||||
|
});
|
||||||
|
#[hdl(sim)]
|
||||||
|
if let RenamedMOp::<_>::TransformedMove(mop) = self.mop.mop.inner() {
|
||||||
|
#[hdl(sim)]
|
||||||
|
match mop {
|
||||||
|
L2RegisterFileMOp::<_, _>::ReadL2Reg(v) => {
|
||||||
|
l2_reg(&mut shared_state, &v.common.imm);
|
||||||
|
}
|
||||||
|
L2RegisterFileMOp::<_, _>::WriteL2Reg(v) => {
|
||||||
|
l2_reg(&mut shared_state, &v.common.imm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
struct RobEntriesDebugState {
|
||||||
|
unrenamed: MOpInstance<MOp>,
|
||||||
|
/// number of renamed µOps that this unrenamed µOp corresponds to
|
||||||
|
renamed_entries_len: UInt<8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SimValueDefault for RobEntriesDebugState {
|
||||||
|
#[hdl]
|
||||||
|
fn sim_value_default(self) -> SimValue<Self> {
|
||||||
|
let Self {
|
||||||
|
unrenamed,
|
||||||
|
renamed_entries_len,
|
||||||
|
} = self;
|
||||||
|
#[hdl(sim)]
|
||||||
|
Self {
|
||||||
|
unrenamed: zeroed(unrenamed),
|
||||||
|
renamed_entries_len: renamed_entries_len.sim_value_default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct RobEntries<C: PhantomConstCpuConfig> {
|
||||||
|
unrenamed: SimValue<MOpInstance<MOp>>,
|
||||||
|
rename_table_updates: Vec<RenameTableUpdate<C>>,
|
||||||
|
renamed_entries: VecDeque<RobEntry<C>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: PhantomConstCpuConfig> RobEntries<C> {
|
||||||
|
#[hdl]
|
||||||
|
fn debug_state(&self) -> SimValue<RobEntriesDebugState> {
|
||||||
|
let Self {
|
||||||
|
unrenamed,
|
||||||
|
rename_table_updates: _,
|
||||||
|
renamed_entries,
|
||||||
|
} = self;
|
||||||
|
#[hdl(sim)]
|
||||||
|
RobEntriesDebugState {
|
||||||
|
unrenamed,
|
||||||
|
renamed_entries_len: u8::try_from(renamed_entries.len())
|
||||||
|
.expect("renamed_entries.len() should fit in u8"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[hdl(get(|c| c.rob_size.get() * (1 + COMMON_MOP_SRC_LEN)))]
|
||||||
|
type L1RegMaxRefCount<C: PhantomConstGet<CpuConfig>> = DynSize;
|
||||||
|
|
||||||
|
#[hdl(no_static)]
|
||||||
|
pub(crate) struct ReorderBufferDebugState<C: PhantomConstGet<CpuConfig>> {
|
||||||
|
next_renamed_mop_id: MOpId,
|
||||||
|
entries: ArrayVec<RobEntriesDebugState, CpuConfigRobSize<C>>,
|
||||||
|
incomplete_back_entry: HdlOption<RobEntriesDebugState>,
|
||||||
|
renamed: ArrayVec<RobEntryDebugState<C>, CpuConfigRobSize<C>>,
|
||||||
|
l1_reg_ref_counts: ArrayType<
|
||||||
|
ArrayType<
|
||||||
|
UIntInRangeInclusiveType<ConstUsize<0>, L1RegMaxRefCount<C>>,
|
||||||
|
CpuConfig2PowOutRegNumWidth<C>,
|
||||||
|
>,
|
||||||
|
CpuConfigUnitCount<C>,
|
||||||
|
>,
|
||||||
|
l2_reg_ref_counts:
|
||||||
|
ArrayType<UIntInRangeInclusiveType<ConstUsize<0>, CpuConfigRobSize<C>>, L2RegFileLen<C>>,
|
||||||
|
config: C,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: PhantomConstCpuConfig> SimValueDefault for ReorderBufferDebugState<C> {
|
||||||
|
#[hdl]
|
||||||
|
fn sim_value_default(self) -> SimValue<Self> {
|
||||||
|
let Self {
|
||||||
|
next_renamed_mop_id,
|
||||||
|
entries,
|
||||||
|
incomplete_back_entry,
|
||||||
|
renamed,
|
||||||
|
l1_reg_ref_counts,
|
||||||
|
l2_reg_ref_counts,
|
||||||
|
config,
|
||||||
|
} = self;
|
||||||
|
#[hdl(sim)]
|
||||||
|
Self {
|
||||||
|
next_renamed_mop_id: next_renamed_mop_id.sim_value_default(),
|
||||||
|
entries: entries.sim_value_default(),
|
||||||
|
incomplete_back_entry: incomplete_back_entry.sim_value_default(),
|
||||||
|
renamed: renamed.sim_value_default(),
|
||||||
|
l1_reg_ref_counts: zeroed(l1_reg_ref_counts),
|
||||||
|
l2_reg_ref_counts: zeroed(l2_reg_ref_counts),
|
||||||
|
config,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct ReorderBuffer<C: PhantomConstCpuConfig> {
|
||||||
|
next_renamed_mop_id: SimValue<MOpId>,
|
||||||
|
entries: VecDeque<RobEntries<C>>,
|
||||||
|
incomplete_back_entry: Option<RobEntries<C>>,
|
||||||
|
l1_reg_ref_counts: Box<[Box<[usize]>]>,
|
||||||
|
l2_reg_ref_counts: Box<[usize]>,
|
||||||
|
config: C,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: PhantomConstCpuConfig> ReorderBuffer<C> {
|
||||||
|
pub(crate) fn new(config: C) -> Self {
|
||||||
|
Self {
|
||||||
|
next_renamed_mop_id: MOpId.zero().into_sim_value(),
|
||||||
|
entries: VecDeque::new(),
|
||||||
|
incomplete_back_entry: None,
|
||||||
|
l1_reg_ref_counts: vec![
|
||||||
|
vec![0; CpuConfig2PowOutRegNumWidth[config]].into_boxed_slice();
|
||||||
|
CpuConfigUnitCount[config]
|
||||||
|
]
|
||||||
|
.into_boxed_slice(),
|
||||||
|
l2_reg_ref_counts: vec![0; L2RegNum.l2_reg_count()].into_boxed_slice(),
|
||||||
|
config,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[hdl]
|
||||||
|
pub(crate) fn l1_reg_ref_counts(&self) -> &[Box<[usize]>] {
|
||||||
|
let mut expected = vec![
|
||||||
|
vec![0usize; CpuConfig2PowOutRegNumWidth[self.config]]
|
||||||
|
.into_boxed_slice();
|
||||||
|
CpuConfigUnitCount[self.config]
|
||||||
|
];
|
||||||
|
for entry in self.renamed() {
|
||||||
|
entry.for_each_reg(
|
||||||
|
(),
|
||||||
|
|(), l1| {
|
||||||
|
if let Some(unit_index) = UnitNum::index_sim(&l1.unit_num) {
|
||||||
|
expected[unit_index][UnitOutRegNum::value_sim(&l1.unit_out_reg)] += 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|(), _l2| {},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
assert_eq!(*expected, *self.l1_reg_ref_counts);
|
||||||
|
&self.l1_reg_ref_counts
|
||||||
|
}
|
||||||
|
#[hdl]
|
||||||
|
pub(crate) fn l2_reg_ref_counts(&self) -> &[usize] {
|
||||||
|
let mut expected = vec![0usize; L2RegNum.l2_reg_count()];
|
||||||
|
for entry in self.renamed() {
|
||||||
|
entry.for_each_reg(
|
||||||
|
(),
|
||||||
|
|(), _l1| {},
|
||||||
|
|(), l2| expected[L2RegNum::value_sim(l2)] += 1,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
assert_eq!(*expected, *self.l2_reg_ref_counts);
|
||||||
|
&self.l2_reg_ref_counts
|
||||||
|
}
|
||||||
|
#[hdl]
|
||||||
|
pub(crate) fn debug_state(&self) -> SimValue<ReorderBufferDebugState<C>> {
|
||||||
|
let Self {
|
||||||
|
next_renamed_mop_id,
|
||||||
|
entries,
|
||||||
|
incomplete_back_entry,
|
||||||
|
l1_reg_ref_counts,
|
||||||
|
l2_reg_ref_counts,
|
||||||
|
config,
|
||||||
|
} = self;
|
||||||
|
let ty = ReorderBufferDebugState[*config];
|
||||||
|
#[hdl(sim)]
|
||||||
|
ReorderBufferDebugState::<_> {
|
||||||
|
next_renamed_mop_id,
|
||||||
|
entries: ty
|
||||||
|
.entries
|
||||||
|
.from_iter_sim(
|
||||||
|
zeroed(StaticType::TYPE),
|
||||||
|
entries.iter().map(RobEntries::debug_state),
|
||||||
|
)
|
||||||
|
.expect("known to fit"),
|
||||||
|
incomplete_back_entry: incomplete_back_entry.as_ref().map(|v| v.debug_state()),
|
||||||
|
renamed: ty
|
||||||
|
.renamed
|
||||||
|
.from_iter_sim(
|
||||||
|
ty.renamed.element().sim_value_default(),
|
||||||
|
self.renamed().map(|v| v.debug_state(*config)),
|
||||||
|
)
|
||||||
|
.ok()
|
||||||
|
.expect("known to fit"),
|
||||||
|
l1_reg_ref_counts: l1_reg_ref_counts.to_sim_value_with_type(ty.l1_reg_ref_counts),
|
||||||
|
l2_reg_ref_counts: l2_reg_ref_counts.to_sim_value_with_type(ty.l2_reg_ref_counts),
|
||||||
|
config,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub(crate) fn unrenamed_len(&self) -> usize {
|
||||||
|
self.entries.len()
|
||||||
|
}
|
||||||
|
pub(crate) fn unrenamed(
|
||||||
|
&self,
|
||||||
|
) -> impl DoubleEndedIterator<Item = &SimValue<MOpInstance<MOp>>> + Clone {
|
||||||
|
self.entries.iter().map(|v| &v.unrenamed)
|
||||||
|
}
|
||||||
|
fn retire_groups_unrenamed_ranges(
|
||||||
|
&self,
|
||||||
|
) -> impl Clone + Iterator<Item = std::ops::Range<usize>> {
|
||||||
|
let mut next_group_start = 0;
|
||||||
|
self.entries
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.filter_map(move |(index, entry)| {
|
||||||
|
if *entry.unrenamed.is_last_mop_in_insn {
|
||||||
|
let group_start = next_group_start;
|
||||||
|
next_group_start = index + 1;
|
||||||
|
Some(group_start..next_group_start)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pub(crate) fn retire_groups(
|
||||||
|
&self,
|
||||||
|
) -> impl Clone + Iterator<Item: DoubleEndedIterator<Item = &VecDeque<RobEntry<C>>> + Clone>
|
||||||
|
{
|
||||||
|
self.retire_groups_unrenamed_ranges().map(|range| {
|
||||||
|
self.entries
|
||||||
|
.range(range)
|
||||||
|
.map(|entries| &entries.renamed_entries)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pub(crate) fn renamed_len(&self) -> usize {
|
||||||
|
let Self {
|
||||||
|
next_renamed_mop_id: _,
|
||||||
|
entries,
|
||||||
|
incomplete_back_entry,
|
||||||
|
l1_reg_ref_counts: _,
|
||||||
|
l2_reg_ref_counts: _,
|
||||||
|
config: _,
|
||||||
|
} = self;
|
||||||
|
entries
|
||||||
|
.iter()
|
||||||
|
.chain(incomplete_back_entry)
|
||||||
|
.map(|entries| entries.renamed_entries.len())
|
||||||
|
.sum()
|
||||||
|
}
|
||||||
|
pub(crate) fn renamed(&self) -> impl DoubleEndedIterator<Item = &RobEntry<C>> + Clone {
|
||||||
|
let Self {
|
||||||
|
next_renamed_mop_id: _,
|
||||||
|
entries,
|
||||||
|
incomplete_back_entry,
|
||||||
|
l1_reg_ref_counts: _,
|
||||||
|
l2_reg_ref_counts: _,
|
||||||
|
config: _,
|
||||||
|
} = self;
|
||||||
|
entries
|
||||||
|
.iter()
|
||||||
|
.chain(incomplete_back_entry)
|
||||||
|
.flat_map(|entries| &entries.renamed_entries)
|
||||||
|
}
|
||||||
|
pub(crate) fn renamed_mut(&mut self) -> impl DoubleEndedIterator<Item = &mut RobEntry<C>> {
|
||||||
|
let Self {
|
||||||
|
next_renamed_mop_id: _,
|
||||||
|
entries,
|
||||||
|
incomplete_back_entry,
|
||||||
|
l1_reg_ref_counts: _,
|
||||||
|
l2_reg_ref_counts: _,
|
||||||
|
config: _,
|
||||||
|
} = self;
|
||||||
|
entries
|
||||||
|
.iter_mut()
|
||||||
|
.chain(incomplete_back_entry)
|
||||||
|
.flat_map(|entries| &mut entries.renamed_entries)
|
||||||
|
}
|
||||||
|
fn try_renamed_by_id_mut(&mut self, id: &SimValue<MOpId>) -> Option<&mut RobEntry<C>> {
|
||||||
|
self.renamed_mut().find(|v| v.mop.id == *id)
|
||||||
|
}
|
||||||
|
pub(crate) fn renamed_by_id_mut(&mut self, id: &SimValue<MOpId>) -> &mut RobEntry<C> {
|
||||||
|
match self.try_renamed_by_id_mut(id) {
|
||||||
|
Some(v) => v,
|
||||||
|
None => panic!("MOpId not found: {id:?}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub(crate) fn renamed_push_back_with_new_id(
|
||||||
|
&mut self,
|
||||||
|
unrenamed: &SimValue<MOpInstance<MOp>>,
|
||||||
|
mut renamed: RobEntry<C>,
|
||||||
|
) -> &SimValue<MOpId> {
|
||||||
|
let replacement_id = self
|
||||||
|
.next_renamed_mop_id
|
||||||
|
.as_int()
|
||||||
|
.wrapping_add(1)
|
||||||
|
.into_sim_value();
|
||||||
|
renamed.mop.id = mem::replace(&mut self.next_renamed_mop_id, replacement_id);
|
||||||
|
println!("renamed_push_back_with_new_id: {:?}", renamed.mop);
|
||||||
|
renamed.for_each_reg(
|
||||||
|
(),
|
||||||
|
|(), l1| {
|
||||||
|
if let Some(unit_index) = UnitNum::index_sim(&l1.unit_num) {
|
||||||
|
self.l1_reg_ref_counts[unit_index]
|
||||||
|
[UnitOutRegNum::value_sim(&l1.unit_out_reg)] += 1;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|(), l2| self.l2_reg_ref_counts[L2RegNum::value_sim(l2)] += 1,
|
||||||
|
);
|
||||||
|
let renamed_entries = &mut self
|
||||||
|
.incomplete_back_entry
|
||||||
|
.get_or_insert_with(|| RobEntries {
|
||||||
|
unrenamed: unrenamed.clone(),
|
||||||
|
rename_table_updates: Vec::new(),
|
||||||
|
renamed_entries: VecDeque::new(),
|
||||||
|
})
|
||||||
|
.renamed_entries;
|
||||||
|
renamed_entries.push_back(renamed);
|
||||||
|
&renamed_entries.back().expect("just pushed").mop.id
|
||||||
|
}
|
||||||
|
pub(crate) fn has_incomplete_back_entry(&self) -> bool {
|
||||||
|
self.incomplete_back_entry.is_some()
|
||||||
|
}
|
||||||
|
pub(crate) fn finished_unrenamed_push_back(&mut self, unrenamed: &SimValue<MOpInstance<MOp>>) {
|
||||||
|
let entry = self
|
||||||
|
.incomplete_back_entry
|
||||||
|
.take()
|
||||||
|
.unwrap_or_else(|| RobEntries {
|
||||||
|
unrenamed: unrenamed.clone(),
|
||||||
|
rename_table_updates: Vec::new(),
|
||||||
|
renamed_entries: VecDeque::new(),
|
||||||
|
});
|
||||||
|
self.entries.push_back(entry);
|
||||||
|
}
|
||||||
|
pub(crate) fn clear(&mut self) {
|
||||||
|
let Self {
|
||||||
|
next_renamed_mop_id: _,
|
||||||
|
entries,
|
||||||
|
incomplete_back_entry,
|
||||||
|
l1_reg_ref_counts,
|
||||||
|
l2_reg_ref_counts,
|
||||||
|
config: _,
|
||||||
|
} = self;
|
||||||
|
entries.clear();
|
||||||
|
l2_reg_ref_counts.fill(0);
|
||||||
|
for i in l1_reg_ref_counts {
|
||||||
|
i.fill(0);
|
||||||
|
}
|
||||||
|
*incomplete_back_entry = None;
|
||||||
|
}
|
||||||
|
pub(crate) fn unrenamed_back_append_rename_table_update(
|
||||||
|
&mut self,
|
||||||
|
unrenamed: &SimValue<MOpInstance<MOp>>,
|
||||||
|
update: RenameTableUpdate<C>,
|
||||||
|
) {
|
||||||
|
self.incomplete_back_entry
|
||||||
|
.get_or_insert_with(|| RobEntries {
|
||||||
|
unrenamed: unrenamed.clone(),
|
||||||
|
rename_table_updates: Vec::new(),
|
||||||
|
renamed_entries: VecDeque::new(),
|
||||||
|
})
|
||||||
|
.rename_table_updates
|
||||||
|
.push(update);
|
||||||
|
}
|
||||||
|
pub(crate) fn all_mops_are_finished_and_or_caused_cancel(&self) -> bool {
|
||||||
|
self.renamed().next().is_none_or(|entry| {
|
||||||
|
entry.all_prior_mops_finished_and_or_caused_cancel
|
||||||
|
&& entry.mop_in_unit_state.is_finished_and_or_caused_cancel()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pub(crate) fn entries_pop_front(
|
||||||
|
&mut self,
|
||||||
|
) -> Option<(Vec<RenameTableUpdate<C>>, VecDeque<RobEntry<C>>)> {
|
||||||
|
let RobEntries {
|
||||||
|
unrenamed: _,
|
||||||
|
rename_table_updates,
|
||||||
|
renamed_entries,
|
||||||
|
} = self.entries.pop_front()?;
|
||||||
|
for entry in &renamed_entries {
|
||||||
|
entry.for_each_reg(
|
||||||
|
(),
|
||||||
|
|(), l1| {
|
||||||
|
if let Some(unit_index) = UnitNum::index_sim(&l1.unit_num) {
|
||||||
|
let ref_count = &mut self.l1_reg_ref_counts[unit_index]
|
||||||
|
[UnitOutRegNum::value_sim(&l1.unit_out_reg)];
|
||||||
|
*ref_count = ref_count.checked_sub(1).unwrap_or_else(|| {
|
||||||
|
unreachable!("ReorderBuffer: l1 ref count went negative: {l1:?}")
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|(), l2| {
|
||||||
|
let ref_count = &mut self.l2_reg_ref_counts[L2RegNum::value_sim(l2)];
|
||||||
|
*ref_count = ref_count.checked_sub(1).unwrap_or_else(|| {
|
||||||
|
unreachable!("ReorderBuffer: l2 ref count went negative: {l2:?}")
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Some((rename_table_updates, renamed_entries))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,14 +2,12 @@
|
||||||
// See Notices.txt for copyright information
|
// See Notices.txt for copyright information
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config::{CpuConfig, PhantomConstCpuConfig},
|
config::CpuConfig,
|
||||||
instruction::{
|
instruction::{
|
||||||
AluBranchMOp, LoadStoreMOp, MOp, MOpDestReg, MOpInto, MOpRegNum, MOpTrait,
|
AluBranchMOp, LoadStoreMOp, MOp, MOpDestReg, MOpInto, MOpRegNum, MOpTrait,
|
||||||
MOpVariantVisitOps, MOpVariantVisitor, MOpVisitVariants, PRegNum, RenamedMOp,
|
MOpVariantVisitOps, MOpVariantVisitor, MOpVisitVariants, RenamedMOp, mop_enum,
|
||||||
UnitOutRegNum, mop_enum,
|
|
||||||
},
|
},
|
||||||
register::{FlagsMode, PRegValue},
|
rename_execute_retire::ExecuteToUnitInterface,
|
||||||
unit::unit_base::UnitToRegAlloc,
|
|
||||||
};
|
};
|
||||||
use fayalite::{
|
use fayalite::{
|
||||||
bundle::{Bundle, BundleType},
|
bundle::{Bundle, BundleType},
|
||||||
|
|
@ -20,7 +18,6 @@ use serde::{Deserialize, Serialize};
|
||||||
use std::ops::ControlFlow;
|
use std::ops::ControlFlow;
|
||||||
|
|
||||||
pub mod alu_branch;
|
pub mod alu_branch;
|
||||||
pub mod unit_base;
|
|
||||||
|
|
||||||
macro_rules! all_units {
|
macro_rules! all_units {
|
||||||
(
|
(
|
||||||
|
|
@ -342,92 +339,23 @@ all_units! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[hdl]
|
|
||||||
pub struct GlobalState {
|
|
||||||
pub flags_mode: FlagsMode,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[hdl(cmp_eq)]
|
|
||||||
pub struct UnitResultCompleted<ExtraOut> {
|
|
||||||
pub value: PRegValue,
|
|
||||||
pub extra_out: ExtraOut,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[hdl(cmp_eq, no_static)]
|
|
||||||
pub struct UnitOutputWrite<C: PhantomConstGet<CpuConfig>> {
|
|
||||||
pub which: UnitOutRegNum<C>,
|
|
||||||
pub value: PRegValue,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[hdl(cmp_eq)]
|
|
||||||
pub struct TrapData {
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
#[hdl]
|
|
||||||
pub enum UnitResult<ExtraOut> {
|
|
||||||
Completed(UnitResultCompleted<ExtraOut>),
|
|
||||||
Trap(TrapData),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<ExtraOut: Type> UnitResult<ExtraOut> {
|
|
||||||
pub fn extra_out_ty(self) -> ExtraOut {
|
|
||||||
self.Completed.extra_out
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[hdl(no_static)]
|
|
||||||
pub struct UnitOutput<C: PhantomConstGet<CpuConfig>, ExtraOut> {
|
|
||||||
pub which: UnitOutRegNum<C>,
|
|
||||||
pub result: UnitResult<ExtraOut>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<C: PhantomConstCpuConfig, ExtraOut: Type> UnitOutput<C, ExtraOut> {
|
|
||||||
pub fn extra_out_ty(self) -> ExtraOut {
|
|
||||||
self.result.extra_out_ty()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[hdl(cmp_eq, no_static)]
|
|
||||||
pub struct UnitCancelInput<C: PhantomConstGet<CpuConfig>> {
|
|
||||||
pub which: UnitOutRegNum<C>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait UnitTrait:
|
pub trait UnitTrait:
|
||||||
'static + Send + Sync + std::fmt::Debug + fayalite::intern::SupportsPtrEqWithTypeId
|
'static + Send + Sync + std::fmt::Debug + fayalite::intern::SupportsPtrEqWithTypeId
|
||||||
{
|
{
|
||||||
type Type: BundleType;
|
type Type: BundleType;
|
||||||
type ExtraOut: Type;
|
|
||||||
type MOp: Type;
|
|
||||||
|
|
||||||
fn ty(&self) -> Self::Type;
|
fn ty(&self) -> Self::Type;
|
||||||
fn extra_out_ty(&self) -> Self::ExtraOut;
|
|
||||||
fn mop_ty(&self) -> Self::MOp;
|
|
||||||
|
|
||||||
fn unit_kind(&self) -> UnitKind;
|
fn unit_kind(&self) -> UnitKind;
|
||||||
|
|
||||||
fn extract_mop(
|
|
||||||
&self,
|
|
||||||
mop: Expr<
|
|
||||||
RenamedMOp<UnitOutRegNum<PhantomConst<CpuConfig>>, PRegNum<PhantomConst<CpuConfig>>>,
|
|
||||||
>,
|
|
||||||
) -> Expr<HdlOption<Self::MOp>>;
|
|
||||||
|
|
||||||
fn module(&self) -> Interned<Module<Self::Type>>;
|
fn module(&self) -> Interned<Module<Self::Type>>;
|
||||||
|
fn cd(&self, this: Expr<Self::Type>) -> Option<Expr<ClockDomain>>;
|
||||||
fn unit_to_reg_alloc(
|
fn from_execute(
|
||||||
&self,
|
&self,
|
||||||
this: Expr<Self::Type>,
|
this: Expr<Self::Type>,
|
||||||
) -> Expr<UnitToRegAlloc<PhantomConst<CpuConfig>, Self::MOp, Self::ExtraOut>>;
|
) -> Expr<ExecuteToUnitInterface<PhantomConst<CpuConfig>>>;
|
||||||
|
|
||||||
fn cd(&self, this: Expr<Self::Type>) -> Expr<ClockDomain>;
|
|
||||||
|
|
||||||
fn global_state(&self, this: Expr<Self::Type>) -> Expr<GlobalState>;
|
|
||||||
|
|
||||||
fn to_dyn(&self) -> DynUnit;
|
fn to_dyn(&self) -> DynUnit;
|
||||||
}
|
}
|
||||||
|
|
||||||
type DynUnitTrait = dyn UnitTrait<Type = Bundle, ExtraOut = CanonicalType, MOp = CanonicalType>;
|
type DynUnitTrait = dyn UnitTrait<Type = Bundle>;
|
||||||
|
|
||||||
impl fayalite::intern::InternedCompare for DynUnitTrait {
|
impl fayalite::intern::InternedCompare for DynUnitTrait {
|
||||||
type InternedCompareKey = fayalite::intern::PtrEqWithTypeId;
|
type InternedCompareKey = fayalite::intern::PtrEqWithTypeId;
|
||||||
|
|
@ -439,59 +367,34 @@ impl fayalite::intern::InternedCompare for DynUnitTrait {
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||||
pub struct DynUnit {
|
pub struct DynUnit {
|
||||||
ty: Bundle,
|
ty: Bundle,
|
||||||
extra_out_ty: CanonicalType,
|
|
||||||
mop_ty: CanonicalType,
|
|
||||||
unit_kind: UnitKind,
|
unit_kind: UnitKind,
|
||||||
unit: Interned<DynUnitTrait>,
|
unit: Interned<DynUnitTrait>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UnitTrait for DynUnit {
|
impl UnitTrait for DynUnit {
|
||||||
type Type = Bundle;
|
type Type = Bundle;
|
||||||
type ExtraOut = CanonicalType;
|
|
||||||
type MOp = CanonicalType;
|
|
||||||
|
|
||||||
fn ty(&self) -> Self::Type {
|
fn ty(&self) -> Self::Type {
|
||||||
self.ty
|
self.ty
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extra_out_ty(&self) -> Self::ExtraOut {
|
|
||||||
self.extra_out_ty
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mop_ty(&self) -> Self::MOp {
|
|
||||||
self.mop_ty
|
|
||||||
}
|
|
||||||
|
|
||||||
fn unit_kind(&self) -> UnitKind {
|
fn unit_kind(&self) -> UnitKind {
|
||||||
self.unit_kind
|
self.unit_kind
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extract_mop(
|
|
||||||
&self,
|
|
||||||
mop: Expr<
|
|
||||||
RenamedMOp<UnitOutRegNum<PhantomConst<CpuConfig>>, PRegNum<PhantomConst<CpuConfig>>>,
|
|
||||||
>,
|
|
||||||
) -> Expr<HdlOption<Self::MOp>> {
|
|
||||||
self.unit.extract_mop(mop)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn module(&self) -> Interned<Module<Self::Type>> {
|
fn module(&self) -> Interned<Module<Self::Type>> {
|
||||||
self.unit.module()
|
self.unit.module()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unit_to_reg_alloc(
|
fn cd(&self, this: Expr<Self::Type>) -> Option<Expr<ClockDomain>> {
|
||||||
&self,
|
|
||||||
this: Expr<Self::Type>,
|
|
||||||
) -> Expr<UnitToRegAlloc<PhantomConst<CpuConfig>, Self::MOp, Self::ExtraOut>> {
|
|
||||||
self.unit.unit_to_reg_alloc(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cd(&self, this: Expr<Self::Type>) -> Expr<ClockDomain> {
|
|
||||||
self.unit.cd(this)
|
self.unit.cd(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn global_state(&self, this: Expr<Self::Type>) -> Expr<GlobalState> {
|
fn from_execute(
|
||||||
self.unit.global_state(this)
|
&self,
|
||||||
|
this: Expr<Self::Type>,
|
||||||
|
) -> Expr<ExecuteToUnitInterface<PhantomConst<CpuConfig>>> {
|
||||||
|
self.unit.from_execute(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_dyn(&self) -> DynUnit {
|
fn to_dyn(&self) -> DynUnit {
|
||||||
|
|
@ -504,61 +407,34 @@ pub struct DynUnitWrapper<T>(pub T);
|
||||||
|
|
||||||
impl<T: UnitTrait + Clone + std::hash::Hash + Eq> UnitTrait for DynUnitWrapper<T> {
|
impl<T: UnitTrait + Clone + std::hash::Hash + Eq> UnitTrait for DynUnitWrapper<T> {
|
||||||
type Type = Bundle;
|
type Type = Bundle;
|
||||||
type ExtraOut = CanonicalType;
|
|
||||||
type MOp = CanonicalType;
|
|
||||||
|
|
||||||
fn ty(&self) -> Self::Type {
|
fn ty(&self) -> Self::Type {
|
||||||
Bundle::from_canonical(self.0.ty().canonical())
|
Bundle::from_canonical(self.0.ty().canonical())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extra_out_ty(&self) -> Self::ExtraOut {
|
|
||||||
self.0.extra_out_ty().canonical()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mop_ty(&self) -> Self::MOp {
|
|
||||||
self.0.mop_ty().canonical()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn unit_kind(&self) -> UnitKind {
|
fn unit_kind(&self) -> UnitKind {
|
||||||
self.0.unit_kind()
|
self.0.unit_kind()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extract_mop(
|
|
||||||
&self,
|
|
||||||
mop: Expr<
|
|
||||||
RenamedMOp<UnitOutRegNum<PhantomConst<CpuConfig>>, PRegNum<PhantomConst<CpuConfig>>>,
|
|
||||||
>,
|
|
||||||
) -> Expr<HdlOption<Self::MOp>> {
|
|
||||||
Expr::from_enum(Expr::as_enum(self.0.extract_mop(mop)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn module(&self) -> Interned<Module<Self::Type>> {
|
fn module(&self) -> Interned<Module<Self::Type>> {
|
||||||
self.0.module().canonical().intern_sized()
|
self.0.module().canonical().intern_sized()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unit_to_reg_alloc(
|
fn cd(&self, this: Expr<Self::Type>) -> Option<Expr<ClockDomain>> {
|
||||||
&self,
|
|
||||||
this: Expr<Self::Type>,
|
|
||||||
) -> Expr<UnitToRegAlloc<PhantomConst<CpuConfig>, Self::MOp, Self::ExtraOut>> {
|
|
||||||
Expr::from_bundle(Expr::as_bundle(
|
|
||||||
self.0.unit_to_reg_alloc(Expr::from_bundle(this)),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cd(&self, this: Expr<Self::Type>) -> Expr<ClockDomain> {
|
|
||||||
self.0.cd(Expr::from_bundle(this))
|
self.0.cd(Expr::from_bundle(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn global_state(&self, this: Expr<Self::Type>) -> Expr<GlobalState> {
|
fn from_execute(
|
||||||
self.0.global_state(Expr::from_bundle(this))
|
&self,
|
||||||
|
this: Expr<Self::Type>,
|
||||||
|
) -> Expr<ExecuteToUnitInterface<PhantomConst<CpuConfig>>> {
|
||||||
|
self.0.from_execute(Expr::from_bundle(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_dyn(&self) -> DynUnit {
|
fn to_dyn(&self) -> DynUnit {
|
||||||
let unit = self.intern();
|
let unit = self.intern();
|
||||||
DynUnit {
|
DynUnit {
|
||||||
ty: unit.ty(),
|
ty: unit.ty(),
|
||||||
extra_out_ty: unit.extra_out_ty(),
|
|
||||||
mop_ty: unit.mop_ty(),
|
|
||||||
unit_kind: unit.unit_kind(),
|
unit_kind: unit.unit_kind(),
|
||||||
unit: Interned::cast_unchecked(unit, |v: &Self| -> &DynUnitTrait { v }),
|
unit: Interned::cast_unchecked(unit, |v: &Self| -> &DynUnitTrait { v }),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,611 +0,0 @@
|
||||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
||||||
// See Notices.txt for copyright information
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
config::{CpuConfig, CpuConfigUnitCount, PhantomConstCpuConfig},
|
|
||||||
instruction::{COMMON_MOP_SRC_LEN, MOpTrait, PRegNum, UnitNum, UnitOutRegNum},
|
|
||||||
register::PRegValue,
|
|
||||||
unit::{UnitCancelInput, UnitOutput, UnitOutputWrite},
|
|
||||||
util::tree_reduce::tree_reduce,
|
|
||||||
};
|
|
||||||
use fayalite::{
|
|
||||||
memory::splat_mask,
|
|
||||||
module::{memory_with_loc, wire_with_loc},
|
|
||||||
prelude::*,
|
|
||||||
ty::StaticType,
|
|
||||||
util::ready_valid::ReadyValid,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[hdl(no_static)]
|
|
||||||
pub struct UnitForwardingInfo<C: PhantomConstGet<CpuConfig>> {
|
|
||||||
pub unit_output_writes: ArrayType<HdlOption<UnitOutputWrite<C>>, CpuConfigUnitCount<C>>,
|
|
||||||
pub unit_reg_frees: ArrayType<HdlOption<UnitOutRegNum<C>>, CpuConfigUnitCount<C>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[hdl]
|
|
||||||
pub struct UnitInput<MOp: Type> {
|
|
||||||
pub mop: MOp,
|
|
||||||
pub pc: UInt<64>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[hdl(no_static)]
|
|
||||||
pub struct UnitToRegAlloc<C: PhantomConstGet<CpuConfig>, MOp: Type, ExtraOut: Type> {
|
|
||||||
#[hdl(flip)]
|
|
||||||
pub unit_forwarding_info: UnitForwardingInfo<C>,
|
|
||||||
#[hdl(flip)]
|
|
||||||
pub input: ReadyValid<UnitInput<MOp>>,
|
|
||||||
#[hdl(flip)]
|
|
||||||
pub cancel_input: HdlOption<UnitCancelInput<C>>,
|
|
||||||
pub output: HdlOption<UnitOutput<C, ExtraOut>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<C: PhantomConstCpuConfig, MOp: Type, ExtraOut: Type> UnitToRegAlloc<C, MOp, ExtraOut> {
|
|
||||||
pub fn mop_ty(self) -> MOp {
|
|
||||||
self.input.data.HdlSome.mop
|
|
||||||
}
|
|
||||||
pub fn extra_out_ty(self) -> ExtraOut {
|
|
||||||
self.output.HdlSome.extra_out_ty()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[hdl(no_static)]
|
|
||||||
pub struct ExecuteStart<
|
|
||||||
C: PhantomConstGet<CpuConfig>,
|
|
||||||
MOp: Type + MOpTrait<DestReg = UnitOutRegNum<C>>,
|
|
||||||
> {
|
|
||||||
pub mop: MOp,
|
|
||||||
pub pc: UInt<64>,
|
|
||||||
pub src_values: Array<PRegValue, { COMMON_MOP_SRC_LEN }>,
|
|
||||||
pub config: C,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[hdl(no_static)]
|
|
||||||
pub struct ExecuteEnd<C: PhantomConstGet<CpuConfig>, ExtraOut> {
|
|
||||||
pub unit_output: UnitOutput<C, ExtraOut>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[hdl]
|
|
||||||
enum InFlightOpState {
|
|
||||||
Ready,
|
|
||||||
Running,
|
|
||||||
CanceledAndRunning,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl InFlightOpState {
|
|
||||||
fn ready_next_state(canceling: bool, starting: bool, ending: bool) -> Expr<HdlOption<Self>> {
|
|
||||||
match (canceling, starting, ending) {
|
|
||||||
(false, false, _) => HdlSome(InFlightOpState.Ready()),
|
|
||||||
(false, true, false) => HdlSome(InFlightOpState.Running()),
|
|
||||||
(false, true, true) => HdlNone(),
|
|
||||||
(true, false, _) => HdlNone(),
|
|
||||||
(true, true, false) => HdlSome(InFlightOpState.CanceledAndRunning()),
|
|
||||||
(true, true, true) => HdlNone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn running_next_state(canceling: bool, _starting: bool, ending: bool) -> Expr<HdlOption<Self>> {
|
|
||||||
match (canceling, ending) {
|
|
||||||
(false, false) => HdlSome(InFlightOpState.Running()),
|
|
||||||
(false, true) => HdlNone(),
|
|
||||||
(true, false) => HdlSome(InFlightOpState.CanceledAndRunning()),
|
|
||||||
(true, true) => HdlNone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn canceled_and_running_next_state(
|
|
||||||
_canceling: bool,
|
|
||||||
_starting: bool,
|
|
||||||
ending: bool,
|
|
||||||
) -> Expr<HdlOption<Self>> {
|
|
||||||
if ending {
|
|
||||||
HdlNone()
|
|
||||||
} else {
|
|
||||||
HdlSome(InFlightOpState.CanceledAndRunning())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// FIXME: this is working around #[hdl] match not supporting matching values inside structs yet
|
|
||||||
#[hdl]
|
|
||||||
fn connect_next_state(
|
|
||||||
canceling: Expr<Bool>,
|
|
||||||
starting: Expr<Bool>,
|
|
||||||
ending: Expr<Bool>,
|
|
||||||
next_state_fn: fn(canceling: bool, starting: bool, ending: bool) -> Expr<HdlOption<Self>>,
|
|
||||||
next_state: Expr<HdlOption<Self>>,
|
|
||||||
) {
|
|
||||||
#[hdl]
|
|
||||||
fn recurse<const N: usize>(
|
|
||||||
exprs: &[Expr<Bool>; N],
|
|
||||||
bools: &mut [bool; N],
|
|
||||||
f: &mut impl FnMut(&[bool; N]),
|
|
||||||
arg_index: usize,
|
|
||||||
) {
|
|
||||||
if arg_index < N {
|
|
||||||
#[hdl]
|
|
||||||
if exprs[arg_index] {
|
|
||||||
bools[arg_index] = true;
|
|
||||||
recurse(exprs, bools, f, arg_index + 1);
|
|
||||||
} else {
|
|
||||||
bools[arg_index] = false;
|
|
||||||
recurse(exprs, bools, f, arg_index + 1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
f(bools);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
recurse(
|
|
||||||
&[canceling, starting, ending],
|
|
||||||
&mut [false; 3],
|
|
||||||
&mut |&[canceling, starting, ending]| {
|
|
||||||
connect(next_state, next_state_fn(canceling, starting, ending))
|
|
||||||
},
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[hdl]
|
|
||||||
struct InFlightOp<MOp: Type> {
|
|
||||||
state: InFlightOpState,
|
|
||||||
mop: MOp,
|
|
||||||
pc: UInt<64>,
|
|
||||||
src_ready_flags: Array<Bool, { COMMON_MOP_SRC_LEN }>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[hdl]
|
|
||||||
struct InFlightOpsSummary<OpIndexWidth: Size> {
|
|
||||||
empty_op_index: HdlOption<UIntType<OpIndexWidth>>,
|
|
||||||
ready_op_index: HdlOption<UIntType<OpIndexWidth>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<OpIndexWidth: Size> InFlightOpsSummary<OpIndexWidth> {
|
|
||||||
#[hdl]
|
|
||||||
fn new<MOp: Type>(
|
|
||||||
op_index: usize,
|
|
||||||
op_index_ty: UIntType<OpIndexWidth>,
|
|
||||||
in_flight_op: impl ToExpr<Type = HdlOption<InFlightOp<MOp>>>,
|
|
||||||
) -> Expr<Self> {
|
|
||||||
let empty_op_index = wire_with_loc(
|
|
||||||
&format!("empty_op_index_{op_index}"),
|
|
||||||
SourceLocation::caller(),
|
|
||||||
HdlOption[op_index_ty],
|
|
||||||
);
|
|
||||||
connect(empty_op_index, HdlOption[op_index_ty].HdlNone());
|
|
||||||
let ready_op_index = wire_with_loc(
|
|
||||||
&format!("ready_op_index_{op_index}"),
|
|
||||||
SourceLocation::caller(),
|
|
||||||
HdlOption[op_index_ty],
|
|
||||||
);
|
|
||||||
connect(ready_op_index, HdlOption[op_index_ty].HdlNone());
|
|
||||||
#[hdl]
|
|
||||||
if let HdlSome(in_flight_op) = in_flight_op {
|
|
||||||
#[hdl]
|
|
||||||
let InFlightOp::<_> {
|
|
||||||
state,
|
|
||||||
mop: _,
|
|
||||||
pc: _,
|
|
||||||
src_ready_flags,
|
|
||||||
} = in_flight_op;
|
|
||||||
connect(ready_op_index, HdlOption[op_index_ty].HdlNone());
|
|
||||||
#[hdl]
|
|
||||||
match state {
|
|
||||||
InFlightOpState::Ready =>
|
|
||||||
{
|
|
||||||
#[hdl]
|
|
||||||
if src_ready_flags.cmp_eq([true; COMMON_MOP_SRC_LEN]) {
|
|
||||||
connect(ready_op_index, HdlSome(op_index.cast_to(op_index_ty)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
InFlightOpState::CanceledAndRunning | InFlightOpState::Running => {}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
connect(empty_op_index, HdlSome(op_index.cast_to(op_index_ty)));
|
|
||||||
}
|
|
||||||
#[hdl]
|
|
||||||
InFlightOpsSummary::<_> {
|
|
||||||
empty_op_index,
|
|
||||||
ready_op_index,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[hdl]
|
|
||||||
fn combine(l: impl ToExpr<Type = Self>, r: impl ToExpr<Type = Self>) -> Expr<Self> {
|
|
||||||
let l = l.to_expr();
|
|
||||||
let r = r.to_expr();
|
|
||||||
#[hdl]
|
|
||||||
InFlightOpsSummary::<_> {
|
|
||||||
empty_op_index: HdlOption::or(l.empty_op_index, r.empty_op_index),
|
|
||||||
ready_op_index: HdlOption::or(l.ready_op_index, r.ready_op_index),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl InFlightOpsSummary<DynSize> {
|
|
||||||
fn summarize<MOp: Type, MaxInFlight: Size>(
|
|
||||||
in_flight_ops: impl ToExpr<Type = ArrayType<HdlOption<InFlightOp<MOp>>, MaxInFlight>>,
|
|
||||||
) -> Expr<Self> {
|
|
||||||
let in_flight_ops = in_flight_ops.to_expr();
|
|
||||||
let max_in_flight = in_flight_ops.ty().len();
|
|
||||||
let index_range = 0..max_in_flight;
|
|
||||||
let index_ty = UInt::range(index_range.clone());
|
|
||||||
tree_reduce(
|
|
||||||
index_range.map(|i| Self::new(i, index_ty, in_flight_ops[i])),
|
|
||||||
Self::combine,
|
|
||||||
)
|
|
||||||
.expect("in_flight_ops is known to have len > 0")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[hdl_module]
|
|
||||||
pub fn unit_base<
|
|
||||||
MOp: Type
|
|
||||||
+ MOpTrait<
|
|
||||||
DestReg = UnitOutRegNum<PhantomConst<CpuConfig>>,
|
|
||||||
SrcReg = PRegNum<PhantomConst<CpuConfig>>,
|
|
||||||
>,
|
|
||||||
ExtraOut: Type,
|
|
||||||
>(
|
|
||||||
config: PhantomConst<CpuConfig>,
|
|
||||||
unit_index: usize,
|
|
||||||
mop_ty: MOp,
|
|
||||||
extra_out_ty: ExtraOut,
|
|
||||||
) {
|
|
||||||
#[hdl]
|
|
||||||
let cd: ClockDomain = m.input();
|
|
||||||
#[hdl]
|
|
||||||
let unit_to_reg_alloc: UnitToRegAlloc<PhantomConst<CpuConfig>, MOp, ExtraOut> =
|
|
||||||
m.output(UnitToRegAlloc[config][mop_ty][extra_out_ty]);
|
|
||||||
#[hdl]
|
|
||||||
let execute_start: ReadyValid<ExecuteStart<PhantomConst<CpuConfig>, MOp>> =
|
|
||||||
m.output(ReadyValid[ExecuteStart[config][mop_ty]]);
|
|
||||||
#[hdl]
|
|
||||||
let execute_end: HdlOption<ExecuteEnd<PhantomConst<CpuConfig>, ExtraOut>> =
|
|
||||||
m.input(HdlOption[ExecuteEnd[config][extra_out_ty]]);
|
|
||||||
|
|
||||||
connect(execute_start.data, execute_start.ty().data.HdlNone());
|
|
||||||
|
|
||||||
let max_in_flight = config.get().unit_max_in_flight(unit_index).get();
|
|
||||||
let in_flight_op_ty = InFlightOp[mop_ty];
|
|
||||||
#[hdl]
|
|
||||||
let in_flight_ops = reg_builder()
|
|
||||||
.clock_domain(cd)
|
|
||||||
.reset(repeat(HdlOption[in_flight_op_ty].HdlNone(), max_in_flight));
|
|
||||||
|
|
||||||
let in_flight_ops_summary_value = InFlightOpsSummary::summarize(in_flight_ops);
|
|
||||||
#[hdl]
|
|
||||||
let in_flight_ops_summary = wire(in_flight_ops_summary_value.ty());
|
|
||||||
connect(in_flight_ops_summary, in_flight_ops_summary_value);
|
|
||||||
|
|
||||||
connect(
|
|
||||||
unit_to_reg_alloc.input.ready,
|
|
||||||
HdlOption::is_some(in_flight_ops_summary.empty_op_index),
|
|
||||||
);
|
|
||||||
|
|
||||||
#[hdl]
|
|
||||||
let UnitForwardingInfo::<_> {
|
|
||||||
unit_output_writes,
|
|
||||||
unit_reg_frees,
|
|
||||||
} = unit_to_reg_alloc.unit_forwarding_info;
|
|
||||||
#[hdl]
|
|
||||||
let read_src_regs = wire(mop_ty.src_regs_ty());
|
|
||||||
connect(
|
|
||||||
read_src_regs,
|
|
||||||
repeat(PRegNum[config].const_zero(), ConstUsize),
|
|
||||||
);
|
|
||||||
#[hdl]
|
|
||||||
let read_src_values = wire();
|
|
||||||
connect(read_src_values, [PRegValue::zeroed(); COMMON_MOP_SRC_LEN]);
|
|
||||||
#[hdl]
|
|
||||||
let input_src_regs = wire(mop_ty.src_regs_ty());
|
|
||||||
connect(
|
|
||||||
input_src_regs,
|
|
||||||
repeat(PRegNum[config].const_zero(), ConstUsize),
|
|
||||||
);
|
|
||||||
#[hdl]
|
|
||||||
let input_src_regs_valid = wire();
|
|
||||||
connect(input_src_regs_valid, [true; COMMON_MOP_SRC_LEN]);
|
|
||||||
let mut unit_output_regs_valid: Vec<MemBuilder<Bool>> = (0..unit_output_writes.ty().len())
|
|
||||||
.map(|unit_index| {
|
|
||||||
let mut mem = memory_with_loc(
|
|
||||||
&format!("unit_{unit_index}_output_regs_valid"),
|
|
||||||
Bool,
|
|
||||||
SourceLocation::caller(),
|
|
||||||
);
|
|
||||||
mem.depth(1 << config.get().out_reg_num_width);
|
|
||||||
mem
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
for unit_index in 0..unit_output_writes.ty().len() {
|
|
||||||
let mut unit_output_regs = memory_with_loc(
|
|
||||||
&format!("unit_{unit_index}_output_regs"),
|
|
||||||
PRegValue,
|
|
||||||
SourceLocation::caller(),
|
|
||||||
);
|
|
||||||
unit_output_regs.depth(1 << config.get().out_reg_num_width);
|
|
||||||
|
|
||||||
for src_index in 0..COMMON_MOP_SRC_LEN {
|
|
||||||
let read_port = unit_output_regs.new_read_port();
|
|
||||||
let p_reg_num = read_src_regs[src_index];
|
|
||||||
connect_any(read_port.addr, p_reg_num.unit_out_reg.value);
|
|
||||||
connect(read_port.en, false);
|
|
||||||
connect(read_port.clk, cd.clk);
|
|
||||||
#[hdl]
|
|
||||||
if UnitNum::is_index(p_reg_num.unit_num, unit_index) {
|
|
||||||
connect(read_port.en, true);
|
|
||||||
connect(read_src_values[src_index], read_port.data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for src_index in 0..COMMON_MOP_SRC_LEN {
|
|
||||||
let read_port = unit_output_regs_valid[unit_index].new_read_port();
|
|
||||||
let p_reg_num = input_src_regs[src_index];
|
|
||||||
connect_any(read_port.addr, p_reg_num.unit_out_reg.value);
|
|
||||||
connect(read_port.en, false);
|
|
||||||
connect(read_port.clk, cd.clk);
|
|
||||||
#[hdl]
|
|
||||||
if UnitNum::is_index(p_reg_num.unit_num, unit_index) {
|
|
||||||
connect(read_port.en, true);
|
|
||||||
connect(input_src_regs_valid[src_index], read_port.data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let write_port = unit_output_regs.new_write_port();
|
|
||||||
connect_any(write_port.addr, 0u8);
|
|
||||||
connect(write_port.en, false);
|
|
||||||
connect(write_port.clk, cd.clk);
|
|
||||||
connect(write_port.data, PRegValue::zeroed());
|
|
||||||
connect(write_port.mask, splat_mask(PRegValue, true.to_expr()));
|
|
||||||
let ready_write_port = unit_output_regs_valid[unit_index].new_write_port();
|
|
||||||
connect_any(ready_write_port.addr, 0u8);
|
|
||||||
connect(ready_write_port.en, false);
|
|
||||||
connect(ready_write_port.clk, cd.clk);
|
|
||||||
connect(ready_write_port.data, true);
|
|
||||||
connect(ready_write_port.mask, true);
|
|
||||||
#[hdl]
|
|
||||||
if let HdlSome(unit_output_write) = unit_output_writes[unit_index] {
|
|
||||||
connect_any(write_port.addr, unit_output_write.which.value);
|
|
||||||
connect(write_port.data, unit_output_write.value);
|
|
||||||
connect(write_port.en, true);
|
|
||||||
connect_any(ready_write_port.addr, unit_output_write.which.value);
|
|
||||||
connect(ready_write_port.en, true);
|
|
||||||
let p_reg_num = #[hdl]
|
|
||||||
PRegNum::<_> {
|
|
||||||
unit_num: UnitNum[config].from_index(unit_index),
|
|
||||||
unit_out_reg: unit_output_write.which,
|
|
||||||
};
|
|
||||||
for src_index in 0..COMMON_MOP_SRC_LEN {
|
|
||||||
#[hdl]
|
|
||||||
if input_src_regs[src_index].cmp_eq(p_reg_num) {
|
|
||||||
connect(input_src_regs_valid[src_index], true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let free_write_port = unit_output_regs_valid[unit_index].new_write_port();
|
|
||||||
connect_any(free_write_port.addr, 0u8);
|
|
||||||
connect(free_write_port.en, false);
|
|
||||||
connect(free_write_port.clk, cd.clk);
|
|
||||||
connect(free_write_port.data, false);
|
|
||||||
connect(free_write_port.mask, true);
|
|
||||||
#[hdl]
|
|
||||||
if let HdlSome(unit_reg_free) = unit_reg_frees[unit_index] {
|
|
||||||
connect_any(free_write_port.addr, unit_reg_free.value);
|
|
||||||
connect(free_write_port.en, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[hdl]
|
|
||||||
if let HdlSome(ready_op_index) = in_flight_ops_summary.ready_op_index {
|
|
||||||
#[hdl]
|
|
||||||
if let HdlSome(in_flight_op) = in_flight_ops[ready_op_index] {
|
|
||||||
connect(
|
|
||||||
execute_start.data,
|
|
||||||
HdlSome(
|
|
||||||
#[hdl]
|
|
||||||
ExecuteStart::<_, _> {
|
|
||||||
mop: in_flight_op.mop,
|
|
||||||
pc: in_flight_op.pc,
|
|
||||||
src_values: read_src_values,
|
|
||||||
config,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
connect(
|
|
||||||
unit_to_reg_alloc.output,
|
|
||||||
unit_to_reg_alloc.output.ty().HdlNone(),
|
|
||||||
);
|
|
||||||
|
|
||||||
#[hdl]
|
|
||||||
let input_in_flight_op = wire(HdlOption[in_flight_op_ty]);
|
|
||||||
connect(input_in_flight_op, HdlOption[in_flight_op_ty].HdlNone());
|
|
||||||
#[hdl]
|
|
||||||
if let HdlSome(input) = ReadyValid::firing_data(unit_to_reg_alloc.input) {
|
|
||||||
#[hdl]
|
|
||||||
let UnitInput::<_> { mop, pc } = input;
|
|
||||||
#[hdl]
|
|
||||||
let input_mop_src_regs = wire(mop_ty.src_regs_ty());
|
|
||||||
connect(
|
|
||||||
input_mop_src_regs,
|
|
||||||
repeat(PRegNum[config].const_zero(), ConstUsize),
|
|
||||||
);
|
|
||||||
MOp::connect_src_regs(mop, input_mop_src_regs);
|
|
||||||
let src_ready_flags = wire_with_loc(
|
|
||||||
"input_in_flight_op_src_ready_flags",
|
|
||||||
SourceLocation::caller(),
|
|
||||||
StaticType::TYPE,
|
|
||||||
);
|
|
||||||
connect(src_ready_flags, input_src_regs_valid);
|
|
||||||
connect(input_src_regs, input_mop_src_regs);
|
|
||||||
#[hdl]
|
|
||||||
if unit_to_reg_alloc.cancel_input.cmp_ne(HdlSome(
|
|
||||||
#[hdl]
|
|
||||||
UnitCancelInput::<_> {
|
|
||||||
which: MOp::dest_reg(mop),
|
|
||||||
},
|
|
||||||
)) {
|
|
||||||
connect(
|
|
||||||
input_in_flight_op,
|
|
||||||
HdlSome(
|
|
||||||
#[hdl]
|
|
||||||
InFlightOp::<_> {
|
|
||||||
state: InFlightOpState.Ready(),
|
|
||||||
mop,
|
|
||||||
pc,
|
|
||||||
src_ready_flags,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
#[hdl]
|
|
||||||
if let HdlSome(empty_op_index) = in_flight_ops_summary.empty_op_index {
|
|
||||||
connect(in_flight_ops[empty_op_index], input_in_flight_op);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[hdl]
|
|
||||||
let in_flight_op_next_state = wire(Array[HdlOption[InFlightOpState]][max_in_flight]);
|
|
||||||
#[hdl]
|
|
||||||
let in_flight_op_next_src_ready_flags =
|
|
||||||
wire(Array[in_flight_op_ty.src_ready_flags][max_in_flight]);
|
|
||||||
#[hdl]
|
|
||||||
let in_flight_op_canceling = wire(Array[Bool][max_in_flight]);
|
|
||||||
#[hdl]
|
|
||||||
let in_flight_op_execute_starting = wire(Array[Bool][max_in_flight]);
|
|
||||||
#[hdl]
|
|
||||||
let in_flight_op_execute_ending = wire(Array[Bool][max_in_flight]);
|
|
||||||
for in_flight_op_index in 0..max_in_flight {
|
|
||||||
connect(
|
|
||||||
in_flight_op_next_src_ready_flags[in_flight_op_index],
|
|
||||||
[false; COMMON_MOP_SRC_LEN],
|
|
||||||
);
|
|
||||||
connect(in_flight_op_canceling[in_flight_op_index], false);
|
|
||||||
connect(in_flight_op_execute_starting[in_flight_op_index], false);
|
|
||||||
connect(in_flight_op_execute_ending[in_flight_op_index], false);
|
|
||||||
#[hdl]
|
|
||||||
if let HdlSome(in_flight_op) = in_flight_ops[in_flight_op_index] {
|
|
||||||
#[hdl]
|
|
||||||
let InFlightOp::<_> {
|
|
||||||
state,
|
|
||||||
mop,
|
|
||||||
pc,
|
|
||||||
src_ready_flags,
|
|
||||||
} = in_flight_op;
|
|
||||||
let which = MOp::dest_reg(mop);
|
|
||||||
let src_regs = wire_with_loc(
|
|
||||||
&format!("in_flight_op_src_regs_{in_flight_op_index}"),
|
|
||||||
SourceLocation::caller(),
|
|
||||||
mop_ty.src_regs_ty(),
|
|
||||||
);
|
|
||||||
connect(src_regs, repeat(PRegNum[config].const_zero(), ConstUsize));
|
|
||||||
MOp::connect_src_regs(mop, src_regs);
|
|
||||||
|
|
||||||
#[hdl]
|
|
||||||
if in_flight_ops_summary.ready_op_index.cmp_eq(HdlSome(
|
|
||||||
in_flight_op_index.cast_to(in_flight_ops_summary.ty().ready_op_index.HdlSome),
|
|
||||||
)) {
|
|
||||||
connect(read_src_regs, src_regs);
|
|
||||||
}
|
|
||||||
|
|
||||||
connect(
|
|
||||||
in_flight_op_next_src_ready_flags[in_flight_op_index],
|
|
||||||
src_ready_flags,
|
|
||||||
);
|
|
||||||
for unit_index in 0..unit_output_writes.ty().len() {
|
|
||||||
#[hdl]
|
|
||||||
if let HdlSome(unit_output_write) = unit_output_writes[unit_index] {
|
|
||||||
#[hdl]
|
|
||||||
let UnitOutputWrite::<_> {
|
|
||||||
which: unit_out_reg,
|
|
||||||
value: _,
|
|
||||||
} = unit_output_write;
|
|
||||||
let p_reg_num = #[hdl]
|
|
||||||
PRegNum::<_> {
|
|
||||||
unit_num: UnitNum[config].from_index(unit_index),
|
|
||||||
unit_out_reg,
|
|
||||||
};
|
|
||||||
for src_index in 0..COMMON_MOP_SRC_LEN {
|
|
||||||
#[hdl]
|
|
||||||
if p_reg_num.cmp_eq(src_regs[src_index]) {
|
|
||||||
connect(
|
|
||||||
in_flight_op_next_src_ready_flags[in_flight_op_index][src_index],
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
connect(
|
|
||||||
in_flight_op_canceling[in_flight_op_index],
|
|
||||||
unit_to_reg_alloc.cancel_input.cmp_eq(HdlSome(
|
|
||||||
#[hdl]
|
|
||||||
UnitCancelInput::<_> { which },
|
|
||||||
)),
|
|
||||||
);
|
|
||||||
|
|
||||||
#[hdl]
|
|
||||||
if let HdlSome(execute_end) = execute_end {
|
|
||||||
#[hdl]
|
|
||||||
let ExecuteEnd::<_, _> { unit_output } = execute_end;
|
|
||||||
#[hdl]
|
|
||||||
if which.cmp_eq(unit_output.which) {
|
|
||||||
connect(in_flight_op_execute_ending[in_flight_op_index], true);
|
|
||||||
#[hdl]
|
|
||||||
if !in_flight_op_canceling[in_flight_op_index] {
|
|
||||||
#[hdl]
|
|
||||||
match state {
|
|
||||||
InFlightOpState::Running | InFlightOpState::Ready => {
|
|
||||||
connect(unit_to_reg_alloc.output, HdlSome(unit_output))
|
|
||||||
}
|
|
||||||
InFlightOpState::CanceledAndRunning => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[hdl]
|
|
||||||
if let HdlSome(execute_start) = ReadyValid::firing_data(execute_start) {
|
|
||||||
#[hdl]
|
|
||||||
if which.cmp_eq(MOp::dest_reg(execute_start.mop)) {
|
|
||||||
connect(in_flight_op_execute_starting[in_flight_op_index], true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let connect_next_state = |f| {
|
|
||||||
InFlightOpState::connect_next_state(
|
|
||||||
in_flight_op_canceling[in_flight_op_index],
|
|
||||||
in_flight_op_execute_starting[in_flight_op_index],
|
|
||||||
in_flight_op_execute_ending[in_flight_op_index],
|
|
||||||
f,
|
|
||||||
in_flight_op_next_state[in_flight_op_index],
|
|
||||||
);
|
|
||||||
};
|
|
||||||
#[hdl]
|
|
||||||
match state {
|
|
||||||
InFlightOpState::Ready => connect_next_state(InFlightOpState::ready_next_state),
|
|
||||||
InFlightOpState::Running => connect_next_state(InFlightOpState::running_next_state),
|
|
||||||
InFlightOpState::CanceledAndRunning => {
|
|
||||||
connect_next_state(InFlightOpState::canceled_and_running_next_state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[hdl]
|
|
||||||
if let HdlSome(state) = in_flight_op_next_state[in_flight_op_index] {
|
|
||||||
connect(
|
|
||||||
in_flight_ops[in_flight_op_index],
|
|
||||||
HdlSome(
|
|
||||||
#[hdl]
|
|
||||||
InFlightOp::<_> {
|
|
||||||
state,
|
|
||||||
mop,
|
|
||||||
pc,
|
|
||||||
src_ready_flags: in_flight_op_next_src_ready_flags[in_flight_op_index],
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
connect(
|
|
||||||
in_flight_ops[in_flight_op_index],
|
|
||||||
HdlOption[in_flight_op_ty].HdlNone(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
connect(in_flight_op_next_state[in_flight_op_index], HdlNone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,8 +1,15 @@
|
||||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
// See Notices.txt for copyright information
|
// See Notices.txt for copyright information
|
||||||
|
|
||||||
use fayalite::{expr::ops::ArrayLiteral, module::wire_with_loc, prelude::*};
|
use fayalite::{
|
||||||
use std::num::NonZero;
|
bundle::BundleType, expr::ops::ArrayLiteral, module::wire_with_loc, prelude::*,
|
||||||
|
sim::vcd::VcdWriterDecls, util::RcWriter,
|
||||||
|
};
|
||||||
|
use std::{
|
||||||
|
num::NonZero,
|
||||||
|
panic::Location,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
};
|
||||||
|
|
||||||
pub mod array_vec;
|
pub mod array_vec;
|
||||||
pub mod tree_reduce;
|
pub mod tree_reduce;
|
||||||
|
|
@ -310,3 +317,152 @@ impl LFSR31 {
|
||||||
state
|
state
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct CheckedVcdOutput {
|
||||||
|
writer: Option<RcWriter>,
|
||||||
|
expected_path: PathBuf,
|
||||||
|
expected_contents: Result<String, (Option<PathBuf>, std::io::Error)>,
|
||||||
|
location: &'static Location<'static>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CheckedVcdOutput {
|
||||||
|
#[must_use]
|
||||||
|
#[track_caller]
|
||||||
|
pub fn new<T: BundleType>(sim: &mut Simulation<T>, expected_path: PathBuf) -> Self {
|
||||||
|
let writer = RcWriter::default();
|
||||||
|
sim.add_trace_writer(VcdWriterDecls::new(writer.clone()));
|
||||||
|
Self {
|
||||||
|
writer: Some(writer),
|
||||||
|
expected_contents: std::fs::read_to_string(&expected_path).map_err(|e| {
|
||||||
|
eprintln!(
|
||||||
|
"error: failed to read expected VCD from: {}",
|
||||||
|
expected_path.display(),
|
||||||
|
);
|
||||||
|
(std::env::current_dir().ok(), e)
|
||||||
|
}),
|
||||||
|
expected_path,
|
||||||
|
location: Location::caller(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[must_use]
|
||||||
|
#[track_caller]
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub fn __checked_vcd_output_macro_helper<T: BundleType>(
|
||||||
|
sim: &mut Simulation<T>,
|
||||||
|
cargo_manifest_dir: &'static str,
|
||||||
|
path: &'static str,
|
||||||
|
) -> Self {
|
||||||
|
Self::new(sim, Path::new(cargo_manifest_dir).join(path))
|
||||||
|
}
|
||||||
|
pub fn with_vcd_output<R>(&self, f: impl FnOnce(&str) -> R) -> R {
|
||||||
|
let Some(writer) = &self.writer else {
|
||||||
|
unreachable!();
|
||||||
|
};
|
||||||
|
writer.clone().borrow(|output| {
|
||||||
|
let Ok(output) = str::from_utf8(output) else {
|
||||||
|
unreachable!("VcdWriter writes valid UTF-8");
|
||||||
|
};
|
||||||
|
f(output)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
#[track_caller]
|
||||||
|
pub fn finish(mut self) {
|
||||||
|
let Ok(()) = self.finish_impl(|msg| panic!("{msg}"));
|
||||||
|
}
|
||||||
|
fn finish_impl<E>(
|
||||||
|
&mut self,
|
||||||
|
error: impl FnOnce(std::fmt::Arguments<'_>) -> E,
|
||||||
|
) -> Result<(), E> {
|
||||||
|
let Self {
|
||||||
|
writer: Some(writer),
|
||||||
|
expected_path,
|
||||||
|
expected_contents,
|
||||||
|
location,
|
||||||
|
} = self
|
||||||
|
else {
|
||||||
|
// already finished
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
let Ok(vcd) = String::from_utf8(writer.take()) else {
|
||||||
|
unreachable!("VcdWriter writes valid UTF-8");
|
||||||
|
};
|
||||||
|
let expected_path_d = expected_path.display();
|
||||||
|
if expected_contents
|
||||||
|
.as_ref()
|
||||||
|
.is_ok_and(|expected_contents| *expected_contents == vcd)
|
||||||
|
{
|
||||||
|
// avoid written output from being split from threads interleaving writes to stdout
|
||||||
|
let _stdout = std::io::stderr().lock();
|
||||||
|
// use println to get output captured by tests
|
||||||
|
println!("\n{location}: generated VCD matches the expected VCD in {expected_path_d}");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
// avoid written output from being split from threads interleaving writes to stderr
|
||||||
|
let _stderr = std::io::stderr().lock();
|
||||||
|
let error = |msg: std::fmt::Arguments<'_>| {
|
||||||
|
// print msg at both beginning and end so it's easier to find when the vcd is huge
|
||||||
|
Err(error(format_args!(
|
||||||
|
"\n{msg}####### VCD:\n{vcd}\n#######\n{msg}"
|
||||||
|
)))
|
||||||
|
};
|
||||||
|
let error = |msg: std::fmt::Arguments<'_>| match &*expected_contents {
|
||||||
|
Ok(_) => error(format_args!(
|
||||||
|
"{location}: generated VCD doesn't match the expected VCD in {expected_path_d}\n\
|
||||||
|
{msg}",
|
||||||
|
)),
|
||||||
|
Err((Some(current_dir), e)) => error(format_args!(
|
||||||
|
"{location}: generated VCD doesn't match the expected VCD in {expected_path_d}\n\
|
||||||
|
error: failed to read: {e}\n\
|
||||||
|
current dir: {current_dir}\n\
|
||||||
|
{msg}",
|
||||||
|
current_dir = current_dir.display(),
|
||||||
|
)),
|
||||||
|
Err((None, e)) => error(format_args!(
|
||||||
|
"{location}: generated VCD doesn't match the expected VCD in {expected_path_d}\n\
|
||||||
|
error: failed to read: {e}\n\
|
||||||
|
{msg}",
|
||||||
|
)),
|
||||||
|
};
|
||||||
|
const OVERWRITE_VAR_NAME: &str = "OVERWRITE_EXPECTED_VCD";
|
||||||
|
const OVERWRITE_VAR_VALUE: &str = "overwrite";
|
||||||
|
match std::env::var_os(OVERWRITE_VAR_NAME) {
|
||||||
|
Some(v) if v == OVERWRITE_VAR_VALUE => match std::fs::write(&expected_path, &vcd) {
|
||||||
|
Ok(()) => error(format_args!(
|
||||||
|
"warning: since `{OVERWRITE_VAR_NAME}={OVERWRITE_VAR_VALUE}` is set -- writing the generated VCD to {expected_path_d}\n"
|
||||||
|
)),
|
||||||
|
Err(e) => error(format_args!(
|
||||||
|
"error: since `{OVERWRITE_VAR_NAME}={OVERWRITE_VAR_VALUE}` is set -- tried to write the generated VCD to {expected_path_d}\n\
|
||||||
|
error: failed to write: {e}"
|
||||||
|
)),
|
||||||
|
},
|
||||||
|
_ => error(format_args!(
|
||||||
|
"note: rerun the test with the environment variable `{OVERWRITE_VAR_NAME}={OVERWRITE_VAR_VALUE}`\n\
|
||||||
|
to update the expected output to match the generated output.\n"
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for CheckedVcdOutput {
|
||||||
|
#[track_caller]
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let _ = self.finish_impl(|msg| {
|
||||||
|
if std::thread::panicking() {
|
||||||
|
eprintln!("{msg}"); // use eprintln to get output captured by tests
|
||||||
|
} else {
|
||||||
|
panic!("{msg}");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! checked_vcd_output {
|
||||||
|
($sim:expr, $path_relative_to_manifest_dir:expr $(,)?) => {
|
||||||
|
$crate::util::CheckedVcdOutput::__checked_vcd_output_macro_helper(
|
||||||
|
$sim,
|
||||||
|
::std::env!("CARGO_MANIFEST_DIR"),
|
||||||
|
::std::concat!($path_relative_to_manifest_dir),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
12085
crates/cpu/tests/expected/rename_execute_retire_head_n1.vcd
generated
12085
crates/cpu/tests/expected/rename_execute_retire_head_n1.vcd
generated
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
207237
crates/cpu/tests/expected/rename_execute_retire_save_restore_gprs_real.vcd
generated
Normal file
207237
crates/cpu/tests/expected/rename_execute_retire_save_restore_gprs_real.vcd
generated
Normal file
File diff suppressed because it is too large
Load diff
141044
crates/cpu/tests/expected/rename_execute_retire_slow_loop.vcd
generated
141044
crates/cpu/tests/expected/rename_execute_retire_slow_loop.vcd
generated
File diff suppressed because it is too large
Load diff
|
|
@ -2,6 +2,7 @@
|
||||||
// See Notices.txt for copyright information
|
// See Notices.txt for copyright information
|
||||||
|
|
||||||
use cpu::{
|
use cpu::{
|
||||||
|
checked_vcd_output,
|
||||||
config::{
|
config::{
|
||||||
CpuConfig, CpuConfigFetchWidth, CpuConfigMaxUnitMaxInFlight, PhantomConstCpuConfig,
|
CpuConfig, CpuConfigFetchWidth, CpuConfigMaxUnitMaxInFlight, PhantomConstCpuConfig,
|
||||||
UnitConfig,
|
UnitConfig,
|
||||||
|
|
@ -15,13 +16,13 @@ use cpu::{
|
||||||
ShiftRotateMode, StoreMOp, UnitNum, WriteL2RegMOp,
|
ShiftRotateMode, StoreMOp, UnitNum, WriteL2RegMOp,
|
||||||
},
|
},
|
||||||
next_pc::CallStackOp,
|
next_pc::CallStackOp,
|
||||||
register::{PRegFlags, PRegFlagsPowerISA, PRegValue},
|
register::{FlagsMode, PRegFlags, PRegFlagsPowerISA, PRegValue},
|
||||||
rename_execute_retire::{
|
rename_execute_retire::{
|
||||||
ExecuteToUnitInterface, MOpId, MOpInstance, NextPcPredictorOp, PostDecodeOutputInterface,
|
ExecuteToUnitInterface, GlobalState, MOpId, MOpInstance, NextPcPredictorOp,
|
||||||
RenamedMOp, RetireToNextPcInterface, RetireToNextPcInterfaceInner, UnitCausedCancel,
|
PostDecodeOutputInterface, RenamedMOp, RetireToNextPcInterface,
|
||||||
UnitEnqueue, UnitFinishCauseCancel, UnitInputsReady, UnitMOpCantCauseCancel,
|
RetireToNextPcInterfaceInner, UnitCausedCancel, UnitEnqueue, UnitFinishCauseCancel,
|
||||||
UnitMOpIsNoLongerSpeculative, UnitOutputReady, rename_execute_retire,
|
UnitInputsReady, UnitMOpCantCauseCancel, UnitMOpIsNoLongerSpeculative, UnitOutputReady,
|
||||||
to_unit_interfaces::ExecuteToUnitInterfaces,
|
rename_execute_retire, to_unit_interfaces::ExecuteToUnitInterfaces,
|
||||||
},
|
},
|
||||||
unit::{UnitKind, UnitMOp},
|
unit::{UnitKind, UnitMOp},
|
||||||
util::array_vec::ArrayVec,
|
util::array_vec::ArrayVec,
|
||||||
|
|
@ -30,9 +31,7 @@ use fayalite::{
|
||||||
bundle::BundleType,
|
bundle::BundleType,
|
||||||
module::instance_with_loc,
|
module::instance_with_loc,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
sim::vcd::VcdWriterDecls,
|
|
||||||
ty::{OpaqueSimValue, StaticType},
|
ty::{OpaqueSimValue, StaticType},
|
||||||
util::RcWriter,
|
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{
|
use std::{
|
||||||
|
|
@ -1665,10 +1664,18 @@ trait MockExecutionStateTrait: Default {
|
||||||
#[hdl]
|
#[hdl]
|
||||||
fn run_add_sub<C: PhantomConstCpuConfig, SrcCount: KnownSize>(
|
fn run_add_sub<C: PhantomConstCpuConfig, SrcCount: KnownSize>(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
global_state: &SimValue<GlobalState>,
|
||||||
pc: u64,
|
pc: u64,
|
||||||
mop: &SimValue<AddSubMOp<PRegNum<C>, PRegNum<C>, SrcCount>>,
|
mop: &SimValue<AddSubMOp<PRegNum<C>, PRegNum<C>, SrcCount>>,
|
||||||
src_values: &[SimValue<TraceAsString<PRegValue>>; COMMON_MOP_SRC_LEN],
|
src_values: &[SimValue<TraceAsString<PRegValue>>; COMMON_MOP_SRC_LEN],
|
||||||
) -> SimValue<TraceAsString<PRegValue>> {
|
) -> SimValue<TraceAsString<PRegValue>> {
|
||||||
|
#[hdl(sim)]
|
||||||
|
let GlobalState { flags_mode } = global_state;
|
||||||
|
#[hdl(sim)]
|
||||||
|
match flags_mode {
|
||||||
|
FlagsMode::PowerISA(_) => {}
|
||||||
|
_ => todo!("flags_mode={flags_mode:?}"),
|
||||||
|
}
|
||||||
#[hdl(sim)]
|
#[hdl(sim)]
|
||||||
let AddSubMOp::<_, _, _> {
|
let AddSubMOp::<_, _, _> {
|
||||||
alu_common,
|
alu_common,
|
||||||
|
|
@ -1732,9 +1739,17 @@ trait MockExecutionStateTrait: Default {
|
||||||
#[hdl]
|
#[hdl]
|
||||||
fn run_compare<C: PhantomConstCpuConfig, SrcCount: KnownSize>(
|
fn run_compare<C: PhantomConstCpuConfig, SrcCount: KnownSize>(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
global_state: &SimValue<GlobalState>,
|
||||||
mop: &SimValue<CompareMOp<PRegNum<C>, PRegNum<C>, SrcCount>>,
|
mop: &SimValue<CompareMOp<PRegNum<C>, PRegNum<C>, SrcCount>>,
|
||||||
src_values: &[SimValue<TraceAsString<PRegValue>>; COMMON_MOP_SRC_LEN],
|
src_values: &[SimValue<TraceAsString<PRegValue>>; COMMON_MOP_SRC_LEN],
|
||||||
) -> SimValue<TraceAsString<PRegValue>> {
|
) -> SimValue<TraceAsString<PRegValue>> {
|
||||||
|
#[hdl(sim)]
|
||||||
|
let GlobalState { flags_mode } = global_state;
|
||||||
|
#[hdl(sim)]
|
||||||
|
match flags_mode {
|
||||||
|
FlagsMode::PowerISA(_) => {}
|
||||||
|
_ => todo!("flags_mode={flags_mode:?}"),
|
||||||
|
}
|
||||||
#[hdl(sim)]
|
#[hdl(sim)]
|
||||||
let CompareMOp::<_, _, _> {
|
let CompareMOp::<_, _, _> {
|
||||||
common,
|
common,
|
||||||
|
|
@ -1791,9 +1806,17 @@ trait MockExecutionStateTrait: Default {
|
||||||
#[hdl]
|
#[hdl]
|
||||||
fn run_shift_rotate<C: PhantomConstCpuConfig>(
|
fn run_shift_rotate<C: PhantomConstCpuConfig>(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
global_state: &SimValue<GlobalState>,
|
||||||
mop: &SimValue<ShiftRotateMOp<PRegNum<C>, PRegNum<C>>>,
|
mop: &SimValue<ShiftRotateMOp<PRegNum<C>, PRegNum<C>>>,
|
||||||
src_values: &[SimValue<TraceAsString<PRegValue>>; COMMON_MOP_SRC_LEN],
|
src_values: &[SimValue<TraceAsString<PRegValue>>; COMMON_MOP_SRC_LEN],
|
||||||
) -> SimValue<TraceAsString<PRegValue>> {
|
) -> SimValue<TraceAsString<PRegValue>> {
|
||||||
|
#[hdl(sim)]
|
||||||
|
let GlobalState { flags_mode } = global_state;
|
||||||
|
#[hdl(sim)]
|
||||||
|
match flags_mode {
|
||||||
|
FlagsMode::PowerISA(_) => {}
|
||||||
|
_ => todo!("flags_mode={flags_mode:?}"),
|
||||||
|
}
|
||||||
#[hdl(sim)]
|
#[hdl(sim)]
|
||||||
let ShiftRotateMOp::<_, _> { alu_common, mode } = mop;
|
let ShiftRotateMOp::<_, _> { alu_common, mode } = mop;
|
||||||
#[hdl(sim)]
|
#[hdl(sim)]
|
||||||
|
|
@ -1973,7 +1996,7 @@ trait MockExecutionStateTrait: Default {
|
||||||
#[hdl]
|
#[hdl]
|
||||||
fn run_branch<C: PhantomConstCpuConfig, SrcCount: KnownSize>(
|
fn run_branch<C: PhantomConstCpuConfig, SrcCount: KnownSize>(
|
||||||
&mut self,
|
&mut self,
|
||||||
id: &SimValue<MOpId>,
|
global_state: &SimValue<GlobalState>,
|
||||||
pc: u64,
|
pc: u64,
|
||||||
fallthrough_pc: u64,
|
fallthrough_pc: u64,
|
||||||
predicted_next_pc: u64,
|
predicted_next_pc: u64,
|
||||||
|
|
@ -1985,6 +2008,13 @@ trait MockExecutionStateTrait: Default {
|
||||||
SimValue<NextPcPredictorOp<C>>,
|
SimValue<NextPcPredictorOp<C>>,
|
||||||
Option<SimValue<UnitCausedCancel<C>>>,
|
Option<SimValue<UnitCausedCancel<C>>>,
|
||||||
) {
|
) {
|
||||||
|
#[hdl(sim)]
|
||||||
|
let GlobalState { flags_mode } = global_state;
|
||||||
|
#[hdl(sim)]
|
||||||
|
match flags_mode {
|
||||||
|
FlagsMode::PowerISA(_) => {}
|
||||||
|
_ => todo!("flags_mode={flags_mode:?}"),
|
||||||
|
}
|
||||||
#[hdl(sim)]
|
#[hdl(sim)]
|
||||||
let BranchMOp::<_, _, _> {
|
let BranchMOp::<_, _, _> {
|
||||||
common,
|
common,
|
||||||
|
|
@ -2093,6 +2123,7 @@ trait MockExecutionStateTrait: Default {
|
||||||
#[hdl]
|
#[hdl]
|
||||||
fn run_mop<C: PhantomConstCpuConfig>(
|
fn run_mop<C: PhantomConstCpuConfig>(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
global_state: &SimValue<GlobalState>,
|
||||||
mop: &SimValue<MOpInstance<RenamedMOp<C>>>,
|
mop: &SimValue<MOpInstance<RenamedMOp<C>>>,
|
||||||
src_values: &[SimValue<TraceAsString<PRegValue>>; COMMON_MOP_SRC_LEN],
|
src_values: &[SimValue<TraceAsString<PRegValue>>; COMMON_MOP_SRC_LEN],
|
||||||
config: C,
|
config: C,
|
||||||
|
|
@ -2106,7 +2137,7 @@ trait MockExecutionStateTrait: Default {
|
||||||
#[hdl(sim)]
|
#[hdl(sim)]
|
||||||
let MOpInstance::<_> {
|
let MOpInstance::<_> {
|
||||||
fetch_block_id: _,
|
fetch_block_id: _,
|
||||||
id,
|
id: _,
|
||||||
pc,
|
pc,
|
||||||
predicted_next_pc,
|
predicted_next_pc,
|
||||||
size_in_bytes,
|
size_in_bytes,
|
||||||
|
|
@ -2135,14 +2166,14 @@ trait MockExecutionStateTrait: Default {
|
||||||
match mop {
|
match mop {
|
||||||
AluBranchMOp::<_, _>::AddSub(mop) => (
|
AluBranchMOp::<_, _>::AddSub(mop) => (
|
||||||
Some((
|
Some((
|
||||||
self.run_add_sub(pc.as_int(), mop, src_values),
|
self.run_add_sub(global_state, pc.as_int(), mop, src_values),
|
||||||
empty_predictor_op(),
|
empty_predictor_op(),
|
||||||
)),
|
)),
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
AluBranchMOp::<_, _>::AddSubI(mop) => (
|
AluBranchMOp::<_, _>::AddSubI(mop) => (
|
||||||
Some((
|
Some((
|
||||||
self.run_add_sub(pc.as_int(), mop, src_values),
|
self.run_add_sub(global_state, pc.as_int(), mop, src_values),
|
||||||
empty_predictor_op(),
|
empty_predictor_op(),
|
||||||
)),
|
)),
|
||||||
None,
|
None,
|
||||||
|
|
@ -2157,20 +2188,29 @@ trait MockExecutionStateTrait: Default {
|
||||||
todo!("implement LogicalI")
|
todo!("implement LogicalI")
|
||||||
}
|
}
|
||||||
AluBranchMOp::<_, _>::ShiftRotate(mop) => (
|
AluBranchMOp::<_, _>::ShiftRotate(mop) => (
|
||||||
Some((self.run_shift_rotate(mop, src_values), empty_predictor_op())),
|
Some((
|
||||||
|
self.run_shift_rotate(global_state, mop, src_values),
|
||||||
|
empty_predictor_op(),
|
||||||
|
)),
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
AluBranchMOp::<_, _>::Compare(mop) => (
|
AluBranchMOp::<_, _>::Compare(mop) => (
|
||||||
Some((self.run_compare(mop, src_values), empty_predictor_op())),
|
Some((
|
||||||
|
self.run_compare(global_state, mop, src_values),
|
||||||
|
empty_predictor_op(),
|
||||||
|
)),
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
AluBranchMOp::<_, _>::CompareI(mop) => (
|
AluBranchMOp::<_, _>::CompareI(mop) => (
|
||||||
Some((self.run_compare(mop, src_values), empty_predictor_op())),
|
Some((
|
||||||
|
self.run_compare(global_state, mop, src_values),
|
||||||
|
empty_predictor_op(),
|
||||||
|
)),
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
AluBranchMOp::<_, _>::Branch(mop) => {
|
AluBranchMOp::<_, _>::Branch(mop) => {
|
||||||
let (value, predictor_op, cancel) = self.run_branch(
|
let (value, predictor_op, cancel) = self.run_branch(
|
||||||
id,
|
global_state,
|
||||||
pc.as_int(),
|
pc.as_int(),
|
||||||
fallthrough_pc,
|
fallthrough_pc,
|
||||||
predicted_next_pc.as_int(),
|
predicted_next_pc.as_int(),
|
||||||
|
|
@ -2182,7 +2222,7 @@ trait MockExecutionStateTrait: Default {
|
||||||
}
|
}
|
||||||
AluBranchMOp::<_, _>::BranchI(mop) => {
|
AluBranchMOp::<_, _>::BranchI(mop) => {
|
||||||
let (value, predictor_op, cancel) = self.run_branch(
|
let (value, predictor_op, cancel) = self.run_branch(
|
||||||
id,
|
global_state,
|
||||||
pc.as_int(),
|
pc.as_int(),
|
||||||
fallthrough_pc,
|
fallthrough_pc,
|
||||||
predicted_next_pc.as_int(),
|
predicted_next_pc.as_int(),
|
||||||
|
|
@ -2270,12 +2310,16 @@ impl<C: PhantomConstCpuConfig> MockUnitOp<C> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[hdl]
|
#[hdl]
|
||||||
fn try_run<E: MockExecutionStateTrait>(&mut self, execution_state: &mut E) {
|
fn try_run<E: MockExecutionStateTrait>(
|
||||||
|
&mut self,
|
||||||
|
global_state: &SimValue<GlobalState>,
|
||||||
|
execution_state: &mut E,
|
||||||
|
) {
|
||||||
if self.output_ready.is_some() || self.caused_cancel.is_some() {
|
if self.output_ready.is_some() || self.caused_cancel.is_some() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let (output, caused_cancel) =
|
let (output, caused_cancel) =
|
||||||
execution_state.run_mop(&self.mop, &self.src_values, self.config);
|
execution_state.run_mop(global_state, &self.mop, &self.src_values, self.config);
|
||||||
assert!(output.is_some() || caused_cancel.is_some());
|
assert!(output.is_some() || caused_cancel.is_some());
|
||||||
println!("try_run: {:#x}: {:?}", self.mop.pc.as_int(), self.mop.mop);
|
println!("try_run: {:#x}: {:?}", self.mop.pc.as_int(), self.mop.mop);
|
||||||
println!("<- {:?}", self.src_values);
|
println!("<- {:?}", self.src_values);
|
||||||
|
|
@ -2318,6 +2362,7 @@ impl<C: PhantomConstCpuConfig> MockUnitOp<C> {
|
||||||
|
|
||||||
#[hdl(no_static)]
|
#[hdl(no_static)]
|
||||||
struct MockUnitDebugState<C: PhantomConstGet<CpuConfig>, E> {
|
struct MockUnitDebugState<C: PhantomConstGet<CpuConfig>, E> {
|
||||||
|
global_state: GlobalState,
|
||||||
ops: ArrayVec<MockUnitOpDebugState<C>, CpuConfigMaxUnitMaxInFlight<C>>,
|
ops: ArrayVec<MockUnitOpDebugState<C>, CpuConfigMaxUnitMaxInFlight<C>>,
|
||||||
execution_state: E,
|
execution_state: E,
|
||||||
config: C,
|
config: C,
|
||||||
|
|
@ -2325,6 +2370,7 @@ struct MockUnitDebugState<C: PhantomConstGet<CpuConfig>, E> {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct MockUnitState<C: PhantomConstCpuConfig, E> {
|
struct MockUnitState<C: PhantomConstCpuConfig, E> {
|
||||||
|
global_state: SimValue<GlobalState>,
|
||||||
ops: BTreeMap<SimValue<MOpId>, MockUnitOp<C>>,
|
ops: BTreeMap<SimValue<MOpId>, MockUnitOp<C>>,
|
||||||
execution_state: E,
|
execution_state: E,
|
||||||
config: C,
|
config: C,
|
||||||
|
|
@ -2332,8 +2378,14 @@ struct MockUnitState<C: PhantomConstCpuConfig, E> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C: PhantomConstCpuConfig, E: MockExecutionStateTrait> MockUnitState<C, E> {
|
impl<C: PhantomConstCpuConfig, E: MockExecutionStateTrait> MockUnitState<C, E> {
|
||||||
fn new(execution_state: E, config: C, unit_index: usize) -> Self {
|
fn new(
|
||||||
|
global_state: SimValue<GlobalState>,
|
||||||
|
execution_state: E,
|
||||||
|
config: C,
|
||||||
|
unit_index: usize,
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
global_state,
|
||||||
ops: BTreeMap::new(),
|
ops: BTreeMap::new(),
|
||||||
execution_state,
|
execution_state,
|
||||||
config,
|
config,
|
||||||
|
|
@ -2343,6 +2395,7 @@ impl<C: PhantomConstCpuConfig, E: MockExecutionStateTrait> MockUnitState<C, E> {
|
||||||
#[hdl]
|
#[hdl]
|
||||||
fn debug_state(&self) -> SimValue<MockUnitDebugState<C, E::DebugState>> {
|
fn debug_state(&self) -> SimValue<MockUnitDebugState<C, E::DebugState>> {
|
||||||
let Self {
|
let Self {
|
||||||
|
global_state,
|
||||||
ops,
|
ops,
|
||||||
execution_state,
|
execution_state,
|
||||||
config,
|
config,
|
||||||
|
|
@ -2352,6 +2405,7 @@ impl<C: PhantomConstCpuConfig, E: MockExecutionStateTrait> MockUnitState<C, E> {
|
||||||
let ret_ty = MockUnitDebugState[*config][execution_state.ty()];
|
let ret_ty = MockUnitDebugState[*config][execution_state.ty()];
|
||||||
#[hdl(sim)]
|
#[hdl(sim)]
|
||||||
MockUnitDebugState::<_, _> {
|
MockUnitDebugState::<_, _> {
|
||||||
|
global_state,
|
||||||
ops: ret_ty
|
ops: ret_ty
|
||||||
.ops
|
.ops
|
||||||
.from_iter_sim(
|
.from_iter_sim(
|
||||||
|
|
@ -2459,7 +2513,7 @@ impl<C: PhantomConstCpuConfig, E: MockExecutionStateTrait> MockUnitState<C, E> {
|
||||||
caused_cancel: None,
|
caused_cancel: None,
|
||||||
config: self.config,
|
config: self.config,
|
||||||
};
|
};
|
||||||
op.try_run(&mut self.execution_state);
|
op.try_run(&self.global_state, &mut self.execution_state);
|
||||||
self.ops.insert(op.mop.id.clone(), op);
|
self.ops.insert(op.mop.id.clone(), op);
|
||||||
}
|
}
|
||||||
#[hdl]
|
#[hdl]
|
||||||
|
|
@ -2470,6 +2524,7 @@ impl<C: PhantomConstCpuConfig, E: MockExecutionStateTrait> MockUnitState<C, E> {
|
||||||
}
|
}
|
||||||
fn cancel_all(&mut self) {
|
fn cancel_all(&mut self) {
|
||||||
let Self {
|
let Self {
|
||||||
|
global_state: _,
|
||||||
ops,
|
ops,
|
||||||
execution_state: _,
|
execution_state: _,
|
||||||
config: _,
|
config: _,
|
||||||
|
|
@ -2503,6 +2558,7 @@ fn mock_unit<#[hdl(skip)] E: MockExecutionStateTrait>(
|
||||||
async |mut sim| {
|
async |mut sim| {
|
||||||
#[hdl]
|
#[hdl]
|
||||||
let ExecuteToUnitInterface::<_> {
|
let ExecuteToUnitInterface::<_> {
|
||||||
|
global_state: _,
|
||||||
enqueue,
|
enqueue,
|
||||||
inputs_ready: _,
|
inputs_ready: _,
|
||||||
is_no_longer_speculative: _,
|
is_no_longer_speculative: _,
|
||||||
|
|
@ -2537,6 +2593,7 @@ fn mock_unit<#[hdl(skip)] E: MockExecutionStateTrait>(
|
||||||
debug_state,
|
debug_state,
|
||||||
#[hdl(sim)]
|
#[hdl(sim)]
|
||||||
MockUnitDebugState::<_, _> {
|
MockUnitDebugState::<_, _> {
|
||||||
|
global_state: zeroed(GlobalState),
|
||||||
ops: zeroed(debug_state.ty().ops),
|
ops: zeroed(debug_state.ty().ops),
|
||||||
execution_state: SimValue::into_bundle(E::zeroed_debug_state()),
|
execution_state: SimValue::into_bundle(E::zeroed_debug_state()),
|
||||||
config,
|
config,
|
||||||
|
|
@ -2569,11 +2626,17 @@ fn mock_unit<#[hdl(skip)] E: MockExecutionStateTrait>(
|
||||||
unit_index: usize,
|
unit_index: usize,
|
||||||
mut sim: ExternModuleSimulationState,
|
mut sim: ExternModuleSimulationState,
|
||||||
) {
|
) {
|
||||||
let mut state = MockUnitState::new(execution_state, config, unit_index);
|
let mut state = MockUnitState::new(
|
||||||
|
sim.read(from_execute.global_state).await,
|
||||||
|
execution_state,
|
||||||
|
config,
|
||||||
|
unit_index,
|
||||||
|
);
|
||||||
loop {
|
loop {
|
||||||
{
|
{
|
||||||
#[hdl]
|
#[hdl]
|
||||||
let ExecuteToUnitInterface::<_> {
|
let ExecuteToUnitInterface::<_> {
|
||||||
|
global_state: _,
|
||||||
enqueue,
|
enqueue,
|
||||||
inputs_ready: _,
|
inputs_ready: _,
|
||||||
is_no_longer_speculative: _,
|
is_no_longer_speculative: _,
|
||||||
|
|
@ -2599,6 +2662,7 @@ fn mock_unit<#[hdl(skip)] E: MockExecutionStateTrait>(
|
||||||
{
|
{
|
||||||
#[hdl]
|
#[hdl]
|
||||||
let ExecuteToUnitInterface::<_> {
|
let ExecuteToUnitInterface::<_> {
|
||||||
|
global_state,
|
||||||
enqueue: _, // we ignore enqueues since we don't need to track order for these instructions
|
enqueue: _, // we ignore enqueues since we don't need to track order for these instructions
|
||||||
inputs_ready,
|
inputs_ready,
|
||||||
is_no_longer_speculative,
|
is_no_longer_speculative,
|
||||||
|
|
@ -2609,6 +2673,7 @@ fn mock_unit<#[hdl(skip)] E: MockExecutionStateTrait>(
|
||||||
cancel_all,
|
cancel_all,
|
||||||
config: _,
|
config: _,
|
||||||
} = from_execute;
|
} = from_execute;
|
||||||
|
state.global_state = sim.read_past(global_state, cd.clk).await;
|
||||||
#[hdl(sim)]
|
#[hdl(sim)]
|
||||||
if let HdlSome(inputs_ready) = sim.read_past(inputs_ready, cd.clk).await {
|
if let HdlSome(inputs_ready) = sim.read_past(inputs_ready, cd.clk).await {
|
||||||
state.handle_inputs_ready(inputs_ready);
|
state.handle_inputs_ready(inputs_ready);
|
||||||
|
|
@ -2665,6 +2730,7 @@ fn mock_combinational_unit<#[hdl(skip)] E: MockExecutionStateTrait>(
|
||||||
let (from_execute, config, unit_index) = args;
|
let (from_execute, config, unit_index) = args;
|
||||||
#[hdl]
|
#[hdl]
|
||||||
let ExecuteToUnitInterface::<_> {
|
let ExecuteToUnitInterface::<_> {
|
||||||
|
global_state: _,
|
||||||
enqueue,
|
enqueue,
|
||||||
inputs_ready: _,
|
inputs_ready: _,
|
||||||
is_no_longer_speculative: _,
|
is_no_longer_speculative: _,
|
||||||
|
|
@ -2708,6 +2774,7 @@ fn mock_combinational_unit<#[hdl(skip)] E: MockExecutionStateTrait>(
|
||||||
loop {
|
loop {
|
||||||
#[hdl]
|
#[hdl]
|
||||||
let ExecuteToUnitInterface::<_> {
|
let ExecuteToUnitInterface::<_> {
|
||||||
|
global_state,
|
||||||
enqueue,
|
enqueue,
|
||||||
inputs_ready,
|
inputs_ready,
|
||||||
is_no_longer_speculative: _, // we don't care about being speculative for these instructions
|
is_no_longer_speculative: _, // we don't care about being speculative for these instructions
|
||||||
|
|
@ -2719,7 +2786,8 @@ fn mock_combinational_unit<#[hdl(skip)] E: MockExecutionStateTrait>(
|
||||||
config: _,
|
config: _,
|
||||||
} = from_execute;
|
} = from_execute;
|
||||||
sim.write(enqueue.ready, true).await; // we ignore enqueues since we don't need to track order for these instructions
|
sim.write(enqueue.ready, true).await; // we ignore enqueues since we don't need to track order for these instructions
|
||||||
let mut state = MockUnitState::new(E::default(), config, unit_index);
|
let global_state = sim.read(global_state).await;
|
||||||
|
let mut state = MockUnitState::new(global_state, E::default(), config, unit_index);
|
||||||
#[hdl(sim)]
|
#[hdl(sim)]
|
||||||
if let HdlSome(inputs_ready) = sim.read(inputs_ready).await {
|
if let HdlSome(inputs_ready) = sim.read(inputs_ready).await {
|
||||||
state.handle_inputs_ready(inputs_ready);
|
state.handle_inputs_ready(inputs_ready);
|
||||||
|
|
@ -3142,6 +3210,7 @@ fn mock_l2_reg_file_unit(config: PhantomConst<CpuConfig>, unit_index: usize) {
|
||||||
async |mut sim| {
|
async |mut sim| {
|
||||||
#[hdl]
|
#[hdl]
|
||||||
let ExecuteToUnitInterface::<_> {
|
let ExecuteToUnitInterface::<_> {
|
||||||
|
global_state: _,
|
||||||
enqueue,
|
enqueue,
|
||||||
inputs_ready: _,
|
inputs_ready: _,
|
||||||
is_no_longer_speculative: _,
|
is_no_longer_speculative: _,
|
||||||
|
|
@ -3203,6 +3272,7 @@ fn mock_l2_reg_file_unit(config: PhantomConst<CpuConfig>, unit_index: usize) {
|
||||||
{
|
{
|
||||||
#[hdl]
|
#[hdl]
|
||||||
let ExecuteToUnitInterface::<_> {
|
let ExecuteToUnitInterface::<_> {
|
||||||
|
global_state: _,
|
||||||
enqueue,
|
enqueue,
|
||||||
inputs_ready: _,
|
inputs_ready: _,
|
||||||
is_no_longer_speculative: _,
|
is_no_longer_speculative: _,
|
||||||
|
|
@ -3226,6 +3296,7 @@ fn mock_l2_reg_file_unit(config: PhantomConst<CpuConfig>, unit_index: usize) {
|
||||||
{
|
{
|
||||||
#[hdl]
|
#[hdl]
|
||||||
let ExecuteToUnitInterface::<_> {
|
let ExecuteToUnitInterface::<_> {
|
||||||
|
global_state: _,
|
||||||
enqueue,
|
enqueue,
|
||||||
inputs_ready,
|
inputs_ready,
|
||||||
is_no_longer_speculative,
|
is_no_longer_speculative,
|
||||||
|
|
@ -3822,6 +3893,7 @@ fn mock_load_store_unit<#[hdl(skip)] MI: MakeInsns>(
|
||||||
async |mut sim| {
|
async |mut sim| {
|
||||||
#[hdl]
|
#[hdl]
|
||||||
let ExecuteToUnitInterface::<_> {
|
let ExecuteToUnitInterface::<_> {
|
||||||
|
global_state: _,
|
||||||
enqueue,
|
enqueue,
|
||||||
inputs_ready: _,
|
inputs_ready: _,
|
||||||
is_no_longer_speculative: _,
|
is_no_longer_speculative: _,
|
||||||
|
|
@ -3901,6 +3973,7 @@ fn mock_load_store_unit<#[hdl(skip)] MI: MakeInsns>(
|
||||||
{
|
{
|
||||||
#[hdl]
|
#[hdl]
|
||||||
let ExecuteToUnitInterface::<_> {
|
let ExecuteToUnitInterface::<_> {
|
||||||
|
global_state: _,
|
||||||
enqueue,
|
enqueue,
|
||||||
inputs_ready: _,
|
inputs_ready: _,
|
||||||
is_no_longer_speculative: _,
|
is_no_longer_speculative: _,
|
||||||
|
|
@ -3926,6 +3999,7 @@ fn mock_load_store_unit<#[hdl(skip)] MI: MakeInsns>(
|
||||||
{
|
{
|
||||||
#[hdl]
|
#[hdl]
|
||||||
let ExecuteToUnitInterface::<_> {
|
let ExecuteToUnitInterface::<_> {
|
||||||
|
global_state: _,
|
||||||
enqueue,
|
enqueue,
|
||||||
inputs_ready,
|
inputs_ready,
|
||||||
is_no_longer_speculative,
|
is_no_longer_speculative,
|
||||||
|
|
@ -3983,10 +4057,16 @@ fn mock_load_store_unit<#[hdl(skip)] MI: MakeInsns>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum AluBranchKind {
|
||||||
|
MockUnit,
|
||||||
|
MockCombinationalUnit,
|
||||||
|
Real,
|
||||||
|
}
|
||||||
|
|
||||||
#[hdl_module]
|
#[hdl_module]
|
||||||
fn rename_execute_retire_test_harness<#[hdl(skip)] MI: MakeInsns>(
|
fn rename_execute_retire_test_harness<#[hdl(skip)] MI: MakeInsns>(
|
||||||
config: PhantomConst<CpuConfig>,
|
config: PhantomConst<CpuConfig>,
|
||||||
alu_branch_is_combinatorial: bool,
|
alu_branch_kind: AluBranchKind,
|
||||||
) {
|
) {
|
||||||
#[hdl]
|
#[hdl]
|
||||||
let cd: ClockDomain = m.input();
|
let cd: ClockDomain = m.input();
|
||||||
|
|
@ -4032,15 +4112,16 @@ fn rename_execute_retire_test_harness<#[hdl(skip)] MI: MakeInsns>(
|
||||||
connect(mock_unit.from_execute, to_unit);
|
connect(mock_unit.from_execute, to_unit);
|
||||||
connect(started_any_l2_reg_file_ops, mock_unit.started_any);
|
connect(started_any_l2_reg_file_ops, mock_unit.started_any);
|
||||||
}
|
}
|
||||||
UnitKind::AluBranch => {
|
UnitKind::AluBranch => match alu_branch_kind {
|
||||||
if alu_branch_is_combinatorial {
|
AluBranchKind::MockCombinationalUnit => {
|
||||||
let mock_unit = instance_with_loc(
|
let mock_unit = instance_with_loc(
|
||||||
&dut.ty().to_units.unit_field_name(unit_index),
|
&dut.ty().to_units.unit_field_name(unit_index),
|
||||||
mock_combinational_unit::<()>(config, unit_index),
|
mock_combinational_unit::<()>(config, unit_index),
|
||||||
SourceLocation::caller(),
|
SourceLocation::caller(),
|
||||||
);
|
);
|
||||||
connect(mock_unit.from_execute, to_unit);
|
connect(mock_unit.from_execute, to_unit);
|
||||||
} else {
|
}
|
||||||
|
AluBranchKind::MockUnit => {
|
||||||
let mock_unit = instance_with_loc(
|
let mock_unit = instance_with_loc(
|
||||||
&dut.ty().to_units.unit_field_name(unit_index),
|
&dut.ty().to_units.unit_field_name(unit_index),
|
||||||
mock_unit::<()>(config, unit_index),
|
mock_unit::<()>(config, unit_index),
|
||||||
|
|
@ -4049,7 +4130,15 @@ fn rename_execute_retire_test_harness<#[hdl(skip)] MI: MakeInsns>(
|
||||||
connect(mock_unit.cd, cd);
|
connect(mock_unit.cd, cd);
|
||||||
connect(mock_unit.from_execute, to_unit);
|
connect(mock_unit.from_execute, to_unit);
|
||||||
}
|
}
|
||||||
}
|
AluBranchKind::Real => {
|
||||||
|
let unit = instance_with_loc(
|
||||||
|
&dut.ty().to_units.unit_field_name(unit_index),
|
||||||
|
cpu::unit::alu_branch::alu_branch(config, unit_index),
|
||||||
|
SourceLocation::caller(),
|
||||||
|
);
|
||||||
|
connect(unit.from_execute, to_unit);
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4124,25 +4213,13 @@ fn test_rename_execute_retire_fibonacci_non_combinatorial() {
|
||||||
config.fetch_width = NonZeroUsize::new(3).unwrap();
|
config.fetch_width = NonZeroUsize::new(3).unwrap();
|
||||||
let m = rename_execute_retire_test_harness::<FibonacciInsns>(
|
let m = rename_execute_retire_test_harness::<FibonacciInsns>(
|
||||||
PhantomConst::new_sized(config),
|
PhantomConst::new_sized(config),
|
||||||
false,
|
AluBranchKind::MockUnit,
|
||||||
);
|
);
|
||||||
let mut sim = Simulation::new(m);
|
let mut sim = Simulation::new(m);
|
||||||
let writer = RcWriter::default();
|
let _checked_vcd_output = checked_vcd_output!(
|
||||||
sim.add_trace_writer(VcdWriterDecls::new(writer.clone()));
|
&mut sim,
|
||||||
struct DumpVcdOnDrop {
|
"tests/expected/rename_execute_retire_fibonacci_non_combinatorial.vcd",
|
||||||
writer: Option<RcWriter>,
|
);
|
||||||
}
|
|
||||||
impl Drop for DumpVcdOnDrop {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
if let Some(mut writer) = self.writer.take() {
|
|
||||||
let vcd = String::from_utf8(writer.take()).unwrap();
|
|
||||||
println!("####### VCD:\n{vcd}\n#######");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let mut writer = DumpVcdOnDrop {
|
|
||||||
writer: Some(writer),
|
|
||||||
};
|
|
||||||
sim.write_clock(sim.io().cd.clk, false);
|
sim.write_clock(sim.io().cd.clk, false);
|
||||||
sim.write_reset(sim.io().cd.rst, true);
|
sim.write_reset(sim.io().cd.rst, true);
|
||||||
for cycle in 0..200 {
|
for cycle in 0..200 {
|
||||||
|
|
@ -4154,12 +4231,6 @@ fn test_rename_execute_retire_fibonacci_non_combinatorial() {
|
||||||
sim.write_reset(sim.io().cd.rst, false);
|
sim.write_reset(sim.io().cd.rst, false);
|
||||||
}
|
}
|
||||||
assert!(sim.read_bool(sim.io().all_outputs_written));
|
assert!(sim.read_bool(sim.io().all_outputs_written));
|
||||||
// FIXME: vcd is just whatever rename_execute_retire does now, which isn't known to be correct
|
|
||||||
let vcd = String::from_utf8(writer.writer.take().unwrap().take()).unwrap();
|
|
||||||
println!("####### VCD:\n{vcd}\n#######");
|
|
||||||
if vcd != include_str!("expected/rename_execute_retire_fibonacci_non_combinatorial.vcd") {
|
|
||||||
panic!();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[hdl]
|
#[hdl]
|
||||||
|
|
@ -4177,25 +4248,15 @@ fn test_rename_execute_retire_fibonacci_combinatorial() {
|
||||||
NonZeroUsize::new(20).unwrap(),
|
NonZeroUsize::new(20).unwrap(),
|
||||||
);
|
);
|
||||||
config.fetch_width = NonZeroUsize::new(3).unwrap();
|
config.fetch_width = NonZeroUsize::new(3).unwrap();
|
||||||
let m =
|
let m = rename_execute_retire_test_harness::<FibonacciInsns>(
|
||||||
rename_execute_retire_test_harness::<FibonacciInsns>(PhantomConst::new_sized(config), true);
|
PhantomConst::new_sized(config),
|
||||||
|
AluBranchKind::MockCombinationalUnit,
|
||||||
|
);
|
||||||
let mut sim = Simulation::new(m);
|
let mut sim = Simulation::new(m);
|
||||||
let writer = RcWriter::default();
|
let _checked_vcd_output = checked_vcd_output!(
|
||||||
sim.add_trace_writer(VcdWriterDecls::new(writer.clone()));
|
&mut sim,
|
||||||
struct DumpVcdOnDrop {
|
"tests/expected/rename_execute_retire_fibonacci_combinatorial.vcd",
|
||||||
writer: Option<RcWriter>,
|
);
|
||||||
}
|
|
||||||
impl Drop for DumpVcdOnDrop {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
if let Some(mut writer) = self.writer.take() {
|
|
||||||
let vcd = String::from_utf8(writer.take()).unwrap();
|
|
||||||
println!("####### VCD:\n{vcd}\n#######");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let mut writer = DumpVcdOnDrop {
|
|
||||||
writer: Some(writer),
|
|
||||||
};
|
|
||||||
sim.write_clock(sim.io().cd.clk, false);
|
sim.write_clock(sim.io().cd.clk, false);
|
||||||
sim.write_reset(sim.io().cd.rst, true);
|
sim.write_reset(sim.io().cd.rst, true);
|
||||||
for cycle in 0..200 {
|
for cycle in 0..200 {
|
||||||
|
|
@ -4207,12 +4268,6 @@ fn test_rename_execute_retire_fibonacci_combinatorial() {
|
||||||
sim.write_reset(sim.io().cd.rst, false);
|
sim.write_reset(sim.io().cd.rst, false);
|
||||||
}
|
}
|
||||||
assert!(sim.read_bool(sim.io().all_outputs_written));
|
assert!(sim.read_bool(sim.io().all_outputs_written));
|
||||||
// FIXME: vcd is just whatever rename_execute_retire does now, which isn't known to be correct
|
|
||||||
let vcd = String::from_utf8(writer.writer.take().unwrap().take()).unwrap();
|
|
||||||
println!("####### VCD:\n{vcd}\n#######");
|
|
||||||
if vcd != include_str!("expected/rename_execute_retire_fibonacci_combinatorial.vcd") {
|
|
||||||
panic!();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SlowLoopInsns;
|
struct SlowLoopInsns;
|
||||||
|
|
@ -4310,25 +4365,15 @@ fn test_rename_execute_retire_slow_loop() {
|
||||||
NonZeroUsize::new(20).unwrap(),
|
NonZeroUsize::new(20).unwrap(),
|
||||||
);
|
);
|
||||||
config.fetch_width = NonZeroUsize::new(4).unwrap();
|
config.fetch_width = NonZeroUsize::new(4).unwrap();
|
||||||
let m =
|
let m = rename_execute_retire_test_harness::<SlowLoopInsns>(
|
||||||
rename_execute_retire_test_harness::<SlowLoopInsns>(PhantomConst::new_sized(config), true);
|
PhantomConst::new_sized(config),
|
||||||
|
AluBranchKind::MockCombinationalUnit,
|
||||||
|
);
|
||||||
let mut sim = Simulation::new(m);
|
let mut sim = Simulation::new(m);
|
||||||
let writer = RcWriter::default();
|
let _checked_vcd_output = checked_vcd_output!(
|
||||||
sim.add_trace_writer(VcdWriterDecls::new(writer.clone()));
|
&mut sim,
|
||||||
struct DumpVcdOnDrop {
|
"tests/expected/rename_execute_retire_slow_loop.vcd",
|
||||||
writer: Option<RcWriter>,
|
);
|
||||||
}
|
|
||||||
impl Drop for DumpVcdOnDrop {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
if let Some(mut writer) = self.writer.take() {
|
|
||||||
let vcd = String::from_utf8(writer.take()).unwrap();
|
|
||||||
println!("####### VCD:\n{vcd}\n#######");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let mut writer = DumpVcdOnDrop {
|
|
||||||
writer: Some(writer),
|
|
||||||
};
|
|
||||||
sim.write_clock(sim.io().cd.clk, false);
|
sim.write_clock(sim.io().cd.clk, false);
|
||||||
sim.write_reset(sim.io().cd.rst, true);
|
sim.write_reset(sim.io().cd.rst, true);
|
||||||
for cycle in 0..350 {
|
for cycle in 0..350 {
|
||||||
|
|
@ -4342,12 +4387,6 @@ fn test_rename_execute_retire_slow_loop() {
|
||||||
assert!(sim.read_bool(sim.io().all_outputs_written));
|
assert!(sim.read_bool(sim.io().all_outputs_written));
|
||||||
// make sure we're actually testing L2 reg file ops
|
// make sure we're actually testing L2 reg file ops
|
||||||
assert!(sim.read_bool(sim.io().started_any_l2_reg_file_ops));
|
assert!(sim.read_bool(sim.io().started_any_l2_reg_file_ops));
|
||||||
// FIXME: vcd is just whatever rename_execute_retire does now, which isn't known to be correct
|
|
||||||
let vcd = String::from_utf8(writer.writer.take().unwrap().take()).unwrap();
|
|
||||||
println!("####### VCD:\n{vcd}\n#######");
|
|
||||||
if vcd != include_str!("expected/rename_execute_retire_slow_loop.vcd") {
|
|
||||||
panic!();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// equivalent of Unix's `head -n1`
|
/// equivalent of Unix's `head -n1`
|
||||||
|
|
@ -4449,25 +4488,13 @@ fn test_rename_execute_retire_head_n1() {
|
||||||
NonZeroUsize::new(20).unwrap(),
|
NonZeroUsize::new(20).unwrap(),
|
||||||
);
|
);
|
||||||
config.fetch_width = NonZeroUsize::new(2).unwrap();
|
config.fetch_width = NonZeroUsize::new(2).unwrap();
|
||||||
let m =
|
let m = rename_execute_retire_test_harness::<HeadN1Insns>(
|
||||||
rename_execute_retire_test_harness::<HeadN1Insns>(PhantomConst::new_sized(config), true);
|
PhantomConst::new_sized(config),
|
||||||
|
AluBranchKind::MockCombinationalUnit,
|
||||||
|
);
|
||||||
let mut sim = Simulation::new(m);
|
let mut sim = Simulation::new(m);
|
||||||
let writer = RcWriter::default();
|
let _checked_vcd_output =
|
||||||
sim.add_trace_writer(VcdWriterDecls::new(writer.clone()));
|
checked_vcd_output!(&mut sim, "tests/expected/rename_execute_retire_head_n1.vcd");
|
||||||
struct DumpVcdOnDrop {
|
|
||||||
writer: Option<RcWriter>,
|
|
||||||
}
|
|
||||||
impl Drop for DumpVcdOnDrop {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
if let Some(mut writer) = self.writer.take() {
|
|
||||||
let vcd = String::from_utf8(writer.take()).unwrap();
|
|
||||||
println!("####### VCD:\n{vcd}\n#######");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let mut writer = DumpVcdOnDrop {
|
|
||||||
writer: Some(writer),
|
|
||||||
};
|
|
||||||
sim.write_clock(sim.io().cd.clk, false);
|
sim.write_clock(sim.io().cd.clk, false);
|
||||||
sim.write_reset(sim.io().cd.rst, true);
|
sim.write_reset(sim.io().cd.rst, true);
|
||||||
for cycle in 0..300 {
|
for cycle in 0..300 {
|
||||||
|
|
@ -4479,12 +4506,6 @@ fn test_rename_execute_retire_head_n1() {
|
||||||
sim.write_reset(sim.io().cd.rst, false);
|
sim.write_reset(sim.io().cd.rst, false);
|
||||||
}
|
}
|
||||||
assert!(sim.read_bool(sim.io().all_outputs_written));
|
assert!(sim.read_bool(sim.io().all_outputs_written));
|
||||||
// FIXME: vcd is just whatever rename_execute_retire does now, which isn't known to be correct
|
|
||||||
let vcd = String::from_utf8(writer.writer.take().unwrap().take()).unwrap();
|
|
||||||
println!("####### VCD:\n{vcd}\n#######");
|
|
||||||
if vcd != include_str!("expected/rename_execute_retire_head_n1.vcd") {
|
|
||||||
panic!();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SaveRestoreGprsInsns;
|
struct SaveRestoreGprsInsns;
|
||||||
|
|
@ -4566,25 +4587,49 @@ fn test_rename_execute_retire_save_restore_gprs() {
|
||||||
config.fetch_width = NonZeroUsize::new(2).unwrap();
|
config.fetch_width = NonZeroUsize::new(2).unwrap();
|
||||||
let m = rename_execute_retire_test_harness::<SaveRestoreGprsInsns>(
|
let m = rename_execute_retire_test_harness::<SaveRestoreGprsInsns>(
|
||||||
PhantomConst::new_sized(config),
|
PhantomConst::new_sized(config),
|
||||||
true,
|
AluBranchKind::MockCombinationalUnit,
|
||||||
);
|
);
|
||||||
let mut sim = Simulation::new(m);
|
let mut sim = Simulation::new(m);
|
||||||
let writer = RcWriter::default();
|
let _checked_vcd_output = checked_vcd_output!(
|
||||||
sim.add_trace_writer(VcdWriterDecls::new(writer.clone()));
|
&mut sim,
|
||||||
struct DumpVcdOnDrop {
|
"tests/expected/rename_execute_retire_save_restore_gprs.vcd",
|
||||||
writer: Option<RcWriter>,
|
);
|
||||||
}
|
sim.write_clock(sim.io().cd.clk, false);
|
||||||
impl Drop for DumpVcdOnDrop {
|
sim.write_reset(sim.io().cd.rst, true);
|
||||||
fn drop(&mut self) {
|
for cycle in 0..700 {
|
||||||
if let Some(mut writer) = self.writer.take() {
|
sim.advance_time(SimDuration::from_nanos(500));
|
||||||
let vcd = String::from_utf8(writer.take()).unwrap();
|
println!("clock tick: {cycle}");
|
||||||
println!("####### VCD:\n{vcd}\n#######");
|
sim.write_clock(sim.io().cd.clk, true);
|
||||||
}
|
sim.advance_time(SimDuration::from_nanos(500));
|
||||||
}
|
sim.write_clock(sim.io().cd.clk, false);
|
||||||
}
|
sim.write_reset(sim.io().cd.rst, false);
|
||||||
let mut writer = DumpVcdOnDrop {
|
}
|
||||||
writer: Some(writer),
|
assert!(sim.read_bool(sim.io().all_outputs_written));
|
||||||
};
|
}
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
#[test]
|
||||||
|
fn test_rename_execute_retire_save_restore_gprs_real() {
|
||||||
|
let _n = SourceLocation::normalize_files_for_tests();
|
||||||
|
let mut config = CpuConfig::new(
|
||||||
|
vec![
|
||||||
|
UnitConfig::new(UnitKind::AluBranch),
|
||||||
|
UnitConfig::new(UnitKind::AluBranch),
|
||||||
|
UnitConfig::new(UnitKind::LoadStore),
|
||||||
|
UnitConfig::new(UnitKind::TransformedMove),
|
||||||
|
],
|
||||||
|
NonZeroUsize::new(20).unwrap(),
|
||||||
|
);
|
||||||
|
config.fetch_width = NonZeroUsize::new(2).unwrap();
|
||||||
|
let m = rename_execute_retire_test_harness::<SaveRestoreGprsInsns>(
|
||||||
|
PhantomConst::new_sized(config),
|
||||||
|
AluBranchKind::Real,
|
||||||
|
);
|
||||||
|
let mut sim = Simulation::new(m);
|
||||||
|
let _checked_vcd_output = checked_vcd_output!(
|
||||||
|
&mut sim,
|
||||||
|
"tests/expected/rename_execute_retire_save_restore_gprs_real.vcd",
|
||||||
|
);
|
||||||
sim.write_clock(sim.io().cd.clk, false);
|
sim.write_clock(sim.io().cd.clk, false);
|
||||||
sim.write_reset(sim.io().cd.rst, true);
|
sim.write_reset(sim.io().cd.rst, true);
|
||||||
for cycle in 0..700 {
|
for cycle in 0..700 {
|
||||||
|
|
@ -4596,10 +4641,4 @@ fn test_rename_execute_retire_save_restore_gprs() {
|
||||||
sim.write_reset(sim.io().cd.rst, false);
|
sim.write_reset(sim.io().cd.rst, false);
|
||||||
}
|
}
|
||||||
assert!(sim.read_bool(sim.io().all_outputs_written));
|
assert!(sim.read_bool(sim.io().all_outputs_written));
|
||||||
// FIXME: vcd is just whatever rename_execute_retire does now, which isn't known to be correct
|
|
||||||
let vcd = String::from_utf8(writer.writer.take().unwrap().take()).unwrap();
|
|
||||||
println!("####### VCD:\n{vcd}\n#######");
|
|
||||||
if vcd != include_str!("expected/rename_execute_retire_save_restore_gprs.vcd") {
|
|
||||||
panic!();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue