add cpu::test::decode_and_run_single_insn and some formal tests of running PowerISA instructions #15

Merged
programmerjake merged 17 commits from programmerjake/cpu:decode-and-test-harness into master 2026-06-19 03:57:09 +00:00
39 changed files with 855063 additions and 110566 deletions

8
Cargo.lock generated
View file

@ -388,7 +388,7 @@ checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6"
[[package]]
name = "fayalite"
version = "0.3.0"
source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#cf3e6cfc6bc33eebf2d2862c7a1948b9cf40ecac"
source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#bb34aeb7f1881ad03a320f9f792994a88afe5fe2"
dependencies = [
"base64",
"bitvec",
@ -417,7 +417,7 @@ dependencies = [
[[package]]
name = "fayalite-proc-macros"
version = "0.3.0"
source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#cf3e6cfc6bc33eebf2d2862c7a1948b9cf40ecac"
source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#bb34aeb7f1881ad03a320f9f792994a88afe5fe2"
dependencies = [
"fayalite-proc-macros-impl",
]
@ -425,7 +425,7 @@ dependencies = [
[[package]]
name = "fayalite-proc-macros-impl"
version = "0.3.0"
source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#cf3e6cfc6bc33eebf2d2862c7a1948b9cf40ecac"
source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#bb34aeb7f1881ad03a320f9f792994a88afe5fe2"
dependencies = [
"base16ct 0.2.0",
"num-bigint",
@ -440,7 +440,7 @@ dependencies = [
[[package]]
name = "fayalite-visit-gen"
version = "0.3.0"
source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#cf3e6cfc6bc33eebf2d2862c7a1948b9cf40ecac"
source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#bb34aeb7f1881ad03a320f9f792994a88afe5fe2"
dependencies = [
"indexmap",
"prettyplease",

View file

@ -16,6 +16,7 @@ use crate::{
register::{
PRegFlags, PRegFlagsPowerISA, PRegFlagsPowerISAView, PRegFlagsViewTrait, ViewUnused,
},
test::decode_and_run_single_insn::{DecodeOneInsn, DecodeOneInsnOutput},
util::{
Rotate,
array_vec::{ArrayVec, Length},
@ -138,10 +139,11 @@ struct DecodeState<'a> {
conditions: Option<&'static str>,
header: &'static crate::powerisa_instructions_xml::InstructionHeader,
insn: &'static crate::powerisa_instructions_xml::Instruction,
output: Expr<ArrayVec<MOp, ConstUsize<3>>>,
output: Expr<ArrayVec<TraceAsString<MOp>, ConstUsize<3>>>,
is_illegal: Expr<Bool>,
first_input: Expr<UInt<32>>,
second_input: Expr<HdlOption<UInt<32>>>,
has_second_input: Expr<Bool>,
second_input_value: Expr<UInt<32>>,
second_input_used: Expr<Bool>,
field_wires: &'a mut HashMap<String, HashMap<Expr<UInt>, Expr<UInt>>>,
}
@ -428,10 +430,9 @@ impl DecodeState<'_> {
Entry::Occupied(entry) => *entry.get(),
}
}
#[hdl]
fn decode_word(
&mut self,
matches: &mut Expr<Bool>,
matches: &mut Option<Expr<Bool>>,
fields: &mut BTreeMap<&'static str, Expr<UInt>>,
word: Expr<UInt<32>>,
fields_inner: &'static InstructionBitFieldsInner,
@ -524,7 +525,10 @@ impl DecodeState<'_> {
}
} else {
let value: u32 = name.parse().expect("bit field name must have at least one letter, be all `/`, or be a valid decimal number");
*matches = *matches & field.cmp_eq(value);
*matches = match *matches {
Some(matches) => Some(matches & field.cmp_eq(value)),
None => Some(field.cmp_eq(value)),
};
if orig_name.contains(char::is_alphabetic) {
if fields
.insert(orig_name, value.cast_to(field.ty()).to_expr())
@ -536,20 +540,19 @@ impl DecodeState<'_> {
}
}
}
#[hdl]
#[track_caller]
fn decode_scope<FS: FieldSet, F: FnOnce(&mut Self, FS)>(&mut self, f: F) {
let mut fields = BTreeMap::new();
let mut matches = true.to_expr();
let mut f = Some(f);
#[hdl]
#[track_caller]
fn run<'a, FS: FieldSet, F: FnOnce(&mut DecodeState<'a>, FS)>(
this: &mut DecodeState<'a>,
matches: Expr<Bool>,
matches: Option<Expr<Bool>>,
fields: &mut BTreeMap<&str, Expr<UInt>>,
f: &mut Option<F>,
) {
let matches = matches.expect("matches is known to be Some");
#[hdl]
if matches {
connect(
@ -561,23 +564,22 @@ impl DecodeState<'_> {
}
}
if let Some(prefix) = self.header.bit_fields().prefix() {
#[hdl]
if let HdlSome(suffix_word) = self.second_input {
self.decode_word(
&mut matches,
&mut fields,
self.first_input,
prefix.fields_inner(),
);
self.decode_word(
&mut matches,
&mut fields,
suffix_word,
self.header.bit_fields().fields_inner(),
);
run(self, matches, &mut fields, &mut f);
}
let mut matches = Some(self.has_second_input);
self.decode_word(
&mut matches,
&mut fields,
self.first_input,
prefix.fields_inner(),
);
self.decode_word(
&mut matches,
&mut fields,
self.second_input_value,
self.header.bit_fields().fields_inner(),
);
run(self, matches, &mut fields, &mut f);
} else {
let mut matches = None;
self.decode_word(
&mut matches,
&mut fields,
@ -594,7 +596,7 @@ impl DecodeState<'_> {
1usize.cast_to_static::<Length<_>>(),
);
connect(
this.output[0],
*this.output[0],
BranchMOp::branch_i(
MOpDestReg::new(
[if this.mnemonic.contains('l') {
@ -635,7 +637,7 @@ impl DecodeState<'_> {
let no_cr_bit = bo[4]; // BO_0 in specification
let (cr_field, condition_mode) = cr_bit_cond(bi);
#[hdl]
let branch_mop = wire();
let branch_mop: TraceAsString<MOp> = wire();
#[hdl]
let branch_lr_dest_reg = wire();
connect(branch_lr_dest_reg, MOpRegNum::const_zero());
@ -656,7 +658,7 @@ impl DecodeState<'_> {
#[hdl]
if no_ctr & no_cr_bit {
connect(
branch_mop,
*branch_mop,
BranchMOp::branch_i(
dest,
[MOpRegNum::const_zero(), src1],
@ -668,7 +670,7 @@ impl DecodeState<'_> {
);
} else if no_cr_bit {
connect(
branch_mop,
*branch_mop,
BranchMOp::branch_ctr(
dest,
[MOpRegNum::const_zero(), src1, branch_ctr_reg],
@ -681,7 +683,7 @@ impl DecodeState<'_> {
);
} else {
connect(
branch_mop,
*branch_mop,
BranchMOp::branch_cond_ctr(
dest,
[cr_field, src1, branch_ctr_reg],
@ -709,7 +711,7 @@ impl DecodeState<'_> {
2usize.cast_to_static::<Length<_>>(),
);
connect(
this.output[0],
*this.output[0],
AddSubMOp::add_sub_i(
MOpDestReg::new([MOpRegNum::power_isa_ctr_reg()], []),
[MOpRegNum::power_isa_ctr_reg(), MOpRegNum::const_zero()],
@ -782,7 +784,7 @@ impl DecodeState<'_> {
- bt_flag_index.cast_to(uint_ty))
% PRegFlags::FLAG_COUNT;
connect(
this.output[0],
*this.output[0],
LogicalFlagsMOp::logical_flags(
MOpDestReg::new([bt_reg], []),
[ba_reg, bb_reg, bt_reg],
@ -808,7 +810,7 @@ impl DecodeState<'_> {
1usize.cast_to_static::<Length<_>>(),
);
connect(
this.output[0],
*this.output[0],
MoveRegMOp::move_reg(
MOpDestReg::new([crf(bf)], []),
[crf(bfa)],
@ -885,7 +887,7 @@ impl DecodeState<'_> {
#[hdl]
if r {
connect(
this.output[0],
*this.output[0],
AddSubMOp::add_sub_i(
MOpDestReg::new([ea_reg], []),
[MOpRegNum::const_zero(); 2],
@ -899,7 +901,7 @@ impl DecodeState<'_> {
);
} else {
connect(
this.output[0],
*this.output[0],
AddSubMOp::add_sub_i(
MOpDestReg::new([ea_reg], []),
[gpr_or_zero(ra), MOpRegNum::const_zero()],
@ -913,7 +915,7 @@ impl DecodeState<'_> {
);
}
connect(
this.output[1],
*this.output[1],
LoadMOp::load(MOpDestReg::new([gpr(rt)], []), [ea_reg], width, conversion),
);
},
@ -927,7 +929,7 @@ impl DecodeState<'_> {
);
let ea_reg = ea_reg(ra);
connect(
this.output[0],
*this.output[0],
AddSubMOp::add_sub_i(
MOpDestReg::new([ea_reg], []),
[gpr_or_zero(ra), gpr(rb)],
@ -940,7 +942,7 @@ impl DecodeState<'_> {
),
);
connect(
this.output[1],
*this.output[1],
LoadMOp::load(MOpDestReg::new([gpr(rt)], []), [ea_reg], width, conversion),
);
});
@ -952,7 +954,7 @@ impl DecodeState<'_> {
);
let ea_reg = ea_reg(ra);
connect(
this.output[0],
*this.output[0],
AddSubMOp::add_sub_i(
MOpDestReg::new([ea_reg], []),
[gpr_or_zero(ra), MOpRegNum::const_zero()],
@ -965,7 +967,7 @@ impl DecodeState<'_> {
),
);
connect(
this.output[1],
*this.output[1],
LoadMOp::load(MOpDestReg::new([gpr(rt)], []), [ea_reg], width, conversion),
);
});
@ -983,7 +985,7 @@ impl DecodeState<'_> {
);
let ea_reg = ea_reg(ra);
connect(
this.output[0],
*this.output[0],
AddSubMOp::add_sub_i(
MOpDestReg::new([ea_reg], []),
[gpr_or_zero(ra), MOpRegNum::const_zero()],
@ -996,7 +998,7 @@ impl DecodeState<'_> {
),
);
connect(
this.output[1],
*this.output[1],
LoadMOp::load(MOpDestReg::new([gpr(rt)], []), [ea_reg], width, conversion),
);
});
@ -1063,9 +1065,9 @@ impl DecodeState<'_> {
} else {
(MOpRegNum::power_isa_temp_reg(), false.to_expr())
};
write_compute_ea_insn(this.output[0], ea_reg);
write_compute_ea_insn(*this.output[0], ea_reg);
connect(
this.output[1],
*this.output[1],
StoreMOp::store(
MOpDestReg::new([], []),
[ea_reg, gpr(rs)],
@ -1080,7 +1082,7 @@ impl DecodeState<'_> {
3usize.cast_to_static::<Length<_>>(),
);
connect(
this.output[2],
*this.output[2],
MoveRegMOp::move_reg(
MOpDestReg::new([gpr(ra)], []),
[ea_reg],
@ -1202,7 +1204,7 @@ impl DecodeState<'_> {
1usize.cast_to_static::<Length<_>>(),
);
connect(
this.output[0],
*this.output[0],
AddSubMOp::add_sub_i(
MOpDestReg::new([gpr(rt)], []),
#[hdl]
@ -1225,7 +1227,7 @@ impl DecodeState<'_> {
1usize.cast_to_static::<Length<_>>(),
);
connect(
this.output[0],
*this.output[0],
AddSubMOp::add_sub_i(
MOpDestReg::new([gpr(rt)], []),
#[hdl]
@ -1252,7 +1254,7 @@ impl DecodeState<'_> {
1usize.cast_to_static::<Length<_>>(),
);
connect(
this.output[0],
*this.output[0],
AddSubMOp::add_sub_i(
MOpDestReg::new([gpr(rt)], []),
#[hdl]
@ -1279,7 +1281,7 @@ impl DecodeState<'_> {
+ (d1 << 1).cast_to_static::<SInt<16>>()
+ d2.cast_to_static::<SInt<16>>();
connect(
this.output[0],
*this.output[0],
AddSubMOp::add_sub_i(
MOpDestReg::new([gpr(rt)], []),
#[hdl]
@ -1306,7 +1308,7 @@ impl DecodeState<'_> {
1usize.cast_to_static::<Length<_>>(),
);
connect(
this.output[0],
*this.output[0],
AddSubMOp::add_sub(
MOpDestReg::new(
[gpr(rt)],
@ -1337,7 +1339,7 @@ impl DecodeState<'_> {
1usize.cast_to_static::<Length<_>>(),
);
connect(
this.output[0],
*this.output[0],
AddSubMOp::add_sub_i(
MOpDestReg::new(
[gpr(rt), MOpRegNum::power_isa_xer_ca_ca32_reg()],
@ -1369,7 +1371,7 @@ impl DecodeState<'_> {
1usize.cast_to_static::<Length<_>>(),
);
connect(
this.output[0],
*this.output[0],
AddSubMOp::add_sub(
MOpDestReg::new(
[
@ -1406,7 +1408,7 @@ impl DecodeState<'_> {
1usize.cast_to_static::<Length<_>>(),
);
connect(
this.output[0],
*this.output[0],
AddSubMOp::add_sub_i(
MOpDestReg::new(
[gpr(rt), MOpRegNum::power_isa_xer_ca_ca32_reg()],
@ -1438,7 +1440,7 @@ impl DecodeState<'_> {
1usize.cast_to_static::<Length<_>>(),
);
connect(
this.output[0],
*this.output[0],
AddSubMOp::add_sub(
MOpDestReg::new(
[gpr(rt), MOpRegNum::power_isa_xer_ca_ca32_reg()],
@ -1471,7 +1473,7 @@ impl DecodeState<'_> {
1usize.cast_to_static::<Length<_>>(),
);
connect(
this.output[0],
*this.output[0],
AddSubMOp::add_sub(
MOpDestReg::new(
[gpr(rt), MOpRegNum::power_isa_xer_ca_ca32_reg()],
@ -1504,7 +1506,7 @@ impl DecodeState<'_> {
1usize.cast_to_static::<Length<_>>(),
);
connect(
this.output[0],
*this.output[0],
AddSubMOp::add_sub(
MOpDestReg::new(
[gpr(rt), MOpRegNum::power_isa_xer_ca_ca32_reg()],
@ -1537,7 +1539,7 @@ impl DecodeState<'_> {
1usize.cast_to_static::<Length<_>>(),
);
connect(
this.output[0],
*this.output[0],
AddSubMOp::add_sub(
MOpDestReg::new(
[gpr(rt), MOpRegNum::power_isa_xer_ca_ca32_reg()],
@ -1575,7 +1577,7 @@ impl DecodeState<'_> {
1usize.cast_to_static::<Length<_>>(),
);
connect(
this.output[0],
*this.output[0],
AddSubMOp::add_sub(
MOpDestReg::new(
[gpr(rt)],
@ -1615,7 +1617,7 @@ impl DecodeState<'_> {
connect(compare_mode, CompareMode.S32());
}
connect(
this.output[0],
*this.output[0],
CompareMOp::compare_i(
MOpDestReg::new([crf(bf)], []),
[gpr(ra)],
@ -1643,7 +1645,7 @@ impl DecodeState<'_> {
connect(compare_mode, CompareMode.S32());
}
connect(
this.output[0],
*this.output[0],
CompareMOp::compare(
MOpDestReg::new([crf(bf)], []),
[gpr(ra), gpr(rb)],
@ -1670,7 +1672,7 @@ impl DecodeState<'_> {
connect(compare_mode, CompareMode.U32());
}
connect(
this.output[0],
*this.output[0],
CompareMOp::compare_i(
MOpDestReg::new([crf(bf)], []),
[gpr(ra)],
@ -1698,7 +1700,7 @@ impl DecodeState<'_> {
connect(compare_mode, CompareMode.U32());
}
connect(
this.output[0],
*this.output[0],
CompareMOp::compare(
MOpDestReg::new([crf(bf)], []),
[gpr(ra), gpr(rb)],
@ -1725,7 +1727,7 @@ impl DecodeState<'_> {
connect(compare_mode, CompareMode.CmpRBOne());
}
connect(
this.output[0],
*this.output[0],
CompareMOp::compare(
MOpDestReg::new([crf(bf)], []),
[gpr(ra), gpr(rb)],
@ -1744,7 +1746,7 @@ impl DecodeState<'_> {
1usize.cast_to_static::<Length<_>>(),
);
connect(
this.output[0],
*this.output[0],
CompareMOp::compare(
MOpDestReg::new([crf(bf)], []),
[gpr(ra), gpr(rb)],
@ -1770,7 +1772,7 @@ impl DecodeState<'_> {
1usize.cast_to_static::<Length<_>>(),
);
connect(
this.output[0],
*this.output[0],
LogicalMOp::logical_i(
MOpDestReg::new(
[gpr(ra)],
@ -1824,7 +1826,7 @@ impl DecodeState<'_> {
1usize.cast_to_static::<Length<_>>(),
);
connect(
this.output[0],
*this.output[0],
LogicalMOp::logical(
MOpDestReg::new([gpr(ra)], [(MOpRegNum::POWER_ISA_CR_0_REG_NUM, rc)]),
[gpr(rs), gpr(rb)],
@ -1838,7 +1840,7 @@ impl DecodeState<'_> {
if rs.reg_num.cmp_eq(rb.reg_num) & !rc {
// found `mr`
connect(
this.output[0],
*this.output[0],
MoveRegMOp::move_reg(
MOpDestReg::new([gpr(ra)], []),
[gpr(rs)],
@ -1867,7 +1869,7 @@ impl DecodeState<'_> {
1usize.cast_to_static::<Length<_>>(),
);
connect(
this.output[0],
*this.output[0],
LogicalMOp::logical_i(
MOpDestReg::new([gpr(ra)], [(MOpRegNum::POWER_ISA_CR_0_REG_NUM, rc)]),
[gpr(rs)],
@ -1945,7 +1947,7 @@ impl DecodeState<'_> {
);
let dest_logic_op = self.rotate_dest_logic_op(msb0_mask_begin, msb0_mask_end, false);
connect(
self.output[0],
*self.output[0],
ShiftRotateMOp::shift_rotate(
MOpDestReg::new([gpr(ra.0)], [(MOpRegNum::POWER_ISA_CR_0_REG_NUM, rc.0)]),
[gpr(rs.0), gpr(rs.0), gpr(rb.0)],
@ -2004,7 +2006,7 @@ impl DecodeState<'_> {
}
}
connect(
self.output[0],
*self.output[0],
ShiftRotateMOp::shift_rotate(
MOpDestReg::new([gpr(ra.0)], [(MOpRegNum::POWER_ISA_CR_0_REG_NUM, rc.0)]),
[gpr(rs.0), gpr(rs.0), rotate_imm_src2],
@ -2156,7 +2158,7 @@ impl DecodeState<'_> {
1usize.cast_to_static::<Length<_>>(),
);
connect(
this.output[0],
*this.output[0],
ShiftRotateMOp::shift_rotate(
MOpDestReg::new(
[gpr(ra), MOpRegNum::power_isa_xer_ca_ca32_reg()],
@ -2186,7 +2188,7 @@ impl DecodeState<'_> {
1usize.cast_to_static::<Length<_>>(),
);
connect(
this.output[0],
*this.output[0],
ShiftRotateMOp::shift_rotate(
MOpDestReg::new(
[gpr(ra), MOpRegNum::power_isa_xer_ca_ca32_reg()],
@ -2218,7 +2220,7 @@ impl DecodeState<'_> {
1usize.cast_to_static::<Length<_>>(),
);
connect(
this.output[0],
*this.output[0],
ShiftRotateMOp::shift_rotate(
MOpDestReg::new(
[
@ -2280,7 +2282,7 @@ impl DecodeState<'_> {
1usize.cast_to_static::<Length<_>>(),
);
connect(
this.output[0],
*this.output[0],
ShiftRotateMOp::shift_rotate(
MOpDestReg::new([gpr(ra)], [(MOpRegNum::POWER_ISA_CR_0_REG_NUM, rc)]),
[gpr(rs), MOpRegNum::const_zero(), MOpRegNum::const_zero()],
@ -2312,7 +2314,7 @@ impl DecodeState<'_> {
);
if is_write_to_spr {
connect(
self.output[0],
*self.output[0],
MoveRegMOp::move_reg(
MOpDestReg::new([spr], []),
[gpr(reg)],
@ -2321,7 +2323,7 @@ impl DecodeState<'_> {
);
} else {
connect(
self.output[0],
*self.output[0],
MoveRegMOp::move_reg(
MOpDestReg::new([gpr(reg)], []),
[spr],
@ -2337,7 +2339,7 @@ impl DecodeState<'_> {
1usize.cast_to_static::<Length<_>>(),
);
connect(
self.output[0],
*self.output[0],
ReadSpecialMOp::read_special(
MOpDestReg::new([gpr(reg)], []),
[MOpRegNum::const_zero(); 0],
@ -2412,7 +2414,7 @@ impl DecodeState<'_> {
1usize.cast_to_static::<Length<_>>(),
);
connect(
this.output[0],
*this.output[0],
LogicalFlagsMOp::logical_flags(
MOpDestReg::new([crf(bf)], []),
[
@ -2741,10 +2743,39 @@ const DECODE_FNS: &[(&[&str], DecodeFn)] = &[
),
];
const MAX_MOPS_PER_INSN: usize = 3;
#[derive(Debug)]
pub struct DecodeFilterArgs<'a> {
mnemonic: &'a str,
}
impl DecodeFilterArgs<'_> {
pub fn mnemonic(&self) -> &str {
self.mnemonic
}
}
pub trait DecodeFilter {
fn should_include(&mut self, args: &DecodeFilterArgs<'_>) -> bool;
}
impl<T: ?Sized + FnMut(&DecodeFilterArgs<'_>) -> bool> DecodeFilter for T {
fn should_include(&mut self, args: &DecodeFilterArgs<'_>) -> bool {
self(args)
}
}
impl DecodeFilter for () {
fn should_include(&mut self, _args: &DecodeFilterArgs<'_>) -> bool {
true
}
}
#[hdl_module]
pub fn decode_one_insn() {
pub fn decode_one_insn(mut filter: impl DecodeFilter) {
#[hdl]
let output: ArrayVec<MOp, ConstUsize<3>> = m.output();
let output: ArrayVec<TraceAsString<MOp>, ConstUsize<{ MAX_MOPS_PER_INSN }>> = m.output();
#[hdl]
let is_illegal: Bool = m.output();
#[hdl]
@ -2758,6 +2789,20 @@ pub fn decode_one_insn() {
connect(second_input_used, false);
connect(is_illegal, true);
#[hdl]
let has_second_input = wire();
#[hdl]
let second_input_value = wire();
#[hdl]
if let HdlSome(second_input) = second_input {
connect(has_second_input, true);
connect(second_input_value, second_input);
} else {
connect(has_second_input, false);
connect(second_input_value, 0u32);
}
let mut decode_fns = BTreeMap::new();
for &(mnemonics, decode_fn) in DECODE_FNS {
for &mnemonic in mnemonics {
@ -2798,24 +2843,70 @@ pub fn decode_one_insn() {
let Some(decode_fn) = decode_fns.get(mnemonic) else {
panic!("unhandled mnemonic: {mnemonic:?}");
};
decode_fn(&mut DecodeState {
mnemonic,
arguments,
conditions,
header,
insn,
output,
is_illegal,
first_input,
second_input,
second_input_used,
field_wires: &mut field_wires,
});
if filter.should_include(&DecodeFilterArgs { mnemonic }) {
decode_fn(&mut DecodeState {
mnemonic,
arguments,
conditions,
header,
insn,
output,
is_illegal,
first_input,
has_second_input,
second_input_value,
second_input_used,
field_wires: &mut field_wires,
});
}
}
}
}
}
impl DecodeOneInsn for decode_one_insn {
type Input = (UInt<32>, HdlOption<UInt<32>>);
fn input_ty(self) -> Self::Input {
StaticType::TYPE
}
type MaxMOpCount = ConstUsize<{ MAX_MOPS_PER_INSN }>;
fn max_mop_count(self) -> <Self::MaxMOpCount as Size>::SizeType {
ConstUsize
}
#[hdl]
fn connect_io(
this: Expr<Self>,
input: Expr<Self::Input>,
output: Expr<DecodeOneInsnOutput<Self::MaxMOpCount>>,
) {
#[hdl]
let (first_input, second_input) = input;
connect(this.first_input, first_input);
connect(this.second_input, second_input);
#[hdl]
let size_in_bytes = wire();
#[hdl]
if this.second_input_used {
connect(size_in_bytes, 8_hdl_u4);
} else {
connect(size_in_bytes, 4_hdl_u4);
}
connect(
output,
#[hdl]
DecodeOneInsnOutput::<_> {
mops: this.output,
size_in_bytes,
is_illegal: this.is_illegal,
},
);
}
}
#[hdl_module]
pub fn simple_power_isa_decoder(config: PhantomConst<CpuConfig>) {
todo!()

View file

@ -40,6 +40,8 @@ impl<T: MOpTrait> MOpInto<T> for T {
}
}
pub trait MOpLeaf<Target: MOpTrait>: MOpInto<Target> {}
pub trait MOpVariantVisitOps {
type MOp: MOpTrait<
DestReg = <Self::Target as MOpTrait>::DestReg,
@ -115,7 +117,7 @@ pub trait MOpTrait: Type {
input: &mut SimValue<Self>,
f: &mut impl FnMut(&mut SimValue<Self::SrcReg>, usize),
);
fn connect_src_regs(
fn connect_to_src_regs(
input: impl ToExpr<Type = Self>,
src_regs: impl ToExpr<Type = Array<Self::SrcReg, { COMMON_MOP_SRC_LEN }>>,
) {
@ -533,6 +535,8 @@ impl<SrcCount: KnownSize> StaticType for CommonMOpDefaultImm<SrcCount> {
}
impl<SrcCount: KnownSize> HdlPartialEqImpl<Self> for CommonMOpDefaultImm<SrcCount> {
const TRY_STRUCTURAL_EQ: bool = true;
#[track_caller]
fn cmp_value_eq(
_lhs: Self,
@ -714,6 +718,35 @@ pub const COMMON_MOP_2_IMM_WIDTH: usize = common_mop_max_imm_size(2);
pub const COMMON_MOP_3_IMM_WIDTH: usize = common_mop_max_imm_size(3);
macro_rules! common_mop_struct {
(
#[leaf]
$(#[debug($($debug:tt)*)])?
#[mapped(<$NewDestReg:ident, $NewSrcReg:ident> $mapped_ty:ty)]
$(#[$struct_meta:meta])*
$vis:vis struct $MOp:ident<$DestReg:ident: $DestRegBound:ident, $SrcReg:ident: $SrcRegBound:ident $(, $Generic:ident: $GenericBound:ident)* $(,)?> {
$($body:tt)*
}
) => {
common_mop_struct! {
$(#[debug($($debug)*)])?
#[mapped(<$NewDestReg, $NewSrcReg> $mapped_ty)]
$(#[$struct_meta])*
$vis struct $MOp<$DestReg: $DestRegBound, $SrcReg: $SrcRegBound $(, $Generic: $GenericBound)*> {
$($body)*
}
}
impl<
$DestReg: $DestRegBound,
$SrcReg: $SrcRegBound,
$($Generic: $GenericBound,)*
Target: MOpTrait,
> MOpLeaf<Target> for $MOp<$DestReg, $SrcReg, $($Generic),*>
where
Self: MOpInto<Target>,
{
}
};
(
#[debug($(#[$debug_hdl:ident])? |$debug_value:ident, $debug_f:ident| $debug_block:block $(where $($debug_where:tt)*)?)]
#[mapped(<$NewDestReg:ident, $SrcReg:ident> $mapped_ty:ty)]
@ -1322,6 +1355,7 @@ macro_rules! maybe_write_comma_flag {
}
common_mop_struct! {
#[leaf]
#[debug(#[hdl] |this, f| {
#[hdl(sim)]
let AddSubMOp::<_, _, _> {
@ -1909,6 +1943,7 @@ impl LogicalFlagsMOpImm {
}
common_mop_struct! {
#[leaf]
#[debug(#[hdl] |this, f| {
#[hdl(sim)]
let LogicalFlagsMOp::<_, _> {
@ -2073,6 +2108,7 @@ impl<DestReg: Type, SrcReg: Type> LogicalFlagsMOp<DestReg, SrcReg> {
}
common_mop_struct! {
#[leaf]
#[debug(#[hdl] |this, f| {
#[hdl(sim)]
let LogicalMOp::<_, _, _> {
@ -2350,6 +2386,7 @@ impl SimValueDebug for ShiftRotateMOpImm {
}
common_mop_struct! {
#[leaf]
#[debug(#[hdl] |this, f| {
#[hdl(sim)]
let ShiftRotateMOp::<_, _> {
@ -2465,6 +2502,7 @@ impl CompareMode {
}
common_mop_struct! {
#[leaf]
#[debug(#[hdl] |this, f| {
#[hdl(sim)]
let CompareMOp::<_, _, _> {
@ -2569,6 +2607,7 @@ impl ConditionMode {
}
common_mop_struct! {
#[leaf]
#[debug(#[hdl] |this, f| {
#[hdl(sim)]
let BranchMOp::<_, _, _> {
@ -2736,6 +2775,7 @@ pub enum ReadSpecialMOpImm {
}
common_mop_struct! {
#[leaf]
#[debug(#[hdl] |this, f| {
#[hdl(sim)]
let ReadSpecialMOp::<_, _> {
@ -2826,6 +2866,7 @@ impl SimValueDebug for L2RegNum {
}
common_mop_struct! {
#[leaf]
#[debug(#[hdl] |this, f| {
#[hdl(sim)]
let ReadL2RegMOp::<_, _> {
@ -2868,6 +2909,7 @@ impl<DestReg: Type, SrcReg: Type> ReadL2RegMOp<DestReg, SrcReg> {
}
common_mop_struct! {
#[leaf]
#[debug(#[hdl] |this, f| {
#[hdl(sim)]
let WriteL2RegMOp::<_, _> {
@ -3014,6 +3056,7 @@ impl<DestReg: Type, SrcReg: Type, SrcCount: KnownSize>
}
common_mop_struct! {
#[leaf]
#[debug(#[hdl] |this, f| {
#[hdl(sim)]
let LoadMOp::<_, _> {
@ -3062,6 +3105,7 @@ impl<DestReg: Type, SrcReg: Type> LoadMOp<DestReg, SrcReg> {
}
common_mop_struct! {
#[leaf]
#[debug(#[hdl] |this, f| {
#[hdl(sim)]
let StoreMOp::<_, _> {
@ -3120,6 +3164,7 @@ mop_enum! {
}
common_mop_struct! {
#[leaf]
#[debug(#[hdl] |this, f| {
#[hdl(sim)]
let MoveRegMOp::<_, _> {

View file

@ -11,5 +11,6 @@ pub mod powerisa_instructions_xml;
pub mod reg_alloc;
pub mod register;
pub mod rename_execute_retire;
pub mod test;
pub mod unit;
pub mod util;

View file

@ -106,7 +106,7 @@ pub struct WipDecodedInsn {
pub kind: WipDecodedInsnKind,
}
#[hdl]
#[hdl(cmp_eq)]
pub enum CallStackOp {
None,
Push(UInt<64>),

View file

@ -13,7 +13,7 @@ use crate::{
COMMON_MOP_SRC_LEN, L2RegNum, MOp, MOpDestReg, MOpRegNum, MOpTrait, PRegNum, ReadL2RegMOp,
UnitNum, UnitOutRegNum, WriteL2RegMOp,
},
next_pc::{CallStackOp, SimValueDefault},
next_pc::{CallStackOp, FETCH_BLOCK_ID_WIDTH, SimValueDefault},
register::{FlagsMode, PRegFlagsPowerISA, PRegValue},
rename_execute_retire::{
rename_table::{RenameTable, RenameTableDebugState, RenameTableEntry, RenameTableUpdate},
@ -42,7 +42,7 @@ pub type MOpId = UInt<{ MOP_ID_WIDTH }>;
#[hdl(custom_debug(sim))]
/// A &micro;Op along with the state needed for this instance of the &micro;Op.
pub struct MOpInstance<MOp> {
pub fetch_block_id: UInt<8>,
pub fetch_block_id: UInt<{ FETCH_BLOCK_ID_WIDTH }>,
pub id: MOpId,
pub pc: UInt<64>,
/// initialized to 0 by decoder, overwritten by `next_pc()`
@ -217,6 +217,7 @@ pub struct UnitMOpCantCauseCancel<C: PhantomConstGet<CpuConfig>> {
pub struct ExecuteToUnitInterface<C: PhantomConstGet<CpuConfig>> {
pub global_state: GlobalState,
/// Enqueues happen in program order, they are not re-ordered by out-of-order execution.
/// if [`Self::unit_outputs_ready`] is `false`, then `enqueue.data` is always [`HdlNone`].
pub enqueue: ReadyValid<UnitEnqueue<C>>,
/// if [`Self::unit_outputs_ready`] is `false`, then this is always [`HdlNone`]
pub inputs_ready: HdlOption<UnitInputsReady<C>>,

View file

@ -1,11 +1,15 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{config::CpuConfig, rename_execute_retire::ExecuteToUnitInterface};
use crate::{
config::{CpuConfig, CpuConfigUnitCount},
rename_execute_retire::ExecuteToUnitInterface,
};
use fayalite::{
bundle::{BundleField, BundleType, NoBuilder},
expr::ops::FieldAccess,
intern::{Intern, Interned, Memoize},
module::{connect_with_loc, wire_with_loc},
prelude::*,
ty::{
OpaqueSimValue, OpaqueSimValueSlice, OpaqueSimValueWriter, OpaqueSimValueWritten,
@ -102,6 +106,42 @@ impl<C: Type + PhantomConstGet<CpuConfig>> ExecuteToUnitInterfaces<C> {
}
MyMemoize(PhantomData).get_owned(this.to_expr())
}
#[track_caller]
pub fn unit_fields_as_array_wire_with_loc(
this: impl ToExpr<Type = Self>,
wire_name: &str,
source_location: SourceLocation,
array_wire_is_dest: bool,
) -> Expr<ArrayType<ExecuteToUnitInterface<C>, CpuConfigUnitCount<C>>> {
let this = this.to_expr();
let config = this.ty().config;
let array_wire = wire_with_loc(
wire_name,
source_location,
ArrayType[ExecuteToUnitInterface[config]][CpuConfigUnitCount[config]],
);
for (unit_field, array_element) in Self::unit_fields(this).into_iter().zip(array_wire) {
if array_wire_is_dest {
connect_with_loc(array_element, unit_field, source_location);
} else {
connect_with_loc(unit_field, array_element, source_location);
}
}
array_wire
}
#[track_caller]
pub fn unit_fields_as_array_wire(
this: impl ToExpr<Type = Self>,
wire_name: &str,
array_wire_is_dest: bool,
) -> Expr<ArrayType<ExecuteToUnitInterface<C>, CpuConfigUnitCount<C>>> {
Self::unit_fields_as_array_wire_with_loc(
this,
wire_name,
SourceLocation::caller(),
array_wire_is_dest,
)
}
}
#[doc(hidden)]

4
crates/cpu/src/test.rs Normal file
View file

@ -0,0 +1,4 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
pub mod decode_and_run_single_insn;

View file

@ -0,0 +1,952 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{
config::{CpuConfig, CpuConfigUnitCount, PhantomConstCpuConfig},
instruction::{
COMMON_MOP_SRC_LEN, MOp, MOpDestReg, MOpRegNum, MOpTrait, MoveRegMOp, PRegNum, UnitNum,
UnitOutRegNum,
},
next_pc::{CallStackOp, FETCH_BLOCK_ID_WIDTH},
register::PRegValue,
rename_execute_retire::{
ExecuteToUnitInterface, GlobalState, MOpId, MOpInstance, NextPcPredictorOp, RenamedMOp,
UnitCausedCancel, UnitEnqueue, UnitFinishCauseCancel, UnitInputsReady,
UnitMOpIsNoLongerSpeculative, UnitOutputReady, to_unit_interfaces::ExecuteToUnitInterfaces,
},
unit::{HdlUnitKind, UnitMOp},
util::array_vec::ArrayVec,
};
use fayalite::{
bundle::BundleType,
int::UIntInRangeType,
intern::{Intern, Interned},
module::wire_with_loc,
prelude::*,
util::{prefix_sum::reduce, ready_valid::ReadyValid},
};
/// make arrays dynamically-sized to avoid putting large types on the stack
#[hdl(get(|c| 1 << MOpRegNum::WIDTH))]
type MOpRegCount<C: PhantomConstGet<CpuConfig>> = DynSize;
#[hdl(cmp_eq, no_static)]
pub struct Registers<C: PhantomConstGet<CpuConfig>> {
pub regs: ArrayType<TraceAsString<PRegValue>, MOpRegCount<C>>,
pub config: C,
}
impl<C: PhantomConstCpuConfig> Registers<C> {
#[hdl]
pub fn zeroed(self) -> Expr<Self> {
#[hdl]
Self {
regs: repeat(PRegValue::zeroed().into_trace_as_string(), self.regs.len()),
config: self.config,
}
}
#[hdl]
pub fn zeroed_sim(self) -> SimValue<Self> {
#[hdl(sim)]
Self {
regs: vec![PRegValue::zeroed_sim().into_trace_as_string(); self.regs.len()],
config: self.config,
}
}
}
#[hdl(no_static)]
pub struct DecodeOneInsnOutput<MaxMOpCount: Size> {
pub mops: ArrayVec<TraceAsString<MOp>, MaxMOpCount>,
pub size_in_bytes: UInt<4>,
pub is_illegal: Bool,
}
pub trait DecodeOneInsn: BundleType {
type Input: Type;
fn input_ty(self) -> Self::Input;
type MaxMOpCount: Size;
fn max_mop_count(self) -> <Self::MaxMOpCount as Size>::SizeType;
fn connect_io(
this: Expr<Self>,
input: Expr<Self::Input>,
output: Expr<DecodeOneInsnOutput<Self::MaxMOpCount>>,
);
}
pub type DecodeOneInsnInput<T> = <T as DecodeOneInsn>::Input;
#[doc(hidden)]
#[expect(non_upper_case_globals)]
pub const DecodeOneInsnInput: DecodeOneInsnInputWithoutGenerics =
DecodeOneInsnInputWithoutGenerics {};
#[doc(hidden)]
pub struct DecodeOneInsnInputWithoutGenerics {}
impl<T: DecodeOneInsn> std::ops::Index<T> for DecodeOneInsnInputWithoutGenerics {
type Output = DecodeOneInsnInput<T>;
fn index(&self, index: T) -> &Self::Output {
Interned::into_inner(index.input_ty().intern_sized())
}
}
pub type DecodeOneInsnMaxMOpCount<T> = <T as DecodeOneInsn>::MaxMOpCount;
#[doc(hidden)]
#[expect(non_upper_case_globals)]
pub const DecodeOneInsnMaxMOpCount: DecodeOneInsnMaxMOpCountWithoutGenerics =
DecodeOneInsnMaxMOpCountWithoutGenerics {};
#[doc(hidden)]
pub struct DecodeOneInsnMaxMOpCountWithoutGenerics {}
impl<T: DecodeOneInsn> std::ops::Index<T> for DecodeOneInsnMaxMOpCountWithoutGenerics {
type Output = <T::MaxMOpCount as Size>::SizeType;
fn index(&self, index: T) -> &Self::Output {
Interned::into_inner(index.max_mop_count().intern_sized())
}
}
#[hdl(no_static)]
pub struct DecodeAndRunSingleInsnInput<C: PhantomConstGet<CpuConfig>, I> {
pub decoder_input: I,
pub fetch_block_id: UInt<{ FETCH_BLOCK_ID_WIDTH }>,
pub first_id: MOpId,
pub pc: UInt<64>,
pub predicted_next_pc: UInt<64>,
pub regs: Registers<C>,
pub config: C,
}
#[hdl(no_static)]
pub struct DecodeAndRunSingleInsnOutput<C: PhantomConstGet<CpuConfig>, MaxMOpCount: Size> {
pub regs: Registers<C>,
pub cancel_and_start_at: HdlOption<UInt<64>>,
pub retired_insns: ArrayVec<NextPcPredictorOp<C>, MaxMOpCount>,
pub config: C,
}
#[hdl(no_static)]
struct UnrenamedMOpState<C: PhantomConstGet<CpuConfig>> {
unrenamed: MOpInstance<MOp>,
renamed: HdlOption<TraceAsString<RenamedMOp<C>>>,
unit_num: TraceAsString<UnitNum<C>>,
}
#[hdl(no_static)]
enum Error {
NoUnitsOfKind(HdlUnitKind),
UnexpectedOutputReadyId(MOpId),
UnexpectedFinishCauseCancelId(MOpId),
InconsistentState,
}
#[hdl(no_static)]
struct CancelState<C: PhantomConstGet<CpuConfig>> {
canceling_units: ArrayType<Bool, CpuConfigUnitCount<C>>,
config: C,
}
#[hdl]
enum UnitState {
NotYetEnqueued,
SentInputsReady,
OutputReady,
FinishedAndOrCausedCancel,
}
#[hdl(no_static)]
struct RunMOpState<C: PhantomConstGet<CpuConfig>, MaxMOpCount: Size> {
mop_index: UIntInRangeType<ConstUsize<0>, MaxMOpCount>,
unit_state: UnitState,
config: C,
}
#[hdl(no_static)]
enum RunState<C: PhantomConstGet<CpuConfig>, MaxMOpCount: Size> {
RunningMOp(RunMOpState<C, MaxMOpCount>),
CancelingUnits(CancelState<C>),
Finished,
Error(TraceAsString<Error>),
}
#[hdl(no_static)]
struct OutputState<C: PhantomConstGet<CpuConfig>> {
dest_value: TraceAsString<PRegValue>,
predictor_op: NextPcPredictorOp<C>,
}
#[hdl(no_static)]
struct DecodeAndRunSingleInsnState<C: PhantomConstGet<CpuConfig>, MaxMOpCount: Size> {
mops: ArrayVec<UnrenamedMOpState<C>, MaxMOpCount>,
regs: Registers<C>,
run_state: RunState<C, MaxMOpCount>,
output_state: HdlOption<OutputState<C>>,
cancel_and_start_at: HdlOption<UInt<64>>,
retired_insns: ArrayVec<NextPcPredictorOp<C>, MaxMOpCount>,
config: C,
}
#[hdl(no_static)]
enum StepOutput<C: PhantomConstGet<CpuConfig>, MaxMOpCount: Size> {
Finished(DecodeAndRunSingleInsnOutput<C, MaxMOpCount>),
Step(DecodeAndRunSingleInsnState<C, MaxMOpCount>),
Error(TraceAsString<Error>),
}
#[hdl]
fn connect_to_unit_nop<C: Type + PhantomConstGet<CpuConfig>>(
to_unit: Expr<ExecuteToUnitInterface<C>>,
global_state: impl ToExpr<Type = GlobalState>,
) {
#[hdl]
let ExecuteToUnitInterface::<_> {
global_state: to_unit_global_state,
enqueue,
inputs_ready,
is_no_longer_speculative,
cant_cause_cancel: _,
output_ready: _,
finish_cause_cancel: _,
unit_outputs_ready,
cancel_all,
config: _,
} = to_unit;
connect(to_unit_global_state, global_state);
connect(enqueue.data, enqueue.ty().data.HdlNone());
connect(inputs_ready, inputs_ready.ty().HdlNone());
connect(
is_no_longer_speculative,
is_no_longer_speculative.ty().HdlNone(),
);
connect(unit_outputs_ready, false);
connect(cancel_all.data, HdlNone());
}
impl<C: PhantomConstCpuConfig, MaxMOpCount: Size> DecodeAndRunSingleInsnState<C, MaxMOpCount> {
#[hdl]
fn new(
input: Expr<DecodeAndRunSingleInsnInput<C, impl Type>>,
decoder_output: Expr<DecodeOneInsnOutput<MaxMOpCount>>,
config: C,
max_mop_count: MaxMOpCount::SizeType,
) -> Expr<Self> {
#[hdl]
let new_state = wire(DecodeAndRunSingleInsnState[config][max_mop_count]);
#[hdl]
let Self {
mops,
regs,
run_state,
output_state,
cancel_and_start_at,
retired_insns,
config: _,
} = new_state;
connect(run_state, run_state.ty().Finished());
connect(output_state, output_state.ty().HdlNone());
connect(cancel_and_start_at, HdlNone());
connect(retired_insns, retired_insns.ty().new());
connect(
mops,
ArrayVec::map(
decoder_output.mops,
mops.ty().element(),
|index, decoded_mop| {
if index == 0 {
connect(
run_state,
run_state.ty().RunningMOp(
#[hdl]
RunMOpState::<_, _> {
mop_index: 0usize.cast_to(run_state.ty().RunningMOp.mop_index),
unit_state: UnitState.NotYetEnqueued(),
config,
},
),
);
}
let retval = wire_with_loc(
&format!("mops_{index}"),
SourceLocation::caller(),
UnrenamedMOpState[config],
);
#[hdl]
let UnrenamedMOpState::<_> {
unrenamed,
renamed,
unit_num,
} = retval;
#[hdl]
let MOpInstance::<_> {
fetch_block_id,
id,
pc,
predicted_next_pc,
size_in_bytes,
is_first_mop_in_insn,
is_last_mop_in_insn,
mop,
} = unrenamed;
connect(fetch_block_id, input.fetch_block_id);
connect(id, (input.first_id + index).cast_to_static::<MOpId>());
connect(pc, input.pc);
connect(predicted_next_pc, input.predicted_next_pc);
connect(size_in_bytes, decoder_output.size_in_bytes);
connect(is_first_mop_in_insn, index == 0);
connect(
is_last_mop_in_insn,
ArrayVec::len(decoder_output.mops).cmp_eq(index + 1),
);
connect(mop, decoded_mop);
#[hdl]
let renamed_untransformed_move = wire(
HdlOption[TraceAsString[UnitMOp[PRegNum[config]][PRegNum[config]]
[MoveRegMOp[PRegNum[config]][PRegNum[config]]]]],
);
#[hdl]
if let MOp::TransformedMove(_) = *mop {
connect(
renamed_untransformed_move,
renamed_untransformed_move.ty().HdlNone(),
);
} else {
let unit_kind = MOp::kind(*mop);
let available_units = config.get().available_units_for_kind(unit_kind);
let chosen_unit = reduce(
available_units.into_iter().enumerate().map(
|(unit_index, available_unit)| {
#[hdl]
let chosen_unit = wire(UnitNum[config]);
connect(chosen_unit, chosen_unit.ty().const_zero());
#[hdl]
if available_unit {
connect(
chosen_unit,
chosen_unit.ty().from_index(unit_index),
);
}
chosen_unit
},
),
|a, b| {
#[hdl]
let chosen_unit = wire(UnitNum[config]);
connect(chosen_unit, a);
#[hdl]
if a.cmp_eq(chosen_unit.ty().const_zero()) {
connect(chosen_unit, b);
}
chosen_unit
},
)
.unwrap_or(UnitNum[config].const_zero());
#[hdl]
if chosen_unit.cmp_eq(chosen_unit.ty().const_zero()) {
connect(
run_state,
run_state
.ty()
.Error(Error.NoUnitsOfKind(unit_kind).to_trace_as_string()),
);
}
let unit_out_reg_num_ty = UnitOutRegNum[config];
connect(
renamed_untransformed_move,
HdlSome(
MOpTrait::map_regs(
*mop,
#[hdl]
PRegNum::<_> {
unit_num: chosen_unit,
unit_out_reg: #[hdl]
UnitOutRegNum::<_> {
value: !unit_out_reg_num_ty.value.zero(),
config,
},
},
PRegNum[config],
&mut |src_reg, index| {
#[hdl]
let renamed_src_reg = wire(PRegNum[config]);
#[hdl]
if src_reg.cmp_eq(MOpRegNum::const_zero()) {
connect(
renamed_src_reg,
renamed_src_reg.ty().const_zero(),
);
} else {
connect(
renamed_src_reg,
#[hdl]
PRegNum::<_> {
unit_num: chosen_unit,
unit_out_reg: unit_out_reg_num_ty
.new_sim(index),
},
);
}
renamed_src_reg
},
)
.to_trace_as_string(),
),
);
}
connect(renamed, renamed.ty().HdlNone());
connect(*unit_num, unit_num.ty().inner_ty().const_zero());
#[hdl]
if let HdlSome(renamed_untransformed_move) = renamed_untransformed_move {
let v = UnitMOp::try_with_transformed_move_op(
*renamed_untransformed_move,
renamed.ty().HdlSome.inner_ty().TransformedMove,
|dest, _src| {
connect(dest, dest.ty().HdlNone());
},
);
#[hdl]
if let HdlSome(v) = v {
connect(renamed, HdlSome(v.to_trace_as_string()));
connect(*unit_num, MOpTrait::dest_reg(v).unit_num);
}
}
retval
},
),
);
connect(regs, input.regs);
#[hdl]
if PRegValue::zeroed().cmp_ne(*regs.regs[MOpRegNum::CONST_ZERO_REG_NUM as usize]) {
connect(
run_state,
run_state
.ty()
.Error(Error.InconsistentState().to_trace_as_string()),
);
}
new_state
}
#[hdl]
fn step(
this: Expr<Self>,
to_units: Expr<ExecuteToUnitInterfaces<C>>,
global_state: Expr<GlobalState>,
) -> Expr<StepOutput<C, MaxMOpCount>> {
#[hdl]
let Self {
mops,
regs,
run_state,
output_state,
cancel_and_start_at,
retired_insns,
config,
} = this;
let config = config.ty();
let max_mop_count = MaxMOpCount::from_usize(mops.ty().capacity());
#[hdl]
let stepped = wire(StepOutput[config][max_mop_count]);
let to_units_as_array = ExecuteToUnitInterfaces::unit_fields_as_array_wire(
to_units,
"to_units_as_array",
false,
);
for to_unit in to_units_as_array {
connect_to_unit_nop(to_unit, global_state);
}
#[hdl]
let stepped_step = wire(stepped.ty().Step);
connect(stepped_step, this);
connect(stepped, stepped.ty().Step(stepped_step));
#[hdl]
match run_state {
RunState::<_, _>::RunningMOp(run_mop_state) => {
#[hdl]
let RunMOpState::<_, _> {
mop_index,
unit_state,
config: _,
} = run_mop_state;
let mop_index = mop_index.cast_to(UInt[mop_index.ty().bit_width()]);
#[hdl]
let mop = wire(mops.ty().element());
connect(mop, mops[mop_index]);
#[hdl]
let UnrenamedMOpState::<_> {
unrenamed,
renamed,
unit_num,
} = mop;
#[hdl]
let unrenamed_src_regs = wire();
connect(
unrenamed_src_regs,
repeat(
MOpRegNum::const_zero(),
ConstUsize::<{ COMMON_MOP_SRC_LEN }>,
),
);
MOpTrait::connect_to_src_regs(*unrenamed.mop, unrenamed_src_regs);
#[hdl]
let src_values: Array<
TraceAsString<PRegValue>,
{ COMMON_MOP_SRC_LEN },
> = wire();
for (src_value, src_reg) in src_values.into_iter().zip(unrenamed_src_regs) {
connect(src_value, regs.regs[src_reg.value]);
}
#[hdl]
if let HdlSome(unit_index) = UnitNum::as_index(*unit_num) {
#[hdl]
let to_unit = wire(to_units_as_array.ty().element());
connect(to_units_as_array[unit_index], to_unit);
connect_to_unit_nop(to_unit, global_state);
#[hdl]
let ExecuteToUnitInterface::<_> {
global_state: _,
enqueue,
inputs_ready,
is_no_longer_speculative,
cant_cause_cancel: _, // we don't track if it can cause a cancel
output_ready,
finish_cause_cancel,
unit_outputs_ready,
cancel_all,
config: _,
} = to_unit;
connect(unit_outputs_ready, true);
connect(cancel_all.data, HdlNone());
#[hdl]
let unit_state_after_start = wire();
connect(unit_state_after_start, unit_state);
#[hdl]
if let UnitState::NotYetEnqueued = unit_state {
#[hdl]
if let HdlSome(renamed) = renamed {
let mop = #[hdl]
MOpInstance::<_> {
fetch_block_id: unrenamed.fetch_block_id,
id: unrenamed.id,
pc: unrenamed.pc,
predicted_next_pc: unrenamed.predicted_next_pc,
size_in_bytes: unrenamed.size_in_bytes,
is_first_mop_in_insn: unrenamed.is_first_mop_in_insn,
is_last_mop_in_insn: unrenamed.is_last_mop_in_insn,
mop: renamed,
};
connect(
enqueue.data,
HdlSome(
#[hdl]
UnitEnqueue::<_> { mop, config },
),
);
#[hdl]
if enqueue.ready {
connect(
inputs_ready,
HdlSome(
#[hdl]
UnitInputsReady::<_> {
mop,
src_values,
config,
},
),
);
connect(
is_no_longer_speculative,
HdlSome(
#[hdl]
UnitMOpIsNoLongerSpeculative::<_> {
id: unrenamed.id,
config,
},
),
);
connect(unit_state_after_start, UnitState.SentInputsReady());
}
} else {
connect(
stepped,
stepped
.ty()
.Error(Error.InconsistentState().to_trace_as_string()),
);
}
}
#[hdl]
let unit_state_after_output_ready = wire();
connect(unit_state_after_output_ready, unit_state_after_start);
#[hdl]
let output_state_after_output_ready = wire(output_state.ty());
connect(output_state_after_output_ready, output_state);
#[hdl]
if let HdlSome(output_ready) = output_ready {
#[hdl]
let UnitOutputReady::<_> {
id: output_ready_id,
dest_value,
predictor_op,
} = output_ready;
#[hdl]
match unit_state_after_start {
UnitState::SentInputsReady => {
connect(
output_state_after_output_ready,
HdlSome(
#[hdl]
OutputState::<_> {
dest_value,
predictor_op,
},
),
);
connect(unit_state_after_output_ready, UnitState.OutputReady());
}
UnitState::NotYetEnqueued
| UnitState::OutputReady
| UnitState::FinishedAndOrCausedCancel => connect(
stepped,
stepped
.ty()
.Error(Error.InconsistentState().to_trace_as_string()),
),
}
#[hdl]
if unrenamed.id.cmp_ne(output_ready_id) {
connect(
stepped,
stepped.ty().Error(
Error
.UnexpectedOutputReadyId(output_ready_id)
.to_trace_as_string(),
),
);
}
}
#[hdl]
if let HdlSome(finish_cause_cancel) = finish_cause_cancel {
#[hdl]
let UnitFinishCauseCancel::<_> {
id: finish_cause_cancel_id,
caused_cancel,
config: _,
} = finish_cause_cancel;
connect(stepped_step.output_state, output_state.ty().HdlNone());
#[hdl]
match unit_state_after_output_ready {
UnitState::OutputReady => {}
UnitState::NotYetEnqueued
| UnitState::SentInputsReady
| UnitState::FinishedAndOrCausedCancel => connect(
stepped,
stepped
.ty()
.Error(Error.InconsistentState().to_trace_as_string()),
),
}
#[hdl]
let do_retire = wire();
#[hdl]
if do_retire {
#[hdl]
if let HdlSome(output_state) = output_state_after_output_ready {
#[hdl]
let OutputState::<_> {
dest_value,
predictor_op,
} = output_state;
let old_len = ArrayVec::len(retired_insns);
let len_ty = old_len.ty();
let old_len = old_len.cast_to(UInt[len_ty.bit_width()]);
connect(
ArrayVec::len(stepped_step.retired_insns),
(old_len + 1u8).cast_to(len_ty),
);
connect(stepped_step.retired_insns[old_len], predictor_op);
for dest_reg in MOpDestReg::regs(MOpTrait::dest_reg(*unrenamed.mop))
{
#[hdl]
if MOpRegNum::const_zero().cmp_ne(dest_reg) {
connect(stepped_step.regs.regs[dest_reg.value], dest_value);
}
}
} else {
connect(
stepped,
stepped
.ty()
.Error(Error.InconsistentState().to_trace_as_string()),
);
}
}
#[hdl]
if let HdlSome(caused_cancel) = caused_cancel {
#[hdl]
let UnitCausedCancel::<_> {
start_at_pc,
cancel_after_retire,
config: _,
} = caused_cancel;
connect(stepped_step.cancel_and_start_at, HdlSome(start_at_pc));
connect(do_retire, cancel_after_retire);
connect(
stepped_step.run_state,
stepped_step.ty().run_state.CancelingUnits(
#[hdl]
CancelState::<_> {
canceling_units: repeat(
true,
CancelState[config].canceling_units.len(),
),
config,
},
),
);
} else {
connect(do_retire, true);
#[hdl]
let next_mop_index = wire(mops.ty().len_ty());
connect(
next_mop_index,
(mop_index + 1u8).cast_to(next_mop_index.ty()),
);
#[hdl]
if next_mop_index.cmp_lt(ArrayVec::len(mops)) {
connect(
stepped_step.run_state,
stepped_step.ty().run_state.RunningMOp(
#[hdl]
RunMOpState::<_, _> {
mop_index: next_mop_index
.cast_to(mop_index.ty())
.cast_to(run_mop_state.ty().mop_index),
unit_state: UnitState.NotYetEnqueued(),
config,
},
),
);
} else {
connect(
stepped_step.run_state,
stepped_step.ty().run_state.Finished(),
);
}
}
#[hdl]
if unrenamed.id.cmp_ne(finish_cause_cancel_id) {
connect(
stepped,
stepped.ty().Error(
Error
.UnexpectedFinishCauseCancelId(finish_cause_cancel_id)
.to_trace_as_string(),
),
);
}
} else {
connect(stepped_step.output_state, output_state_after_output_ready);
connect(
stepped_step.run_state,
stepped_step.ty().run_state.RunningMOp(
#[hdl]
RunMOpState::<_, _> {
mop_index: run_mop_state.mop_index,
unit_state: unit_state_after_output_ready,
config,
},
),
);
}
} else {
#[hdl]
if let MOp::TransformedMove(mop) = *unrenamed.mop {
#[hdl]
let MoveRegMOp::<_, _> { common: _ } = mop;
} else {
connect(
stepped,
stepped
.ty()
.Error(Error.InconsistentState().to_trace_as_string()),
);
}
let old_len = ArrayVec::len(retired_insns);
let len_ty = old_len.ty();
let old_len = old_len.cast_to(UInt[len_ty.bit_width()]);
connect(
ArrayVec::len(stepped_step.retired_insns),
(old_len + 1u8).cast_to(len_ty),
);
connect(
stepped_step.retired_insns[old_len],
#[hdl]
NextPcPredictorOp::<_> {
call_stack_op: CallStackOp.None(),
cond_br_taken: HdlNone(),
config,
},
);
for dest_reg in MOpDestReg::regs(MOpTrait::dest_reg(*unrenamed.mop)) {
#[hdl]
if MOpRegNum::const_zero().cmp_ne(dest_reg) {
connect(stepped_step.regs.regs[dest_reg.value], src_values[0]);
}
}
}
}
RunState::<_, _>::CancelingUnits(cancel_state) => {
#[hdl]
let CancelState::<_> {
canceling_units,
config: _,
} = cancel_state;
#[hdl]
let done_canceling_units = wire();
connect(done_canceling_units, true);
#[hdl]
let still_canceling_units = wire(canceling_units.ty());
for ((to_unit, canceling_unit), still_canceling_unit) in to_units_as_array
.into_iter()
.zip(canceling_units)
.zip(still_canceling_units)
{
connect(still_canceling_unit, false);
#[hdl]
if canceling_unit {
connect(to_unit.cancel_all.data, HdlSome(()));
#[hdl]
if !to_unit.cancel_all.ready {
connect(still_canceling_unit, true);
connect(done_canceling_units, false);
}
}
}
#[hdl]
if done_canceling_units {
connect(
stepped,
stepped.ty().Finished(
#[hdl]
DecodeAndRunSingleInsnOutput::<_, _> {
regs,
cancel_and_start_at,
retired_insns,
config,
},
),
);
} else {
connect(
stepped_step.run_state,
run_state.ty().CancelingUnits(
#[hdl]
CancelState::<_> {
canceling_units: still_canceling_units,
config,
},
),
);
}
}
RunState::<_, _>::Finished => connect(
stepped,
stepped.ty().Finished(
#[hdl]
DecodeAndRunSingleInsnOutput::<_, _> {
regs,
cancel_and_start_at,
retired_insns,
config,
},
),
),
RunState::<_, _>::Error(error) => connect(stepped, stepped.ty().Error(error)),
}
stepped
}
}
#[hdl_module]
pub fn decode_and_run_single_insn<C: Type + PhantomConstCpuConfig, D: Type + DecodeOneInsn>(
config: C,
decoder: Interned<Module<D>>,
) {
#[hdl]
let cd: ClockDomain = m.input();
#[hdl]
let input: ReadyValid<DecodeAndRunSingleInsnInput<C, DecodeOneInsnInput<D>>> = m.input(
ReadyValid[DecodeAndRunSingleInsnInput[config][DecodeOneInsnInput[decoder.io_ty()]]],
);
#[hdl]
let global_state: GlobalState = m.input();
#[hdl]
let output: ReadyValid<DecodeAndRunSingleInsnOutput<C, DecodeOneInsnMaxMOpCount<D>>> = m
.output(
ReadyValid
[DecodeAndRunSingleInsnOutput[config][DecodeOneInsnMaxMOpCount[decoder.io_ty()]]],
);
#[hdl]
let to_units: ExecuteToUnitInterfaces<C> = m.output(ExecuteToUnitInterfaces[config]);
#[hdl]
let error: Bool = m.output();
connect(error, false);
#[hdl]
let decoder = instance(decoder);
#[hdl]
let decoder_input = wire(decoder.ty().input_ty());
connect(decoder_input, decoder_input.ty().uninit());
#[hdl]
let decoder_output = wire(DecodeOneInsnOutput[decoder.ty().max_mop_count()]);
D::connect_io(decoder, decoder_input, decoder_output);
#[hdl]
let state_reg = reg_builder().clock_domain(cd).reset(
HdlOption[DecodeAndRunSingleInsnState[config][decoder.ty().max_mop_count()]].HdlNone(),
);
#[hdl]
if let HdlSome(state) = state_reg {
connect(input.ready, false);
let step = DecodeAndRunSingleInsnState::step(state, to_units, global_state);
#[hdl]
match step {
StepOutput::<_, _>::Finished(v) => {
connect(output.data, HdlSome(v));
#[hdl]
if output.ready {
connect(state_reg, state_reg.ty().HdlNone());
}
}
StepOutput::<_, _>::Step(state) => {
connect(state_reg, HdlSome(state));
connect(output.data, output.ty().data.HdlNone());
}
StepOutput::<_, _>::Error(_) => {
connect(output.data, output.ty().data.HdlNone());
connect(error, true);
}
}
} else {
for to_unit in ExecuteToUnitInterfaces::unit_fields(to_units) {
connect_to_unit_nop(to_unit, global_state);
}
connect(input.ready, true);
connect(output.data, output.ty().data.HdlNone());
#[hdl]
if let HdlSome(input) = input.data {
connect(decoder_input, input.decoder_input);
connect(
state_reg,
HdlSome(DecodeAndRunSingleInsnState::new(
input,
decoder_output,
config,
decoder.ty().max_mop_count(),
)),
);
}
}
}

View file

@ -4,7 +4,7 @@
use crate::{
config::CpuConfig,
instruction::{
AluBranchMOp, LoadStoreMOp, MOp, MOpDestReg, MOpInto, MOpRegNum, MOpTrait,
AluBranchMOp, LoadStoreMOp, MOp, MOpDestReg, MOpInto, MOpLeaf, MOpRegNum, MOpTrait,
MOpVariantVisitOps, MOpVariantVisitor, MOpVisitVariants, RenamedMOp, mop_enum,
},
rename_execute_retire::ExecuteToUnitInterface,
@ -45,9 +45,9 @@ macro_rules! all_units {
}
impl $UnitKind {
pub fn unit(self, config: PhantomConst<CpuConfig>, unit_index: usize) -> DynUnit {
pub fn unit(self, config: PhantomConst<CpuConfig>, unit_index: usize, filter: &mut impl RenamedMOpFilter) -> DynUnit {
match self {
$($UnitKind::$Unit => $create_dyn_unit_fn(config, unit_index),)*
$($UnitKind::$Unit => $create_dyn_unit_fn(config, unit_index, filter),)*
}
}
}
@ -326,19 +326,67 @@ all_units! {
this.TransformedMove.mapped_ty(new_dest_reg, new_src_reg)
})] TransformedMoveOp: Type
> {
#[create_dyn_unit_fn = |config, unit_index| alu_branch::AluBranch::new(config, unit_index).to_dyn()]
#[create_dyn_unit_fn = |config, unit_index, filter| alu_branch::AluBranch::new(config, unit_index, filter).to_dyn()]
#[extract(alu_branch_mop, alu_branch_mop_sim, alu_branch_mop_sim_ref, alu_branch_mop_sim_mut)]
AluBranch(AluBranchMOp<DestReg, SrcReg>),
#[transformed_move]
#[create_dyn_unit_fn = |config, unit_index| todo!()]
#[create_dyn_unit_fn = |config, unit_index, filter| todo!()]
#[extract(transformed_move_mop, transformed_move_mop_sim, transformed_move_mop_sim_ref, transformed_move_mop_sim_mut)]
TransformedMove(TransformedMoveOp),
#[create_dyn_unit_fn = |config, unit_index| todo!()]
#[create_dyn_unit_fn = |config, unit_index, filter| todo!()]
#[extract(load_store_mop, load_store_mop_sim, load_store_mop_sim_ref, load_store_mop_sim_mut)]
LoadStore(LoadStoreMOp<DestReg, SrcReg>),
}
}
pub trait RenamedMOpFilter {
fn should_include(
&mut self,
mop: &SimValue<crate::rename_execute_retire::RenamedMOp<PhantomConst<CpuConfig>>>,
) -> bool;
fn should_include_ty<
T: MOpLeaf<crate::rename_execute_retire::RenamedMOp<PhantomConst<CpuConfig>>>,
>(
&mut self,
ty: T,
) -> bool
where
Self: Sized,
{
self.should_include(
&T::mop_into(
UInt::new_dyn(ty.canonical().bit_width())
.zero()
.to_expr()
.cast_bits_to(ty),
)
.to_sim_value(),
)
}
}
impl<
T: ?Sized
+ FnMut(&SimValue<crate::rename_execute_retire::RenamedMOp<PhantomConst<CpuConfig>>>) -> bool,
> RenamedMOpFilter for T
{
fn should_include(
&mut self,
mop: &SimValue<crate::rename_execute_retire::RenamedMOp<PhantomConst<CpuConfig>>>,
) -> bool {
self(mop)
}
}
impl RenamedMOpFilter for () {
fn should_include(
&mut self,
_mop: &SimValue<crate::rename_execute_retire::RenamedMOp<PhantomConst<CpuConfig>>>,
) -> bool {
true
}
}
pub trait UnitTrait:
'static + Send + Sync + std::fmt::Debug + fayalite::intern::SupportsPtrEqWithTypeId
{

View file

@ -18,7 +18,7 @@ use crate::{
ExecuteToUnitInterface, GlobalState, NextPcPredictorOp, RenamedMOp, UnitCausedCancel,
UnitFinishCauseCancel, UnitInputsReady, UnitOutputReady,
},
unit::{DynUnit, DynUnitWrapper, UnitKind, UnitTrait},
unit::{DynUnit, DynUnitWrapper, RenamedMOpFilter, UnitKind, UnitTrait},
};
use fayalite::{
expr::CastToImpl,
@ -87,8 +87,11 @@ fn add_sub<C: Type + PhantomConstCpuConfig, SrcCount: KnownSize>(config: C) {
connect(src1, src_values[1].int_fp);
}
#[hdl]
let carry_in = wire();
connect(carry_in, carry_in_before_inversion ^ invert_carry_in);
let carry_in: UInt<1> = wire();
connect(
carry_in,
(carry_in_before_inversion ^ invert_carry_in).cast_to_static::<UInt<1>>(),
);
#[hdl]
let src0 = wire();
connect(src0, src_values[0].int_fp);
@ -104,7 +107,12 @@ fn add_sub<C: Type + PhantomConstCpuConfig, SrcCount: KnownSize>(config: C) {
connect(pc_or_zero, pc);
}
let sum_of_sliced = |slice: RangeTo<usize>| {
src0[slice] + src1[slice] + src_values[2].int_fp[slice] + pc_or_zero[slice] + imm[slice]
src0[slice]
+ src1[slice]
+ src_values[2].int_fp[slice]
+ pc_or_zero[slice]
+ imm[slice]
+ carry_in
};
#[hdl]
let sum: UInt<64> = wire();
@ -1235,7 +1243,11 @@ fn read_special<C: Type + PhantomConstCpuConfig>(config: C) {
}
#[hdl_module]
pub fn alu_branch(config: PhantomConst<CpuConfig>, unit_index: usize) {
pub fn alu_branch(
config: PhantomConst<CpuConfig>,
unit_index: usize,
filter: &mut impl RenamedMOpFilter,
) {
#[hdl]
let from_execute: ExecuteToUnitInterface<PhantomConst<CpuConfig>> =
m.input(ExecuteToUnitInterface[config]);
@ -1294,193 +1306,237 @@ pub fn alu_branch(config: PhantomConst<CpuConfig>, unit_index: usize) {
#[hdl]
match mop {
AluBranchMOp::<_, _>::AddSub(mop) => {
#[hdl]
let add_sub = instance(add_sub(config));
#[hdl]
let add_sub::<_, _> {
global_state: global_state_,
pc,
mop: mop_,
src_values: src_values_,
output,
} = add_sub;
connect(global_state_, global_state);
connect(pc, mop_instance.pc);
connect(*mop_, mop);
connect(src_values_, src_values);
connect(dest_value, output);
if filter.should_include_ty(mop.ty()) {
#[hdl]
let add_sub = instance(add_sub(config));
#[hdl]
let add_sub::<_, _> {
global_state: global_state_,
pc,
mop: mop_,
src_values: src_values_,
output,
} = add_sub;
connect(global_state_, global_state);
connect(pc, mop_instance.pc);
connect(*mop_, mop);
connect(src_values_, src_values);
connect(dest_value, output);
} else {
connect(*dest_value, PRegValue::zeroed());
}
}
AluBranchMOp::<_, _>::AddSubI(mop) => {
#[hdl]
let add_sub_i = instance(add_sub(config));
#[hdl]
let add_sub::<_, _> {
global_state: global_state_,
pc,
mop: mop_,
src_values: src_values_,
output,
} = add_sub_i;
connect(global_state_, global_state);
connect(pc, mop_instance.pc);
connect(*mop_, mop);
connect(src_values_, src_values);
connect(dest_value, output);
if filter.should_include_ty(mop.ty()) {
#[hdl]
let add_sub_i = instance(add_sub(config));
#[hdl]
let add_sub::<_, _> {
global_state: global_state_,
pc,
mop: mop_,
src_values: src_values_,
output,
} = add_sub_i;
connect(global_state_, global_state);
connect(pc, mop_instance.pc);
connect(*mop_, mop);
connect(src_values_, src_values);
connect(dest_value, output);
} else {
connect(*dest_value, PRegValue::zeroed());
}
}
AluBranchMOp::<_, _>::LogicalFlags(mop) => {
#[hdl]
let logical_flags = instance(logical_flags(config));
#[hdl]
let logical_flags::<_> {
global_state: global_state_,
mop: mop_,
src_values: src_values_,
output,
} = logical_flags;
connect(global_state_, global_state);
connect(*mop_, mop);
connect(src_values_, src_values);
connect(dest_value, output);
if filter.should_include_ty(mop.ty()) {
#[hdl]
let logical_flags = instance(logical_flags(config));
#[hdl]
let logical_flags::<_> {
global_state: global_state_,
mop: mop_,
src_values: src_values_,
output,
} = logical_flags;
connect(global_state_, global_state);
connect(*mop_, mop);
connect(src_values_, src_values);
connect(dest_value, output);
} else {
connect(*dest_value, PRegValue::zeroed());
}
}
AluBranchMOp::<_, _>::Logical(mop) => {
#[hdl]
let logical = instance(logical(config));
#[hdl]
let logical::<_, _> {
global_state: global_state_,
mop: mop_,
src_values: src_values_,
output,
} = logical;
connect(global_state_, global_state);
connect(*mop_, mop);
connect(src_values_, src_values);
connect(dest_value, output);
if filter.should_include_ty(mop.ty()) {
#[hdl]
let logical = instance(logical(config));
#[hdl]
let logical::<_, _> {
global_state: global_state_,
mop: mop_,
src_values: src_values_,
output,
} = logical;
connect(global_state_, global_state);
connect(*mop_, mop);
connect(src_values_, src_values);
connect(dest_value, output);
} else {
connect(*dest_value, PRegValue::zeroed());
}
}
AluBranchMOp::<_, _>::LogicalI(mop) => {
#[hdl]
let logical_i = instance(logical(config));
#[hdl]
let logical::<_, _> {
global_state: global_state_,
mop: mop_,
src_values: src_values_,
output,
} = logical_i;
connect(global_state_, global_state);
connect(*mop_, mop);
connect(src_values_, src_values);
connect(dest_value, output);
if filter.should_include_ty(mop.ty()) {
#[hdl]
let logical_i = instance(logical(config));
#[hdl]
let logical::<_, _> {
global_state: global_state_,
mop: mop_,
src_values: src_values_,
output,
} = logical_i;
connect(global_state_, global_state);
connect(*mop_, mop);
connect(src_values_, src_values);
connect(dest_value, output);
} else {
connect(*dest_value, PRegValue::zeroed());
}
}
AluBranchMOp::<_, _>::ShiftRotate(mop) => {
#[hdl]
let shift_rotate = instance(shift_rotate(config));
#[hdl]
let shift_rotate::<_> {
global_state: global_state_,
mop: mop_,
src_values: src_values_,
output,
} = shift_rotate;
connect(global_state_, global_state);
connect(*mop_, mop);
connect(src_values_, src_values);
connect(dest_value, output);
if filter.should_include_ty(mop.ty()) {
#[hdl]
let shift_rotate = instance(shift_rotate(config));
#[hdl]
let shift_rotate::<_> {
global_state: global_state_,
mop: mop_,
src_values: src_values_,
output,
} = shift_rotate;
connect(global_state_, global_state);
connect(*mop_, mop);
connect(src_values_, src_values);
connect(dest_value, output);
} else {
connect(*dest_value, PRegValue::zeroed());
}
}
AluBranchMOp::<_, _>::Compare(mop) => {
#[hdl]
let compare = instance(compare(config));
#[hdl]
let compare::<_, _> {
global_state: global_state_,
mop: mop_,
src_values: src_values_,
output,
} = compare;
connect(global_state_, global_state);
connect(*mop_, mop);
connect(src_values_, src_values);
connect(dest_value, output);
if filter.should_include_ty(mop.ty()) {
#[hdl]
let compare = instance(compare(config));
#[hdl]
let compare::<_, _> {
global_state: global_state_,
mop: mop_,
src_values: src_values_,
output,
} = compare;
connect(global_state_, global_state);
connect(*mop_, mop);
connect(src_values_, src_values);
connect(dest_value, output);
} else {
connect(*dest_value, PRegValue::zeroed());
}
}
AluBranchMOp::<_, _>::CompareI(mop) => {
#[hdl]
let compare_i = instance(compare(config));
#[hdl]
let compare::<_, _> {
global_state: global_state_,
mop: mop_,
src_values: src_values_,
output,
} = compare_i;
connect(global_state_, global_state);
connect(*mop_, mop);
connect(src_values_, src_values);
connect(dest_value, output);
if filter.should_include_ty(mop.ty()) {
#[hdl]
let compare_i = instance(compare(config));
#[hdl]
let compare::<_, _> {
global_state: global_state_,
mop: mop_,
src_values: src_values_,
output,
} = compare_i;
connect(global_state_, global_state);
connect(*mop_, mop);
connect(src_values_, src_values);
connect(dest_value, output);
} else {
connect(*dest_value, PRegValue::zeroed());
}
}
AluBranchMOp::<_, _>::Branch(mop) => {
#[hdl]
let branch = instance(branch(config));
#[hdl]
let branch::<_, _> {
global_state: global_state_,
pc: pc_,
fallthrough_pc: fallthrough_pc_,
predicted_next_pc,
mop: mop_,
src_values: src_values_,
output,
predictor_op: predictor_op_,
caused_cancel: caused_cancel_,
} = branch;
connect(global_state_, global_state);
connect(pc_, mop_instance.pc);
connect(fallthrough_pc_, fallthrough_pc);
connect(predicted_next_pc, mop_instance.predicted_next_pc);
connect(*mop_, mop);
connect(src_values_, src_values);
connect(dest_value, output);
connect(predictor_op, predictor_op_);
connect(caused_cancel, caused_cancel_);
if filter.should_include_ty(mop.ty()) {
#[hdl]
let branch = instance(branch(config));
#[hdl]
let branch::<_, _> {
global_state: global_state_,
pc: pc_,
fallthrough_pc: fallthrough_pc_,
predicted_next_pc,
mop: mop_,
src_values: src_values_,
output,
predictor_op: predictor_op_,
caused_cancel: caused_cancel_,
} = branch;
connect(global_state_, global_state);
connect(pc_, mop_instance.pc);
connect(fallthrough_pc_, fallthrough_pc);
connect(predicted_next_pc, mop_instance.predicted_next_pc);
connect(*mop_, mop);
connect(src_values_, src_values);
connect(dest_value, output);
connect(predictor_op, predictor_op_);
connect(caused_cancel, caused_cancel_);
} else {
connect(*dest_value, PRegValue::zeroed());
}
}
AluBranchMOp::<_, _>::BranchI(mop) => {
#[hdl]
let branch_i = instance(branch(config));
#[hdl]
let branch::<_, _> {
global_state: global_state_,
pc: pc_,
fallthrough_pc: fallthrough_pc_,
predicted_next_pc,
mop: mop_,
src_values: src_values_,
output,
predictor_op: predictor_op_,
caused_cancel: caused_cancel_,
} = branch_i;
connect(global_state_, global_state);
connect(pc_, mop_instance.pc);
connect(fallthrough_pc_, fallthrough_pc);
connect(predicted_next_pc, mop_instance.predicted_next_pc);
connect(*mop_, mop);
connect(src_values_, src_values);
connect(dest_value, output);
connect(predictor_op, predictor_op_);
connect(caused_cancel, caused_cancel_);
if filter.should_include_ty(mop.ty()) {
#[hdl]
let branch_i = instance(branch(config));
#[hdl]
let branch::<_, _> {
global_state: global_state_,
pc: pc_,
fallthrough_pc: fallthrough_pc_,
predicted_next_pc,
mop: mop_,
src_values: src_values_,
output,
predictor_op: predictor_op_,
caused_cancel: caused_cancel_,
} = branch_i;
connect(global_state_, global_state);
connect(pc_, mop_instance.pc);
connect(fallthrough_pc_, fallthrough_pc);
connect(predicted_next_pc, mop_instance.predicted_next_pc);
connect(*mop_, mop);
connect(src_values_, src_values);
connect(dest_value, output);
connect(predictor_op, predictor_op_);
connect(caused_cancel, caused_cancel_);
} else {
connect(*dest_value, PRegValue::zeroed());
}
}
AluBranchMOp::<_, _>::ReadSpecial(mop) => {
#[hdl]
let read_special = instance(read_special(config));
#[hdl]
let read_special::<_> {
global_state: global_state_,
mop: mop_,
src_values: src_values_,
output,
} = read_special;
connect(global_state_, global_state);
connect(*mop_, mop);
connect(src_values_, src_values);
connect(dest_value, output);
if filter.should_include_ty(mop.ty()) {
#[hdl]
let read_special = instance(read_special(config));
#[hdl]
let read_special::<_> {
global_state: global_state_,
mop: mop_,
src_values: src_values_,
output,
} = read_special;
connect(global_state_, global_state);
connect(*mop_, mop);
connect(src_values_, src_values);
connect(dest_value, output);
} else {
connect(*dest_value, PRegValue::zeroed());
}
}
}
connect(
@ -1530,10 +1586,14 @@ pub struct AluBranch {
}
impl AluBranch {
pub fn new(config: PhantomConst<CpuConfig>, unit_index: usize) -> Self {
pub fn new(
config: PhantomConst<CpuConfig>,
unit_index: usize,
filter: &mut impl RenamedMOpFilter,
) -> Self {
Self {
config,
module: alu_branch(config, unit_index),
module: alu_branch(config, unit_index, filter),
}
}
}

View file

@ -317,152 +317,3 @@ impl LFSR31 {
state
}
}
pub struct CheckedVcdOutput {
writer: Option<RcWriter>,
expected_path: PathBuf,
expected_contents: Result<String, (Option<PathBuf>, std::io::Error)>,
location: &'static Location<'static>,
}
impl CheckedVcdOutput {
#[must_use]
#[track_caller]
pub fn new<T: BundleType>(sim: &mut Simulation<T>, expected_path: PathBuf) -> Self {
let writer = RcWriter::default();
sim.add_trace_writer(VcdWriterDecls::new(writer.clone()));
Self {
writer: Some(writer),
expected_contents: std::fs::read_to_string(&expected_path).map_err(|e| {
eprintln!(
"error: failed to read expected VCD from: {}",
expected_path.display(),
);
(std::env::current_dir().ok(), e)
}),
expected_path,
location: Location::caller(),
}
}
#[must_use]
#[track_caller]
#[doc(hidden)]
pub fn __checked_vcd_output_macro_helper<T: BundleType>(
sim: &mut Simulation<T>,
cargo_manifest_dir: &'static str,
path: &'static str,
) -> Self {
Self::new(sim, Path::new(cargo_manifest_dir).join(path))
}
pub fn with_vcd_output<R>(&self, f: impl FnOnce(&str) -> R) -> R {
let Some(writer) = &self.writer else {
unreachable!();
};
writer.clone().borrow(|output| {
let Ok(output) = str::from_utf8(output) else {
unreachable!("VcdWriter writes valid UTF-8");
};
f(output)
})
}
#[track_caller]
pub fn finish(mut self) {
let Ok(()) = self.finish_impl(|msg| panic!("{msg}"));
}
fn finish_impl<E>(
&mut self,
error: impl FnOnce(std::fmt::Arguments<'_>) -> E,
) -> Result<(), E> {
let Self {
writer: Some(writer),
expected_path,
expected_contents,
location,
} = self
else {
// already finished
return Ok(());
};
let Ok(vcd) = String::from_utf8(writer.take()) else {
unreachable!("VcdWriter writes valid UTF-8");
};
let expected_path_d = expected_path.display();
if expected_contents
.as_ref()
.is_ok_and(|expected_contents| *expected_contents == vcd)
{
// avoid written output from being split from threads interleaving writes to stdout
let _stdout = std::io::stderr().lock();
// use println to get output captured by tests
println!("\n{location}: generated VCD matches the expected VCD in {expected_path_d}");
return Ok(());
}
// avoid written output from being split from threads interleaving writes to stderr
let _stderr = std::io::stderr().lock();
let error = |msg: std::fmt::Arguments<'_>| {
// print msg at both beginning and end so it's easier to find when the vcd is huge
Err(error(format_args!(
"\n{msg}####### VCD:\n{vcd}\n#######\n{msg}"
)))
};
let error = |msg: std::fmt::Arguments<'_>| match &*expected_contents {
Ok(_) => error(format_args!(
"{location}: generated VCD doesn't match the expected VCD in {expected_path_d}\n\
{msg}",
)),
Err((Some(current_dir), e)) => error(format_args!(
"{location}: generated VCD doesn't match the expected VCD in {expected_path_d}\n\
error: failed to read: {e}\n\
current dir: {current_dir}\n\
{msg}",
current_dir = current_dir.display(),
)),
Err((None, e)) => error(format_args!(
"{location}: generated VCD doesn't match the expected VCD in {expected_path_d}\n\
error: failed to read: {e}\n\
{msg}",
)),
};
const OVERWRITE_VAR_NAME: &str = "OVERWRITE_EXPECTED_VCD";
const OVERWRITE_VAR_VALUE: &str = "overwrite";
match std::env::var_os(OVERWRITE_VAR_NAME) {
Some(v) if v == OVERWRITE_VAR_VALUE => match std::fs::write(&expected_path, &vcd) {
Ok(()) => error(format_args!(
"warning: since `{OVERWRITE_VAR_NAME}={OVERWRITE_VAR_VALUE}` is set -- writing the generated VCD to {expected_path_d}\n"
)),
Err(e) => error(format_args!(
"error: since `{OVERWRITE_VAR_NAME}={OVERWRITE_VAR_VALUE}` is set -- tried to write the generated VCD to {expected_path_d}\n\
error: failed to write: {e}"
)),
},
_ => error(format_args!(
"note: rerun the test with the environment variable `{OVERWRITE_VAR_NAME}={OVERWRITE_VAR_VALUE}`\n\
to update the expected output to match the generated output.\n"
)),
}
}
}
impl Drop for CheckedVcdOutput {
#[track_caller]
fn drop(&mut self) {
let _ = self.finish_impl(|msg| {
if std::thread::panicking() {
eprintln!("{msg}"); // use eprintln to get output captured by tests
} else {
panic!("{msg}");
}
});
}
}
#[macro_export]
macro_rules! checked_vcd_output {
($sim:expr, $path_relative_to_manifest_dir:expr $(,)?) => {
$crate::util::CheckedVcdOutput::__checked_vcd_output_macro_helper(
$sim,
::std::env!("CARGO_MANIFEST_DIR"),
::std::concat!($path_relative_to_manifest_dir),
)
};
}

View file

@ -226,6 +226,10 @@ impl<T: Type, N: Size> ArrayVec<T, N> {
#[hdl]
let mapped_array_vec = wire(this.ty().mapped_ty(new_element_ty));
connect(mapped_array_vec.len, this.len);
connect(
mapped_array_vec.elements,
mapped_array_vec.ty().elements.uninit(),
);
Self::for_each(this, |index, element| {
connect(mapped_array_vec[index], f(index, element));
});

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -2,7 +2,6 @@
// See Notices.txt for copyright information
use cpu::{
checked_vcd_output,
config::{
CpuConfig, CpuConfigFetchWidth, CpuConfigMaxUnitMaxInFlight, PhantomConstCpuConfig,
UnitConfig,
@ -29,6 +28,7 @@ use cpu::{
};
use fayalite::{
bundle::BundleType,
checked_vcd_output,
module::instance_with_loc,
prelude::*,
ty::{OpaqueSimValue, StaticType},
@ -4079,7 +4079,7 @@ fn rename_execute_retire_test_harness<#[hdl(skip)] MI: MakeInsns>(
AluBranchKind::Real => {
let unit = instance_with_loc(
&dut.ty().to_units.unit_field_name(unit_index),
cpu::unit::alu_branch::alu_branch(config, unit_index),
cpu::unit::alu_branch::alu_branch(config, unit_index, &mut ()),
SourceLocation::caller(),
);
connect(unit.from_execute, to_unit);

File diff suppressed because it is too large Load diff

View file

@ -3,10 +3,9 @@
use crate::test_cases::TestCase;
use cpu::{
checked_vcd_output, decoder::simple_power_isa::decode_one_insn, instruction::MOp,
util::array_vec::ArrayVec,
decoder::simple_power_isa::decode_one_insn, instruction::MOp, util::array_vec::ArrayVec,
};
use fayalite::prelude::*;
use fayalite::{checked_vcd_output, prelude::*};
use std::{fmt::Write as _, io::Write, process::Command};
mod test_cases;
@ -126,7 +125,7 @@ fn test_test_cases_assembly() -> std::io::Result<()> {
#[test]
fn test_decode_insn() {
let _n = SourceLocation::normalize_files_for_tests();
let m = decode_one_insn();
let m = decode_one_insn(());
let mut sim = Simulation::new(m);
let _checked_vcd_output = checked_vcd_output!(
&mut sim,
@ -149,7 +148,7 @@ fn test_decode_insn() {
#[derive(Debug)]
#[expect(dead_code, reason = "used only for Debug formatting")]
struct FormattedOutput<'a> {
insns: &'a [SimValue<MOp>],
insns: &'a [SimValue<TraceAsString<MOp>>],
second_input_used: bool,
is_illegal: bool,
}

View file

@ -16,14 +16,23 @@ mod fixed_point_store;
mod move_to_from_system_register;
mod prefixed_no_operation;
#[hdl(get(|_| 3))]
pub type TestCaseOutputLen<C: PhantomConstGet<()>> = DynSize;
pub struct TestCase {
pub mnemonic: &'static str,
pub first_input: u32,
pub second_input: Option<u32>,
pub output: SimValue<ArrayVec<MOp, ConstUsize<3>>>,
pub output: SimValue<ArrayVec<TraceAsString<MOp>, TestCaseOutputLen<PhantomConst<()>>>>,
pub loc: &'static std::panic::Location<'static>,
}
impl TestCase {
pub fn output_ty() -> ArrayVec<TraceAsString<MOp>, TestCaseOutputLen<PhantomConst<()>>> {
ArrayVec[TraceAsString[MOp]][TestCaseOutputLen[PhantomConst::default()]]
}
}
impl fmt::Debug for TestCase {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self {
@ -49,90 +58,91 @@ impl fmt::Debug for TestCase {
}
}
#[inline(never)] // prevent stack overflow
#[track_caller]
fn insn_empty(mnemonic: &'static str, first_input: u32, second_input: Option<u32>) -> TestCase {
let zero_mop = UInt::new_dyn(MOp.canonical().bit_width())
.zero()
.cast_bits_to(MOp);
.cast_bits_to(MOp)
.into_trace_as_string();
TestCase {
mnemonic,
first_input,
second_input,
output: ArrayVec::new_sim(ArrayVec[MOp][ConstUsize], &zero_mop),
output: TestCase::output_ty().new_sim(zero_mop),
loc: std::panic::Location::caller(),
}
}
#[inline(never)] // prevent stack overflow
#[track_caller]
fn insn_single(
fn insn_single<T: ToSimValue<Type = MOp>>(
mnemonic: &'static str,
first_input: u32,
second_input: Option<u32>,
output: impl ToSimValue<Type = MOp>,
// use a closure to avoid having too many variables in one function causing a stack overflow
output: impl FnOnce() -> T,
) -> TestCase {
let zero_mop = UInt::new_dyn(MOp.canonical().bit_width())
.zero()
.cast_bits_to(MOp);
let mut single_storage = ArrayVec::new_sim(ArrayVec[MOp][ConstUsize], &zero_mop);
ArrayVec::try_push_sim(&mut single_storage, zero_mop).expect("known to have space");
ArrayVec::elements_sim_mut(&mut single_storage)[0] = output.to_sim_value();
TestCase {
mnemonic,
first_input,
second_input,
output: single_storage,
loc: std::panic::Location::caller(),
}
let mut retval = insn_empty(mnemonic, first_input, second_input);
ArrayVec::try_push_sim(
&mut retval.output,
output().into_sim_value().into_trace_as_string(),
)
.expect("known to have space");
retval
}
#[inline(never)] // prevent stack overflow
#[track_caller]
fn insn_double(
fn insn_double<T: ToSimValue<Type = MOp>>(
mnemonic: &'static str,
first_input: u32,
second_input: Option<u32>,
insns: [impl ToSimValue<Type = MOp>; 2],
// use a closure to avoid having too many variables in one function causing a stack overflow
insns: impl FnOnce() -> [T; 2],
) -> TestCase {
let zero_mop = UInt::new_dyn(MOp.canonical().bit_width())
.zero()
.cast_bits_to(MOp);
let mut single_storage = ArrayVec::new_sim(ArrayVec[MOp][ConstUsize], &zero_mop);
ArrayVec::try_push_sim(&mut single_storage, &zero_mop).expect("known to have space");
ArrayVec::try_push_sim(&mut single_storage, zero_mop).expect("known to have space");
ArrayVec::elements_sim_mut(&mut single_storage)[0] = insns[0].to_sim_value();
ArrayVec::elements_sim_mut(&mut single_storage)[1] = insns[1].to_sim_value();
TestCase {
mnemonic,
first_input,
second_input,
output: single_storage,
loc: std::panic::Location::caller(),
}
let mut retval = insn_empty(mnemonic, first_input, second_input);
let [insn0, insn1] = insns();
ArrayVec::try_push_sim(
&mut retval.output,
insn0.into_sim_value().into_trace_as_string(),
)
.expect("known to have space");
ArrayVec::try_push_sim(
&mut retval.output,
insn1.into_sim_value().into_trace_as_string(),
)
.expect("known to have space");
retval
}
#[inline(never)] // prevent stack overflow
#[track_caller]
fn insn_triple(
fn insn_triple<T: ToSimValue<Type = MOp>>(
mnemonic: &'static str,
first_input: u32,
second_input: Option<u32>,
insns: [impl ToSimValue<Type = MOp>; 3],
// use a closure to avoid having too many variables in one function causing a stack overflow
insns: impl FnOnce() -> [T; 3],
) -> TestCase {
let zero_mop = UInt::new_dyn(MOp.canonical().bit_width())
.zero()
.cast_bits_to(MOp);
let mut single_storage = ArrayVec::new_sim(ArrayVec[MOp][ConstUsize], &zero_mop);
ArrayVec::try_push_sim(&mut single_storage, &zero_mop).expect("known to have space");
ArrayVec::try_push_sim(&mut single_storage, &zero_mop).expect("known to have space");
ArrayVec::try_push_sim(&mut single_storage, zero_mop).expect("known to have space");
ArrayVec::elements_sim_mut(&mut single_storage)[0] = insns[0].to_sim_value();
ArrayVec::elements_sim_mut(&mut single_storage)[1] = insns[1].to_sim_value();
ArrayVec::elements_sim_mut(&mut single_storage)[2] = insns[2].to_sim_value();
TestCase {
mnemonic,
first_input,
second_input,
output: single_storage,
loc: std::panic::Location::caller(),
}
let mut retval = insn_empty(mnemonic, first_input, second_input);
let [insn0, insn1, insn2] = insns();
ArrayVec::try_push_sim(
&mut retval.output,
insn0.into_sim_value().into_trace_as_string(),
)
.expect("known to have space");
ArrayVec::try_push_sim(
&mut retval.output,
insn1.into_sim_value().into_trace_as_string(),
)
.expect("known to have space");
ArrayVec::try_push_sim(
&mut retval.output,
insn2.into_sim_value().into_trace_as_string(),
)
.expect("known to have space");
retval
}
pub fn test_cases() -> Vec<TestCase> {

View file

@ -9,10 +9,7 @@ use fayalite::prelude::*;
/// covers instructions in PowerISA v3.1C Book I 2.4 Branch Instructions
pub fn test_cases_book_i_2_4_branch(retval: &mut Vec<TestCase>) {
retval.push(insn_single(
"b 0x345678",
0x48345678,
None,
retval.push(insn_single("b 0x345678", 0x48345678, None, || {
BranchMOp::branch_i(
MOpDestReg::new_sim(&[], &[]),
[MOpRegNum::const_zero(); 2],
@ -20,12 +17,9 @@ pub fn test_cases_book_i_2_4_branch(retval: &mut Vec<TestCase>) {
true,
false,
false,
),
));
retval.push(insn_single(
"ba 0x345678",
0x4834567a,
None,
)
}));
retval.push(insn_single("ba 0x345678", 0x4834567a, None, || {
BranchMOp::branch_i(
MOpDestReg::new_sim(&[], &[]),
[MOpRegNum::const_zero(); 2],
@ -33,12 +27,9 @@ pub fn test_cases_book_i_2_4_branch(retval: &mut Vec<TestCase>) {
false,
false,
false,
),
));
retval.push(insn_single(
"bl 0x345678",
0x48345679,
None,
)
}));
retval.push(insn_single("bl 0x345678", 0x48345679, None, || {
BranchMOp::branch_i(
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_LR_REG_NUM], &[]),
[MOpRegNum::const_zero(); 2],
@ -46,12 +37,9 @@ pub fn test_cases_book_i_2_4_branch(retval: &mut Vec<TestCase>) {
true,
true,
false,
),
));
retval.push(insn_single(
"bla 0x345678",
0x4834567b,
None,
)
}));
retval.push(insn_single("bla 0x345678", 0x4834567b, None, || {
BranchMOp::branch_i(
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_LR_REG_NUM], &[]),
[MOpRegNum::const_zero(); 2],
@ -59,18 +47,15 @@ pub fn test_cases_book_i_2_4_branch(retval: &mut Vec<TestCase>) {
false,
true,
false,
),
));
fn insn_dec_ctr_and(
)
}));
fn insn_dec_ctr_and<T: ToSimValue<Type = MOp>>(
mnemonic: &'static str,
first_input: u32,
second_input: Option<u32>,
second_insn: impl ToSimValue<Type = MOp>,
second_insn: impl FnOnce() -> T,
) -> TestCase {
insn_double(
mnemonic,
first_input,
second_input,
insn_double(mnemonic, first_input, second_input, || {
[
AddSubMOp::add_sub_i::<MOp>(
MOpDestReg::new([MOpRegNum::power_isa_ctr_reg()], []),
@ -83,9 +68,9 @@ pub fn test_cases_book_i_2_4_branch(retval: &mut Vec<TestCase>) {
false,
)
.into_sim_value(),
second_insn.into_sim_value(),
],
)
second_insn().into_sim_value(),
]
})
}
macro_rules! insn_branch_conds {
(
@ -137,255 +122,281 @@ pub fn test_cases_book_i_2_4_branch(retval: &mut Vec<TestCase>) {
concat!($mnemonic, " 0, 0, ", $asm_last_arg),
$encoding,
None,
BranchMOp::branch_cond_ctr(
$dest,
[
MOpRegNum::power_isa_cr_reg_imm(0),
$src1,
MOpRegNum::power_isa_ctr_reg(),
],
$imm.cast_to_static::<SInt<_>>(),
true,
ConditionMode.SLt(),
true,
$pc_relative,
$lk,
$is_ret,
),
|| {
BranchMOp::branch_cond_ctr(
$dest,
[
MOpRegNum::power_isa_cr_reg_imm(0),
$src1,
MOpRegNum::power_isa_ctr_reg(),
],
$imm.cast_to_static::<SInt<_>>(),
true,
ConditionMode.SLt(),
true,
$pc_relative,
$lk,
$is_ret,
)
},
));
retval.push(insn_dec_ctr_and(
concat!($mnemonic, " 0, 1, ", $asm_last_arg),
$encoding | 0x010000,
None,
BranchMOp::branch_cond_ctr(
$dest,
[
MOpRegNum::power_isa_cr_reg_imm(0),
$src1,
MOpRegNum::power_isa_ctr_reg(),
],
$imm.cast_to_static::<SInt<_>>(),
true,
ConditionMode.SGt(),
true,
$pc_relative,
$lk,
$is_ret,
),
|| {
BranchMOp::branch_cond_ctr(
$dest,
[
MOpRegNum::power_isa_cr_reg_imm(0),
$src1,
MOpRegNum::power_isa_ctr_reg(),
],
$imm.cast_to_static::<SInt<_>>(),
true,
ConditionMode.SGt(),
true,
$pc_relative,
$lk,
$is_ret,
)
},
));
retval.push(insn_dec_ctr_and(
concat!($mnemonic, " 0, 2, ", $asm_last_arg),
$encoding | 0x020000,
None,
BranchMOp::branch_cond_ctr(
$dest,
[
MOpRegNum::power_isa_cr_reg_imm(0),
$src1,
MOpRegNum::power_isa_ctr_reg(),
],
$imm.cast_to_static::<SInt<_>>(),
true,
ConditionMode.Eq(),
true,
$pc_relative,
$lk,
$is_ret,
),
|| {
BranchMOp::branch_cond_ctr(
$dest,
[
MOpRegNum::power_isa_cr_reg_imm(0),
$src1,
MOpRegNum::power_isa_ctr_reg(),
],
$imm.cast_to_static::<SInt<_>>(),
true,
ConditionMode.Eq(),
true,
$pc_relative,
$lk,
$is_ret,
)
},
));
retval.push(insn_dec_ctr_and(
concat!($mnemonic, " 0, 3, ", $asm_last_arg),
$encoding | 0x030000,
None,
BranchMOp::branch_cond_ctr(
$dest,
[
MOpRegNum::power_isa_cr_reg_imm(0),
$src1,
MOpRegNum::power_isa_ctr_reg(),
],
$imm.cast_to_static::<SInt<_>>(),
true,
ConditionMode.Overflow(),
true,
$pc_relative,
$lk,
$is_ret,
),
|| {
BranchMOp::branch_cond_ctr(
$dest,
[
MOpRegNum::power_isa_cr_reg_imm(0),
$src1,
MOpRegNum::power_isa_ctr_reg(),
],
$imm.cast_to_static::<SInt<_>>(),
true,
ConditionMode.Overflow(),
true,
$pc_relative,
$lk,
$is_ret,
)
},
));
retval.push(insn_dec_ctr_and(
concat!($mnemonic, " 0, 9, ", $asm_last_arg),
$encoding | 0x090000,
None,
BranchMOp::branch_cond_ctr(
$dest,
[
MOpRegNum::power_isa_cr_reg_imm(2),
$src1,
MOpRegNum::power_isa_ctr_reg(),
],
$imm.cast_to_static::<SInt<_>>(),
true,
ConditionMode.SGt(),
true,
$pc_relative,
$lk,
$is_ret,
),
|| {
BranchMOp::branch_cond_ctr(
$dest,
[
MOpRegNum::power_isa_cr_reg_imm(2),
$src1,
MOpRegNum::power_isa_ctr_reg(),
],
$imm.cast_to_static::<SInt<_>>(),
true,
ConditionMode.SGt(),
true,
$pc_relative,
$lk,
$is_ret,
)
},
));
retval.push(insn_dec_ctr_and(
concat!($mnemonic, " 2, 0, ", $asm_last_arg),
$encoding | (2 << 21),
None,
BranchMOp::branch_cond_ctr(
$dest,
[
MOpRegNum::power_isa_cr_reg_imm(0),
$src1,
MOpRegNum::power_isa_ctr_reg(),
],
$imm.cast_to_static::<SInt<_>>(),
true,
ConditionMode.SLt(),
false,
$pc_relative,
$lk,
$is_ret,
),
|| {
BranchMOp::branch_cond_ctr(
$dest,
[
MOpRegNum::power_isa_cr_reg_imm(0),
$src1,
MOpRegNum::power_isa_ctr_reg(),
],
$imm.cast_to_static::<SInt<_>>(),
true,
ConditionMode.SLt(),
false,
$pc_relative,
$lk,
$is_ret,
)
},
));
}
retval.push(insn_single(
concat!($mnemonic, " 4, 0, ", $asm_last_arg),
$encoding | (4 << 21),
None,
BranchMOp::branch_cond_ctr(
$dest,
[
MOpRegNum::power_isa_cr_reg_imm(0),
$src1,
MOpRegNum::const_zero(),
],
$imm.cast_to_static::<SInt<_>>(),
true,
ConditionMode.SLt(),
true,
$pc_relative,
$lk,
$is_ret,
),
|| {
BranchMOp::branch_cond_ctr(
$dest,
[
MOpRegNum::power_isa_cr_reg_imm(0),
$src1,
MOpRegNum::const_zero(),
],
$imm.cast_to_static::<SInt<_>>(),
true,
ConditionMode.SLt(),
true,
$pc_relative,
$lk,
$is_ret,
)
},
));
if !$mnemonic.starts_with("bcctr") {
retval.push(insn_dec_ctr_and(
concat!($mnemonic, " 8, 0, ", $asm_last_arg),
$encoding | (8 << 21),
None,
BranchMOp::branch_cond_ctr(
$dest,
[
MOpRegNum::power_isa_cr_reg_imm(0),
$src1,
MOpRegNum::power_isa_ctr_reg(),
],
$imm.cast_to_static::<SInt<_>>(),
false,
ConditionMode.SLt(),
true,
$pc_relative,
$lk,
$is_ret,
),
|| {
BranchMOp::branch_cond_ctr(
$dest,
[
MOpRegNum::power_isa_cr_reg_imm(0),
$src1,
MOpRegNum::power_isa_ctr_reg(),
],
$imm.cast_to_static::<SInt<_>>(),
false,
ConditionMode.SLt(),
true,
$pc_relative,
$lk,
$is_ret,
)
},
));
retval.push(insn_dec_ctr_and(
concat!($mnemonic, " 10, 0, ", $asm_last_arg),
$encoding | (10 << 21),
None,
BranchMOp::branch_cond_ctr(
$dest,
[
MOpRegNum::power_isa_cr_reg_imm(0),
$src1,
MOpRegNum::power_isa_ctr_reg(),
],
$imm.cast_to_static::<SInt<_>>(),
false,
ConditionMode.SLt(),
false,
$pc_relative,
$lk,
$is_ret,
),
|| {
BranchMOp::branch_cond_ctr(
$dest,
[
MOpRegNum::power_isa_cr_reg_imm(0),
$src1,
MOpRegNum::power_isa_ctr_reg(),
],
$imm.cast_to_static::<SInt<_>>(),
false,
ConditionMode.SLt(),
false,
$pc_relative,
$lk,
$is_ret,
)
},
));
}
retval.push(insn_single(
concat!($mnemonic, " 12, 0, ", $asm_last_arg),
$encoding | (12 << 21),
None,
BranchMOp::branch_cond_ctr(
$dest,
[
MOpRegNum::power_isa_cr_reg_imm(0),
$src1,
MOpRegNum::const_zero(),
],
$imm.cast_to_static::<SInt<_>>(),
false,
ConditionMode.SLt(),
true,
$pc_relative,
$lk,
$is_ret,
),
|| {
BranchMOp::branch_cond_ctr(
$dest,
[
MOpRegNum::power_isa_cr_reg_imm(0),
$src1,
MOpRegNum::const_zero(),
],
$imm.cast_to_static::<SInt<_>>(),
false,
ConditionMode.SLt(),
true,
$pc_relative,
$lk,
$is_ret,
)
},
));
if !$mnemonic.starts_with("bcctr") {
retval.push(insn_dec_ctr_and(
concat!($mnemonic, " 16, 0, ", $asm_last_arg),
$encoding | (16 << 21),
None,
BranchMOp::branch_ctr(
$dest,
[
MOpRegNum::const_zero(),
$src1,
MOpRegNum::power_isa_ctr_reg(),
],
$imm.cast_to_static::<SInt<_>>(),
true,
$pc_relative,
$lk,
$is_ret,
),
|| {
BranchMOp::branch_ctr(
$dest,
[
MOpRegNum::const_zero(),
$src1,
MOpRegNum::power_isa_ctr_reg(),
],
$imm.cast_to_static::<SInt<_>>(),
true,
$pc_relative,
$lk,
$is_ret,
)
},
));
retval.push(insn_dec_ctr_and(
concat!($mnemonic, " 18, 0, ", $asm_last_arg),
$encoding | (18 << 21),
None,
BranchMOp::branch_ctr(
$dest,
[
MOpRegNum::const_zero(),
$src1,
MOpRegNum::power_isa_ctr_reg(),
],
$imm.cast_to_static::<SInt<_>>(),
false,
$pc_relative,
$lk,
$is_ret,
),
|| {
BranchMOp::branch_ctr(
$dest,
[
MOpRegNum::const_zero(),
$src1,
MOpRegNum::power_isa_ctr_reg(),
],
$imm.cast_to_static::<SInt<_>>(),
false,
$pc_relative,
$lk,
$is_ret,
)
},
));
}
retval.push(insn_single(
concat!($mnemonic, " 20, 0, ", $asm_last_arg),
$encoding | (20 << 21),
None,
BranchMOp::branch_i(
$dest,
[MOpRegNum::const_zero(), $src1],
$imm.cast_to_static::<SInt<_>>(),
$pc_relative,
$lk,
$is_ret,
),
|| {
BranchMOp::branch_i(
$dest,
[MOpRegNum::const_zero(), $src1],
$imm.cast_to_static::<SInt<_>>(),
$pc_relative,
$lk,
$is_ret,
)
},
));
};
}
@ -435,18 +446,20 @@ pub fn test_cases_book_i_2_4_branch(retval: &mut Vec<TestCase>) {
".long 0x4e400461 # bctarl 18, 0, 0",
0x4e400461,
None,
BranchMOp::branch_ctr(
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_LR_REG_NUM], &[]),
[
MOpRegNum::const_zero(),
MOpRegNum::power_isa_tar_reg(),
MOpRegNum::power_isa_ctr_reg(),
],
0.cast_to_static::<SInt<_>>(),
false,
false,
true,
false,
),
|| {
BranchMOp::branch_ctr(
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_LR_REG_NUM], &[]),
[
MOpRegNum::const_zero(),
MOpRegNum::power_isa_tar_reg(),
MOpRegNum::power_isa_ctr_reg(),
],
0.cast_to_static::<SInt<_>>(),
false,
false,
true,
false,
)
},
));
}

View file

@ -20,58 +20,70 @@ pub fn test_cases_book_i_2_5_condition_register(retval: &mut Vec<TestCase>) {
concat!($mnemonic, " 4*cr3+so, 4*cr1+gt, 4*cr5+lt"),
$encoding | 0x01e5a000,
None,
LogicalFlagsMOp::logical_flags(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]),
[
MOpRegNum::power_isa_cr_reg_imm(1),
MOpRegNum::power_isa_cr_reg_imm(5),
MOpRegNum::power_isa_cr_reg_imm(3),
],
LogicalFlagsMOpImm::from_swizzle_fn::<PRegFlagsPowerISA>(|src0, src1, src2| {
let mut dest = src2.map(|v| Some(v.into()));
dest.so = Some((src0.cr_gt, src1.cr_lt).into());
dest
}),
$lut,
),
|| {
LogicalFlagsMOp::logical_flags(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]),
[
MOpRegNum::power_isa_cr_reg_imm(1),
MOpRegNum::power_isa_cr_reg_imm(5),
MOpRegNum::power_isa_cr_reg_imm(3),
],
LogicalFlagsMOpImm::from_swizzle_fn::<PRegFlagsPowerISA>(
|src0, src1, src2| {
let mut dest = src2.map(|v| Some(v.into()));
dest.so = Some((src0.cr_gt, src1.cr_lt).into());
dest
},
),
$lut,
)
},
));
retval.push(insn_single(
concat!($mnemonic, " lt, gt, eq"),
$encoding | 0x00011000,
None,
LogicalFlagsMOp::logical_flags(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(0)], &[]),
[
MOpRegNum::power_isa_cr_reg_imm(0),
MOpRegNum::power_isa_cr_reg_imm(0),
MOpRegNum::power_isa_cr_reg_imm(0),
],
LogicalFlagsMOpImm::from_swizzle_fn::<PRegFlagsPowerISA>(|src0, src1, src2| {
let mut dest = src2.map(|v| Some(v.into()));
dest.cr_lt = Some((src0.cr_gt, src1.cr_eq).into());
dest
}),
$lut,
),
|| {
LogicalFlagsMOp::logical_flags(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(0)], &[]),
[
MOpRegNum::power_isa_cr_reg_imm(0),
MOpRegNum::power_isa_cr_reg_imm(0),
MOpRegNum::power_isa_cr_reg_imm(0),
],
LogicalFlagsMOpImm::from_swizzle_fn::<PRegFlagsPowerISA>(
|src0, src1, src2| {
let mut dest = src2.map(|v| Some(v.into()));
dest.cr_lt = Some((src0.cr_gt, src1.cr_eq).into());
dest
},
),
$lut,
)
},
));
retval.push(insn_single(
concat!($mnemonic, " gt, gt, eq"),
$encoding | 0x00211000,
None,
LogicalFlagsMOp::logical_flags(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(0)], &[]),
[
MOpRegNum::power_isa_cr_reg_imm(0),
MOpRegNum::power_isa_cr_reg_imm(0),
MOpRegNum::power_isa_cr_reg_imm(0),
],
LogicalFlagsMOpImm::from_swizzle_fn::<PRegFlagsPowerISA>(|src0, src1, src2| {
let mut dest = src2.map(|v| Some(v.into()));
dest.cr_gt = Some((src0.cr_gt, src1.cr_eq).into());
dest
}),
$lut,
),
|| {
LogicalFlagsMOp::logical_flags(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(0)], &[]),
[
MOpRegNum::power_isa_cr_reg_imm(0),
MOpRegNum::power_isa_cr_reg_imm(0),
MOpRegNum::power_isa_cr_reg_imm(0),
],
LogicalFlagsMOpImm::from_swizzle_fn::<PRegFlagsPowerISA>(
|src0, src1, src2| {
let mut dest = src2.map(|v| Some(v.into()));
dest.cr_gt = Some((src0.cr_gt, src1.cr_eq).into());
dest
},
),
$lut,
)
},
));
}};
}
@ -89,11 +101,13 @@ pub fn test_cases_book_i_2_5_condition_register(retval: &mut Vec<TestCase>) {
concat!("mcrf ", $dest, ", ", $src),
$encoding,
None,
MoveRegMOp::move_reg(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num($dest)], &[]),
[MOpRegNum::power_isa_cr_reg_imm($src)],
0i8.cast_to_static::<SInt<_>>(),
),
|| {
MoveRegMOp::move_reg(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num($dest)], &[]),
[MOpRegNum::power_isa_cr_reg_imm($src)],
0i8.cast_to_static::<SInt<_>>(),
)
},
));
};
}

View file

@ -7,10 +7,7 @@ use fayalite::prelude::*;
/// covers instructions in PowerISA v3.1C Book I 3.3.9 Fixed-Point Arithmetic Instructions
pub fn test_cases_book_i_3_3_9_fixed_point_arithmetic(retval: &mut Vec<TestCase>) {
retval.push(insn_single(
"addi 3, 4, 0x1234",
0x38641234,
None,
retval.push(insn_single("addi 3, 4, 0x1234", 0x38641234, None, || {
AddSubMOp::add_sub_i(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]),
[MOpRegNum::power_isa_gpr_reg_imm(4), MOpRegNum::const_zero()],
@ -20,42 +17,43 @@ pub fn test_cases_book_i_3_3_9_fixed_point_arithmetic(retval: &mut Vec<TestCase>
false,
false,
false,
),
));
)
}));
retval.push(insn_single(
"paddi 3, 4, 0x123456789, 0",
0x06012345,
Some(0x38646789),
AddSubMOp::add_sub_i(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]),
[MOpRegNum::power_isa_gpr_reg_imm(4), MOpRegNum::const_zero()],
0x123456789i64.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
false,
),
|| {
AddSubMOp::add_sub_i(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]),
[MOpRegNum::power_isa_gpr_reg_imm(4), MOpRegNum::const_zero()],
0x123456789i64.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
false,
)
},
));
retval.push(insn_single(
"paddi 3, 0, 0x123456789, 1",
0x06112345,
Some(0x38606789),
AddSubMOp::add_sub_i(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]),
[MOpRegNum::const_zero(), MOpRegNum::const_zero()],
0x123456789i64.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
true,
),
|| {
AddSubMOp::add_sub_i(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]),
[MOpRegNum::const_zero(), MOpRegNum::const_zero()],
0x123456789i64.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
true,
)
},
));
retval.push(insn_single(
"addis 3, 4, 0x1234",
0x3C641234,
None,
retval.push(insn_single("addis 3, 4, 0x1234", 0x3C641234, None, || {
AddSubMOp::add_sub_i(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]),
[MOpRegNum::power_isa_gpr_reg_imm(4), MOpRegNum::const_zero()],
@ -65,12 +63,9 @@ pub fn test_cases_book_i_3_3_9_fixed_point_arithmetic(retval: &mut Vec<TestCase>
false,
false,
false,
),
));
retval.push(insn_single(
"addpcis 3, 0x1234",
0x4c7a1204,
None,
)
}));
retval.push(insn_single("addpcis 3, 0x1234", 0x4c7a1204, None, || {
AddSubMOp::add_sub_i(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]),
[MOpRegNum::const_zero(); _],
@ -80,12 +75,9 @@ pub fn test_cases_book_i_3_3_9_fixed_point_arithmetic(retval: &mut Vec<TestCase>
false,
false,
true,
),
));
retval.push(insn_single(
"add. 3, 4, 5",
0x7c642a15,
None,
)
}));
retval.push(insn_single("add. 3, 4, 5", 0x7c642a15, None, || {
AddSubMOp::add_sub(
MOpDestReg::new_sim(
&[MOpRegNum::power_isa_gpr_reg_num(3)],
@ -102,12 +94,9 @@ pub fn test_cases_book_i_3_3_9_fixed_point_arithmetic(retval: &mut Vec<TestCase>
false,
false,
false,
),
));
retval.push(insn_single(
"addic. 3, 4, 0x1234",
0x34641234,
None,
)
}));
retval.push(insn_single("addic. 3, 4, 0x1234", 0x34641234, None, || {
AddSubMOp::add_sub_i(
MOpDestReg::new_sim(
&[
@ -123,12 +112,9 @@ pub fn test_cases_book_i_3_3_9_fixed_point_arithmetic(retval: &mut Vec<TestCase>
false,
false,
false,
),
));
retval.push(insn_single(
"subf. 3, 4, 5",
0x7c642851,
None,
)
}));
retval.push(insn_single("subf. 3, 4, 5", 0x7c642851, None, || {
AddSubMOp::add_sub(
MOpDestReg::new_sim(
&[MOpRegNum::power_isa_gpr_reg_num(3)],
@ -145,12 +131,9 @@ pub fn test_cases_book_i_3_3_9_fixed_point_arithmetic(retval: &mut Vec<TestCase>
false,
true,
false,
),
));
retval.push(insn_single(
"subfic 3, 4, 0x1234",
0x20641234,
None,
)
}));
retval.push(insn_single("subfic 3, 4, 0x1234", 0x20641234, None, || {
AddSubMOp::add_sub_i(
MOpDestReg::new_sim(
&[
@ -166,12 +149,9 @@ pub fn test_cases_book_i_3_3_9_fixed_point_arithmetic(retval: &mut Vec<TestCase>
false,
true,
false,
),
));
retval.push(insn_single(
"addc. 3, 4, 5",
0x7c642815,
None,
)
}));
retval.push(insn_single("addc. 3, 4, 5", 0x7c642815, None, || {
AddSubMOp::add_sub(
MOpDestReg::new_sim(
&[
@ -191,12 +171,9 @@ pub fn test_cases_book_i_3_3_9_fixed_point_arithmetic(retval: &mut Vec<TestCase>
false,
false,
false,
),
));
retval.push(insn_single(
"subfc. 3, 4, 5",
0x7c642811,
None,
)
}));
retval.push(insn_single("subfc. 3, 4, 5", 0x7c642811, None, || {
AddSubMOp::add_sub(
MOpDestReg::new_sim(
&[
@ -216,12 +193,9 @@ pub fn test_cases_book_i_3_3_9_fixed_point_arithmetic(retval: &mut Vec<TestCase>
false,
true,
false,
),
));
retval.push(insn_single(
"adde. 3, 4, 5",
0x7c642915,
None,
)
}));
retval.push(insn_single("adde. 3, 4, 5", 0x7c642915, None, || {
AddSubMOp::add_sub(
MOpDestReg::new_sim(
&[
@ -241,12 +215,9 @@ pub fn test_cases_book_i_3_3_9_fixed_point_arithmetic(retval: &mut Vec<TestCase>
true,
false,
false,
),
));
retval.push(insn_single(
"subfe. 3, 4, 5",
0x7c642911,
None,
)
}));
retval.push(insn_single("subfe. 3, 4, 5", 0x7c642911, None, || {
AddSubMOp::add_sub(
MOpDestReg::new_sim(
&[
@ -266,12 +237,9 @@ pub fn test_cases_book_i_3_3_9_fixed_point_arithmetic(retval: &mut Vec<TestCase>
true,
false,
false,
),
));
retval.push(insn_single(
"addme. 3, 4",
0x7c6401d5,
None,
)
}));
retval.push(insn_single("addme. 3, 4", 0x7c6401d5, None, || {
AddSubMOp::add_sub(
MOpDestReg::new_sim(
&[
@ -291,12 +259,9 @@ pub fn test_cases_book_i_3_3_9_fixed_point_arithmetic(retval: &mut Vec<TestCase>
true,
false,
false,
),
));
retval.push(insn_single(
"subfme. 3, 4",
0x7c6401d1,
None,
)
}));
retval.push(insn_single("subfme. 3, 4", 0x7c6401d1, None, || {
AddSubMOp::add_sub(
MOpDestReg::new_sim(
&[
@ -316,12 +281,9 @@ pub fn test_cases_book_i_3_3_9_fixed_point_arithmetic(retval: &mut Vec<TestCase>
true,
false,
false,
),
));
retval.push(insn_single(
"addze. 3, 4",
0x7c640195,
None,
)
}));
retval.push(insn_single("addze. 3, 4", 0x7c640195, None, || {
AddSubMOp::add_sub(
MOpDestReg::new_sim(
&[
@ -341,12 +303,9 @@ pub fn test_cases_book_i_3_3_9_fixed_point_arithmetic(retval: &mut Vec<TestCase>
true,
false,
false,
),
));
retval.push(insn_single(
"subfze. 3, 4",
0x7c640191,
None,
)
}));
retval.push(insn_single("subfze. 3, 4", 0x7c640191, None, || {
AddSubMOp::add_sub(
MOpDestReg::new_sim(
&[
@ -366,12 +325,9 @@ pub fn test_cases_book_i_3_3_9_fixed_point_arithmetic(retval: &mut Vec<TestCase>
true,
false,
false,
),
));
retval.push(insn_single(
"neg. 3, 4",
0x7c6400d1,
None,
)
}));
retval.push(insn_single("neg. 3, 4", 0x7c6400d1, None, || {
AddSubMOp::add_sub(
MOpDestReg::new_sim(
&[MOpRegNum::power_isa_gpr_reg_num(3)],
@ -388,6 +344,6 @@ pub fn test_cases_book_i_3_3_9_fixed_point_arithmetic(retval: &mut Vec<TestCase>
false,
true,
false,
),
));
)
}));
}

View file

@ -11,28 +11,29 @@ pub fn test_cases_book_i_3_3_10_fixed_point_compare(retval: &mut Vec<TestCase>)
"cmpi 3, 0, 4, 0x1234",
0x2d841234,
None,
CompareMOp::compare_i(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]),
[MOpRegNum::power_isa_gpr_reg_imm(4)],
0x1234.cast_to_static::<SInt<_>>(),
CompareMode.S32(),
),
|| {
CompareMOp::compare_i(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]),
[MOpRegNum::power_isa_gpr_reg_imm(4)],
0x1234.cast_to_static::<SInt<_>>(),
CompareMode.S32(),
)
},
));
retval.push(insn_single(
"cmpi 3, 1, 4, -0x7655",
0x2da489ab,
None,
CompareMOp::compare_i(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]),
[MOpRegNum::power_isa_gpr_reg_imm(4)],
(0x89abu16 as i16).cast_to_static::<SInt<_>>(),
CompareMode.S64(),
),
|| {
CompareMOp::compare_i(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]),
[MOpRegNum::power_isa_gpr_reg_imm(4)],
(0x89abu16 as i16).cast_to_static::<SInt<_>>(),
CompareMode.S64(),
)
},
));
retval.push(insn_single(
"cmp 3, 0, 4, 5",
0x7d842800,
None,
retval.push(insn_single("cmp 3, 0, 4, 5", 0x7d842800, None, || {
CompareMOp::compare(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]),
[
@ -40,12 +41,9 @@ pub fn test_cases_book_i_3_3_10_fixed_point_compare(retval: &mut Vec<TestCase>)
MOpRegNum::power_isa_gpr_reg_imm(5),
],
CompareMode.S32(),
),
));
retval.push(insn_single(
"cmp 3, 1, 4, 5",
0x7da42800,
None,
)
}));
retval.push(insn_single("cmp 3, 1, 4, 5", 0x7da42800, None, || {
CompareMOp::compare(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]),
[
@ -53,34 +51,35 @@ pub fn test_cases_book_i_3_3_10_fixed_point_compare(retval: &mut Vec<TestCase>)
MOpRegNum::power_isa_gpr_reg_imm(5),
],
CompareMode.S64(),
),
));
)
}));
retval.push(insn_single(
"cmpli 3, 0, 4, 0x1234",
0x29841234,
None,
CompareMOp::compare_i(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]),
[MOpRegNum::power_isa_gpr_reg_imm(4)],
0x1234.cast_to_static::<SInt<_>>(),
CompareMode.U32(),
),
|| {
CompareMOp::compare_i(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]),
[MOpRegNum::power_isa_gpr_reg_imm(4)],
0x1234.cast_to_static::<SInt<_>>(),
CompareMode.U32(),
)
},
));
retval.push(insn_single(
"cmpli 3, 1, 4, 0x89ab",
0x29a489ab,
None,
CompareMOp::compare_i(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]),
[MOpRegNum::power_isa_gpr_reg_imm(4)],
0x89ab.cast_to_static::<SInt<_>>(),
CompareMode.U64(),
),
|| {
CompareMOp::compare_i(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]),
[MOpRegNum::power_isa_gpr_reg_imm(4)],
0x89ab.cast_to_static::<SInt<_>>(),
CompareMode.U64(),
)
},
));
retval.push(insn_single(
"cmpl 3, 0, 4, 5",
0x7d842840,
None,
retval.push(insn_single("cmpl 3, 0, 4, 5", 0x7d842840, None, || {
CompareMOp::compare(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]),
[
@ -88,12 +87,9 @@ pub fn test_cases_book_i_3_3_10_fixed_point_compare(retval: &mut Vec<TestCase>)
MOpRegNum::power_isa_gpr_reg_imm(5),
],
CompareMode.U32(),
),
));
retval.push(insn_single(
"cmpl 3, 1, 4, 5",
0x7da42840,
None,
)
}));
retval.push(insn_single("cmpl 3, 1, 4, 5", 0x7da42840, None, || {
CompareMOp::compare(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]),
[
@ -101,12 +97,9 @@ pub fn test_cases_book_i_3_3_10_fixed_point_compare(retval: &mut Vec<TestCase>)
MOpRegNum::power_isa_gpr_reg_imm(5),
],
CompareMode.U64(),
),
));
retval.push(insn_single(
"cmprb 3, 0, 4, 5",
0x7d842980,
None,
)
}));
retval.push(insn_single("cmprb 3, 0, 4, 5", 0x7d842980, None, || {
CompareMOp::compare(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]),
[
@ -114,12 +107,9 @@ pub fn test_cases_book_i_3_3_10_fixed_point_compare(retval: &mut Vec<TestCase>)
MOpRegNum::power_isa_gpr_reg_imm(5),
],
CompareMode.CmpRBOne(),
),
));
retval.push(insn_single(
"cmprb 3, 1, 4, 5",
0x7da42980,
None,
)
}));
retval.push(insn_single("cmprb 3, 1, 4, 5", 0x7da42980, None, || {
CompareMOp::compare(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]),
[
@ -127,12 +117,9 @@ pub fn test_cases_book_i_3_3_10_fixed_point_compare(retval: &mut Vec<TestCase>)
MOpRegNum::power_isa_gpr_reg_imm(5),
],
CompareMode.CmpRBTwo(),
),
));
retval.push(insn_single(
"cmpeqb 3, 4, 5",
0x7d8429c0,
None,
)
}));
retval.push(insn_single("cmpeqb 3, 4, 5", 0x7d8429c0, None, || {
CompareMOp::compare(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]),
[
@ -140,6 +127,6 @@ pub fn test_cases_book_i_3_3_10_fixed_point_compare(retval: &mut Vec<TestCase>)
MOpRegNum::power_isa_gpr_reg_imm(5),
],
CompareMode.CmpEqB(),
),
));
)
}));
}

View file

@ -21,31 +21,33 @@ pub fn test_cases_book_i_3_3_2_fixed_point_load(retval: &mut Vec<TestCase>) {
concat!($mnemonic, " ", $dest, ", ", $disp, "(", $ra, "), ", $r),
$prefix,
Some($suffix),
[
AddSubMOp::add_sub_i(
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_TEMP_REG_NUM], &[]),
[
if $r != 0 || $ra == 0 {
MOpRegNum::const_zero()
} else {
MOpRegNum::power_isa_gpr_reg_imm($ra)
},
MOpRegNum::const_zero(),
],
($disp as i64).cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
$r != 0,
),
LoadMOp::load(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num($dest)], &[]),
[MOpRegNum::power_isa_temp_reg()],
LoadStoreWidth.$width(),
LoadStoreConversion.$conversion(),
),
],
|| {
[
AddSubMOp::add_sub_i(
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_TEMP_REG_NUM], &[]),
[
if $r != 0 || $ra == 0 {
MOpRegNum::const_zero()
} else {
MOpRegNum::power_isa_gpr_reg_imm($ra)
},
MOpRegNum::const_zero(),
],
($disp as i64).cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
$r != 0,
),
LoadMOp::load(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num($dest)], &[]),
[MOpRegNum::power_isa_temp_reg()],
LoadStoreWidth.$width(),
LoadStoreConversion.$conversion(),
),
]
},
));
};
}
@ -60,31 +62,33 @@ pub fn test_cases_book_i_3_3_2_fixed_point_load(retval: &mut Vec<TestCase>) {
concat!($mnemonic, " ", $dest, ", ", $disp, "(", $ra, ")"),
$encoding,
None,
[
AddSubMOp::add_sub_i(
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_TEMP_REG_NUM], &[]),
[
if $ra == 0 {
MOpRegNum::const_zero()
} else {
MOpRegNum::power_isa_gpr_reg_imm($ra)
},
MOpRegNum::const_zero(),
],
($disp as i64).cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
false,
),
LoadMOp::load(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num($dest)], &[]),
[MOpRegNum::power_isa_temp_reg()],
LoadStoreWidth.$width(),
LoadStoreConversion.$conversion(),
),
],
|| {
[
AddSubMOp::add_sub_i(
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_TEMP_REG_NUM], &[]),
[
if $ra == 0 {
MOpRegNum::const_zero()
} else {
MOpRegNum::power_isa_gpr_reg_imm($ra)
},
MOpRegNum::const_zero(),
],
($disp as i64).cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
false,
),
LoadMOp::load(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num($dest)], &[]),
[MOpRegNum::power_isa_temp_reg()],
LoadStoreWidth.$width(),
LoadStoreConversion.$conversion(),
),
]
},
));
};
}
@ -99,27 +103,29 @@ pub fn test_cases_book_i_3_3_2_fixed_point_load(retval: &mut Vec<TestCase>) {
concat!($mnemonic, " ", $dest, ", ", $disp, "(", $ra, ")"),
$encoding,
None,
[
AddSubMOp::add_sub_i(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num($ra)], &[]),
[
MOpRegNum::power_isa_gpr_reg_imm($ra),
MOpRegNum::const_zero(),
],
($disp as i64).cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
false,
),
LoadMOp::load(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num($dest)], &[]),
[MOpRegNum::power_isa_gpr_reg_imm($ra)],
LoadStoreWidth.$width(),
LoadStoreConversion.$conversion(),
),
],
|| {
[
AddSubMOp::add_sub_i(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num($ra)], &[]),
[
MOpRegNum::power_isa_gpr_reg_imm($ra),
MOpRegNum::const_zero(),
],
($disp as i64).cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
false,
),
LoadMOp::load(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num($dest)], &[]),
[MOpRegNum::power_isa_gpr_reg_imm($ra)],
LoadStoreWidth.$width(),
LoadStoreConversion.$conversion(),
),
]
},
));
};
}
@ -134,31 +140,33 @@ pub fn test_cases_book_i_3_3_2_fixed_point_load(retval: &mut Vec<TestCase>) {
concat!($mnemonic, " ", $dest, ", ", $ra, ", ", $rb),
$encoding,
None,
[
AddSubMOp::add_sub_i(
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_TEMP_REG_NUM], &[]),
[
if $ra == 0 {
MOpRegNum::const_zero()
} else {
MOpRegNum::power_isa_gpr_reg_imm($ra)
},
MOpRegNum::power_isa_gpr_reg_imm($rb),
],
0.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
false,
),
LoadMOp::load(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num($dest)], &[]),
[MOpRegNum::power_isa_temp_reg()],
LoadStoreWidth.$width(),
LoadStoreConversion.$conversion(),
),
],
|| {
[
AddSubMOp::add_sub_i(
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_TEMP_REG_NUM], &[]),
[
if $ra == 0 {
MOpRegNum::const_zero()
} else {
MOpRegNum::power_isa_gpr_reg_imm($ra)
},
MOpRegNum::power_isa_gpr_reg_imm($rb),
],
0.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
false,
),
LoadMOp::load(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num($dest)], &[]),
[MOpRegNum::power_isa_temp_reg()],
LoadStoreWidth.$width(),
LoadStoreConversion.$conversion(),
),
]
},
));
};
}
@ -173,27 +181,29 @@ pub fn test_cases_book_i_3_3_2_fixed_point_load(retval: &mut Vec<TestCase>) {
concat!($mnemonic, " ", $dest, ", ", $ra, ", ", $rb),
$encoding,
None,
[
AddSubMOp::add_sub_i(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num($ra)], &[]),
[
MOpRegNum::power_isa_gpr_reg_imm($ra),
MOpRegNum::power_isa_gpr_reg_imm($rb),
],
0.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
false,
),
LoadMOp::load(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num($dest)], &[]),
[MOpRegNum::power_isa_gpr_reg_imm($ra)],
LoadStoreWidth.$width(),
LoadStoreConversion.$conversion(),
),
],
|| {
[
AddSubMOp::add_sub_i(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num($ra)], &[]),
[
MOpRegNum::power_isa_gpr_reg_imm($ra),
MOpRegNum::power_isa_gpr_reg_imm($rb),
],
0.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
false,
),
LoadMOp::load(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num($dest)], &[]),
[MOpRegNum::power_isa_gpr_reg_imm($ra)],
LoadStoreWidth.$width(),
LoadStoreConversion.$conversion(),
),
]
},
));
};
}

