1092 lines
38 KiB
Rust
1092 lines
38 KiB
Rust
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
// See Notices.txt for copyright information
|
|
use crate::{unit::UnitMOp, util::range_u32_len};
|
|
use fayalite::{
|
|
expr::ops::{ArrayLiteral, ExprPartialEq},
|
|
intern::Interned,
|
|
prelude::*,
|
|
};
|
|
use std::{fmt, marker::PhantomData, ops::Range};
|
|
|
|
pub mod power_isa;
|
|
|
|
pub trait MOpInto<Target: MOpTrait>: MOpTrait {
|
|
fn mop_into_ty(self) -> Target;
|
|
fn mop_into(this: Expr<Self>) -> Expr<Target>;
|
|
}
|
|
|
|
impl<T: MOpTrait> MOpInto<T> for T {
|
|
fn mop_into_ty(self) -> T {
|
|
self
|
|
}
|
|
fn mop_into(this: Expr<Self>) -> Expr<T> {
|
|
this
|
|
}
|
|
}
|
|
|
|
pub trait MOpTrait: Type {
|
|
type Mapped<NewDestReg: Type, NewSrcRegWidth: Size>: MOpTrait<DestReg = NewDestReg, SrcRegWidth = NewSrcRegWidth>;
|
|
type DestReg: Type;
|
|
type SrcRegWidth: Size;
|
|
fn dest_reg_ty(self) -> Self::DestReg;
|
|
fn dest_reg(input: impl ToExpr<Type = Self>) -> Expr<Self::DestReg>;
|
|
fn src_reg_width(self) -> <Self::SrcRegWidth as Size>::SizeType;
|
|
fn src_reg_ty(self) -> UIntType<Self::SrcRegWidth> {
|
|
UInt[self.src_reg_width()]
|
|
}
|
|
fn src_regs_ty(self) -> Array<UIntType<Self::SrcRegWidth>, { COMMON_MOP_SRC_LEN }> {
|
|
Array[self.src_reg_ty()][ConstUsize::<{ COMMON_MOP_SRC_LEN }>]
|
|
}
|
|
fn for_each_src_reg(
|
|
input: impl ToExpr<Type = Self>,
|
|
f: &mut impl FnMut(Expr<UIntType<Self::SrcRegWidth>>, usize),
|
|
);
|
|
fn connect_src_regs(
|
|
input: impl ToExpr<Type = Self>,
|
|
src_regs: impl ToExpr<Type = Array<UIntType<Self::SrcRegWidth>, { COMMON_MOP_SRC_LEN }>>,
|
|
) {
|
|
let src_regs = src_regs.to_expr();
|
|
Self::for_each_src_reg(input.to_expr(), &mut |src_reg, index| {
|
|
connect(src_regs[index], src_reg);
|
|
});
|
|
}
|
|
fn mapped_ty<NewDestReg: Type, NewSrcRegWidth: Size>(
|
|
self,
|
|
new_dest_reg: NewDestReg,
|
|
new_src_reg_width: NewSrcRegWidth::SizeType,
|
|
) -> Self::Mapped<NewDestReg, NewSrcRegWidth>;
|
|
fn map_regs<NewDestReg: Type, NewSrcRegWidth: Size>(
|
|
input: impl ToExpr<Type = Self>,
|
|
new_dest: impl ToExpr<Type = NewDestReg>,
|
|
new_src_reg_width: NewSrcRegWidth::SizeType,
|
|
map_src: &mut impl FnMut(
|
|
Expr<UIntType<Self::SrcRegWidth>>,
|
|
usize,
|
|
) -> Expr<UIntType<NewSrcRegWidth>>,
|
|
) -> Expr<Self::Mapped<NewDestReg, NewSrcRegWidth>>;
|
|
}
|
|
|
|
pub trait CommonMOpTrait: MOpTrait {
|
|
type PrefixPad: KnownSize;
|
|
type SrcCount: KnownSize;
|
|
type CommonMOpTraitMapped<NewDestReg: Type, NewSrcRegWidth: Size>: CommonMOpTrait<
|
|
DestReg = NewDestReg,
|
|
SrcRegWidth = NewSrcRegWidth,
|
|
PrefixPad = Self::PrefixPad,
|
|
SrcCount = Self::SrcCount,
|
|
>;
|
|
type CommonMOpTraitDestReg: Type;
|
|
type CommonMOpTraitSrcRegWidth: Size;
|
|
fn common_mop_ty(
|
|
self,
|
|
) -> CommonMOp<Self::PrefixPad, Self::DestReg, Self::SrcRegWidth, Self::SrcCount>;
|
|
fn common_mop(
|
|
input: impl ToExpr<Type = Self>,
|
|
) -> Expr<CommonMOp<Self::PrefixPad, Self::DestReg, Self::SrcRegWidth, Self::SrcCount>>;
|
|
fn with_common_mop_ty<NewDestReg: Type, NewSrcRegWidth: Size>(
|
|
self,
|
|
new_common_mop_ty: CommonMOp<Self::PrefixPad, NewDestReg, NewSrcRegWidth, Self::SrcCount>,
|
|
) -> Self::Mapped<NewDestReg, NewSrcRegWidth>;
|
|
fn with_common_mop<NewDestReg: Type, NewSrcRegWidth: Size>(
|
|
input: impl ToExpr<Type = Self>,
|
|
new_common_mop: impl ToExpr<
|
|
Type = CommonMOp<Self::PrefixPad, NewDestReg, NewSrcRegWidth, Self::SrcCount>,
|
|
>,
|
|
) -> Expr<Self::Mapped<NewDestReg, NewSrcRegWidth>>;
|
|
}
|
|
|
|
impl<T: CommonMOpTrait> MOpTrait for T {
|
|
type Mapped<NewDestReg: Type, NewSrcRegWidth: Size> =
|
|
T::CommonMOpTraitMapped<NewDestReg, NewSrcRegWidth>;
|
|
type DestReg = T::CommonMOpTraitDestReg;
|
|
type SrcRegWidth = T::CommonMOpTraitSrcRegWidth;
|
|
fn dest_reg_ty(self) -> Self::DestReg {
|
|
self.common_mop_ty().dest
|
|
}
|
|
fn dest_reg(input: impl ToExpr<Type = Self>) -> Expr<Self::DestReg> {
|
|
T::common_mop(input).dest
|
|
}
|
|
fn src_reg_width(self) -> <Self::SrcRegWidth as Size>::SizeType {
|
|
self.common_mop_ty().src.element().width
|
|
}
|
|
fn for_each_src_reg(
|
|
input: impl ToExpr<Type = Self>,
|
|
f: &mut impl FnMut(Expr<UIntType<Self::SrcRegWidth>>, usize),
|
|
) {
|
|
let input = input.to_expr();
|
|
let common = T::common_mop(input);
|
|
for index in 0..T::SrcCount::VALUE {
|
|
f(common.src[index], index);
|
|
}
|
|
}
|
|
fn mapped_ty<NewDestReg: Type, NewSrcRegWidth: Size>(
|
|
self,
|
|
new_dest_reg: NewDestReg,
|
|
new_src_reg_width: NewSrcRegWidth::SizeType,
|
|
) -> Self::Mapped<NewDestReg, NewSrcRegWidth> {
|
|
self.with_common_mop_ty(
|
|
CommonMOp[T::PrefixPad::SIZE][new_dest_reg][new_src_reg_width][T::SrcCount::SIZE],
|
|
)
|
|
}
|
|
fn map_regs<NewDestReg: Type, NewSrcRegWidth: Size>(
|
|
input: impl ToExpr<Type = Self>,
|
|
new_dest: impl ToExpr<Type = NewDestReg>,
|
|
new_src_reg_width: NewSrcRegWidth::SizeType,
|
|
map_src: &mut impl FnMut(
|
|
Expr<UIntType<Self::SrcRegWidth>>,
|
|
usize,
|
|
) -> Expr<UIntType<NewSrcRegWidth>>,
|
|
) -> Expr<Self::Mapped<NewDestReg, NewSrcRegWidth>> {
|
|
let input = input.to_expr();
|
|
let common = T::common_mop(input);
|
|
let new_dest = new_dest.to_expr();
|
|
T::with_common_mop(
|
|
input,
|
|
CommonMOp::new(
|
|
common.prefix_pad,
|
|
new_dest,
|
|
ArrayLiteral::new(
|
|
UIntType[new_src_reg_width],
|
|
Interned::from_iter(
|
|
(0..T::SrcCount::VALUE)
|
|
.map(|index| Expr::canonical(map_src(common.src[index], index))),
|
|
),
|
|
)
|
|
.to_expr(),
|
|
CommonMOp::imm(common),
|
|
),
|
|
)
|
|
}
|
|
}
|
|
|
|
#[hdl]
|
|
pub enum OutputIntegerMode {
|
|
Full64,
|
|
DupLow32,
|
|
ZeroExt32,
|
|
SignExt32,
|
|
ZeroExt16,
|
|
SignExt16,
|
|
ZeroExt8,
|
|
SignExt8,
|
|
}
|
|
|
|
impl ExprPartialEq<Self> for OutputIntegerMode {
|
|
fn cmp_eq(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
|
|
lhs.cast_to_bits().cmp_eq(rhs.cast_to_bits())
|
|
}
|
|
|
|
fn cmp_ne(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
|
|
lhs.cast_to_bits().cmp_ne(rhs.cast_to_bits())
|
|
}
|
|
}
|
|
|
|
pub const MOP_IMM_WIDTH: usize = 34;
|
|
pub const MOP_MIN_REG_WIDTH: usize = 8;
|
|
pub const COMMON_MOP_SRC_LEN: usize = 3;
|
|
pub const COMMON_MOP_MIN_SRC_LEN_WITH_FULL_IMM: usize = 2;
|
|
pub const COMMON_MOP_IMM_LOW_WIDTH: usize = CommonMOpWithMaxSrcCount::IMM_WIDTH - 1;
|
|
|
|
#[hdl(cmp_eq)]
|
|
pub struct CommonMOp<PrefixPad: KnownSize, DestReg: Type, SrcRegWidth: Size, SrcCount: KnownSize> {
|
|
pub prefix_pad: UIntType<PrefixPad>,
|
|
pub dest: DestReg,
|
|
pub src: Array<UIntType<SrcRegWidth>, { COMMON_MOP_SRC_LEN }>,
|
|
pub imm_low: UInt<{ COMMON_MOP_IMM_LOW_WIDTH }>,
|
|
pub imm_sign: SInt<1>,
|
|
pub _phantom: PhantomData<SrcCount>,
|
|
}
|
|
|
|
impl<PrefixPad: KnownSize, DestReg: Type, SrcRegWidth: Size, SrcCount: KnownSize> CommonMOpTrait
|
|
for CommonMOp<PrefixPad, DestReg, SrcRegWidth, SrcCount>
|
|
{
|
|
type PrefixPad = PrefixPad;
|
|
type SrcCount = SrcCount;
|
|
type CommonMOpTraitMapped<NewDestReg: Type, NewSrcRegWidth: Size> =
|
|
CommonMOp<PrefixPad, NewDestReg, NewSrcRegWidth, SrcCount>;
|
|
type CommonMOpTraitDestReg = DestReg;
|
|
type CommonMOpTraitSrcRegWidth = SrcRegWidth;
|
|
fn common_mop_ty(
|
|
self,
|
|
) -> CommonMOp<Self::PrefixPad, Self::DestReg, Self::SrcRegWidth, Self::SrcCount> {
|
|
self
|
|
}
|
|
fn common_mop(
|
|
input: impl ToExpr<Type = Self>,
|
|
) -> Expr<CommonMOp<Self::PrefixPad, Self::DestReg, Self::SrcRegWidth, Self::SrcCount>> {
|
|
input.to_expr()
|
|
}
|
|
fn with_common_mop_ty<NewDestReg: Type, NewSrcRegWidth: Size>(
|
|
self,
|
|
new_common_mop_ty: CommonMOp<Self::PrefixPad, NewDestReg, NewSrcRegWidth, Self::SrcCount>,
|
|
) -> Self::Mapped<NewDestReg, NewSrcRegWidth> {
|
|
new_common_mop_ty
|
|
}
|
|
fn with_common_mop<NewDestReg: Type, NewSrcRegWidth: Size>(
|
|
input: impl ToExpr<Type = Self>,
|
|
new_common_mop: impl ToExpr<
|
|
Type = CommonMOp<Self::PrefixPad, NewDestReg, NewSrcRegWidth, Self::SrcCount>,
|
|
>,
|
|
) -> Expr<Self::Mapped<NewDestReg, NewSrcRegWidth>> {
|
|
let _ = input.to_expr();
|
|
new_common_mop.to_expr()
|
|
}
|
|
}
|
|
|
|
#[hdl(cmp_eq)]
|
|
pub struct CommonMOpImmParts<ImmInSrcCount: Size> {
|
|
// fields must be in this exact order
|
|
pub imm_low: UInt<{ COMMON_MOP_IMM_LOW_WIDTH }>,
|
|
pub reversed_src: ArrayType<UInt<{ MOP_MIN_REG_WIDTH }>, ImmInSrcCount>,
|
|
pub imm_sign: SInt<1>,
|
|
}
|
|
|
|
type CommonMOpWithMaxSrcCount = CommonMOpForImm<{ COMMON_MOP_SRC_LEN }>;
|
|
|
|
type CommonMOpForImm<const SRC_COUNT: usize> =
|
|
CommonMOp<ConstUsize<0>, (), ConstUsize<{ MOP_MIN_REG_WIDTH }>, ConstUsize<SRC_COUNT>>;
|
|
|
|
pub const COMMON_MOP_0_IMM_WIDTH: usize = CommonMOpForImm::<0>::IMM_WIDTH;
|
|
pub const COMMON_MOP_1_IMM_WIDTH: usize = CommonMOpForImm::<1>::IMM_WIDTH;
|
|
pub const COMMON_MOP_2_IMM_WIDTH: usize = CommonMOpForImm::<2>::IMM_WIDTH;
|
|
pub const COMMON_MOP_3_IMM_WIDTH: usize = CommonMOpForImm::<3>::IMM_WIDTH;
|
|
const COMMON_MOP_0_IMM_IN_SRC_COUNT: usize = CommonMOpForImm::<0>::IMM_IN_SRC_COUNT;
|
|
|
|
impl<PrefixPad: KnownSize, DestReg: Type, SrcRegWidth: Size, SrcCount: KnownSize>
|
|
CommonMOp<PrefixPad, DestReg, SrcRegWidth, SrcCount>
|
|
{
|
|
pub const IMM_IN_SRC_COUNT: usize = {
|
|
assert!(SrcCount::VALUE <= COMMON_MOP_SRC_LEN, "too many sources");
|
|
const _: () = assert!(COMMON_MOP_MIN_SRC_LEN_WITH_FULL_IMM <= COMMON_MOP_SRC_LEN);
|
|
(COMMON_MOP_SRC_LEN - COMMON_MOP_MIN_SRC_LEN_WITH_FULL_IMM)
|
|
- SrcCount::VALUE.saturating_sub(COMMON_MOP_MIN_SRC_LEN_WITH_FULL_IMM)
|
|
};
|
|
pub const IMM_IN_SRC_RANGE: Range<usize> =
|
|
(COMMON_MOP_SRC_LEN - Self::IMM_IN_SRC_COUNT)..COMMON_MOP_SRC_LEN;
|
|
pub const IMM_WIDTH: usize = {
|
|
MOP_IMM_WIDTH - (COMMON_MOP_0_IMM_IN_SRC_COUNT - Self::IMM_IN_SRC_COUNT) * MOP_MIN_REG_WIDTH
|
|
};
|
|
pub fn imm_ty() -> SInt {
|
|
SInt::new(Self::IMM_WIDTH)
|
|
}
|
|
pub fn imm_parts_ty() -> CommonMOpImmParts<DynSize> {
|
|
let retval = CommonMOpImmParts[Self::IMM_IN_SRC_COUNT];
|
|
assert_eq!(
|
|
retval.canonical().bit_width(),
|
|
Self::IMM_WIDTH,
|
|
"{retval:#?}"
|
|
);
|
|
retval
|
|
}
|
|
#[hdl]
|
|
pub fn new(
|
|
prefix_pad: impl ToExpr<Type = UIntType<PrefixPad>>,
|
|
dest: impl ToExpr<Type = DestReg>,
|
|
src: impl ToExpr<Type = ArrayType<UIntType<SrcRegWidth>, SrcCount>>,
|
|
imm: impl ToExpr<Type = SInt>,
|
|
) -> Expr<Self> {
|
|
let prefix_pad = prefix_pad.to_expr();
|
|
let dest = dest.to_expr();
|
|
let src_in = src.to_expr();
|
|
let imm = imm.to_expr();
|
|
assert_eq!(Expr::ty(imm), Self::imm_ty());
|
|
let src_reg_ty = Expr::ty(src_in).element();
|
|
let imm_parts = imm.cast_to_bits().cast_bits_to(Self::imm_parts_ty());
|
|
let mut src = [0_hdl_u0.cast_to(src_reg_ty); COMMON_MOP_SRC_LEN];
|
|
for i in 0..SrcCount::VALUE {
|
|
src[i] = src_in[i];
|
|
}
|
|
for (reversed_src_index, src_index) in Self::IMM_IN_SRC_RANGE.rev().enumerate() {
|
|
src[src_index] = imm_parts.reversed_src[reversed_src_index].cast_to(src_reg_ty);
|
|
}
|
|
#[hdl]
|
|
Self {
|
|
prefix_pad,
|
|
dest,
|
|
src: ArrayLiteral::new(
|
|
src_reg_ty,
|
|
Interned::from_iter(src.iter().map(|v| Expr::canonical(*v))),
|
|
)
|
|
.to_expr(),
|
|
imm_low: Expr::from_dyn_int(imm[..COMMON_MOP_IMM_LOW_WIDTH]),
|
|
imm_sign: Expr::from_dyn_int(imm >> (Self::IMM_WIDTH - 1)),
|
|
_phantom: PhantomData,
|
|
}
|
|
}
|
|
#[hdl]
|
|
pub fn imm(expr: impl ToExpr<Type = Self>) -> Expr<SInt> {
|
|
let expr = expr.to_expr();
|
|
let reversed_src = Vec::from_iter(
|
|
Self::IMM_IN_SRC_RANGE
|
|
.rev()
|
|
.map(|src_index| expr.src[src_index].cast_to_static()),
|
|
);
|
|
let imm_parts = {
|
|
#[hdl]
|
|
CommonMOpImmParts {
|
|
imm_low: expr.imm_low,
|
|
reversed_src,
|
|
imm_sign: expr.imm_sign,
|
|
}
|
|
};
|
|
imm_parts.cast_to_bits().cast_bits_to(Self::imm_ty())
|
|
}
|
|
#[hdl]
|
|
pub fn connect_to_imm(expr: impl ToExpr<Type = Self>, imm: impl ToExpr<Type = SInt>) {
|
|
let expr = expr.to_expr();
|
|
let src_reg_ty = Expr::ty(expr).src.element();
|
|
let imm = imm.to_expr();
|
|
assert_eq!(Expr::ty(imm), Self::imm_ty());
|
|
let imm_parts = imm.cast_to_bits().cast_bits_to(Self::imm_parts_ty());
|
|
let mut src = [Some(0_hdl_u0.cast_to(src_reg_ty)); COMMON_MOP_SRC_LEN];
|
|
for i in 0..SrcCount::VALUE {
|
|
src[i] = None;
|
|
}
|
|
for (reversed_src_index, src_index) in Self::IMM_IN_SRC_RANGE.rev().enumerate() {
|
|
src[src_index] = Some(imm_parts.reversed_src[reversed_src_index].cast_to(src_reg_ty));
|
|
}
|
|
for i in 0..COMMON_MOP_SRC_LEN {
|
|
if let Some(v) = src[i] {
|
|
connect(expr.src[i], v);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
macro_rules! common_mop_struct {
|
|
(
|
|
#[mapped(<$NewDestReg:ident, $SrcRegWidth:ident> $mapped_ty:ty)]
|
|
$(#[$struct_meta:meta])*
|
|
$vis:vis struct $MOp:ident<$($Generic:ident: $GenericBound:ident),* $(,)?> {
|
|
#[common]
|
|
$(#[$common_meta:meta])*
|
|
$common_vis:vis $common:ident: $common_ty:ty,
|
|
$(
|
|
$(#[$field_meta:meta])*
|
|
$field_vis:vis $field:ident: $field_ty:ty,
|
|
)*
|
|
}
|
|
) => {
|
|
$(#[$struct_meta])*
|
|
$vis struct $MOp<$($Generic: $GenericBound),*> {
|
|
$(#[$common_meta])*
|
|
$common_vis $common: $common_ty,
|
|
$(
|
|
$(#[$field_meta])*
|
|
$field_vis $field: $field_ty,
|
|
)*
|
|
}
|
|
|
|
impl<$($Generic: $GenericBound),*> CommonMOpTrait for $MOp<$($Generic),*> {
|
|
type PrefixPad = <$common_ty as CommonMOpTrait>::PrefixPad;
|
|
type SrcCount = <$common_ty as CommonMOpTrait>::SrcCount;
|
|
type CommonMOpTraitMapped<$NewDestReg: Type, $SrcRegWidth: Size> = $mapped_ty;
|
|
type CommonMOpTraitDestReg = <$common_ty as CommonMOpTrait>::CommonMOpTraitDestReg;
|
|
type CommonMOpTraitSrcRegWidth = <$common_ty as CommonMOpTrait>::CommonMOpTraitSrcRegWidth;
|
|
|
|
fn common_mop_ty(
|
|
self,
|
|
) -> CommonMOp<Self::PrefixPad, Self::DestReg, Self::SrcRegWidth, Self::SrcCount> {
|
|
CommonMOpTrait::common_mop_ty(self.$common)
|
|
}
|
|
fn common_mop(
|
|
input: impl ToExpr<Type = Self>,
|
|
) -> Expr<CommonMOp<Self::PrefixPad, Self::DestReg, Self::SrcRegWidth, Self::SrcCount>> {
|
|
CommonMOpTrait::common_mop(input.to_expr().$common)
|
|
}
|
|
fn with_common_mop_ty<NewDestReg: Type, NewSrcRegWidth: Size>(
|
|
self,
|
|
new_common_mop_ty: CommonMOp<Self::PrefixPad, NewDestReg, NewSrcRegWidth, Self::SrcCount>,
|
|
) -> Self::Mapped<NewDestReg, NewSrcRegWidth> {
|
|
$MOp {
|
|
$common: CommonMOpTrait::with_common_mop_ty(self.$common, new_common_mop_ty),
|
|
$($field: self.$field,)*
|
|
}
|
|
}
|
|
#[hdl]
|
|
fn with_common_mop<NewDestReg: Type, NewSrcRegWidth: Size>(
|
|
input: impl ToExpr<Type = Self>,
|
|
new_common_mop: impl ToExpr<
|
|
Type = CommonMOp<Self::PrefixPad, NewDestReg, NewSrcRegWidth, Self::SrcCount>,
|
|
>,
|
|
) -> Expr<Self::Mapped<NewDestReg, NewSrcRegWidth>> {
|
|
let input = input.to_expr();
|
|
#[hdl]
|
|
$MOp {
|
|
$common: CommonMOpTrait::with_common_mop(input.$common, new_common_mop),
|
|
$($field: input.$field,)*
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
macro_rules! mop_enum {
|
|
(
|
|
#[impl_mop_into = $impl_mop_into:tt]
|
|
$(#[$enum_meta:meta])*
|
|
$vis:vis enum $MOp:ident<
|
|
$DestReg:ident: Type,
|
|
$SrcRegWidth:ident: Size
|
|
$(, #[MOp(get_ty = $mop_types_get_ty:expr)] $MOpTypes:ident: Type)*
|
|
$(, #[Size(get_size = $sizes_get_size:expr)] $Sizes:ident: Size)*
|
|
> {
|
|
$(#[$($first_variant_meta:tt)*])*
|
|
$FirstVariant:ident($first_ty:ty),
|
|
$(
|
|
$(#[$variant_meta:meta])*
|
|
$Variant:ident($ty:ty),
|
|
)*
|
|
}
|
|
) => {
|
|
$(#[$enum_meta])*
|
|
$vis enum $MOp<$DestReg: Type, $SrcRegWidth: Size $(, $MOpTypes: Type)* $(, $Sizes: Size)*> {
|
|
$(#[$first_variant_meta])*
|
|
$FirstVariant($first_ty),
|
|
$(
|
|
$(#[$variant_meta])*
|
|
$Variant($ty),
|
|
)*
|
|
}
|
|
|
|
mop_enum! {
|
|
@impl_variants
|
|
#[impl_mop_into = $impl_mop_into]
|
|
enum $MOp [
|
|
$DestReg: Type,
|
|
$SrcRegWidth: Size
|
|
$(, #[MOp(get_ty = $mop_types_get_ty)] $MOpTypes: Type)*
|
|
$(, #[Size(get_size = $sizes_get_size)] $Sizes: Size)*
|
|
] {
|
|
$FirstVariant($first_ty),
|
|
$($Variant($ty),)*
|
|
}
|
|
}
|
|
|
|
impl<
|
|
$DestReg: Type,
|
|
$SrcRegWidth: Size,
|
|
$($MOpTypes: Type + MOpTrait<DestReg = $DestReg, SrcRegWidth = $SrcRegWidth>,)*
|
|
$($Sizes: Size,)*
|
|
> MOpTrait for $MOp<
|
|
$DestReg,
|
|
$SrcRegWidth,
|
|
$($MOpTypes,)*
|
|
$($Sizes,)*
|
|
> {
|
|
type Mapped<NewDestReg: Type, NewSrcRegWidth: Size> = $MOp<
|
|
NewDestReg,
|
|
NewSrcRegWidth,
|
|
$(<$MOpTypes as MOpTrait>::Mapped<NewDestReg, NewSrcRegWidth>,)*
|
|
$($Sizes,)*
|
|
>;
|
|
type DestReg = $DestReg;
|
|
type SrcRegWidth = $SrcRegWidth;
|
|
fn dest_reg_ty(self) -> Self::DestReg {
|
|
self.$FirstVariant.dest_reg_ty()
|
|
}
|
|
#[hdl]
|
|
fn dest_reg(input: impl ToExpr<Type = Self>) -> Expr<Self::DestReg> {
|
|
let input = input.to_expr();
|
|
#[hdl]
|
|
let dest_reg = wire(Expr::ty(input).dest_reg_ty());
|
|
#[hdl]
|
|
match input {
|
|
Self::$FirstVariant(v) => connect(dest_reg, <$first_ty as MOpTrait>::dest_reg(v)),
|
|
$(Self::$Variant(v) => connect(dest_reg, <$ty as MOpTrait>::dest_reg(v)),)*
|
|
}
|
|
dest_reg
|
|
}
|
|
fn src_reg_width(self) -> <Self::SrcRegWidth as Size>::SizeType {
|
|
self.$FirstVariant.src_reg_width()
|
|
}
|
|
#[hdl]
|
|
fn for_each_src_reg(
|
|
input: impl ToExpr<Type = Self>,
|
|
f: &mut impl FnMut(Expr<UIntType<Self::SrcRegWidth>>, usize),
|
|
) {
|
|
#[hdl]
|
|
match input {
|
|
Self::$FirstVariant(v) => MOpTrait::for_each_src_reg(v, f),
|
|
$(Self::$Variant(v) => MOpTrait::for_each_src_reg(v, f),)*
|
|
}
|
|
}
|
|
fn mapped_ty<NewDestReg: Type, NewSrcRegWidth: Size>(
|
|
self,
|
|
new_dest_reg: NewDestReg,
|
|
new_src_reg_width: NewSrcRegWidth::SizeType,
|
|
) -> Self::Mapped<NewDestReg, NewSrcRegWidth> {
|
|
$MOp[new_dest_reg][new_src_reg_width]$([$mop_types_get_ty(self, new_dest_reg, new_src_reg_width)])*$([$sizes_get_size(self)])*
|
|
}
|
|
#[hdl]
|
|
fn map_regs<NewDestReg: Type, NewSrcRegWidth: Size>(
|
|
input: impl ToExpr<Type = Self>,
|
|
new_dest: impl ToExpr<Type = NewDestReg>,
|
|
new_src_reg_width: NewSrcRegWidth::SizeType,
|
|
map_src: &mut impl FnMut(
|
|
Expr<UIntType<Self::SrcRegWidth>>,
|
|
usize,
|
|
) -> Expr<UIntType<NewSrcRegWidth>>,
|
|
) -> Expr<Self::Mapped<NewDestReg, NewSrcRegWidth>> {
|
|
let input = input.to_expr();
|
|
let new_dest = new_dest.to_expr();
|
|
let mapped_ty = Expr::ty(input).mapped_ty(Expr::ty(new_dest), new_src_reg_width);
|
|
#[hdl]
|
|
let mapped_regs = wire(mapped_ty);
|
|
#[hdl]
|
|
match input {
|
|
Self::$FirstVariant(v) => connect(mapped_regs, mapped_ty.$FirstVariant(MOpTrait::map_regs(v, new_dest, new_src_reg_width, map_src))),
|
|
$(Self::$Variant(v) => connect(mapped_regs, mapped_ty.$Variant(MOpTrait::map_regs(v, new_dest, new_src_reg_width, map_src))),)*
|
|
}
|
|
mapped_regs
|
|
}
|
|
}
|
|
};
|
|
(
|
|
@impl_variants
|
|
#[impl_mop_into = true]
|
|
enum $MOp:ident $generics:tt {
|
|
$($Variant:ident($ty:ty),)+
|
|
}
|
|
) => {
|
|
$(mop_enum! {
|
|
@impl_variant
|
|
enum $MOp $generics {
|
|
$Variant($ty),
|
|
}
|
|
})+
|
|
};
|
|
(
|
|
@impl_variants
|
|
#[impl_mop_into = false]
|
|
enum $MOp:ident $generics:tt {
|
|
$($Variant:ident($ty:ty),)+
|
|
}
|
|
) => {};
|
|
(
|
|
@impl_variant
|
|
enum $MOp:ident[$DestReg:ident: Type, $SrcRegWidth:ident: Size $(, #[Size(get_size = $sizes_get_size:expr)] $Sizes:ident: Size)*] {
|
|
$Variant:ident($ty:ty),
|
|
}
|
|
) => {
|
|
impl<$DestReg: Type, $SrcRegWidth: Size, Target: MOpTrait, $($Sizes: Size,)*> MOpInto<Target> for $ty
|
|
where
|
|
$MOp<$DestReg, $SrcRegWidth, $($Sizes,)*>: MOpInto<Target>
|
|
{
|
|
fn mop_into_ty(self) -> Target {
|
|
MOpInto::mop_into_ty($MOp[MOpTrait::dest_reg_ty(self)][MOpTrait::src_reg_width(self)]$([$sizes_get_size(self)])*)
|
|
}
|
|
fn mop_into(this: Expr<Self>) -> Expr<Target> {
|
|
MOpInto::mop_into(MOpInto::<$MOp<$DestReg, $SrcRegWidth, $($Sizes,)*>>::mop_into_ty(Expr::ty(this)).$Variant(this))
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
pub(crate) use mop_enum;
|
|
|
|
common_mop_struct! {
|
|
#[mapped(<NewDestReg, NewSrcRegWidth> AluCommonMOp<NewDestReg, NewSrcRegWidth, SrcCount>)]
|
|
#[hdl(cmp_eq)]
|
|
pub struct AluCommonMOp<DestReg: Type, SrcRegWidth: Size, SrcCount: KnownSize> {
|
|
#[common]
|
|
pub common: CommonMOp<ConstUsize<0>, DestReg, SrcRegWidth, SrcCount>,
|
|
pub output_integer_mode: OutputIntegerMode,
|
|
}
|
|
}
|
|
|
|
common_mop_struct! {
|
|
#[mapped(<NewDestReg, NewSrcRegWidth> AddSubMOp<NewDestReg, NewSrcRegWidth, SrcCount>)]
|
|
#[hdl(cmp_eq)]
|
|
pub struct AddSubMOp<DestReg: Type, SrcRegWidth: Size, SrcCount: KnownSize> {
|
|
#[common]
|
|
pub alu_common: AluCommonMOp<DestReg, SrcRegWidth, SrcCount>,
|
|
pub invert_src0: Bool,
|
|
/// * if this is `true`, use `alu_common.src[1]`'s [`PRegFlagsPowerISA::xer_ca`] as a carry-in/borrow-in
|
|
/// * else, use `alu_common.src[1]` as a normal addend
|
|
pub src1_is_carry_in: Bool,
|
|
pub invert_carry_in: Bool,
|
|
pub add_pc: Bool,
|
|
}
|
|
}
|
|
|
|
impl<DestReg: Type, SrcRegWidth: Size> AddSubMOp<DestReg, SrcRegWidth, ConstUsize<3>> {
|
|
#[hdl]
|
|
pub fn add_sub<Target: MOpTrait>(
|
|
dest: impl ToExpr<Type = DestReg>,
|
|
src: impl ToExpr<Type = Array<UIntType<SrcRegWidth>, 3>>,
|
|
imm: impl ToExpr<Type = SInt<{ COMMON_MOP_3_IMM_WIDTH }>>,
|
|
output_integer_mode: impl ToExpr<Type = OutputIntegerMode>,
|
|
invert_src0: impl ToExpr<Type = Bool>,
|
|
src1_is_carry_in: impl ToExpr<Type = Bool>,
|
|
invert_carry_in: impl ToExpr<Type = Bool>,
|
|
add_pc: impl ToExpr<Type = Bool>,
|
|
) -> Expr<Target>
|
|
where
|
|
Self: MOpInto<Target>,
|
|
{
|
|
MOpInto::mop_into(
|
|
#[hdl]
|
|
AddSubMOp {
|
|
alu_common: #[hdl]
|
|
AluCommonMOp {
|
|
common: CommonMOp::new(0_hdl_u0, dest, src, Expr::as_dyn_int(imm.to_expr())),
|
|
output_integer_mode,
|
|
},
|
|
invert_src0,
|
|
src1_is_carry_in,
|
|
invert_carry_in,
|
|
add_pc,
|
|
},
|
|
)
|
|
}
|
|
}
|
|
|
|
impl<DestReg: Type, SrcRegWidth: Size> AddSubMOp<DestReg, SrcRegWidth, ConstUsize<2>> {
|
|
#[hdl]
|
|
pub fn add_sub_i<Target: MOpTrait>(
|
|
dest: impl ToExpr<Type = DestReg>,
|
|
src: impl ToExpr<Type = Array<UIntType<SrcRegWidth>, 2>>,
|
|
imm: impl ToExpr<Type = SInt<{ COMMON_MOP_2_IMM_WIDTH }>>,
|
|
output_integer_mode: impl ToExpr<Type = OutputIntegerMode>,
|
|
invert_src0: impl ToExpr<Type = Bool>,
|
|
src1_is_carry_in: impl ToExpr<Type = Bool>,
|
|
invert_carry_in: impl ToExpr<Type = Bool>,
|
|
add_pc: impl ToExpr<Type = Bool>,
|
|
) -> Expr<Target>
|
|
where
|
|
Self: MOpInto<Target>,
|
|
{
|
|
MOpInto::mop_into(
|
|
#[hdl]
|
|
AddSubMOp {
|
|
alu_common: #[hdl]
|
|
AluCommonMOp {
|
|
common: CommonMOp::new(0_hdl_u0, dest, src, Expr::as_dyn_int(imm.to_expr())),
|
|
output_integer_mode,
|
|
},
|
|
invert_src0,
|
|
src1_is_carry_in,
|
|
invert_carry_in,
|
|
add_pc,
|
|
},
|
|
)
|
|
}
|
|
}
|
|
|
|
common_mop_struct! {
|
|
#[mapped(<NewDestReg, NewSrcRegWidth> LogicalMOp<NewDestReg, NewSrcRegWidth>)]
|
|
#[hdl(cmp_eq)]
|
|
pub struct LogicalMOp<DestReg: Type, SrcRegWidth: Size> {
|
|
#[common]
|
|
pub alu_common: AluCommonMOp<DestReg, SrcRegWidth, ConstUsize<2>>,
|
|
pub lut: UInt<4>,
|
|
}
|
|
}
|
|
|
|
impl<DestReg: Type, SrcRegWidth: Size> LogicalMOp<DestReg, SrcRegWidth> {
|
|
#[hdl]
|
|
pub fn logical<Target: MOpTrait>(
|
|
dest: impl ToExpr<Type = DestReg>,
|
|
src: impl ToExpr<Type = Array<UIntType<SrcRegWidth>, 2>>,
|
|
imm: impl ToExpr<Type = SInt<{ COMMON_MOP_2_IMM_WIDTH }>>,
|
|
output_integer_mode: impl ToExpr<Type = OutputIntegerMode>,
|
|
lut: impl ToExpr<Type = UInt<4>>,
|
|
) -> Expr<Target>
|
|
where
|
|
Self: MOpInto<Target>,
|
|
{
|
|
MOpInto::mop_into(
|
|
#[hdl]
|
|
LogicalMOp {
|
|
alu_common: #[hdl]
|
|
AluCommonMOp {
|
|
common: CommonMOp::new(0_hdl_u0, dest, src, Expr::as_dyn_int(imm.to_expr())),
|
|
output_integer_mode,
|
|
},
|
|
lut,
|
|
},
|
|
)
|
|
}
|
|
}
|
|
|
|
common_mop_struct! {
|
|
#[mapped(<NewDestReg, NewSrcRegWidth> BranchMOp<NewDestReg, NewSrcRegWidth>)]
|
|
#[hdl(cmp_eq)]
|
|
pub struct BranchMOp<DestReg: Type, SrcRegWidth: Size> {
|
|
#[common]
|
|
pub alu_common: AluCommonMOp<DestReg, SrcRegWidth, ConstUsize<2>>,
|
|
pub lut: UInt<4>,
|
|
}
|
|
}
|
|
|
|
mop_enum! {
|
|
#[impl_mop_into = true]
|
|
#[hdl]
|
|
pub enum AluBranchMOp<DestReg: Type, SrcRegWidth: Size> {
|
|
AddSub(AddSubMOp<DestReg, SrcRegWidth, ConstUsize<3>>),
|
|
AddSubI(AddSubMOp<DestReg, SrcRegWidth, ConstUsize<2>>),
|
|
Logical(LogicalMOp<DestReg, SrcRegWidth>),
|
|
}
|
|
}
|
|
|
|
common_mop_struct! {
|
|
#[mapped(<NewDestReg, NewSrcRegWidth> ReadL2RegMOp<NewDestReg, NewSrcRegWidth>)]
|
|
#[hdl(cmp_eq)]
|
|
pub struct ReadL2RegMOp<DestReg: Type, SrcRegWidth: Size> {
|
|
#[common]
|
|
pub common: CommonMOp<ConstUsize<1>, DestReg, SrcRegWidth, ConstUsize<0>>,
|
|
}
|
|
}
|
|
|
|
common_mop_struct! {
|
|
#[mapped(<NewDestReg, NewSrcRegWidth> WriteL2RegMOp<NewDestReg, NewSrcRegWidth>)]
|
|
#[hdl(cmp_eq)]
|
|
pub struct WriteL2RegMOp<DestReg: Type, SrcRegWidth: Size> {
|
|
#[common]
|
|
pub common: CommonMOp<ConstUsize<1>, DestReg, SrcRegWidth, ConstUsize<1>>,
|
|
}
|
|
}
|
|
|
|
mop_enum! {
|
|
#[impl_mop_into = true]
|
|
#[hdl]
|
|
pub enum L2RegisterFileMOp<DestReg: Type, SrcRegWidth: Size> {
|
|
ReadL2Reg(ReadL2RegMOp<DestReg, SrcRegWidth>),
|
|
WriteL2Reg(WriteL2RegMOp<DestReg, SrcRegWidth>),
|
|
}
|
|
}
|
|
|
|
common_mop_struct! {
|
|
#[mapped(<NewDestReg, NewSrcRegWidth> LoadStoreCommonMOp<NewDestReg, NewSrcRegWidth, SrcCount>)]
|
|
#[hdl(cmp_eq)]
|
|
pub struct LoadStoreCommonMOp<DestReg: Type, SrcRegWidth: Size, SrcCount: KnownSize> {
|
|
#[common]
|
|
pub common: CommonMOp<ConstUsize<1>, DestReg, SrcRegWidth, SrcCount>,
|
|
}
|
|
}
|
|
|
|
common_mop_struct! {
|
|
#[mapped(<NewDestReg, NewSrcRegWidth> LoadMOp<NewDestReg, NewSrcRegWidth>)]
|
|
#[hdl(cmp_eq)]
|
|
pub struct LoadMOp<DestReg: Type, SrcRegWidth: Size> {
|
|
#[common]
|
|
pub load_store_common: LoadStoreCommonMOp<DestReg, SrcRegWidth, ConstUsize<1>>,
|
|
}
|
|
}
|
|
|
|
common_mop_struct! {
|
|
#[mapped(<NewDestReg, NewSrcRegWidth> StoreMOp<NewDestReg, NewSrcRegWidth>)]
|
|
#[hdl(cmp_eq)]
|
|
pub struct StoreMOp<DestReg: Type, SrcRegWidth: Size> {
|
|
#[common]
|
|
pub load_store_common: LoadStoreCommonMOp<DestReg, SrcRegWidth, ConstUsize<2>>,
|
|
}
|
|
}
|
|
|
|
mop_enum! {
|
|
#[impl_mop_into = true]
|
|
#[hdl]
|
|
pub enum LoadStoreMOp<DestReg: Type, SrcRegWidth: Size> {
|
|
Load(CommonMOp<ConstUsize<1>, DestReg, SrcRegWidth, ConstUsize<0>>),
|
|
Store(CommonMOp<ConstUsize<1>, DestReg, SrcRegWidth, ConstUsize<1>>),
|
|
}
|
|
}
|
|
|
|
common_mop_struct! {
|
|
#[mapped(<NewDestReg, NewSrcRegWidth> MoveRegMOp<NewDestReg, NewSrcRegWidth>)]
|
|
#[hdl(cmp_eq)]
|
|
pub struct MoveRegMOp<DestReg: Type, SrcRegWidth: Size> {
|
|
#[common]
|
|
pub common: CommonMOp<ConstUsize<2>, DestReg, SrcRegWidth, ConstUsize<1>>,
|
|
}
|
|
}
|
|
|
|
#[hdl(cmp_eq)]
|
|
/// there may be more than one unit of a given kind, so UnitNum is not the same as UnitKind.
|
|
/// zero is used for built-in constants, such as the zero register
|
|
pub struct UnitNum<Width: Size> {
|
|
pub adj_value: UIntType<Width>,
|
|
}
|
|
|
|
impl<Width: Size> UnitNum<Width> {
|
|
#[hdl]
|
|
pub fn const_zero(self) -> Expr<Self> {
|
|
#[hdl]
|
|
UnitNum {
|
|
adj_value: CONST_ZERO_UNIT_NUM.cast_to(self.adj_value),
|
|
}
|
|
}
|
|
#[hdl]
|
|
pub fn from_index(self, index: usize) -> Expr<Self> {
|
|
#[hdl]
|
|
UnitNum {
|
|
adj_value: (index + 1).cast_to(self.adj_value),
|
|
}
|
|
}
|
|
pub fn is_index(expr: impl ToExpr<Type = Self>, index: usize) -> Expr<Bool> {
|
|
let expr = expr.to_expr();
|
|
Expr::ty(expr)
|
|
.from_index(index)
|
|
.adj_value
|
|
.cmp_eq(expr.adj_value)
|
|
}
|
|
#[hdl]
|
|
pub fn as_index(expr: impl ToExpr<Type = Self>) -> Expr<HdlOption<UIntType<Width>>> {
|
|
let expr = expr.to_expr();
|
|
#[hdl]
|
|
let unit_index = wire(HdlOption[Expr::ty(expr).adj_value]);
|
|
connect(unit_index, Expr::ty(unit_index).HdlNone());
|
|
#[hdl]
|
|
if expr.adj_value.cmp_ne(0u8) {
|
|
connect(
|
|
unit_index,
|
|
HdlSome((expr.adj_value - 1u8).cast_to(Expr::ty(expr).adj_value)),
|
|
);
|
|
}
|
|
unit_index
|
|
}
|
|
}
|
|
|
|
pub const CONST_ZERO_UNIT_NUM: usize = 0;
|
|
|
|
#[hdl(cmp_eq)]
|
|
pub struct UnitOutRegNum<Width: Size> {
|
|
pub value: UIntType<Width>,
|
|
}
|
|
|
|
#[hdl(cmp_eq)]
|
|
/// Physical Register Number -- registers in the CPU's backend
|
|
pub struct PRegNum<UnitNumWidth: Size, OutRegNumWidth: Size> {
|
|
pub unit_num: UnitNum<UnitNumWidth>,
|
|
pub unit_out_reg: UnitOutRegNum<OutRegNumWidth>,
|
|
}
|
|
|
|
impl<UnitNumWidth: Size, OutRegNumWidth: Size> PRegNum<UnitNumWidth, OutRegNumWidth> {
|
|
#[hdl]
|
|
pub fn const_zero(self) -> Expr<Self> {
|
|
#[hdl]
|
|
PRegNum {
|
|
unit_num: self.unit_num.const_zero(),
|
|
unit_out_reg: #[hdl]
|
|
UnitOutRegNum {
|
|
value: 0u8.cast_to(self.unit_out_reg.value),
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
#[hdl(cmp_eq)]
|
|
/// µOp Register Number -- register in a micro-operation before register renaming
|
|
#[doc(alias = "UOpRegNum")] // help you find it in the docs if you mis-spell it
|
|
#[doc(alias = "\u{B5}OpRegNum")] // micro sign
|
|
#[doc(alias = "\u{39C}OpRegNum")] // greek capital letter mu
|
|
#[doc(alias = "\u{3BC}OpRegNum")] // greek small letter mu
|
|
pub struct MOpRegNum {
|
|
pub value: UInt<{ MOpRegNum::WIDTH }>,
|
|
}
|
|
|
|
impl MOpRegNum {
|
|
pub const WIDTH: usize = 8;
|
|
pub const CONST_ZERO_REG_NUM: u32 = 0;
|
|
#[hdl]
|
|
pub fn const_zero() -> Expr<Self> {
|
|
#[hdl]
|
|
MOpRegNum {
|
|
value: Self::CONST_ZERO_REG_NUM.cast_to_static(),
|
|
}
|
|
}
|
|
/// a lot of instructions write to flag registers that we want
|
|
/// to register allocate separately.
|
|
///
|
|
/// e.g. x86 `CF` is not modified by `INC`, but is by `ADD`, so to not slow down `INC`,
|
|
/// we'd need to have `CF` be renamed separately from other flags such as `ZF`.
|
|
///
|
|
/// Note this doesn't mean that each instruction has to write to multiple physical registers,
|
|
/// all registers written by an instruction are renamed to the same physical register.
|
|
//
|
|
// TODO: maybe add more registers later.
|
|
pub const FLAG_REG_NUMS: Range<u32> = 0xFE..0x100;
|
|
/// registers handled by a special small rename table (for flags and stuff, since it has more read/write ports)
|
|
pub const SPECIAL_REG_NUMS: Range<u32> = Self::FLAG_REG_NUMS;
|
|
/// registers handled by the large rename table for normal registers (has less read/write ports)
|
|
pub const NORMAL_REG_NUMS: Range<u32> =
|
|
Self::CONST_ZERO_REG_NUM + 1..Self::SPECIAL_REG_NUMS.start;
|
|
}
|
|
|
|
#[hdl(cmp_eq)]
|
|
/// all the registers this instruction will write to, they are all renamed to the same physical register.
|
|
pub struct MOpDestReg {
|
|
/// some instructions have multiple destination registers, e.g. x86 div
|
|
pub normal_regs: Array<MOpRegNum, { MOpDestReg::NORMAL_REG_COUNT }>,
|
|
/// a lot of instructions also write to flag registers.
|
|
///
|
|
/// when an element with index `index` is `HdlSome(())`,
|
|
/// then the register to write to is [`MOpRegNum::FLAG_REG_NUMS[index]`][MOpRegNum::FLAG_REG_NUMS].
|
|
pub flag_regs: Array<HdlOption<()>, { range_u32_len(&MOpRegNum::FLAG_REG_NUMS) }>,
|
|
}
|
|
|
|
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
|
|
pub enum RenameTableName {
|
|
/// the large rename table for normal registers (has less read/write ports)
|
|
Normal,
|
|
/// a special small rename table (for flags and stuff, since it has more read/write ports)
|
|
Special,
|
|
}
|
|
|
|
impl RenameTableName {
|
|
pub const fn reg_range(self) -> std::ops::Range<u32> {
|
|
match self {
|
|
Self::Normal => MOpRegNum::NORMAL_REG_NUMS,
|
|
Self::Special => MOpRegNum::SPECIAL_REG_NUMS,
|
|
}
|
|
}
|
|
pub const fn as_str(self) -> &'static str {
|
|
match self {
|
|
Self::Normal => "rename_table_normal",
|
|
Self::Special => "rename_table_special",
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
|
pub enum MOpDestRegKind {
|
|
NormalReg {
|
|
/// index in `MOpDestReg::normal_regs`
|
|
dest_reg_index: usize,
|
|
},
|
|
FlagReg {
|
|
/// index in `MOpDestReg::flag_regs`
|
|
flag_reg_index: usize,
|
|
/// value for `MOpRegNum::value`
|
|
reg_num: u32,
|
|
},
|
|
}
|
|
|
|
#[derive(Copy, Clone, Debug)]
|
|
pub struct MOpDestRegName {
|
|
base_name: &'static str,
|
|
index: usize,
|
|
reg_num: Option<u32>,
|
|
}
|
|
|
|
impl fmt::Display for MOpDestRegName {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
let Self {
|
|
base_name,
|
|
index,
|
|
reg_num,
|
|
} = self;
|
|
write!(f, "{base_name}{index}")?;
|
|
if let Some(reg_num) = reg_num {
|
|
write!(f, "_r{reg_num:02X}")?;
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl MOpDestRegKind {
|
|
pub const fn reg_range(self) -> std::ops::Range<u32> {
|
|
match self {
|
|
Self::NormalReg { .. } => MOpRegNum::NORMAL_REG_NUMS,
|
|
Self::FlagReg { .. } => MOpRegNum::FLAG_REG_NUMS,
|
|
}
|
|
}
|
|
pub const fn rename_table_names(self) -> &'static [RenameTableName] {
|
|
match self {
|
|
Self::NormalReg { .. } => &[RenameTableName::Normal, RenameTableName::Special],
|
|
Self::FlagReg { .. } => &[RenameTableName::Special],
|
|
}
|
|
}
|
|
pub fn fixed_reg_num(self) -> Option<u32> {
|
|
match self {
|
|
Self::NormalReg { dest_reg_index: _ } => None,
|
|
Self::FlagReg {
|
|
flag_reg_index: _,
|
|
reg_num,
|
|
} => Some(reg_num),
|
|
}
|
|
}
|
|
pub fn reg_name(self) -> MOpDestRegName {
|
|
match self {
|
|
Self::NormalReg { dest_reg_index } => MOpDestRegName {
|
|
base_name: "dest",
|
|
index: dest_reg_index,
|
|
reg_num: None,
|
|
},
|
|
Self::FlagReg {
|
|
flag_reg_index,
|
|
reg_num,
|
|
} => MOpDestRegName {
|
|
base_name: "flag",
|
|
index: flag_reg_index,
|
|
reg_num: Some(reg_num),
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
impl MOpDestReg {
|
|
pub const NORMAL_REG_COUNT: usize = 2;
|
|
pub const REG_COUNT: usize = Self::NORMAL_REG_COUNT + range_u32_len(&MOpRegNum::FLAG_REG_NUMS);
|
|
pub const REG_KINDS: [MOpDestRegKind; Self::REG_COUNT] = {
|
|
let mut retval = [MOpDestRegKind::NormalReg { dest_reg_index: 0 }; Self::REG_COUNT];
|
|
let mut write_index = 0;
|
|
let mut dest_reg_index = 0;
|
|
while dest_reg_index < Self::NORMAL_REG_COUNT {
|
|
retval[write_index] = MOpDestRegKind::NormalReg { dest_reg_index };
|
|
write_index += 1;
|
|
dest_reg_index += 1;
|
|
}
|
|
let mut flag_reg_index = 0;
|
|
while flag_reg_index < range_u32_len(&MOpRegNum::FLAG_REG_NUMS) {
|
|
retval[write_index] = MOpDestRegKind::FlagReg {
|
|
flag_reg_index,
|
|
reg_num: flag_reg_index as u32 + MOpRegNum::FLAG_REG_NUMS.start,
|
|
};
|
|
write_index += 1;
|
|
flag_reg_index += 1;
|
|
}
|
|
// make sure we didn't miss filling any
|
|
assert!(write_index == Self::REG_COUNT);
|
|
retval
|
|
};
|
|
#[hdl]
|
|
pub fn regs(this: impl ToExpr<Type = Self>) -> [Expr<MOpRegNum>; Self::REG_COUNT] {
|
|
let this = this.to_expr();
|
|
std::array::from_fn(|index| match Self::REG_KINDS[index] {
|
|
MOpDestRegKind::NormalReg { dest_reg_index } => this.normal_regs[dest_reg_index],
|
|
MOpDestRegKind::FlagReg {
|
|
flag_reg_index,
|
|
reg_num,
|
|
} => {
|
|
#[hdl]
|
|
let flag_reg = wire();
|
|
connect(flag_reg, MOpRegNum::const_zero());
|
|
#[hdl]
|
|
if let HdlSome(v) = this.flag_regs[flag_reg_index] {
|
|
let () = *v;
|
|
connect(
|
|
flag_reg,
|
|
#[hdl]
|
|
MOpRegNum {
|
|
value: reg_num.cast_to_static(),
|
|
},
|
|
);
|
|
}
|
|
flag_reg
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
#[hdl]
|
|
pub type MOp = UnitMOp<
|
|
MOpDestReg,
|
|
ConstUsize<{ MOpRegNum::WIDTH }>,
|
|
MoveRegMOp<MOpDestReg, ConstUsize<{ MOpRegNum::WIDTH }>>,
|
|
>;
|
|
|
|
#[hdl]
|
|
pub type RenamedMOp<DestReg: Type, SrcRegWidth: Size> =
|
|
UnitMOp<DestReg, SrcRegWidth, L2RegisterFileMOp<DestReg, SrcRegWidth>>;
|