use crate::config::Config;
use crate::tests::support::builder::instruction::InstructionBuilder;
use crate::tests::support::harness::TestContext;
const PROGRAM_BASE: u64 = 0x8000_0000;
const SIM_CONTROL: u32 = 5;
const VALUE: u32 = 6;
const COUNTER: u32 = 7;
const LOOPS: i32 = 50;
fn command(program: &mut Vec<u32>, arg: i32, command: i32) {
let i = InstructionBuilder::new;
program.push(i().addi(VALUE, 0, arg).build());
program.push(i().sd(SIM_CONTROL, VALUE, 8).build());
program.push(i().addi(VALUE, 0, command).build());
program.push(i().sd(SIM_CONTROL, VALUE, 0).build());
}
fn marked_program(before: i32, before_arg: i32) -> Vec<u32> {
let i = InstructionBuilder::new;
let mut program = vec![i().lui(SIM_CONTROL, 0x102).build()];
for _ in 0..40 {
program.push(i().addi(0, 0, 0).build());
}
command(&mut program, before_arg, before);
program.push(i().addi(COUNTER, 0, LOOPS).build());
program.push(i().addi(COUNTER, COUNTER, -1).build());
program.push(i().bne(COUNTER, 0, -4).build());
command(&mut program, 42, 2);
command(&mut program, 5, 3);
program.push(i().jal(0, 0).build());
program
}
fn run(program: &[u32]) -> TestContext {
let mut config = Config::default();
config.system.console = crate::config::Console::Quiet;
let mut ctx = TestContext::new_with_config(&config).load_program(PROGRAM_BASE, program);
ctx.run(20_000);
assert_eq!(ctx.sim.state.check_exit(), Some(5), "the guest ended the run with its code");
ctx
}
fn is_the_loop(instructions: u64) -> bool {
let loop_instructions = 2 * LOOPS as u64;
(loop_instructions..loop_instructions + 20).contains(&instructions)
}
#[test]
fn two_dumps_subtract_to_the_region_between_them_and_keep_the_whole_run() {
let ctx = run(&marked_program(2, 1));
let dumps = &ctx.sim.state.stats_dumps;
assert_eq!(dumps.iter().map(|d| d.label).collect::<Vec<_>>(), vec![1, 42]);
let region = dumps[1].instructions_retired - dumps[0].instructions_retired;
assert!(is_the_loop(region), "the region holds the loop: {region}");
let alu_ops = ctx.sim.state.cores[0].units.stat_paths.commit.op_alu;
let (start, end) = (&dumps[0].stats, &dumps[1].stats);
let counted = |stats: &crate::sim::stats::Stats| stats.get(alu_ops).unwrap_or(0.0);
assert_eq!(counted(&end.since(start)), counted(end) - counted(start));
assert!(counted(start) > 0.0, "the whole run's counts are kept");
}
#[test]
fn a_guest_reset_starts_the_dumped_window_at_the_region() {
let ctx = run(&marked_program(1, 0));
let dumps = &ctx.sim.state.stats_dumps;
assert_eq!(dumps.len(), 1);
assert_eq!(dumps[0].label, 42);
assert!(is_the_loop(dumps[0].instructions_retired), "{}", dumps[0].instructions_retired);
assert!(dumps[0].cycles < ctx.sim.state.cycle, "the window starts after the run did");
}