這次要來實作指令decoder,負責pipeline中的decode stage。計組教科書上常見的pipeline架構依序為:fetch、decode、execute、memory、write back,當然在一個instruction-accurate(IA)的模擬器中,沒有必要去切得這麼細,目前只要大致分成fetch、decode以及execute三個步驟就好,memory與write back就合併在execute stage。這三個步驟做的處理羅列如下:
暫時先以常數取代,等memory model實作完成後再來時做此stage。
稍微研究了現有的模擬器是如何實作decoder,發現主要有兩種方法:
fn decode(inst_bytes) -> InstID {
for entry in instruction_table_buffer {
if (inst_bytes & entry.mask) == entry.opcode
return entry.inst_id
for entry in instruction_table {
if (inst_bytes & entry.mask) == entry.opcode {
instruction_table_buffer.add(entry)
return entry.inst_id
}
}
}
fn decode(inst_bytes) -> InstID {
switch (inst_bytes & mask_a) {
case 0: {
switch (inst_bytes & mask_b) {
case 0:
return InstID::SUB
...
}
}
case 1:
return InstID::ADD
...
}
}
這邊採用第二種方案,並且保留彈性,之後也可以實作第一種方案,並且比較兩種的效能。
很簡單,直接呼叫對應的指令function就好。
將以上的三個stage結合起來就可以完整的執行一條指令:
impl RVCore {
fn step(&mut self, inst_bytes: u32) {
// Decode
let inst = self.id_instance.decode(inst_bytes);
// Execute
(inst.operate)(self, &inst);
self.pc += inst.len;
}
}
let mut core = RVCore{};
// Fetch, 假設fetch的結果為0x00002197(AUIPC)
core.step(0x00002197);
完整的程式碼可以參考此連結