Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions src/iss/arch/riscv_hart_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -1019,8 +1019,11 @@ template <typename BASE = logging::disass> struct riscv_hart_common : public BAS
void set_max_irq_num(unsigned i) { mcause_max_irq = std::max(1u << util::ilog2(i), 16u); }

void set_clint_irq_num(unsigned num) {
assert(num <= traits<BASE>::XLEN);
clint_custom_irq_mask = std::numeric_limits<reg_t>::max() >> (traits<BASE>::XLEN - num);
assert(num <= max_clint_irq_num && "CLINT irq count exceeds what the hart supports");
clint_custom_irq_mask = num >= 64 ? std::numeric_limits<uint64_t>::max() : (1ULL << num) - 1;
// mcause needs to be able to hold the highest irq number
while(mcause_max_irq < num)
mcause_max_irq <<= 1;
}

protected:
Expand Down Expand Up @@ -1091,6 +1094,8 @@ template <typename BASE = logging::disass> struct riscv_hart_common : public BAS
int64_t instret_offset{0};
semihosting_cb_t<reg_t> semihosting_cb;
unsigned mcause_max_irq{traits<BASE>::XLEN};
// AIA adds mieh/miph on RV32, so a hart with FEAT_AIA may raise this to 64
unsigned max_clint_irq_num{traits<BASE>::XLEN};
uint64_t clint_custom_irq_mask{0xffff};
};

Expand Down
1 change: 1 addition & 0 deletions src/iss/arch/riscv_hart_m_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ riscv_hart_m_p<BASE, FEAT>::riscv_hart_m_p()
this->add_debug_csrs();

