add PowerISA decoder #7
9 changed files with 113763 additions and 102103 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,3 +1,4 @@
|
|||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
# See Notices.txt for copyright information
|
||||
/target
|
||||
OPF_PowerISA_v3.1C.pdf
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>>),
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
103910
crates/cpu/tests/expected/decode_one_insn.vcd
generated
103910
crates/cpu/tests/expected/decode_one_insn.vcd
generated
File diff suppressed because it is too large
Load diff
110779
crates/cpu/tests/expected/reg_alloc.vcd
generated
110779
crates/cpu/tests/expected/reg_alloc.vcd
generated
File diff suppressed because it is too large
Load diff
|
|
@ -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:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue