iT邦幫忙

2021 iThome 鐵人賽

DAY 2
0

這次要實作的指令屬於 Explict Memory Access,
也因為 Read 已經做出來了,這次也會順便把 Implict Memory Access 實做完,
換句話說就是把 fakeInstructionMemory 移到真正的 Memory 裡面吧!

I-type

指令格式如下:

|31     20|19   15|14    12|11   7|6       0|
+-------------------------------------------+
|   imm   |  rs1  | funct3 |  rd  | opcode  |
+-------------------------------------------+

LW

rd = memory_read(base + imm, 4)

|31     20|19   15|14    12|11   7|6       0|
+-------------------------------------------+
|   imm   | base  |  010   |  rd  | 0000011 |
+-------------------------------------------+

LH

rd = sign_ext(memory_read(base + imm, 2))

|31     20|19   15|14    12|11   7|6       0|
+-------------------------------------------+
|   imm   | base  |  001   |  rd  | 0000011 |
+-------------------------------------------+

LB

rd = sign_ext(memory_read(base + imm, 1))

|31     20|19   15|14    12|11   7|6       0|
+-------------------------------------------+
|   imm   | base  |  000   |  rd  | 0000011 |
+-------------------------------------------+

LHU

rd = memory_read(base + imm, 2)

|31     20|19   15|14    12|11   7|6       0|
+-------------------------------------------+
|   imm   | base  |  101   |  rd  | 0000011 |
+-------------------------------------------+

LBU

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


上一篇
RISC-V: U-type Load Immediate指令
下一篇
RISC-V: Memory Store 指令
系列文
猴子都寫得出來的 RISC-V CPU Emulator31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言