cpu/crates/cpu/src/unit.rs

467 lines
15 KiB
Rust

// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{
config::CpuConfig,
instruction::{
AluBranchMOp, LoadStoreMOp, MOp, MOpDestReg, MOpInto, MOpRegNum, MOpTrait, RenamedMOp,
UnitOutRegNum, mop_enum,
},
register::{FlagsMode, PRegValue},
unit::unit_base::UnitToRegAlloc,
};
use fayalite::{
bundle::{Bundle, BundleType},
intern::{Intern, Interned},
prelude::*,
};
use serde::{Deserialize, Serialize};
pub mod alu_branch;
pub mod unit_base;
macro_rules! all_units {
(
#[hdl_unit_kind = $HdlUnitKind:ident]
#[unit_kind = $UnitKind:ident]
#[hdl]
$(#[$enum_meta:meta])*
$vis:vis enum $UnitMOpEnum:ident<$DestReg:ident: Type, $SrcRegWidth:ident: Size, #[MOp(get_ty = $transformed_move_op_get_ty:expr)] $TransformedMoveOp:ident: Type> {
$(
$(#[transformed_move $($transformed_move:tt)*])?
#[create_dyn_unit_fn = $create_dyn_unit_fn:expr]
#[extract = $extract:ident]
$(#[$variant_meta:meta])*
$Unit:ident($Op:ty),
)*
}
) => {
$(#[$enum_meta])*
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Serialize, Deserialize)]
$vis enum $UnitKind {
$(
$(#[$variant_meta])*
$Unit,
)*
}
impl $UnitKind {
pub fn unit(self, config: &CpuConfig, unit_index: usize) -> DynUnit {
match self {
$($UnitKind::$Unit => $create_dyn_unit_fn(config, unit_index),)*
}
}
}
impl ToExpr for $UnitKind {
type Type = $HdlUnitKind;
fn to_expr(&self) -> Expr<Self::Type> {
match self {
$($UnitKind::$Unit => $HdlUnitKind.$Unit(),)*
}
}
}
#[hdl]
$(#[$enum_meta])*
$vis enum $HdlUnitKind {
$(
$(#[$variant_meta])*
$Unit,
)*
}
mop_enum! {
#[impl_mop_into = false]
#[hdl]
$(#[$enum_meta])*
$vis enum $UnitMOpEnum<$DestReg: Type, $SrcRegWidth: Size, #[MOp(get_ty = $transformed_move_op_get_ty)] $TransformedMoveOp: Type> {
$(
$(#[$variant_meta])*
$Unit($Op),
)*
}
}
impl<$DestReg: Type, $SrcRegWidth: Size, $TransformedMoveOp: Type> $UnitMOpEnum<$DestReg, $SrcRegWidth, $TransformedMoveOp> {
#[hdl]
$vis fn kind(expr: impl ToExpr<Type = Self>) -> Expr<$HdlUnitKind> {
#[hdl]
let unit_kind = wire();
#[hdl]
match expr {
$(Self::$Unit(_) => connect(unit_kind, $HdlUnitKind.$Unit()),)*
}
unit_kind
}
$(
#[hdl]
$vis fn $extract(expr: impl ToExpr<Type = Self>) -> Expr<HdlOption<$Op>> {
let expr = expr.to_expr();
let ty = Expr::ty(expr);
#[hdl]
let $extract = wire(HdlOption[ty.$Unit]);
connect($extract, HdlOption[ty.$Unit].HdlNone());
#[hdl]
if let Self::$Unit(v) = expr {
connect($extract, HdlSome(v));
}
$extract
}
)*
$vis fn with_transformed_move_op_ty<T>(self, new_transformed_move_op_ty: T) -> $UnitMOpEnum<$DestReg, $SrcRegWidth, T>
where
T: MOpTrait<DestReg = $DestReg, SrcRegWidth = $SrcRegWidth>,
$TransformedMoveOp: MOpTrait<DestReg = $DestReg, SrcRegWidth = $SrcRegWidth>,
{
$UnitMOpEnum[self.dest_reg_ty()][self.src_reg_width()][new_transformed_move_op_ty]
}
}
all_units! {
@split_by_transformed_move
$vis enum $UnitMOpEnum<$DestReg: Type, $SrcRegWidth: Size, $TransformedMoveOp: Type> {
$(
$(#[transformed_move $($transformed_move)*])?
$Unit($Op),
)*
}
}
impl CpuConfig {
#[hdl]
pub fn available_units_for_kind(&self, unit_kind: impl ToExpr<Type = $HdlUnitKind>) -> Expr<Array<Bool>> {
#[hdl]
let available_units_for_kind = wire(Array[Bool][self.units.len()]);
#[hdl]
match unit_kind {
$($HdlUnitKind::$Unit => for (index, unit) in self.units.iter().enumerate() {
connect(available_units_for_kind[index], unit.kind == $UnitKind::$Unit);
})*
}
available_units_for_kind
}
}
};
(
@split_by_transformed_move
$vis:vis enum $UnitMOpEnum:ident<$DestReg:ident: Type, $SrcRegWidth:ident: Size, $TransformedMoveOp:ident: Type> {
$($BeforeUnit:ident($BeforeOp:ty),)*
#[transformed_move]
$TransformedMove:ident($TransformedMoveOp2:ty),
$($AfterUnit:ident($AfterOp:ty),)*
}
) => {
impl<$DestReg: Type, $SrcRegWidth: Size, $TransformedMoveOp: Type> $UnitMOpEnum<$DestReg, $SrcRegWidth, $TransformedMoveOp> {
#[hdl]
$vis fn try_with_transformed_move_op<T>(
this: impl ToExpr<Type = Self>,
new_transformed_move_op_ty: T,
connect_transformed_move_op: impl FnOnce(Expr<HdlOption<$UnitMOpEnum<$DestReg, $SrcRegWidth, T>>>, Expr<$TransformedMoveOp>),
) -> Expr<HdlOption<$UnitMOpEnum<$DestReg, $SrcRegWidth, T>>>
where
T: MOpTrait<DestReg = $DestReg, SrcRegWidth = $SrcRegWidth>,
$TransformedMoveOp: MOpTrait<DestReg = $DestReg, SrcRegWidth = $SrcRegWidth>,
{
let this = this.to_expr();
let new_ty = Expr::ty(this).with_transformed_move_op_ty(new_transformed_move_op_ty);
#[hdl]
let with_transformed_move_op = wire(HdlOption[new_ty]);
connect(with_transformed_move_op, Expr::ty(with_transformed_move_op).HdlNone());
// workaround #[hdl] match expanding to a loop, so you can't move variables in it
let mut connect_transformed_move_op = Some(connect_transformed_move_op);
#[hdl]
match this {
$(Self::$BeforeUnit(unit) => connect(with_transformed_move_op, HdlSome(new_ty.$BeforeUnit(unit))),)*
Self::$TransformedMove(unit) => connect_transformed_move_op.take().expect("only reached once")(with_transformed_move_op, unit),
$(Self::$AfterUnit(unit) => connect(with_transformed_move_op, HdlSome(new_ty.$AfterUnit(unit))),)*
}
with_transformed_move_op
}
}
const _: () = {
#[hdl]
type $DestReg = MOpDestReg;
type $SrcRegWidth = ConstUsize<{ MOpRegNum::WIDTH }>;
$(impl MOpInto<MOp> for $BeforeOp {
fn mop_into_ty(self) -> MOp {
MOp
}
fn mop_into(this: Expr<Self>) -> Expr<MOp> {
MOp.$BeforeUnit(this)
}
})*
$(impl MOpInto<MOp> for $AfterOp {
fn mop_into_ty(self) -> MOp {
MOp
}
fn mop_into(this: Expr<Self>) -> Expr<MOp> {
MOp.$AfterUnit(this)
}
})*
};
$(impl<$DestReg: Type, $SrcRegWidth: Size> MOpInto<RenamedMOp<$DestReg, $SrcRegWidth>> for $BeforeOp {
fn mop_into_ty(self) -> RenamedMOp<$DestReg, $SrcRegWidth> {
RenamedMOp[MOpTrait::dest_reg_ty(self)][MOpTrait::src_reg_width(self)]
}
fn mop_into(this: Expr<Self>) -> Expr<RenamedMOp<$DestReg, $SrcRegWidth>> {
MOpInto::<RenamedMOp<$DestReg, $SrcRegWidth>>::mop_into_ty(Expr::ty(this)).$BeforeUnit(this)
}
})*
$(impl<$DestReg: Type, $SrcRegWidth: Size> MOpInto<RenamedMOp<$DestReg, $SrcRegWidth>> for $AfterOp {
fn mop_into_ty(self) -> RenamedMOp<$DestReg, $SrcRegWidth> {
RenamedMOp[MOpTrait::dest_reg_ty(self)][MOpTrait::src_reg_width(self)]
}
fn mop_into(this: Expr<Self>) -> Expr<RenamedMOp<$DestReg, $SrcRegWidth>> {
MOpInto::<RenamedMOp<$DestReg, $SrcRegWidth>>::mop_into_ty(Expr::ty(this)).$AfterUnit(this)
}
})*
};
}
all_units! {
#[hdl_unit_kind = HdlUnitKind]
#[unit_kind = UnitKind]
#[hdl]
pub enum UnitMOp<
DestReg: Type,
SrcRegWidth: Size,
#[MOp(get_ty = |this: UnitMOp<DestReg, SrcRegWidth, TransformedMoveOp>, new_dest_reg, new_src_reg_width| {
this.TransformedMove.mapped_ty(new_dest_reg, new_src_reg_width)
})] TransformedMoveOp: Type
> {
#[create_dyn_unit_fn = |config, unit_index| alu_branch::AluBranch::new(config, unit_index).to_dyn()]
#[extract = alu_branch_mop]
AluBranch(AluBranchMOp<DestReg, SrcRegWidth>),
#[transformed_move]
#[create_dyn_unit_fn = |config, unit_index| todo!()]
#[extract = transformed_move_mop]
TransformedMove(TransformedMoveOp),
#[create_dyn_unit_fn = |config, unit_index| todo!()]
#[extract = load_store_mop]
LoadStore(LoadStoreMOp<DestReg, SrcRegWidth>),
}
}
#[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)]
pub struct UnitOutputWrite<OutRegNumWidth: Size> {
pub which: UnitOutRegNum<OutRegNumWidth>,
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]
pub struct UnitOutput<OutRegNumWidth: Size, ExtraOut> {
pub which: UnitOutRegNum<OutRegNumWidth>,
pub result: UnitResult<ExtraOut>,
}
impl<OutRegNumWidth: Size, ExtraOut: Type> UnitOutput<OutRegNumWidth, 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>,
}
pub trait UnitTrait:
'static + Send + Sync + std::fmt::Debug + fayalite::intern::SupportsPtrEqWithTypeId
{
type Type: BundleType;
type ExtraOut: Type;
type MOp: 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 extract_mop(
&self,
mop: Expr<RenamedMOp<UnitOutRegNum<DynSize>, DynSize>>,
) -> Expr<HdlOption<Self::MOp>>;
fn module(&self) -> Interned<Module<Self::Type>>;
fn unit_to_reg_alloc(
&self,
this: Expr<Self::Type>,
) -> Expr<UnitToRegAlloc<Self::MOp, Self::ExtraOut, DynSize, DynSize, DynSize>>;
fn cd(&self, this: Expr<Self::Type>) -> Expr<ClockDomain>;
fn global_state(&self, this: Expr<Self::Type>) -> Expr<GlobalState>;
fn to_dyn(&self) -> DynUnit;
}
type DynUnitTrait = dyn UnitTrait<Type = Bundle, ExtraOut = CanonicalType, MOp = CanonicalType>;
impl fayalite::intern::InternedCompare for DynUnitTrait {
type InternedCompareKey = fayalite::intern::PtrEqWithTypeId;
fn interned_compare_key_ref(this: &Self) -> Self::InternedCompareKey {
Self::get_ptr_eq_with_type_id(this)
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct DynUnit {
ty: Bundle,
extra_out_ty: CanonicalType,
mop_ty: CanonicalType,
unit_kind: UnitKind,
unit: Interned<DynUnitTrait>,
}
impl UnitTrait for DynUnit {
type Type = Bundle;
type ExtraOut = CanonicalType;
type MOp = CanonicalType;
fn ty(&self) -> Self::Type {
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 {
self.unit_kind
}
fn extract_mop(
&self,
mop: Expr<RenamedMOp<UnitOutRegNum<DynSize>, DynSize>>,
) -> Expr<HdlOption<Self::MOp>> {
self.unit.extract_mop(mop)
}
fn module(&self) -> Interned<Module<Self::Type>> {
self.unit.module()
}
fn unit_to_reg_alloc(
&self,
this: Expr<Self::Type>,
) -> Expr<UnitToRegAlloc<Self::MOp, Self::ExtraOut, DynSize, DynSize, DynSize>> {
self.unit.unit_to_reg_alloc(this)
}
fn cd(&self, this: Expr<Self::Type>) -> Expr<ClockDomain> {
self.unit.cd(this)
}
fn global_state(&self, this: Expr<Self::Type>) -> Expr<GlobalState> {
self.unit.global_state(this)
}
fn to_dyn(&self) -> DynUnit {
*self
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct DynUnitWrapper<T>(pub T);
impl<T: UnitTrait + Clone + std::hash::Hash + Eq> UnitTrait for DynUnitWrapper<T> {
type Type = Bundle;
type ExtraOut = CanonicalType;
type MOp = CanonicalType;
fn ty(&self) -> Self::Type {
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 {
self.0.unit_kind()
}
fn extract_mop(
&self,
mop: Expr<RenamedMOp<UnitOutRegNum<DynSize>, DynSize>>,
) -> Expr<HdlOption<Self::MOp>> {
Expr::from_enum(Expr::as_enum(self.0.extract_mop(mop)))
}
fn module(&self) -> Interned<Module<Self::Type>> {
self.0.module().canonical().intern_sized()
}
fn unit_to_reg_alloc(
&self,
this: Expr<Self::Type>,
) -> Expr<UnitToRegAlloc<Self::MOp, Self::ExtraOut, DynSize, DynSize, DynSize>> {
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))
}
fn global_state(&self, this: Expr<Self::Type>) -> Expr<GlobalState> {
self.0.global_state(Expr::from_bundle(this))
}
fn to_dyn(&self) -> DynUnit {
let unit = self.intern();
DynUnit {
ty: unit.ty(),
extra_out_ty: unit.extra_out_ty(),
mop_ty: unit.mop_ty(),
unit_kind: unit.unit_kind(),
unit: Interned::cast_unchecked(unit, |v: &Self| -> &DynUnitTrait { v }),
}
}
}