RISC-V: Memory Load指令

这次要实作的指令属於 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


<<:  离职倒数2天:40%的工作没意义,为什麽还抢着做

>>:  【Day 20】ECS on Outposts 的限制

Gin 表单

Golang Gin 表单 今天真的有点爆炸了,几乎没时间补文章,只能抽空拿点时间来写,如果在gin...

Day 8 矛盾又挣扎揭弊者

我们仰望着同一片天空,却看着不同的地方,即使同一片花瓣,我们也会因为不同季节,而有不同的感受,也许对...

Day 27 上传自己的 Image 到 Dockerhub

藉着 Day 14 建一个 Node.js 容器 所建立的基底,来制作一个 Image 并上传到 D...

[Day 最後一天]心得感想

终於结束了!! 一开始以为 30 天可以咻的一声就过去了 没想到真的十分的不容易呀 更尤其是这中间居...

[第二十七天]从0开始的UnityAR手机游戏开发-虚拟摇杆 Joystick 01

点击Window→Asset Store开启Unity资产商店 在Asset Store的搜寻列搜寻...