Skip to content

Commit 648fadb

Browse files
committed
[dv] Add co-simulation framework
1 parent f4e3eef commit 648fadb

File tree

8 files changed

+961
-0
lines changed

8 files changed

+961
-0
lines changed

dv/cosim/cosim.core

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
CAPI=2:
2+
# Copyright lowRISC contributors.
3+
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
4+
# SPDX-License-Identifier: Apache-2.0
5+
6+
name: "lowrisc:dv:cosim"
7+
description: "Co-simulator framework"
8+
filesets:
9+
files_cpp:
10+
files:
11+
- cosim.h: { is_include_file: true }
12+
- spike_cosim.cc
13+
- spike_cosim.h: { is_include_file: true }
14+
file_type: cppSource
15+
16+
targets:
17+
default:
18+
filesets:
19+
- files_cpp

dv/cosim/cosim.h

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
// Copyright lowRISC contributors.
2+
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3+
// SPDX-License-Identifier: Apache-2.0
4+
5+
#include <string>
6+
#include <vector>
7+
8+
#ifndef COSIM_H_
9+
#define COSIM_H_
10+
11+
// Information about a dside transaction observed on the DUT memory interface
12+
struct DSideAccessInfo {
13+
// set when the access is a store, in which case `data` is the store data from
14+
// the DUT. Otherwise `data` is the load data provided to the DUT.
15+
bool store;
16+
// `addr` is the address and must be 32-bit aligned. `data` and `be` are
17+
// aligned to the address. For example an 8-bit store of 0xff to 0x12 has
18+
// `data` as 0x00ff0000, `addr` as 0x10 and `be` as 0b0100.
19+
uint32_t data;
20+
uint32_t addr;
21+
uint32_t be;
22+
23+
// set if an error response to the transaction is seen.
24+
bool error;
25+
26+
// `misaligned_first` and `misaligned_second` are set when the transaction is
27+
// generated for a misaligned store or load instruction. `misaligned_first`
28+
// is true when the transaction is for the lower half and `misaligned_second`
29+
// is true when the transaction is for the upper half, if it exists.
30+
//
31+
// For example an unaligned 32-bit load to 0x3 produces a transaction with
32+
// `addr` as 0x0 and `misaligned_first` set to true, then a transaction with
33+
// `addr` as 0x4 and `misaligned_second` set to true. An unaligned 16-bit load
34+
// to 0x01 only produces a transaction with `addr` as 0x0 and
35+
// `misaligned_first` set to true, there is no second half.
36+
bool misaligned_first;
37+
bool misaligned_second;
38+
};
39+
40+
class Cosim {
41+
public:
42+
virtual ~Cosim() {}
43+
44+
// Add a memory to the co-simulator environment.
45+
//
46+
// Use `backdoor_write_mem`/`backdoor_read_mem` to access it from the
47+
// simulation environment.
48+
virtual void add_memory(uint32_t base_addr, size_t size) = 0;
49+
50+
// Write bytes to co-simulator memory.
51+
//
52+
// returns false if write fails (e.g. because no memory exists at the bytes
53+
// being written).
54+
virtual bool backdoor_write_mem(uint32_t addr, size_t len,
55+
const uint8_t *data_in) = 0;
56+
57+
// Read bytes from co-simulator memory.
58+
//
59+
// returns false if read fails (e.g. because no memory exists at the bytes
60+
// being read).
61+
virtual bool backdoor_read_mem(uint32_t addr, size_t len,
62+
uint8_t *data_out) = 0;
63+
64+
// Step the co-simulator, checking register write and PC of executed
65+
// instruction match the supplied values. `write_reg` gives the index of the
66+
// written register along with `write_reg_data` which provides the data. A
67+
// `write_reg` of 0 indicates no register write occurred.
68+
//
69+
// `sync_trap` is set to true when the instruction caused a synchronous trap.
70+
// In this case the instruction doesn't retire so no register write occurs (so
71+
// `write_reg` must be 0).
72+
//
73+
// Returns false if there are any errors; use `get_errors` to obtain details
74+
virtual bool step(uint32_t write_reg, uint32_t write_reg_data, uint32_t pc,
75+
bool sync_trap) = 0;
76+
77+
// When more than one of `set_mip`, `set_nmi` or `set_debug_req` is called
78+
// before `step` which one takes effect is chosen by the co-simulator. Which
79+
// should take priority is architecturally defined by the RISC-V
80+
// specification.
81+
82+
// Set the value of MIP.
83+
//
84+
// At the next call of `step`, the MIP value will take effect (i.e. if it's a
85+
// new interrupt that is enabled it will step straight to that handler).
86+
virtual void set_mip(uint32_t mip) = 0;
87+
88+
// Set the state of the NMI (non-maskable interrupt) line.
89+
//
90+
// The NMI signal is a level triggered interrupt. When the NMI is taken the
91+
// CPU ignores the NMI line until an `mret` instruction is executed. If the
92+
// NMI is high following the `mret` (regardless of whether it has been low or
93+
// not whilst the first NMI is being handled) a new NMI is taken.
94+
//
95+
// When an NMI is due to be taken that will occur at the next call of `step`.
96+
virtual void set_nmi(bool nmi) = 0;
97+
98+
// Set the debug request.
99+
//
100+
// When set to true the core will enter debug mode at the next step
101+
virtual void set_debug_req(bool debug_req) = 0;
102+
103+
// Set the value of mcycle.
104+
//
105+
// The co-simulation model doesn't alter the value of mcycle itself (other
106+
// than instructions that do a direct CSR write). mcycle should be set to the
107+
// correct value before any `step` call that may execute an instruction that
108+
// observes the value of mcycle.
109+
//
110+
// A full 64-bit value is provided setting both the mcycle and mcycleh CSRs.
111+
virtual void set_mcycle(uint64_t mcycle) = 0;
112+
113+
// Tell the co-simulation model about observed transactions on the dside
114+
// memory interface of the DUT. Accesses are notified once the response to a
115+
// transaction is seen.
116+
//
117+
// Observed transactions for the DUT are checked against accesses from the
118+
// co-simulation model when a memory access occurs during a `step`. If there
119+
// is a mismatch an error is reported which can be obtained via `get_errors`.
120+
virtual void notify_dside_access(const DSideAccessInfo &access_info) = 0;
121+
122+
// Tell the co-simulation model about an error response to an iside fetch.
123+
//
124+
// `addr` must be 32-bit aligned.
125+
//
126+
// The next step following a call to `set_iside_error` must produce an
127+
// instruction fault at the given address.
128+
virtual void set_iside_error(uint32_t addr) = 0;
129+
130+
// Get a vector of strings describing errors that have occurred during `step`
131+
virtual const std::vector<std::string> &get_errors() = 0;
132+
133+
// Clear internal vector of error descriptions
134+
virtual void clear_errors() = 0;
135+
};
136+
137+
#endif // COSIM_H_