View file

@ -25,21 +25,23 @@ pub fn test_cases_book_i_3_3_13_fixed_point_logical(retval: &mut Vec<TestCase>)
),
$encoding,
None,
LogicalMOp::logical_i(
MOpDestReg::new_sim(
&[MOpRegNum::power_isa_gpr_reg_num($dest)],
if $mnemonic.contains('.') {
&[MOpRegNum::POWER_ISA_CR_0_REG_NUM]
} else {
&[]
},
),
[MOpRegNum::power_isa_gpr_reg_imm($src)],
(($imm as u32) << if $mnemonic.contains('s') { 16 } else { 0 })
.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
Lut4::from_fn(|$a, $b| $lut_fn),
),
|| {
LogicalMOp::logical_i(
MOpDestReg::new_sim(
&[MOpRegNum::power_isa_gpr_reg_num($dest)],
if $mnemonic.contains('.') {
&[MOpRegNum::POWER_ISA_CR_0_REG_NUM]
} else {
&[]
},
),
[MOpRegNum::power_isa_gpr_reg_imm($src)],
(($imm as u32) << if $mnemonic.contains('s') { 16 } else { 0 })
.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
Lut4::from_fn(|$a, $b| $lut_fn),
)
},
));
};
}
@ -98,23 +100,25 @@ pub fn test_cases_book_i_3_3_13_fixed_point_logical(retval: &mut Vec<TestCase>)
),
$encoding,
None,
LogicalMOp::logical(
MOpDestReg::new_sim(
&[MOpRegNum::power_isa_gpr_reg_num($dest)],
if $mnemonic.contains('.') {
&[MOpRegNum::POWER_ISA_CR_0_REG_NUM]
} else {
&[]
},
),
[
MOpRegNum::power_isa_gpr_reg_imm($src0),
MOpRegNum::power_isa_gpr_reg_imm($src1),
],
0.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
Lut4::from_fn(|$a, $b| $lut_fn),
),
|| {
LogicalMOp::logical(
MOpDestReg::new_sim(
&[MOpRegNum::power_isa_gpr_reg_num($dest)],
if $mnemonic.contains('.') {
&[MOpRegNum::POWER_ISA_CR_0_REG_NUM]
} else {
&[]
},
),
[
MOpRegNum::power_isa_gpr_reg_imm($src0),
MOpRegNum::power_isa_gpr_reg_imm($src1),
],
0.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
Lut4::from_fn(|$a, $b| $lut_fn),
)
},
));
};
}
@ -157,11 +161,13 @@ pub fn test_cases_book_i_3_3_13_fixed_point_logical(retval: &mut Vec<TestCase>)
"or 3, 4, 4", // mr 3, 4
0x7c832378,
None,
MoveRegMOp::move_reg(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]),
[MOpRegNum::power_isa_gpr_reg_imm(4)],
0.cast_to_static::<SInt<_>>(),
),
|| {
MoveRegMOp::move_reg(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]),
[MOpRegNum::power_isa_gpr_reg_imm(4)],
0.cast_to_static::<SInt<_>>(),
)
},
));
insn_logic! {
"or." 3, 4, 5;
@ -223,20 +229,22 @@ pub fn test_cases_book_i_3_3_13_fixed_point_logical(retval: &mut Vec<TestCase>)
concat!($mnemonic, " ", stringify!($dest), ", ", stringify!($src)),
$encoding,
None,
LogicalMOp::logical_i(
MOpDestReg::new_sim(
&[MOpRegNum::power_isa_gpr_reg_num($dest)],
if $mnemonic.contains('.') {
&[MOpRegNum::POWER_ISA_CR_0_REG_NUM]
} else {
&[]
},
),
[MOpRegNum::power_isa_gpr_reg_imm($src)],
0.cast_to_static::<SInt<_>>(),
OutputIntegerMode.$OutputIntegerMode(),
Lut4::from_fn(|a, b| a | b),
),
|| {
LogicalMOp::logical_i(
MOpDestReg::new_sim(
&[MOpRegNum::power_isa_gpr_reg_num($dest)],
if $mnemonic.contains('.') {
&[MOpRegNum::POWER_ISA_CR_0_REG_NUM]
} else {
&[]
},
),
[MOpRegNum::power_isa_gpr_reg_imm($src)],
0.cast_to_static::<SInt<_>>(),
OutputIntegerMode.$OutputIntegerMode(),
Lut4::from_fn(|a, b| a | b),
)
},
));
};
}

