Skip to content

Commit

Permalink
Merge branch 'irq-support' with basic interrupt support usable for se…
Browse files Browse the repository at this point in the history
…rial port
  • Loading branch information
ppisa committed Nov 26, 2023
2 parents 202d313 + 3821f94 commit 669d28d
Show file tree
Hide file tree
Showing 17 changed files with 367 additions and 169 deletions.
68 changes: 25 additions & 43 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -317,69 +317,51 @@ is selected.
<details>
<summary>Implemented CSR registers and their usage</summary>

(NOTICE: Coprocessor0 will have to be replaced with RISC-V status registers)
(NOTICE: Replacement of MIPS Coprocessor0 by RISC-V control status registers is work in progress)

List of interrupt sources:

| Irq number | Cause/Status Bit | Source |
|-----------:|-----------------:|:---------------------------------------------|
| 2 / HW0 | 10 | Serial port ready to accept character to Tx |
| 3 / HW1 | 11 | There is received character ready to be read |
| 7 / HW5 | 15 | Counter reached value in Compare register |
| 7 | 7 | Machine timer interrupt |
| 16 / HW0 | 16 | There is received character ready to be read |
| 17 / HW1 | 17 | Serial port ready to accept character to Tx |

Following coprocessor 0 registers are recognized
Following Control Status registers are recognized

| Number | Name | Description |
|-------:|:-----------|:--------------------------------------------------------------------|
| $4,2 | UserLocal | Used as TLS base by operating system usually |
| $8,0 | BadVAddr | Reports the address for the most recent address-related exception |
| $9,0 | Count | Processor cycle count |
| $11,0 | Compare | Timer interrupt control |
| $12,0 | Status | Processor status and control |
| $13,0 | Cause | Cause of last exception |
| $14,0 | EPC | Program counter at last exception |
| $15,1 | EBase | Exception vector base register |
| $16,0 | Config | Configuration registers |

`mtc0` and `mfc0` are used to copy value from/to general purpose registers to/from coprocessor 0 register.

Hardware/special registers implemented:

| Number | Name | Description |
|-------:|:-----------|:---------------------------------------------------------|
| 0 | CPUNum | CPU number, fixed to 0 |
| 1 | SYNCI_Step | Increment required for instruction cache synchronization |
| 2 | CC | Cycle counter |
| 3 | CCRes | Cycle counter resolution, fixed on 1 |
| 29 | UserLocal | Read only value of Coprocessor 0 $4,2 register |
| 0x300 | mstatus | Machine status register. |
| 0x304 | mie | Machine interrupt-enable register. |
| 0x305 | mtvec | Machine trap-handler base address. |
| 0x340 | mscratch | Scratch register for machine trap handlers. |
| 0x341 | mepc | Machine exception program counter. |
| 0x342 | mcause | Machine trap cause. |
| 0x343 | mtval | Machine bad address or instruction. |
| 0x344 | mip | Machine interrupt pending. |
| 0x34A | mtinsr | Machine trap instruction (transformed). |
| 0x34B | mtval2 | Machine bad guest physical address. |
| 0xB00 | mcycle | Machine cycle counter. |
| 0xB02 | minstret | Machine instructions-retired counter. |
| 0xF11 | mvendorid | Vendor ID. |
| 0xF12 | marchid | Architecture ID. |
| 0xF13 | mimpid | Implementation ID. |
| 0xF14 | mhardid | Hardware thread ID. |

`csrr`, `csrw`, `csrrs` , `csrrs` and `csrrw` are used to copy and exchange value from/to RISC-V control status registers.

Sequence to enable serial port receive interrupt:

Decide location of interrupt service routine the first. The default address is 0x80000180. The base can be
changed (`EBase` register) and then PC is set to address EBase + 0x180. This is in accordance with MIPS release 1 and 2
manuals.
Decide location of interrupt service routine the first. The address of the common trap handler is defined by `mtvec` register and then PC is set to this address when exception or interrupt is accepted.

Enable bit 11 (interrupt mask) in the Status register. Ensure that bit 1 (`EXL`) is zero and bit 0 (`IE`) is set to one.
Enable bit 16 in the machine Interrupt-Enable register (`mie`). Ensure that bit 3 (`mstatus.mie` - machine global interrupt-enable) of Machine Status register is set is set to one.

Enable interrupt in the receiver status register (bit 1 of `SERP_RX_ST_REG`).

Write character to the terminal. It should be immediately consumed by the serial port receiver if interrupt is enabled
in `SERP_RX_ST_REG`. CPU should report interrupt exception and when it propagates to the execution phase `PC` is set to
the interrupt routine start address.

Some hints how to direct linker to place interrupt handler routine at appropriate address. Implement interrupt routine
in new section

```
.section .irq_handler, "ax"
```

Use next linker option to place section start at right address

```
-Wl,--section-start=.irq_handler=0x80000180
```

</details>

### System Calls Support
Expand Down
16 changes: 11 additions & 5 deletions src/cli/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -356,12 +356,13 @@ void configure_osemu(QCommandLineParser &p, MachineConfig &config, Machine *mach
QCoreApplication::exit(1);
}
}
const static machine::ExceptionCause ecall_variats[] = {machine::EXCAUSE_ECALL_ANY,
machine::EXCAUSE_ECALL_M, machine::EXCAUSE_ECALL_S, machine::EXCAUSE_ECALL_U};

if (config.osemu_enable()) {
auto *osemu_handler = new osemu::OsSyscallExceptionHandler(
config.osemu_known_syscall_stop(), config.osemu_unknown_syscall_stop(),
config.osemu_fs_root());
machine->register_exception_handler(machine::EXCAUSE_SYSCALL, osemu_handler);
if (std_out) {
machine->connect(
osemu_handler, &osemu::OsSyscallExceptionHandler::char_written,
Expand All @@ -370,11 +371,16 @@ void configure_osemu(QCommandLineParser &p, MachineConfig &config, Machine *mach
/*connect(
osemu_handler, &osemu::OsSyscallExceptionHandler::rx_byte_pool, terminal,
&TerminalDock::rx_byte_pool);*/
machine->set_step_over_exception(machine::EXCAUSE_SYSCALL, true);
machine->set_stop_on_exception(machine::EXCAUSE_SYSCALL, false);
for (unsigned int i = 0; i < sizeof(ecall_variats)/sizeof(ecall_variats[0]); i++) {
machine->register_exception_handler(ecall_variats[i], osemu_handler);
machine->set_step_over_exception(ecall_variats[i], true);
machine->set_stop_on_exception(ecall_variats[i], false);
}
} else {
machine->set_step_over_exception(machine::EXCAUSE_SYSCALL, false);
machine->set_stop_on_exception(machine::EXCAUSE_SYSCALL, config.osemu_exception_stop());
for (unsigned int i = 0; i < sizeof(ecall_variats)/sizeof(ecall_variats[0]); i++) {
machine->set_step_over_exception(ecall_variats[i], false);
machine->set_stop_on_exception(ecall_variats[i], config.osemu_exception_stop());
}
}
}

Expand Down
26 changes: 17 additions & 9 deletions src/cli/reporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,25 @@ void Reporter::machine_exit() {
constexpr const char *get_exception_name(ExceptionCause exception_cause) {
switch (exception_cause) {
case EXCAUSE_NONE: return "NONE";
case EXCAUSE_INT: return "INT";
case EXCAUSE_UNKNOWN: return "UNKNOWN";
case EXCAUSE_ADDRL: return "ADDRL";
case EXCAUSE_ADDRS: return "ADDRS";
case EXCAUSE_IBUS: return "IBUS";
case EXCAUSE_DBUS: return "DBUS";
case EXCAUSE_SYSCALL: return "SYSCALL";
case EXCAUSE_INSN_FAULT: return "INSN_FAULT";
case EXCAUSE_INSN_ILLEGAL: return "INSN_ILLEGAL";
case EXCAUSE_BREAK: return "BREAK";
case EXCAUSE_OVERFLOW: return "OVERFLOW";
case EXCAUSE_TRAP: return "TRAP";
case EXCAUSE_LOAD_MISALIGNED: return "LOAD_MISALIGNED";
case EXCAUSE_LOAD_FAULT: return "LOAD_FAULT";
case EXCAUSE_STORE_MISALIGNED: return "STORE_MISALIGNED";
case EXCAUSE_STORE_FAULT: return "STORE_FAULT";
case EXCAUSE_ECALL_U: return "ECALL_U";
case EXCAUSE_ECALL_S: return "ECALL_S";
case EXCAUSE_RESERVED_10: return "RESERVED_10";
case EXCAUSE_ECALL_M: return "ECALL_M";
case EXCAUSE_INSN_PAGE_FAULT: return "INSN_PAGE_FAULT";
case EXCAUSE_LOAD_PAGE_FAULT: return "LOAD_PAGE_FAULT";
case EXCAUSE_RESERVED_14: return "RESERVED_14";
case EXCAUSE_STORE_PAGE_FAULT: return "STORE_PAGE_FAULT";
// Simulator specific exception cause codes, alliases
case EXCAUSE_HWBREAK: return "HWBREAK";
case EXCAUSE_ECALL_ANY: return "ECALL_ANY";
case EXCAUSE_INT: return "INT";
default: UNREACHABLE
}
}
Expand Down
19 changes: 13 additions & 6 deletions src/gui/mainwindow/mainwindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -263,22 +263,29 @@ void MainWindow::create_core(

set_speed(); // Update machine speed to current settings

const static machine::ExceptionCause ecall_variats[] = {machine::EXCAUSE_ECALL_ANY,
machine::EXCAUSE_ECALL_M, machine::EXCAUSE_ECALL_S, machine::EXCAUSE_ECALL_U};

if (config.osemu_enable()) {
auto *osemu_handler = new osemu::OsSyscallExceptionHandler(
config.osemu_known_syscall_stop(), config.osemu_unknown_syscall_stop(),
config.osemu_fs_root());
machine->register_exception_handler(machine::EXCAUSE_SYSCALL, osemu_handler);
connect(
osemu_handler, &osemu::OsSyscallExceptionHandler::char_written, terminal.data(),
QOverload<int, unsigned int>::of(&TerminalDock::tx_byte));
connect(
osemu_handler, &osemu::OsSyscallExceptionHandler::rx_byte_pool, terminal.data(),
&TerminalDock::rx_byte_pool);
machine->set_step_over_exception(machine::EXCAUSE_SYSCALL, true);
machine->set_stop_on_exception(machine::EXCAUSE_SYSCALL, false);
for (unsigned int i = 0; i < sizeof(ecall_variats)/sizeof(ecall_variats[0]); i++) {
machine->register_exception_handler(ecall_variats[i], osemu_handler);
machine->set_step_over_exception(ecall_variats[i], true);
machine->set_stop_on_exception(ecall_variats[i], false);
}
} else {
machine->set_step_over_exception(machine::EXCAUSE_SYSCALL, false);
machine->set_stop_on_exception(machine::EXCAUSE_SYSCALL, config.osemu_exception_stop());
for (unsigned int i = 0; i < sizeof(ecall_variats)/sizeof(ecall_variats[0]); i++) {
machine->set_step_over_exception(ecall_variats[i], false);
machine->set_stop_on_exception(ecall_variats[i], config.osemu_exception_stop());
}
}

// Connect machine signals and slots
Expand Down Expand Up @@ -773,4 +780,4 @@ void MainWindow::build_execute_no_check() {
proc->setWorkingDirectory(work_dir);
proc->start("make", {}, QProcess::Unbuffered | QProcess::ReadOnly);
}
}
}
26 changes: 17 additions & 9 deletions src/gui/windows/coreview/data.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,25 @@ using machine::RegisterValue;

static const std::unordered_map<unsigned, QString> EXCEPTION_NAME_TABLE = {
{ machine::EXCAUSE_NONE, QStringLiteral("NONE") },
{ machine::EXCAUSE_INT, QStringLiteral("INT") },
{ machine::EXCAUSE_ADDRL, QStringLiteral("ADDRL") },
{ machine::EXCAUSE_ADDRS, QStringLiteral("ADDRS") },
{ machine::EXCAUSE_IBUS, QStringLiteral("IBUS") },
{ machine::EXCAUSE_DBUS, QStringLiteral("DBUS") },
{ machine::EXCAUSE_SYSCALL, QStringLiteral("SYSCALL") },
{ machine::EXCAUSE_INSN_FAULT, QStringLiteral("I_FAULT") },
{ machine::EXCAUSE_INSN_ILLEGAL, QStringLiteral("I_ILLEGAL") },
{ machine::EXCAUSE_BREAK, QStringLiteral("BREAK") },
{ machine::EXCAUSE_OVERFLOW, QStringLiteral("OVERFLOW") },
{ machine::EXCAUSE_TRAP, QStringLiteral("TRAP") },
{ machine::EXCAUSE_LOAD_MISALIGNED, QStringLiteral("L_MALIGN") },
{ machine::EXCAUSE_LOAD_FAULT, QStringLiteral("L_FAULT") },
{ machine::EXCAUSE_STORE_MISALIGNED, QStringLiteral("S_MALIGN") },
{ machine::EXCAUSE_STORE_FAULT, QStringLiteral("S_FAULT") },
{ machine::EXCAUSE_ECALL_U, QStringLiteral("ECALL_U") },
{ machine::EXCAUSE_ECALL_S, QStringLiteral("ECALL_S") },
{ machine::EXCAUSE_RESERVED_10, QStringLiteral("RES_10") },
{ machine::EXCAUSE_ECALL_M, QStringLiteral("ECALL_M") },
{ machine::EXCAUSE_INSN_PAGE_FAULT, QStringLiteral("I_PGFAULT") },
{ machine::EXCAUSE_LOAD_PAGE_FAULT, QStringLiteral("L_PGFAULT") },
{ machine::EXCAUSE_RESERVED_14, QStringLiteral("RES_14") },
{ machine::EXCAUSE_STORE_PAGE_FAULT, QStringLiteral("S_PGFAULT") },
// Simulator specific exception cause codes, alliases
{ machine::EXCAUSE_HWBREAK, QStringLiteral("HWBREAK") },
{ machine::EXCAUSE_UNKNOWN, QStringLiteral("UNKNOWN") },
{ machine::EXCAUSE_ECALL_ANY, QStringLiteral("ECALL") },
{ machine::EXCAUSE_INT, QStringLiteral("INT") },
};

static const std::unordered_map<unsigned, QString> STALL_TEXT_TABLE = {
Expand Down
4 changes: 4 additions & 0 deletions src/machine/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@ if(NOT ${WASM})
add_test(NAME cache COMMAND cache_test)

add_executable(instruction_test
csr/controlstate.cpp
csr/controlstate.h
instruction.cpp
instruction.h
instruction.test.cpp
Expand All @@ -151,6 +153,8 @@ if(NOT ${WASM})
add_test(NAME instruction COMMAND instruction_test)

add_executable(program_loader_test
csr/controlstate.cpp
csr/controlstate.h
instruction.cpp
instruction.h
memory/backend/backend_memory.h
Expand Down
7 changes: 5 additions & 2 deletions src/machine/bitfield.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,14 @@ struct BitField {

template<typename T>
[[nodiscard]] T decode(T val) const {
return (val >> offset) & ((1L << count) - 1);
return (val >> offset) & (((uint64_t)1 << count) - 1);
}
template<typename T>
[[nodiscard]] T encode(T val) const {
return ((val & ((1L << count) - 1)) << offset);
return ((val & (((uint64_t)1 << count) - 1)) << offset);
}
[[nodiscard]] uint64_t mask() const {
return (((uint64_t)1 << count) - 1) << offset;
}
};

Expand Down
36 changes: 25 additions & 11 deletions src/machine/core.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ bool Core::handle_exception(
Address next_addr,
Address jump_branch_pc,
Address mem_ref_addr) {
if (excause == EXCAUSE_UNKNOWN) {
if (excause == EXCAUSE_INSN_ILLEGAL) {
throw SIMULATOR_EXCEPTION(
UnsupportedInstruction, "Instruction with following encoding is not supported",
QString::number(inst.data(), 16));
Expand All @@ -142,7 +142,7 @@ bool Core::handle_exception(
control_state->update_exception_cause(excause);
if (control_state->read_internal(CSR::Id::MTVEC) != 0
&& !get_step_over_exception(excause)) {
control_state->set_status_exl(true);
control_state->exception_initiate(CSR::PrivilegeLevel::MACHINE, CSR::PrivilegeLevel::MACHINE);
regs->write_pc(control_state->exception_pc_address());
}
}
Expand Down Expand Up @@ -224,7 +224,7 @@ DecodeState Core::decode(const FetchInterstage &dt) {

dt.inst.flags_alu_op_mem_ctl(flags, alu_op, mem_ctl);

if (!(flags & IMF_SUPPORTED)) { excause = EXCAUSE_UNKNOWN; }
if (!(flags & IMF_SUPPORTED)) { excause = EXCAUSE_INSN_ILLEGAL; }

RegisterId num_rs = (flags & (IMF_ALU_REQ_RS | IMF_ALU_RS_ID)) ? dt.inst.rs() : 0;
RegisterId num_rt = (flags & IMF_ALU_REQ_RT) ? dt.inst.rt() : 0;
Expand All @@ -246,7 +246,7 @@ DecodeState Core::decode(const FetchInterstage &dt) {
if (flags & IMF_EBREAK) {
excause = EXCAUSE_BREAK;
} else if (flags & IMF_ECALL) {
excause = EXCAUSE_SYSCALL;
excause = EXCAUSE_ECALL_ANY;
}
}
if (flags & IMF_FORCE_W_OP)
Expand Down Expand Up @@ -297,6 +297,7 @@ DecodeState Core::decode(const FetchInterstage &dt) {
.csr = bool(flags & IMF_CSR),
.csr_to_alu = bool(flags & IMF_CSR_TO_ALU),
.csr_write = csr_write,
.xret = bool(flags & IMF_XRET),
.insert_stall_before = bool(flags & IMF_CSR) } };
}

Expand Down Expand Up @@ -365,6 +366,7 @@ ExecuteState Core::execute(const DecodeInterstage &dt) {
.alu_zero = alu_val == 0,
.csr = dt.csr,
.csr_write = dt.csr_write,
.xret = dt.xret,
} };
}

Expand All @@ -374,6 +376,7 @@ MemoryState Core::memory(const ExecuteInterstage &dt) {
bool memread = dt.memread;
bool memwrite = dt.memwrite;
bool regwrite = dt.regwrite;
Address computed_next_inst_addr;

enum ExceptionCause excause = dt.excause;
if (excause == EXCAUSE_NONE) {
Expand All @@ -395,20 +398,30 @@ MemoryState Core::memory(const ExecuteInterstage &dt) {
regwrite = false;
}

// Conditional branch (BXX = BEQ | BNE...) is executed and should be taken.
const bool branch_bxx_taken = dt.branch_bxx && (!dt.branch_val ^ !dt.alu_zero);
// Unconditional jump should be taken (JALX = JAL | JALR).
const bool branch_jalx = dt.branch_jalr || dt.branch_jal;

computed_next_inst_addr = compute_next_inst_addr(dt, branch_bxx_taken);

bool csr_written = false;
if (control_state != nullptr && dt.is_valid && dt.excause == EXCAUSE_NONE) {
control_state->increment_internal(CSR::Id::MINSTRET, 1);
if (dt.csr_write) {
control_state->write(dt.csr_address, dt.alu_val);
csr_written = true;
}
if (dt.xret) {
control_state->exception_return(CSR::PrivilegeLevel::MACHINE);
if (this->xlen == Xlen::_32)
computed_next_inst_addr = Address(control_state->read_internal(CSR::Id::MEPC).as_u32());
else
computed_next_inst_addr = Address(control_state->read_internal(CSR::Id::MEPC).as_u64());
csr_written = true;
}
}

// Conditional branch (BXX = BEQ | BNE...) is executed and should be taken.
const bool branch_bxx_taken = dt.branch_bxx && (!dt.branch_val ^ !dt.alu_zero);
// Unconditional jump should be taken (JALX = JAL | JALR).
const bool branch_jalx = dt.branch_jalr || dt.branch_jal;

return { MemoryInternalState {
.mem_read_val = towrite_val,
.mem_write_val = dt.val_rt,
Expand All @@ -422,13 +435,14 @@ MemoryState Core::memory(const ExecuteInterstage &dt) {
.branch_outcome = branch_bxx_taken || branch_jalx,
.branch_jalx = branch_jalx,
.branch_jalr = dt.branch_jalr,
.xret = dt.xret,
},
MemoryInterstage {
.inst = dt.inst,
.inst_addr = dt.inst_addr,
.next_inst_addr = dt.next_inst_addr,
.predicted_next_inst_addr = dt.predicted_next_inst_addr,
.computed_next_inst_addr = compute_next_inst_addr(dt, branch_bxx_taken),
.computed_next_inst_addr = computed_next_inst_addr,
.mem_addr = mem_addr,
.towrite_val = [=]() -> RegisterValue {
if (dt.csr) return dt.csr_read_val;
Expand All @@ -448,7 +462,7 @@ WritebackState Core::writeback(const MemoryInterstage &dt) {
if (dt.regwrite) { regs->write_gp(dt.num_rd, dt.towrite_val); }

return WritebackState { WritebackInternalState {
.inst = dt.inst,
.inst = (dt.excause == EXCAUSE_NONE)? dt.inst: Instruction::NOP,
.inst_addr = dt.inst_addr,
.value = dt.towrite_val,
.num_rd = dt.num_rd,
Expand Down
Loading

0 comments on commit 669d28d

Please sign in to comment.