dv/cosim/cosim_dpi.cc

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
// Copyright lowRISC contributors.
2+
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3+
// SPDX-License-Identifier: Apache-2.0
4+
5+
#include <svdpi.h>
6+
#include <cassert>
7+
8+
#include "cosim.h"
9+
#include "cosim_dpi.h"
10+
11+
int riscv_cosim_step(Cosim *cosim, const svBitVecVal *write_reg,
12+
const svBitVecVal *write_reg_data, const svBitVecVal *pc,
13+
svBit sync_trap) {
14+
assert(cosim);
15+
16+
return cosim->step(write_reg[0], write_reg_data[0], pc[0], sync_trap) ? 1 : 0;
17+
}
18+
19+
void riscv_cosim_set_mip(Cosim *cosim, const svBitVecVal *mip) {
20+
assert(cosim);
21+
22+
cosim->set_mip(mip[0]);
23+
}
24+
25+
void riscv_cosim_set_nmi(Cosim *cosim, svBit nmi) {
26+
assert(cosim);
27+
28+
cosim->set_nmi(nmi);
29+
}
30+
31+
void riscv_cosim_set_debug_req(Cosim *cosim, svBit debug_req) {
32+
assert(cosim);
33+
34+
cosim->set_debug_req(debug_req);
35+
}
36+
37+
void riscv_cosim_set_mcycle(Cosim *cosim, svBitVecVal *mcycle) {
38+
assert(cosim);
39+
40+
uint64_t mcycle_full = mcycle[0] | (uint64_t)mcycle[1] << 32;
41+
cosim->set_mcycle(mcycle_full);
42+
}
43+
44+
void riscv_cosim_notify_dside_access(Cosim *cosim, svBit store,
45+
svBitVecVal *addr, svBitVecVal *data,
46+
svBitVecVal *be, svBit error,
47+
svBit misaligned_first,
48+
svBit misaligned_second) {
49+
assert(cosim);
50+
51+
cosim->notify_dside_access(
52+
DSideAccessInfo{.store = store,
53+
.data = data[0],
54+
.addr = addr[0],
55+
.be = be[0],
56+
.error = error,
57+
.misaligned_first = misaligned_first,
58+
.misaligned_second = misaligned_second});
59+
}
60+
61+
void riscv_cosim_set_iside_error(Cosim *cosim, svBitVecVal *addr) {
62+
assert(cosim);
63+
64+
cosim->set_iside_error(addr[0]);
65+
}
66+
67+
int riscv_cosim_get_num_errors(Cosim *cosim) {
68+
assert(cosim);
69+
70+
return cosim->get_errors().size();
71+
}
72+
73+
const char *riscv_cosim_get_error(Cosim *cosim, int index) {
74+
assert(cosim);
75+
76+
if (index >= cosim->get_errors().size()) {
77+
return nullptr;
78+
}
79+
80+
return cosim->get_errors()[index].c_str();
81+
}
82+
83+
void riscv_cosim_clear_errors(Cosim *cosim) {
84+
assert(cosim);
85+
86+
cosim->clear_errors();
87+
}
88+
89+
void riscv_cosim_write_mem_byte(Cosim *cosim, const svBitVecVal *addr,
90+
const svBitVecVal *d) {
91+
assert(cosim);
92+
uint8_t byte = d[0] & 0xff;
93+
cosim->backdoor_write_mem(addr[0], 1, &byte);
94+
}