View file

@ -105,7 +105,7 @@ pub fn test_cases_book_i_3_3_14_fixed_point_rotate_and_shift(retval: &mut Vec<Te
),
$encoding,
None,
ShiftRotateMOp::shift_rotate(
|| ShiftRotateMOp::shift_rotate(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num($dest)], &[]),
[
MOpRegNum::power_isa_gpr_reg_imm($src),
@ -131,7 +131,7 @@ pub fn test_cases_book_i_3_3_14_fixed_point_rotate_and_shift(retval: &mut Vec<Te
),
$encoding | 1,
None,
ShiftRotateMOp::shift_rotate(
|| ShiftRotateMOp::shift_rotate(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num($dest)], &[MOpRegNum::POWER_ISA_CR_0_REG_NUM],),
[
MOpRegNum::power_isa_gpr_reg_imm($src),
@ -168,7 +168,7 @@ pub fn test_cases_book_i_3_3_14_fixed_point_rotate_and_shift(retval: &mut Vec<Te
),
$encoding,
None,
ShiftRotateMOp::shift_rotate(
|| ShiftRotateMOp::shift_rotate(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num($dest)], &[]),
[
MOpRegNum::power_isa_gpr_reg_imm($src),
@ -199,7 +199,7 @@ pub fn test_cases_book_i_3_3_14_fixed_point_rotate_and_shift(retval: &mut Vec<Te
),
$encoding | 1,
None,
ShiftRotateMOp::shift_rotate(
|| ShiftRotateMOp::shift_rotate(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num($dest)], &[MOpRegNum::POWER_ISA_CR_0_REG_NUM],),
[
MOpRegNum::power_isa_gpr_reg_imm($src),
@ -855,21 +855,23 @@ pub fn test_cases_book_i_3_3_14_fixed_point_rotate_and_shift(retval: &mut Vec<Te
),
$encoding,
None,
ShiftRotateMOp::shift_rotate(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num($dest)], &[]),
[
MOpRegNum::power_isa_gpr_reg_imm($src),
MOpRegNum::const_zero(),
MOpRegNum::power_isa_gpr_reg_imm($amount),
],
shift_imm(
None,
false,
ShiftAmountOverflowBehavior.$overflow_behavior(),
),
OutputIntegerMode.Full64(),
ShiftRotateMode.$shift_rotate_mode(),
),
|| {
ShiftRotateMOp::shift_rotate(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num($dest)], &[]),
[
MOpRegNum::power_isa_gpr_reg_imm($src),
MOpRegNum::const_zero(),
MOpRegNum::power_isa_gpr_reg_imm($amount),
],
shift_imm(
None,
false,
ShiftAmountOverflowBehavior.$overflow_behavior(),
),
OutputIntegerMode.Full64(),
ShiftRotateMode.$shift_rotate_mode(),
)
},
));
retval.push(insn_single(
concat!(
@ -883,24 +885,26 @@ pub fn test_cases_book_i_3_3_14_fixed_point_rotate_and_shift(retval: &mut Vec<Te
),
$encoding | 1,
None,
ShiftRotateMOp::shift_rotate(
MOpDestReg::new_sim(
&[MOpRegNum::power_isa_gpr_reg_num($dest)],
&[MOpRegNum::POWER_ISA_CR_0_REG_NUM],
),
[
MOpRegNum::power_isa_gpr_reg_imm($src),
MOpRegNum::const_zero(),
MOpRegNum::power_isa_gpr_reg_imm($amount),
],
shift_imm(
None,
false,
ShiftAmountOverflowBehavior.$overflow_behavior(),
),
OutputIntegerMode.Full64(),
ShiftRotateMode.$shift_rotate_mode(),
),
|| {
ShiftRotateMOp::shift_rotate(
MOpDestReg::new_sim(
&[MOpRegNum::power_isa_gpr_reg_num($dest)],
&[MOpRegNum::POWER_ISA_CR_0_REG_NUM],
),
[
MOpRegNum::power_isa_gpr_reg_imm($src),
MOpRegNum::const_zero(),
MOpRegNum::power_isa_gpr_reg_imm($amount),
],
shift_imm(
None,
false,
ShiftAmountOverflowBehavior.$overflow_behavior(),
),
OutputIntegerMode.Full64(),
ShiftRotateMode.$shift_rotate_mode(),
)
},
));
}};
}
@ -923,17 +927,19 @@ pub fn test_cases_book_i_3_3_14_fixed_point_rotate_and_shift(retval: &mut Vec<Te
),
$encoding,
None,
ShiftRotateMOp::shift_rotate(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num($dest)], &[]),
[
MOpRegNum::const_zero(),
MOpRegNum::power_isa_gpr_reg_imm($src),
MOpRegNum::power_isa_gpr_reg_imm($amount),
],
shift_imm(None, true, ShiftAmountOverflowBehavior.$overflow_behavior()),
OutputIntegerMode.Full64(),
ShiftRotateMode.$shift_rotate_mode(),
),
|| {
ShiftRotateMOp::shift_rotate(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num($dest)], &[]),
[
MOpRegNum::const_zero(),
MOpRegNum::power_isa_gpr_reg_imm($src),
MOpRegNum::power_isa_gpr_reg_imm($amount),
],
shift_imm(None, true, ShiftAmountOverflowBehavior.$overflow_behavior()),
OutputIntegerMode.Full64(),
ShiftRotateMode.$shift_rotate_mode(),
)
},
));
retval.push(insn_single(
concat!(
@ -947,20 +953,22 @@ pub fn test_cases_book_i_3_3_14_fixed_point_rotate_and_shift(retval: &mut Vec<Te
),
$encoding | 1,
None,
ShiftRotateMOp::shift_rotate(
MOpDestReg::new_sim(
&[MOpRegNum::power_isa_gpr_reg_num($dest)],
&[MOpRegNum::POWER_ISA_CR_0_REG_NUM],
),
[
MOpRegNum::const_zero(),
MOpRegNum::power_isa_gpr_reg_imm($src),
MOpRegNum::power_isa_gpr_reg_imm($amount),
],
shift_imm(None, true, ShiftAmountOverflowBehavior.$overflow_behavior()),
OutputIntegerMode.Full64(),
ShiftRotateMode.$shift_rotate_mode(),
),
|| {
ShiftRotateMOp::shift_rotate(
MOpDestReg::new_sim(
&[MOpRegNum::power_isa_gpr_reg_num($dest)],
&[MOpRegNum::POWER_ISA_CR_0_REG_NUM],
),
[
MOpRegNum::const_zero(),
MOpRegNum::power_isa_gpr_reg_imm($src),
MOpRegNum::power_isa_gpr_reg_imm($amount),
],
shift_imm(None, true, ShiftAmountOverflowBehavior.$overflow_behavior()),
OutputIntegerMode.Full64(),
ShiftRotateMode.$shift_rotate_mode(),
)
},
));
}};
}
@ -983,23 +991,25 @@ pub fn test_cases_book_i_3_3_14_fixed_point_rotate_and_shift(retval: &mut Vec<Te
),
$encoding,
None,
ShiftRotateMOp::shift_rotate(
MOpDestReg::new_sim(
&[
MOpRegNum::power_isa_gpr_reg_num($dest),
MOpRegNum::POWER_ISA_XER_CA_CA32_REG_NUM,
|| {
ShiftRotateMOp::shift_rotate(
MOpDestReg::new_sim(
&[
MOpRegNum::power_isa_gpr_reg_num($dest),
MOpRegNum::POWER_ISA_XER_CA_CA32_REG_NUM,
],
&[],
),
[
MOpRegNum::power_isa_gpr_reg_imm($src),
MOpRegNum::const_zero(),
MOpRegNum::power_isa_gpr_reg_imm($amount),
],
&[],
),
[
MOpRegNum::power_isa_gpr_reg_imm($src),
MOpRegNum::const_zero(),
MOpRegNum::power_isa_gpr_reg_imm($amount),
],
shift_imm(None, true, ShiftAmountOverflowBehavior.$overflow_behavior()),
OutputIntegerMode.Full64(),
ShiftRotateMode.$shift_rotate_mode(),
),
shift_imm(None, true, ShiftAmountOverflowBehavior.$overflow_behavior()),
OutputIntegerMode.Full64(),
ShiftRotateMode.$shift_rotate_mode(),
)
},
));
retval.push(insn_single(
concat!(
@ -1013,23 +1023,25 @@ pub fn test_cases_book_i_3_3_14_fixed_point_rotate_and_shift(retval: &mut Vec<Te
),
$encoding | 1,
None,
ShiftRotateMOp::shift_rotate(
MOpDestReg::new_sim(
&[
MOpRegNum::power_isa_gpr_reg_num($dest),
MOpRegNum::POWER_ISA_XER_CA_CA32_REG_NUM,
|| {
ShiftRotateMOp::shift_rotate(
MOpDestReg::new_sim(
&[
MOpRegNum::power_isa_gpr_reg_num($dest),
MOpRegNum::POWER_ISA_XER_CA_CA32_REG_NUM,
],
&[MOpRegNum::POWER_ISA_CR_0_REG_NUM],
),
[
MOpRegNum::power_isa_gpr_reg_imm($src),
MOpRegNum::const_zero(),
MOpRegNum::power_isa_gpr_reg_imm($amount),
],
&[MOpRegNum::POWER_ISA_CR_0_REG_NUM],
),
[
MOpRegNum::power_isa_gpr_reg_imm($src),
MOpRegNum::const_zero(),
MOpRegNum::power_isa_gpr_reg_imm($amount),
],
shift_imm(None, true, ShiftAmountOverflowBehavior.$overflow_behavior()),
OutputIntegerMode.Full64(),
ShiftRotateMode.$shift_rotate_mode(),
),
shift_imm(None, true, ShiftAmountOverflowBehavior.$overflow_behavior()),
OutputIntegerMode.Full64(),
ShiftRotateMode.$shift_rotate_mode(),
)
},
));
}};
}
@ -1051,27 +1063,29 @@ pub fn test_cases_book_i_3_3_14_fixed_point_rotate_and_shift(retval: &mut Vec<Te
),
$encoding,
None,
ShiftRotateMOp::shift_rotate(
MOpDestReg::new_sim(
&[
MOpRegNum::power_isa_gpr_reg_num($dest),
MOpRegNum::POWER_ISA_XER_CA_CA32_REG_NUM,
|| {
ShiftRotateMOp::shift_rotate(
MOpDestReg::new_sim(
&[
MOpRegNum::power_isa_gpr_reg_num($dest),
MOpRegNum::POWER_ISA_XER_CA_CA32_REG_NUM,
],
&[],
),
[
MOpRegNum::power_isa_gpr_reg_imm($src),
MOpRegNum::const_zero(),
MOpRegNum::const_zero(),
],
&[],
),
[
MOpRegNum::power_isa_gpr_reg_imm($src),
MOpRegNum::const_zero(),
MOpRegNum::const_zero(),
],
shift_imm(
Some($amount),
true,
ShiftAmountOverflowBehavior.WrapToUInt6(),
),
OutputIntegerMode.Full64(),
ShiftRotateMode.$shift_rotate_mode(),
),
shift_imm(
Some($amount),
true,
ShiftAmountOverflowBehavior.WrapToUInt6(),
),
OutputIntegerMode.Full64(),
ShiftRotateMode.$shift_rotate_mode(),
)
},
));
retval.push(insn_single(
concat!(
@ -1085,27 +1099,29 @@ pub fn test_cases_book_i_3_3_14_fixed_point_rotate_and_shift(retval: &mut Vec<Te
),
$encoding | 1,
None,
ShiftRotateMOp::shift_rotate(
MOpDestReg::new_sim(
&[
MOpRegNum::power_isa_gpr_reg_num($dest),
MOpRegNum::POWER_ISA_XER_CA_CA32_REG_NUM,
|| {
ShiftRotateMOp::shift_rotate(
MOpDestReg::new_sim(
&[
MOpRegNum::power_isa_gpr_reg_num($dest),
MOpRegNum::POWER_ISA_XER_CA_CA32_REG_NUM,
],
&[MOpRegNum::POWER_ISA_CR_0_REG_NUM],
),
[
MOpRegNum::power_isa_gpr_reg_imm($src),
MOpRegNum::const_zero(),
MOpRegNum::const_zero(),
],
&[MOpRegNum::POWER_ISA_CR_0_REG_NUM],
),
[
MOpRegNum::power_isa_gpr_reg_imm($src),
MOpRegNum::const_zero(),
MOpRegNum::const_zero(),
],
shift_imm(
Some($amount),
true,
ShiftAmountOverflowBehavior.WrapToUInt6(),
),
OutputIntegerMode.Full64(),
ShiftRotateMode.$shift_rotate_mode(),
),
shift_imm(
Some($amount),
true,
ShiftAmountOverflowBehavior.WrapToUInt6(),
),
OutputIntegerMode.Full64(),
ShiftRotateMode.$shift_rotate_mode(),
)
},
));
}};
}
@ -1212,21 +1228,23 @@ pub fn test_cases_book_i_3_3_14_fixed_point_rotate_and_shift(retval: &mut Vec<Te
),
$encoding,
None,
ShiftRotateMOp::shift_rotate(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num($dest)], &[]),
[
MOpRegNum::power_isa_gpr_reg_imm($src),
MOpRegNum::const_zero(),
MOpRegNum::const_zero(),
],
shift_imm(
Some($amount),
false,
ShiftAmountOverflowBehavior.WrapToUInt6(),
),
OutputIntegerMode.Full64(),
ShiftRotateMode.SignExt32To64BitThenShift(),
),
|| {
ShiftRotateMOp::shift_rotate(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num($dest)], &[]),
[
MOpRegNum::power_isa_gpr_reg_imm($src),
MOpRegNum::const_zero(),
MOpRegNum::const_zero(),
],
shift_imm(
Some($amount),
false,
ShiftAmountOverflowBehavior.WrapToUInt6(),
),
OutputIntegerMode.Full64(),
ShiftRotateMode.SignExt32To64BitThenShift(),
)
},
));
retval.push(insn_single(
concat!(
@ -1240,24 +1258,26 @@ pub fn test_cases_book_i_3_3_14_fixed_point_rotate_and_shift(retval: &mut Vec<Te
),
$encoding | 1,
None,
ShiftRotateMOp::shift_rotate(
MOpDestReg::new_sim(
&[MOpRegNum::power_isa_gpr_reg_num($dest)],
&[MOpRegNum::POWER_ISA_CR_0_REG_NUM],
),
[
MOpRegNum::power_isa_gpr_reg_imm($src),
MOpRegNum::const_zero(),
MOpRegNum::const_zero(),
],
shift_imm(
Some($amount),
false,
ShiftAmountOverflowBehavior.WrapToUInt6(),
),
OutputIntegerMode.Full64(),
ShiftRotateMode.SignExt32To64BitThenShift(),
),
|| {
ShiftRotateMOp::shift_rotate(
MOpDestReg::new_sim(
&[MOpRegNum::power_isa_gpr_reg_num($dest)],
&[MOpRegNum::POWER_ISA_CR_0_REG_NUM],
),
[
MOpRegNum::power_isa_gpr_reg_imm($src),
MOpRegNum::const_zero(),
MOpRegNum::const_zero(),
],
shift_imm(
Some($amount),
false,
ShiftAmountOverflowBehavior.WrapToUInt6(),
),
OutputIntegerMode.Full64(),
ShiftRotateMode.SignExt32To64BitThenShift(),
)
},
));
}};
}

