RISC-V: U-type Load Immediate指令

每日一 Bug,
其实作天就有发现 get_imm() 运算结果怪怪的,
如果会 Sign-Extension,怎麽 get_imm(24, 20) 结果竟然是 31,
如果不会 Sign-Estension,前面的 get_imm(31, 20) 怎麽会回传负数?
大家可以先猜猜问题出在哪,下面再为大家解答!
先给一点提示:读文件,治百病

这次修改的范围刚好会碰到在 Register File 生灰尘的 Program Counter,
就顺便让它动起来吧!

另外特别感谢 Ruinland Maskman 的发文:RISC-V core上porting Linux
在研究这个系统的过程中,发现了另外一个 RISC-V Emulator exactstep
各方面实作很完整,指令解码方式跟 RISC-V-TLM 也完全不一样,
对设计新的 Instruction Decoder 很有帮助。

U-type

指令格式如下:

|31                      12|11   7|6       0|
+-------------------------------------------+
|            imm           |  rd  | opcode  |
+-------------------------------------------+

LUI

采用 20 bit Asymmetric 的设计,
搭配 I-type 指令就可以指定任何想要的数值。
rd = imm << 12;

|31                      12|11   7|6       0|
+-------------------------------------------+
|            imm           |  rd  | opcode  |
+-------------------------------------------+

AUIPC

imm 设定为 0 可以取得 Program Counter,
规格书上有提到其实 JALR 也一样可以有一样的效果,
但是会影响 Pipeline 和 Branch Predictor 系统的运作流程。

目前没有使用这道指令的经验,
看起来跟 Boot Loader 准备好 MMU 设定之後跳过去指定位置执行有关,
有看过的夥伴欢迎帮忙补充。
rd = current_pc + (imm << 12);

|31                      12|11   7|6       0|
+-------------------------------------------+
|            imm           |  rd  | opcode  |
+-------------------------------------------+

实际程序

github 页面 Tag: ITDay14
写 Tag 写这麽多天,刚刚才发现根本忘记推上 github...

首先先来解决那个奇怪的 Bug:
根据 SystemC 2.3.1 文件所述,
sc_int<32> 只有在 Assign 之後才会执行 extend_sign()

//sc_int_base.h
sc_int_base& operator <<= ( int_type v )
    { m_val <<= v; extend_sign(); return *this; }

照上面的描述可以知道,
单纯的 << Operator 是不会进行 sign extension 的,
需要 Assign 才行,
改成下面这样就没问题了:

//instructionDecoder.cpp
int32_t INSTRUCTION_DECODER::get_imm(uint32_t end, uint32_t start)
{
    auto value = sc_dt::sc_int<32>(instruction_value);
    value <<= (31-end);
    value >>= (31 - end + start);
	return   value;
}

这次刚好用到 Program Counter 相关的功能,
就顺便做完它。
看起来是个小小的功能,做起来其实很有趣,
参考了相对成熟的 RISC-V-TLMexactstep
前者是在执行後回传 PC 是否需要 +4,後者是直接在每道指令执行时决定,
一个会在每个 Ececute Function 开不必要的介面,
另一个会多写很多不必要的程序码增加修改时错误的机率。

最後决定自己换一个方法:
EXECUTOR 中增加一个暂存的 new_pc ,
在执行指令前设定为 current_pc + 4,
在由执行中的指令依照需求覆盖,并在最後写入。

...
//executor.h
uint32_t new_pc; //write back to register when execute finished
...
//executor.cpp
void EXECUTOR::execute()
{
	new_pc = register_file->get_pc() + 4;
...
	register_file->set_pc(new_pc);
}

指令的部分相对单纯

//instructionDecoderInterface.h
...
		LUI_OP = 0b0110111,
		AUIPC_OP = 0b0010111,
...
//executor.cpp
...

		case INSTRUCTION_DECODER_INTERFACE::LUI_OP:
			LUI_E();
			break;
		case INSTRUCTION_DECODER_INTERFACE::AUIPC_OP:
			AUIPC_E();
			break;
...
void EXECUTOR::LUI_E()
{
	auto rd = instruction_decoder->get_rd();

	auto value = (instruction_decoder->get_imm(31, 12) << 12);
	register_file->set_value_integer(rd, value);
...
}
void EXECUTOR::AUIPC_E()
{
	auto rd = instruction_decoder->get_rd();

	auto value = register_file->get_pc() + (instruction_decoder->get_imm(31, 12) << 12);
	register_file->set_value_integer(rd, value);
...
}
...

<<:  不要为了 Unit Test 而写 Unit Test

>>:  Day13-旧网站重写成Vue_4_TAB页签式选单

【C++】Stack and Queues

堆叠跟柱列在程序中算是很基本的资料结构~ 它们的储存特性一个是LIFO~ 另一个则是FIFO~ 学习...

.NET Core第17天_LabelTagHelper的使用

LabelTagHelper的使用 对应於HTML tag的封装,用於给予对应的显示名称。 当中的f...

javascript函式的变形2

现在我们来学习函式的进阶,全域变数跟区域变数的差别和使用方法。区域变数的差别和使用方法。 ...

笔记-Color in Image and Video

Basics of Color Light and Spectra(光和光谱) 可见光(visibl...

Day4 - Yolo? 那是什麽? 能玩吗?

今天要介绍的是(小鼓滚奏)……………..YOLO! YOLO(you only live once)...