diff --git a/crates/cpu/src/register.rs b/crates/cpu/src/register.rs index 43eb52b..56b7e45 100644 --- a/crates/cpu/src/register.rs +++ b/crates/cpu/src/register.rs @@ -1,6 +1,8 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information -use fayalite::prelude::*; + +use fayalite::{expr::CastToImpl, int::BoolOrIntType, prelude::*}; +use std::fmt; #[hdl] pub enum FlagsMode { @@ -8,92 +10,427 @@ pub enum FlagsMode { X86(PRegFlagsX86), } -#[hdl(cmp_eq)] -pub struct PRegFlagsPowerISA {} +trait PRegFlagsViewTraitSealed { + type UnusedInner: AsRef<[T]> + + AsMut<[T]> + + IntoIterator< + Item = T, + IntoIter: DoubleEndedIterator + + ExactSizeIterator + + std::iter::FusedIterator + + Default, + >; + const UNUSED_INNER_LEN: usize; + fn unused_inner_map( + v: Self::UnusedInner, + f: impl FnMut(T) -> R, + ) -> Self::UnusedInner; + fn unused_inner_from_fn(f: impl FnMut(usize) -> T) -> Self::UnusedInner; + fn unused_inner_each_ref(v: &Self::UnusedInner) -> Self::UnusedInner<&T>; + fn unused_inner_each_mut(v: &mut Self::UnusedInner) -> Self::UnusedInner<&mut T>; +} -impl PRegFlagsPowerISA { - pub fn xer_ca(flags: impl ToExpr) -> Expr { - flags.to_expr().pwr_ca_x86_cf +#[expect(private_bounds)] +pub trait PRegFlagsViewTrait: Type + PRegFlagsViewTraitSealed { + type View; + fn view(flags: impl ToExpr>) -> Self::View>; + fn view_sim(flags: impl ToSimValue>) -> Self::View>; + fn view_sim_ref(flags: &SimValue>) -> Self::View<&SimValue>; + fn view_sim_mut(flags: &mut SimValue>) -> Self::View<&mut SimValue>; + fn from_view(view: Self::View) -> Expr>; + fn from_view_sim(view: Self::View) -> SimValue>; +} + +pub struct ViewUnused(V::UnusedInner); + +pub struct ViewUnusedIntoIter( + as IntoIterator>::IntoIter, +); + +impl Iterator for ViewUnusedIntoIter { + type Item = T; + + fn next(&mut self) -> Option { + self.0.next() } - pub fn xer_ca32(flags: impl ToExpr) -> Expr { - flags.to_expr().pwr_ca32_x86_af + + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() } - pub fn xer_ov(flags: impl ToExpr) -> Expr { - flags.to_expr().pwr_ov_x86_of + + fn fold(self, init: B, f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + self.0.fold(init, f) } - pub fn xer_ov32(flags: impl ToExpr) -> Expr { - flags.to_expr().pwr_ov32_x86_df + + fn count(self) -> usize { + self.0.count() } - /// 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) { - // list all flags explicitly so we don't miss handling any new flags - #[hdl] - let 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: _, - } = flags; + + fn last(self) -> Option { + self.0.last() } } -#[hdl(cmp_eq)] -pub struct PRegFlagsX86 {} +impl DoubleEndedIterator for ViewUnusedIntoIter { + fn next_back(&mut self) -> Option { + self.0.next_back() + } -impl PRegFlagsX86 { - pub fn cf(flags: impl ToExpr) -> Expr { - flags.to_expr().pwr_ca_x86_cf + fn rfold(self, init: B, f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + self.0.rfold(init, f) } - pub fn zf(flags: impl ToExpr) -> Expr { - flags.to_expr().pwr_cr_eq_x86_zf +} + +impl ExactSizeIterator for ViewUnusedIntoIter { + fn len(&self) -> usize { + self.0.len() } - pub fn sf(flags: impl ToExpr) -> Expr { - flags.to_expr().pwr_cr_lt_x86_sf +} + +impl std::iter::FusedIterator for ViewUnusedIntoIter {} + +impl Default for ViewUnusedIntoIter { + fn default() -> Self { + Self(Default::default()) } - pub fn of(flags: impl ToExpr) -> Expr { - flags.to_expr().pwr_ov_x86_of +} + +impl Clone for ViewUnusedIntoIter +where + as IntoIterator>::IntoIter: Clone, +{ + fn clone(&self) -> Self { + Self(self.0.clone()) } - pub fn af(flags: impl ToExpr) -> Expr { - flags.to_expr().pwr_ca32_x86_af +} + +impl IntoIterator for ViewUnused { + type Item = T; + type IntoIter = ViewUnusedIntoIter; + + fn into_iter(self) -> Self::IntoIter { + ViewUnusedIntoIter(self.0.into_iter()) } - pub fn pf(flags: impl ToExpr) -> Expr { - flags.to_expr().pwr_cr_gt_x86_pf +} + +impl Default for ViewUnused { + fn default() -> Self { + Self::from_fn(|_| T::default()) } - pub fn df(flags: impl ToExpr) -> Expr { - flags.to_expr().pwr_ov32_x86_df +} + +impl ViewUnused { + pub fn iter(&self) -> std::slice::Iter<'_, T> { + self.into_iter() } - #[hdl] - pub fn clear_unused(flags: impl ToExpr) { - // list all flags explicitly so we don't miss handling any new flags - #[hdl] - let 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, - } = flags; - connect(unused1, false); + pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, T> { + self.into_iter() + } + pub fn from_fn(f: impl FnMut(usize) -> T) -> Self { + ViewUnused(V::unused_inner_from_fn(f)) + } + pub fn each_ref(&self) -> ViewUnused<&T, V> { + ViewUnused(V::unused_inner_each_ref(&self.0)) + } + pub fn each_mut(&mut self) -> ViewUnused<&mut T, V> { + ViewUnused(V::unused_inner_each_mut(&mut self.0)) + } + pub fn map(self, f: impl FnMut(T) -> R) -> ViewUnused { + ViewUnused(V::unused_inner_map(self.0, f)) + } + pub fn splat(v: T) -> Self + where + T: Clone, + { + let mut v = Some(v); + Self::from_fn(|i| { + let v = if i == V::UNUSED_INNER_LEN - 1 { + v.take() + } else { + v.clone() + }; + let Some(v) = v else { + unreachable!(); + }; + v + }) + } +} + +impl ViewUnused, V> { + pub fn clear(&mut self) { + for i in self.iter_mut() { + SimValue::bits_mut(i).bits_mut().fill(false); + } + } +} + +impl ViewUnused<&'_ mut SimValue, V> { + pub fn clear(&mut self) { + for i in self.iter_mut() { + SimValue::bits_mut(i).bits_mut().fill(false); + } + } +} + +impl ViewUnused, V> +where + UInt: CastToImpl, +{ + pub fn clear(self) { + for i in self { + connect(i, UInt::new_dyn(i.ty().width()).zero().cast_to(i.ty())); + } + } +} + +impl<'a, T, V: PRegFlagsViewTrait> IntoIterator for &'a ViewUnused { + type Item = &'a T; + type IntoIter = std::slice::Iter<'a, T>; + + fn into_iter(self) -> Self::IntoIter { + self.0.as_ref().iter() + } +} + +impl<'a, T, V: PRegFlagsViewTrait> IntoIterator for &'a mut ViewUnused { + type Item = &'a mut T; + type IntoIter = std::slice::IterMut<'a, T>; + + fn into_iter(self) -> Self::IntoIter { + self.0.as_mut().iter_mut() + } +} + +impl Clone for ViewUnused +where + V::UnusedInner: Clone, +{ + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} + +impl Copy for ViewUnused where V::UnusedInner: Copy {} + +impl fmt::Debug for ViewUnused +where + V::UnusedInner: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("ViewUnused").field(&self.0).finish() + } +} + +macro_rules! impl_view_trait { + ( + $(#[$flags_mode_meta:meta])* + $flags_mode_vis:vis struct $FlagsMode:ident {} + + $(#[$view_meta:meta])* + $view_vis:vis struct $View:ident { + $(#[$unused_field_meta:meta])* + $unused_vis:vis $unused:ident: ViewUnused([$($unused_field:ident),* $(,)?]), + $($(#[$view_field_meta:meta])* + $view_field_vis:vis $view_field:ident: $flags_field:ident,)* + } + ) => { + $(#[$flags_mode_meta])* + $flags_mode_vis struct $FlagsMode {} + + $(#[$view_meta])* + $view_vis struct $View { + $(#[$unused_field_meta])* + $unused_vis $unused: ViewUnused, + $($(#[$view_field_meta])* + $view_field_vis $view_field: T,)* + } + + impl $View<&'_ mut T> { + $view_vis const fn reborrow<'a>(&'a mut self) -> $View<&'a mut T> { + let $View { + $unused: ViewUnused([$($unused_field,)*]), + $($view_field: $flags_field,)* + } = self; + $View { + $unused: ViewUnused([$(&mut **$unused_field,)*]), + $($view_field: &mut **$flags_field,)* + } + } + } + + impl PRegFlagsViewTraitSealed for $FlagsMode { + type UnusedInner = [T; Self::UNUSED_INNER_LEN]; + const UNUSED_INNER_LEN: usize = { + let v: &[&str] = &[$(stringify!($unused_field),)*]; + v.len() + }; + fn unused_inner_map( + v: Self::UnusedInner, + f: impl FnMut(T) -> R, + ) -> Self::UnusedInner { + v.map(f) + } + fn unused_inner_from_fn(f: impl FnMut(usize) -> T) -> Self::UnusedInner { + std::array::from_fn(f) + } + fn unused_inner_each_ref( + v: &Self::UnusedInner, + ) -> Self::UnusedInner<&T> { + v.each_ref() + } + fn unused_inner_each_mut( + v: &mut Self::UnusedInner, + ) -> Self::UnusedInner<&mut T> { + v.each_mut() + } + } + + impl PRegFlagsViewTrait for $FlagsMode { + type View = $View; + + #[hdl] + fn view(flags: impl ToExpr>) -> Self::View> { + #[hdl] + let PRegFlags:: { + $($unused_field,)* + $($flags_field,)* + } = flags.to_expr(); + $View { + $unused: ViewUnused([$($unused_field,)*]), + $($view_field: $flags_field,)* + } + } + + #[hdl] + fn view_sim(flags: impl ToSimValue>) -> Self::View> { + #[hdl(sim)] + let PRegFlags:: { + $($unused_field,)* + $($flags_field,)* + } = flags.into_sim_value(); + $View { + $unused: ViewUnused([$($unused_field,)*]), + $($view_field: $flags_field,)* + } + } + + #[hdl] + fn view_sim_ref(flags: &SimValue>) -> Self::View<&SimValue> { + #[hdl(sim)] + let PRegFlags:: { + $($unused_field,)* + $($flags_field,)* + } = flags; + $View { + $unused: ViewUnused([$($unused_field,)*]), + $($view_field: $flags_field,)* + } + } + + #[hdl] + fn view_sim_mut(flags: &mut SimValue>) -> Self::View<&mut SimValue> { + #[hdl(sim)] + let PRegFlags:: { + $($unused_field,)* + $($flags_field,)* + } = flags; + $View { + $unused: ViewUnused([$($unused_field,)*]), + $($view_field: $flags_field,)* + } + } + + #[hdl] + fn from_view(view: Self::View) -> Expr> { + let $View { + $unused: ViewUnused([$($unused_field,)*]), + $($view_field: $flags_field,)* + } = view; + #[hdl] + PRegFlags::<_> { + $($unused_field,)* + $($flags_field,)* + } + } + + #[hdl] + fn from_view_sim(view: Self::View) -> SimValue> { + let $View { + $unused: ViewUnused([$($unused_field,)*]), + $($view_field: $flags_field,)* + } = view; + #[hdl(sim)] + PRegFlags::<_> { + $($unused_field,)* + $($flags_field,)* + } + } + } + }; +} + +impl_view_trait! { + #[hdl(cmp_eq)] + pub struct PRegFlagsPowerISA {} + + #[derive(Copy, Clone, Debug)] + #[non_exhaustive] + pub struct PRegFlagsPowerISAView { + pub unused: ViewUnused([]), + pub xer_ca: pwr_ca_x86_cf, + pub xer_ca32: pwr_ca32_x86_af, + pub xer_ov: pwr_ov_x86_of, + pub xer_ov32: pwr_ov32_x86_df, + /// both `CR.SO` and `XER.SO` since instructions that write to both always write the same value + pub so: pwr_so, + pub cr_lt: pwr_cr_lt_x86_sf, + pub cr_gt: pwr_cr_gt_x86_pf, + pub cr_eq: pwr_cr_eq_x86_zf, + } +} + +impl_view_trait! { + #[hdl(cmp_eq)] + pub struct PRegFlagsX86 {} + + #[derive(Copy, Clone, Debug)] + #[non_exhaustive] + pub struct PRegFlagsX86View { + pub unused: ViewUnused([pwr_so]), + pub cf: pwr_ca_x86_cf, + pub zf: pwr_cr_eq_x86_zf, + pub sf: pwr_cr_lt_x86_sf, + pub of: pwr_ov_x86_of, + pub af: pwr_ca32_x86_af, + pub pf: pwr_cr_gt_x86_pf, + pub df: pwr_ov32_x86_df, + } +} + +impl_view_trait! { + #[hdl(cmp_eq)] + pub struct PRegFlagsAllUnused {} + + #[derive(Copy, Clone, Debug)] + #[non_exhaustive] + pub struct PRegFlagsAllUnusedView { + pub unused: ViewUnused([ + 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, + ]), } } @@ -105,33 +442,90 @@ impl PRegFlagsX86 { /// /// * 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, +pub struct PRegFlags { + pwr_ca_x86_cf: T, + pwr_ca32_x86_af: T, + pwr_ov_x86_of: T, + pwr_ov32_x86_df: T, + pwr_cr_lt_x86_sf: T, + pwr_cr_gt_x86_pf: T, + pwr_cr_eq_x86_zf: T, + pwr_so: T, +} + +impl PRegFlags { + pub fn view(flags: impl ToExpr) -> V::View> { + V::view(flags) + } + pub fn view_sim( + flags: impl ToSimValue, + ) -> V::View> { + V::view_sim(flags) + } + pub fn view_sim_ref(flags: &SimValue) -> V::View<&SimValue> { + V::view_sim_ref(flags) + } + pub fn view_sim_mut( + flags: &mut SimValue, + ) -> V::View<&mut SimValue> { + V::view_sim_mut(flags) + } + pub fn from_view(view: V::View>) -> Expr { + V::from_view(view) + } + pub fn from_view_sim( + view: V::View>, + ) -> SimValue { + V::from_view_sim(view) + } + pub fn fields(flags: impl ToExpr) -> ViewUnused, PRegFlagsAllUnused> { + Self::view::(flags).unused + } + pub fn fields_sim( + flags: impl ToSimValue, + ) -> ViewUnused, PRegFlagsAllUnused> { + Self::view_sim::(flags).unused + } + pub fn fields_sim_ref(flags: &SimValue) -> ViewUnused<&SimValue, PRegFlagsAllUnused> { + Self::view_sim_ref::(flags).unused + } + pub fn fields_sim_mut( + flags: &mut SimValue, + ) -> ViewUnused<&mut SimValue, PRegFlagsAllUnused> { + Self::view_sim_mut::(flags).unused + } + pub fn from_fields( + fields: ViewUnused, PRegFlagsAllUnused>, + ) -> Expr { + Self::from_view::(PRegFlagsAllUnusedView { unused: fields }) + } + pub fn from_fields_sim( + fields: ViewUnused, PRegFlagsAllUnused>, + ) -> SimValue { + Self::from_view_sim::(PRegFlagsAllUnusedView { unused: fields }) + } + /// if trying to set all fields individually, prefer using the individual accessor + /// functions and [`PRegFlagsPowerISA::clear_unused()`]/[`PRegFlagsX86::clear_unused()`]/etc. + pub fn splat(v: impl ToExpr) -> Expr { + Self::from_fields(ViewUnused::splat(v.to_expr())) + } + /// if trying to set all fields individually, prefer using the individual accessor + /// functions and [`PRegFlagsPowerISA::clear_unused()`]/[`PRegFlagsX86::clear_unused()`]/etc. + pub fn splat_sim(v: impl ToSimValue) -> SimValue { + Self::from_fields_sim(ViewUnused::splat(v.into_sim_value())) + } } 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, - } + Self::splat(false) + } + /// if trying to set all fields individually, prefer using the individual accessor + /// functions and [`PRegFlagsPowerISA::clear_unused()`]/[`PRegFlagsX86::clear_unused()`]/etc. + pub fn zeroed_sim() -> SimValue { + Self::splat_sim(false) } } diff --git a/crates/cpu/src/unit/alu_branch.rs b/crates/cpu/src/unit/alu_branch.rs index f2c255a..1cbd79a 100644 --- a/crates/cpu/src/unit/alu_branch.rs +++ b/crates/cpu/src/unit/alu_branch.rs @@ -7,7 +7,10 @@ use crate::{ AddSubMOp, AluBranchMOp, AluCommonMOp, BranchMOp, COMMON_MOP_SRC_LEN, CommonMOp, CompareMOp, LogicalMOp, MOpTrait, OutputIntegerMode, RenamedMOp, UnitOutRegNum, }, - register::{FlagsMode, PRegFlagsPowerISA, PRegFlagsX86, PRegValue}, + register::{ + FlagsMode, PRegFlagsPowerISA, PRegFlagsPowerISAView, PRegFlagsViewTrait, PRegFlagsX86, + PRegFlagsX86View, PRegValue, ViewUnused, + }, unit::{ DynUnit, DynUnitWrapper, GlobalState, UnitKind, UnitMOp, UnitOutput, UnitResult, UnitResultCompleted, UnitTrait, @@ -56,13 +59,13 @@ fn add_sub( FlagsMode::PowerISA(_) => { connect( carry_in_before_inversion, - PRegFlagsPowerISA::xer_ca(src_values[1].flags), + PRegFlagsPowerISA::view(src_values[1].flags).xer_ca, ); } FlagsMode::X86(_) => { connect( carry_in_before_inversion, - PRegFlagsX86::cf(src_values[1].flags), + PRegFlagsX86::view(src_values[1].flags).cf, ); } } @@ -199,27 +202,36 @@ fn add_sub( #[hdl] match flags_mode { FlagsMode::PowerISA(_) => { - PRegFlagsPowerISA::clear_unused(flags); - connect(PRegFlagsPowerISA::xer_ca(flags), pwr_ca); - connect(PRegFlagsPowerISA::xer_ca32(flags), pwr_ca32); - connect(PRegFlagsPowerISA::xer_ov(flags), pwr_ov); - connect(PRegFlagsPowerISA::xer_ov32(flags), pwr_ov32); - connect(PRegFlagsPowerISA::cr_lt(flags), pwr_cr_lt); - connect(PRegFlagsPowerISA::cr_gt(flags), pwr_cr_gt); - connect(PRegFlagsPowerISA::cr_eq(flags), pwr_cr_eq); - connect(PRegFlagsPowerISA::so(flags), pwr_so); + connect( + flags, + PRegFlagsPowerISA::from_view(PRegFlagsPowerISAView { + unused: ViewUnused::splat(false.to_expr()), + xer_ca: pwr_ca, + xer_ca32: pwr_ca32, + xer_ov: pwr_ov, + xer_ov32: pwr_ov32, + so: pwr_so, + cr_lt: pwr_cr_lt, + cr_gt: pwr_cr_gt, + cr_eq: pwr_cr_eq, + }), + ); } FlagsMode::X86(_) => { - PRegFlagsX86::clear_unused(flags); - connect(PRegFlagsX86::cf(flags), x86_cf); - connect(PRegFlagsX86::af(flags), x86_af); - connect(PRegFlagsX86::of(flags), x86_of); - connect(PRegFlagsX86::sf(flags), x86_sf); - connect(PRegFlagsX86::pf(flags), x86_pf); - connect(PRegFlagsX86::zf(flags), x86_zf); - - // this insn doesn't write DF, so it's output isn't used for reading DF - connect(PRegFlagsX86::df(flags), false); + connect( + flags, + PRegFlagsX86::from_view(PRegFlagsX86View { + unused: ViewUnused::splat(false.to_expr()), + cf: x86_cf, + zf: x86_zf, + sf: x86_sf, + of: x86_of, + af: x86_af, + pf: x86_pf, + // this insn doesn't write DF, so it's output isn't used for reading DF + df: false.to_expr(), + }), + ); } } #[hdl]