ibex/dv/cosim/cosim.h
Greg Chadwick 60fbb6ba2f [cosim] Update comment on set_mip in Cosim interface
The concept of pre and post MIP values was introduced a while ago but
the comments in the interface weren't updated to explain what they are.
2025-02-18 16:56:40 +00:00

166 lines
6.9 KiB
C++

// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
#include <string>
#include <vector>
#ifndef COSIM_H_
#define COSIM_H_
// Information about a dside transaction observed on the DUT memory interface
struct DSideAccessInfo {
// set when the access is a store, in which case `data` is the store data from
// the DUT. Otherwise `data` is the load data provided to the DUT.
bool store;
// `addr` is the address and must be 32-bit aligned. `data` and `be` are
// aligned to the address. For example an 8-bit store of 0xff to 0x12 has
// `data` as 0x00ff0000, `addr` as 0x10 and `be` as 0b0100.
uint32_t data;
uint32_t addr;
uint32_t be;
// set if an error response to the transaction is seen.
bool error;
// `misaligned_first` and `misaligned_second` are set when the transaction is
// generated for a misaligned store or load instruction. `misaligned_first`
// is true when the transaction is for the lower half and `misaligned_second`
// is true when the transaction is for the upper half, if it exists.
//
// For example an unaligned 32-bit load to 0x3 produces a transaction with
// `addr` as 0x0 and `misaligned_first` set to true, then a transaction with
// `addr` as 0x4 and `misaligned_second` set to true. An unaligned 16-bit load
// to 0x01 only produces a transaction with `addr` as 0x0 and
// `misaligned_first` set to true, there is no second half.
bool misaligned_first;
bool misaligned_second;
bool misaligned_first_saw_error;
bool m_mode_access;
};
class Cosim {
public:
virtual ~Cosim() {}
// Add a memory to the co-simulator environment.
//
// Use `backdoor_write_mem`/`backdoor_read_mem` to access it from the
// simulation environment.
virtual void add_memory(uint32_t base_addr, size_t size) = 0;
// Write bytes to co-simulator memory.
//
// returns false if write fails (e.g. because no memory exists at the bytes
// being written).
virtual bool backdoor_write_mem(uint32_t addr, size_t len,
const uint8_t *data_in) = 0;
// Read bytes from co-simulator memory.
//
// returns false if read fails (e.g. because no memory exists at the bytes
// being read).
virtual bool backdoor_read_mem(uint32_t addr, size_t len,
uint8_t *data_out) = 0;
// Step the co-simulator, checking register write and PC of executed
// instruction match the supplied values. `write_reg` gives the index of the
// written register along with `write_reg_data` which provides the data. A
// `write_reg` of 0 indicates no register write occurred.
//
// `sync_trap` is set to true when the instruction caused a synchronous trap.
// In this case the instruction doesn't retire so no register write occurs (so
// `write_reg` must be 0).
//
// Returns false if there are any errors; use `get_errors` to obtain details
virtual bool step(uint32_t write_reg, uint32_t write_reg_data, uint32_t pc,
bool sync_trap, bool suppress_reg_write) = 0;
// When more than one of `set_mip`, `set_nmi` or `set_debug_req` is called
// before `step` which one takes effect is chosen by the co-simulator. Which
// should take priority is architecturally defined by the RISC-V
// specification.
// Set the value of MIP.
//
// Two versions of MIP must be supplied the `pre_mip` and the `post_mip`. The
// `pre_mip` is the value of MIP that is used to determine if an interrupt is
// pending. The `post_mip` is the value of MIP that the next instruction
// executed (which will be the first instruction of the interrupt vector when
// an interrupt ir triggered) observes. These will be different in the case
// where an interrupt is raised triggering an interrupt handler but then
// drops before the first instruction of the handler has executed.
//
// At the next call of `step`, the MIP values will take effect (i.e. if it's a
// new interrupt that is enabled it will step straight to that handler).
virtual void set_mip(uint32_t pre_mip, uint32_t post_mip) = 0;
// Set the state of the NMI (non-maskable interrupt) line.
//
// The NMI signal is a level triggered interrupt. When the NMI is taken the
// CPU ignores the NMI line until an `mret` instruction is executed. If the
// NMI is high following the `mret` (regardless of whether it has been low or
// not whilst the first NMI is being handled) a new NMI is taken.
//
// When an NMI is due to be taken that will occur at the next call of `step`.
virtual void set_nmi(bool nmi) = 0;
// Set the state of the internal NMI (non-maskable interrupt) line.
// Behaviour wise this is almost as same as external NMI case explained at
// set_nmi method. Difference is that this one is a response from Ibex rather
// than an input.
virtual void set_nmi_int(bool nmi_int) = 0;
// Set the debug request.
//
// When set to true the core will enter debug mode at the next step
virtual void set_debug_req(bool debug_req) = 0;
// Set the value of mcycle.
//
// The co-simulation model doesn't alter the value of mcycle itself (other
// than instructions that do a direct CSR write). mcycle should be set to the
// correct value before any `step` call that may execute an instruction that
// observes the value of mcycle.
//
// A full 64-bit value is provided setting both the mcycle and mcycleh CSRs.
virtual void set_mcycle(uint64_t mcycle) = 0;
// Set the value of a CSR. This is used when it is needed to have direct
// communication between DUT and Spike (e.g. Performance counters).
virtual void set_csr(const int csr_num, const uint32_t new_val) = 0;
// Set the ICache scramble key valid bit that is visible in CPUCTRLSTS.
virtual void set_ic_scr_key_valid(bool valid) = 0;
// Tell the co-simulation model about observed transactions on the dside
// memory interface of the DUT. Accesses are notified once the response to a
// transaction is seen.
//
// Observed transactions for the DUT are checked against accesses from the
// co-simulation model when a memory access occurs during a `step`. If there
// is a mismatch an error is reported which can be obtained via `get_errors`.
virtual void notify_dside_access(const DSideAccessInfo &access_info) = 0;
// Tell the co-simulation model about an error response to an iside fetch.
//
// `addr` must be 32-bit aligned.
//
// The next step following a call to `set_iside_error` must produce an
// instruction fault at the given address.
virtual void set_iside_error(uint32_t addr) = 0;
// Get a vector of strings describing errors that have occurred during `step`
virtual const std::vector<std::string> &get_errors() = 0;
// Clear internal vector of error descriptions
virtual void clear_errors() = 0;
// Returns a count of instructions executed by co-simulator and DUT without
// failures.
virtual unsigned int get_insn_cnt() = 0;
};
#endif // COSIM_H_