Compare commits

...

2 commits

9 changed files with 114260 additions and 102194 deletions

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,6 +1,14 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use fayalite::prelude::*;
use crate::instruction::ConditionMode;
use fayalite::{
expr::CastToImpl,
int::{BoolOrIntType, UIntInRange},
prelude::*,
ty::StaticType,
};
use std::fmt;
#[hdl]
pub enum FlagsMode {
@ -8,131 +16,750 @@ pub enum FlagsMode {
X86(PRegFlagsX86),
}
#[hdl(cmp_eq)]
pub struct PRegFlagsPowerISA {}
trait PRegFlagsViewTraitSealed {
type UnusedInner<T>: AsRef<[T]>
+ AsMut<[T]>
+ IntoIterator<
Item = T,
IntoIter: DoubleEndedIterator<Item = T>
+ ExactSizeIterator
+ std::iter::FusedIterator
+ Default,
>;
const UNUSED_INNER_LEN: usize;
fn unused_inner_map<T, R>(
v: Self::UnusedInner<T>,
f: impl FnMut(T) -> R,
) -> Self::UnusedInner<R>;
fn unused_inner_from_fn<T>(f: impl FnMut(usize) -> T) -> Self::UnusedInner<T>;
fn unused_inner_each_ref<T>(v: &Self::UnusedInner<T>) -> Self::UnusedInner<&T>;
fn unused_inner_each_mut<T>(v: &mut Self::UnusedInner<T>) -> Self::UnusedInner<&mut T>;
}
#[expect(private_bounds)]
pub trait PRegFlagsViewTrait: Type + PRegFlagsViewTraitSealed {
type View<T>;
fn view<T: Type>(flags: impl ToExpr<Type = PRegFlags<T>>) -> Self::View<Expr<T>>;
fn view_sim<T: Type>(flags: impl ToSimValue<Type = PRegFlags<T>>) -> Self::View<SimValue<T>>;
fn view_sim_ref<T: Type>(flags: &SimValue<PRegFlags<T>>) -> Self::View<&SimValue<T>>;
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>);
pub struct ViewUnusedIntoIter<T, V: PRegFlagsViewTrait>(
<V::UnusedInner<T> as IntoIterator>::IntoIter,
);
impl<T, V: PRegFlagsViewTrait> Iterator for ViewUnusedIntoIter<T, V> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
fn fold<B, F>(self, init: B, f: F) -> B
where
F: FnMut(B, Self::Item) -> B,
{
self.0.fold(init, f)
}
fn count(self) -> usize {
self.0.count()
}
fn last(self) -> Option<Self::Item> {
self.0.last()
}
}
impl<T, V: PRegFlagsViewTrait> DoubleEndedIterator for ViewUnusedIntoIter<T, V> {
fn next_back(&mut self) -> Option<Self::Item> {
self.0.next_back()
}
fn rfold<B, F>(self, init: B, f: F) -> B
where
F: FnMut(B, Self::Item) -> B,
{
self.0.rfold(init, f)
}
}
impl<T, V: PRegFlagsViewTrait> ExactSizeIterator for ViewUnusedIntoIter<T, V> {
fn len(&self) -> usize {
self.0.len()
}
}
impl<T, V: PRegFlagsViewTrait> std::iter::FusedIterator for ViewUnusedIntoIter<T, V> {}
impl<T, V: PRegFlagsViewTrait> Default for ViewUnusedIntoIter<T, V> {
fn default() -> Self {
Self(Default::default())
}
}
impl<T: Clone, V: PRegFlagsViewTrait> Clone for ViewUnusedIntoIter<T, V>
where
<V::UnusedInner<T> as IntoIterator>::IntoIter: Clone,
{
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<T, V: PRegFlagsViewTrait> IntoIterator for ViewUnused<T, V> {
type Item = T;
type IntoIter = ViewUnusedIntoIter<T, V>;
fn into_iter(self) -> Self::IntoIter {
ViewUnusedIntoIter(self.0.into_iter())
}
}
impl<T: Default, V: PRegFlagsViewTrait> Default for ViewUnused<T, V> {
fn default() -> Self {
Self::from_fn(|_| T::default())
}
}
impl<T, V: PRegFlagsViewTrait> ViewUnused<T, V> {
pub fn iter(&self) -> std::slice::Iter<'_, T> {
self.into_iter()
}
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<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,
{
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
})
}
pub fn splat_copied(v: T) -> Self
where
T: Copy,
{
Self::from_fn(|_| v)
}
}
impl<T: BoolOrIntType, V: PRegFlagsViewTrait> ViewUnused<SimValue<T>, V> {
pub fn clear(&mut self) {
for i in self.iter_mut() {
SimValue::bits_mut(i).bits_mut().fill(false);
}
}
}
impl<T: BoolOrIntType, V: PRegFlagsViewTrait> ViewUnused<&'_ mut SimValue<T>, V> {
pub fn clear(&mut self) {
for i in self.iter_mut() {
SimValue::bits_mut(i).bits_mut().fill(false);
}
}
}
impl<T: BoolOrIntType, V: PRegFlagsViewTrait> ViewUnused<Expr<T>, V>
where
UInt: CastToImpl<T, ValueOutput: ToExpr>,
{
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<T, V> {
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<T, V> {
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<T: Clone, V: PRegFlagsViewTrait> Clone for ViewUnused<T, V>
where
V::UnusedInner<T>: Clone,
{
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<T: Copy, V: PRegFlagsViewTrait> Copy for ViewUnused<T, V> where V::UnusedInner<T>: Copy {}
impl<T: fmt::Debug, V: PRegFlagsViewTrait> fmt::Debug for ViewUnused<T, V>
where
V::UnusedInner<T>: 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<T> {
$(#[$unused_field_meta])*
$unused_vis $unused: ViewUnused<T, $FlagsMode>,
$($(#[$view_field_meta])*
$view_field_vis $view_field: T,)*
}
impl<T> $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<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];
const UNUSED_INNER_LEN: usize = {
let v: &[&str] = &[$(stringify!($unused_field),)*];
v.len()
};
fn unused_inner_map<T, R>(
v: Self::UnusedInner<T>,
f: impl FnMut(T) -> R,
) -> Self::UnusedInner<R> {
v.map(f)
}
fn unused_inner_from_fn<T>(f: impl FnMut(usize) -> T) -> Self::UnusedInner<T> {
std::array::from_fn(f)
}
fn unused_inner_each_ref<T>(
v: &Self::UnusedInner<T>,
) -> Self::UnusedInner<&T> {
v.each_ref()
}
fn unused_inner_each_mut<T>(
v: &mut Self::UnusedInner<T>,
) -> Self::UnusedInner<&mut T> {
v.each_mut()
}
}
impl PRegFlagsViewTrait for $FlagsMode {
type View<T> = $View<T>;
#[hdl]
fn view<T: Type>(flags: impl ToExpr<Type = PRegFlags<T>>) -> Self::View<Expr<T>> {
#[hdl]
let PRegFlags::<T> {
$($unused_field,)*
$($flags_field,)*
} = flags.to_expr();
$View {
$unused: ViewUnused([$($unused_field,)*]),
$($view_field: $flags_field,)*
}
}
#[hdl]
fn view_sim<T: Type>(flags: impl ToSimValue<Type = PRegFlags<T>>) -> Self::View<SimValue<T>> {
#[hdl(sim)]
let PRegFlags::<T> {
$($unused_field,)*
$($flags_field,)*
} = flags.into_sim_value();
$View {
$unused: ViewUnused([$($unused_field,)*]),
$($view_field: $flags_field,)*
}
}
#[hdl]
fn view_sim_ref<T: Type>(flags: &SimValue<PRegFlags<T>>) -> Self::View<&SimValue<T>> {
#[hdl(sim)]
let PRegFlags::<T> {
$($unused_field,)*
$($flags_field,)*
} = flags;
$View {
$unused: ViewUnused([$($unused_field,)*]),
$($view_field: $flags_field,)*
}
}
#[hdl]
fn view_sim_mut<T: Type>(flags: &mut SimValue<PRegFlags<T>>) -> Self::View<&mut SimValue<T>> {
#[hdl(sim)]
let PRegFlags::<T> {
$($unused_field,)*
$($flags_field,)*
} = flags;
$View {
$unused: ViewUnused([$($unused_field,)*]),
$($view_field: $flags_field,)*
}
}
#[hdl]
fn from_view<T: ToExpr>(view: Self::View<T>) -> Expr<PRegFlags<T::Type>> {
let $View {
$unused: ViewUnused([$($unused_field,)*]),
$($view_field: $flags_field,)*
} = view;
#[hdl]
PRegFlags::<_> {
$($unused_field,)*
$($flags_field,)*
}
}
#[hdl]
fn from_view_sim<T: ToSimValue>(view: Self::View<T>) -> SimValue<PRegFlags<T::Type>> {
let $View {
$unused: ViewUnused([$($unused_field,)*]),
$($view_field: $flags_field,)*
} = view;
#[hdl(sim)]
PRegFlags::<_> {
$($unused_field,)*
$($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()
}
}
};
}
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,
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 xer_ca(flags: impl ToExpr<Type = PRegFlags>) -> Expr<Bool> {
flags.to_expr().pwr_ca_x86_cf
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 xer_ca32(flags: impl ToExpr<Type = PRegFlags>) -> Expr<Bool> {
flags.to_expr().pwr_ca32_x86_af
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 xer_ov(flags: impl ToExpr<Type = PRegFlags>) -> Expr<Bool> {
flags.to_expr().pwr_ov_x86_of
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 xer_ov32(flags: impl ToExpr<Type = PRegFlags>) -> Expr<Bool> {
flags.to_expr().pwr_ov32_x86_df
}
/// both `CR<N>.SO` and `XER.SO` since instructions that write to both always write the same value
pub fn so(flags: impl ToExpr<Type = PRegFlags>) -> Expr<Bool> {
flags.to_expr().pwr_so
}
pub fn cr_lt(flags: impl ToExpr<Type = PRegFlags>) -> Expr<Bool> {
flags.to_expr().pwr_cr_lt_x86_sf
}
pub fn cr_gt(flags: impl ToExpr<Type = PRegFlags>) -> Expr<Bool> {
flags.to_expr().pwr_cr_gt_x86_pf
}
pub fn cr_eq(flags: impl ToExpr<Type = PRegFlags>) -> Expr<Bool> {
flags.to_expr().pwr_cr_eq_x86_zf
}
#[hdl]
pub fn clear_unused(flags: impl ToExpr<Type = PRegFlags>) {
// 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;
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"))
}
}
#[hdl(cmp_eq)]
pub struct PRegFlagsX86 {}
impl_view_trait! {
#[hdl(cmp_eq)]
pub struct PRegFlagsX86 {}
impl PRegFlagsX86 {
pub fn cf(flags: impl ToExpr<Type = PRegFlags>) -> Expr<Bool> {
flags.to_expr().pwr_ca_x86_cf
}
pub fn zf(flags: impl ToExpr<Type = PRegFlags>) -> Expr<Bool> {
flags.to_expr().pwr_cr_eq_x86_zf
}
pub fn sf(flags: impl ToExpr<Type = PRegFlags>) -> Expr<Bool> {
flags.to_expr().pwr_cr_lt_x86_sf
}
pub fn of(flags: impl ToExpr<Type = PRegFlags>) -> Expr<Bool> {
flags.to_expr().pwr_ov_x86_of
}
pub fn af(flags: impl ToExpr<Type = PRegFlags>) -> Expr<Bool> {
flags.to_expr().pwr_ca32_x86_af
}
pub fn pf(flags: impl ToExpr<Type = PRegFlags>) -> Expr<Bool> {
flags.to_expr().pwr_cr_gt_x86_pf
}
pub fn df(flags: impl ToExpr<Type = PRegFlags>) -> Expr<Bool> {
flags.to_expr().pwr_ov32_x86_df
}
#[hdl]
pub fn clear_unused(flags: impl ToExpr<Type = PRegFlags>) {
// 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);
#[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,
}
}
#[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 {
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,
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,)*
}
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,
}
}
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)
}
pub fn view_sim<V: PRegFlagsViewTrait>(
flags: impl ToSimValue<Type = Self>,
) -> V::View<SimValue<T>> {
V::view_sim(flags)
}
pub fn view_sim_ref<V: PRegFlagsViewTrait>(flags: &SimValue<Self>) -> V::View<&SimValue<T>> {
V::view_sim_ref(flags)
}
pub fn view_sim_mut<V: PRegFlagsViewTrait>(
flags: &mut SimValue<Self>,
) -> V::View<&mut SimValue<T>> {
V::view_sim_mut(flags)
}
pub fn from_view<V: PRegFlagsViewTrait>(view: V::View<impl ToExpr<Type = T>>) -> Expr<Self> {
V::from_view(view)
}
pub fn from_view_sim<V: PRegFlagsViewTrait>(
view: V::View<impl ToSimValue<Type = T>>,
) -> SimValue<Self> {
V::from_view_sim(view)
}
pub fn fields(flags: impl ToExpr<Type = Self>) -> ViewUnused<Expr<T>, PRegFlagsAllUnused> {
Self::view::<PRegFlagsAllUnused>(flags).unused
}
pub fn fields_sim(
flags: impl ToSimValue<Type = Self>,
) -> ViewUnused<SimValue<T>, PRegFlagsAllUnused> {
Self::view_sim::<PRegFlagsAllUnused>(flags).unused
}
pub fn fields_sim_ref(flags: &SimValue<Self>) -> ViewUnused<&SimValue<T>, PRegFlagsAllUnused> {
Self::view_sim_ref::<PRegFlagsAllUnused>(flags).unused
}
pub fn fields_sim_mut(
flags: &mut SimValue<Self>,
) -> ViewUnused<&mut SimValue<T>, PRegFlagsAllUnused> {
Self::view_sim_mut::<PRegFlagsAllUnused>(flags).unused
}
pub fn from_fields(
fields: ViewUnused<impl ToExpr<Type = T>, PRegFlagsAllUnused>,
) -> Expr<Self> {
Self::from_view::<PRegFlagsAllUnused>(PRegFlagsAllUnusedView { unused: fields })
}
pub fn from_fields_sim(
fields: ViewUnused<impl ToSimValue<Type = T>, PRegFlagsAllUnused>,
) -> SimValue<Self> {
Self::from_view_sim::<PRegFlagsAllUnused>(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<Type = T>) -> Expr<Self> {
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<Type = T>) -> SimValue<Self> {
Self::from_fields_sim(ViewUnused::splat(v.into_sim_value()))
}
}
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.
#[hdl]
pub fn zeroed() -> Expr<PRegFlags> {
#[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<PRegFlags> {
Self::splat_sim(false)
}
pub const FLAG_COUNT: usize = PRegFlagsAllUnused::UNUSED_INNER_LEN;
}
#[hdl(cmp_eq)]

View file

@ -5,9 +5,13 @@ 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,
PRegFlagsX86View, PRegValue, ViewUnused,
},
register::{FlagsMode, PRegFlagsPowerISA, PRegFlagsX86, PRegValue},
unit::{
DynUnit, DynUnitWrapper, GlobalState, UnitKind, UnitMOp, UnitOutput, UnitResult,
UnitResultCompleted, UnitTrait,
@ -56,13 +60,13 @@ fn add_sub<SrcCount: KnownSize>(
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 +203,36 @@ fn add_sub<SrcCount: KnownSize>(
#[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]
@ -230,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>>>,
@ -362,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: