implement decoding 8/16/32/64-bit store instructions -- all of Power ISA v3.1c Book I 3.3.3
All checks were successful
/ test (pull_request) Successful in 27m59s

This commit is contained in:
Jacob Lifshay 2026-01-25 15:06:14 -08:00
parent 706d54ae0d
commit 0824b63d31
Signed by: programmerjake
SSH key fingerprint: SHA256:HnFTLGpSm4Q4Fj502oCFisjZSoakwEuTsJJMSke63RQ
4 changed files with 122563 additions and 87706 deletions

View file

@ -6,7 +6,7 @@ use crate::{
instruction::{
AddSubMOp, BranchMOp, CompareMOp, CompareMode, ConditionMode, LoadMOp, LoadStoreConversion,
LoadStoreWidth, LogicalFlagsMOp, LogicalFlagsMOpImm, LogicalMOp, Lut4, MOp, MOpDestReg,
MOpRegNum, MoveRegMOp, OutputIntegerMode,
MOpRegNum, MoveRegMOp, OutputIntegerMode, StoreMOp,
},
powerisa_instructions_xml::{
InstructionBitFieldName, InstructionBitFieldsInner, Instructions, TextLineItem,
@ -135,7 +135,7 @@ struct DecodeState {
conditions: Option<&'static str>,
header: &'static crate::powerisa_instructions_xml::InstructionHeader,
insn: &'static crate::powerisa_instructions_xml::Instruction,
output: Expr<ArrayVec<MOp, ConstUsize<2>>>,
output: Expr<ArrayVec<MOp, ConstUsize<3>>>,
is_illegal: Expr<Bool>,
first_input: Expr<UInt<32>>,
second_input: Expr<HdlOption<UInt<32>>>,
@ -959,6 +959,196 @@ impl DecodeState {
}
}
#[hdl]
fn decode_store_8_16_32_64_bit(&mut self) {
let (is_prefixed, is_update, is_indexed, width) = match self.mnemonic {
"stb" => (false, false, false, 8),
"pstb" => (true, false, false, 8),
"stbx" => (false, false, true, 8),
"stbu" => (false, true, false, 8),
"stbux" => (false, true, true, 8),
"sth" => (false, false, false, 16),
"psth" => (true, false, false, 16),
"sthx" => (false, false, true, 16),
"sthu" => (false, true, false, 16),
"sthux" => (false, true, true, 16),
"stw" => (false, false, false, 32),
"pstw" => (true, false, false, 32),
"stwx" => (false, false, true, 32),
"stwu" => (false, true, false, 32),
"stwux" => (false, true, true, 32),
"std" => (false, false, false, 64),
"pstd" => (true, false, false, 64),
"stdx" => (false, false, true, 64),
"stdu" => (false, true, false, 64),
"stdux" => (false, true, true, 64),
_ => unreachable!(),
};
let width = match width {
8 => LoadStoreWidth.Width8Bit(),
16 => LoadStoreWidth.Width16Bit(),
32 => LoadStoreWidth.Width32Bit(),
64 => LoadStoreWidth.Width64Bit(),
_ => unreachable!(),
};
#[hdl]
fn do_store(
this: &mut DecodeState,
rs: Expr<FieldGpr>,
ra: Expr<FieldGpr>,
is_update: bool,
width: Expr<LoadStoreWidth>,
write_compute_ea_insn: impl FnOnce(Expr<MOp>, Expr<MOpRegNum>),
) {
connect(
ArrayVec::len(this.output),
2usize.cast_to_static::<Length<_>>(),
);
let (ea_reg, ea_reg_conflict) = if is_update {
let ea_reg = wire_with_loc(
&format!("{}_ea_reg", this.mnemonic),
SourceLocation::caller(),
MOpRegNum,
);
connect(ea_reg, gpr(ra));
let ea_reg_conflict = ra.reg_num.cmp_eq(rs.reg_num);
#[hdl]
if ea_reg_conflict {
connect(ea_reg, MOpRegNum::power_isa_temp_reg());
}
(ea_reg, ea_reg_conflict)
} else {
(MOpRegNum::power_isa_temp_reg(), false.to_expr())
};
write_compute_ea_insn(this.output[0], ea_reg);
connect(
this.output[1],
StoreMOp::store(
MOpDestReg::new([], []),
[ea_reg.value, gpr(rs).value],
width,
LoadStoreConversion.ZeroExt(),
),
);
#[hdl]
if ea_reg_conflict {
connect(
ArrayVec::len(this.output),
3usize.cast_to_static::<Length<_>>(),
);
connect(
this.output[2],
MoveRegMOp::move_reg(
MOpDestReg::new([gpr(ra)], []),
[ea_reg.value],
0.cast_to_static::<SInt<_>>(),
),
);
}
}
if is_prefixed {
assert_eq!(self.arguments, Some("RS,D(RA),R"));
assert!(!is_indexed);
self.decode_scope(
|this, (FieldRS(rs), FieldD0(d0), FieldD1(d1), FieldRA(ra), FieldR(r))| {
let d = ((d0 << 16) + d1.cast_to(SInt[32])).cast_to_static::<SInt<_>>();
do_store(this, rs, ra, is_update, width, |insn, ea_reg| {
#[hdl]
if r {
connect(
insn,
AddSubMOp::add_sub_i(
MOpDestReg::new([ea_reg], []),
[MOpRegNum::const_zero().value; 2],
d,
OutputIntegerMode.Full64(),
false,
false,
false,
true,
),
);
} else {
connect(
insn,
AddSubMOp::add_sub_i(
MOpDestReg::new([ea_reg], []),
[gpr_or_zero(ra).value, MOpRegNum::const_zero().value],
d,
OutputIntegerMode.Full64(),
false,
false,
false,
false,
),
);
}
});
},
);
} else if is_indexed {
assert_eq!(self.arguments, Some("RS,RA,RB"));
self.decode_scope(|this, (FieldRS(rs), FieldRA(ra), FieldRB(rb))| {
do_store(this, rs, ra, is_update, width, |insn, ea_reg| {
connect(
insn,
AddSubMOp::add_sub_i(
MOpDestReg::new([ea_reg], []),
[gpr_or_zero(ra).value, gpr(rb).value],
0.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
false,
),
);
});
});
} else if self.arguments == Some("RS,disp(RA)") {
self.decode_scope(|this, (FieldRS(rs), FieldRA(ra), FieldDS(ds))| {
do_store(this, rs, ra, is_update, width, |insn, ea_reg| {
connect(
insn,
AddSubMOp::add_sub_i(
MOpDestReg::new([ea_reg], []),
[gpr_or_zero(ra).value, MOpRegNum::const_zero().value],
(ds << 2).cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
false,
),
);
});
});
} else {
assert_eq!(
self.arguments,
Some("RS,D(RA)"),
"mnemonic={:?}",
self.mnemonic,
);
self.decode_scope(|this, (FieldRS(rs), FieldRA(ra), FieldD(d))| {
do_store(this, rs, ra, is_update, width, |insn, ea_reg| {
connect(
insn,
AddSubMOp::add_sub_i(
MOpDestReg::new([ea_reg], []),
[gpr_or_zero(ra).value, MOpRegNum::const_zero().value],
d.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
false,
),
);
});
});
}
}
#[hdl]
fn decode_addi_paddi(&mut self) {
match self.mnemonic {
"addi" => {
@ -1745,9 +1935,7 @@ const DECODE_FNS: &[(&[&str], DecodeFn)] = &[
"stb", "pstb", "stbx", "stbu", "stbux", "sth", "psth", "sthx", "sthu", "sthux", "stw",
"pstw", "stwx", "stwu", "stwux", "std", "pstd", "stdx", "stdu", "stdux",
],
|_state| {
// TODO
},
DecodeState::decode_store_8_16_32_64_bit,
),
(&["lq", "plq", "stq", "pstq"], |_state| {
// TODO
@ -2008,7 +2196,7 @@ const DECODE_FNS: &[(&[&str], DecodeFn)] = &[
#[hdl_module]
pub fn decode_one_insn() {
#[hdl]
let output: ArrayVec<MOp, ConstUsize<2>> = m.output();
let output: ArrayVec<MOp, ConstUsize<3>> = m.output();
#[hdl]
let is_illegal: Bool = m.output();
#[hdl]

File diff suppressed because it is too large Load diff

View file

@ -11,6 +11,7 @@ mod fixed_point_arithmetic;
mod fixed_point_compare;
mod fixed_point_load;
mod fixed_point_logical;
mod fixed_point_store;
mod move_to_from_system_register;
mod prefixed_no_operation;
@ -18,7 +19,7 @@ pub struct TestCase {
pub mnemonic: &'static str,
pub first_input: u32,
pub second_input: Option<u32>,
pub output: SimValue<ArrayVec<MOp, ConstUsize<2>>>,
pub output: SimValue<ArrayVec<MOp, ConstUsize<3>>>,
pub loc: &'static std::panic::Location<'static>,
}
@ -107,11 +108,38 @@ fn insn_double(
}
}
#[track_caller]
fn insn_triple(
mnemonic: &'static str,
first_input: u32,
second_input: Option<u32>,
insns: [impl ToSimValue<Type = MOp>; 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(),
}
}
pub fn test_cases() -> Vec<TestCase> {
let mut retval = Vec::new();
branch::test_cases_book_i_2_4_branch(&mut retval);
condition_register::test_cases_book_i_2_5_condition_register(&mut retval);
fixed_point_load::test_cases_book_i_3_3_2_fixed_point_load(&mut retval);
fixed_point_store::test_cases_book_i_3_3_3_fixed_point_store(&mut retval);
fixed_point_arithmetic::test_cases_book_i_3_3_9_fixed_point_arithmetic(&mut retval);
fixed_point_compare::test_cases_book_i_3_3_10_fixed_point_compare(&mut retval);
fixed_point_logical::test_cases_book_i_3_3_13_fixed_point_logical(&mut retval);

View file

@ -0,0 +1,512 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::test_cases::{TestCase, insn_double, insn_triple};
use cpu::instruction::{
AddSubMOp, LoadStoreConversion, LoadStoreWidth, MOpDestReg, MOpRegNum, MoveRegMOp,
OutputIntegerMode, StoreMOp,
};
use fayalite::prelude::*;
/// covers instructions in PowerISA v3.1C Book I 3.3.3 Fixed-Point Store Instructions
pub fn test_cases_book_i_3_3_3_fixed_point_store(retval: &mut Vec<TestCase>) {
macro_rules! store_prefixed {
(
$mnemonic:literal $rs:literal, $disp:literal($ra:literal), $r:literal;
$prefix:literal, $suffix:literal;
$width:ident;
) => {
retval.push(insn_double(
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().value
} else {
MOpRegNum::power_isa_gpr_reg_imm($ra).value
},
MOpRegNum::const_zero().value,
],
($disp as i64).cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
$r != 0,
),
StoreMOp::store(
MOpDestReg::new_sim(&[], &[]),
[
MOpRegNum::power_isa_temp_reg().value,
MOpRegNum::power_isa_gpr_reg_imm($rs).value,
],
LoadStoreWidth.$width(),
LoadStoreConversion.ZeroExt(),
),
],
));
};
}
macro_rules! store {
(
$mnemonic:literal $rs:literal, $disp:literal($ra:literal);
$encoding:literal;
$width:ident;
) => {
retval.push(insn_double(
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().value
} else {
MOpRegNum::power_isa_gpr_reg_imm($ra).value
},
MOpRegNum::const_zero().value,
],
($disp as i64).cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
false,
),
StoreMOp::store(
MOpDestReg::new_sim(&[], &[]),
[
MOpRegNum::power_isa_temp_reg().value,
MOpRegNum::power_isa_gpr_reg_imm($rs).value,
],
LoadStoreWidth.$width(),
LoadStoreConversion.ZeroExt(),
),
],
));
};
}
macro_rules! store_update {
(
$mnemonic:literal $rs:literal, $disp:literal($ra:literal);
$encoding:literal;
$width:ident;
) => {
if $ra == $rs {
retval.push(insn_triple(
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).value,
MOpRegNum::const_zero().value,
],
($disp as i64).cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
false,
),
StoreMOp::store(
MOpDestReg::new_sim(&[], &[]),
[
MOpRegNum::power_isa_temp_reg().value,
MOpRegNum::power_isa_gpr_reg_imm($rs).value,
],
LoadStoreWidth.$width(),
LoadStoreConversion.ZeroExt(),
),
MoveRegMOp::move_reg(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num($ra)], &[]),
[MOpRegNum::power_isa_temp_reg().value],
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).value,
MOpRegNum::const_zero().value,
],
($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).value,
MOpRegNum::power_isa_gpr_reg_imm($rs).value,
],
LoadStoreWidth.$width(),
LoadStoreConversion.ZeroExt(),
),
],
));
}
};
}
macro_rules! store_indexed {
(
$mnemonic:literal $rs:literal, $ra:literal, $rb:literal;
$encoding:literal;
$width:ident;
) => {
retval.push(insn_double(
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().value
} else {
MOpRegNum::power_isa_gpr_reg_imm($ra).value
},
MOpRegNum::power_isa_gpr_reg_imm($rb).value,
],
0.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
false,
),
StoreMOp::store(
MOpDestReg::new_sim(&[], &[]),
[
MOpRegNum::power_isa_temp_reg().value,
MOpRegNum::power_isa_gpr_reg_imm($rs).value,
],
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).value,
MOpRegNum::power_isa_gpr_reg_imm($rb).value,
],
0.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
false,
),
StoreMOp::store(
MOpDestReg::new_sim(&[], &[]),
[
MOpRegNum::power_isa_temp_reg().value,
MOpRegNum::power_isa_gpr_reg_imm($rs).value,
],
LoadStoreWidth.$width(),
LoadStoreConversion.ZeroExt(),
),
MoveRegMOp::move_reg(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num($ra)], &[]),
[MOpRegNum::power_isa_temp_reg().value],
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().value
} else {
MOpRegNum::power_isa_gpr_reg_imm($ra).value
},
MOpRegNum::power_isa_gpr_reg_imm($rb).value,
],
0.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
false,
),
StoreMOp::store(
MOpDestReg::new_sim(&[], &[]),
[
MOpRegNum::power_isa_gpr_reg_imm($ra).value,
MOpRegNum::power_isa_gpr_reg_imm($rs).value,
],
LoadStoreWidth.$width(),
LoadStoreConversion.ZeroExt(),
),
],
));
}
};
}
store! {
"stb" 3, 0x1234(4);
0x98641234;
Width8Bit;
}
store! {
"stb" 3, 0x1234(0);
0x98601234;
Width8Bit;
}
store_prefixed! {
"pstb" 3, 0x123456789(4), 0;
0x06012345, 0x98646789;
Width8Bit;
}
store_prefixed! {
"pstb" 3, 0x123456789(0), 0;
0x06012345, 0x98606789;
Width8Bit;
}
store_prefixed! {
"pstb" 3, 0x123456789(0), 1;
0x06112345, 0x98606789;
Width8Bit;
}
store_indexed! {
"stbx" 3, 4, 5;
0x7c6429ae;
Width8Bit;
}
store_indexed! {
"stbx" 3, 0, 5;
0x7c6029ae;
Width8Bit;
}
store_update! {
"stbu" 3, 0x1234(4);
0x9c641234;
Width8Bit;
}
store_update! {
"stbu" 3, 0x1234(3);
0x9c631234;
Width8Bit;
}
store_update_indexed! {
"stbux" 3, 4, 5;
0x7c6429ee;
Width8Bit;
}
store_update_indexed! {
"stbux" 3, 3, 5;
0x7c6329ee;
Width8Bit;
}
store! {
"sth" 3, 0x1234(4);
0xb0641234;
Width16Bit;
}
store! {
"sth" 3, 0x1234(0);
0xb0601234;
Width16Bit;
}
store_prefixed! {
"psth" 3, 0x123456789(4), 0;
0x06012345, 0xb0646789;
Width16Bit;
}
store_prefixed! {
"psth" 3, 0x123456789(0), 0;
0x06012345, 0xb0606789;
Width16Bit;
}
store_prefixed! {
"psth" 3, 0x123456789(0), 1;
0x06112345, 0xb0606789;
Width16Bit;
}
store_indexed! {
"sthx" 3, 4, 5;
0x7c642b2e;
Width16Bit;
}
store_indexed! {
"sthx" 3, 0, 5;
0x7c602b2e;
Width16Bit;
}
store_update! {
"sthu" 3, 0x1234(4);
0xb4641234;
Width16Bit;
}
store_update! {
"sthu" 3, 0x1234(3);
0xb4631234;
Width16Bit;
}
store_update_indexed! {
"sthux" 3, 4, 5;
0x7c642b6e;
Width16Bit;
}
store_update_indexed! {
"sthux" 3, 3, 5;
0x7c632b6e;
Width16Bit;
}
store! {
"stw" 3, 0x1234(4);
0x90641234;
Width32Bit;
}
store! {
"stw" 3, 0x1234(0);
0x90601234;
Width32Bit;
}
store_prefixed! {
"pstw" 3, 0x123456789(4), 0;
0x06012345, 0x90646789;
Width32Bit;
}
store_prefixed! {
"pstw" 3, 0x123456789(0), 0;
0x06012345, 0x90606789;
Width32Bit;
}
store_prefixed! {
"pstw" 3, 0x123456789(0), 1;
0x06112345, 0x90606789;
Width32Bit;
}
store_indexed! {
"stwx" 3, 4, 5;
0x7c64292e;
Width32Bit;
}
store_indexed! {
"stwx" 3, 0, 5;
0x7c60292e;
Width32Bit;
}
store_update! {
"stwu" 3, 0x1234(4);
0x94641234;
Width32Bit;
}
store_update! {
"stwu" 3, 0x1234(3);
0x94631234;
Width32Bit;
}
store_update_indexed! {
"stwux" 3, 4, 5;
0x7c64296e;
Width32Bit;
}
store_update_indexed! {
"stwux" 3, 3, 5;
0x7c63296e;
Width32Bit;
}
store! {
"std" 3, 0x1234(4);
0xf8641234;
Width64Bit;
}
store! {
"std" 3, 0x1234(0);
0xf8601234;
Width64Bit;
}
store_prefixed! {
"pstd" 3, 0x123456789(4), 0;
0x04012345, 0xf4646789;
Width64Bit;
}
store_prefixed! {
"pstd" 3, 0x123456789(0), 0;
0x04012345, 0xf4606789;
Width64Bit;
}
store_prefixed! {
"pstd" 3, 0x123456789(0), 1;
0x04112345, 0xf4606789;
Width64Bit;
}
store_indexed! {
"stdx" 3, 4, 5;
0x7c64292a;
Width64Bit;
}
store_indexed! {
"stdx" 3, 0, 5;
0x7c60292a;
Width64Bit;
}
store_update! {
"stdu" 3, 0x1234(4);
0xf8641235;
Width64Bit;
}
store_update! {
"stdu" 3, 0x1234(3);
0xf8631235;
Width64Bit;
}
store_update_indexed! {
"stdux" 3, 4, 5;
0x7c64296a;
Width64Bit;
}
store_update_indexed! {
"stdux" 3, 3, 5;
0x7c63296a;
Width64Bit;
}
}