今天一樣是簡單的 SLL、SRL、SRA 指令實作,
再一樣為了 Code Stream Logger 做準備,
另外再加碼一個 run 過這份程式的人一定會發現的 Bug 吧。
指令格式如下:
|31 25|24 20|19 15|14 12|11 7|6 0|
+---------------------------------------------------+
| func7 | rs2 | rs1 | funct3 | rd | opcode |
+---------------------------------------------------+
rd = rs1 << rs2
|31 25|24 20|19 15|14 12|11 7|6 0|
+-------------------------------------------------------+
| 0000000 | rs2 | rs1 | 001 | rd | 0110011 |
+-------------------------------------------------------+
rd = (uint_32)rs1 >> rs2
|31 25|24 20|19 15|14 12|11 7|6 0|
+-------------------------------------------------------+
| 0000000 | rs2 | rs1 | 101 | rd | 0110011 |
+-------------------------------------------------------+
rd = rs1 >> rs2
|31 25|24 20|19 15|14 12|11 7|6 0|
+-------------------------------------------------------+
| 0100000 | rs2 | rs1 | 101 | rd | 0110011 |
+-------------------------------------------------------+
github 頁面 Tag: ITDay25
這次的 Bug 是幾天前做 Exception 留下來的,
當時把 Cpu 的 this
放進 shared_ptr ,
又因為 Cpu 在 main
結束的時候會解構,
會有解構兩次造成 crash 的情況,
但是當時想到兩個解法,其中一個是用 Raw Pointer ,
另外一個是在 Cpu 內放一個 shared_ptr,
都不是很漂亮,這個 Bug 又不影響運作過程就沒立刻動手。
雖然另外指定一個什麼事都不做的 Deleter 也沒比 Raw Pointer 好到哪去,
但是不用改使用介面的情況下,是最好的選擇了,
至少這樣做在寫 Executer 的 Unit Test 可以直接用 shared_ptr。
//cpu.cpp
...
executor->set_cpu(std::shared_ptr<CPU_INTERFACE>(this, [](CPU *p){}));
...
這次正式的把 Decoder 從 Executor 移到 Instruction Decoder 了,
把程式複製貼上再取代真的有夠無聊,
之後找找看有沒有 vim 的功能可以加速這個過程。
改到心很累, Function Name 的部分就之後再改吧,
到時候 Refactor 預計 99% 都會刪掉改寫。
//executor.cpp
...
void EXECUTOR::command_dispatch()
{
switch (instruction_decoder->get_instruction()) {
case INSTRUCTION_DECODER_INTERFACE::ADDI_INSTRUCTION_ENUM:
ADDI_E();
break;
case INSTRUCTION_DECODER_INTERFACE::ANDI_INSTRUCTION_ENUM:
ANDI_E();
break;
...
//instructionDecoder.cpp
...
INSTRUCTION_DECODER_INTERFACE::Instruction INSTRUCTION_DECODER::get_instruction()
{
return cmmand_dispatch();
}
INSTRUCTION_DECODER_INTERFACE::Instruction INSTRUCTION_DECODER::cmmand_dispatch()
{
switch (get_opcode()) {
case INSTRUCTION_DECODER_INTERFACE::IMM_OP:
return imm_dispatch();
case INSTRUCTION_DECODER_INTERFACE::LUI_OP:
return LUI_INSTRUCTION_ENUM;
...
指令實作的地方把 instruction_name_map
裡的註解取消了,
其他部分大家都很熟就不多說,照著貼:
//instructionDecoderInterface.h
...
SLL_FN3 = 0b001,
SRL_FN3 = 0b101,
SRA_FN3 = 0b101,
...
SLL_FN7 = 0b0000000,
SRL_FN7 = 0b0000000,
SRA_FN7 = 0b0100000,
...
//executor.cpp
...
case INSTRUCTION_DECODER_INTERFACE::SLL_FN3:
SLL_E();
//do not check FN7 for readibility, refactor in future
break;
case INSTRUCTION_DECODER_INTERFACE::SRL_FN3:
switch (instruction_decoder->get_func7()) {
case INSTRUCTION_DECODER_INTERFACE::SRL_FN7:
SRL_E();
break;
case INSTRUCTION_DECODER_INTERFACE::SRA_FN7:
SRA_E();
break;
default:
std::cout << "INVALID: Func7 in REG_OP :" << instruction_decoder->get_func3() << std::endl;
break;
}
break;
...
void EXECUTOR::SLL_E()
{
auto rd = instruction_decoder->get_rd();
auto rs1 = instruction_decoder->get_rs1();
auto rs2 = instruction_decoder->get_rs2();
auto value = register_file->get_value_integer(rs1) << register_file->get_value_integer(rs2);
register_file->set_value_integer(rd, value);
}
void EXECUTOR::SRL_E()
{
auto rd = instruction_decoder->get_rd();
auto rs1 = instruction_decoder->get_rs1();
auto rs2 = instruction_decoder->get_rs2();
auto value = (uint32_t)register_file->get_value_integer(rs1) >> register_file->get_value_integer(rs2);
register_file->set_value_integer(rd, value);
}
void EXECUTOR::SRA_E()
{
auto rd = instruction_decoder->get_rd();
auto rs1 = instruction_decoder->get_rs1();
auto rs2 = instruction_decoder->get_rs2();
auto value = register_file->get_value_integer(rs1) >> register_file->get_value_integer(rs2);
register_file->set_value_integer(rd, value);
}
...