add PowerISA decoder #7

Merged
programmerjake merged 35 commits from programmerjake/cpu:add-powerisa-decoder into master 2026-01-29 02:22:14 +00:00
9 changed files with 113763 additions and 102103 deletions
Showing only changes of commit 9b8d99e9af - Show all commits

1
.gitignore vendored
View file

@ -1,3 +1,4 @@
# SPDX-License-Identifier: LGPL-3.0-or-later
# See Notices.txt for copyright information
/target
OPF_PowerISA_v3.1C.pdf

View file

@ -4,12 +4,14 @@
use crate::{
config::CpuConfig,
instruction::{
AddSubMOp, BranchMOp, CompareMOp, CompareMode, ConditionMode, LogicalMOp, Lut4, MOp,
MOpDestReg, MOpRegNum, MoveRegMOp, OutputIntegerMode,
AddSubMOp, BranchMOp, CompareMOp, CompareMode, ConditionMode, LogicalFlagsMOp,
LogicalFlagsMOpImm, LogicalMOp, Lut4, MOp, MOpDestReg, MOpRegNum, MoveRegMOp,
OutputIntegerMode,
},
powerisa_instructions_xml::{
InstructionBitFieldName, InstructionBitFieldsInner, Instructions, TextLineItem,
},
register::{PRegFlagsPowerISA, PRegFlagsPowerISAView},
util::array_vec::{ArrayVec, Length},
};
use fayalite::{module::wire_with_loc, prelude::*, ty::StaticType};
@ -258,21 +260,28 @@ fn crf(this: impl ToExpr<Type = FieldCrf>) -> Expr<MOpRegNum> {
#[hdl]
fn cr_bit(cr_bit: impl ToExpr<Type = FieldCrBit>) -> (Expr<MOpRegNum>, Expr<ConditionMode>) {
let cr_bit = cr_bit.to_expr();
#[hdl]
let condition_mode = wire();
let field_bit = cr_bit.bit_num.cast_to_static::<UInt<2>>();
let field_num = (cr_bit.bit_num >> 2).cast_to_static::<UInt<3>>();
#[hdl]
if field_bit.cmp_eq(0_hdl_u2) {
connect(condition_mode, ConditionMode.SLt());
} else if field_bit.cmp_eq(1_hdl_u2) {
connect(condition_mode, ConditionMode.SGt());
} else if field_bit.cmp_eq(2_hdl_u2) {
connect(condition_mode, ConditionMode.Eq());
} else {
connect(condition_mode, ConditionMode.Overflow());
}
(MOpRegNum::power_isa_cr_reg(field_num), condition_mode)
(
MOpRegNum::power_isa_cr_reg(field_num),
PRegFlagsPowerISA::cr_condition_modes_msb0().to_expr()[field_bit],
)
}
#[hdl]
fn cr_bit_sim(
cr_bit: impl ToSimValue<Type = FieldCrBit>,
) -> (SimValue<MOpRegNum>, SimValue<ConditionMode>) {
let cr_bit = cr_bit.into_sim_value();
let field_bit = *cr_bit
.bit_num
.cast_to_static::<UInt<2>>()
.cast_to_static::<fayalite::int::UIntInRange<0, 4>>();
let field_num = (&cr_bit.bit_num >> 2).cast_to_static::<UInt<3>>();
(
MOpRegNum::power_isa_cr_reg_sim(&field_num),
PRegFlagsPowerISA::cr_condition_modes_msb0_sim()[field_bit].clone(),
)
}
impl DecodeState {
@ -1354,6 +1363,40 @@ impl DecodeState {
);
});
}
/// for `mcrxrx`
#[hdl]
fn decode_mcrxrx(&mut self) {
self.decode_scope(|this, (FieldBF(bf),)| {
connect(
ArrayVec::len(this.output),
1usize.cast_to_static::<Length<_>>(),
);
connect(
this.output[0],
LogicalFlagsMOp::logical_flags(
MOpDestReg::new([crf(bf)], []),
[
MOpRegNum::power_isa_xer_ca_ca32_reg().value,
MOpRegNum::const_zero().value,
MOpRegNum::power_isa_xer_so_ov_ov32_reg().value,
],
LogicalFlagsMOpImm::from_swizzle_fn::<PRegFlagsPowerISA>(|src0, src1, src2| {
let mut dest = PRegFlagsPowerISAView::splat(None);
for (dest_field, src_field) in dest.cr_bits_msb0_mut().into_iter().zip([
src2.xer_ov.into(),
src2.xer_ov32.into(),
(src0.xer_ca, src1.xer_ca).into(),
(src0.xer_ca32, src1.xer_ca32).into(),
]) {
*dest_field = Some(src_field);
}
dest
}),
Lut4::from_fn(|a, b| a | b),
),
);
});
}
/// for `pnop`
#[hdl]
fn decode_pnop(&mut self) {
@ -1586,10 +1629,10 @@ const DECODE_FNS: &[(&[&str], DecodeFn)] = &[
// TODO
},
),
(&["mcrxrx"], DecodeState::decode_mcrxrx),
(
&[
"mcrxrx", "mtocrf", "mtcrf", "mfocrf", "mfcr", "setb", "setbc", "setbcr", "setnbc",
"setnbcr",
"mtocrf", "mtcrf", "mfocrf", "mfcr", "setb", "setbc", "setbcr", "setnbc", "setnbcr",
],
|_state| {
// TODO

View file

@ -1,10 +1,14 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{unit::UnitMOp, util::range_u32_len};
use crate::{
register::{PRegFlags, PRegFlagsViewTrait, PRegValue, ViewUnused},
unit::UnitMOp,
util::{Rotate, range_u32_len},
};
use fayalite::{
expr::{CastToImpl, HdlPartialEqImpl, ops::ArrayLiteral},
int::BoolOrIntType,
intern::{Intern, InternSlice, Interned},
expr::{HdlPartialEqImpl, ops::ArrayLiteral},
int::{BoolOrIntType, UIntInRange, UIntInRangeInclusive},
intern::Interned,
module::wire_with_loc,
prelude::*,
ty::StaticType,
@ -966,6 +970,566 @@ impl Lut4 {
}
}
/// immediate values for [`LogicalFlagsMOp`]. See [`LogicalFlagsMOp`] for a description of the operation.
#[hdl(cmp_eq)]
pub struct LogicalFlagsMOpImm {
pub src0_start: UIntInRange<0, { PRegFlags::FLAG_COUNT }>,
pub src1_start: UIntInRange<0, { PRegFlags::FLAG_COUNT }>,
pub src2_start: UIntInRange<0, { PRegFlags::FLAG_COUNT }>,
pub dest_start: UIntInRange<0, { PRegFlags::FLAG_COUNT }>,
pub dest_count: UIntInRangeInclusive<0, { PRegFlags::FLAG_COUNT }>,
}
#[cfg(test)]
#[test]
fn test_logical_flags_mop_imm_fits() {
let needed_width = LogicalFlagsMOpImm.canonical().bit_width();
type TheMOpCommon = LogicalFlagsMOpCommon<MOpDestReg, ConstUsize<{ MOpRegNum::WIDTH }>>;
let imm_width = TheMOpCommon::IMM_WIDTH;
assert!(
needed_width <= imm_width,
"needed_width={needed_width} imm_width={imm_width}",
);
}
/// intentionally not publicly constructable
#[derive(Copy, Clone)]
pub struct LogicalFlagsMOpImmFromSwizzleFnSrc<const SRC: usize> {
flag_index: usize,
}
impl<const SRC: usize> fmt::Debug for LogicalFlagsMOpImmFromSwizzleFnSrc<SRC> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { flag_index } = self;
write!(f, "src{SRC}[{flag_index}]")
}
}
#[derive(Copy, Clone)]
enum LogicalFlagsMOpImmFromSwizzleFnDestInner {
Src01 {
src0_flag_index: usize,
src1_flag_index: usize,
},
Src2 {
src2_flag_index: usize,
},
}
impl fmt::Debug for LogicalFlagsMOpImmFromSwizzleFnDestInner {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Src01 {
src0_flag_index,
src1_flag_index,
} => write!(
f,
"lut.output(src0[{src0_flag_index}], src1[{src1_flag_index}])"
),
Self::Src2 { src2_flag_index } => write!(f, "src2[{src2_flag_index}]"),
}
}
}
#[derive(Copy, Clone)]
pub struct LogicalFlagsMOpImmFromSwizzleFnDest(LogicalFlagsMOpImmFromSwizzleFnDestInner);
impl fmt::Debug for LogicalFlagsMOpImmFromSwizzleFnDest {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl
From<(
LogicalFlagsMOpImmFromSwizzleFnSrc<0>,
LogicalFlagsMOpImmFromSwizzleFnSrc<1>,
)> for LogicalFlagsMOpImmFromSwizzleFnDest
{
fn from(
value: (
LogicalFlagsMOpImmFromSwizzleFnSrc<0>,
LogicalFlagsMOpImmFromSwizzleFnSrc<1>,
),
) -> Self {
Self(LogicalFlagsMOpImmFromSwizzleFnDestInner::Src01 {
src0_flag_index: value.0.flag_index,
src1_flag_index: value.1.flag_index,
})
}
}
impl From<LogicalFlagsMOpImmFromSwizzleFnSrc<2>> for LogicalFlagsMOpImmFromSwizzleFnDest {
fn from(value: LogicalFlagsMOpImmFromSwizzleFnSrc<2>) -> Self {
Self(LogicalFlagsMOpImmFromSwizzleFnDestInner::Src2 {
src2_flag_index: value.flag_index,
})
}
}
#[derive(Clone, Debug)]
pub struct LogicalFlagsMopImmTryFromSwizzleFnError(String);
impl fmt::Display for LogicalFlagsMopImmTryFromSwizzleFnError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl std::error::Error for LogicalFlagsMopImmTryFromSwizzleFnError {}
impl LogicalFlagsMOpImm {
pub const SWIZZLE_CAPACITY: usize = 4;
#[track_caller]
pub fn from_imm(imm: impl ToExpr<Type = SInt>) -> Expr<Self> {
let imm_ty =
LogicalFlagsMOpCommon::<MOpDestReg, ConstUsize<{ MOpRegNum::WIDTH }>>::imm_ty();
let imm = imm.to_expr();
assert_eq!(imm_ty, imm.ty(), "imm must have the correct width");
imm.cast_to(UInt[LogicalFlagsMOpImm.canonical().bit_width()])
.cast_bits_to(LogicalFlagsMOpImm)
}
pub fn to_imm(this: impl ToExpr<Type = Self>) -> Expr<SInt> {
let imm_ty =
LogicalFlagsMOpCommon::<MOpDestReg, ConstUsize<{ MOpRegNum::WIDTH }>>::imm_ty();
this.to_expr().cast_to_bits().cast_to(imm_ty)
}
fn flags_operation_impl<Flags, I, IU, C, CU, Lut, U, B, AF>(
src0_start: I,
src1_start: I,
src2_start: I,
dest_start: I,
dest_count: C,
lut: Lut,
src: [Flags; 3],
to_fields: impl Fn(&Flags) -> AF,
from_fields: impl Fn(AF) -> Flags,
lut_output_fn: impl Fn(Lut, U, U) -> U,
named_a: impl Fn(&str, AF) -> AF,
from_array: impl Fn([B; PRegFlags::FLAG_COUNT]) -> AF,
) -> Flags
where
Flags: ValueType<Type = PRegFlags>,
I: ValueType<Type = UIntInRange<0, { PRegFlags::FLAG_COUNT }>> + CastTo<Output<UInt> = IU>,
IU: ValueType<Type = UInt> + Clone,
C: ValueType<Type = UIntInRangeInclusive<0, { PRegFlags::FLAG_COUNT }>>
+ CastTo<Output<UInt> = CU>,
CU: ValueType<Type = UInt> + Clone + HdlPartialOrd<usize, Output = B>,
Lut: ValueType<Type = Lut4>,
U: CastBitsTo<Type = UInt, Output<Array<Bool, { PRegFlags::FLAG_COUNT }>> = AF>,
B: ValueType<Type = Bool>
+ Clone
+ std::ops::Not<Output = B>
+ std::ops::BitAnd<Output = B>
+ std::ops::BitOr<Output = B>,
AF: ValueType<Type = Array<Bool, { PRegFlags::FLAG_COUNT }>>
+ CastToBits<Output = U>
+ std::ops::Index<usize, Output = B>
+ Rotate<IU, Output = AF>,
{
// note all these rotations are array rotations,
// so v.rotate_left(n) will move v[n] to v[0]
// and v.rotate_right(n) will move v[0] to v[n].
// this is *not* the same as rotating an integer left.
let src0_start = src0_start.cast_to(UInt[src0_start.ty().bit_width()]);
let src1_start = src1_start.cast_to(UInt[src1_start.ty().bit_width()]);
let src2_start = src2_start.cast_to(UInt[src2_start.ty().bit_width()]);
let dest_start = dest_start.cast_to(UInt[dest_start.ty().bit_width()]);
let dest_count = dest_count.cast_to(UInt[dest_count.ty().bit_width()]);
let src0 = to_fields(&src[0]);
let src1 = to_fields(&src[1]);
let src2 = to_fields(&src[2]);
let rotated_src0 = named_a("rotated_src0", src0.rotate_left(src0_start));
let rotated_src1 = named_a("rotated_src1", src1.rotate_left(src1_start));
let rotated_src2 = named_a("rotated_src2", src2.rotate_left(src2_start));
let lut_output = named_a(
"lut_output",
lut_output_fn(
lut,
rotated_src0.cast_to_bits(),
rotated_src1.cast_to_bits(),
)
.cast_bits_to(Array::<Bool, { PRegFlags::FLAG_COUNT }>::TYPE),
);
let mask = named_a(
"mask",
from_array(std::array::from_fn(|i| dest_count.cmp_gt(i))),
);
let rotated_mask = named_a("rotated_mask", mask.rotate_right(dest_start));
let dest = named_a(
"dest",
from_array(std::array::from_fn(|i| {
(rotated_mask[i].clone() & lut_output[i].clone())
| (!rotated_mask[i].clone() & rotated_src2[i].clone())
})),
);
from_fields(dest)
}
#[hdl]
pub fn flags_operation(
this: impl ToExpr<Type = Self>,
lut: impl ToExpr<Type = Lut4>,
src: impl ToExpr<Type = Array<PRegFlags, 3>>,
) -> Expr<PRegFlags> {
#[hdl]
let Self {
src0_start,
src1_start,
src2_start,
dest_start,
dest_count,
} = this;
Self::flags_operation_impl(
src0_start,
src1_start,
src2_start,
dest_start,
dest_count,
lut.to_expr(),
*src.to_expr(),
|v| {
ArrayLiteral::new(
Bool,
PRegFlags::fields(v)
.into_iter()
.map(Expr::canonical)
.collect(),
)
.to_expr()
},
|v| PRegFlags::from_fields(ViewUnused::from_fn(|i| v[i])),
Lut4::output,
|name, v| {
let w = wire_with_loc(name, SourceLocation::caller(), StaticType::TYPE);
connect(w, v);
w
},
|v| v.to_expr(),
)
}
#[hdl]
pub fn flags_operation_sim(
this: impl ToSimValue<Type = Self>,
lut: impl ToSimValue<Type = Lut4>,
src: impl ToSimValue<Type = Array<PRegFlags, 3>>,
) -> SimValue<PRegFlags> {
#[hdl(sim)]
let Self {
src0_start,
src1_start,
src2_start,
dest_start,
dest_count,
} = this;
Self::flags_operation_impl(
src0_start,
src1_start,
src2_start,
dest_start,
dest_count,
lut.into_sim_value(),
SimValue::into_value(src.into_sim_value()),
|v| {
let fields = PRegFlags::fields_sim(v);
let fields = fields.iter().as_slice();
SimValue::from_value(
Array::new_static(Bool),
std::array::from_fn(|i| fields[i].clone()),
)
},
|v| PRegFlags::from_fields_sim(ViewUnused::from_fn(|i| v[i].clone())),
Lut4::output_sim,
|_name, v| v,
|v| v.into_sim_value(),
)
}
#[hdl]
pub fn try_from_swizzle_fn<V: PRegFlagsViewTrait>(
swizzle_fn: impl FnOnce(
V::View<LogicalFlagsMOpImmFromSwizzleFnSrc<0>>,
V::View<LogicalFlagsMOpImmFromSwizzleFnSrc<1>>,
V::View<LogicalFlagsMOpImmFromSwizzleFnSrc<2>>,
) -> V::View<Option<LogicalFlagsMOpImmFromSwizzleFnDest>>,
) -> Result<SimValue<Self>, LogicalFlagsMopImmTryFromSwizzleFnError> {
use LogicalFlagsMOpImmFromSwizzleFnDestInner::*;
fn err(
v: String,
) -> Result<std::convert::Infallible, LogicalFlagsMopImmTryFromSwizzleFnError> {
Err(LogicalFlagsMopImmTryFromSwizzleFnError(v))
}
fn undo_rotation<T: Copy, R>(
input: [T; PRegFlags::FLAG_COUNT],
rotate_fn: impl Fn(&mut [T], usize),
matches: impl Fn(&[T; PRegFlags::FLAG_COUNT]) -> Option<R>,
) -> Option<(usize, [T; PRegFlags::FLAG_COUNT], R)> {
for i in 0..PRegFlags::FLAG_COUNT {
let mut unrotated = input;
// rotate by the reversed amount to undo the rotation
rotate_fn(&mut unrotated, PRegFlags::FLAG_COUNT - i);
if let Some(r) = matches(&unrotated) {
return Some((i, unrotated, r));
}
}
None
}
let swizzled = V::view_into_view_unused(swizzle_fn(
V::view_unused_into_view(ViewUnused::from_fn(|flag_index| {
LogicalFlagsMOpImmFromSwizzleFnSrc { flag_index }
})),
V::view_unused_into_view(ViewUnused::from_fn(|flag_index| {
LogicalFlagsMOpImmFromSwizzleFnSrc { flag_index }
})),
V::view_unused_into_view(ViewUnused::from_fn(|flag_index| {
LogicalFlagsMOpImmFromSwizzleFnSrc { flag_index }
})),
));
let Ok(swizzled) = <[_; PRegFlags::FLAG_COUNT]>::try_from(swizzled.iter().as_slice())
else {
unreachable!();
};
// this basically works by following the steps of `flags_operation_impl()` in reverse
let rotated_mask = swizzled.map(|v| v.map(|v| matches!(v.0, Src01 { .. })));
let lut_output = swizzled.map(|v| match v?.0 {
Src01 {
src0_flag_index,
src1_flag_index,
} => Some((src0_flag_index, src1_flag_index)),
Src2 { .. } => None,
});
let rotated_src2 = swizzled.map(|v| match v?.0 {
Src01 { .. } => None,
Src2 { src2_flag_index } => Some(src2_flag_index),
});
let get_dest_count_from_mask = |mask: &[Option<bool>; PRegFlags::FLAG_COUNT]| {
let dest_count = mask
.iter()
.rposition(|v| matches!(v, Some(true)))
.map(|v| v + 1)
.unwrap_or(0);
mask.iter()
.enumerate()
.into_iter()
.all(|(i, v)| v.is_none_or(|v| v == (i < dest_count)))
.then_some(dest_count)
};
let Some((dest_start, mask, dest_count)) = undo_rotation(
rotated_mask,
|v, n| v.rotate_right(n),
get_dest_count_from_mask,
) else {
match err(format!(
"there is no possible setting of `dest_start`!\n\
swizzled={swizzled:#?}\n\
rotated_mask={rotated_mask:?}"
))? {}
};
let rotated_src0 = lut_output.map(|v| v.map(|v| v.0));
let rotated_src1 = lut_output.map(|v| v.map(|v| v.1));
let get_src_start = |rotated_src: [Option<usize>; _],
name: &str|
-> Result<usize, LogicalFlagsMopImmTryFromSwizzleFnError> {
if let Some((src_start, _, _)) = undo_rotation(
rotated_src,
|v, n| v.rotate_left(n),
|v| {
v.iter()
.zip(0usize..)
.all(|(v, i)| v.is_none_or(|v| v == i))
.then_some(())
},
) {
Ok(src_start)
} else {
match err(format!(
"there is no possible setting of `{name}`!\n\
swizzled={swizzled:#?}\n\
dest_count={dest_count} dest_start={dest_start}\n\
rotated_mask={rotated_mask:?}\n\
mask={mask:?}\n\
rotated_{name}={rotated_src:?}"
))? {}
}
};
let src0_start = get_src_start(rotated_src0, "src0")?;
let src1_start = get_src_start(rotated_src1, "src1")?;
let src2_start = get_src_start(rotated_src2, "src2")?;
Ok(
#[hdl(sim)]
Self {
src0_start: src0_start.cast_to(LogicalFlagsMOpImm.src0_start),
src1_start: src1_start.cast_to(LogicalFlagsMOpImm.src1_start),
src2_start: src2_start.cast_to(LogicalFlagsMOpImm.src2_start),
dest_start: dest_start.cast_to(LogicalFlagsMOpImm.dest_start),
dest_count: dest_count.cast_to(LogicalFlagsMOpImm.dest_count),
},
)
}
#[track_caller]
pub fn from_swizzle_fn<V: PRegFlagsViewTrait>(
swizzle_fn: impl FnOnce(
V::View<LogicalFlagsMOpImmFromSwizzleFnSrc<0>>,
V::View<LogicalFlagsMOpImmFromSwizzleFnSrc<1>>,
V::View<LogicalFlagsMOpImmFromSwizzleFnSrc<2>>,
) -> V::View<Option<LogicalFlagsMOpImmFromSwizzleFnDest>>,
) -> SimValue<Self> {
match Self::try_from_swizzle_fn::<V>(swizzle_fn) {
Ok(v) => v,
Err(e) => panic!("try_from_swizzle_fn failed: {e}"),
}
}
}
#[hdl]
type LogicalFlagsMOpCommon<DestReg: Type, SrcRegWidth: Size> =
CommonMOp<ConstUsize<0>, DestReg, SrcRegWidth, ConstUsize<3>>;
common_mop_struct! {
#[mapped(<NewDestReg, NewSrcRegWidth> LogicalFlagsMOp<NewDestReg, NewSrcRegWidth>)]
#[hdl(cmp_eq)]
/// Operation:
/// ```
/// # // set up a bunch of mock types and variables -- they don't necessarily match the real types
/// # struct Lut4;
/// # impl Lut4 {
/// # fn output(&self, a: &'static str, b: &'static str) -> &'static str {
/// # format!("lut.output({a}, {b})").leak() // return a Copy type to make it look nicer
/// # }
/// # }
/// # struct ViewUnused([&'static str; PRegFlags::FLAG_COUNT]);
/// # impl ViewUnused {
/// # fn from_fn(f: impl FnMut(usize) -> &'static str) -> Self {
/// # Self(std::array::from_fn(f))
/// # }
/// # fn iter(&self) -> std::slice::Iter<'_, &'static str> {
/// # self.0.iter()
/// # }
/// # }
/// # struct PRegFlags(ViewUnused);
/// # impl PRegFlags {
/// # fn fields(self) -> ViewUnused {
/// # self.0
/// # }
/// # fn from_fields(v: ViewUnused) -> Self {
/// # Self(v)
/// # }
/// # const FLAG_COUNT: usize = 8; // doesn't necessarily match the real type
/// # }
/// # let lut = Lut4;
/// # let src0_start = 1usize;
/// # let src1_start = 2usize;
/// # let src2_start = 3usize;
/// # let dest_start = 4usize;
/// # let dest_count = 5usize;
/// # let src0 = PRegFlags(ViewUnused::from_fn(|i| format!("src0[{i}]").leak()));
/// # let src1 = PRegFlags(ViewUnused::from_fn(|i| format!("src1[{i}]").leak()));
/// # let src2 = PRegFlags(ViewUnused::from_fn(|i| format!("src2[{i}]").leak()));
/// /// convert `v` to the range `0..PRegFlags::FLAG_COUNT` by wrapping around
/// fn wrap(v: i64) -> usize {
/// v.rem_euclid(PRegFlags::FLAG_COUNT as i64) as usize
/// }
/// let src0 = src0.fields();
/// let src1 = src1.fields();
/// let src2 = src2.fields();
/// let src0 = src0.iter().as_slice();
/// let src1 = src1.iter().as_slice();
/// let src2 = src2.iter().as_slice();
/// let dest = PRegFlags::from_fields(ViewUnused::from_fn(|i| {
/// if wrap(i as i64 - dest_start as i64) < dest_count {
/// let src0 = src0[wrap(i as i64 + src0_start as i64)];
/// let src1 = src1[wrap(i as i64 + src1_start as i64)];
/// lut.output(src0, src1)
/// } else {
/// src2[wrap(i as i64 + src2_start as i64)]
/// }
/// }));
/// # let expected = [
/// # "lut.output(src0[1], src1[2])",
/// # "src2[4]",
/// # "src2[5]",
/// # "src2[6]",
/// # "lut.output(src0[5], src1[6])",
/// # "lut.output(src0[6], src1[7])",
/// # "lut.output(src0[7], src1[0])",
/// # "lut.output(src0[0], src1[1])",
/// # ];
/// # assert_eq!(dest.0.0, expected);
/// ```
pub struct LogicalFlagsMOp<DestReg: Type, SrcRegWidth: Size> {
#[common]
pub common: LogicalFlagsMOpCommon<DestReg, SrcRegWidth>,
pub lut: Lut4,
}
}
impl<DestReg: Type, SrcRegWidth: Size> LogicalFlagsMOp<DestReg, SrcRegWidth> {
#[hdl]
pub fn operation(
this: impl ToExpr<Type = Self>,
src: impl ToExpr<Type = Array<PRegValue, 3>>,
) -> Expr<PRegValue> {
let this = this.to_expr();
#[hdl]
PRegValue {
int_fp: 0u64,
flags: LogicalFlagsMOpImm::flags_operation(
Self::imm(this),
this.lut,
src.to_expr().map(|v| v.flags),
),
}
}
#[hdl]
pub fn operation_sim(
this: impl ToSimValue<Type = Self>,
src: impl ToSimValue<Type = Array<PRegValue, 3>>,
) -> SimValue<PRegValue> {
let this = this.into_sim_value();
#[hdl(sim)]
PRegValue {
int_fp: 0u64,
flags: LogicalFlagsMOpImm::flags_operation_sim(
Self::imm(&this),
SimValue::into_value(this).lut,
SimValue::into_value(src.into_sim_value()).map(|v| SimValue::into_value(v).flags),
),
}
}
#[hdl]
pub fn imm(this: impl ToExpr<Type = Self>) -> Expr<LogicalFlagsMOpImm> {
LogicalFlagsMOpImm::from_imm(LogicalFlagsMOpCommon::<DestReg, SrcRegWidth>::imm(
this.to_expr().common,
))
}
#[hdl]
pub fn logical_flags<Target: MOpTrait>(
dest: impl ToExpr<Type = DestReg>,
src: impl ToExpr<Type = Array<UIntType<SrcRegWidth>, 3>>,
imm: impl ToExpr<Type = LogicalFlagsMOpImm>,
lut: impl ToExpr<Type = Lut4>,
) -> Expr<Target>
where
Self: MOpInto<Target>,
{
MOpInto::mop_into(
#[hdl]
LogicalFlagsMOp {
common: CommonMOp::new(
0_hdl_u0,
dest,
src,
LogicalFlagsMOpImm::to_imm(imm.to_expr()),
),
lut,
},
)
}
}
common_mop_struct! {
#[mapped(<NewDestReg, NewSrcRegWidth> LogicalMOp<NewDestReg, NewSrcRegWidth, SrcCount>)]
#[hdl(cmp_eq)]
@ -1340,6 +1904,7 @@ mop_enum! {
pub enum AluBranchMOp<DestReg: Type, SrcRegWidth: Size> {
AddSub(AddSubMOp<DestReg, SrcRegWidth, ConstUsize<3>>),
AddSubI(AddSubMOp<DestReg, SrcRegWidth, ConstUsize<2>>),
LogicalFlags(LogicalFlagsMOp<DestReg, SrcRegWidth>),
Logical(LogicalMOp<DestReg, SrcRegWidth, ConstUsize<2>>),
LogicalI(LogicalMOp<DestReg, SrcRegWidth, ConstUsize<1>>),
Compare(CompareMOp<DestReg, SrcRegWidth, ConstUsize<2>>),

View file

@ -1,7 +1,13 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use fayalite::{expr::CastToImpl, int::BoolOrIntType, prelude::*};
use crate::instruction::ConditionMode;
use fayalite::{
expr::CastToImpl,
int::{BoolOrIntType, UIntInRange},
prelude::*,
ty::StaticType,
};
use std::fmt;
#[hdl]
@ -39,6 +45,8 @@ pub trait PRegFlagsViewTrait: Type + PRegFlagsViewTraitSealed {
fn view_sim_mut<T: Type>(flags: &mut SimValue<PRegFlags<T>>) -> Self::View<&mut SimValue<T>>;
fn from_view<T: ToExpr>(view: Self::View<T>) -> Expr<PRegFlags<T::Type>>;
fn from_view_sim<T: ToSimValue>(view: Self::View<T>) -> SimValue<PRegFlags<T::Type>>;
fn view_unused_into_view<T>(unused: ViewUnused<T, PRegFlagsAllUnused>) -> Self::View<T>;
fn view_into_view_unused<T>(view: Self::View<T>) -> ViewUnused<T, PRegFlagsAllUnused>;
}
pub struct ViewUnused<T, V: PRegFlagsViewTrait>(V::UnusedInner<T>);
@ -144,6 +152,10 @@ impl<T, V: PRegFlagsViewTrait> ViewUnused<T, V> {
pub fn map<R>(self, f: impl FnMut(T) -> R) -> ViewUnused<R, V> {
ViewUnused(V::unused_inner_map(self.0, f))
}
pub fn zip<U>(self, other: ViewUnused<U, V>) -> ViewUnused<(T, U), V> {
let mut iter = self.into_iter().zip(other);
ViewUnused::from_fn(|_| iter.next().expect("known to be Some"))
}
pub fn splat(v: T) -> Self
where
T: Clone,
@ -161,6 +173,12 @@ impl<T, V: PRegFlagsViewTrait> ViewUnused<T, V> {
v
})
}
pub fn splat_copied(v: T) -> Self
where
T: Copy,
{
Self::from_fn(|_| v)
}
}
impl<T: BoolOrIntType, V: PRegFlagsViewTrait> ViewUnused<SimValue<T>, V> {
@ -264,6 +282,63 @@ macro_rules! impl_view_trait {
}
}
}
impl<T> $View<T> {
$view_vis fn splat(v: T) -> Self
where
T: Clone,
{
$View {
$($view_field: v.clone(),)*
$unused: ViewUnused::splat(v),
}
}
$view_vis const fn splat_copied(v: T) -> Self
where
T: Copy,
{
$View {
$($view_field: v,)*
$unused: ViewUnused([v; _]),
}
}
$view_vis fn map<R>(self, mut f: impl FnMut(T) -> R) -> $View<R> {
#![allow(unused_mut)]
let $View {
$unused,
$($view_field,)*
} = self;
$View {
$($view_field: f($view_field),)*
$unused: $unused.map(f),
}
}
$view_vis fn zip<U>(self, other: $View<U>) -> $View<(T, U)> {
struct Fields<T> {
$($unused_field: T,)*
$($flags_field: T,)*
}
let $View {
$unused: ViewUnused([$($unused_field,)*]),
$($view_field: $flags_field,)*
} = self;
let this = Fields {
$($unused_field,)*
$($flags_field,)*
};
let $View {
$unused: ViewUnused([$($unused_field,)*]),
$($view_field: $flags_field,)*
} = other;
let other = Fields {
$($unused_field,)*
$($flags_field,)*
};
$View {
$unused: ViewUnused([$((this.$unused_field, other.$unused_field),)*]),
$($view_field: (this.$flags_field, other.$flags_field),)*
}
}
}
impl PRegFlagsViewTraitSealed for $FlagsMode {
type UnusedInner<T> = [T; Self::UNUSED_INNER_LEN];
@ -372,6 +447,26 @@ macro_rules! impl_view_trait {
$($flags_field,)*
}
}
fn view_unused_into_view<T>(unused: ViewUnused<T, PRegFlagsAllUnused>) -> Self::View<T> {
let fields = Fields::from_view_unused(unused);
$View {
$unused: ViewUnused([$(fields.$unused_field,)*]),
$($view_field: fields.$flags_field,)*
}
}
fn view_into_view_unused<T>(view: Self::View<T>) -> ViewUnused<T, PRegFlagsAllUnused> {
let $View {
$unused: ViewUnused([$($unused_field,)*]),
$($view_field: $flags_field,)*
} = view;
let fields = Fields {
$($unused_field,)*
$($flags_field,)*
};
fields.into_view_unused()
}
}
};
}
@ -388,11 +483,109 @@ impl_view_trait! {
pub xer_ca32: pwr_ca32_x86_af,
pub xer_ov: pwr_ov_x86_of,
pub xer_ov32: pwr_ov32_x86_df,
/// both `CR<N>.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,
/// both `CR<N>.SO` and `XER.SO` since instructions that write to both always write the same value
pub so: pwr_so,
}
}
impl PRegFlagsPowerISAView<Option<usize>> {
pub const CR_BIT_LE_INDEXES: Self = {
let mut v = Self::splat_copied(None);
let bits = v.cr_bits_lsb0_mut();
let mut i = 0;
while i < bits.len() {
*bits[i] = Some(i);
i += 1;
}
v
};
pub const CR_BIT_BE_INDEXES: Self = {
let mut v = Self::splat_copied(None);
let bits = v.cr_bits_msb0_mut();
let mut i = 0;
while i < bits.len() {
*bits[i] = Some(i);
i += 1;
}
v
};
}
impl PRegFlagsPowerISAView<Option<SimValue<ConditionMode>>> {
pub fn cr_condition_modes_sim() -> Self {
PRegFlagsPowerISAView::cr_condition_modes().map(|v| v.map(ToSimValue::into_sim_value))
}
}
impl PRegFlagsPowerISAView<Option<Expr<ConditionMode>>> {
pub fn cr_condition_modes() -> Self {
Self {
unused: ViewUnused([]),
xer_ca: None,
xer_ca32: None,
xer_ov: None,
xer_ov32: None,
cr_lt: Some(ConditionMode.SLt()),
cr_gt: Some(ConditionMode.SGt()),
cr_eq: Some(ConditionMode.Eq()),
so: Some(ConditionMode.Overflow()),
}
}
}
impl<T> PRegFlagsPowerISAView<T> {
pub fn into_cr_bits_msb0(self) -> [T; 4] {
[self.cr_lt, self.cr_gt, self.cr_eq, self.so]
}
pub const fn cr_bits_msb0_ref(&self) -> [&T; 4] {
[&self.cr_lt, &self.cr_gt, &self.cr_eq, &self.so]
}
pub const fn cr_bits_msb0_mut(&mut self) -> [&mut T; 4] {
[
&mut self.cr_lt,
&mut self.cr_gt,
&mut self.cr_eq,
&mut self.so,
]
}
pub fn into_cr_bits_lsb0(self) -> [T; 4] {
let mut retval = self.into_cr_bits_msb0();
retval.reverse();
retval
}
pub const fn cr_bits_lsb0_ref(&self) -> [&T; 4] {
let [b0, b1, b2, b3] = self.cr_bits_msb0_ref();
[b3, b2, b1, b0]
}
pub const fn cr_bits_lsb0_mut(&mut self) -> [&mut T; 4] {
let [b0, b1, b2, b3] = self.cr_bits_msb0_mut();
[b3, b2, b1, b0]
}
}
impl PRegFlagsPowerISA {
pub fn cr_condition_modes_msb0() -> [Expr<ConditionMode>; 4] {
PRegFlagsPowerISAView::cr_condition_modes()
.into_cr_bits_msb0()
.map(|v| v.expect("known to be Some"))
}
pub fn cr_condition_modes_lsb0() -> [Expr<ConditionMode>; 4] {
PRegFlagsPowerISAView::cr_condition_modes()
.into_cr_bits_lsb0()
.map(|v| v.expect("known to be Some"))
}
pub fn cr_condition_modes_msb0_sim() -> [SimValue<ConditionMode>; 4] {
PRegFlagsPowerISAView::cr_condition_modes_sim()
.into_cr_bits_msb0()
.map(|v| v.expect("known to be Some"))
}
pub fn cr_condition_modes_lsb0_sim() -> [SimValue<ConditionMode>; 4] {
PRegFlagsPowerISAView::cr_condition_modes_sim()
.into_cr_bits_lsb0()
.map(|v| v.expect("known to be Some"))
}
}
@ -414,46 +607,78 @@ impl_view_trait! {
}
}
impl_view_trait! {
#[hdl(cmp_eq)]
pub struct PRegFlagsAllUnused {}
macro_rules! impl_flags {
(
$(#[$struct_meta:meta])*
$struct_vis:vis struct $PRegFlags:ident<$T:ident: Type = Bool> {
$($field:ident: T,)*
}
) => {
$(#[$struct_meta])*
$struct_vis struct $PRegFlags<$T: Type = Bool> {
$($field: $T,)*
}
#[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,
]),
struct Fields<$T> {
$($field: $T,)*
}
impl<$T> Fields<$T> {
fn from_view_unused(unused: ViewUnused<$T, PRegFlagsAllUnused>) -> Self {
let ViewUnused([
$($field,)*
]) = unused;
Self {
$($field,)*
}
}
fn into_view_unused(self) -> ViewUnused<$T, PRegFlagsAllUnused> {
ViewUnused([
$(self.$field,)*
])
}
}
impl_view_trait! {
#[hdl(cmp_eq)]
pub struct PRegFlagsAllUnused {}
#[derive(Copy, Clone, Debug)]
#[non_exhaustive]
pub struct PRegFlagsAllUnusedView {
pub unused: ViewUnused([
$($field,)*
]),
}
}
};
}
impl_flags! {
#[hdl(cmp_eq)]
/// 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<T: Type = Bool> {
pwr_ca32_x86_af: T,
pwr_ca_x86_cf: T,
pwr_ov32_x86_df: T,
pwr_ov_x86_of: T,
pwr_so: T,
pwr_cr_eq_x86_zf: T,
pwr_cr_gt_x86_pf: T,
pwr_cr_lt_x86_sf: T,
}
}
#[hdl(cmp_eq)]
/// 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<T: Type = Bool> {
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<T: Type> PRegFlags<T> {
pub const fn field_ty(self) -> T {
self.pwr_so
}
pub fn view<V: PRegFlagsViewTrait>(flags: impl ToExpr<Type = Self>) -> V::View<Expr<T>> {
V::view(flags)
}
@ -516,6 +741,13 @@ impl<T: Type> PRegFlags<T> {
}
}
impl PRegFlags<UIntInRange<0, { PRegFlags::FLAG_COUNT }>> {
pub fn flag_indexes() -> SimValue<Self> {
let ty = <Self as StaticType>::TYPE.field_ty();
Self::from_fields_sim(ViewUnused::from_fn(|i| i.to_sim_value_with_type(ty)))
}
}
impl PRegFlags {
/// if trying to set all fields individually, prefer using the individual accessor
/// functions and [`PRegFlagsPowerISA::clear_unused()`]/[`PRegFlagsX86::clear_unused()`]/etc.
@ -527,6 +759,7 @@ impl PRegFlags {
pub fn zeroed_sim() -> SimValue<PRegFlags> {
Self::splat_sim(false)
}
pub const FLAG_COUNT: usize = PRegFlagsAllUnused::UNUSED_INNER_LEN;
}
#[hdl(cmp_eq)]

View file

@ -5,7 +5,8 @@ use crate::{
config::CpuConfig,
instruction::{
AddSubMOp, AluBranchMOp, AluCommonMOp, BranchMOp, COMMON_MOP_SRC_LEN, CommonMOp,
CompareMOp, LogicalMOp, MOpTrait, OutputIntegerMode, RenamedMOp, UnitOutRegNum,
CompareMOp, LogicalFlagsMOp, LogicalMOp, MOpTrait, OutputIntegerMode, RenamedMOp,
UnitOutRegNum,
},
register::{
FlagsMode, PRegFlagsPowerISA, PRegFlagsPowerISAView, PRegFlagsViewTrait, PRegFlagsX86,
@ -242,6 +243,20 @@ fn add_sub<SrcCount: KnownSize>(
}
}
#[hdl]
fn logical_flags(
mop: Expr<LogicalFlagsMOp<UnitOutRegNum<DynSize>, DynSize>>,
flags_mode: Expr<FlagsMode>,
src_values: Expr<Array<PRegValue, { COMMON_MOP_SRC_LEN }>>,
) -> Expr<UnitResultCompleted<()>> {
// TODO: finish
#[hdl]
UnitResultCompleted::<_> {
value: PRegValue::zeroed(),
extra_out: (),
}
}
#[hdl]
fn logical(
mop: Expr<LogicalMOp<UnitOutRegNum<DynSize>, DynSize, ConstUsize<2>>>,
@ -374,6 +389,23 @@ pub fn alu_branch(config: &CpuConfig, unit_index: usize) {
},
),
),
AluBranchMOp::<_, _>::LogicalFlags(mop) => connect(
unit_base.execute_end,
HdlSome(
#[hdl]
ExecuteEnd::<_, _> {
unit_output: #[hdl]
UnitOutput::<_, _> {
which: MOpTrait::dest_reg(mop),
result: UnitResult[()].Completed(logical_flags(
mop,
global_state.flags_mode,
src_values,
)),
},
},
),
),
AluBranchMOp::<_, _>::Logical(mop) => connect(
unit_base.execute_end,
HdlSome(

View file

@ -1,6 +1,9 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use fayalite::{expr::ops::ArrayLiteral, module::wire_with_loc, prelude::*};
use std::num::NonZero;
pub mod array_vec;
pub mod tree_reduce;
@ -25,3 +28,151 @@ pub(crate) const fn range_u32_nth_or_panic(range: &std::ops::Range<u32>, index:
panic!("index out of range")
}
}
// TODO: move to fayalite
pub trait Rotate<Amount> {
type Output;
/// like [`usize::rotate_left`] or [`<[T]>::rotate_left`](slice::rotate_left) depending on `Self` -- note that in lsb0 those rotate in opposite directions
fn rotate_left(&self, amount: Amount) -> Self::Output;
/// like [`usize::rotate_right`] or [`<[T]>::rotate_right`](slice::rotate_right) depending on `Self` -- note that in lsb0 those rotate in opposite directions
fn rotate_right(&self, amount: Amount) -> Self::Output;
}
impl<VSz: Size, ASz: Size> Rotate<Expr<UIntType<ASz>>> for Expr<UIntType<VSz>> {
type Output = Self;
/// like [`usize::rotate_left`]
fn rotate_left(&self, amount: Expr<UIntType<ASz>>) -> Self::Output {
if self.ty().width() == 0 {
return *self;
}
let amount = amount % self.ty().width();
let l = *self << amount;
let r = *self >> (self.ty().width() - amount);
(l | r).cast_to(self.ty())
}
/// like [`usize::rotate_right`]
fn rotate_right(&self, amount: Expr<UIntType<ASz>>) -> Self::Output {
if self.ty().width() == 0 {
return *self;
}
let amount = amount % self.ty().width();
let l = *self << (self.ty().width() - amount).cast_to(amount.ty());
let r = *self >> amount;
(l | r).cast_to(self.ty())
}
}
impl<VSz: Size, ASz: Size> Rotate<SimValue<UIntType<ASz>>> for SimValue<UIntType<VSz>> {
type Output = Self;
/// like [`usize::rotate_left`]
fn rotate_left(&self, amount: SimValue<UIntType<ASz>>) -> Self::Output {
if self.ty().width() == 0 {
return self.clone();
}
let amount = amount % self.ty().width();
let l = self << &amount;
let r = self >> (self.ty().width() - amount);
(l | r).cast_to(self.ty())
}
/// like [`usize::rotate_right`]
fn rotate_right(&self, amount: SimValue<UIntType<ASz>>) -> Self::Output {
if self.ty().width() == 0 {
return self.clone();
}
let amount = amount % self.ty().width();
let l = self << (self.ty().width() - &amount).cast_to(amount.ty());
let r = self >> amount;
(l | r).cast_to(self.ty())
}
}
fn array_rotate_helper<T: Type, N: Size, AmountSize: Size>(
mut array: Expr<ArrayType<T, N>>,
amount: Expr<UIntType<AmountSize>>,
rotate_fn: impl Fn(&mut [Expr<T>], usize),
rotate_fn_name: &str,
) -> Expr<ArrayType<T, N>> {
let Some(mut prev_step_size) = NonZero::new(array.ty().len()) else {
return array;
};
fn named<T: Type>(v: Expr<T>, name: impl AsRef<str>) -> Expr<T> {
let w = wire_with_loc(name.as_ref(), SourceLocation::caller(), v.ty());
connect(w, v);
w
}
fn non_empty_array_to_expr<T: Type, Len: Size>(
v: impl AsRef<[Expr<T>]>,
) -> Expr<ArrayType<T, Len>> {
let v = v.as_ref();
ArrayLiteral::new(v[0].ty(), v.iter().map(|v| Expr::canonical(*v)).collect()).to_expr()
}
fn mux<T: Type>(b: Expr<Bool>, true_v: Expr<T>, false_v: Expr<T>) -> Expr<T> {
let a: Expr<Array<T, 2>> = non_empty_array_to_expr([false_v, true_v]);
a[b.cast_to_static::<UInt<1>>()]
}
let amount_ty = amount.ty();
let mut amount = (amount % prev_step_size).cast_to(amount_ty);
loop {
(prev_step_size, amount, array) =
if let Some(step_size) = NonZero::new(prev_step_size.get() / 2) {
let amount = named(amount, format!("{rotate_fn_name}_amount_{prev_step_size}"));
let do_rotate = amount.cmp_ge(step_size);
let mut rotated_array = (*array).clone();
rotate_fn(rotated_array.as_mut(), step_size.get());
let rotated_array = named(
non_empty_array_to_expr(rotated_array),
format!("{rotate_fn_name}_rotated_array_{step_size}"),
);
let array = mux(do_rotate, rotated_array, array);
let array = named(array, format!("{rotate_fn_name}_array_{step_size}"));
let amount = mux(do_rotate, (amount - step_size).cast_to(amount_ty), amount);
(step_size, amount, array)
} else {
return array;
};
}
}
impl<T: Type, N: Size, AmountSize: Size> Rotate<Expr<UIntType<AmountSize>>>
for Expr<ArrayType<T, N>>
{
type Output = Self;
/// like [`<[T]>::rotate_left`](slice::rotate_left)
fn rotate_left(&self, amount: Expr<UIntType<AmountSize>>) -> Self::Output {
array_rotate_helper(*self, amount, <[Expr<T>]>::rotate_left, "rotate_left")
}
/// like [`<[T]>::rotate_right`](slice::rotate_right)
fn rotate_right(&self, amount: Expr<UIntType<AmountSize>>) -> Self::Output {
array_rotate_helper(*self, amount, <[Expr<T>]>::rotate_right, "rotate_right")
}
}
impl<T: Type, N: Size, AmountSize: Size> Rotate<SimValue<UIntType<AmountSize>>>
for SimValue<ArrayType<T, N>>
{
type Output = Self;
/// like [`<[T]>::rotate_left`](slice::rotate_left)
fn rotate_left(&self, amount: SimValue<UIntType<AmountSize>>) -> Self::Output {
if self.ty().len() == 0 {
return self.clone();
}
let Ok(amount) = usize::try_from(amount.to_bigint() % self.ty().len()) else {
unreachable!();
};
let mut retval = self.clone();
AsMut::<[SimValue<T>]>::as_mut(&mut SimValue::value_mut(&mut retval)).rotate_left(amount);
retval
}
/// like [`<[T]>::rotate_right`](slice::rotate_right)
fn rotate_right(&self, amount: SimValue<UIntType<AmountSize>>) -> Self::Output {
if self.ty().len() == 0 {
return self.clone();
}
let Ok(amount) = usize::try_from(amount.to_bigint() % self.ty().len()) else {
unreachable!();
};
let mut retval = self.clone();
AsMut::<[SimValue<T>]>::as_mut(&mut SimValue::value_mut(&mut retval)).rotate_right(amount);
retval
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -4,8 +4,9 @@
use cpu::{
decoder::simple_power_isa::decode_one_insn,
instruction::{
AddSubMOp, BranchMOp, CompareMOp, CompareMode, ConditionMode, LogicalMOp, Lut4, MOp,
MOpDestReg, MOpRegNum, MoveRegMOp, OutputIntegerMode,
AddSubMOp, BranchMOp, CompareMOp, CompareMode, ConditionMode, LogicalFlagsMOp,
LogicalFlagsMOpImm, LogicalMOp, Lut4, MOp, MOpDestReg, MOpRegNum, MoveRegMOp,
OutputIntegerMode,
},
util::array_vec::ArrayVec,
};
@ -922,6 +923,33 @@ fn test_cases() -> Vec<TestCase> {
0x7c8307b5;
SignExt32;
}
#[hdl]
fn mcrxrx_imm() -> SimValue<LogicalFlagsMOpImm> {
#[hdl(sim)]
LogicalFlagsMOpImm {
// if the order of flags in PRegFlags changes, this will need to be updated
src0_start: 4usize.cast_to(LogicalFlagsMOpImm.src0_start),
src1_start: 4usize.cast_to(LogicalFlagsMOpImm.src1_start),
src2_start: 4usize.cast_to(LogicalFlagsMOpImm.src2_start),
dest_start: 0usize.cast_to(LogicalFlagsMOpImm.dest_start),
dest_count: 6usize.cast_to(LogicalFlagsMOpImm.dest_count),
}
}
retval.push(insn_single(
"mcrxrx 3",
0x7d800480,
None,
LogicalFlagsMOp::logical_flags(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]),
[
MOpRegNum::power_isa_xer_ca_ca32_reg().value,
MOpRegNum::const_zero().value,
MOpRegNum::power_isa_xer_so_ov_ov32_reg().value,
],
mcrxrx_imm(),
Lut4::from_fn(|a, b| a | b),
),
));
// ensure pnop decodes to zero instructions
retval.push(insn_empty(
// LLVM doesn't support the pnop instruction: