| // 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; |
| }; |
| |
| 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. |
| // |
| // At the next call of `step`, the MIP value 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 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_ |