unit::alu_branch: implement for ShiftRotateMOp; TODO: flags and shift amount overflow

This commit is contained in:
Jacob Lifshay 2026-05-25 23:15:43 -07:00
parent dd2fe36869
commit 99c019431b
Signed by: programmerjake
SSH key fingerprint: SHA256:HnFTLGpSm4Q4Fj502oCFisjZSoakwEuTsJJMSke63RQ
6 changed files with 133317 additions and 1315 deletions

View file

@ -6,7 +6,8 @@ use crate::{
instruction::{ instruction::{
AddSubMOp, AluBranchMOp, AluCommonMOp, BranchMOp, COMMON_MOP_SRC_LEN, CommonMOp, AddSubMOp, AluBranchMOp, AluCommonMOp, BranchMOp, COMMON_MOP_SRC_LEN, CommonMOp,
CommonMOpDefaultImm, CompareMOp, CompareMode, ConditionMode, LogicalFlagsMOp, LogicalMOp, CommonMOpDefaultImm, CompareMOp, CompareMode, ConditionMode, LogicalFlagsMOp, LogicalMOp,
OutputIntegerMode, PRegNum, ReadSpecialMOp, ShiftRotateMOp, OutputIntegerMode, PRegNum, ReadSpecialMOp, ShiftRotateDestLogicOp, ShiftRotateMOp,
ShiftRotateMOpImm, ShiftRotateMode,
}, },
next_pc::CallStackOp, next_pc::CallStackOp,
register::{ register::{
@ -280,17 +281,227 @@ fn logical_i<C: PhantomConstCpuConfig>(
PRegValue::zeroed().to_trace_as_string() PRegValue::zeroed().to_trace_as_string()
} }
#[hdl]
fn funnel_shift<W: Size, A: Size>(
high: impl ToExpr<Type = UIntType<W>>,
low: impl ToExpr<Type = UIntType<W>>,
shift_right: impl ToExpr<Type = Bool>,
shift_amount: impl ToExpr<Type = UIntType<A>>,
) -> Expr<UIntType<W>> {
let high = high.to_expr();
let low = low.to_expr();
let shift_right = shift_right.to_expr();
let shift_amount = shift_amount.to_expr();
let int_ty = high.ty();
assert_eq!(int_ty, low.ty());
#[hdl]
let funnel_shift_high = wire(int_ty);
connect(funnel_shift_high, high);
#[hdl]
let funnel_shift_low = wire(int_ty);
connect(funnel_shift_low, low);
#[hdl]
let funnel_shift_right = wire();
connect(funnel_shift_right, shift_right);
#[hdl]
let funnel_shift_amount = wire(
UInt[shift_amount
.ty()
.width()
.min(UInt::range_usize(0..int_ty.width()).width())],
);
connect_any(funnel_shift_amount, shift_amount);
#[hdl]
let funnel_shift_reverse_amount = wire(funnel_shift_amount.ty());
connect_any(
funnel_shift_reverse_amount,
int_ty.width() - funnel_shift_amount,
);
#[hdl]
let funnel_shift_high_left_amount = wire(funnel_shift_amount.ty());
#[hdl]
let funnel_shift_low_right_amount = wire(funnel_shift_amount.ty());
#[hdl]
let funnel_shift_retval = wire(int_ty);
connect_any(
funnel_shift_retval,
(funnel_shift_high << funnel_shift_high_left_amount)
| (funnel_shift_low >> funnel_shift_low_right_amount),
);
#[hdl]
if funnel_shift_right {
connect(funnel_shift_high_left_amount, funnel_shift_reverse_amount);
connect(funnel_shift_low_right_amount, funnel_shift_amount);
#[hdl]
if shift_amount.cmp_eq(0u8) {
connect(funnel_shift_retval, funnel_shift_low);
}
} else {
connect(funnel_shift_high_left_amount, funnel_shift_amount);
connect(funnel_shift_low_right_amount, funnel_shift_reverse_amount);
#[hdl]
if shift_amount.cmp_eq(0u8) {
connect(funnel_shift_retval, funnel_shift_high);
}
}
funnel_shift_retval
}
#[hdl] #[hdl]
fn shift_rotate<C: PhantomConstCpuConfig>( fn shift_rotate<C: PhantomConstCpuConfig>(
global_state: Expr<GlobalState>, global_state: Expr<GlobalState>,
pc: Expr<UInt<64>>,
mop: Expr<ShiftRotateMOp<PRegNum<C>, PRegNum<C>>>, mop: Expr<ShiftRotateMOp<PRegNum<C>, PRegNum<C>>>,
src_values: Expr<Array<TraceAsString<PRegValue>, { COMMON_MOP_SRC_LEN }>>, src_values: Expr<Array<TraceAsString<PRegValue>, { COMMON_MOP_SRC_LEN }>>,
) -> Expr<TraceAsString<PRegValue>> { ) -> Expr<TraceAsString<PRegValue>> {
#[hdl] #[hdl]
let GlobalState { flags_mode } = global_state; let GlobalState { flags_mode } = global_state;
// TODO: finish #[hdl]
PRegValue::zeroed().to_trace_as_string() let ShiftRotateMOp::<_, _> { alu_common, mode } = mop;
#[hdl]
let AluCommonMOp::<_, _, _, _> {
common,
output_integer_mode,
} = alu_common;
#[hdl]
let ShiftRotateMOpImm {
shift_rotate_amount: shift_rotate_amount_opt,
shift_rotate_right,
shift_amount_overflow_behavior,
dest_logic_op,
} = common.imm;
#[hdl]
let shift_rotate_amount = wire();
#[hdl]
match shift_rotate_amount_opt {
HdlSome(shift_rotate_amount_opt) => connect(
shift_rotate_amount,
shift_rotate_amount_opt.cast_to_static::<UInt<64>>(),
),
HdlNone => connect(shift_rotate_amount, src_values[2].int_fp),
}
#[hdl]
let funnel_shift_2x64_high: UInt<64> = wire();
#[hdl]
let funnel_shift_2x64_low: UInt<64> = wire();
#[hdl]
let funnel_shift_2x64_amount = wire(UInt::range_usize(0..funnel_shift_2x64_high.ty().width()));
#[hdl]
let funnel_shift_2x64_output: UInt<64> = wire();
connect(
funnel_shift_2x64_output,
funnel_shift(
funnel_shift_2x64_high,
funnel_shift_2x64_low,
shift_rotate_right,
funnel_shift_2x64_amount,
),
);
#[hdl]
let shifted_rotated: UInt<64> = wire();
let excess_bits_for_2x64 = |width| funnel_shift_2x64_low.ty().width() - width;
let connect_for_funnel_shift = |width| {
// TODO: handle large shift_rotate_amount >= width
connect(
funnel_shift_2x64_high,
src_values[0]
.int_fp
.cast_to(UInt[width])
.cast_to_static::<UInt<64>>(),
);
connect_any(
funnel_shift_2x64_low,
src_values[1].int_fp.cast_to(UInt[width]) << excess_bits_for_2x64(width),
);
connect_any(funnel_shift_2x64_amount, shift_rotate_amount);
#[hdl]
if shift_rotate_right {
connect_any(
shifted_rotated,
funnel_shift_2x64_output >> excess_bits_for_2x64(8),
);
} else {
connect(
shifted_rotated,
funnel_shift_2x64_output
.cast_to(UInt[width])
.cast_to_static::<UInt<64>>(),
);
}
};
let connect_for_sign_ext_then_shift = |width| {
// TODO: handle large shift_rotate_amount >= 64
let input = src_values[0]
.int_fp
.cast_to(SInt[width])
.cast_to_static::<SInt<64>>();
let input_sign_bit = input >> (input.ty().width() - 1);
#[hdl]
if shift_rotate_right {
connect(funnel_shift_2x64_high, input_sign_bit.cast_to_static());
connect(funnel_shift_2x64_low, input.cast_to_static());
connect_any(funnel_shift_2x64_amount, shift_rotate_amount);
connect(shifted_rotated, funnel_shift_2x64_output);
} else {
connect(funnel_shift_2x64_high, input.cast_to_static());
connect(funnel_shift_2x64_low, 0u64);
connect_any(funnel_shift_2x64_amount, shift_rotate_amount);
connect(shifted_rotated, funnel_shift_2x64_output);
}
};
#[hdl]
match mode {
ShiftRotateMode::FunnelShift2x8Bit => connect_for_funnel_shift(8),
ShiftRotateMode::FunnelShift2x16Bit => connect_for_funnel_shift(16),
ShiftRotateMode::FunnelShift2x32Bit => connect_for_funnel_shift(32),
ShiftRotateMode::FunnelShift2x64Bit => connect_for_funnel_shift(64),
ShiftRotateMode::SignExt8To64BitThenShift => connect_for_sign_ext_then_shift(8),
ShiftRotateMode::SignExt16To64BitThenShift => connect_for_sign_ext_then_shift(16),
ShiftRotateMode::SignExt32To64BitThenShift => connect_for_sign_ext_then_shift(32),
ShiftRotateMode::ShiftSigned64 => connect_for_sign_ext_then_shift(64),
}
#[hdl]
let masked: UInt<64> = wire();
#[hdl]
if let HdlSome(dest_logic_op) = dest_logic_op {
connect(
masked,
ShiftRotateDestLogicOp::operation(dest_logic_op, shifted_rotated, src_values[2].int_fp),
);
} else {
connect(masked, shifted_rotated);
}
#[hdl]
let int_fp: UInt<64> = wire();
fn connect_int_fp_cast<T: IntType + StaticType>(int_fp: Expr<UInt<64>>, masked: Expr<UInt<64>>)
where
UInt<64>: CastToImpl<T>,
T: CastToImpl<UInt<64>>,
{
connect(
int_fp,
masked.cast_to_static::<T>().cast_to_static::<UInt<64>>(),
)
}
#[hdl]
match output_integer_mode {
OutputIntegerMode::Full64 => connect(int_fp, masked),
OutputIntegerMode::DupLow32 => connect_any(
int_fp,
masked.cast_to_static::<UInt<32>>() | (masked.cast_to_static::<UInt<32>>() << 32),
),
OutputIntegerMode::ZeroExt32 => connect_int_fp_cast::<UInt<32>>(int_fp, masked),
OutputIntegerMode::SignExt32 => connect_int_fp_cast::<SInt<32>>(int_fp, masked),
OutputIntegerMode::ZeroExt16 => connect_int_fp_cast::<UInt<16>>(int_fp, masked),
OutputIntegerMode::SignExt16 => connect_int_fp_cast::<SInt<16>>(int_fp, masked),
OutputIntegerMode::ZeroExt8 => connect_int_fp_cast::<UInt<8>>(int_fp, masked),
OutputIntegerMode::SignExt8 => connect_int_fp_cast::<SInt<8>>(int_fp, masked),
}
let retval = #[hdl]
PRegValue {
int_fp,
flags: PRegFlags::zeroed(), // TODO: compute flags
};
retval.into_trace_as_string()
} }
#[hdl] #[hdl]
@ -703,10 +914,7 @@ pub fn alu_branch(config: PhantomConst<CpuConfig>, unit_index: usize) {
); );
} }
AluBranchMOp::<_, _>::ShiftRotate(mop) => { AluBranchMOp::<_, _>::ShiftRotate(mop) => {
connect( connect(dest_value, shift_rotate(global_state, mop, src_values));
dest_value,
shift_rotate(global_state, mop_instance.pc, mop, src_values),
);
} }
AluBranchMOp::<_, _>::Compare(mop) => { AluBranchMOp::<_, _>::Compare(mop) => {
connect(dest_value, compare(global_state, mop, src_values)); connect(dest_value, compare(global_state, mop, src_values));

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

@ -1642,7 +1642,7 @@ macro_rules! impl_funnel_shift {
} }
fn funnel_shr(high: Self, low: Self, shift: u32) -> Self { fn funnel_shr(high: Self, low: Self, shift: u32) -> Self {
if shift == 0 { if shift == 0 {
high low
} else { } else {
(high << ($ty::BITS - shift)) | (low >> shift) (high << ($ty::BITS - shift)) | (low >> shift)
} }
@ -4585,6 +4585,42 @@ fn test_rename_execute_retire_head_n1() {
assert!(sim.read_bool(sim.io().all_outputs_written)); assert!(sim.read_bool(sim.io().all_outputs_written));
} }
#[hdl]
#[test]
fn test_rename_execute_retire_head_n1_real() {
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),
AluBranchKind::Real,
);
let mut sim = Simulation::new(m);
let _checked_vcd_output = checked_vcd_output!(
&mut sim,
"tests/expected/rename_execute_retire_head_n1_real.vcd",
);
sim.write_clock(sim.io().cd.clk, false);
sim.write_reset(sim.io().cd.rst, true);
for cycle in 0..300 {
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));
}
struct SaveRestoreGprsInsns; struct SaveRestoreGprsInsns;
impl SaveRestoreGprsInsns { impl SaveRestoreGprsInsns {