if(FEAT & FEAT_AIA && traits<BASE>::XLEN == 32) {
this->max_clint_irq_num = 64;
this->csr_rd_cb[miph] = MK_CSR_RD_CB(read_ip);
this->csr_wr_cb[miph] = MK_CSR_WR_CB(write_ip);
this->csr_rd_cb[mieh] = MK_CSR_RD_CB(read_ie);
Expand Down
5 changes: 3 additions & 2 deletions src/iss/arch/riscv_hart_msu_vp.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ template <typename BASE, features_e FEAT = FEAT_NONE> class riscv_hart_msu_vp :
public:
using core = BASE;
using base = riscv_hart_common<BASE>;
using this_class = riscv_hart_msu_vp<BASE>;
using this_class = riscv_hart_msu_vp<BASE, FEAT>;
using reg_t = typename core::reg_t;
using phys_addr_t = typename core::phys_addr_t;

Expand Down Expand Up @@ -187,6 +187,7 @@ riscv_hart_msu_vp<BASE, FEAT>::riscv_hart_msu_vp()
this->add_debug_csrs();

if(FEAT & FEAT_AIA && traits<BASE>::XLEN == 32) {
this->max_clint_irq_num = 64;
this->csr_rd_cb[miph] = MK_CSR_RD_CB(read_ip);
this->csr_wr_cb[miph] = MK_CSR_WR_CB(write_ip);
this->csr_rd_cb[mieh] = MK_CSR_RD_CB(read_ie);
Expand Down Expand Up @@ -484,7 +485,7 @@ template <typename BASE, features_e FEAT> iss::status riscv_hart_msu_vp<BASE, FE
switch(addr) {
case mip:
case miph:
if(unlikely(addr == mieh))
if(unlikely(addr == miph))
val = (this->mip_csr & mask) >> 32;
else
val = this->mip_csr & mask;
Expand Down
3 changes: 2 additions & 1 deletion src/iss/arch/riscv_hart_mu_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ riscv_hart_mu_p<BASE, FEAT>::riscv_hart_mu_p()
this->add_debug_csrs();

if(FEAT & FEAT_AIA && traits<BASE>::XLEN == 32) {
this->max_clint_irq_num = 64;
this->csr_rd_cb[miph] = MK_CSR_RD_CB(read_ip);
this->csr_wr_cb[miph] = MK_CSR_WR_CB(write_ip);
this->csr_rd_cb[mieh] = MK_CSR_RD_CB(read_ie);
Expand Down Expand Up @@ -400,7 +401,7 @@ template <typename BASE, features_e FEAT> iss::status riscv_hart_mu_p<BASE, FEAT

template <typename BASE, features_e FEAT> iss::status riscv_hart_mu_p<BASE, FEAT>::write_ip(unsigned addr, reg_t val) {
uint64_t lval = val;
if(addr == mieh) {
if(addr == miph) {
lval <<= 32;
lval |= static_cast<uint32_t>(this->mip_csr);
} else if(sizeof(reg_t) == 4)
Expand Down
9 changes: 2 additions & 7 deletions src/sysc/core2sc_adapter.h
Original file line number Diff line number Diff line change
Expand Up @@ -324,13 +324,8 @@ template <typename PLAT> class core2sc_adapter : public PLAT, public sc2core_if
}

void _local_irq(short id, bool value) {
uint64_t mask = 0;
if(sizeof(reg_t) == 4) {
assert(id < 32 && "CLINT cannot handle more than 32 irq");
} else {
assert(id < 64 && "CLINT cannot handle more than 64 irq");
}
mask = 1 << id;
assert(id < this->max_clint_irq_num && "CLINT irq id exceeds what the hart supports");
uint64_t const mask = 1ULL << id;
if(value) {
this->mip_csr |= mask;
wfi_evt.notify();
Expand Down
9 changes: 8 additions & 1 deletion src/sysc/register_cores.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
namespace iss {
namespace interp {
using namespace sysc;
__attribute__((used)) volatile std::array<bool, 22> riscv_init = {
__attribute__((used)) volatile std::array<bool, 23> riscv_init = {
iss_factory::instance().register_creator("rv32i_m:interp",
[](unsigned gdb_port, sysc::riscv::core_complex_if* cc) -> iss_factory::base_t {
auto* cpu = new core2sc_adapter<arch::riscv_hart_m_p<arch::rv32i>>(cc);
Expand All @@ -73,6 +73,13 @@ __attribute__((used)) volatile std::array<bool, 22> riscv_init = {
std::make_unique<iss::mem::pmp<iss::arch::rv32imac>>(cpu->get_priv_if()));
return {sysc::core_ptr{cpu}, vm_ptr{create(static_cast<arch::rv32imac*>(cpu), gdb_port)}};
}),
iss_factory::instance().register_creator("rv32imac_mp_aia:interp", // rv32imac_mp with AIA
[](unsigned gdb_port, sysc::riscv::core_complex_if* cc) -> iss_factory::base_t {
auto* cpu = new core2sc_adapter<arch::riscv_hart_m_p<arch::rv32imac, arch::FEAT_AIA>>(cc);
cpu->memories.insert_before_last(
std::make_unique<iss::mem::pmp<iss::arch::rv32imac>>(cpu->get_priv_if()));
return {sysc::core_ptr{cpu}, vm_ptr{create(static_cast<arch::rv32imac*>(cpu), gdb_port)}};
}),
iss_factory::instance().register_creator("rv32imac_mu:interp",
[](unsigned gdb_port, sysc::riscv::core_complex_if* cc) -> iss_factory::base_t {
auto* cpu = new core2sc_adapter<arch::riscv_hart_mu_p<arch::rv32imac>>(cc);
Expand Down
1 change: 1 addition & 0 deletions tests/sysc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ include(GoogleTest)
add_executable(sysc_tests
test_main.cpp
core_complex_test.cpp
aia_csr_test.cpp
)
target_link_libraries(sysc_tests PRIVATE scc::components GTest::gtest)
# whole-archive so the static core registrations in register_cores.cpp are kept
Expand Down
58 changes: 58 additions & 0 deletions tests/sysc/aia_csr_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#include <gtest/gtest.h>
#include <iss/arch/riscv_hart_msu_vp.h>
#include <iss/arch/riscv_hart_mu_p.h>
#include <iss/arch/rv32gc.h>
#include <iss/arch/rv32imac.h>

namespace {

using namespace iss::arch;

template <typename HART> class AiaCsrTest : public ::testing::Test {
protected:
using reg_t = typename HART::reg_t;

void SetUp() override {
hart.reset(0);
hart.set_clint_irq_num(64);
}

reg_t read_csr(unsigned csr) {
reg_t val = 0;
EXPECT_EQ(hart.read(csr_addr(iss::access_type::READ, csr), sizeof(val), reinterpret_cast<uint8_t*>(&val)), iss::Ok);
return val;
}

void write_csr(unsigned csr, reg_t val) {
EXPECT_EQ(hart.write(csr_addr(iss::access_type::WRITE, csr), sizeof(val), reinterpret_cast<uint8_t const*>(&val)), iss::Ok);
}

HART hart;

private:
static iss::addr_t csr_addr(iss::access_type access, unsigned csr) {
return {iss::address_type::LOGICAL, access, traits<typename HART::core>::CSR, csr};
}
};

using MuAiaCsrTest = AiaCsrTest<riscv_hart_mu_p<rv32imac, FEAT_AIA>>;

TEST_F(MuAiaCsrTest, WritingMiphSetsTheUpperHalfOfMipAndLeavesTheLowerHalfAlone) {
constexpr reg_t irq40 = 1U << (40 - 32);
write_csr(miph, irq40);

EXPECT_EQ(read_csr(miph), irq40);
EXPECT_EQ(read_csr(mip), 0u);
}

using MsuAiaCsrTest = AiaCsrTest<riscv_hart_msu_vp<rv32gc, FEAT_AIA>>;

TEST_F(MsuAiaCsrTest, WritingMiphSetsTheUpperHalfOfMipAndLeavesTheLowerHalfAlone) {
constexpr reg_t irq40 = 1U << (40 - 32);
write_csr(miph, irq40);

EXPECT_EQ(read_csr(miph), irq40);
EXPECT_EQ(read_csr(mip), 0u);
}

} // namespace
61 changes: 61 additions & 0 deletions tests/sysc/core_complex_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,14 @@ TEST_F(CoreComplexTest, RunsAProgramFromTheResetAddress) {
EXPECT_GT(loop_count(), 0u);
}

TEST_F(CoreComplexTest, RunsAProgramOnAnRv32imacCoreWithPmpAndAia) {
tb->cpu.core_type.set_value("rv32imac_mp_aia");
sc_start(sc_time(100, SC_US));

EXPECT_EQ(start_count(), 1u);
EXPECT_GT(loop_count(), 0u);
}

TEST_F(CoreComplexTest, AdvancesSimulationTimeWhileRunning) {
sc_start(sc_time(100, SC_US));
auto loops_at_100us = loop_count();
Expand Down Expand Up @@ -89,4 +97,57 @@ TEST_F(CoreComplexTest, RestartsAtTheResetAddressAfterAResetWhileRunning) {
EXPECT_EQ(start_count(), 2u);
}

constexpr uint64_t trap_handler_addr = 0x100;
constexpr int32_t mcause_addr = 0x408;
constexpr unsigned upper_local_irq = 40;

// Enables only upper_local_irq through mieh, then spins. The trap handler
// stores mcause and spins.
std::vector<uint32_t> upper_local_irq_program() {
using namespace rv32;
constexpr unsigned mstatus = 0x300, mtvec = 0x305, mieh = 0x314, mcause = 0x342;
constexpr unsigned mstatus_mie = 0x8;
std::vector<uint32_t> program{
addi(5, 0, trap_handler_addr), // t0 = handler
csrrw(0, mtvec, 5), // mtvec = t0
addi(5, 0, 1 << (upper_local_irq - 32)), // t0 = enable bit
csrrw(0, mieh, 5), // mieh = t0
csrrsi(0, mstatus, mstatus_mie), // mstatus.MIE = 1
jal(0, 0), // spin
};
program.resize(trap_handler_addr / 4, 0);
std::vector<uint32_t> trap_handler{
csrrs(6, mcause, 0), // t1 = mcause
sw(6, mcause_addr, 0), // store t1
jal(0, 0), // spin
};
program.insert(program.end(), trap_handler.begin(), trap_handler.end());
return program;
}

class AiaCoreComplexTest : public SystemCTestFixture {
protected:
void SetUp() override {
tlm::tlm_global_quantum::instance().set(sc_time(1, SC_US));
tb = std::make_unique<testbench>("tb", 64);
tb->cpu.core_type.set_value("rv32imac_mp_aia");
tb->load(reset_address, upper_local_irq_program());
}

void TearDown() override {
tb.reset();
SystemCTestFixture::TearDown();
}

std::unique_ptr<testbench> tb;
};

TEST_F(AiaCoreComplexTest, TakesALocalInterruptAbove31OnRv32) {
sc_spawn([this] { tb->clint_irq[upper_local_irq].write_now(true); });
sc_start(sc_time(100, SC_US));

constexpr uint32_t interrupt_bit = 1U << 31;
EXPECT_EQ(tb->read_word(mcause_addr), interrupt_bit | upper_local_irq);
}

} // namespace
6 changes: 6 additions & 0 deletions tests/sysc/rv32_program.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ constexpr uint32_t lw(unsigned rd, int32_t offset, unsigned rs1) { return i_type

constexpr uint32_t addi(unsigned rd, unsigned rs1, int32_t imm) { return i_type(imm, rs1, 0, rd, 0x13); }

constexpr uint32_t csrrw(unsigned rd, unsigned csr, unsigned rs1) { return i_type(csr, rs1, 1, rd, 0x73); }

constexpr uint32_t csrrs(unsigned rd, unsigned csr, unsigned rs1) { return i_type(csr, rs1, 2, rd, 0x73); }

constexpr uint32_t csrrsi(unsigned rd, unsigned csr, unsigned uimm) { return i_type(csr, uimm, 6, rd, 0x73); }

constexpr uint32_t sw(unsigned rs2, int32_t offset, unsigned rs1) {
auto imm = static_cast<uint32_t>(offset & 0xfff);
return ((imm >> 5) << 25) | (rs2 << 20) | (rs1 << 15) | (2 << 12) | ((imm & 0x1f) << 7) | 0x23;
Expand Down
14 changes: 10 additions & 4 deletions tests/sysc/testbench.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <scc/router.h>
#include <sysc/core_complex.h>
#include <systemc>
#include <tlm/scc/signal_initiator_mixin.h>
#include <tlm>
#include <vector>

Expand All @@ -13,14 +14,19 @@
struct testbench : sc_core::sc_module {
static constexpr uint64_t ram_size = 1ULL << 20;

sysc::riscv::core_complex<> cpu{"cpu"};
sysc::riscv::core_complex<> cpu;
scc::router<> bus{"bus", 1, 2};
scc::memory<ram_size> ram{"ram"};
sc_core::sc_signal<sc_core::sc_time> clk{"clk", sc_core::sc_time(10, sc_core::SC_NS)};
sc_core::sc_signal<bool> rst{"rst", false};

explicit testbench(sc_core::sc_module_name const& nm)
: sc_core::sc_module(nm) {
sc_core::sc_vector<tlm::scc::tlm_signal_bool_out> clint_irq{"clint_irq"};

explicit testbench(sc_core::sc_module_name const& nm, size_t clint_irq_count = 32)
: sc_core::sc_module(nm)
, cpu("cpu", clint_irq_count) {
clint_irq.init(clint_irq_count);
for(auto i = 0U; i < clint_irq_count; ++i)
clint_irq[i].bind(cpu.clint_irq_i[i]);
cpu.ibus(bus.target[0]);
cpu.dbus(bus.target[1]);
bus.bind_target(ram.target, 0, 0, ram_size);
Expand Down
Loading