這次要實作的指令屬於 Explict Memory Access,
也因為 Read 已經做出來了,這次也會順便把 Implict Memory Access 實做完,
換句話說就是把 fakeInstructionMemory
移到真正的 Memory 裡面吧!
指令格式如下:
|31 20|19 15|14 12|11 7|6 0|
+-------------------------------------------+
| imm | rs1 | funct3 | rd | opcode |
+-------------------------------------------+
rd = memory_read(base + imm, 4)
|31 20|19 15|14 12|11 7|6 0|
+-------------------------------------------+
| imm | base | 010 | rd | 0000011 |
+-------------------------------------------+
rd = sign_ext(memory_read(base + imm, 2))
|31 20|19 15|14 12|11 7|6 0|
+-------------------------------------------+
| imm | base | 001 | rd | 0000011 |
+-------------------------------------------+
rd = sign_ext(memory_read(base + imm, 1))
|31 20|19 15|14 12|11 7|6 0|
+-------------------------------------------+
| imm | base | 000 | rd | 0000011 |
+-------------------------------------------+
rd = memory_read(base + imm, 2)
|31 20|19 15|14 12|11 7|6 0|
+-------------------------------------------+
| imm | base | 101 | rd | 0000011 |
+-------------------------------------------+
rd = memory_read(base + imm, 1)
|31 20|19 15|14 12|11 7|6 0|
+-------------------------------------------+
| imm | base | 100 | rd | 0000011 |
+-------------------------------------------+
github 頁面 Tag: ITDay15
這次描述的順序跟實作順序不太一樣,
為了不要一次改太大的範圍,導致開發中難以確定問題來源,
實做的時候先支援 R/W ,
第二步實作 LOAD 指令確認 R/W 功能正確,
第三步實做簡易的 Program Loader,
第四步實做 Instruction Fetch 的功能並維持 LOAD 指令正確,
最後才把 fakeInstructionMemory
移除,
變相的把 LOAD 運算結果當作 Unit Test 使用。
如果因為某個地方改動影響很大,像這樣的流程做起來卡卡的時候,
通常都是相依性太高了,我會把元件拆得更細一點,
幸好目前都還算順利,不需要大改。
首先從 Memory 開始,這邊新增了一個簡易的 Program Loader,
並支援了完整的 R/W 的功能(之前只有 Read,而且只能支援一個 Byte)。
//memory.cpp
...
void MEMORY::programLoader(std::array<uint32_t, 4096> &binary)
{
for(auto instruction: binary) {
dataMemory.push_back(instruction & 0xFF);
dataMemory.push_back(instruction >> 8 & 0xFF);
dataMemory.push_back(instruction >> 16 & 0xFF);
dataMemory.push_back(instruction >> 24 & 0xFF);
if(instruction != 0)
std::cout << "inst: " << instruction << std::endl;
}
}
...
for(int i = 0; i < len; i++) {
if(adr < dataMemory.size()) {
if(cmd == tlm::TLM_READ_COMMAND) {
((char *)ptr)[i] = dataMemory[adr + i];
} else if(cmd == tlm::TLM_WRITE_COMMAND) {
dataMemory[adr + i] = ((char *)ptr)[i];
} else {
std::cout << "unsupported operation" << adr << "!!" << std::endl;
}
} else {
trans.set_response_status(tlm::TLM_BURST_ERROR_RESPONSE);
std::cout << "error an address " << adr << "!!" << std::endl;
return;
}
}
...
在 CPU 中,把妨礙閱讀的 fakeInstructionMemory
移除,
直接從 Memory 取得指令,
並且和 register_file 一樣,把 address_space 的實體放進 Executor 內。
...
void CPU::step()
{
uint32_t instruction = address_space->read(register_file->get_pc(), 4);
instruction_decoder->set_instruction(instruction);
executor->execute();
}
...
void CPU::set_executor(const std::shared_ptr<EXECUTOR_INTERFACE> &instance)
{
void CPU::set_address_space(const std::shared_ptr<ADDRESS_SPACE_INTERFACE> &instance)
{
...
if(executor != nullptr) {
executor->set_address_space(address_space);
}
}
...
if(address_space != nullptr) {
executor->set_address_space(address_space);
}
}
讀到這邊,Executor 內部實做應該熟到爛掉了,
就只列出一中一個當作範例,<< 24 >> 24
這邊是用來做 Sign Extension 用的,
Build in Type 沒有像 SystemC 一樣要先 Assign 的限制,直接 shift 就可以了。
...
case INSTRUCTION_DECODER_INTERFACE::LOAD_OP:
switch (instruction_decoder->get_func3()) {
case INSTRUCTION_DECODER_INTERFACE::LB_FN3:
LB_E();
break;
...
void EXECUTOR::LB_E()
{
auto rs1 = instruction_decoder->get_rs1();
auto rd = instruction_decoder->get_rd();
auto addr = register_file->get_value_integer(rs1) + (uint32_t) instruction_decoder->get_imm(31, 20);
auto value = address_space->read(addr, 1) << 24 >> 24;
register_file->set_value_integer(rd, value);
}
...
大家應該也有發現這部份越來越複雜了,
可惜目前只能暫時先放著,等 Logger 做出來會好一點。
雖然 Unit Test 才是真正的解決方案,
但看來應該是完賽後才能補了。
$ make run
SystemC 2.3.3-Accellera --- Sep 17 2021 22:09:07
Copyright (c) 1996-2018 by all Contributors,
ALL RIGHTS RESERVED
inst: 1048723
inst: 4293951763
inst: 19
inst: 2147524627
inst: 2147561747
inst: 32575891
inst: 1167891
inst: 1074909843
inst: 4919
inst: 4887
inst: 4199171
inst: 4215555
ADDI
rs1: 0
rd: 1
value: 1
ADDI
rs1: 1
rd: 2
value: 0
ADDI
rs1: 0
rd: 0
value: 0
SLTI
rs1: 1
rd: 0
value: 0
SLTIU
rs1: 2
rd: 2
value: 1
SLLI
rs1: 2
rd: 3
value: -2147483648
SRLI
rs1: 3
rd: 4
value: 1073741824
SRAI
rs1: 3
rd: 5
value: -1073741824
LUI
rd: 6
value: 4096
AUIPC
rd: 6
value: 4132
LH
rd: 6
addr: 4
value: -32493
LHU
rd: 6
addr: 4
value: 33043