rename_execute_retire: add a head -n1 test
All checks were successful
/ test (pull_request) Successful in 9m24s

This commit is contained in:
Jacob Lifshay 2026-05-18 22:12:45 -07:00
parent 0d3c41fa14
commit 79ac190093
Signed by: programmerjake
SSH key fingerprint: SHA256:HnFTLGpSm4Q4Fj502oCFisjZSoakwEuTsJJMSke63RQ
8 changed files with 100223 additions and 3334 deletions

View file

@ -10,8 +10,9 @@ use cpu::{
AddSubMOp, AluBranchMOp, AluCommonMOp, BranchMOp, COMMON_MOP_SRC_LEN, CommonMOp,
CompareMOp, CompareMode, ConditionMode, L2RegNum, L2RegisterFileMOp, LoadMOp,
LoadStoreCommonMOp, LoadStoreConversion, LoadStoreMOp, LoadStoreWidth, MOp, MOpDestReg,
MOpRegNum, MOpTrait, MoveRegMOp, OutputIntegerMode, PRegNum, ReadL2RegMOp, StoreMOp,
UnitNum, WriteL2RegMOp,
MOpRegNum, MOpTrait, MoveRegMOp, OutputIntegerMode, PRegNum, ReadL2RegMOp,
ShiftAmountOverflowBehavior, ShiftRotateDestLogicOp, ShiftRotateMOp, ShiftRotateMOpImm,
ShiftRotateMode, StoreMOp, UnitNum, WriteL2RegMOp,
},
next_pc::CallStackOp,
register::{PRegFlags, PRegFlagsPowerISA, PRegValue},
@ -331,6 +332,43 @@ impl InsnsBuilder {
},
));
}
fn power_isa_bdnz(&mut self, target: InsnsBuilderLabel) {
let pc = self.pc;
self.add_insn(Insn::new_lazy(
4,
format!("bdnz {}", self.labels[target.0].name),
move |labels| {
[
AddSubMOp::add_sub_i::<MOp>(
MOpDestReg::new([MOpRegNum::power_isa_ctr_reg()], []),
[MOpRegNum::power_isa_ctr_reg(), MOpRegNum::const_zero()],
(-1).cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
false,
),
BranchMOp::branch_ctr(
MOpDestReg::new_sim(&[], &[]),
[
MOpRegNum::const_zero(),
MOpRegNum::const_zero(),
MOpRegNum::power_isa_ctr_reg(),
],
labels[target.0]
.pc()
.wrapping_sub(pc)
.cast_to_static::<SInt<_>>(),
true,
true,
false,
false,
),
]
},
));
}
fn power_isa_bl(&mut self, target: InsnsBuilderLabel) {
let pc = self.pc;
self.add_insn(Insn::new_lazy(
@ -402,6 +440,45 @@ impl InsnsBuilder {
)],
));
}
fn power_isa_cmplwi(&mut self, dest: usize, src: usize, imm: u16) {
self.add_insn(Insn::new(
4,
format!("cmplwi {dest}, {src}, {imm:#x}"),
[CompareMOp::compare_i(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(dest)], &[]),
[MOpRegNum::power_isa_gpr_reg_imm(src)],
imm.cast_to_static::<SInt<_>>(),
CompareMode.U32(),
)],
));
}
fn power_isa_lbzu(&mut self, dest: usize, src: usize, disp: i16) {
self.add_insn(Insn::new(
4,
format!("lbzu {dest}, {}({src})", signed_hex(disp)),
[
AddSubMOp::add_sub_i(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(src)], &[]),
[
MOpRegNum::power_isa_gpr_reg_imm(src),
MOpRegNum::const_zero(),
],
disp.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(src)],
LoadStoreWidth.Width8Bit(),
LoadStoreConversion.ZeroExt(),
),
],
));
}
fn power_isa_ld(&mut self, dest: usize, src: usize, disp: i16) {
self.add_insn(Insn::new(
4,
@ -429,6 +506,36 @@ impl InsnsBuilder {
],
));
}
fn power_isa_stb(&mut self, value_src: usize, addr_src: usize, disp: i16) {
self.add_insn(Insn::new(
4,
format!("stb {value_src}, {}({addr_src})", signed_hex(disp)),
[
AddSubMOp::add_sub_i(
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_TEMP_REG_NUM], &[]),
[
MOpRegNum::power_isa_gpr_or_zero_reg_imm(addr_src),
MOpRegNum::const_zero(),
],
disp.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(value_src),
],
LoadStoreWidth.Width8Bit(),
LoadStoreConversion.ZeroExt(),
),
],
));
}
fn power_isa_std(&mut self, value_src: usize, addr_src: usize, disp: i16) {
self.add_insn(Insn::new(
4,
@ -459,6 +566,72 @@ impl InsnsBuilder {
],
));
}
fn power_isa_stdu(&mut self, value_src: usize, addr_src: usize, disp: i16) {
if value_src == addr_src {
self.add_insn(Insn::new(
4,
format!("stdu {value_src}, {}({addr_src})", signed_hex(disp)),
[
AddSubMOp::add_sub_i(
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_TEMP_REG_NUM], &[]),
[
MOpRegNum::power_isa_gpr_reg_imm(addr_src),
MOpRegNum::const_zero(),
],
disp.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(value_src),
],
LoadStoreWidth.Width64Bit(),
LoadStoreConversion.ZeroExt(),
),
MoveRegMOp::move_reg(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(addr_src)], &[]),
[MOpRegNum::power_isa_temp_reg()],
0.cast_to_static::<SInt<_>>(),
),
],
));
} else {
self.add_insn(Insn::new(
4,
format!("stdu {value_src}, {}({addr_src})", signed_hex(disp)),
[
AddSubMOp::add_sub_i(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(addr_src)], &[]),
[
MOpRegNum::power_isa_gpr_reg_imm(addr_src),
MOpRegNum::const_zero(),
],
disp.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
false,
),
StoreMOp::store(
MOpDestReg::new_sim(&[], &[]),
[
MOpRegNum::power_isa_gpr_reg_imm(addr_src),
MOpRegNum::power_isa_gpr_reg_imm(value_src),
],
LoadStoreWidth.Width64Bit(),
LoadStoreConversion.ZeroExt(),
),
],
));
}
}
fn power_isa_mflr(&mut self, dest: usize) {
self.add_insn(Insn::new(
4,
@ -481,6 +654,28 @@ impl InsnsBuilder {
)],
));
}
fn power_isa_mfctr(&mut self, dest: usize) {
self.add_insn(Insn::new(
4,
format!("mfctr {dest}"),
[MoveRegMOp::move_reg(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(dest)], &[]),
[MOpRegNum::power_isa_ctr_reg()],
0i8.cast_to_static::<SInt<_>>(),
)],
));
}
fn power_isa_mtctr(&mut self, src: usize) {
self.add_insn(Insn::new(
4,
format!("mtctr {src}"),
[MoveRegMOp::move_reg(
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_CTR_REG_NUM], &[]),
[MOpRegNum::power_isa_gpr_reg_imm(src)],
0i8.cast_to_static::<SInt<_>>(),
)],
));
}
fn power_isa_mr(&mut self, dest: usize, src: usize) {
self.add_insn(Insn::new(
4,
@ -492,6 +687,64 @@ impl InsnsBuilder {
)],
));
}
#[hdl]
fn power_isa_rlwinm(
&mut self,
dest: usize,
src: usize,
shift: u32,
msb0_mask_begin: u32,
msb0_mask_end: u32,
) {
assert!(shift < 32);
assert!(msb0_mask_begin < 32);
assert!(msb0_mask_end < 32);
let insn = format!("rlwinm {dest}, {src}, {shift}, {msb0_mask_begin}, {msb0_mask_end}");
let msb0_mask_begin = msb0_mask_begin + 32;
let msb0_mask_end = msb0_mask_end + 32;
// gives the correct value modulo 2^6 even when `msb0_mask_begin > msb0_mask_end`
let rotated_output_len = msb0_mask_end.wrapping_sub(msb0_mask_begin).wrapping_add(1) % 64;
// account for lsb0 vs. msb0
let rotated_output_start = !msb0_mask_end % 64;
self.add_insn(Insn::new(
4,
insn,
[ShiftRotateMOp::shift_rotate(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(dest)], &[]),
[
MOpRegNum::power_isa_gpr_reg_imm(src),
MOpRegNum::power_isa_gpr_reg_imm(src),
MOpRegNum::const_zero(),
],
#[hdl]
ShiftRotateMOpImm {
shift_rotate_amount: HdlSome(shift.cast_to_static::<UInt<_>>()),
shift_rotate_right: false,
shift_amount_overflow_behavior: ShiftAmountOverflowBehavior.WrapToWidth(),
dest_logic_op: if rotated_output_len == 0 {
// rotated_output_len wrapped around to 64
HdlNone()
} else {
HdlSome(
#[hdl]
ShiftRotateDestLogicOp {
rotated_output_start: rotated_output_start
.cast_to_static::<UInt<_>>(),
rotated_output_len: rotated_output_len.cast_to_static::<UInt<_>>(),
fallback_is_src2: false,
},
)
},
},
OutputIntegerMode.Full64(),
ShiftRotateMode.FunnelShift2x32Bit(),
)],
));
}
fn power_isa_clrlwi(&mut self, dest: usize, src: usize, width: u32) {
assert!(width < 32);
self.power_isa_rlwinm(dest, src, 0, width, 31);
}
}
trait MakeInsns {
@ -499,59 +752,6 @@ trait MakeInsns {
fn make_load_store_execution_state() -> MockMemory;
}
struct FibonacciInsns;
impl MakeInsns for FibonacciInsns {
fn make_insns() -> Insns {
let mut b = InsnsBuilder::new();
let fib = b.new_label("fib");
let fib_small = b.new_label("fib_small");
b.power_isa_ld(3, 0, MockMemory::IO_ADDR as i16); // load input
b.power_isa_addi(1, 0, 0x4000); // setup stack pointer
b.power_isa_bl(fib);
b.power_isa_std(3, 0, MockMemory::IO_ADDR as i16); // store output
let done = b.new_defined_label("done");
b.power_isa_b(done);
b.set_pc(0x1000);
b.define_label(fib);
b.power_isa_cmpldi(0, 3, 1);
b.power_isa_ble(fib_small); // if input <= 1 goto fib_small
// push stack frame
b.power_isa_mflr(0);
b.power_isa_addi(1, 1, -16);
b.power_isa_std(0, 1, 0);
b.power_isa_std(30, 1, 8);
b.power_isa_addi(30, 3, -2);
b.power_isa_addi(3, 3, -1);
b.power_isa_bl(fib); // fib(input - 1)
b.power_isa_mr(2, 3);
b.power_isa_mr(3, 30);
b.power_isa_mr(30, 2);
b.power_isa_bl(fib); // fib(input - 2)
b.power_isa_add(3, 3, 30); // set output to fib(input - 1) + fib(input - 2)
// pop stack frame and return
b.power_isa_ld(0, 1, 0);
b.power_isa_ld(30, 1, 8);
b.power_isa_addi(1, 1, 16);
b.power_isa_mtlr(0);
b.power_isa_blr();
b.define_label(fib_small);
b.power_isa_addi(3, 0, 1); // set output to 1
b.power_isa_blr(); // return
b.build()
}
fn make_load_store_execution_state() -> MockMemory {
MockMemory::new(4, 5, [])
}
}
#[hdl]
struct BrPredDebugState {
call_stack: ArrayVec<UInt<64>, ConstUsize<{ BrPredState::CALL_STACK_CAPACITY }>>,
@ -1045,7 +1245,7 @@ type SimOnlyMemoryChunk = SimOnly<MemoryChunk>;
#[hdl(no_static)]
struct MockMemoryDebugState<C: PhantomConstGet<()> = PhantomConst<()>> {
wrote_output: Bool,
wrote_outputs: Bool,
memory: ArrayType<SimOnlyMemoryChunk, MockMemoryDebugStateMemorySize<C>>,
l1_cache:
Array<TraceAsString<HdlOption<MockCacheLineDebugState>>, { MockMemory::CACHE_LINE_COUNT }>,
@ -1053,9 +1253,9 @@ struct MockMemoryDebugState<C: PhantomConstGet<()> = PhantomConst<()>> {
#[derive(Debug, Clone)]
struct MockMemory {
input: u64,
expected_output: u64,
wrote_output: bool,
input: std::vec::IntoIter<u64>,
expected_output: Vec<u64>,
output: Vec<u64>,
memory: BTreeMap<u64, SimOnlyValue<MemoryChunk>>,
l1_cache: [Option<MockCacheLine>; Self::CACHE_LINE_COUNT],
}
@ -1106,14 +1306,14 @@ impl MockMemory {
const LOG2_CACHE_LINE_COUNT: usize = 2;
const CACHE_LINE_COUNT: usize = 1 << Self::LOG2_CACHE_LINE_COUNT;
fn new<'a, I: IntoIterator<Item = (u64, &'a [u8])>>(
input: u64,
expected_output: u64,
input: Vec<u64>,
expected_output: Vec<u64>,
initial_memory: I,
) -> Self {
let mut retval = Self {
input,
input: input.into_iter(),
output: Vec::with_capacity(expected_output.len()),
expected_output,
wrote_output: false,
memory: BTreeMap::new(),
l1_cache: [const { None }; _],
};
@ -1125,6 +1325,11 @@ impl MockMemory {
retval.cache_flush();
retval
}
fn wrote_outputs(&self) -> bool {
// function filling `output` checks that it matches `expected_output`,
// so we just need to check the length here
self.output.len() == self.expected_output.len()
}
fn cache_flush(&mut self) {
self.l1_cache.fill(None);
}
@ -1179,11 +1384,18 @@ impl MockMemory {
}
Err(FillCacheError::Uncacheable) => {
assert!(!is_speculative);
let mut value = None;
for (chunk_byte_index, byte) in chunk_range.zip(&mut bytes[load_store_range]) {
let offset = chunk_addr
.wrapping_add(chunk_byte_index as u64)
.wrapping_sub(Self::IO_ADDR);
let value = self.input.to_le_bytes();
let value = value
.get_or_insert_with(|| {
self.input
.next()
.unwrap_or_else(|| panic!("read input too many times"))
})
.to_le_bytes();
assert!(offset < value.len() as u64);
*byte = value[offset as usize];
}
@ -1230,11 +1442,16 @@ impl MockMemory {
value[offset as usize] = *byte;
}
let value = u64::from_le_bytes(value);
assert!(
self.output.len() < self.expected_output.len(),
"too many outputs"
);
assert_eq!(
self.expected_output, value,
self.expected_output[self.output.len()],
value,
"output doesn't match expected output"
);
self.wrote_output = true;
self.output.push(value);
}
Err(FillCacheError::CacheMiss) => return Err(AddressCantBeSpeculativelyAccessed),
}
@ -1267,8 +1484,8 @@ impl MockMemory {
fn debug_state(&self) -> SimValue<MockMemoryDebugState> {
let Self {
input: _,
output: _,
expected_output: _,
wrote_output,
memory,
l1_cache,
} = self;
@ -1276,7 +1493,7 @@ impl MockMemory {
let empty_chunk = SimOnlyValue::new(MemoryChunk::default());
#[hdl(sim)]
MockMemoryDebugState::<_> {
wrote_output,
wrote_outputs: self.wrote_outputs(),
memory: SimValue::from_array_elements(
ret_ty.memory,
memory
@ -1409,6 +1626,37 @@ impl MockMemory {
}
}
trait FunnelShift {
fn funnel_shl(high: Self, low: Self, shift: u32) -> Self;
fn funnel_shr(high: Self, low: Self, shift: u32) -> Self;
}
macro_rules! impl_funnel_shift {
($ty:ident) => {
impl FunnelShift for $ty {
fn funnel_shl(high: Self, low: Self, shift: u32) -> Self {
if shift == 0 {
high
} else {
(high << shift) | (low >> ($ty::BITS - shift))
}
}
fn funnel_shr(high: Self, low: Self, shift: u32) -> Self {
if shift == 0 {
high
} else {
(high << ($ty::BITS - shift)) | (low >> shift)
}
}
}
};
}
impl_funnel_shift!(u8);
impl_funnel_shift!(u16);
impl_funnel_shift!(u32);
impl_funnel_shift!(u64);
trait MockExecutionStateTrait: Default {
type DebugState: BundleType;
fn debug_state_ty() -> Self::DebugState;
@ -1541,6 +1789,188 @@ trait MockExecutionStateTrait: Default {
}
}
#[hdl]
fn run_shift_rotate<C: PhantomConstCpuConfig>(
&mut self,
mop: &SimValue<ShiftRotateMOp<PRegNum<C>, PRegNum<C>>>,
src_values: &[SimValue<TraceAsString<PRegValue>>; COMMON_MOP_SRC_LEN],
) -> SimValue<TraceAsString<PRegValue>> {
#[hdl(sim)]
let ShiftRotateMOp::<_, _> { alu_common, mode } = mop;
#[hdl(sim)]
let AluCommonMOp::<_, _, _, _> {
common,
output_integer_mode,
} = alu_common;
#[hdl(sim)]
let CommonMOp::<_, _, _, _, _> {
prefix_pad: _,
dest: _,
src: _,
imm,
} = common;
#[hdl(sim)]
let ShiftRotateMOpImm {
shift_rotate_amount,
shift_rotate_right,
shift_amount_overflow_behavior,
dest_logic_op,
} = imm;
let shift_rotate_amount = #[hdl(sim)]
match shift_rotate_amount {
HdlSome(shift_rotate_amount) => {
shift_rotate_amount.cast_to_static::<UInt<64>>().as_int()
}
HdlNone => src_values[2].inner().int_fp.as_int(),
};
let shifted_rotated = #[hdl(sim)]
match mode {
ShiftRotateMode::FunnelShift2x8Bit => {
if shift_rotate_amount >= 8 {
todo!("large shift_rotate_amount={shift_rotate_amount}");
}
if **shift_rotate_right {
FunnelShift::funnel_shr(
src_values[0].inner().int_fp.as_int() as u8,
src_values[1].inner().int_fp.as_int() as u8,
shift_rotate_amount as _,
) as u64
} else {
FunnelShift::funnel_shl(
src_values[0].inner().int_fp.as_int() as u8,
src_values[1].inner().int_fp.as_int() as u8,
shift_rotate_amount as _,
) as u64
}
}
ShiftRotateMode::FunnelShift2x16Bit => {
if shift_rotate_amount >= 16 {
todo!("large shift_rotate_amount={shift_rotate_amount}");
}
if **shift_rotate_right {
FunnelShift::funnel_shr(
src_values[0].inner().int_fp.as_int() as u16,
src_values[1].inner().int_fp.as_int() as u16,
shift_rotate_amount as _,
) as u64
} else {
FunnelShift::funnel_shl(
src_values[0].inner().int_fp.as_int() as u16,
src_values[1].inner().int_fp.as_int() as u16,
shift_rotate_amount as _,
) as u64
}
}
ShiftRotateMode::FunnelShift2x32Bit => {
if shift_rotate_amount >= 32 {
todo!("large shift_rotate_amount={shift_rotate_amount}");
}
if **shift_rotate_right {
FunnelShift::funnel_shr(
src_values[0].inner().int_fp.as_int() as u32,
src_values[1].inner().int_fp.as_int() as u32,
shift_rotate_amount as _,
) as u64
} else {
FunnelShift::funnel_shl(
src_values[0].inner().int_fp.as_int() as u32,
src_values[1].inner().int_fp.as_int() as u32,
shift_rotate_amount as _,
) as u64
}
}
ShiftRotateMode::FunnelShift2x64Bit => {
if shift_rotate_amount >= 64 {
todo!("large shift_rotate_amount={shift_rotate_amount}");
}
if **shift_rotate_right {
FunnelShift::funnel_shr(
src_values[0].inner().int_fp.as_int(),
src_values[1].inner().int_fp.as_int(),
shift_rotate_amount as _,
)
} else {
FunnelShift::funnel_shl(
src_values[0].inner().int_fp.as_int(),
src_values[1].inner().int_fp.as_int(),
shift_rotate_amount as _,
)
}
}
ShiftRotateMode::SignExt8To64BitThenShift => {
if shift_rotate_amount >= 64 {
todo!("large shift_rotate_amount={shift_rotate_amount}");
}
let input = src_values[0].inner().int_fp.as_int() as i8 as i64;
if **shift_rotate_right {
(input >> shift_rotate_amount) as u64
} else {
(input << shift_rotate_amount) as u64
}
}
ShiftRotateMode::SignExt16To64BitThenShift => {
if shift_rotate_amount >= 64 {
todo!("large shift_rotate_amount={shift_rotate_amount}");
}
let input = src_values[0].inner().int_fp.as_int() as i16 as i64;
if **shift_rotate_right {
(input >> shift_rotate_amount) as u64
} else {
(input << shift_rotate_amount) as u64
}
}
ShiftRotateMode::SignExt32To64BitThenShift => {
if shift_rotate_amount >= 64 {
todo!("large shift_rotate_amount={shift_rotate_amount}");
}
let input = src_values[0].inner().int_fp.as_int() as i32 as i64;
if **shift_rotate_right {
(input >> shift_rotate_amount) as u64
} else {
(input << shift_rotate_amount) as u64
}
}
ShiftRotateMode::ShiftSigned64 => {
if shift_rotate_amount >= 64 {
todo!("large shift_rotate_amount={shift_rotate_amount}");
}
let input = src_values[0].inner().int_fp.as_int() as i64;
if **shift_rotate_right {
(input >> shift_rotate_amount) as u64
} else {
(input << shift_rotate_amount) as u64
}
}
};
let masked = #[hdl(sim)]
if let HdlSome(dest_logic_op) = dest_logic_op {
ShiftRotateDestLogicOp::operation_sim(
dest_logic_op,
shifted_rotated,
&src_values[2].inner().int_fp,
)
.as_int()
} else {
shifted_rotated
};
let int_fp = #[hdl(sim)]
match output_integer_mode {
OutputIntegerMode::Full64 => masked,
OutputIntegerMode::DupLow32 => (masked & 0xFFFFFFFF) | (masked << 32),
OutputIntegerMode::ZeroExt32 => masked as u32 as u64,
OutputIntegerMode::SignExt32 => masked as i32 as u64,
OutputIntegerMode::ZeroExt16 => masked as u16 as u64,
OutputIntegerMode::SignExt16 => masked as i16 as u64,
OutputIntegerMode::ZeroExt8 => masked as u8 as u64,
OutputIntegerMode::SignExt8 => masked as i8 as u64,
};
let retval = #[hdl(sim)]
PRegValue {
int_fp,
flags: PRegFlags::zeroed_sim(), // TODO: compute flags
};
retval.into_trace_as_string()
}
#[hdl]
fn run_branch<C: PhantomConstCpuConfig, SrcCount: KnownSize>(
&mut self,
id: &SimValue<MOpId>,
@ -1582,9 +2012,11 @@ trait MockExecutionStateTrait: Default {
.get(2)
.is_some_and(|src2| *src2 != PRegNum[config].const_zero_sim());
let src2_cond = if has_src2 {
let _ = invert_src2_eq_zero;
let _ = src2;
todo!("has_src2");
if **invert_src2_eq_zero {
src2.inner().int_fp.as_int() != 0
} else {
src2.inner().int_fp.as_int() == 0
}
} else {
true
};
@ -1724,9 +2156,10 @@ trait MockExecutionStateTrait: Default {
AluBranchMOp::<_, _>::LogicalI(mop) => {
todo!("implement LogicalI")
}
AluBranchMOp::<_, _>::ShiftRotate(mop) => {
todo!("implement ShiftRotate")
}
AluBranchMOp::<_, _>::ShiftRotate(mop) => (
Some((self.run_shift_rotate(mop, src_values), empty_predictor_op())),
None,
),
AluBranchMOp::<_, _>::Compare(mop) => (
Some((self.run_compare(mop, src_values), empty_predictor_op())),
None,
@ -3367,7 +3800,7 @@ fn mock_load_store_unit<#[hdl(skip)] MI: MakeInsns>(
.await;
}
sim.write(debug_state, state.debug_state()).await;
sim.write(all_outputs_written, state.memory.wrote_output)
sim.write(all_outputs_written, state.memory.wrote_outputs())
.await;
sim.wait_for_clock_edge(cd.clk).await;
{
@ -3489,6 +3922,59 @@ fn rename_execute_retire_test_harness<#[hdl(skip)] MI: MakeInsns>(config: Phanto
}
}
struct FibonacciInsns;
impl MakeInsns for FibonacciInsns {
fn make_insns() -> Insns {
let mut b = InsnsBuilder::new();
let fib = b.new_label("fib");
let fib_small = b.new_label("fib_small");
b.power_isa_ld(3, 0, MockMemory::IO_ADDR as i16); // load input
b.power_isa_addi(1, 0, 0x4000); // setup stack pointer
b.power_isa_bl(fib);
b.power_isa_std(3, 0, MockMemory::IO_ADDR as i16); // store output
let done = b.new_defined_label("done");
b.power_isa_b(done);
b.set_pc(0x1000);
b.define_label(fib);
b.power_isa_cmpldi(0, 3, 1);
b.power_isa_ble(fib_small); // if input <= 1 goto fib_small
// push stack frame
b.power_isa_mflr(0);
b.power_isa_addi(1, 1, -16);
b.power_isa_std(0, 1, 0);
b.power_isa_std(30, 1, 8);
b.power_isa_addi(30, 3, -2);
b.power_isa_addi(3, 3, -1);
b.power_isa_bl(fib); // fib(input - 1)
b.power_isa_mr(2, 3);
b.power_isa_mr(3, 30);
b.power_isa_mr(30, 2);
b.power_isa_bl(fib); // fib(input - 2)
b.power_isa_add(3, 3, 30); // set output to fib(input - 1) + fib(input - 2)
// pop stack frame and return
b.power_isa_ld(0, 1, 0);
b.power_isa_ld(30, 1, 8);
b.power_isa_addi(1, 1, 16);
b.power_isa_mtlr(0);
b.power_isa_blr();
b.define_label(fib_small);
b.power_isa_addi(3, 0, 1); // set output to 1
b.power_isa_blr(); // return
b.build()
}
fn make_load_store_execution_state() -> MockMemory {
MockMemory::new(vec![4], vec![5], [])
}
}
#[hdl]
#[test]
fn test_rename_execute_retire_fibonacci() {
@ -3613,8 +4099,8 @@ impl MakeInsns for SlowLoopInsns {
});
let loop_count = 4;
MockMemory::new(
loop_count,
expected * loop_count * 2u64.pow(Self::LOG2_RESULT_FACTOR),
vec![loop_count],
vec![expected * loop_count * 2u64.pow(Self::LOG2_RESULT_FACTOR)],
[(Self::CONSTANTS_ADDR, constants.as_flattened())],
)
}
@ -3674,3 +4160,139 @@ fn test_rename_execute_retire_slow_loop() {
panic!();
}
}
/// equivalent of Unix's `head -n1`
struct HeadN1Insns;
impl HeadN1Insns {
const BUFFER_SIZE: u64 = 0x100;
const MAIN_STACK_FRAME_SIZE: u64 = (Self::BUFFER_SIZE + 0x20).next_multiple_of(0x10);
const BUFFER_OFFSET_FROM_SP: i16 = (Self::MAIN_STACK_FRAME_SIZE - Self::BUFFER_SIZE) as i16;
const TEST_INPUT: &str = "Test input.\nLine 2";
}
impl MakeInsns for HeadN1Insns {
fn make_insns() -> Insns {
let mut b = InsnsBuilder::new();
let main = b.new_label("main");
b.power_isa_addi(1, 0, 0x4000); // setup stack pointer
b.power_isa_bl(main);
let done = b.new_defined_label("done");
b.power_isa_b(done);
b.set_pc(0xF00);
let error = b.new_defined_label("error");
b.power_isa_addi(3, 0, -1);
b.power_isa_std(3, 0, MockMemory::IO_ADDR as i16);
b.power_isa_b(error);
b.set_pc(0x1000);
b.define_label(main);
b.power_isa_stdu(1, 1, -(Self::MAIN_STACK_FRAME_SIZE as i16));
b.power_isa_ld(4, 0, MockMemory::IO_ADDR as i16);
b.power_isa_addi(3, 0, Self::BUFFER_SIZE as i16);
b.power_isa_addi(5, 1, Self::BUFFER_OFFSET_FROM_SP - 1);
b.power_isa_mtctr(3);
b.power_isa_addi(3, 0, 0);
let read_loop = b.new_defined_label("read_loop");
b.power_isa_clrlwi(6, 4, 24);
b.power_isa_cmplwi(0, 6, b'\n' as u16);
let done_reading = b.new_label("done_reading");
b.power_isa_beq(done_reading);
b.power_isa_stb(4, 5, 1);
b.power_isa_addi(5, 5, 1);
b.power_isa_addi(3, 3, 1);
b.power_isa_ld(4, 0, MockMemory::IO_ADDR as i16);
b.power_isa_bdnz(read_loop);
b.power_isa_bl(error);
b.define_label(done_reading);
b.power_isa_cmpldi(0, 3, 0);
let done_writing = b.new_label("done_writing");
b.power_isa_beq(done_writing);
b.power_isa_addi(4, 1, Self::BUFFER_OFFSET_FROM_SP - 1);
b.power_isa_mtctr(3);
let write_loop = b.new_defined_label("write_loop");
b.power_isa_lbzu(3, 4, 1);
b.power_isa_std(3, 0, MockMemory::IO_ADDR as i16);
b.power_isa_bdnz(write_loop);
b.define_label(done_writing);
b.power_isa_addi(3, 0, b'\n' as i16);
b.power_isa_std(3, 0, MockMemory::IO_ADDR as i16);
b.power_isa_addi(1, 1, Self::MAIN_STACK_FRAME_SIZE as i16);
b.power_isa_blr();
b.build()
}
fn make_load_store_execution_state() -> MockMemory {
MockMemory::new(
Self::TEST_INPUT.bytes().map(|b| b as u64).collect(),
Self::TEST_INPUT
.split_inclusive('\n')
.next()
.expect("known to contain a newline")
.bytes()
.map(|b| b as u64)
.collect(),
[],
)
}
}
#[hdl]
#[test]
fn test_rename_execute_retire_head_n1() {
let _n = SourceLocation::normalize_files_for_tests();
let mut config = CpuConfig::new(
vec![
UnitConfig::new(UnitKind::AluBranch),
UnitConfig::new(UnitKind::AluBranch),
UnitConfig::new(UnitKind::LoadStore),
UnitConfig::new(UnitKind::TransformedMove),
],
NonZeroUsize::new(20).unwrap(),
);
config.fetch_width = NonZeroUsize::new(2).unwrap();
let m = rename_execute_retire_test_harness::<HeadN1Insns>(PhantomConst::new_sized(config));
let mut sim = Simulation::new(m);
let writer = RcWriter::default();
sim.add_trace_writer(VcdWriterDecls::new(writer.clone()));
struct DumpVcdOnDrop {
writer: Option<RcWriter>,
}
impl Drop for DumpVcdOnDrop {
fn drop(&mut self) {
if let Some(mut writer) = self.writer.take() {
let vcd = String::from_utf8(writer.take()).unwrap();
println!("####### VCD:\n{vcd}\n#######");
}
}
}
let mut writer = DumpVcdOnDrop {
writer: Some(writer),
};
sim.write_clock(sim.io().cd.clk, false);
sim.write_reset(sim.io().cd.rst, true);
for cycle in 0..200 {
sim.advance_time(SimDuration::from_nanos(500));
println!("clock tick: {cycle}");
sim.write_clock(sim.io().cd.clk, true);
sim.advance_time(SimDuration::from_nanos(500));
sim.write_clock(sim.io().cd.clk, false);
sim.write_reset(sim.io().cd.rst, false);
}
assert!(sim.read_bool(sim.io().all_outputs_written));
// FIXME: vcd is just whatever rename_execute_retire does now, which isn't known to be correct
let vcd = String::from_utf8(writer.writer.take().unwrap().take()).unwrap();
println!("####### VCD:\n{vcd}\n#######");
if vcd != include_str!("expected/rename_execute_retire_head_n1.vcd") {
panic!();
}
}