dv/cosim/cosim_dpi.core

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
CAPI=2:
2+
# Copyright lowRISC contributors.
3+
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
4+
# SPDX-License-Identifier: Apache-2.0
5+
6+
name: "lowrisc:dv:cosim_dpi"
7+
description: "DPI wrapper for Co-simulator framework"
8+
filesets:
9+
files_cpp:
10+
depend:
11+
- lowrisc:dv:cosim
12+
files:
13+
- cosim_dpi.cc: { file_type: cppSource }
14+
- cosim_dpi.h: { file_type: cppSource, is_include_file: true }
15+
- cosim_dpi.svh: {file_type: systemVerilogSource }
16+
17+
targets:
18+
default:
19+
filesets:
20+
- files_cpp

dv/cosim/cosim_dpi.h

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Copyright lowRISC contributors.
2+
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3+
// SPDX-License-Identifier: Apache-2.0
4+
5+
#ifndef COSIM_DPI_H_
6+
#define COSIM_DPI_H_
7+
8+
#include <stdint.h>
9+
#include <svdpi.h>
10+
11+
// This adapts the C++ interface of the `Cosim` class to be used via DPI. See
12+
// the documentation in cosim.h for further details
13+
14+
extern "C" {
15+
int riscv_cosim_step(Cosim *cosim, const svBitVecVal *write_reg,
16+
const svBitVecVal *write_reg_data, const svBitVecVal *pc,
17+
svBit sync_trap);
18+
void riscv_cosim_set_mip(Cosim *cosim, const svBitVecVal *mip);
19+
void riscv_cosim_set_nmi(Cosim *cosim, svBit nmi);
20+
void riscv_cosim_set_debug_req(Cosim *cosim, svBit debug_req);
21+
void riscv_cosim_set_mcycle(Cosim *cosim, svBitVecVal *mcycle);
22+
void riscv_cosim_notify_dside_access(Cosim *cosim, svBit store,
23+
svBitVecVal *addr, svBitVecVal *data,
24+
svBitVecVal *be, svBit error,
25+
svBit misaligned_first,
26+
svBit misaligned_second);
27+
void riscv_cosim_set_iside_error(Cosim *cosim, svBitVecVal *addr);
28+
int riscv_cosim_get_num_errors(Cosim *cosim);
29+
const char *riscv_cosim_get_error(Cosim *cosim, int index);
30+
void riscv_cosim_clear_errors(Cosim *cosim);
31+
void riscv_cosim_write_mem_byte(Cosim *cosim, const svBitVecVal *addr,
32+
const svBitVecVal *d);
33+
}
34+
35+
#endif // COSIM_DPI_H_

dv/cosim/cosim_dpi.svh

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright lowRISC contributors.
2+
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3+
// SPDX-License-Identifier: Apache-2.0
4+
5+
// DPI interface to co-simulation model, see `cosim.h` for the interface description.
6+
7+
// Implemented as a header file as VCS needs `import` declarations included in each verilog file
8+
// that uses them.
9+
10+
import "DPI-C" function int riscv_cosim_step(chandle cosim_handle, bit [4:0] write_reg,
11+
bit [31:0] write_reg_data, bit [31:0] pc, bit sync_trap);
12+
import "DPI-C" function void riscv_cosim_set_mip(chandle cosim_handle, bit [31:0] mip);
13+
import "DPI-C" function void riscv_cosim_set_nmi(chandle cosim_handle, bit nmi);
14+
import "DPI-C" function void riscv_cosim_set_debug_req(chandle cosim_handle, bit debug_req);
15+
import "DPI-C" function void riscv_cosim_set_mcycle(chandle cosim_handle, bit [63:0] mcycle);
16+
import "DPI-C" function void riscv_cosim_notify_dside_access(chandle cosim_handle, bit store,
17+
bit [31:0] addr, bit [31:0] data, bit [3:0] be, bit error, bit misaligned_first,
18+
bit misaligned_second);
19+
import "DPI-C" function int riscv_cosim_set_iside_error(chandle cosim_handle, bit [31:0] addr);
20+
import "DPI-C" function int riscv_cosim_get_num_errors(chandle cosim_handle);
21+
import "DPI-C" function string riscv_cosim_get_error(chandle cosim_handle, int index);
22+
import "DPI-C" function void riscv_cosim_clear_errors(chandle cosim_handle);
23+
import "DPI-C" function void riscv_cosim_write_mem_byte(chandle cosim_handle, bit [31:0] addr,
24+
bit [7:0] d);

0 commit comments

Comments
 (0)