diff --git a/crates/cpu/src/config.rs b/crates/cpu/src/config.rs index 61907f4..c67390a 100644 --- a/crates/cpu/src/config.rs +++ b/crates/cpu/src/config.rs @@ -1,14 +1,21 @@ -use crate::instruction::{UnitKind, UnitNum}; +use crate::instruction::{PRegNum, UnitKind, UnitNum}; use fayalite::prelude::*; #[derive(Clone, Eq, PartialEq, Hash, Debug)] #[non_exhaustive] pub struct CpuConfig { pub units: Vec, + pub out_reg_num_width: usize, } impl CpuConfig { + pub fn unit_num_width(&self) -> usize { + UInt::range(0..self.units.len()).width() + } pub fn unit_num(&self) -> UnitNum { - UnitNum[UInt::range(0..self.units.len()).width()] + UnitNum[self.unit_num_width()] + } + pub fn p_reg_num(&self) -> PRegNum { + PRegNum[self.unit_num_width()][self.out_reg_num_width] } } diff --git a/crates/cpu/src/instruction.rs b/crates/cpu/src/instruction.rs index 8d3144b..fc48529 100644 --- a/crates/cpu/src/instruction.rs +++ b/crates/cpu/src/instruction.rs @@ -255,11 +255,18 @@ pub struct PRegNum { } #[hdl] -/// µOp Register Number -- register in a micro-operation +/// µ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<8>, + pub value: UInt<{ MOpRegNum::WIDTH }>, } + +impl MOpRegNum { + pub const WIDTH: usize = 8; + pub const CONST_ZERO_REG_NUM: u32 = 0; +} + +pub type MOp = UnitMOp>; diff --git a/crates/cpu/src/instruction/power_isa.rs b/crates/cpu/src/instruction/power_isa.rs index a242303..11973b0 100644 --- a/crates/cpu/src/instruction/power_isa.rs +++ b/crates/cpu/src/instruction/power_isa.rs @@ -1,4 +1,6 @@ +use crate::{instruction::MOpRegNum, util::range_u32_nth_or_panic}; use fayalite::prelude::*; +use std::ops::Range; #[hdl] pub struct PowerIsaRegNum { @@ -20,3 +22,29 @@ pub struct PowerIsaCrBitNum { pub cr_field: PowerIsaCrFieldNum, pub bit_in_field: UInt<2>, } + +impl MOpRegNum { + pub const POWER_ISA_LR_REG_NUM: u32 = 1; + pub const POWER_ISA_CTR_REG_NUM: u32 = 2; + pub const POWER_ISA_TAR_REG_NUM: u32 = 3; + /// XER bits are stored in [`PRegValue.flags`], bits that don't exist in [`PRegValue.flags`] are stored in [`PRegValue.int_fp`] + /// + /// [`PRegValue.flags`]: struct@crate::register::PRegValue + /// [`PRegValue.int_fp`]: struct@crate::register::PRegValue + pub const POWER_ISA_XER_REG_NUM: u32 = 4; + + pub const POWER_ISA_CR_REG_NUMS: Range = 8..16; + pub const fn power_isa_cr_reg_num(index: usize) -> u32 { + range_u32_nth_or_panic(&Self::POWER_ISA_CR_REG_NUMS, index) + } + + pub const POWER_ISA_GPR_REG_NUMS: Range = 32..64; + pub const fn power_isa_gpr_reg_num(index: usize) -> u32 { + range_u32_nth_or_panic(&Self::POWER_ISA_GPR_REG_NUMS, index) + } + + pub const POWER_ISA_FPR_REG_NUMS: Range = 64..96; + pub const fn power_isa_fpr_reg_num(index: usize) -> u32 { + range_u32_nth_or_panic(&Self::POWER_ISA_FPR_REG_NUMS, index) + } +} diff --git a/crates/cpu/src/lib.rs b/crates/cpu/src/lib.rs index eab7c3e..753a700 100644 --- a/crates/cpu/src/lib.rs +++ b/crates/cpu/src/lib.rs @@ -1,2 +1,4 @@ pub mod config; pub mod instruction; +pub mod register; +pub mod util; diff --git a/crates/cpu/src/register.rs b/crates/cpu/src/register.rs new file mode 100644 index 0000000..7dcd449 --- /dev/null +++ b/crates/cpu/src/register.rs @@ -0,0 +1,148 @@ +use fayalite::prelude::*; + +#[hdl] +pub enum FlagsMode { + PowerISA(PRegFlagsPowerISA), + X86(PRegFlagsX86), +} + +#[hdl] +pub struct PRegFlagsPowerISA {} + +impl PRegFlagsPowerISA { + pub fn xer_ca(flags: impl ToExpr) -> Expr { + flags.to_expr().pwr_ca_x86_cf + } + pub fn xer_ca32(flags: impl ToExpr) -> Expr { + flags.to_expr().pwr_ca32_x86_af + } + pub fn xer_ov(flags: impl ToExpr) -> Expr { + flags.to_expr().pwr_ov_x86_of + } + pub fn xer_ov32(flags: impl ToExpr) -> Expr { + flags.to_expr().pwr_ov32_x86_df + } + /// both `CR.SO` and `XER.SO` since instructions that write to both always write the same value + pub fn so(flags: impl ToExpr) -> Expr { + flags.to_expr().pwr_so + } + pub fn cr_lt(flags: impl ToExpr) -> Expr { + flags.to_expr().pwr_cr_lt_x86_sf + } + pub fn cr_gt(flags: impl ToExpr) -> Expr { + flags.to_expr().pwr_cr_gt_x86_pf + } + pub fn cr_eq(flags: impl ToExpr) -> Expr { + flags.to_expr().pwr_cr_eq_x86_zf + } + #[hdl] + pub fn clear_unused(flags: impl ToExpr) { + #[hdl] + match flags { + // list all flags explicitly so we don't miss handling any new flags + PRegFlags { + pwr_ca_x86_cf: _, + pwr_ca32_x86_af: _, + pwr_ov_x86_of: _, + pwr_ov32_x86_df: _, + pwr_cr_lt_x86_sf: _, + pwr_cr_gt_x86_pf: _, + pwr_cr_eq_x86_zf: _, + pwr_so: _, + } => {} + } + } +} + +#[hdl] +pub struct PRegFlagsX86 {} + +impl PRegFlagsX86 { + pub fn cf(flags: impl ToExpr) -> Expr { + flags.to_expr().pwr_ca_x86_cf + } + pub fn zf(flags: impl ToExpr) -> Expr { + flags.to_expr().pwr_cr_eq_x86_zf + } + pub fn sf(flags: impl ToExpr) -> Expr { + flags.to_expr().pwr_cr_lt_x86_sf + } + pub fn of(flags: impl ToExpr) -> Expr { + flags.to_expr().pwr_ov_x86_of + } + pub fn af(flags: impl ToExpr) -> Expr { + flags.to_expr().pwr_ca32_x86_af + } + pub fn pf(flags: impl ToExpr) -> Expr { + flags.to_expr().pwr_cr_gt_x86_pf + } + pub fn df(flags: impl ToExpr) -> Expr { + flags.to_expr().pwr_ov32_x86_df + } + #[hdl] + pub fn clear_unused(flags: impl ToExpr) { + #[hdl] + match flags { + // list all flags explicitly so we don't miss handling any new flags + PRegFlags { + pwr_ca_x86_cf: _, + pwr_ca32_x86_af: _, + pwr_ov_x86_of: _, + pwr_ov32_x86_df: _, + pwr_cr_lt_x86_sf: _, + pwr_cr_gt_x86_pf: _, + pwr_cr_eq_x86_zf: _, + pwr_so: unused1, + } => connect(unused1, false), + } + } +} + +#[hdl] +/// this is *not* the same as any particular ISA's flags register, +/// on PowerISA it is a combination of some bits from XER with a single 4-bit CR field. +/// +/// Accessor functions depend on the ISA: +/// +/// * PowerISA: [`struct@PRegFlagsPowerISA`] +/// * x86: [`struct@PRegFlagsX86`] +pub struct PRegFlags { + pwr_ca_x86_cf: Bool, + pwr_ca32_x86_af: Bool, + pwr_ov_x86_of: Bool, + pwr_ov32_x86_df: Bool, + pwr_cr_lt_x86_sf: Bool, + pwr_cr_gt_x86_pf: Bool, + pwr_cr_eq_x86_zf: Bool, + pwr_so: Bool, +} + +impl PRegFlags { + /// if trying to set all fields individually, prefer using the individual accessor + /// functions and [`PRegFlagsPowerISA::clear_unused()`]/[`PRegFlagsX86::clear_unused()`]/etc. + #[hdl] + pub fn zeroed() -> Expr { + #[hdl] + PRegFlags { + pwr_ca_x86_cf: false, + pwr_ca32_x86_af: false, + pwr_ov_x86_of: false, + pwr_ov32_x86_df: false, + pwr_cr_lt_x86_sf: false, + pwr_cr_gt_x86_pf: false, + pwr_cr_eq_x86_zf: false, + pwr_so: false, + } + } +} + +#[hdl] +/// Unit output register's value -- a combination of an integer/fp register +/// and flags register and CR field. +/// +/// Register Renaming will independently rename the ISA-level integer/fp +/// register, flags, and CR field portions. +pub struct PRegValue { + pub int_fp: UInt<64>, + pub flags: PRegFlags, +} diff --git a/crates/cpu/src/util.rs b/crates/cpu/src/util.rs new file mode 100644 index 0000000..964875f --- /dev/null +++ b/crates/cpu/src/util.rs @@ -0,0 +1,21 @@ +pub(crate) const fn range_u32_len(range: &std::ops::Range) -> usize { + let retval = range.end.saturating_sub(range.start); + assert!(retval as usize as u32 != retval, "len overflowed"); + retval as usize +} + +pub(crate) const fn range_u32_nth(range: &std::ops::Range, index: usize) -> Option { + if index < range_u32_len(range) { + Some(range.start + index as u32) + } else { + None + } +} + +pub(crate) const fn range_u32_nth_or_panic(range: &std::ops::Range, index: usize) -> u32 { + if let Some(retval) = range_u32_nth(range, index) { + retval + } else { + panic!("index out of range") + } +}