forked from libre-chip/cpu
467 lines
15 KiB
Rust
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 }),
|
|
}
|
|
}
|
|
}
|