forked from libre-chip/cpu
unit::alu_branch: implement for ShiftRotateMOp; TODO: flags and shift amount overflow
This commit is contained in:
parent
dd2fe36869
commit
99c019431b
6 changed files with 133317 additions and 1315 deletions
|
|
@ -6,7 +6,8 @@ use crate::{
|
|||
instruction::{
|
||||
AddSubMOp, AluBranchMOp, AluCommonMOp, BranchMOp, COMMON_MOP_SRC_LEN, CommonMOp,
|
||||
CommonMOpDefaultImm, CompareMOp, CompareMode, ConditionMode, LogicalFlagsMOp, LogicalMOp,
|
||||
OutputIntegerMode, PRegNum, ReadSpecialMOp, ShiftRotateMOp,
|
||||
OutputIntegerMode, PRegNum, ReadSpecialMOp, ShiftRotateDestLogicOp, ShiftRotateMOp,
|
||||
ShiftRotateMOpImm, ShiftRotateMode,
|
||||
},
|
||||
next_pc::CallStackOp,
|
||||
register::{
|
||||
|
|
@ -280,17 +281,227 @@ fn logical_i<C: PhantomConstCpuConfig>(
|
|||
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]
|
||||
fn shift_rotate<C: PhantomConstCpuConfig>(
|
||||
global_state: Expr<GlobalState>,
|
||||
pc: Expr<UInt<64>>,
|
||||
mop: Expr<ShiftRotateMOp<PRegNum<C>, PRegNum<C>>>,
|
||||
src_values: Expr<Array<TraceAsString<PRegValue>, { COMMON_MOP_SRC_LEN }>>,
|
||||
) -> Expr<TraceAsString<PRegValue>> {
|
||||
#[hdl]
|
||||
let GlobalState { flags_mode } = global_state;
|
||||
// TODO: finish
|
||||
PRegValue::zeroed().to_trace_as_string()
|
||||
#[hdl]
|
||||
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]
|
||||
|
|
@ -703,10 +914,7 @@ pub fn alu_branch(config: PhantomConst<CpuConfig>, unit_index: usize) {
|
|||
);
|
||||
}
|
||||
AluBranchMOp::<_, _>::ShiftRotate(mop) => {
|
||||
connect(
|
||||
dest_value,
|
||||
shift_rotate(global_state, mop_instance.pc, mop, src_values),
|
||||
);
|
||||
connect(dest_value, shift_rotate(global_state, mop, src_values));
|
||||
}
|
||||
AluBranchMOp::<_, _>::Compare(mop) => {
|
||||
connect(dest_value, compare(global_state, mop, src_values));
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
128873
crates/cpu/tests/expected/rename_execute_retire_head_n1_real.vcd
generated
Normal file
128873
crates/cpu/tests/expected/rename_execute_retire_head_n1_real.vcd
generated
Normal file
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
|
|
@ -1642,7 +1642,7 @@ macro_rules! impl_funnel_shift {
|
|||
}
|
||||
fn funnel_shr(high: Self, low: Self, shift: u32) -> Self {
|
||||
if shift == 0 {
|
||||
high
|
||||
low
|
||||
} else {
|
||||
(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));
|
||||
}
|
||||
|
||||
#[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;
|
||||
|
||||
impl SaveRestoreGprsInsns {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue