進到了系統指令的環節,
一樣先參考 RISC-V-TLM 和 exactstep 的做法,
恩...一言難盡。
先從指令介紹開始吧!
指令格式如下:
|31 20|19 15|14 12|11 7|6 0|
+-------------------------------------------+
| imm | rs1 | funct3 | rd | opcode |
+-------------------------------------------+
用來執行需要更高權限才能執行的功能,
例如作業系統的 system call,
在目前參考的指令及版本有提到原先是叫 SCALL,
但因為需要支援更多的使用情境,改成 ECALL。
|31 20|19 15|14 12|11 7|6 0|
+-------------------------------------------+
| 0 | 00000 | 000 | 00000 | 1110011 |
+-------------------------------------------+
用來中斷程式的運作,
讓系統可以把使用權轉交給 Debugger。
其實 Debugger 的 Break Point 就是用這個指令實現的,
只是偷偷把指定位置的指令換掉了不跟使用者講而已!
和 ECALL 一樣,
在目前參考的指令及版本有提到原先是叫 SBREAK,
但因為需要支援更多的使用情境,改成 EBREAK。
|31 20|19 15|14 12|11 7|6 0|
+-------------------------------------------+
| 1 | 00000 | 000 | 00000 | 1110011 |
+-------------------------------------------+
github 頁面 Tag: ITDay21
ECALL 在 RISC-V-TLM 的做法就是直接結束模擬。
exactstep 實作上把 ECALL 接起來自己做掉,
並沒有跳到某個位置執行軟體註冊的 Handler 再跳回來的過程。
根據規格書對 Bare metal hardware platforms 的描述:
The hardware platform defines an execution environment
that begins at power-on reset.",
exactstep 的做法應該是符合規範的。
但還是比較喜歡讓 Software 有彈性的做法,
目前先依照 RISC-V-TLM 的行為實作,
日後再擴充。
EBREAK 則是會發生 Trap,目前沒有實作相關機制來處理,
等之後把 Interrupt 和 CSR 的部分做出來再回來拯救這個指令吧!
//instructionDecoderInterface.h
...
SYSTEM_OP = 0b1110011,
...
enum Func12 {
ECALL_FN12 = 0b0,
EBREAK_FN12 = 0b1,
};
...
//executor.cpp
...
case INSTRUCTION_DECODER_INTERFACE::SYSTEM_OP:
system_dispatch();
break;
...
void EXECUTOR::system_dispatch()
{
switch (instruction_decoder->get_func12()) {
case INSTRUCTION_DECODER_INTERFACE::ECALL_FN12:
ECALL_E();
break;
case INSTRUCTION_DECODER_INTERFACE::EBREAK_FN12:
EBREAK_E();
break;
default:
std::cout << "INVALID: Func3 in MISC_MEM_OP :" << instruction_decoder->get_func3() << std::endl;
break;
}
}
...
void EXECUTOR::ECALL_E()
{
sc_core::sc_stop();
}
void EXECUTOR::EBREAK_E()
{
sc_core::sc_stop();
}
新增了一個 system 專用的 get_func12
//instructionDecoder.cpp
...
uint32_t INSTRUCTION_DECODER::get_func12()
{
return instruction_value.range(31, 20);
}
...
加了一段測試程式,讓最後一道指令 ECALL 結束 Simulator 的運作。
也因為這樣,Cpu 裡面的 Cycle Limit 可以移除了,
之後等做出 Exception 系列功能,
就可以用 Illegal Instruction Exceptions 取代。
//memory.cpp
...
0b000000000000'00000'000'00000'1110011, //ECALL
...
//cpu.cpp
...
void CPU::cpu_thread(void)
{
while(true) {
step();
wait(delay);
}
}
...