View file

@ -20,34 +20,36 @@ pub fn test_cases_book_i_3_3_3_fixed_point_store(retval: &mut Vec<TestCase>) {
concat!($mnemonic, " ", $rs, ", ", $disp, "(", $ra, "), ", $r),
$prefix,
Some($suffix),
[
AddSubMOp::add_sub_i(
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_TEMP_REG_NUM], &[]),
[
if $r != 0 || $ra == 0 {
MOpRegNum::const_zero()
} else {
MOpRegNum::power_isa_gpr_reg_imm($ra)
},
MOpRegNum::const_zero(),
],
($disp as i64).cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
$r != 0,
),
StoreMOp::store(
MOpDestReg::new_sim(&[], &[]),
[
MOpRegNum::power_isa_temp_reg(),
MOpRegNum::power_isa_gpr_reg_imm($rs),
],
LoadStoreWidth.$width(),
LoadStoreConversion.ZeroExt(),
),
],
|| {
[
AddSubMOp::add_sub_i(
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_TEMP_REG_NUM], &[]),
[
if $r != 0 || $ra == 0 {
MOpRegNum::const_zero()
} else {
MOpRegNum::power_isa_gpr_reg_imm($ra)
},
MOpRegNum::const_zero(),
],
($disp as i64).cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
$r != 0,
),
StoreMOp::store(
MOpDestReg::new_sim(&[], &[]),
[
MOpRegNum::power_isa_temp_reg(),
MOpRegNum::power_isa_gpr_reg_imm($rs),
],
LoadStoreWidth.$width(),
LoadStoreConversion.ZeroExt(),
),
]
},
));
};
}
@ -61,34 +63,36 @@ pub fn test_cases_book_i_3_3_3_fixed_point_store(retval: &mut Vec<TestCase>) {
concat!($mnemonic, " ", $rs, ", ", $disp, "(", $ra, ")"),
$encoding,
None,
[
AddSubMOp::add_sub_i(
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_TEMP_REG_NUM], &[]),
[
if $ra == 0 {
MOpRegNum::const_zero()
} else {
MOpRegNum::power_isa_gpr_reg_imm($ra)
},
MOpRegNum::const_zero(),
],
($disp as i64).cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
false,
),
StoreMOp::store(
MOpDestReg::new_sim(&[], &[]),
[
MOpRegNum::power_isa_temp_reg(),
MOpRegNum::power_isa_gpr_reg_imm($rs),
],
LoadStoreWidth.$width(),
LoadStoreConversion.ZeroExt(),
),
],
|| {
[
AddSubMOp::add_sub_i(
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_TEMP_REG_NUM], &[]),
[
if $ra == 0 {
MOpRegNum::const_zero()
} else {
MOpRegNum::power_isa_gpr_reg_imm($ra)
},
MOpRegNum::const_zero(),
],
($disp as i64).cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
false,
),
StoreMOp::store(
MOpDestReg::new_sim(&[], &[]),
[
MOpRegNum::power_isa_temp_reg(),
MOpRegNum::power_isa_gpr_reg_imm($rs),
],
LoadStoreWidth.$width(),
LoadStoreConversion.ZeroExt(),
),
]
},
));
};
}
@ -103,65 +107,69 @@ pub fn test_cases_book_i_3_3_3_fixed_point_store(retval: &mut Vec<TestCase>) {
concat!($mnemonic, " ", $rs, ", ", $disp, "(", $ra, ")"),
$encoding,
None,
[
AddSubMOp::add_sub_i(
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_TEMP_REG_NUM], &[]),
[
MOpRegNum::power_isa_gpr_reg_imm($ra),
MOpRegNum::const_zero(),
],
($disp as i64).cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
false,
),
StoreMOp::store(
MOpDestReg::new_sim(&[], &[]),
[
MOpRegNum::power_isa_temp_reg(),
MOpRegNum::power_isa_gpr_reg_imm($rs),
],
LoadStoreWidth.$width(),
LoadStoreConversion.ZeroExt(),
),
MoveRegMOp::move_reg(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num($ra)], &[]),
[MOpRegNum::power_isa_temp_reg()],
0.cast_to_static::<SInt<_>>(),
),
],
|| {
[
AddSubMOp::add_sub_i(
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_TEMP_REG_NUM], &[]),
[
MOpRegNum::power_isa_gpr_reg_imm($ra),
MOpRegNum::const_zero(),
],
($disp as i64).cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
false,
),
StoreMOp::store(
MOpDestReg::new_sim(&[], &[]),
[
MOpRegNum::power_isa_temp_reg(),
MOpRegNum::power_isa_gpr_reg_imm($rs),
],
LoadStoreWidth.$width(),
LoadStoreConversion.ZeroExt(),
),
MoveRegMOp::move_reg(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num($ra)], &[]),
[MOpRegNum::power_isa_temp_reg()],
0.cast_to_static::<SInt<_>>(),
),
]
},
));
} else {
retval.push(insn_double(
concat!($mnemonic, " ", $rs, ", ", $disp, "(", $ra, ")"),
$encoding,
None,
[
AddSubMOp::add_sub_i(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num($ra)], &[]),
[
MOpRegNum::power_isa_gpr_reg_imm($ra),
MOpRegNum::const_zero(),
],
($disp as i64).cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
false,
),
StoreMOp::store(
MOpDestReg::new_sim(&[], &[]),
[
MOpRegNum::power_isa_gpr_reg_imm($ra),
MOpRegNum::power_isa_gpr_reg_imm($rs),
],
LoadStoreWidth.$width(),
LoadStoreConversion.ZeroExt(),
),
],
|| {
[
AddSubMOp::add_sub_i(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num($ra)], &[]),
[
MOpRegNum::power_isa_gpr_reg_imm($ra),
MOpRegNum::const_zero(),
],
($disp as i64).cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
false,
),
StoreMOp::store(
MOpDestReg::new_sim(&[], &[]),
[
MOpRegNum::power_isa_gpr_reg_imm($ra),
MOpRegNum::power_isa_gpr_reg_imm($rs),
],
LoadStoreWidth.$width(),
LoadStoreConversion.ZeroExt(),
),
]
},
));
}
};
@ -176,86 +184,10 @@ pub fn test_cases_book_i_3_3_3_fixed_point_store(retval: &mut Vec<TestCase>) {
concat!($mnemonic, " ", $rs, ", ", $ra, ", ", $rb),
$encoding,
None,
[
AddSubMOp::add_sub_i(
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_TEMP_REG_NUM], &[]),
[
if $ra == 0 {
MOpRegNum::const_zero()
} else {
MOpRegNum::power_isa_gpr_reg_imm($ra)
},
MOpRegNum::power_isa_gpr_reg_imm($rb),
],
0.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
false,
),
StoreMOp::store(
MOpDestReg::new_sim(&[], &[]),
[
MOpRegNum::power_isa_temp_reg(),
MOpRegNum::power_isa_gpr_reg_imm($rs),
],
LoadStoreWidth.$width(),
LoadStoreConversion.ZeroExt(),
),
],
));
};
}
macro_rules! store_update_indexed {
(
$mnemonic:literal $rs:literal, $ra:literal, $rb:literal;
$encoding:literal;
$width:ident;
) => {
if $ra == $rs {
retval.push(insn_triple(
concat!($mnemonic, " ", $rs, ", ", $ra, ", ", $rb),
$encoding,
None,
|| {
[
AddSubMOp::add_sub_i(
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_TEMP_REG_NUM], &[]),
[
MOpRegNum::power_isa_gpr_reg_imm($ra),
MOpRegNum::power_isa_gpr_reg_imm($rb),
],
0.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
false,
),
StoreMOp::store(
MOpDestReg::new_sim(&[], &[]),
[
MOpRegNum::power_isa_temp_reg(),
MOpRegNum::power_isa_gpr_reg_imm($rs),
],
LoadStoreWidth.$width(),
LoadStoreConversion.ZeroExt(),
),
MoveRegMOp::move_reg(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num($ra)], &[]),
[MOpRegNum::power_isa_temp_reg()],
0.cast_to_static::<SInt<_>>(),
),
],
));
} else {
retval.push(insn_double(
concat!($mnemonic, " ", $rs, ", ", $ra, ", ", $rb),
$encoding,
None,
[
AddSubMOp::add_sub_i(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num($ra)], &[]),
[
if $ra == 0 {
MOpRegNum::const_zero()
@ -274,13 +206,95 @@ pub fn test_cases_book_i_3_3_3_fixed_point_store(retval: &mut Vec<TestCase>) {
StoreMOp::store(
MOpDestReg::new_sim(&[], &[]),
[
MOpRegNum::power_isa_gpr_reg_imm($ra),
MOpRegNum::power_isa_temp_reg(),
MOpRegNum::power_isa_gpr_reg_imm($rs),
],
LoadStoreWidth.$width(),
LoadStoreConversion.ZeroExt(),
),
],
]
},
));
};
}
macro_rules! store_update_indexed {
(
$mnemonic:literal $rs:literal, $ra:literal, $rb:literal;
$encoding:literal;
$width:ident;
) => {
if $ra == $rs {
retval.push(insn_triple(
concat!($mnemonic, " ", $rs, ", ", $ra, ", ", $rb),
$encoding,
None,
|| {
[
AddSubMOp::add_sub_i(
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_TEMP_REG_NUM], &[]),
[
MOpRegNum::power_isa_gpr_reg_imm($ra),
MOpRegNum::power_isa_gpr_reg_imm($rb),
],
0.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
false,
),
StoreMOp::store(
MOpDestReg::new_sim(&[], &[]),
[
MOpRegNum::power_isa_temp_reg(),
MOpRegNum::power_isa_gpr_reg_imm($rs),
],
LoadStoreWidth.$width(),
LoadStoreConversion.ZeroExt(),
),
MoveRegMOp::move_reg(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num($ra)], &[]),
[MOpRegNum::power_isa_temp_reg()],
0.cast_to_static::<SInt<_>>(),
),
]
},
));
} else {
retval.push(insn_double(
concat!($mnemonic, " ", $rs, ", ", $ra, ", ", $rb),
$encoding,
None,
|| {
[
AddSubMOp::add_sub_i(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num($ra)], &[]),
[
if $ra == 0 {
MOpRegNum::const_zero()
} else {
MOpRegNum::power_isa_gpr_reg_imm($ra)
},
MOpRegNum::power_isa_gpr_reg_imm($rb),
],
0.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
false,
),
StoreMOp::store(
MOpDestReg::new_sim(&[], &[]),
[
MOpRegNum::power_isa_gpr_reg_imm($ra),
MOpRegNum::power_isa_gpr_reg_imm($rs),
],
LoadStoreWidth.$width(),
LoadStoreConversion.ZeroExt(),
),
]
},
));
}
};

