每日一 Bug,
其實作天就有發現 get_imm()
運算結果怪怪的,
如果會 Sign-Extension,怎麼 get_imm(24, 20)
結果竟然是 31,
如果不會 Sign-Estension,前面的 get_imm(31, 20)
怎麼會回傳負數?
大家可以先猜猜問題出在哪,下面再為大家解答!
先給一點提示:讀文件,治百病
這次修改的範圍剛好會碰到在 Register File 生灰塵的 Program Counter,
就順便讓它動起來吧!
另外特別感謝 Ruinland Maskman 的發文:RISC-V core上porting Linux
在研究這個系統的過程中,發現了另外一個 RISC-V Emulator exactstep,
各方面實作很完整,指令解碼方式跟 RISC-V-TLM 也完全不一樣,
對設計新的 Instruction Decoder 很有幫助。
指令格式如下:
|31 12|11 7|6 0|
+-------------------------------------------+
| imm | rd | opcode |
+-------------------------------------------+
採用 20 bit Asymmetric 的設計,
搭配 I-type 指令就可以指定任何想要的數值。
rd = imm << 12;
|31 12|11 7|6 0|
+-------------------------------------------+
| imm | rd | opcode |
+-------------------------------------------+
imm 設定為 0 可以取得 Program Counter,
規格書上有提到其實 JALR 也一樣可以有一樣的效果,
但是會影響 Pipeline 和 Branch Predictor 系統的運作流程。
目前沒有使用這道指令的經驗,
看起來跟 Boot Loader 準備好 MMU 設定之後跳過去指定位置執行有關,
有看過的夥伴歡迎幫忙補充。
rd = current_pc + (imm << 12);
|31 12|11 7|6 0|
+-------------------------------------------+
| imm | rd | opcode |
+-------------------------------------------+
github 頁面 Tag: ITDay14
寫 Tag 寫這麼多天,剛剛才發現根本忘記推上 github...
首先先來解決那個奇怪的 Bug:
根據 SystemC 2.3.1 文件所述,
sc_int<32> 只有在 Assign 之後才會執行 extend_sign()
,
//sc_int_base.h
sc_int_base& operator <<= ( int_type v )
{ m_val <<= v; extend_sign(); return *this; }
照上面的描述可以知道,
單純的 << Operator 是不會進行 sign extension 的,
需要 Assign 才行,
改成下面這樣就沒問題了:
//instructionDecoder.cpp
int32_t INSTRUCTION_DECODER::get_imm(uint32_t end, uint32_t start)
{
auto value = sc_dt::sc_int<32>(instruction_value);
value <<= (31-end);
value >>= (31 - end + start);
return value;
}
這次剛好用到 Program Counter 相關的功能,
就順便做完它。
看起來是個小小的功能,做起來其實很有趣,
參考了相對成熟的 RISC-V-TLM 和 exactstep,
前者是在執行後回傳 PC 是否需要 +4,後者是直接在每道指令執行時決定,
一個會在每個 Ececute Function 開不必要的介面,
另一個會多寫很多不必要的程式碼增加修改時錯誤的機率。
最後決定自己換一個方法:EXECUTOR
中增加一個暫存的 new_pc ,
在執行指令前設定為 current_pc + 4,
在由執行中的指令依照需求覆蓋,並在最後寫入。
...
//executor.h
uint32_t new_pc; //write back to register when execute finished
...
//executor.cpp
void EXECUTOR::execute()
{
new_pc = register_file->get_pc() + 4;
...
register_file->set_pc(new_pc);
}
指令的部分相對單純
//instructionDecoderInterface.h
...
LUI_OP = 0b0110111,
AUIPC_OP = 0b0010111,
...
//executor.cpp
...
case INSTRUCTION_DECODER_INTERFACE::LUI_OP:
LUI_E();
break;
case INSTRUCTION_DECODER_INTERFACE::AUIPC_OP:
AUIPC_E();
break;
...
void EXECUTOR::LUI_E()
{
auto rd = instruction_decoder->get_rd();
auto value = (instruction_decoder->get_imm(31, 12) << 12);
register_file->set_value_integer(rd, value);
...
}
void EXECUTOR::AUIPC_E()
{
auto rd = instruction_decoder->get_rd();
auto value = register_file->get_pc() + (instruction_decoder->get_imm(31, 12) << 12);
register_file->set_value_integer(rd, value);
...
}
...