RISC-V: I-type 位元运算指令

看到标题一定都猜到了,
相信大家对这几道指令都不陌生,这次实作的就是 ANDI、ORI、XORI,
对应到程序语言中,分别是 &、|、^ 的位元运算。

为了方便阅读,接下来章节都会列出指令格式。

I-type

指令格式如下:

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

ANDI

rd = rs1 & imm

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

ORI

rd = rs1 | imm

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

XORI

rd = rs1 ^ imm

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

NOT

没错,和这次实作的第一道指令一样
这个指令也是致敬 XORI,
RISC-V 的 imm 运算前都会进行 sign extension,
只要填入 -1 ,就会变成:
rd = rs1 ^ (0xFFFF)
= ~rs1

|31     20|19   15|14    12|11   7|6       0|
+-------------------------------------------+
|   -1    |  rs1  |  100   |  rd  | 0010011 |
+-------------------------------------------+

实际程序

github 页面 Tag: ITDay11

这次修了一个已知的小 Bug,
一开始在 Register File 其实没有实作 x0 Hard Wire to Zero 的功能。
导致写入 x0 实际上也是会把值存进去的,读取也会读取实际的值。

还有一个 Bug 在 Decoder 里面,留着明天处理,
答案其实在 Console 印出来的运算结果里面,大家可以先猜猜看罗。

太习惯平常的工作流程了,
开发没有 Unit Test 保护,写起来实在没什麽没安全感。

//register.cpp
void REGISTER::set_value_integer(unsigned int register_index, int32_t value)
{
	if(register_index == 0)
		return;
...
}

int32_t REGISTER::get_value_integer(unsigned int register_index)
{
	if(register_index == 0)
		return 0;
...
}

这次在 Decoder 新增了 Func3,增加可读性。
也把 OP_IMM 改成 IMM_OP
让 vim 的 auto complete 能够更精准地提供选项。

//instructionDecoderInterface.h
	enum Opcode {
		IMM_OP = 0b0010011,
	};

	enum Func3 {
		//IMM_OP
		ADDI_FN3 = 0b000,
		ANDI_FN3 = 0b111,
		ORI_FN3 = 0b110,
		XORI_FN3 = 0b100,
	};

在 Executor 里面新增 ANDI、ORI、XORI 的实作,
另外把 if 用 switch 取代,减少重复性的程序码。

void EXECUTOR::execute()
{

	switch (instruction_decoder->get_opcode()) {
		case INSTRUCTION_DECODER_INTERFACE::IMM_OP:
			switch (instruction_decoder->get_func3()) {
				case INSTRUCTION_DECODER_INTERFACE::ADDI_FN3:
					ADDI_E();
					break;
				case INSTRUCTION_DECODER_INTERFACE::ANDI_FN3:
					ANDI_E();
					break;
				case INSTRUCTION_DECODER_INTERFACE::XORI_FN3:
					ORI_E();
					break;
				case INSTRUCTION_DECODER_INTERFACE::ORI_FN3:
					XORI_E();
					break;
				default:
					std::cout << "INVALID: Func3 in IMM_OP :" << instruction_decoder->get_func3() << std::endl;
					break;
			}
			break;
		default:
			std::cout << "INVALID: Opcode :" << instruction_decoder->get_opcode() << std::endl;
			break;
	}
}

执行结果

$ make run
./simulator

        SystemC 2.3.3-Accellera --- Sep 17 2021 22:09:07
        Copyright (c) 1996-2018 by all Contributors,
        ALL RIGHTS RESERVED
ADDI
rs1: 0
rd: 1
value: 1
ADDI
rs1: 1
rd: 2
value: 4096
ADDI
rs1: 0
rd: 0
value: 0

<<:  小公司不是一块跳板,小公司本身就是一个伟大的目标

>>:  Day25. Blue Prism让你远离挑灯夜战的日子 –BP自动登打订单

Day 26-Unit Test 应用於 Async Code-2 (情境及应用-6)

Unit Test 应用於 Async Code-2 - 用程序码讲故事(测试码 Exception...

Day1 # Let's Go!

Go(又称 Golang)是 Google 开发的程序语言,详细简介在 wiki 上都可以找得到。 ...

JavaScript学习日记 : Day13 - 闭包(Closure)

当一个函数被建立时,闭包就会被产生,虽然常见的闭包说明方式会使用巢状函数当作例子,这是最常见的例子没...

[区块链&DAPP介绍 Day12] Solidity 教学 - contracts-1

今日来介绍 solidity 里的 contracts。 contract contract 其实是...

第一天:为什麽该学好 Gradle?

开始接触 Gradle 的原因 身为一位 Kotlin 开发者,每天需要接触的就是 JVM 生态系的...