forked from libre-chip/cpu
Compare commits
2 commits
225ceb0dfa
...
99c019431b
| Author | SHA1 | Date | |
|---|---|---|---|
| 99c019431b | |||
| dd2fe36869 |
6 changed files with 362964 additions and 566 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));
|
||||
|
|
|
|||
92406
crates/cpu/tests/expected/rename_execute_retire_fibonacci_real.vcd
generated
Normal file
92406
crates/cpu/tests/expected/rename_execute_retire_fibonacci_real.vcd
generated
Normal file
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
140480
crates/cpu/tests/expected/rename_execute_retire_slow_loop_real.vcd
generated
Normal file
140480
crates/cpu/tests/expected/rename_execute_retire_slow_loop_real.vcd
generated
Normal file
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)
|
||||
}
|
||||
|
|
@ -4270,6 +4270,43 @@ fn test_rename_execute_retire_fibonacci_combinatorial() {
|
|||
assert!(sim.read_bool(sim.io().all_outputs_written));
|
||||
}
|
||||
|
||||
#[hdl]
|
||||
#[test]
|
||||
fn test_rename_execute_retire_fibonacci_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::AluBranch),
|
||||
UnitConfig::new(UnitKind::LoadStore),
|
||||
UnitConfig::new(UnitKind::TransformedMove),
|
||||
],
|
||||
NonZeroUsize::new(20).unwrap(),
|
||||
);
|
||||
config.fetch_width = NonZeroUsize::new(3).unwrap();
|
||||
let m = rename_execute_retire_test_harness::<FibonacciInsns>(
|
||||
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_fibonacci_real.vcd",
|
||||
);
|
||||
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));
|
||||
}
|
||||
|
||||
struct SlowLoopInsns;
|
||||
|
||||
impl SlowLoopInsns {
|
||||
|
|
@ -4389,6 +4426,46 @@ fn test_rename_execute_retire_slow_loop() {
|
|||
assert!(sim.read_bool(sim.io().started_any_l2_reg_file_ops));
|
||||
}
|
||||
|
||||
#[hdl]
|
||||
#[test]
|
||||
fn test_rename_execute_retire_slow_loop_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::AluBranch),
|
||||
UnitConfig::new(UnitKind::AluBranch),
|
||||
UnitConfig::new(UnitKind::LoadStore),
|
||||
UnitConfig::new(UnitKind::TransformedMove),
|
||||
],
|
||||
NonZeroUsize::new(20).unwrap(),
|
||||
);
|
||||
config.fetch_width = NonZeroUsize::new(4).unwrap();
|
||||
let m = rename_execute_retire_test_harness::<SlowLoopInsns>(
|
||||
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_slow_loop_real.vcd",
|
||||
);
|
||||
sim.write_clock(sim.io().cd.clk, false);
|
||||
sim.write_reset(sim.io().cd.rst, true);
|
||||
for cycle in 0..350 {
|
||||
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));
|
||||
// make sure we're actually testing L2 reg file ops
|
||||
assert!(sim.read_bool(sim.io().started_any_l2_reg_file_ops));
|
||||
}
|
||||
|
||||
/// equivalent of Unix's `head -n1`
|
||||
struct HeadN1Insns;
|
||||
|
||||
|
|
@ -4508,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