指令的部分終於完結了!
今天就來做 Logger 吧,
目標是要有一致性和一定程度的可讀性,
讓之後的 Code Stream 分析比較輕鬆。
RISC-V I 指令集在 Instruction List 總共列出 40 道指令,
加上還沒決定怎麼區分的 (SHIMM、FENCE) 目前列出有 41 個類別,
為了讓之後的分析比較好做,
目前會放在指令執行時加入 runtime 資訊。
先支援 Memory Access 和 Branch Prediction,
Out of Order 需要更多的資訊 (例如 Multi-path Fetching 需要未執行到的指令),
目前只能支援 Branch Prediction 神預測永遠猜對的情況。
github 頁面 Tag: ITDay26
Decoder 本身不知道硬體運作的情況,
這邊會帶入指令執行的時候才知道的 Runtime 資訊:
//instructionDecoder.cpp
void INSTRUCTION_DECODER::log_instruction(uint32_t current_pc, uint32_t target_pc, uint32_t rs1Value, uint32_t rs2Value, uint32_t rdValue,int32_t immValue)
{
auto instructionName = instruction_name_map[get_instruction()];
auto rd = get_rd();
auto rs1 = get_rs1();
auto rs2 = get_rs2();
std::cout << "current_pc: 0x" << std::hex << current_pc
<< " target_pc: 0x" << std::hex << target_pc
<< " " << instructionName
<< " " << std::dec << rs1
<< " " << std::dec << rs2
<< " " << std::dec << rd
<< " rs1Value: 0x" << std::hex << rs1Value
<< " rs2Value: 0x" << std::hex << rs2Value
<< " rdValue: 0x" << std::hex << rdValue
<< " immValue: 0x" << std::hex << immValue
<< std::endl;
}
也因為這樣,必須放在每一個指令執行的地方,
一開始有考量要做成每個指令獨立一個 class,
可以在 Cpu 很簡單的呼叫 execute 和 log 就好,
但因為會有很多 object create/destroy 拖慢模擬速度,
指令分類也會帶入過多不必要的複雜度。
反正不管怎麼做猴子都會遇上麻煩,
就先依照 "You aren't gonna need it!" (YAGNI) 原則,
採用簡單粗暴的 Switch Statement,
現在就需要為這個決策付出代價了!
//executor.cpp
...
void EXECUTOR::ANDI_E()
{
auto rs1 = instruction_decoder->get_rs1();
auto rd = instruction_decoder->get_rd();
auto imm = instruction_decoder->get_imm(31, 20);
auto value = register_file->get_value_integer(rs1) & imm;
register_file->set_value_integer(rd, value);
instruction_decoder->log_instruction(
register_file->get_pc(),
new_pc,
register_file->get_value_integer(rs1),
0,
register_file->get_value_integer(rd),
imm);
}
...
Executor 的部分讓猴子逃避一下趕工中,晚點補上,
大家雙十節快樂!
昨晚寫到一半突然發現自己看著一個指令實作發呆 5 分鐘,
決定不補 Code 先補眠。睡眠不足真的很難做這種單調又要集中精神的事情,
實驗證實咖啡對猴子大概沒什麼用,
猴子只能睡覺。