View file

@ -23,10 +23,7 @@ pub fn test_cases_book_i_3_3_19_move_to_from_system_register(retval: &mut Vec<Te
dest_count: 6usize.cast_to(LogicalFlagsMOpImm.dest_count),
}
}
retval.push(insn_single(
"mcrxrx 3",
0x7d800480,
None,
retval.push(insn_single("mcrxrx 3", 0x7d800480, None, || {
LogicalFlagsMOp::logical_flags(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]),
[
@ -36,114 +33,114 @@ pub fn test_cases_book_i_3_3_19_move_to_from_system_register(retval: &mut Vec<Te
],
mcrxrx_imm(),
Lut4::from_fn(|a, b| a | b),
),
));
)
}));
}
/// covers instructions in PowerISA v3.1C Book III 5.4.4 Move To/From System Register Instructions
pub fn test_cases_book_iii_5_4_4_move_to_from_system_register(retval: &mut Vec<TestCase>) {
retval.push(insn_single(
"mflr 3",
0x7c6802a6,
None,
retval.push(insn_single("mflr 3", 0x7c6802a6, None, || {
MoveRegMOp::move_reg(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]),
[MOpRegNum::power_isa_lr_reg()],
0.cast_to_static::<SInt<_>>(),
),
));
retval.push(insn_single(
"mtlr 3",
0x7c6803a6,
None,
)
}));
retval.push(insn_single("mtlr 3", 0x7c6803a6, None, || {
MoveRegMOp::move_reg(
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_LR_REG_NUM], &[]),
[MOpRegNum::power_isa_gpr_reg_imm(3)],
0.cast_to_static::<SInt<_>>(),
),
));
retval.push(insn_single(
"mfctr 3",
0x7c6902a6,
None,
)
}));
retval.push(insn_single("mfctr 3", 0x7c6902a6, None, || {
MoveRegMOp::move_reg(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]),
[MOpRegNum::power_isa_ctr_reg()],
0.cast_to_static::<SInt<_>>(),
),
));
retval.push(insn_single(
"mtctr 3",
0x7c6903a6,
None,
)
}));
retval.push(insn_single("mtctr 3", 0x7c6903a6, None, || {
MoveRegMOp::move_reg(
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_CTR_REG_NUM], &[]),
[MOpRegNum::power_isa_gpr_reg_imm(3)],
0.cast_to_static::<SInt<_>>(),
),
));
)
}));
retval.push(insn_single(
"mfspr 3, 815 # mftar 3",
0x7c6fcaa6,
None,
MoveRegMOp::move_reg(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]),
[MOpRegNum::power_isa_tar_reg()],
0.cast_to_static::<SInt<_>>(),
),
|| {
MoveRegMOp::move_reg(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]),
[MOpRegNum::power_isa_tar_reg()],
0.cast_to_static::<SInt<_>>(),
)
},
));
retval.push(insn_single(
"mtspr 815, 3 # mttar 3",
0x7c6fcba6,
None,
MoveRegMOp::move_reg(
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_TAR_REG_NUM], &[]),
[MOpRegNum::power_isa_gpr_reg_imm(3)],
0.cast_to_static::<SInt<_>>(),
),
|| {
MoveRegMOp::move_reg(
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_TAR_REG_NUM], &[]),
[MOpRegNum::power_isa_gpr_reg_imm(3)],
0.cast_to_static::<SInt<_>>(),
)
},
));
// make sure we generate mfspr and not the phased-out mftb
retval.push(insn_single(
"mfspr 3, 268 # mftb 3",
0x7c6c42a6,
None,
ReadSpecialMOp::read_special(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]),
[MOpRegNum::const_zero(); 0],
ReadSpecialMOpImm.PowerIsaTimeBase(),
),
|| {
ReadSpecialMOp::read_special(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]),
[MOpRegNum::const_zero(); 0],
ReadSpecialMOpImm.PowerIsaTimeBase(),
)
},
));
// make sure we generate mfspr and not the phased-out mftb
retval.push(insn_single(
"mfspr 3, 269 # mftbu 3",
0x7c6d42a6,
None,
ReadSpecialMOp::read_special(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]),
[MOpRegNum::const_zero(); 0],
ReadSpecialMOpImm.PowerIsaTimeBaseU(),
),
|| {
ReadSpecialMOp::read_special(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]),
[MOpRegNum::const_zero(); 0],
ReadSpecialMOpImm.PowerIsaTimeBaseU(),
)
},
));
// phased-out mftb -- not actually generated by the assembler so we have to use .long
retval.push(insn_single(
".long 0x7c6c42e6 # mftb 3, 268",
0x7c6c42e6,
None,
ReadSpecialMOp::read_special(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]),
[MOpRegNum::const_zero(); 0],
ReadSpecialMOpImm.PowerIsaTimeBase(),
),
|| {
ReadSpecialMOp::read_special(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]),
[MOpRegNum::const_zero(); 0],
ReadSpecialMOpImm.PowerIsaTimeBase(),
)
},
));
// phased-out mftb -- not actually generated by the assembler so we have to use .long
retval.push(insn_single(
".long 0x7c6d42e6 # mftb 3, 269",
0x7c6d42e6,
None,
ReadSpecialMOp::read_special(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]),
[MOpRegNum::const_zero(); 0],
ReadSpecialMOpImm.PowerIsaTimeBaseU(),
),
|| {
ReadSpecialMOp::read_special(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]),
[MOpRegNum::const_zero(); 0],
ReadSpecialMOpImm.PowerIsaTimeBaseU(),
)
},
));
}

File diff suppressed because it is too large Load diff