working on reg_alloc
This commit is contained in:
parent
88eff5952b
commit
7efcd872b5
|
@ -15,3 +15,6 @@ rust-version = "1.82.0"
|
|||
|
||||
[workspace.dependencies]
|
||||
fayalite = { git = "https://git.libre-chip.org/libre-chip/fayalite.git", version = "0.3.0", branch = "master" }
|
||||
|
||||
[profile.dev]
|
||||
opt-level = 1
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
use crate::{
|
||||
instruction::{PRegNum, UnitNum, CONST_ZERO_UNIT_NUM},
|
||||
unit::UnitKind,
|
||||
instruction::{PRegNum, UnitNum, UnitOutRegNum, CONST_ZERO_UNIT_NUM},
|
||||
unit::{UnitKind, UnitMOp},
|
||||
};
|
||||
use fayalite::prelude::*;
|
||||
use std::num::NonZeroUsize;
|
||||
|
@ -34,12 +34,21 @@ impl CpuConfig {
|
|||
(CONST_ZERO_UNIT_NUM + 1)..(self.unit_kinds.len() + 1)
|
||||
}
|
||||
pub fn unit_num_width(&self) -> usize {
|
||||
UInt::range(self.non_const_unit_nums()).width()
|
||||
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 unit_mop_in_unit(&self) -> UnitMOp<UnitOutRegNum<DynSize>, DynSize> {
|
||||
UnitMOp[self.unit_out_reg_num()][self.p_reg_num_width()]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,108 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
use crate::unit::UnitMOp;
|
||||
use crate::{unit::UnitMOp, util::range_u32_len};
|
||||
use fayalite::{expr::ops::ArrayLiteral, intern::Interned, prelude::*};
|
||||
use std::marker::PhantomData;
|
||||
use std::{marker::PhantomData, ops::Range};
|
||||
|
||||
pub mod power_isa;
|
||||
|
||||
pub trait MOpTrait: Type {
|
||||
type Mapped<NewDestReg: Type, NewSrcRegWidth: Size>: MOpTrait;
|
||||
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 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;
|
||||
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 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,
|
||||
|
@ -25,15 +122,51 @@ 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]
|
||||
pub struct CommonMOp<PrefixPad: KnownSize, RegWidth: Size, SrcCount: KnownSize> {
|
||||
pub struct CommonMOp<PrefixPad: KnownSize, DestReg: Type, SrcRegWidth: Size, SrcCount: KnownSize> {
|
||||
pub prefix_pad: UIntType<PrefixPad>,
|
||||
pub dest: UIntType<RegWidth>,
|
||||
pub src: Array<UIntType<RegWidth>, { COMMON_MOP_SRC_LEN }>,
|
||||
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]
|
||||
pub struct CommonMOpImmParts<ImmInSrcCount: Size> {
|
||||
// fields must be in this exact order
|
||||
|
@ -45,7 +178,7 @@ pub struct CommonMOpImmParts<ImmInSrcCount: Size> {
|
|||
type CommonMOpWithMaxSrcCount = CommonMOpForImm<{ COMMON_MOP_SRC_LEN }>;
|
||||
|
||||
type CommonMOpForImm<const SRC_COUNT: usize> =
|
||||
CommonMOp<ConstUsize<0>, ConstUsize<{ MOP_MIN_REG_WIDTH }>, ConstUsize<SRC_COUNT>>;
|
||||
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;
|
||||
|
@ -53,8 +186,8 @@ 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, RegWidth: Size, SrcCount: KnownSize>
|
||||
CommonMOp<PrefixPad, RegWidth, SrcCount>
|
||||
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");
|
||||
|
@ -62,7 +195,7 @@ impl<PrefixPad: KnownSize, RegWidth: Size, SrcCount: KnownSize>
|
|||
(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: std::ops::Range<usize> =
|
||||
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
|
||||
|
@ -82,8 +215,8 @@ impl<PrefixPad: KnownSize, RegWidth: Size, SrcCount: KnownSize>
|
|||
#[hdl]
|
||||
pub fn new(
|
||||
prefix_pad: impl ToExpr<Type = UIntType<PrefixPad>>,
|
||||
dest: impl ToExpr<Type = UIntType<RegWidth>>,
|
||||
src: impl ToExpr<Type = ArrayType<UIntType<RegWidth>, SrcCount>>,
|
||||
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();
|
||||
|
@ -91,22 +224,21 @@ impl<PrefixPad: KnownSize, RegWidth: Size, SrcCount: KnownSize>
|
|||
let src_in = src.to_expr();
|
||||
let imm = imm.to_expr();
|
||||
assert_eq!(Expr::ty(imm), Self::imm_ty());
|
||||
let reg_ty = Expr::ty(dest);
|
||||
assert_eq!(reg_ty, Expr::ty(src_in).element());
|
||||
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(reg_ty); COMMON_MOP_SRC_LEN];
|
||||
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(reg_ty);
|
||||
src[src_index] = imm_parts.reversed_src[reversed_src_index].cast_to(src_reg_ty);
|
||||
}
|
||||
#[hdl]
|
||||
Self {
|
||||
prefix_pad,
|
||||
dest,
|
||||
src: ArrayLiteral::new(
|
||||
reg_ty,
|
||||
src_reg_ty,
|
||||
Interned::from_iter(src.iter().map(|v| Expr::canonical(*v))),
|
||||
)
|
||||
.to_expr(),
|
||||
|
@ -136,17 +268,16 @@ impl<PrefixPad: KnownSize, RegWidth: Size, SrcCount: KnownSize>
|
|||
#[hdl]
|
||||
pub fn connect_to_imm(expr: impl ToExpr<Type = Self>, imm: impl ToExpr<Type = SInt>) {
|
||||
let expr = expr.to_expr();
|
||||
let reg_ty = Expr::ty(expr).dest;
|
||||
assert_eq!(reg_ty, Expr::ty(expr).src.element());
|
||||
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(reg_ty)); COMMON_MOP_SRC_LEN];
|
||||
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(reg_ty));
|
||||
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] {
|
||||
|
@ -156,82 +287,307 @@ impl<PrefixPad: KnownSize, RegWidth: Size, SrcCount: KnownSize>
|
|||
}
|
||||
}
|
||||
|
||||
#[hdl]
|
||||
pub struct AluCommonMOp<RegWidth: Size, SrcCount: KnownSize> {
|
||||
pub common: CommonMOp<ConstUsize<0>, RegWidth, SrcCount>,
|
||||
pub output_integer_mode: OutputIntegerMode,
|
||||
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,)*
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[hdl]
|
||||
pub struct AddSubMOp<RegWidth: Size, SrcCount: KnownSize> {
|
||||
pub alu_common: AluCommonMOp<RegWidth, SrcCount>,
|
||||
pub invert_src0: Bool,
|
||||
pub invert_carry_in: Bool,
|
||||
pub invert_carry_out: Bool,
|
||||
pub add_pc: Bool,
|
||||
macro_rules! mop_enum {
|
||||
(
|
||||
$(#[$enum_meta:meta])*
|
||||
$vis:vis enum $MOp:ident<$DestReg:ident: Type, $SrcRegWidth:ident: Size> {
|
||||
$(#[$first_variant_meta:meta])*
|
||||
$FirstVariant:ident($first_ty:ty),
|
||||
$(
|
||||
$(#[$variant_meta:meta])*
|
||||
$Variant:ident($ty:ty),
|
||||
)*
|
||||
}
|
||||
) => {
|
||||
$(#[$enum_meta])*
|
||||
$vis enum $MOp<$DestReg: Type, $SrcRegWidth: Size> {
|
||||
$(#[$first_variant_meta])*
|
||||
$FirstVariant($first_ty),
|
||||
$(
|
||||
$(#[$variant_meta])*
|
||||
$Variant($ty),
|
||||
)*
|
||||
}
|
||||
|
||||
impl<$DestReg: Type, $SrcRegWidth: Size> MOpTrait for $MOp<$DestReg, $SrcRegWidth> {
|
||||
type Mapped<NewDestReg: Type, NewSrcRegWidth: Size> = $MOp<NewDestReg, NewSrcRegWidth>;
|
||||
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 {
|
||||
$MOp::<_, _>::$FirstVariant(v) => connect(dest_reg, MOpTrait::dest_reg(v)),
|
||||
$($MOp::<_, _>::$Variant(v) => connect(dest_reg, MOpTrait::dest_reg(v)),)*
|
||||
}
|
||||
dest_reg
|
||||
}
|
||||
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]
|
||||
}
|
||||
#[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 {
|
||||
$MOp::<_, _>::$FirstVariant(v) => connect(mapped_regs, mapped_ty.$FirstVariant(MOpTrait::map_regs(v, new_dest, new_src_reg_width, map_src))),
|
||||
$($MOp::<_, _>::$Variant(v) => connect(mapped_regs, mapped_ty.$Variant(MOpTrait::map_regs(v, new_dest, new_src_reg_width, map_src))),)*
|
||||
}
|
||||
mapped_regs
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[hdl]
|
||||
pub struct LogicalMOp<RegWidth: Size> {
|
||||
pub alu_common: AluCommonMOp<RegWidth, ConstUsize<2>>,
|
||||
pub lut: UInt<4>,
|
||||
pub(crate) use mop_enum;
|
||||
|
||||
common_mop_struct! {
|
||||
#[mapped(<NewDestReg, NewSrcRegWidth> AluCommonMOp<NewDestReg, NewSrcRegWidth, SrcCount>)]
|
||||
#[hdl]
|
||||
pub struct AluCommonMOp<DestReg: Type, SrcRegWidth: Size, SrcCount: KnownSize> {
|
||||
#[common]
|
||||
pub common: CommonMOp<ConstUsize<0>, DestReg, SrcRegWidth, SrcCount>,
|
||||
pub output_integer_mode: OutputIntegerMode,
|
||||
}
|
||||
}
|
||||
|
||||
#[hdl]
|
||||
pub struct BranchMOp<RegWidth: Size> {
|
||||
pub alu_common: AluCommonMOp<RegWidth, ConstUsize<2>>,
|
||||
pub lut: UInt<4>,
|
||||
common_mop_struct! {
|
||||
#[mapped(<NewDestReg, NewSrcRegWidth> AddSubMOp<NewDestReg, NewSrcRegWidth, SrcCount>)]
|
||||
#[hdl]
|
||||
pub struct AddSubMOp<DestReg: Type, SrcRegWidth: Size, SrcCount: KnownSize> {
|
||||
#[common]
|
||||
pub alu_common: AluCommonMOp<DestReg, SrcRegWidth, SrcCount>,
|
||||
pub invert_src0: Bool,
|
||||
pub invert_carry_in: Bool,
|
||||
pub invert_carry_out: Bool,
|
||||
pub add_pc: Bool,
|
||||
}
|
||||
}
|
||||
|
||||
#[hdl]
|
||||
pub enum AluBranchMOp<RegWidth: Size> {
|
||||
AddSub(AddSubMOp<RegWidth, ConstUsize<3>>),
|
||||
AddSubI(AddSubMOp<RegWidth, ConstUsize<2>>),
|
||||
Logical(LogicalMOp<RegWidth>),
|
||||
common_mop_struct! {
|
||||
#[mapped(<NewDestReg, NewSrcRegWidth> LogicalMOp<NewDestReg, NewSrcRegWidth>)]
|
||||
#[hdl]
|
||||
pub struct LogicalMOp<DestReg: Type, SrcRegWidth: Size> {
|
||||
#[common]
|
||||
pub alu_common: AluCommonMOp<DestReg, SrcRegWidth, ConstUsize<2>>,
|
||||
pub lut: UInt<4>,
|
||||
}
|
||||
}
|
||||
|
||||
#[hdl]
|
||||
pub struct ReadL2RegMOp<RegWidth: Size> {
|
||||
pub common: CommonMOp<ConstUsize<1>, RegWidth, ConstUsize<0>>,
|
||||
common_mop_struct! {
|
||||
#[mapped(<NewDestReg, NewSrcRegWidth> BranchMOp<NewDestReg, NewSrcRegWidth>)]
|
||||
#[hdl]
|
||||
pub struct BranchMOp<DestReg: Type, SrcRegWidth: Size> {
|
||||
#[common]
|
||||
pub alu_common: AluCommonMOp<DestReg, SrcRegWidth, ConstUsize<2>>,
|
||||
pub lut: UInt<4>,
|
||||
}
|
||||
}
|
||||
|
||||
#[hdl]
|
||||
pub struct WriteL2RegMOp<RegWidth: Size> {
|
||||
pub common: CommonMOp<ConstUsize<1>, RegWidth, ConstUsize<1>>,
|
||||
mop_enum! {
|
||||
#[hdl]
|
||||
pub enum AluBranchMOp<DestReg: Type, SrcRegWidth: Size> {
|
||||
AddSub(AddSubMOp<DestReg, SrcRegWidth, ConstUsize<3>>),
|
||||
AddSubI(AddSubMOp<DestReg, SrcRegWidth, ConstUsize<2>>),
|
||||
Logical(LogicalMOp<DestReg, SrcRegWidth>),
|
||||
}
|
||||
}
|
||||
|
||||
#[hdl]
|
||||
pub enum L2RegisterFileMOp<RegWidth: Size> {
|
||||
ReadL2Reg(ReadL2RegMOp<RegWidth>),
|
||||
WriteL2Reg(WriteL2RegMOp<RegWidth>),
|
||||
common_mop_struct! {
|
||||
#[mapped(<NewDestReg, NewSrcRegWidth> ReadL2RegMOp<NewDestReg, NewSrcRegWidth>)]
|
||||
#[hdl]
|
||||
pub struct ReadL2RegMOp<DestReg: Type, SrcRegWidth: Size> {
|
||||
#[common]
|
||||
pub common: CommonMOp<ConstUsize<1>, DestReg, SrcRegWidth, ConstUsize<0>>,
|
||||
}
|
||||
}
|
||||
|
||||
#[hdl]
|
||||
pub struct LoadStoreCommonMOp<RegWidth: Size, SrcCount: KnownSize> {
|
||||
pub common: CommonMOp<ConstUsize<1>, RegWidth, SrcCount>,
|
||||
common_mop_struct! {
|
||||
#[mapped(<NewDestReg, NewSrcRegWidth> WriteL2RegMOp<NewDestReg, NewSrcRegWidth>)]
|
||||
#[hdl]
|
||||
pub struct WriteL2RegMOp<DestReg: Type, SrcRegWidth: Size> {
|
||||
#[common]
|
||||
pub common: CommonMOp<ConstUsize<1>, DestReg, SrcRegWidth, ConstUsize<1>>,
|
||||
}
|
||||
}
|
||||
|
||||
#[hdl]
|
||||
pub struct LoadMOp<RegWidth: Size> {
|
||||
pub load_store_common: LoadStoreCommonMOp<RegWidth, ConstUsize<1>>,
|
||||
mop_enum! {
|
||||
#[hdl]
|
||||
pub enum L2RegisterFileMOp<DestReg: Type, SrcRegWidth: Size> {
|
||||
ReadL2Reg(ReadL2RegMOp<DestReg, SrcRegWidth>),
|
||||
WriteL2Reg(WriteL2RegMOp<DestReg, SrcRegWidth>),
|
||||
}
|
||||
}
|
||||
|
||||
#[hdl]
|
||||
pub struct StoreMOp<RegWidth: Size> {
|
||||
pub load_store_common: LoadStoreCommonMOp<RegWidth, ConstUsize<2>>,
|
||||
common_mop_struct! {
|
||||
#[mapped(<NewDestReg, NewSrcRegWidth> LoadStoreCommonMOp<NewDestReg, NewSrcRegWidth, SrcCount>)]
|
||||
#[hdl]
|
||||
pub struct LoadStoreCommonMOp<DestReg: Type, SrcRegWidth: Size, SrcCount: KnownSize> {
|
||||
#[common]
|
||||
pub common: CommonMOp<ConstUsize<1>, DestReg, SrcRegWidth, SrcCount>,
|
||||
}
|
||||
}
|
||||
|
||||
#[hdl]
|
||||
pub enum LoadStoreMOp<RegWidth: Size> {
|
||||
Load(CommonMOp<ConstUsize<1>, RegWidth, ConstUsize<0>>),
|
||||
Store(CommonMOp<ConstUsize<1>, RegWidth, ConstUsize<1>>),
|
||||
common_mop_struct! {
|
||||
#[mapped(<NewDestReg, NewSrcRegWidth> LoadMOp<NewDestReg, NewSrcRegWidth>)]
|
||||
#[hdl]
|
||||
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]
|
||||
pub struct StoreMOp<DestReg: Type, SrcRegWidth: Size> {
|
||||
#[common]
|
||||
pub load_store_common: LoadStoreCommonMOp<DestReg, SrcRegWidth, ConstUsize<2>>,
|
||||
}
|
||||
}
|
||||
|
||||
mop_enum! {
|
||||
#[hdl]
|
||||
pub enum LoadStoreMOp<DestReg: Type, SrcRegWidth: Size> {
|
||||
Load(CommonMOp<ConstUsize<1>, DestReg, SrcRegWidth, ConstUsize<0>>),
|
||||
Store(CommonMOp<ConstUsize<1>, DestReg, SrcRegWidth, ConstUsize<1>>),
|
||||
}
|
||||
}
|
||||
|
||||
#[hdl]
|
||||
/// 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 value: UIntType<Width>,
|
||||
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;
|
||||
|
@ -248,6 +604,20 @@ pub struct PRegNum<UnitNumWidth: Size, OutRegNumWidth: Size> {
|
|||
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]
|
||||
/// µ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
|
||||
|
@ -261,7 +631,110 @@ pub struct MOpRegNum {
|
|||
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]
|
||||
pub type MOp = UnitMOp<ConstUsize<{ MOpRegNum::WIDTH }>>;
|
||||
/// 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, 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,
|
||||
},
|
||||
}
|
||||
|
||||
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 }>>;
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
// See Notices.txt for copyright information
|
||||
use crate::{
|
||||
config::CpuConfig,
|
||||
instruction::MOp,
|
||||
instruction::{
|
||||
MOp, MOpDestReg, MOpRegNum, MOpTrait, PRegNum, UnitOutRegNum, COMMON_MOP_SRC_LEN,
|
||||
},
|
||||
unit::{TrapData, UnitTrait},
|
||||
util::tree_reduce::tree_reduce_with_state,
|
||||
};
|
||||
|
@ -17,7 +19,7 @@ pub mod unit_free_regs_tracker;
|
|||
|
||||
#[hdl]
|
||||
pub struct FetchedDecodedMOp {
|
||||
pub uop: MOp,
|
||||
pub mop: MOp,
|
||||
/// true if pc doesn't have to be related to the previous instruction.
|
||||
/// (enable to stop detecting when the current instruction isn't
|
||||
/// supposed to be run next, e.g. on branch mis-prediction)
|
||||
|
@ -52,11 +54,27 @@ pub fn reg_alloc(config: &CpuConfig) {
|
|||
HdlNone(),
|
||||
);
|
||||
// TODO: finish
|
||||
|
||||
// the large rename table for normal registers (has less read/write ports)
|
||||
#[hdl]
|
||||
let mut rename_table_normal_mem = memory(config.p_reg_num());
|
||||
rename_table_normal_mem.depth(MOpRegNum::NORMAL_REG_NUMS.len());
|
||||
|
||||
// a special small rename table (for flags and stuff, since it has more read/write ports)
|
||||
#[hdl]
|
||||
let mut rename_table_special_mem = memory(config.p_reg_num());
|
||||
rename_table_special_mem.depth(MOpRegNum::SPECIAL_REG_NUMS.len());
|
||||
|
||||
#[hdl]
|
||||
let available_units =
|
||||
wire(Array[Array[Bool][config.unit_kinds.len()]][config.fetch_width.get()]);
|
||||
#[hdl]
|
||||
let selected_unit_nums = wire(Array[HdlOption[config.unit_num()]][config.fetch_width.get()]);
|
||||
let selected_unit_indexes =
|
||||
wire(Array[HdlOption[UInt[config.unit_num_width()]]][config.fetch_width.get()]);
|
||||
#[hdl]
|
||||
let renamed_mops = wire(Array[HdlOption[config.unit_mop_in_unit()]][config.fetch_width.get()]);
|
||||
#[hdl]
|
||||
let renamed_mops_out_reg = wire(Array[HdlOption[config.p_reg_num()]][config.fetch_width.get()]);
|
||||
for fetch_index in 0..config.fetch_width.get() {
|
||||
connect(
|
||||
fetch_decode_interface.decoded_insns[fetch_index].ready,
|
||||
|
@ -66,50 +84,160 @@ pub fn reg_alloc(config: &CpuConfig) {
|
|||
available_units[fetch_index],
|
||||
repeat(false, config.unit_kinds.len()),
|
||||
);
|
||||
connect(
|
||||
renamed_mops[fetch_index],
|
||||
Expr::ty(renamed_mops).element().HdlNone(),
|
||||
);
|
||||
#[hdl]
|
||||
struct RenameTableReadPort<T> {
|
||||
addr: MOpRegNum,
|
||||
#[hdl(flip)]
|
||||
data: HdlOption<T>,
|
||||
}
|
||||
let make_rename_table_read_port =
|
||||
|mem: &mut MemBuilder<_>,
|
||||
reg_range: std::ops::Range<u32>,
|
||||
src_index: usize,
|
||||
table_name: &str| {
|
||||
let read_port = mem.new_read_port();
|
||||
connect(read_port.clk, cd.clk);
|
||||
connect_any(read_port.addr, 0u8);
|
||||
connect(read_port.en, false);
|
||||
let wire = wire_with_loc(
|
||||
&format!("{table_name}_{fetch_index}_src_{src_index}"),
|
||||
SourceLocation::caller(),
|
||||
RenameTableReadPort[config.p_reg_num()],
|
||||
);
|
||||
connect(wire.addr, MOpRegNum::const_zero());
|
||||
connect(wire.data, Expr::ty(wire.data).HdlNone());
|
||||
#[hdl]
|
||||
if wire.addr.value.cmp_ge(reg_range.start) & wire.addr.value.cmp_lt(reg_range.end) {
|
||||
connect_any(read_port.addr, wire.addr.value - reg_range.start);
|
||||
connect(read_port.en, true);
|
||||
connect(wire.data, HdlSome(read_port.data));
|
||||
for prev_fetch_index in 0..fetch_index {
|
||||
#[hdl]
|
||||
if let HdlSome(decoded_insn) =
|
||||
fetch_decode_interface.decoded_insns[prev_fetch_index].data
|
||||
{
|
||||
#[hdl]
|
||||
if let HdlSome(renamed_mop_out_reg) =
|
||||
renamed_mops_out_reg[prev_fetch_index]
|
||||
{
|
||||
let dest_reg = MOpTrait::dest_reg(decoded_insn.mop);
|
||||
for dest_reg in MOpDestReg::regs(dest_reg) {
|
||||
#[hdl]
|
||||
if dest_reg.value.cmp_eq(wire.addr.value) {
|
||||
connect(wire.data, HdlSome(renamed_mop_out_reg));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
wire
|
||||
};
|
||||
let rename_table_normal_read_ports: [_; COMMON_MOP_SRC_LEN] =
|
||||
std::array::from_fn(|src_index| {
|
||||
make_rename_table_read_port(
|
||||
&mut rename_table_normal_mem,
|
||||
MOpRegNum::NORMAL_REG_NUMS,
|
||||
src_index,
|
||||
"rename_table_normal",
|
||||
)
|
||||
});
|
||||
let rename_table_special_read_ports: [_; COMMON_MOP_SRC_LEN] =
|
||||
std::array::from_fn(|src_index| {
|
||||
make_rename_table_read_port(
|
||||
&mut rename_table_special_mem,
|
||||
MOpRegNum::FLAG_REG_NUMS,
|
||||
src_index,
|
||||
"rename_table_special",
|
||||
)
|
||||
});
|
||||
#[hdl]
|
||||
if let HdlSome(decoded_insn) = fetch_decode_interface.decoded_insns[fetch_index].data {
|
||||
connect(
|
||||
available_units[fetch_index],
|
||||
config.available_units_for_kind(MOp::kind(decoded_insn.uop)),
|
||||
config.available_units_for_kind(MOp::kind(decoded_insn.mop)),
|
||||
);
|
||||
#[hdl]
|
||||
if let HdlSome(renamed_mop_out_reg) = renamed_mops_out_reg[fetch_index] {
|
||||
let dest_reg = MOpTrait::dest_reg(decoded_insn.mop);
|
||||
connect(
|
||||
renamed_mops[fetch_index],
|
||||
HdlSome(MOpTrait::map_regs(
|
||||
decoded_insn.mop,
|
||||
renamed_mop_out_reg.unit_out_reg,
|
||||
config.p_reg_num_width(),
|
||||
&mut |src_reg, src_index| {
|
||||
let src_reg = #[hdl]
|
||||
MOpRegNum { value: src_reg };
|
||||
let renamed_src_reg = wire_with_loc(
|
||||
&format!("renamed_src_reg_{fetch_index}_{src_index}"),
|
||||
SourceLocation::caller(),
|
||||
config.p_reg_num(),
|
||||
);
|
||||
connect(rename_table_normal_read_ports[src_index].addr, src_reg);
|
||||
connect(rename_table_special_read_ports[src_index].addr, src_reg);
|
||||
#[hdl]
|
||||
if let HdlSome(v) = rename_table_normal_read_ports[src_index].data {
|
||||
connect(renamed_src_reg, v);
|
||||
} else if let HdlSome(v) =
|
||||
rename_table_special_read_ports[src_index].data
|
||||
{
|
||||
connect(renamed_src_reg, v);
|
||||
} else {
|
||||
connect(renamed_src_reg, config.p_reg_num().const_zero());
|
||||
}
|
||||
renamed_src_reg.cast_to_bits()
|
||||
},
|
||||
)),
|
||||
);
|
||||
// TODO: write dest_reg to rename table
|
||||
// rename_table_mem.new_write_port()
|
||||
}
|
||||
}
|
||||
connect(
|
||||
selected_unit_nums[fetch_index],
|
||||
selected_unit_indexes[fetch_index],
|
||||
tree_reduce_with_state(
|
||||
0..config.unit_kinds.len(),
|
||||
&mut 0usize,
|
||||
|_state, unit_index| {
|
||||
let selected_unit_leaf = wire_with_loc(
|
||||
&format!("selected_unit_leaf_{fetch_index}_{unit_index}"),
|
||||
let selected_unit_index_leaf = wire_with_loc(
|
||||
&format!("selected_unit_index_leaf_{fetch_index}_{unit_index}"),
|
||||
SourceLocation::caller(),
|
||||
HdlOption[config.unit_num()],
|
||||
HdlOption[UInt[config.unit_num_width()]],
|
||||
);
|
||||
connect(selected_unit_leaf, HdlOption[config.unit_num()].HdlNone());
|
||||
let unit_num = wire_with_loc(
|
||||
&format!("unit_num_{fetch_index}_{unit_index}"),
|
||||
connect(
|
||||
selected_unit_index_leaf,
|
||||
Expr::ty(selected_unit_index_leaf).HdlNone(),
|
||||
);
|
||||
let unit_index_wire = wire_with_loc(
|
||||
&format!("unit_index_{fetch_index}_{unit_index}"),
|
||||
SourceLocation::caller(),
|
||||
config.unit_num(),
|
||||
UInt[config.unit_num_width()],
|
||||
);
|
||||
connect_any(unit_num.value, unit_index);
|
||||
connect_any(unit_index_wire, unit_index);
|
||||
#[hdl]
|
||||
if available_units[fetch_index][unit_index] {
|
||||
connect(selected_unit_leaf, HdlSome(unit_num))
|
||||
connect(selected_unit_index_leaf, HdlSome(unit_index_wire))
|
||||
}
|
||||
selected_unit_leaf
|
||||
selected_unit_index_leaf
|
||||
},
|
||||
|state, l, r| {
|
||||
let selected_unit_node = wire_with_loc(
|
||||
&format!("selected_unit_node_{fetch_index}_{state}"),
|
||||
let selected_unit_index_node = wire_with_loc(
|
||||
&format!("selected_unit_index_node_{fetch_index}_{state}"),
|
||||
SourceLocation::caller(),
|
||||
Expr::ty(l),
|
||||
);
|
||||
*state += 1;
|
||||
connect(selected_unit_node, l);
|
||||
connect(selected_unit_index_node, l);
|
||||
#[hdl]
|
||||
if let HdlNone = l {
|
||||
connect(selected_unit_node, r);
|
||||
connect(selected_unit_index_node, r);
|
||||
}
|
||||
selected_unit_node
|
||||
selected_unit_index_node
|
||||
},
|
||||
)
|
||||
.expect("expected at least one unit"),
|
||||
|
@ -120,14 +248,21 @@ pub fn reg_alloc(config: &CpuConfig) {
|
|||
// TODO: handle assigning multiple instructions to a unit at a time
|
||||
for later_fetch_index in fetch_index + 1..config.fetch_width.get() {
|
||||
#[hdl]
|
||||
if let HdlSome(selected_unit_num) = selected_unit_nums[fetch_index] {
|
||||
if let HdlSome(selected_unit_index) = selected_unit_indexes[fetch_index] {
|
||||
connect(
|
||||
available_units[later_fetch_index][selected_unit_num.value],
|
||||
available_units[later_fetch_index][selected_unit_index],
|
||||
false,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
connect(
|
||||
renamed_mops_out_reg,
|
||||
repeat(
|
||||
HdlOption[config.p_reg_num()].HdlNone(),
|
||||
config.fetch_width.get(),
|
||||
),
|
||||
);
|
||||
for (unit_index, &unit_kind) in config.unit_kinds.iter().enumerate() {
|
||||
let dyn_unit = unit_kind.unit(config);
|
||||
let unit = instance_with_loc(
|
||||
|
@ -136,6 +271,7 @@ pub fn reg_alloc(config: &CpuConfig) {
|
|||
SourceLocation::caller(),
|
||||
);
|
||||
connect(dyn_unit.cd(unit), cd);
|
||||
let unit_input = dyn_unit.input(unit);
|
||||
// TODO: handle assigning multiple instructions to a unit at a time
|
||||
let assign_to_unit_at_once = NonZeroUsize::new(1).unwrap();
|
||||
// TODO: handle retiring multiple instructions from a unit at a time
|
||||
|
@ -156,6 +292,7 @@ pub fn reg_alloc(config: &CpuConfig) {
|
|||
HdlOption[UInt[config.out_reg_num_width]].uninit(), // FIXME: just for debugging
|
||||
);
|
||||
connect(unit_free_regs_tracker.alloc_out[0].ready, false);
|
||||
connect(unit_input.data, Expr::ty(unit_input).data.HdlNone());
|
||||
for fetch_index in 0..config.fetch_width.get() {
|
||||
#[hdl]
|
||||
if let HdlNone = unit_free_regs_tracker.alloc_out[0].data {
|
||||
|
@ -163,10 +300,45 @@ pub fn reg_alloc(config: &CpuConfig) {
|
|||
connect(available_units[fetch_index][unit_index], false);
|
||||
}
|
||||
#[hdl]
|
||||
if let HdlSome(unit_num) = selected_unit_nums[fetch_index] {
|
||||
if !unit_input.ready {
|
||||
// must come after to override connects in loops above
|
||||
connect(available_units[fetch_index][unit_index], false);
|
||||
}
|
||||
#[hdl]
|
||||
if let HdlSome(selected_unit_index) = selected_unit_indexes[fetch_index] {
|
||||
#[hdl]
|
||||
if unit_num.value.cmp_eq(unit_index) {
|
||||
if selected_unit_index.cmp_eq(unit_index) {
|
||||
connect(unit_free_regs_tracker.alloc_out[0].ready, true);
|
||||
#[hdl]
|
||||
if let HdlSome(renamed_mop) =
|
||||
HdlOption::and_then(renamed_mops[fetch_index], |v| dyn_unit.extract_mop(v))
|
||||
{
|
||||
connect(unit_input.data, HdlSome(renamed_mop));
|
||||
} else {
|
||||
connect(
|
||||
unit_input.data,
|
||||
HdlSome(Expr::ty(unit_input).data.HdlSome.uninit()),
|
||||
);
|
||||
// FIXME: add hdl_assert(cd.clk, false.to_expr(), "");
|
||||
}
|
||||
#[hdl]
|
||||
if let HdlSome(unit_out_reg) = unit_free_regs_tracker.alloc_out[0].data {
|
||||
let unit_num = config.unit_num().from_index(unit_index);
|
||||
let unit_out_reg = #[hdl]
|
||||
UnitOutRegNum {
|
||||
value: unit_out_reg,
|
||||
};
|
||||
connect(
|
||||
renamed_mops_out_reg[fetch_index],
|
||||
HdlSome(
|
||||
#[hdl]
|
||||
PRegNum {
|
||||
unit_num,
|
||||
unit_out_reg,
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,9 @@
|
|||
|
||||
use crate::{
|
||||
config::CpuConfig,
|
||||
instruction::{AluBranchMOp, L2RegisterFileMOp, LoadStoreMOp, PRegNum},
|
||||
instruction::{
|
||||
mop_enum, AluBranchMOp, L2RegisterFileMOp, LoadStoreMOp, MOpTrait, PRegNum, UnitOutRegNum,
|
||||
},
|
||||
register::PRegValue,
|
||||
};
|
||||
use fayalite::{
|
||||
|
@ -21,9 +23,10 @@ macro_rules! all_units {
|
|||
#[unit_kind = $UnitKind:ident]
|
||||
#[hdl]
|
||||
$(#[$enum_meta:meta])*
|
||||
$vis:vis enum $UnitMOpEnum:ident<$RegWidth:ident: Size> {
|
||||
$vis:vis enum $UnitMOpEnum:ident<$DestReg:ident: Type, $SrcRegWidth:ident: Size> {
|
||||
$(
|
||||
#[create_dyn_unit_fn = $create_dyn_unit_fn:expr]
|
||||
#[extract = $extract:ident]
|
||||
$(#[$variant_meta:meta])*
|
||||
$Unit:ident($Op:ty),
|
||||
)*
|
||||
|
@ -65,26 +68,43 @@ macro_rules! all_units {
|
|||
)*
|
||||
}
|
||||
|
||||
#[hdl]
|
||||
$(#[$enum_meta])*
|
||||
$vis enum $UnitMOpEnum<$RegWidth: Size> {
|
||||
$(
|
||||
$(#[$variant_meta])*
|
||||
$Unit($Op),
|
||||
)*
|
||||
mop_enum! {
|
||||
#[hdl]
|
||||
$(#[$enum_meta])*
|
||||
$vis enum $UnitMOpEnum<$DestReg: Type, $SrcRegWidth: Size> {
|
||||
$(
|
||||
$(#[$variant_meta])*
|
||||
$Unit($Op),
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
impl<$RegWidth: Size> $UnitMOpEnum<$RegWidth> {
|
||||
impl<$DestReg: Type, $SrcRegWidth: Size> $UnitMOpEnum<$DestReg, $SrcRegWidth> {
|
||||
#[hdl]
|
||||
$vis fn kind(expr: impl ToExpr<Type = Self>) -> Expr<$HdlUnitKind> {
|
||||
#[hdl]
|
||||
let unit_kind = wire();
|
||||
#[hdl]
|
||||
match expr {
|
||||
$($UnitMOpEnum::<$RegWidth>::$Unit(_) => connect(unit_kind, $HdlUnitKind.$Unit()),)*
|
||||
$($UnitMOpEnum::<_, _>::$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 $UnitMOpEnum::<_, _>::$Unit(v) = expr {
|
||||
connect($extract, HdlSome(v));
|
||||
}
|
||||
$extract
|
||||
}
|
||||
)*
|
||||
}
|
||||
|
||||
impl CpuConfig {
|
||||
|
@ -108,13 +128,16 @@ all_units! {
|
|||
#[hdl_unit_kind = HdlUnitKind]
|
||||
#[unit_kind = UnitKind]
|
||||
#[hdl]
|
||||
pub enum UnitMOp<RegWidth: Size> {
|
||||
pub enum UnitMOp<DestReg: Type, SrcRegWidth: Size> {
|
||||
#[create_dyn_unit_fn = |config| alu_branch::AluBranch::new(config).to_dyn()]
|
||||
AluBranch(AluBranchMOp<RegWidth>),
|
||||
#[extract = alu_branch_mop]
|
||||
AluBranch(AluBranchMOp<DestReg, SrcRegWidth>),
|
||||
#[create_dyn_unit_fn = |config| todo!()]
|
||||
L2RegisterFile(L2RegisterFileMOp<RegWidth>),
|
||||
#[extract = l2_register_file_mop]
|
||||
L2RegisterFile(L2RegisterFileMOp<DestReg, SrcRegWidth>),
|
||||
#[create_dyn_unit_fn = |config| todo!()]
|
||||
LoadStore(LoadStoreMOp<RegWidth>),
|
||||
#[extract = load_store_mop]
|
||||
LoadStore(LoadStoreMOp<DestReg, SrcRegWidth>),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -159,9 +182,15 @@ pub trait UnitTrait:
|
|||
|
||||
fn unit_kind(&self) -> UnitKind;
|
||||
|
||||
fn extract_mop(
|
||||
&self,
|
||||
mop: Expr<UnitMOp<UnitOutRegNum<DynSize>, DynSize>>,
|
||||
) -> Expr<HdlOption<Self::MOp>>;
|
||||
|
||||
fn make_module(&self) -> Interned<Module<Self::Type>>;
|
||||
|
||||
// TODO: add other inputs
|
||||
fn input(&self, this: Expr<Self::Type>) -> Expr<ReadyValid<Self::MOp>>;
|
||||
|
||||
fn cancel_input(
|
||||
&self,
|
||||
this: Expr<Self::Type>,
|
||||
|
@ -214,10 +243,21 @@ impl UnitTrait for DynUnit {
|
|||
self.unit_kind
|
||||
}
|
||||
|
||||
fn extract_mop(
|
||||
&self,
|
||||
mop: Expr<UnitMOp<UnitOutRegNum<DynSize>, DynSize>>,
|
||||
) -> Expr<HdlOption<Self::MOp>> {
|
||||
self.unit.extract_mop(mop)
|
||||
}
|
||||
|
||||
fn make_module(&self) -> Interned<Module<Self::Type>> {
|
||||
self.unit.make_module()
|
||||
}
|
||||
|
||||
fn input(&self, this: Expr<Self::Type>) -> Expr<ReadyValid<Self::MOp>> {
|
||||
self.unit.input(this)
|
||||
}
|
||||
|
||||
fn cancel_input(
|
||||
&self,
|
||||
this: Expr<Self::Type>,
|
||||
|
@ -265,10 +305,21 @@ impl<T: UnitTrait + Clone + std::hash::Hash + Eq> UnitTrait for DynUnitWrapper<T
|
|||
self.0.unit_kind()
|
||||
}
|
||||
|
||||
fn extract_mop(
|
||||
&self,
|
||||
mop: Expr<UnitMOp<UnitOutRegNum<DynSize>, DynSize>>,
|
||||
) -> Expr<HdlOption<Self::MOp>> {
|
||||
Expr::from_enum(Expr::as_enum(self.0.extract_mop(mop)))
|
||||
}
|
||||
|
||||
fn make_module(&self) -> Interned<Module<Self::Type>> {
|
||||
self.0.make_module().canonical().intern_sized()
|
||||
}
|
||||
|
||||
fn input(&self, this: Expr<Self::Type>) -> Expr<ReadyValid<Self::MOp>> {
|
||||
Expr::from_bundle(Expr::as_bundle(self.0.input(Expr::from_bundle(this))))
|
||||
}
|
||||
|
||||
fn cancel_input(
|
||||
&self,
|
||||
this: Expr<Self::Type>,
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
|
||||
use crate::{
|
||||
config::CpuConfig,
|
||||
instruction::AluBranchMOp,
|
||||
unit::{DynUnit, DynUnitWrapper, UnitCancelInput, UnitKind, UnitOutput, UnitTrait},
|
||||
instruction::{AluBranchMOp, UnitOutRegNum},
|
||||
unit::{DynUnit, DynUnitWrapper, UnitCancelInput, UnitKind, UnitMOp, UnitOutput, UnitTrait},
|
||||
};
|
||||
use fayalite::{
|
||||
intern::{Intern, Interned},
|
||||
|
@ -16,7 +16,11 @@ use fayalite::{
|
|||
pub fn alu_branch(config: &CpuConfig) {
|
||||
#[hdl]
|
||||
let cd: ClockDomain = m.input();
|
||||
#[hdl]
|
||||
let input: ReadyValid<AluBranchMOp<UnitOutRegNum<DynSize>, DynSize>> =
|
||||
m.input(ReadyValid[AluBranchMOp[config.unit_out_reg_num()][config.p_reg_num_width()]]);
|
||||
// TODO: finish
|
||||
connect(input.ready, true);
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
|
@ -37,7 +41,7 @@ impl AluBranch {
|
|||
impl UnitTrait for AluBranch {
|
||||
type Type = alu_branch;
|
||||
type ExtraOut = ();
|
||||
type MOp = AluBranchMOp<DynSize>;
|
||||
type MOp = AluBranchMOp<UnitOutRegNum<DynSize>, DynSize>;
|
||||
|
||||
fn ty(&self) -> Self::Type {
|
||||
self.module.io_ty()
|
||||
|
@ -48,17 +52,28 @@ impl UnitTrait for AluBranch {
|
|||
}
|
||||
|
||||
fn mop_ty(&self) -> Self::MOp {
|
||||
AluBranchMOp[self.config.p_reg_num().canonical().bit_width()]
|
||||
self.module.io_ty().input.data.HdlSome
|
||||
}
|
||||
|
||||
fn unit_kind(&self) -> UnitKind {
|
||||
UnitKind::AluBranch
|
||||
}
|
||||
|
||||
fn extract_mop(
|
||||
&self,
|
||||
mop: Expr<UnitMOp<UnitOutRegNum<DynSize>, DynSize>>,
|
||||
) -> Expr<HdlOption<Self::MOp>> {
|
||||
UnitMOp::alu_branch_mop(mop)
|
||||
}
|
||||
|
||||
fn make_module(&self) -> Interned<Module<Self::Type>> {
|
||||
self.module
|
||||
}
|
||||
|
||||
fn input(&self, this: Expr<Self::Type>) -> Expr<ReadyValid<Self::MOp>> {
|
||||
this.input
|
||||
}
|
||||
|
||||
fn cancel_input(
|
||||
&self,
|
||||
this: Expr<Self::Type>,
|
||||
|
|
|
@ -5,7 +5,7 @@ pub mod tree_reduce;
|
|||
|
||||
pub(crate) const fn range_u32_len(range: &std::ops::Range<u32>) -> usize {
|
||||
let retval = range.end.saturating_sub(range.start);
|
||||
assert!(retval as usize as u32 != retval, "len overflowed");
|
||||
assert!(retval as usize as u32 == retval, "len overflowed");
|
||||
retval as usize
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue