forked from libre-chip/cpu
WIP adding rename_execute_retire
This commit is contained in:
parent
6ed04c809e
commit
3fe335541b
12 changed files with 99981 additions and 174 deletions
|
|
@ -33,3 +33,6 @@ hex-literal.workspace = true
|
|||
regex = "1.12.2"
|
||||
sha2.workspace = true
|
||||
which.workspace = true
|
||||
|
||||
[lints.rust]
|
||||
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(todo)'] }
|
||||
|
|
|
|||
|
|
@ -1,13 +1,7 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
use crate::{
|
||||
instruction::{CONST_ZERO_UNIT_NUM, MOpTrait, PRegNum, RenamedMOp, UnitNum, UnitOutRegNum},
|
||||
unit::{
|
||||
UnitCancelInput, UnitKind, UnitOutputWrite,
|
||||
unit_base::{UnitForwardingInfo, UnitToRegAlloc},
|
||||
},
|
||||
};
|
||||
use fayalite::prelude::*;
|
||||
use crate::{instruction::CONST_ZERO_UNIT_NUM, unit::UnitKind};
|
||||
use fayalite::{expr::HdlPartialOrdImpl, prelude::*};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::num::NonZeroUsize;
|
||||
|
||||
|
|
@ -101,54 +95,20 @@ impl CpuConfig {
|
|||
pub fn unit_num_width(&self) -> usize {
|
||||
UInt::range(CONST_ZERO_UNIT_NUM..self.non_const_unit_nums().end).width()
|
||||
}
|
||||
pub fn unit_num(&self) -> UnitNum<DynSize> {
|
||||
UnitNum[self.unit_num_width()]
|
||||
}
|
||||
pub fn unit_out_reg_num(&self) -> UnitOutRegNum<DynSize> {
|
||||
UnitOutRegNum[self.out_reg_num_width]
|
||||
}
|
||||
pub fn p_reg_num(&self) -> PRegNum<DynSize, DynSize> {
|
||||
PRegNum[self.unit_num_width()][self.out_reg_num_width]
|
||||
}
|
||||
pub fn p_reg_num_width(&self) -> usize {
|
||||
self.unit_num_width() + self.out_reg_num_width
|
||||
}
|
||||
pub fn renamed_mop_in_unit(&self) -> RenamedMOp<UnitOutRegNum<DynSize>, DynSize> {
|
||||
RenamedMOp[self.unit_out_reg_num()][self.p_reg_num_width()]
|
||||
}
|
||||
pub fn unit_output_write(&self) -> UnitOutputWrite<DynSize> {
|
||||
UnitOutputWrite[self.out_reg_num_width]
|
||||
}
|
||||
pub fn unit_output_writes(&self) -> Array<HdlOption<UnitOutputWrite<DynSize>>> {
|
||||
Array[HdlOption[self.unit_output_write()]][self.non_const_unit_nums().len()]
|
||||
}
|
||||
pub fn unit_cancel_input(&self) -> UnitCancelInput<DynSize> {
|
||||
UnitCancelInput[self.out_reg_num_width]
|
||||
}
|
||||
pub fn unit_forwarding_info(&self) -> UnitForwardingInfo<DynSize, DynSize, DynSize> {
|
||||
UnitForwardingInfo[self.unit_num_width()][self.out_reg_num_width]
|
||||
[self.non_const_unit_nums().len()]
|
||||
}
|
||||
pub fn unit_max_in_flight(&self, unit_index: usize) -> NonZeroUsize {
|
||||
self.units[unit_index]
|
||||
.max_in_flight
|
||||
.unwrap_or(self.default_unit_max_in_flight)
|
||||
}
|
||||
pub fn unit_to_reg_alloc<
|
||||
MOp: Type + MOpTrait<DestReg = UnitOutRegNum<DynSize>, SrcRegWidth = DynSize>,
|
||||
ExtraOut: Type,
|
||||
>(
|
||||
&self,
|
||||
mop_ty: MOp,
|
||||
extra_out_ty: ExtraOut,
|
||||
) -> UnitToRegAlloc<MOp, ExtraOut, DynSize, DynSize, DynSize> {
|
||||
assert_eq!(
|
||||
mop_ty.dest_reg_ty(),
|
||||
self.unit_out_reg_num(),
|
||||
"inconsistent types",
|
||||
);
|
||||
UnitToRegAlloc[mop_ty][extra_out_ty][self.unit_num_width()][self.out_reg_num_width]
|
||||
[self.non_const_unit_nums().len()]
|
||||
/// the maximum of all [`unit_max_in_flight()`][Self::unit_max_in_flight()]
|
||||
pub fn max_unit_max_in_flight(&self) -> NonZeroUsize {
|
||||
(0..self.units.len())
|
||||
.map(|unit_index| self.unit_max_in_flight(unit_index))
|
||||
.max()
|
||||
.unwrap_or(self.default_unit_max_in_flight)
|
||||
}
|
||||
pub fn fetch_width_in_bytes(&self) -> usize {
|
||||
1usize
|
||||
|
|
@ -188,6 +148,21 @@ impl CpuConfig {
|
|||
}
|
||||
}
|
||||
|
||||
#[hdl(get(|c| c.out_reg_num_width))]
|
||||
pub type CpuConfigOutRegNumWidth<C: PhantomConstGet<CpuConfig>> = DynSize;
|
||||
|
||||
#[hdl(get(|c| c.unit_num_width()))]
|
||||
pub type CpuConfigUnitNumWidth<C: PhantomConstGet<CpuConfig>> = DynSize;
|
||||
|
||||
#[hdl(get(|c| c.p_reg_num_width()))]
|
||||
pub type CpuConfigPRegNumWidth<C: PhantomConstGet<CpuConfig>> = DynSize;
|
||||
|
||||
#[hdl(get(|c| 1 << c.out_reg_num_width))]
|
||||
pub type CpuConfig2PowOutRegNumWidth<C: PhantomConstGet<CpuConfig>> = DynSize;
|
||||
|
||||
#[hdl(get(|c| c.units.len()))]
|
||||
pub type CpuConfigUnitCount<C: PhantomConstGet<CpuConfig>> = DynSize;
|
||||
|
||||
#[hdl(get(|c| c.fetch_width.get()))]
|
||||
pub type CpuConfigFetchWidth<C: PhantomConstGet<CpuConfig>> = DynSize;
|
||||
|
||||
|
|
@ -236,6 +211,10 @@ pub type CpuConfigL1ICacheMaxMissesInFlight<C: PhantomConstGet<CpuConfig>> = Dyn
|
|||
#[hdl(get(|c| c.rob_size.get()))]
|
||||
pub type CpuConfigRobSize<C: PhantomConstGet<CpuConfig>> = DynSize;
|
||||
|
||||
/// the maximum of all [`unit_max_in_flight()`][CpuConfig::unit_max_in_flight()]
|
||||
#[hdl(get(|c| c.max_unit_max_in_flight().get()))]
|
||||
pub type CpuConfigMaxUnitMaxInFlight<C: PhantomConstGet<CpuConfig>> = DynSize;
|
||||
|
||||
pub trait PhantomConstCpuConfig:
|
||||
PhantomConstGet<CpuConfig>
|
||||
+ Into<PhantomConst<CpuConfig>>
|
||||
|
|
@ -243,6 +222,7 @@ pub trait PhantomConstCpuConfig:
|
|||
+ Type
|
||||
+ ToSimValue<Type = Self>
|
||||
+ ToExpr<Type = Self>
|
||||
+ HdlPartialOrdImpl<Self>
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -192,6 +192,13 @@ impl MOpRegNum {
|
|||
power_isa_gpr_or_zero_reg
|
||||
}
|
||||
#[hdl]
|
||||
pub fn power_isa_gpr_or_zero_reg_imm(index: usize) -> Expr<Self> {
|
||||
#[hdl]
|
||||
Self {
|
||||
value: Self::power_isa_gpr_or_zero_reg_num(index).cast_to_static::<UInt<_>>(),
|
||||
}
|
||||
}
|
||||
#[hdl]
|
||||
pub fn power_isa_gpr_or_zero_reg_sim(reg_num: &SimValue<UInt<5>>) -> SimValue<Self> {
|
||||
#[hdl(sim)]
|
||||
Self {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,9 @@ pub mod instruction;
|
|||
pub mod main_memory_and_io;
|
||||
pub mod next_pc;
|
||||
pub mod powerisa_instructions_xml;
|
||||
#[cfg(todo)]
|
||||
pub mod reg_alloc;
|
||||
pub mod register;
|
||||
pub mod rename_execute_retire;
|
||||
pub mod unit;
|
||||
pub mod util;
|
||||
|
|
|
|||
1127
crates/cpu/src/rename_execute_retire.rs
Normal file
1127
crates/cpu/src/rename_execute_retire.rs
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -2,7 +2,7 @@
|
|||
// See Notices.txt for copyright information
|
||||
|
||||
use crate::{
|
||||
config::CpuConfig,
|
||||
config::{CpuConfig, PhantomConstCpuConfig},
|
||||
instruction::{
|
||||
AluBranchMOp, LoadStoreMOp, MOp, MOpDestReg, MOpInto, MOpRegNum, MOpTrait,
|
||||
MOpVariantVisitOps, MOpVariantVisitor, MOpVisitVariants, RenamedMOp, UnitOutRegNum,
|
||||
|
|
@ -32,7 +32,7 @@ macro_rules! all_units {
|
|||
$(
|
||||
$(#[transformed_move $($transformed_move:tt)*])?
|
||||
#[create_dyn_unit_fn = $create_dyn_unit_fn:expr]
|
||||
#[extract = $extract:ident]
|
||||
#[extract($extract:ident, $extract_sim:ident, $extract_sim_ref:ident, $extract_sim_mut:ident)]
|
||||
$(#[$variant_meta:meta])*
|
||||
$Unit:ident($Op:ty),
|
||||
)*
|
||||
|
|
@ -48,7 +48,7 @@ macro_rules! all_units {
|
|||
}
|
||||
|
||||
impl $UnitKind {
|
||||
pub fn unit(self, config: &CpuConfig, unit_index: usize) -> DynUnit {
|
||||
pub fn unit(self, config: PhantomConst<CpuConfig>, unit_index: usize) -> DynUnit {
|
||||
match self {
|
||||
$($UnitKind::$Unit => $create_dyn_unit_fn(config, unit_index),)*
|
||||
}
|
||||
|
|
@ -82,6 +82,7 @@ macro_rules! all_units {
|
|||
}
|
||||
|
||||
mop_enum! {
|
||||
#[debug(where $TransformedMoveOp: crate::instruction::MOpDebug)]
|
||||
#[impl_mop_into = false]
|
||||
#[hdl]
|
||||
$(#[$enum_meta])*
|
||||
|
|
@ -112,6 +113,15 @@ macro_rules! all_units {
|
|||
}
|
||||
unit_kind
|
||||
}
|
||||
#[hdl]
|
||||
$vis fn kind_sim(expr: &SimValue<Self>) -> UnitKind {
|
||||
#![allow(unreachable_patterns)]
|
||||
#[hdl(sim)]
|
||||
match expr {
|
||||
$(Self::$Unit(_) => $UnitKind::$Unit,)*
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
$(
|
||||
#[hdl]
|
||||
$vis fn $extract(expr: impl ToExpr<Type = Self>) -> Expr<HdlOption<$Op>> {
|
||||
|
|
@ -126,6 +136,34 @@ macro_rules! all_units {
|
|||
}
|
||||
$extract
|
||||
}
|
||||
#[hdl]
|
||||
$vis fn $extract_sim(expr: impl ToSimValue<Type = Self>) -> Option<SimValue<$Op>> {
|
||||
let expr = expr.into_sim_value();
|
||||
#[hdl(sim)]
|
||||
if let Self::$Unit(v) = expr {
|
||||
Some(v)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
#[hdl]
|
||||
$vis fn $extract_sim_ref(expr: &SimValue<Self>) -> Option<&SimValue<$Op>> {
|
||||
#[hdl(sim)]
|
||||
if let Self::$Unit(v) = expr {
|
||||
Some(v)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
#[hdl]
|
||||
$vis fn $extract_sim_mut(expr: &mut SimValue<Self>) -> Option<&mut SimValue<$Op>> {
|
||||
#[hdl(sim)]
|
||||
if let Self::$Unit(v) = expr {
|
||||
Some(v)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
)*
|
||||
$vis fn with_transformed_move_op_ty<T>(self, new_transformed_move_op_ty: T) -> $UnitMOpEnum<$DestReg, $SrcRegWidth, T>
|
||||
where
|
||||
|
|
@ -196,6 +234,45 @@ macro_rules! all_units {
|
|||
}
|
||||
with_transformed_move_op
|
||||
}
|
||||
#[hdl]
|
||||
$vis fn try_with_transformed_move_op_sim<T, E>(
|
||||
this: impl ToSimValue<Type = Self>,
|
||||
new_transformed_move_op_ty: T,
|
||||
f: impl FnOnce(SimValue<$TransformedMoveOp>) -> Result<SimValue<$UnitMOpEnum<$DestReg, $SrcRegWidth, T>>, E>,
|
||||
) -> Result<SimValue<$UnitMOpEnum<$DestReg, $SrcRegWidth, T>>, E>
|
||||
where
|
||||
T: MOpTrait<DestReg = $DestReg, SrcRegWidth = $SrcRegWidth>,
|
||||
$TransformedMoveOp: MOpTrait<DestReg = $DestReg, SrcRegWidth = $SrcRegWidth>,
|
||||
{
|
||||
#![allow(unreachable_patterns)]
|
||||
let this = this.into_sim_value();
|
||||
let new_ty = this.ty().with_transformed_move_op_ty(new_transformed_move_op_ty);
|
||||
#[hdl(sim)]
|
||||
match this {
|
||||
$(Self::$BeforeUnit(unit) => Ok(
|
||||
#[hdl(sim)]
|
||||
new_ty.$BeforeUnit(unit)
|
||||
),)*
|
||||
Self::$TransformedMove(unit) => f(unit),
|
||||
$(Self::$AfterUnit(unit) => Ok(
|
||||
#[hdl(sim)]
|
||||
new_ty.$AfterUnit(unit)
|
||||
),)*
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
$vis fn with_transformed_move_op_sim<T>(
|
||||
this: impl ToSimValue<Type = Self>,
|
||||
new_transformed_move_op_ty: T,
|
||||
f: impl FnOnce(SimValue<$TransformedMoveOp>) -> SimValue<$UnitMOpEnum<$DestReg, $SrcRegWidth, T>>,
|
||||
) -> SimValue<$UnitMOpEnum<$DestReg, $SrcRegWidth, T>>
|
||||
where
|
||||
T: MOpTrait<DestReg = $DestReg, SrcRegWidth = $SrcRegWidth>,
|
||||
$TransformedMoveOp: MOpTrait<DestReg = $DestReg, SrcRegWidth = $SrcRegWidth>,
|
||||
{
|
||||
let Ok::<_, std::convert::Infallible>(retval) = Self::try_with_transformed_move_op_sim(this, new_transformed_move_op_ty, move |v| Ok(f(v)));
|
||||
retval
|
||||
}
|
||||
}
|
||||
|
||||
const _: () = {
|
||||
|
|
@ -254,14 +331,14 @@ all_units! {
|
|||
})] TransformedMoveOp: Type
|
||||
> {
|
||||
#[create_dyn_unit_fn = |config, unit_index| alu_branch::AluBranch::new(config, unit_index).to_dyn()]
|
||||
#[extract = alu_branch_mop]
|
||||
#[extract(alu_branch_mop, alu_branch_mop_sim, alu_branch_mop_sim_ref, alu_branch_mop_sim_mut)]
|
||||
AluBranch(AluBranchMOp<DestReg, SrcRegWidth>),
|
||||
#[transformed_move]
|
||||
#[create_dyn_unit_fn = |config, unit_index| todo!()]
|
||||
#[extract = transformed_move_mop]
|
||||
#[extract(transformed_move_mop, transformed_move_mop_sim, transformed_move_mop_sim_ref, transformed_move_mop_sim_mut)]
|
||||
TransformedMove(TransformedMoveOp),
|
||||
#[create_dyn_unit_fn = |config, unit_index| todo!()]
|
||||
#[extract = load_store_mop]
|
||||
#[extract(load_store_mop, load_store_mop_sim, load_store_mop_sim_ref, load_store_mop_sim_mut)]
|
||||
LoadStore(LoadStoreMOp<DestReg, SrcRegWidth>),
|
||||
}
|
||||
}
|
||||
|
|
@ -277,9 +354,9 @@ pub struct UnitResultCompleted<ExtraOut> {
|
|||
pub extra_out: ExtraOut,
|
||||
}
|
||||
|
||||
#[hdl(cmp_eq)]
|
||||
pub struct UnitOutputWrite<OutRegNumWidth: Size> {
|
||||
pub which: UnitOutRegNum<OutRegNumWidth>,
|
||||
#[hdl(cmp_eq, no_static)]
|
||||
pub struct UnitOutputWrite<C: PhantomConstGet<CpuConfig>> {
|
||||
pub which: UnitOutRegNum<C>,
|
||||
pub value: PRegValue,
|
||||
}
|
||||
|
||||
|
|
@ -300,21 +377,21 @@ impl<ExtraOut: Type> UnitResult<ExtraOut> {
|
|||
}
|
||||
}
|
||||
|
||||
#[hdl]
|
||||
pub struct UnitOutput<OutRegNumWidth: Size, ExtraOut> {
|
||||
pub which: UnitOutRegNum<OutRegNumWidth>,
|
||||
#[hdl(no_static)]
|
||||
pub struct UnitOutput<C: PhantomConstGet<CpuConfig>, ExtraOut> {
|
||||
pub which: UnitOutRegNum<C>,
|
||||
pub result: UnitResult<ExtraOut>,
|
||||
}
|
||||
|
||||
impl<OutRegNumWidth: Size, ExtraOut: Type> UnitOutput<OutRegNumWidth, ExtraOut> {
|
||||
impl<C: PhantomConstCpuConfig, ExtraOut: Type> UnitOutput<C, ExtraOut> {
|
||||
pub fn extra_out_ty(self) -> ExtraOut {
|
||||
self.result.extra_out_ty()
|
||||
}
|
||||
}
|
||||
|
||||
#[hdl(cmp_eq)]
|
||||
pub struct UnitCancelInput<OutRegNumWidth: Size> {
|
||||
pub which: UnitOutRegNum<OutRegNumWidth>,
|
||||
#[hdl(cmp_eq, no_static)]
|
||||
pub struct UnitCancelInput<C: PhantomConstGet<CpuConfig>> {
|
||||
pub which: UnitOutRegNum<C>,
|
||||
}
|
||||
|
||||
pub trait UnitTrait:
|
||||
|
|
@ -332,7 +409,7 @@ pub trait UnitTrait:
|
|||
|
||||
fn extract_mop(
|
||||
&self,
|
||||
mop: Expr<RenamedMOp<UnitOutRegNum<DynSize>, DynSize>>,
|
||||
mop: Expr<RenamedMOp<UnitOutRegNum<PhantomConst<CpuConfig>>, DynSize>>,
|
||||
) -> Expr<HdlOption<Self::MOp>>;
|
||||
|
||||
fn module(&self) -> Interned<Module<Self::Type>>;
|
||||
|
|
@ -340,7 +417,7 @@ pub trait UnitTrait:
|
|||
fn unit_to_reg_alloc(
|
||||
&self,
|
||||
this: Expr<Self::Type>,
|
||||
) -> Expr<UnitToRegAlloc<Self::MOp, Self::ExtraOut, DynSize, DynSize, DynSize>>;
|
||||
) -> Expr<UnitToRegAlloc<PhantomConst<CpuConfig>, Self::MOp, Self::ExtraOut>>;
|
||||
|
||||
fn cd(&self, this: Expr<Self::Type>) -> Expr<ClockDomain>;
|
||||
|
||||
|
|
@ -390,7 +467,7 @@ impl UnitTrait for DynUnit {
|
|||
|
||||
fn extract_mop(
|
||||
&self,
|
||||
mop: Expr<RenamedMOp<UnitOutRegNum<DynSize>, DynSize>>,
|
||||
mop: Expr<RenamedMOp<UnitOutRegNum<PhantomConst<CpuConfig>>, DynSize>>,
|
||||
) -> Expr<HdlOption<Self::MOp>> {
|
||||
self.unit.extract_mop(mop)
|
||||
}
|
||||
|
|
@ -402,7 +479,7 @@ impl UnitTrait for DynUnit {
|
|||
fn unit_to_reg_alloc(
|
||||
&self,
|
||||
this: Expr<Self::Type>,
|
||||
) -> Expr<UnitToRegAlloc<Self::MOp, Self::ExtraOut, DynSize, DynSize, DynSize>> {
|
||||
) -> Expr<UnitToRegAlloc<PhantomConst<CpuConfig>, Self::MOp, Self::ExtraOut>> {
|
||||
self.unit.unit_to_reg_alloc(this)
|
||||
}
|
||||
|
||||
|
|
@ -445,7 +522,7 @@ impl<T: UnitTrait + Clone + std::hash::Hash + Eq> UnitTrait for DynUnitWrapper<T
|
|||
|
||||
fn extract_mop(
|
||||
&self,
|
||||
mop: Expr<RenamedMOp<UnitOutRegNum<DynSize>, DynSize>>,
|
||||
mop: Expr<RenamedMOp<UnitOutRegNum<PhantomConst<CpuConfig>>, DynSize>>,
|
||||
) -> Expr<HdlOption<Self::MOp>> {
|
||||
Expr::from_enum(Expr::as_enum(self.0.extract_mop(mop)))
|
||||
}
|
||||
|
|
@ -457,7 +534,7 @@ impl<T: UnitTrait + Clone + std::hash::Hash + Eq> UnitTrait for DynUnitWrapper<T
|
|||
fn unit_to_reg_alloc(
|
||||
&self,
|
||||
this: Expr<Self::Type>,
|
||||
) -> Expr<UnitToRegAlloc<Self::MOp, Self::ExtraOut, DynSize, DynSize, DynSize>> {
|
||||
) -> Expr<UnitToRegAlloc<PhantomConst<CpuConfig>, Self::MOp, Self::ExtraOut>> {
|
||||
Expr::from_bundle(Expr::as_bundle(
|
||||
self.0.unit_to_reg_alloc(Expr::from_bundle(this)),
|
||||
))
|
||||
|
|
|
|||
|
|
@ -19,16 +19,13 @@ use crate::{
|
|||
},
|
||||
};
|
||||
use fayalite::{
|
||||
intern::{Intern, Interned},
|
||||
module::wire_with_loc,
|
||||
prelude::*,
|
||||
util::ready_valid::ReadyValid,
|
||||
intern::Interned, module::wire_with_loc, prelude::*, util::ready_valid::ReadyValid,
|
||||
};
|
||||
use std::{collections::HashMap, ops::RangeTo};
|
||||
|
||||
#[hdl]
|
||||
fn add_sub<SrcCount: KnownSize>(
|
||||
mop: Expr<AddSubMOp<UnitOutRegNum<DynSize>, DynSize, SrcCount>>,
|
||||
mop: Expr<AddSubMOp<UnitOutRegNum<PhantomConst<CpuConfig>>, DynSize, SrcCount>>,
|
||||
pc: Expr<UInt<64>>,
|
||||
flags_mode: Expr<FlagsMode>,
|
||||
src_values: Expr<Array<PRegValue, { COMMON_MOP_SRC_LEN }>>,
|
||||
|
|
@ -245,7 +242,7 @@ fn add_sub<SrcCount: KnownSize>(
|
|||
|
||||
#[hdl]
|
||||
fn logical_flags(
|
||||
mop: Expr<LogicalFlagsMOp<UnitOutRegNum<DynSize>, DynSize>>,
|
||||
mop: Expr<LogicalFlagsMOp<UnitOutRegNum<PhantomConst<CpuConfig>>, DynSize>>,
|
||||
flags_mode: Expr<FlagsMode>,
|
||||
src_values: Expr<Array<PRegValue, { COMMON_MOP_SRC_LEN }>>,
|
||||
) -> Expr<UnitResultCompleted<()>> {
|
||||
|
|
@ -259,7 +256,7 @@ fn logical_flags(
|
|||
|
||||
#[hdl]
|
||||
fn logical(
|
||||
mop: Expr<LogicalMOp<UnitOutRegNum<DynSize>, DynSize, ConstUsize<2>>>,
|
||||
mop: Expr<LogicalMOp<UnitOutRegNum<PhantomConst<CpuConfig>>, DynSize, ConstUsize<2>>>,
|
||||
flags_mode: Expr<FlagsMode>,
|
||||
src_values: Expr<Array<PRegValue, { COMMON_MOP_SRC_LEN }>>,
|
||||
) -> Expr<UnitResultCompleted<()>> {
|
||||
|
|
@ -273,7 +270,7 @@ fn logical(
|
|||
|
||||
#[hdl]
|
||||
fn logical_i(
|
||||
mop: Expr<LogicalMOp<UnitOutRegNum<DynSize>, DynSize, ConstUsize<1>>>,
|
||||
mop: Expr<LogicalMOp<UnitOutRegNum<PhantomConst<CpuConfig>>, DynSize, ConstUsize<1>>>,
|
||||
flags_mode: Expr<FlagsMode>,
|
||||
src_values: Expr<Array<PRegValue, { COMMON_MOP_SRC_LEN }>>,
|
||||
) -> Expr<UnitResultCompleted<()>> {
|
||||
|
|
@ -287,7 +284,7 @@ fn logical_i(
|
|||
|
||||
#[hdl]
|
||||
fn shift_rotate(
|
||||
mop: Expr<ShiftRotateMOp<UnitOutRegNum<DynSize>, DynSize>>,
|
||||
mop: Expr<ShiftRotateMOp<UnitOutRegNum<PhantomConst<CpuConfig>>, DynSize>>,
|
||||
flags_mode: Expr<FlagsMode>,
|
||||
src_values: Expr<Array<PRegValue, { COMMON_MOP_SRC_LEN }>>,
|
||||
) -> Expr<UnitResultCompleted<()>> {
|
||||
|
|
@ -301,7 +298,7 @@ fn shift_rotate(
|
|||
|
||||
#[hdl]
|
||||
fn compare<SrcCount: KnownSize>(
|
||||
mop: Expr<CompareMOp<UnitOutRegNum<DynSize>, DynSize, SrcCount>>,
|
||||
mop: Expr<CompareMOp<UnitOutRegNum<PhantomConst<CpuConfig>>, DynSize, SrcCount>>,
|
||||
flags_mode: Expr<FlagsMode>,
|
||||
src_values: Expr<Array<PRegValue, { COMMON_MOP_SRC_LEN }>>,
|
||||
) -> Expr<UnitResultCompleted<()>> {
|
||||
|
|
@ -315,7 +312,7 @@ fn compare<SrcCount: KnownSize>(
|
|||
|
||||
#[hdl]
|
||||
fn branch<SrcCount: KnownSize>(
|
||||
mop: Expr<BranchMOp<UnitOutRegNum<DynSize>, DynSize, SrcCount>>,
|
||||
mop: Expr<BranchMOp<UnitOutRegNum<PhantomConst<CpuConfig>>, DynSize, SrcCount>>,
|
||||
pc: Expr<UInt<64>>,
|
||||
flags_mode: Expr<FlagsMode>,
|
||||
src_values: Expr<Array<PRegValue, { COMMON_MOP_SRC_LEN }>>,
|
||||
|
|
@ -330,7 +327,7 @@ fn branch<SrcCount: KnownSize>(
|
|||
|
||||
#[hdl]
|
||||
fn read_special(
|
||||
mop: Expr<ReadSpecialMOp<UnitOutRegNum<DynSize>, DynSize>>,
|
||||
mop: Expr<ReadSpecialMOp<UnitOutRegNum<PhantomConst<CpuConfig>>, DynSize>>,
|
||||
pc: Expr<UInt<64>>,
|
||||
flags_mode: Expr<FlagsMode>,
|
||||
src_values: Expr<Array<PRegValue, { COMMON_MOP_SRC_LEN }>>,
|
||||
|
|
@ -344,20 +341,18 @@ fn read_special(
|
|||
}
|
||||
|
||||
#[hdl_module]
|
||||
pub fn alu_branch(config: &CpuConfig, unit_index: usize) {
|
||||
pub fn alu_branch(config: PhantomConst<CpuConfig>, unit_index: usize) {
|
||||
#[hdl]
|
||||
let cd: ClockDomain = m.input();
|
||||
#[hdl]
|
||||
let unit_to_reg_alloc: UnitToRegAlloc<
|
||||
AluBranchMOp<UnitOutRegNum<DynSize>, DynSize>,
|
||||
PhantomConst<CpuConfig>,
|
||||
AluBranchMOp<UnitOutRegNum<PhantomConst<CpuConfig>>, DynSize>,
|
||||
(),
|
||||
DynSize,
|
||||
DynSize,
|
||||
DynSize,
|
||||
> = m.output(config.unit_to_reg_alloc(
|
||||
AluBranchMOp[config.unit_out_reg_num()][config.p_reg_num_width()],
|
||||
(),
|
||||
));
|
||||
> = m.output(
|
||||
UnitToRegAlloc[config][AluBranchMOp[UnitOutRegNum[config]][config.get().p_reg_num_width()]]
|
||||
[()],
|
||||
);
|
||||
#[hdl]
|
||||
let global_state: GlobalState = m.input();
|
||||
|
||||
|
|
@ -375,10 +370,11 @@ pub fn alu_branch(config: &CpuConfig, unit_index: usize) {
|
|||
#[hdl]
|
||||
if let HdlSome(execute_start) = ReadyValid::firing_data(unit_base.execute_start) {
|
||||
#[hdl]
|
||||
let ExecuteStart::<_> {
|
||||
let ExecuteStart::<_, _> {
|
||||
mop,
|
||||
pc,
|
||||
src_values,
|
||||
config: _,
|
||||
} = execute_start;
|
||||
#[hdl]
|
||||
match mop {
|
||||
|
|
@ -580,14 +576,14 @@ pub fn alu_branch(config: &CpuConfig, unit_index: usize) {
|
|||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct AluBranch {
|
||||
config: Interned<CpuConfig>,
|
||||
config: PhantomConst<CpuConfig>,
|
||||
module: Interned<Module<alu_branch>>,
|
||||
}
|
||||
|
||||
impl AluBranch {
|
||||
pub fn new(config: &CpuConfig, unit_index: usize) -> Self {
|
||||
pub fn new(config: PhantomConst<CpuConfig>, unit_index: usize) -> Self {
|
||||
Self {
|
||||
config: config.intern(),
|
||||
config,
|
||||
module: alu_branch(config, unit_index),
|
||||
}
|
||||
}
|
||||
|
|
@ -596,7 +592,7 @@ impl AluBranch {
|
|||
impl UnitTrait for AluBranch {
|
||||
type Type = alu_branch;
|
||||
type ExtraOut = ();
|
||||
type MOp = AluBranchMOp<UnitOutRegNum<DynSize>, DynSize>;
|
||||
type MOp = AluBranchMOp<UnitOutRegNum<PhantomConst<CpuConfig>>, DynSize>;
|
||||
|
||||
fn ty(&self) -> Self::Type {
|
||||
self.module.io_ty()
|
||||
|
|
@ -616,7 +612,7 @@ impl UnitTrait for AluBranch {
|
|||
|
||||
fn extract_mop(
|
||||
&self,
|
||||
mop: Expr<RenamedMOp<UnitOutRegNum<DynSize>, DynSize>>,
|
||||
mop: Expr<RenamedMOp<UnitOutRegNum<PhantomConst<CpuConfig>>, DynSize>>,
|
||||
) -> Expr<HdlOption<Self::MOp>> {
|
||||
UnitMOp::alu_branch_mop(mop)
|
||||
}
|
||||
|
|
@ -628,7 +624,7 @@ impl UnitTrait for AluBranch {
|
|||
fn unit_to_reg_alloc(
|
||||
&self,
|
||||
this: Expr<Self::Type>,
|
||||
) -> Expr<UnitToRegAlloc<Self::MOp, Self::ExtraOut, DynSize, DynSize, DynSize>> {
|
||||
) -> Expr<UnitToRegAlloc<PhantomConst<CpuConfig>, Self::MOp, Self::ExtraOut>> {
|
||||
this.unit_to_reg_alloc
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
// See Notices.txt for copyright information
|
||||
|
||||
use crate::{
|
||||
config::CpuConfig,
|
||||
config::{CpuConfig, CpuConfigUnitCount, PhantomConstCpuConfig},
|
||||
instruction::{COMMON_MOP_SRC_LEN, MOpTrait, PRegNum, UnitNum, UnitOutRegNum},
|
||||
register::PRegValue,
|
||||
unit::{UnitCancelInput, UnitOutput, UnitOutputWrite},
|
||||
|
|
@ -15,13 +15,11 @@ use fayalite::{
|
|||
ty::StaticType,
|
||||
util::ready_valid::ReadyValid,
|
||||
};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
#[hdl]
|
||||
pub struct UnitForwardingInfo<UnitNumWidth: Size, OutRegNumWidth: Size, UnitCount: Size> {
|
||||
pub unit_output_writes: ArrayType<HdlOption<UnitOutputWrite<OutRegNumWidth>>, UnitCount>,
|
||||
pub unit_reg_frees: ArrayType<HdlOption<UnitOutRegNum<OutRegNumWidth>>, UnitCount>,
|
||||
pub _phantom: PhantomData<UnitNumWidth>,
|
||||
#[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]
|
||||
|
|
@ -30,26 +28,18 @@ pub struct UnitInput<MOp: Type> {
|
|||
pub pc: UInt<64>,
|
||||
}
|
||||
|
||||
#[hdl]
|
||||
pub struct UnitToRegAlloc<
|
||||
MOp: Type,
|
||||
ExtraOut: Type,
|
||||
UnitNumWidth: Size,
|
||||
OutRegNumWidth: Size,
|
||||
UnitCount: Size,
|
||||
> {
|
||||
#[hdl(no_static)]
|
||||
pub struct UnitToRegAlloc<C: PhantomConstGet<CpuConfig>, MOp: Type, ExtraOut: Type> {
|
||||
#[hdl(flip)]
|
||||
pub unit_forwarding_info: UnitForwardingInfo<UnitNumWidth, OutRegNumWidth, UnitCount>,
|
||||
pub unit_forwarding_info: UnitForwardingInfo<C>,
|
||||
#[hdl(flip)]
|
||||
pub input: ReadyValid<UnitInput<MOp>>,
|
||||
#[hdl(flip)]
|
||||
pub cancel_input: HdlOption<UnitCancelInput<OutRegNumWidth>>,
|
||||
pub output: HdlOption<UnitOutput<OutRegNumWidth, ExtraOut>>,
|
||||
pub cancel_input: HdlOption<UnitCancelInput<C>>,
|
||||
pub output: HdlOption<UnitOutput<C, ExtraOut>>,
|
||||
}
|
||||
|
||||
impl<MOp: Type, ExtraOut: Type, UnitNumWidth: Size, OutRegNumWidth: Size, UnitCount: Size>
|
||||
UnitToRegAlloc<MOp, ExtraOut, UnitNumWidth, OutRegNumWidth, UnitCount>
|
||||
{
|
||||
impl<C: PhantomConstCpuConfig, MOp: Type, ExtraOut: Type> UnitToRegAlloc<C, MOp, ExtraOut> {
|
||||
pub fn mop_ty(self) -> MOp {
|
||||
self.input.data.HdlSome.mop
|
||||
}
|
||||
|
|
@ -58,16 +48,20 @@ impl<MOp: Type, ExtraOut: Type, UnitNumWidth: Size, OutRegNumWidth: Size, UnitCo
|
|||
}
|
||||
}
|
||||
|
||||
#[hdl]
|
||||
pub struct ExecuteStart<MOp: Type + MOpTrait<DestReg = UnitOutRegNum<DynSize>>> {
|
||||
#[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]
|
||||
pub struct ExecuteEnd<OutRegNumWidth: Size, ExtraOut> {
|
||||
pub unit_output: UnitOutput<OutRegNumWidth, ExtraOut>,
|
||||
#[hdl(no_static)]
|
||||
pub struct ExecuteEnd<C: PhantomConstGet<CpuConfig>, ExtraOut> {
|
||||
pub unit_output: UnitOutput<C, ExtraOut>,
|
||||
}
|
||||
|
||||
#[hdl]
|
||||
|
|
@ -240,10 +234,10 @@ impl InFlightOpsSummary<DynSize> {
|
|||
|
||||
#[hdl_module]
|
||||
pub fn unit_base<
|
||||
MOp: Type + MOpTrait<DestReg = UnitOutRegNum<DynSize>, SrcRegWidth = DynSize>,
|
||||
MOp: Type + MOpTrait<DestReg = UnitOutRegNum<PhantomConst<CpuConfig>>, SrcRegWidth = DynSize>,
|
||||
ExtraOut: Type,
|
||||
>(
|
||||
config: &CpuConfig,
|
||||
config: PhantomConst<CpuConfig>,
|
||||
unit_index: usize,
|
||||
mop_ty: MOp,
|
||||
extra_out_ty: ExtraOut,
|
||||
|
|
@ -251,17 +245,18 @@ pub fn unit_base<
|
|||
#[hdl]
|
||||
let cd: ClockDomain = m.input();
|
||||
#[hdl]
|
||||
let unit_to_reg_alloc: UnitToRegAlloc<MOp, ExtraOut, DynSize, DynSize, DynSize> =
|
||||
m.output(config.unit_to_reg_alloc(mop_ty, extra_out_ty));
|
||||
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<MOp>> = m.output(ReadyValid[ExecuteStart[mop_ty]]);
|
||||
let execute_start: ReadyValid<ExecuteStart<PhantomConst<CpuConfig>, MOp>> =
|
||||
m.output(ReadyValid[ExecuteStart[config][mop_ty]]);
|
||||
#[hdl]
|
||||
let execute_end: HdlOption<ExecuteEnd<DynSize, ExtraOut>> =
|
||||
m.input(HdlOption[ExecuteEnd[config.out_reg_num_width][extra_out_ty]]);
|
||||
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.unit_max_in_flight(unit_index).get();
|
||||
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()
|
||||
|
|
@ -279,16 +274,15 @@ pub fn unit_base<
|
|||
);
|
||||
|
||||
#[hdl]
|
||||
let UnitForwardingInfo::<_, _, _> {
|
||||
let UnitForwardingInfo::<_> {
|
||||
unit_output_writes,
|
||||
unit_reg_frees,
|
||||
_phantom: _,
|
||||
} = unit_to_reg_alloc.unit_forwarding_info;
|
||||
#[hdl]
|
||||
let read_src_regs = wire(mop_ty.src_regs_ty());
|
||||
connect(
|
||||
read_src_regs,
|
||||
repeat(config.p_reg_num().const_zero().cast_to_bits(), ConstUsize),
|
||||
repeat(PRegNum[config].const_zero().cast_to_bits(), ConstUsize),
|
||||
);
|
||||
#[hdl]
|
||||
let read_src_values = wire();
|
||||
|
|
@ -297,7 +291,7 @@ pub fn unit_base<
|
|||
let input_src_regs = wire(mop_ty.src_regs_ty());
|
||||
connect(
|
||||
input_src_regs,
|
||||
repeat(config.p_reg_num().const_zero().cast_to_bits(), ConstUsize),
|
||||
repeat(PRegNum[config].const_zero().cast_to_bits(), ConstUsize),
|
||||
);
|
||||
#[hdl]
|
||||
let input_src_regs_valid = wire();
|
||||
|
|
@ -309,7 +303,7 @@ pub fn unit_base<
|
|||
Bool,
|
||||
SourceLocation::caller(),
|
||||
);
|
||||
mem.depth(1 << config.out_reg_num_width);
|
||||
mem.depth(1 << config.get().out_reg_num_width);
|
||||
mem
|
||||
})
|
||||
.collect();
|
||||
|
|
@ -319,11 +313,11 @@ pub fn unit_base<
|
|||
PRegValue,
|
||||
SourceLocation::caller(),
|
||||
);
|
||||
unit_output_regs.depth(1 << config.out_reg_num_width);
|
||||
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].cast_bits_to(config.p_reg_num());
|
||||
let p_reg_num = read_src_regs[src_index].cast_bits_to(PRegNum[config]);
|
||||
connect_any(read_port.addr, p_reg_num.unit_out_reg.value);
|
||||
connect(read_port.en, false);
|
||||
connect(read_port.clk, cd.clk);
|
||||
|
|
@ -336,7 +330,7 @@ pub fn unit_base<
|
|||
|
||||
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].cast_bits_to(config.p_reg_num());
|
||||
let p_reg_num = input_src_regs[src_index].cast_bits_to(PRegNum[config]);
|
||||
connect_any(read_port.addr, p_reg_num.unit_out_reg.value);
|
||||
connect(read_port.en, false);
|
||||
connect(read_port.clk, cd.clk);
|
||||
|
|
@ -367,8 +361,8 @@ pub fn unit_base<
|
|||
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: config.unit_num().from_index(unit_index),
|
||||
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 {
|
||||
|
|
@ -399,10 +393,11 @@ pub fn unit_base<
|
|||
execute_start.data,
|
||||
HdlSome(
|
||||
#[hdl]
|
||||
ExecuteStart::<_> {
|
||||
ExecuteStart::<_, _> {
|
||||
mop: in_flight_op.mop,
|
||||
pc: in_flight_op.pc,
|
||||
src_values: read_src_values,
|
||||
config,
|
||||
},
|
||||
),
|
||||
);
|
||||
|
|
@ -425,7 +420,7 @@ pub fn unit_base<
|
|||
let input_mop_src_regs = wire(mop_ty.src_regs_ty());
|
||||
connect(
|
||||
input_mop_src_regs,
|
||||
repeat(config.p_reg_num().const_zero().cast_to_bits(), ConstUsize),
|
||||
repeat(PRegNum[config].const_zero().cast_to_bits(), ConstUsize),
|
||||
);
|
||||
MOp::connect_src_regs(mop, input_mop_src_regs);
|
||||
let src_ready_flags = wire_with_loc(
|
||||
|
|
@ -497,7 +492,7 @@ pub fn unit_base<
|
|||
);
|
||||
connect(
|
||||
src_regs,
|
||||
repeat(config.p_reg_num().const_zero().cast_to_bits(), ConstUsize),
|
||||
repeat(PRegNum[config].const_zero().cast_to_bits(), ConstUsize),
|
||||
);
|
||||
MOp::connect_src_regs(mop, src_regs);
|
||||
|
||||
|
|
@ -521,8 +516,8 @@ pub fn unit_base<
|
|||
value: _,
|
||||
} = unit_output_write;
|
||||
let p_reg_num = #[hdl]
|
||||
PRegNum::<_, _> {
|
||||
unit_num: config.unit_num().from_index(unit_index),
|
||||
PRegNum::<_> {
|
||||
unit_num: UnitNum[config].from_index(unit_index),
|
||||
unit_out_reg,
|
||||
};
|
||||
for src_index in 0..COMMON_MOP_SRC_LEN {
|
||||
|
|
|
|||
96723
crates/cpu/tests/expected/rename_execute_retire_fibonacci.vcd
generated
Normal file
96723
crates/cpu/tests/expected/rename_execute_retire_fibonacci.vcd
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -1,5 +1,6 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
#![cfg(todo)]
|
||||
|
||||
use cpu::{
|
||||
config::{CpuConfig, UnitConfig},
|
||||
|
|
|
|||
924
crates/cpu/tests/rename_execute_retire.rs
Normal file
924
crates/cpu/tests/rename_execute_retire.rs
Normal file
|
|
@ -0,0 +1,924 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
|
||||
use cpu::{
|
||||
config::{CpuConfig, CpuConfigFetchWidth, PhantomConstCpuConfig, UnitConfig},
|
||||
instruction::{
|
||||
AddSubMOp, AluBranchMOp, BranchMOp, CompareMOp, CompareMode, ConditionMode, LoadMOp,
|
||||
LoadStoreConversion, LoadStoreWidth, MOp, MOpDestReg, MOpRegNum, MOpTrait, MoveRegMOp,
|
||||
OutputIntegerMode, StoreMOp,
|
||||
},
|
||||
rename_execute_retire::{
|
||||
MOpInstance, PostDecodeOutputInterface, RetireToNextPcInterface, rename_execute_retire,
|
||||
},
|
||||
unit::UnitKind,
|
||||
util::array_vec::ArrayVec,
|
||||
};
|
||||
use fayalite::{
|
||||
prelude::*,
|
||||
sim::vcd::VcdWriterDecls,
|
||||
ty::{OpaqueSimValue, StaticType},
|
||||
util::RcWriter,
|
||||
};
|
||||
use std::{
|
||||
cell::Cell,
|
||||
collections::{BTreeMap, VecDeque},
|
||||
fmt::{self, Write},
|
||||
mem,
|
||||
num::NonZeroUsize,
|
||||
};
|
||||
|
||||
fn zeroed<T: Type>(ty: T) -> SimValue<T> {
|
||||
SimValue::from_opaque(
|
||||
ty,
|
||||
OpaqueSimValue::from_bits(UInt::new(ty.canonical().bit_width()).zero()),
|
||||
)
|
||||
}
|
||||
|
||||
fn signed_hex(v: impl Into<i64>) -> impl fmt::Display {
|
||||
let v = v.into();
|
||||
fmt::from_fn(move |f| {
|
||||
if v < 0 {
|
||||
f.write_char('-')?;
|
||||
}
|
||||
write!(f, "{:#x}", v.unsigned_abs())
|
||||
})
|
||||
}
|
||||
|
||||
struct RandomState {
|
||||
state: Cell<u64>,
|
||||
}
|
||||
|
||||
impl RandomState {
|
||||
fn random_u64(&self, key: u32) -> u64 {
|
||||
let state = self.state.replace(self.state.get().wrapping_add(1));
|
||||
// make a pseudo-random number deterministically based on state and key
|
||||
let mut random = state
|
||||
.wrapping_add(1)
|
||||
.wrapping_mul(0x39FF446D8BFB75BB) // random prime
|
||||
.rotate_left(32)
|
||||
.wrapping_mul(0x73161B54984B1C21) // random prime
|
||||
.rotate_right(60);
|
||||
random ^= key as u64;
|
||||
random
|
||||
.wrapping_mul(0x39FF446D8BFB75BB) // random prime
|
||||
.rotate_left(32)
|
||||
.wrapping_mul(0x73161B54984B1C21) // random prime
|
||||
.rotate_right(60)
|
||||
}
|
||||
}
|
||||
|
||||
const START_PC: u64 = 0x0; // match microwatt's reset pc
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct Insn<MOps = Vec<SimValue<self::MOp>>> {
|
||||
size_in_bytes: u8,
|
||||
power_isa: String,
|
||||
mops: MOps,
|
||||
}
|
||||
|
||||
enum LazyMOps {
|
||||
MOps(Vec<SimValue<self::MOp>>),
|
||||
Lazy(Box<dyn FnOnce(&[InsnsBuilderLabelState]) -> Vec<SimValue<self::MOp>>>),
|
||||
}
|
||||
|
||||
impl Insn<LazyMOps> {
|
||||
fn new<I: IntoIterator<Item: ToSimValue<Type = MOp>>>(
|
||||
size_in_bytes: u8,
|
||||
power_isa: String,
|
||||
mops: I,
|
||||
) -> Self {
|
||||
Self {
|
||||
size_in_bytes,
|
||||
power_isa,
|
||||
mops: LazyMOps::MOps(mops.into_iter().map(|mop| mop.into_sim_value()).collect()),
|
||||
}
|
||||
}
|
||||
fn new_lazy<I: IntoIterator<Item: ToSimValue<Type = MOp>>>(
|
||||
size_in_bytes: u8,
|
||||
power_isa: String,
|
||||
lazy_mops: impl FnOnce(&[InsnsBuilderLabelState]) -> I + 'static,
|
||||
) -> Self {
|
||||
Self {
|
||||
size_in_bytes,
|
||||
power_isa,
|
||||
mops: LazyMOps::Lazy(Box::new(|labels| {
|
||||
lazy_mops(labels)
|
||||
.into_iter()
|
||||
.map(|mop| mop.into_sim_value())
|
||||
.collect()
|
||||
})),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Insns {
|
||||
insns: BTreeMap<u64, Insn>,
|
||||
}
|
||||
|
||||
impl Insns {
|
||||
fn get_mop_mut(&mut self, pc: u64, mop_index: usize) -> Option<&mut SimValue<MOp>> {
|
||||
self.insns.get_mut(&pc)?.mops.get_mut(mop_index)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Insns {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_map()
|
||||
.entries(
|
||||
self.insns
|
||||
.iter()
|
||||
.map(|(k, v)| (fmt::from_fn(move |f| write!(f, "{k:#x}")), v)),
|
||||
)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
struct InsnsBuilderLabelState {
|
||||
name: &'static str,
|
||||
pc: Option<u64>,
|
||||
location: &'static std::panic::Location<'static>,
|
||||
}
|
||||
|
||||
impl InsnsBuilderLabelState {
|
||||
fn pc(&self) -> u64 {
|
||||
match self.pc {
|
||||
Some(pc) => pc,
|
||||
None => panic!("label not defined {}: at: {}", self.name, self.location),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct InsnsBuilderLabel(usize);
|
||||
|
||||
struct InsnsBuilder {
|
||||
labels: Vec<InsnsBuilderLabelState>,
|
||||
insns: BTreeMap<u64, Insn<LazyMOps>>,
|
||||
pc: u64,
|
||||
}
|
||||
|
||||
impl InsnsBuilder {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
labels: Vec::new(),
|
||||
insns: BTreeMap::new(),
|
||||
pc: START_PC,
|
||||
}
|
||||
}
|
||||
#[track_caller]
|
||||
fn new_label(&mut self, name: &'static str) -> InsnsBuilderLabel {
|
||||
let retval = InsnsBuilderLabel(self.labels.len());
|
||||
self.labels.push(InsnsBuilderLabelState {
|
||||
pc: None,
|
||||
name,
|
||||
location: std::panic::Location::caller(),
|
||||
});
|
||||
retval
|
||||
}
|
||||
#[track_caller]
|
||||
fn define_label(&mut self, label: InsnsBuilderLabel) {
|
||||
let label_state = &mut self.labels[label.0];
|
||||
if label_state.pc.is_some() {
|
||||
panic!("label already defined at: {}", label_state.location);
|
||||
}
|
||||
label_state.pc = Some(self.pc);
|
||||
label_state.location = std::panic::Location::caller();
|
||||
}
|
||||
#[track_caller]
|
||||
fn new_defined_label(&mut self, name: &'static str) -> InsnsBuilderLabel {
|
||||
let label = self.new_label(name);
|
||||
self.define_label(label);
|
||||
label
|
||||
}
|
||||
#[track_caller]
|
||||
fn add_insn(&mut self, insn: Insn<LazyMOps>) {
|
||||
let pc = self.pc;
|
||||
let next_pc = pc.wrapping_add(insn.size_in_bytes as u64);
|
||||
if self.insns.insert(self.pc, insn).is_some() {
|
||||
panic!("instruction already stored at {pc:#x}");
|
||||
}
|
||||
self.pc = next_pc;
|
||||
}
|
||||
fn build(self) -> Insns {
|
||||
Insns {
|
||||
insns: self
|
||||
.insns
|
||||
.into_iter()
|
||||
.map(|(pc, insn)| {
|
||||
let Insn {
|
||||
size_in_bytes,
|
||||
power_isa,
|
||||
mops,
|
||||
} = insn;
|
||||
(
|
||||
pc,
|
||||
Insn {
|
||||
size_in_bytes,
|
||||
power_isa,
|
||||
mops: match mops {
|
||||
LazyMOps::MOps(mops) => mops,
|
||||
LazyMOps::Lazy(f) => f(&self.labels),
|
||||
},
|
||||
},
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
fn set_pc(&mut self, pc: u64) {
|
||||
self.pc = pc;
|
||||
}
|
||||
fn power_isa_b(&mut self, target: InsnsBuilderLabel) {
|
||||
let pc = self.pc;
|
||||
self.add_insn(Insn::new_lazy(
|
||||
4,
|
||||
format!("b {}", self.labels[target.0].name),
|
||||
move |labels| {
|
||||
[BranchMOp::branch_i(
|
||||
MOpDestReg::new([], []),
|
||||
MOpRegNum::const_zero().value,
|
||||
labels[target.0]
|
||||
.pc()
|
||||
.wrapping_sub(pc)
|
||||
.cast_to_static::<SInt<_>>(),
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
)]
|
||||
},
|
||||
));
|
||||
}
|
||||
fn power_isa_blr(&mut self) {
|
||||
self.add_insn(Insn::new(
|
||||
4,
|
||||
format!("blr"),
|
||||
[BranchMOp::branch_i(
|
||||
MOpDestReg::new([], []),
|
||||
MOpRegNum::power_isa_lr_reg().value,
|
||||
0i8.cast_to_static::<SInt<_>>(),
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
)],
|
||||
));
|
||||
}
|
||||
fn power_isa_bgt(&mut self, target: InsnsBuilderLabel) {
|
||||
let pc = self.pc;
|
||||
self.add_insn(Insn::new_lazy(
|
||||
4,
|
||||
format!("bgt {}", self.labels[target.0].name),
|
||||
move |labels| {
|
||||
[BranchMOp::branch_cond_ctr(
|
||||
MOpDestReg::new([], []),
|
||||
[
|
||||
MOpRegNum::power_isa_cr_reg_imm(0).value,
|
||||
MOpRegNum::const_zero().value,
|
||||
MOpRegNum::const_zero().value,
|
||||
],
|
||||
labels[target.0]
|
||||
.pc()
|
||||
.wrapping_sub(pc)
|
||||
.cast_to_static::<SInt<_>>(),
|
||||
false,
|
||||
ConditionMode.SGt(),
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
)]
|
||||
},
|
||||
));
|
||||
}
|
||||
fn power_isa_bl(&mut self, target: InsnsBuilderLabel) {
|
||||
let pc = self.pc;
|
||||
self.add_insn(Insn::new_lazy(
|
||||
4,
|
||||
format!("bl {}", self.labels[target.0].name),
|
||||
move |labels| {
|
||||
[BranchMOp::branch_i(
|
||||
MOpDestReg::new([MOpRegNum::power_isa_lr_reg()], []),
|
||||
MOpRegNum::const_zero().value,
|
||||
labels[target.0]
|
||||
.pc()
|
||||
.wrapping_sub(pc)
|
||||
.cast_to_static::<SInt<_>>(),
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
)]
|
||||
},
|
||||
));
|
||||
}
|
||||
fn power_isa_add(&mut self, dest: usize, src1: usize, src2: usize) {
|
||||
self.add_insn(Insn::new(
|
||||
4,
|
||||
format!("add {dest}, {src1}, {src2}"),
|
||||
[AddSubMOp::add_sub(
|
||||
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(dest)], &[]),
|
||||
[
|
||||
MOpRegNum::power_isa_gpr_reg_imm(src1).value,
|
||||
MOpRegNum::power_isa_gpr_reg_imm(src2).value,
|
||||
MOpRegNum::const_zero().value,
|
||||
],
|
||||
0i8.cast_to_static::<SInt<_>>(),
|
||||
OutputIntegerMode.Full64(),
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
)],
|
||||
));
|
||||
}
|
||||
fn power_isa_addi(&mut self, dest: usize, src: usize, imm: i16) {
|
||||
self.add_insn(Insn::new(
|
||||
4,
|
||||
format!("addi {dest}, {src}, {}", signed_hex(imm)),
|
||||
[AddSubMOp::add_sub_i(
|
||||
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(dest)], &[]),
|
||||
[
|
||||
MOpRegNum::power_isa_gpr_or_zero_reg_imm(src).value,
|
||||
MOpRegNum::const_zero().value,
|
||||
],
|
||||
imm.cast_to_static::<SInt<_>>(),
|
||||
OutputIntegerMode.Full64(),
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
)],
|
||||
));
|
||||
}
|
||||
fn power_isa_cmpldi(&mut self, dest: usize, src: usize, imm: u16) {
|
||||
self.add_insn(Insn::new(
|
||||
4,
|
||||
format!("cmpldi {dest}, {src}, {imm:#x}"),
|
||||
[CompareMOp::compare_i(
|
||||
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(dest)], &[]),
|
||||
[MOpRegNum::power_isa_gpr_reg_imm(src).value],
|
||||
imm.cast_to_static::<SInt<_>>(),
|
||||
OutputIntegerMode.Full64(),
|
||||
CompareMode.U64(),
|
||||
)],
|
||||
));
|
||||
}
|
||||
fn power_isa_ld(&mut self, dest: usize, src: usize, disp: i16) {
|
||||
self.add_insn(Insn::new(
|
||||
4,
|
||||
format!("ld {dest}, {}({src})", signed_hex(disp)),
|
||||
[
|
||||
AddSubMOp::add_sub_i(
|
||||
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_TEMP_REG_NUM], &[]),
|
||||
[
|
||||
MOpRegNum::power_isa_gpr_or_zero_reg_imm(src).value,
|
||||
MOpRegNum::const_zero().value,
|
||||
],
|
||||
disp.cast_to_static::<SInt<_>>(),
|
||||
OutputIntegerMode.Full64(),
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
LoadMOp::load(
|
||||
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(dest)], &[]),
|
||||
[MOpRegNum::power_isa_temp_reg().value],
|
||||
LoadStoreWidth.Width64Bit(),
|
||||
LoadStoreConversion.ZeroExt(),
|
||||
),
|
||||
],
|
||||
));
|
||||
}
|
||||
fn power_isa_std(&mut self, value_src: usize, addr_src: usize, disp: i16) {
|
||||
self.add_insn(Insn::new(
|
||||
4,
|
||||
format!("std {value_src}, {}({addr_src})", signed_hex(disp)),
|
||||
[
|
||||
AddSubMOp::add_sub_i(
|
||||
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_TEMP_REG_NUM], &[]),
|
||||
[
|
||||
MOpRegNum::power_isa_gpr_or_zero_reg_imm(addr_src).value,
|
||||
MOpRegNum::const_zero().value,
|
||||
],
|
||||
disp.cast_to_static::<SInt<_>>(),
|
||||
OutputIntegerMode.Full64(),
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
StoreMOp::store(
|
||||
MOpDestReg::new_sim(&[], &[]),
|
||||
[
|
||||
MOpRegNum::power_isa_temp_reg().value,
|
||||
MOpRegNum::power_isa_gpr_reg_imm(value_src).value,
|
||||
],
|
||||
LoadStoreWidth.Width64Bit(),
|
||||
LoadStoreConversion.ZeroExt(),
|
||||
),
|
||||
],
|
||||
));
|
||||
}
|
||||
fn power_isa_mflr(&mut self, dest: usize) {
|
||||
self.add_insn(Insn::new(
|
||||
4,
|
||||
format!("mflr {dest}"),
|
||||
[MoveRegMOp::move_reg(
|
||||
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(dest)], &[]),
|
||||
[MOpRegNum::power_isa_lr_reg().value],
|
||||
0i8.cast_to_static::<SInt<_>>(),
|
||||
)],
|
||||
));
|
||||
}
|
||||
fn power_isa_mtlr(&mut self, src: usize) {
|
||||
self.add_insn(Insn::new(
|
||||
4,
|
||||
format!("mtlr {src}"),
|
||||
[MoveRegMOp::move_reg(
|
||||
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_LR_REG_NUM], &[]),
|
||||
[MOpRegNum::power_isa_gpr_reg_imm(src).value],
|
||||
0i8.cast_to_static::<SInt<_>>(),
|
||||
)],
|
||||
));
|
||||
}
|
||||
fn power_isa_mr(&mut self, dest: usize, src: usize) {
|
||||
self.add_insn(Insn::new(
|
||||
4,
|
||||
format!("mr {dest}, {src}"),
|
||||
[MoveRegMOp::move_reg(
|
||||
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(dest)], &[]),
|
||||
[MOpRegNum::power_isa_gpr_reg_imm(src).value],
|
||||
0i8.cast_to_static::<SInt<_>>(),
|
||||
)],
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
trait MakeInsns {
|
||||
fn make_insns() -> Insns;
|
||||
}
|
||||
|
||||
struct FibonacciInsns;
|
||||
|
||||
impl MakeInsns for FibonacciInsns {
|
||||
fn make_insns() -> Insns {
|
||||
let mut b = InsnsBuilder::new();
|
||||
|
||||
let fib = b.new_label("fib");
|
||||
let fib_small = b.new_label("fib_small");
|
||||
b.power_isa_ld(3, 0, -16); // load input
|
||||
b.power_isa_addi(1, 0, 0x4000); // setup stack pointer
|
||||
b.power_isa_bl(fib);
|
||||
b.power_isa_std(3, 0, -16); // store output
|
||||
let done = b.new_defined_label("done");
|
||||
b.power_isa_b(done);
|
||||
|
||||
b.set_pc(0x1000);
|
||||
b.define_label(fib);
|
||||
b.power_isa_cmpldi(0, 3, 1);
|
||||
b.power_isa_bgt(fib_small); // if input > 1 goto fib_small
|
||||
|
||||
// push stack frame
|
||||
b.power_isa_mflr(0);
|
||||
b.power_isa_addi(1, 1, -16);
|
||||
b.power_isa_std(0, 1, 0);
|
||||
b.power_isa_std(30, 1, 8);
|
||||
|
||||
b.power_isa_addi(30, 3, -2);
|
||||
b.power_isa_addi(3, 3, -1);
|
||||
b.power_isa_bl(fib); // fib(input - 1)
|
||||
b.power_isa_mr(2, 3);
|
||||
b.power_isa_mr(3, 30);
|
||||
b.power_isa_mr(30, 2);
|
||||
b.power_isa_bl(fib); // fib(input - 2)
|
||||
b.power_isa_add(3, 3, 30); // set output to fib(input - 1) + fib(input - 2)
|
||||
|
||||
// pop stack frame and return
|
||||
b.power_isa_ld(0, 1, 0);
|
||||
b.power_isa_ld(30, 1, 8);
|
||||
b.power_isa_addi(1, 1, 16);
|
||||
b.power_isa_mtlr(0);
|
||||
b.power_isa_blr();
|
||||
|
||||
b.define_label(fib_small);
|
||||
b.power_isa_addi(3, 0, 1); // set output to 1
|
||||
b.power_isa_blr(); // return
|
||||
|
||||
b.build()
|
||||
}
|
||||
}
|
||||
|
||||
#[hdl(no_static)]
|
||||
struct MockNextPcDebugState<C: PhantomConstGet<CpuConfig>> {
|
||||
next_pc: UInt<64>,
|
||||
next_mop_index: UInt<8>,
|
||||
next_br_pred_state: BrPredDebugState,
|
||||
next_id: UInt<64>,
|
||||
fetch_block_id: UInt<64>,
|
||||
fetch_queue: ArrayVec<(BrPredDebugState, MOpInstance<MOp>), CpuConfigFetchWidth<C>>,
|
||||
config: C,
|
||||
}
|
||||
|
||||
#[hdl]
|
||||
struct BrPredDebugState {
|
||||
call_stack: ArrayVec<UInt<64>, ConstUsize<{ BrPredState::CALL_STACK_CAPACITY }>>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct BrPredState {
|
||||
call_stack: [u64; Self::CALL_STACK_CAPACITY],
|
||||
call_stack_len: usize,
|
||||
}
|
||||
|
||||
impl BrPredState {}
|
||||
|
||||
impl fmt::Debug for BrPredState {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let Self {
|
||||
call_stack,
|
||||
call_stack_len,
|
||||
} = self;
|
||||
f.debug_struct("BrPredState")
|
||||
.field(
|
||||
"call_stack",
|
||||
&fmt::from_fn(|f| {
|
||||
let mut debug_list = f.debug_list();
|
||||
for pc in &call_stack[..*call_stack_len] {
|
||||
debug_list.entry(&fmt::from_fn(|f| write!(f, "{pc:#x}")));
|
||||
}
|
||||
debug_list.finish()
|
||||
}),
|
||||
)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl BrPredState {
|
||||
const CALL_STACK_CAPACITY: usize = 8;
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
call_stack: [0; _],
|
||||
call_stack_len: 0,
|
||||
}
|
||||
}
|
||||
fn predict_call(&mut self, return_pc: u64) {
|
||||
if self.call_stack_len == Self::CALL_STACK_CAPACITY {
|
||||
self.call_stack.copy_within(1.., 0);
|
||||
*self.call_stack.last_mut().expect("known to be non-zero") = return_pc;
|
||||
} else {
|
||||
self.call_stack[self.call_stack_len] = return_pc;
|
||||
self.call_stack_len += 1;
|
||||
}
|
||||
}
|
||||
fn predict_ret(&mut self) -> Option<u64> {
|
||||
if self.call_stack_len > 0 {
|
||||
self.call_stack_len -= 1;
|
||||
Some(mem::replace(&mut self.call_stack[self.call_stack_len], 0))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
fn predict_branch<SrcCount: KnownSize>(
|
||||
&mut self,
|
||||
mop: &SimValue<BranchMOp<MOpDestReg, ConstUsize<{ MOpRegNum::WIDTH }>, SrcCount>>,
|
||||
branch_pc: u64,
|
||||
fallthrough_pc: u64,
|
||||
) -> u64 {
|
||||
if *mop.is_ret {
|
||||
if let Some(retval) = self.predict_ret() {
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
let mut src_regs = [MOpRegNum::CONST_ZERO_REG_NUM; 3];
|
||||
MOpTrait::for_each_src_reg_sim_ref(mop, &mut |reg, index| {
|
||||
src_regs[index] = reg.cast_to_static::<UInt<32>>().as_int();
|
||||
});
|
||||
if src_regs[1] != MOpRegNum::CONST_ZERO_REG_NUM {
|
||||
// indirect branch -- this test doesn't implement predicting them, so just use the fallthrough_pc
|
||||
return fallthrough_pc;
|
||||
}
|
||||
let target_pc = (*mop.common.imm)
|
||||
.cast_to_static::<UInt<64>>()
|
||||
.as_int()
|
||||
.wrapping_add(if *mop.pc_relative { branch_pc } else { 0 });
|
||||
if *mop.is_call {
|
||||
self.predict_call(fallthrough_pc);
|
||||
}
|
||||
if src_regs[0] != MOpRegNum::CONST_ZERO_REG_NUM
|
||||
|| src_regs[2] != MOpRegNum::CONST_ZERO_REG_NUM
|
||||
{
|
||||
// conditional branch -- this test just predicts taken for backward branches and not taken for forward branches
|
||||
if target_pc < fallthrough_pc {
|
||||
target_pc
|
||||
} else {
|
||||
fallthrough_pc
|
||||
}
|
||||
} else {
|
||||
target_pc
|
||||
}
|
||||
}
|
||||
#[hdl]
|
||||
fn debug_state(&self) -> SimValue<BrPredDebugState> {
|
||||
let Self {
|
||||
call_stack,
|
||||
call_stack_len,
|
||||
} = self;
|
||||
#[hdl(sim)]
|
||||
BrPredDebugState {
|
||||
call_stack: BrPredDebugState
|
||||
.call_stack
|
||||
.from_iter_sim(0u64, &call_stack[..*call_stack_len])
|
||||
.expect("known to fit"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct MockNextPcState<'a, C: PhantomConstCpuConfig> {
|
||||
random_state: &'a RandomState,
|
||||
insns: &'a Insns,
|
||||
next_pc: u64,
|
||||
next_mop_index: usize,
|
||||
next_br_pred_state: BrPredState,
|
||||
next_id: u64,
|
||||
fetch_block_id: u64,
|
||||
fetch_queue: VecDeque<(BrPredState, SimValue<MOpInstance<MOp>>)>,
|
||||
config: C,
|
||||
}
|
||||
|
||||
impl<'a, C: PhantomConstCpuConfig> MockNextPcState<'a, C> {
|
||||
fn new(random_state: &'a RandomState, insns: &'a Insns, config: C) -> Self {
|
||||
Self {
|
||||
random_state,
|
||||
insns,
|
||||
next_pc: START_PC,
|
||||
next_mop_index: 0,
|
||||
next_br_pred_state: BrPredState::new(),
|
||||
next_id: 0,
|
||||
fetch_block_id: 0,
|
||||
fetch_queue: VecDeque::with_capacity(config.get().fetch_width.get()),
|
||||
config,
|
||||
}
|
||||
}
|
||||
#[hdl]
|
||||
fn debug_state(&self) -> SimValue<MockNextPcDebugState<C>> {
|
||||
let Self {
|
||||
random_state: _,
|
||||
insns: _,
|
||||
config,
|
||||
next_pc,
|
||||
next_mop_index,
|
||||
next_br_pred_state,
|
||||
next_id,
|
||||
fetch_block_id,
|
||||
fetch_queue,
|
||||
} = self;
|
||||
let ty = MockNextPcDebugState[*config];
|
||||
#[hdl(sim)]
|
||||
MockNextPcDebugState::<_> {
|
||||
next_pc,
|
||||
next_mop_index: *next_mop_index as u8,
|
||||
next_br_pred_state: next_br_pred_state.debug_state(),
|
||||
next_id,
|
||||
fetch_block_id,
|
||||
fetch_queue: ty
|
||||
.fetch_queue
|
||||
.from_iter_sim(
|
||||
zeroed(StaticType::TYPE),
|
||||
fetch_queue
|
||||
.iter()
|
||||
.map(|(br_pred_state, mop)| (br_pred_state.debug_state(), mop)),
|
||||
)
|
||||
.expect("known to fit"),
|
||||
config,
|
||||
}
|
||||
}
|
||||
#[hdl]
|
||||
fn try_push_fetch_queue(&mut self) -> Result<(), ()> {
|
||||
if self.fetch_queue.len() >= self.config.get().fetch_width.get() {
|
||||
return Err(());
|
||||
}
|
||||
let insn = self.insns.insns.get(&self.next_pc).ok_or(())?;
|
||||
let fallthrough_pc = self.next_pc.wrapping_add(insn.size_in_bytes.into());
|
||||
let is_last_mop_index = self.next_mop_index + 1 >= insn.mops.len();
|
||||
let mut predicted_next_pc = if is_last_mop_index {
|
||||
fallthrough_pc
|
||||
} else {
|
||||
self.next_pc
|
||||
};
|
||||
let prev_br_pred_state = self.next_br_pred_state;
|
||||
let mop = &insn.mops[self.next_mop_index];
|
||||
#[hdl(sim)]
|
||||
if let MOp::AluBranch(mop) = mop {
|
||||
#[hdl(sim)]
|
||||
match mop {
|
||||
AluBranchMOp::<_, _>::Branch(mop) => {
|
||||
predicted_next_pc =
|
||||
self.next_br_pred_state
|
||||
.predict_branch(mop, self.next_pc, fallthrough_pc);
|
||||
}
|
||||
AluBranchMOp::<_, _>::BranchI(mop) => {
|
||||
predicted_next_pc =
|
||||
self.next_br_pred_state
|
||||
.predict_branch(mop, self.next_pc, fallthrough_pc);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
let mop = #[hdl(sim)]
|
||||
MOpInstance::<_> {
|
||||
fetch_block_id: self.fetch_block_id.cast_to_static::<UInt<_>>(),
|
||||
id: self.next_id.cast_to_static::<UInt<_>>(),
|
||||
pc: self.next_pc,
|
||||
predicted_next_pc,
|
||||
size_in_bytes: insn.size_in_bytes.cast_to_static::<UInt<_>>(),
|
||||
is_first_mop_in_insn: self.next_mop_index == 0,
|
||||
mop,
|
||||
};
|
||||
println!("pushed to fetch queue: {mop:?}");
|
||||
self.fetch_queue.push_back((prev_br_pred_state, mop));
|
||||
if is_last_mop_index {
|
||||
self.next_mop_index = 0;
|
||||
self.next_pc = predicted_next_pc;
|
||||
} else {
|
||||
self.next_mop_index += 1;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn peek_post_decode_output_insns(
|
||||
&self,
|
||||
) -> SimValue<ArrayVec<MOpInstance<MOp>, CpuConfigFetchWidth<C>>> {
|
||||
let ty = ArrayVec[MOpInstance[MOp]][CpuConfigFetchWidth[self.config]];
|
||||
let peek_size = self.random_state.random_u64(u32::from_le_bytes(*b"pdoi")) as usize
|
||||
% (ty.capacity() + 1);
|
||||
ty.from_iter_sim(
|
||||
zeroed(MOpInstance[MOp]),
|
||||
self.fetch_queue.iter().map(|(_, v)| v).take(peek_size),
|
||||
)
|
||||
.ok()
|
||||
.expect("known to fit")
|
||||
}
|
||||
}
|
||||
|
||||
#[hdl_module(extern)]
|
||||
fn mock_next_pc<#[hdl(skip)] MI: MakeInsns>(config: PhantomConst<CpuConfig>) {
|
||||
#[hdl]
|
||||
let cd: ClockDomain = m.input();
|
||||
#[hdl]
|
||||
let post_decode_output: PostDecodeOutputInterface<PhantomConst<CpuConfig>> =
|
||||
m.output(PostDecodeOutputInterface[config]);
|
||||
#[hdl]
|
||||
let from_retire: RetireToNextPcInterface<PhantomConst<CpuConfig>> =
|
||||
m.input(RetireToNextPcInterface[config]);
|
||||
#[hdl]
|
||||
let debug_state: MockNextPcDebugState<PhantomConst<CpuConfig>> =
|
||||
m.output(MockNextPcDebugState[config]);
|
||||
m.register_clock_for_past(cd.clk);
|
||||
m.extern_module_simulation_fn(
|
||||
(cd, post_decode_output, from_retire, debug_state),
|
||||
|args, mut sim| async move {
|
||||
let (cd, post_decode_output, from_retire, debug_state) = args;
|
||||
// intentionally have a different sequence each time we're reset
|
||||
let random_state = RandomState {
|
||||
state: Cell::new(0),
|
||||
};
|
||||
let insns = MI::make_insns();
|
||||
sim.resettable(
|
||||
cd,
|
||||
async |mut sim| {
|
||||
sim.write(
|
||||
post_decode_output.insns,
|
||||
post_decode_output
|
||||
.ty()
|
||||
.insns
|
||||
.new_sim(zeroed(StaticType::TYPE)),
|
||||
)
|
||||
.await;
|
||||
sim.write(
|
||||
post_decode_output.cancel.data,
|
||||
#[hdl(sim)]
|
||||
HdlNone(),
|
||||
)
|
||||
.await;
|
||||
sim.write(from_retire.inner.ready, false).await;
|
||||
},
|
||||
|sim, ()| {
|
||||
run_fn(
|
||||
cd,
|
||||
post_decode_output,
|
||||
from_retire,
|
||||
debug_state,
|
||||
&random_state,
|
||||
&insns,
|
||||
sim,
|
||||
)
|
||||
},
|
||||
)
|
||||
.await;
|
||||
},
|
||||
);
|
||||
#[hdl]
|
||||
async fn run_fn(
|
||||
cd: Expr<ClockDomain>,
|
||||
post_decode_output: Expr<PostDecodeOutputInterface<PhantomConst<CpuConfig>>>,
|
||||
from_retire: Expr<RetireToNextPcInterface<PhantomConst<CpuConfig>>>,
|
||||
debug_state: Expr<MockNextPcDebugState<PhantomConst<CpuConfig>>>,
|
||||
random_state: &RandomState,
|
||||
insns: &Insns,
|
||||
mut sim: ExternModuleSimulationState,
|
||||
) {
|
||||
let config = post_decode_output.ty().config;
|
||||
let mut state = MockNextPcState::new(random_state, insns, config);
|
||||
loop {
|
||||
while state.try_push_fetch_queue().is_ok() {}
|
||||
state.fetch_block_id += 1;
|
||||
let post_decode_output_insns = state.peek_post_decode_output_insns();
|
||||
sim.write(post_decode_output.insns, &post_decode_output_insns)
|
||||
.await;
|
||||
sim.write(
|
||||
post_decode_output.cancel.data,
|
||||
#[hdl(sim)]
|
||||
HdlNone(),
|
||||
)
|
||||
.await;
|
||||
sim.write(from_retire.inner.ready, false).await;
|
||||
sim.write(debug_state, state.debug_state()).await;
|
||||
sim.wait_for_clock_edge(cd.clk).await;
|
||||
let ready = *sim.read_past(post_decode_output.ready, cd.clk).await;
|
||||
for _ in ArrayVec::elements_sim_ref(&post_decode_output_insns)
|
||||
.iter()
|
||||
.take(ready)
|
||||
{
|
||||
let Some((prev_br_pred_state, started)) = state.fetch_queue.pop_front() else {
|
||||
unreachable!();
|
||||
};
|
||||
println!("started: {started:?}\nprev_br_pred_state: {prev_br_pred_state:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[hdl_module]
|
||||
fn rename_execute_retire_test_harness<#[hdl(skip)] MI: MakeInsns>(config: PhantomConst<CpuConfig>) {
|
||||
#[hdl]
|
||||
let cd: ClockDomain = m.input();
|
||||
#[hdl]
|
||||
let next_pc = instance(mock_next_pc::<MI>(config));
|
||||
connect(next_pc.cd, cd);
|
||||
#[hdl]
|
||||
let dut = instance(rename_execute_retire(config));
|
||||
connect(dut.cd, cd);
|
||||
connect(dut.from_post_decode, next_pc.post_decode_output);
|
||||
connect(next_pc.from_retire, dut.to_next_pc);
|
||||
}
|
||||
|
||||
#[hdl]
|
||||
#[test]
|
||||
fn test_rename_execute_retire_fibonacci() {
|
||||
let _n = SourceLocation::normalize_files_for_tests();
|
||||
let mut config = CpuConfig::new(
|
||||
vec![
|
||||
UnitConfig::new(UnitKind::AluBranch),
|
||||
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(3).unwrap();
|
||||
let m = rename_execute_retire_test_harness::<FibonacciInsns>(PhantomConst::new_sized(config));
|
||||
let mut sim = Simulation::new(m);
|
||||
let writer = RcWriter::default();
|
||||
sim.add_trace_writer(VcdWriterDecls::new(writer.clone()));
|
||||
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_reset(sim.io().cd.rst, true);
|
||||
for cycle in 0..50 {
|
||||
sim.advance_time(SimDuration::from_nanos(500));
|
||||
println!("clock tick: {cycle}");
|
||||
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);
|
||||
}
|
||||
panic!();
|
||||
// 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.vcd") {
|
||||
panic!();
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue