use crate::Simulator;
use crate::common::PhysAddr;
use crate::config::{BackendKind, Config};
use crate::isa::csr::{MSTATUS_FS, MSTATUS_FS_INIT, MSTATUS_VS, MSTATUS_VS_INIT};
use crate::system::SystemState;
use crate::system::simulator::{StopAt, StopReason};
use crate::tests::support::builder::instruction::{ECALL, InstructionBuilder, MRET};
use crate::tests::support::harness::TestContext;
pub const PROGRAM_BASE: u64 = 0x8000_0000;
pub const HANDLER: u64 = PROGRAM_BASE + 0x400;
pub const DATA: u64 = PROGRAM_BASE + 0x1000;
pub const T0: u32 = 5;
pub const T1: u32 = 6;
pub const T2: u32 = 7;
pub const A0: u32 = 10;
pub const A1: u32 = 11;
pub const A2: u32 = 12;
pub const A7: u32 = 17;
pub const MEPC: u32 = 0x341;
pub const MTVEC: u32 = 0x305;
pub const MHARTID: u32 = 0xF14;
const SYS_EXIT: i32 = 93;
pub const BACKENDS: [BackendKind; 2] = [BackendKind::InOrder, BackendKind::OutOfOrder];
pub fn config(backend: BackendKind) -> Config {
let mut config = Config::default();
config.pipeline.backend = backend;
config.system.console = crate::config::Console::Quiet;
config
}
pub fn store_words(ctx: &mut TestContext, at: u64, words: &[u32]) {
for (n, word) in words.iter().enumerate() {
ctx.sim.probe_mem_store(PhysAddr::new(at + 4 * n as u64), u64::from(*word), 4);
}
}
pub fn system(backend: BackendKind, program: &[u32], handler: &[u32]) -> TestContext {
system_with(&config(backend), program, handler)
}
pub fn system_with(config: &Config, program: &[u32], handler: &[u32]) -> TestContext {
prepare(TestContext::new_with_config(config), program, handler)
}
pub fn system_with_configured_latency(
config: &Config,
program: &[u32],
handler: &[u32],
) -> TestContext {
let ctx = TestContext { sim: Simulator::new(SystemState::build(config, "")) };
prepare(ctx, program, handler)
}
fn prepare(ctx: TestContext, program: &[u32], handler: &[u32]) -> TestContext {
let mut ctx = ctx.load_program(PROGRAM_BASE, program);
store_words(&mut ctx, HANDLER, handler);
let csrs = &mut ctx.sim.state.harts[0].csrs;
csrs.mstatus = (csrs.mstatus & !(MSTATUS_FS | MSTATUS_VS)) | MSTATUS_FS_INIT | MSTATUS_VS_INIT;
if !handler.is_empty() {
csrs.mtvec = HANDLER;
}
ctx.set_reg(A1 as usize, DATA);
ctx.set_reg(A2 as usize, DATA + 0x100);
ctx.sim.set_direct_mode(true);
ctx.sim.sync_arch_regs();
ctx
}
pub fn ending_in_spin(mut program: Vec<u32>) -> (Vec<u32>, u64) {
let end = PROGRAM_BASE + 4 * program.len() as u64;
program.push(InstructionBuilder::new().jal(0, 0).build());
(program, end)
}
pub fn run_to_pc(ctx: &mut TestContext, end: u64, context: &str) {
let stop = StopAt { pcs: vec![end], cycles: Some(100_000), ..StopAt::default() };
let reason = ctx.sim.run_to(&stop).expect("the run ticks");
assert_eq!(reason, StopReason::Pc { hart: 0 }, "{context}");
}
pub fn run_to_exit(ctx: &mut TestContext, context: &str) {
let reason = ctx
.sim
.run_to(&StopAt { cycles: Some(100_000), ..StopAt::default() })
.expect("the run ticks");
assert_eq!(reason, StopReason::Exited(0), "{context}");
}
pub fn exit_sequence() -> [u32; 3] {
let i = InstructionBuilder::new;
[i().addi(A0, 0, 0).build(), i().addi(A7, 0, SYS_EXIT).build(), ECALL]
}
pub fn three_handled_ecalls() -> (Vec<u32>, Vec<u32>) {
let i = InstructionBuilder::new;
let mut program = vec![
i().addi(T1, 0, 3).build(),
ECALL,
i().addi(T1, T1, -1).build(),
i().bne(T1, 0, -8).build(),
i().csrrw(0, MTVEC, 0).build(),
];
program.extend(exit_sequence());
let handler = vec![
i().csrrs(T0, MEPC, 0).build(),
i().addi(T0, T0, 4).build(),
i().csrrw(0, MEPC, T0).build(),
MRET,
];
(program, handler)
}