JUMP!
没有其他选择,执行到这就一定得跳!
这次有两种不同格式的指令,分别是 J-type 的 JAL,
以及 I-type 的 JALR。
另外要注意的是,
如果跳到不合法的位置会触发 instruction-address-misaligned exception。
指令格式如下:
|31 12|11 7|6 0|
+-------------------------------------------+
| imm | rd | opcode |
+-------------------------------------------+
指令格式如下:
|31 20|19 15|14 12|11 7|6 0|
+-------------------------------------------+
| imm | rs1 | funct3 | rd | opcode |
+-------------------------------------------+
是一种依照目前指令所在的 Address + Offset 决定跳到哪的指令,
因为 RISC-V 支援 Compress Extension,
必须支援跳到的位置是 Half Word Alignment,
所以这道指令的 imm 是以 Half Word 当作单位,
要乘 2 才会是真正的 Offset。
另外要注意的是,只有在支援 Compress Extension 的时候,
跳到的位置限制才会放宽到 Half Word Alignment,
不支援的情况下要特别注意 Alignment 的问题。
rd = current_pc + 4
pc = current_pc + (imm * 2)
|31 12|11 7|6 0|
+-------------------------------------------+
| imm | rd | 1101111 |
+-------------------------------------------+
这边跟上面不太一样,是完全依照 register 的内容当成 base,
再加上 offset 决定最後跳到的位置,
特别注意的是 register 内的直就直接当作 address 用,
不用为了Alignment 另外乘 2,
也可以跟前面实作的 LUI 配合使用,
20 + 12 bit 的 imm 值可以跳到 Address Space 的任意位置。
当 rs1 设定为 x0 的时候,
只要单行指令跳到 address space 最高/最低的 2KiB 的任意位置,
可以支援简单的 Run Time Library。
另外,虽然规格书明订最後运算的结果会舍去最後一个 bit,
使用上还是和 JAL 一样要注意 Alignment 的问题!
rd = current_pc + 4
pc = (rs1 + imm) & (~0x1)
|31 20|19 15|14 12|11 7|6 0|
+-------------------------------------------+
| imm | rs1 | 000 | rd | 1100111 |
+-------------------------------------------+
跟前面遇过的指令一样,
这道指令也是 opcode 和 func3 都跟 JALR 一样,
只是我们不需要回来的位置了,
所以把 rd 设定为 x0。
x0 = current_pc + 4
pc = (rs1 + imm) & (~0x1)
|31 20|19 15|14 12|11 7|6 0|
+-------------------------------------------+
| imm | rs1 | 000 | 0 | 1100111 |
+-------------------------------------------+
github 页面 Tag: ITDay18
糟糕,JAL 的 imm 格式让实作变得有点脏兮兮的了,
幸好伟大的 sc_dt 提供方便的功能,
让猴子写的 code 可以被未来的猴子看懂拉!
//before
auto offset = ((imm & ~0x7FFFF) | //imm[30:19] > offset[31:20]
(imm & 0xFF) << 11 | //imm[7:0] > offset[19:12]
(imm & (0x1 << 8)) << 2 | //imm[8] > offset[11]
(imm & (0x3FF << 9)) >> 9) //imm[18:9] > offset[10:1]
<< 1; //need to refactor to readable monkey style
//after
int32_t INSTRUCTION_DECODER::get_imm_j()
{
auto value = sc_dt::sc_int<32>();
value(20, 20) = instruction_value(31, 31);
value(19, 12) = instruction_value(19, 12);
value(11, 11) = instruction_value(20, 20);
value(10, 1) = instruction_value(31, 21);
value <<= 12;
value >>= 12;
return value;
}
把取得 offset
的功能搬到 Decoder 改写後,
指令实作的部分就变得简单很多。
//executor.cpp
...
case INSTRUCTION_DECODER_INTERFACE::JAL_OP:
JAL_E();
break;
case INSTRUCTION_DECODER_INTERFACE::JALR_OP:
switch (instruction_decoder->get_func3()) {
case INSTRUCTION_DECODER_INTERFACE::JALR_FN3:
JALR_E();
break;
default:
std::cout << "INVALID: Func3 in JALR_OP :" << instruction_decoder->get_func3() << std::endl;
break;
}
...
void EXECUTOR::JAL_E()
{
auto offset = instruction_decoder->get_imm_j();
auto rd = instruction_decoder->get_rd();
register_file->set_value_integer(rd, new_pc);
new_pc = register_file->get_pc() + offset;
}
void EXECUTOR::JALR_E()
{
auto offset = instruction_decoder->get_imm(31, 20);
auto rs1 = instruction_decoder->get_rs1();
auto rd = instruction_decoder->get_rd();
register_file->set_value_integer(rd, new_pc);
new_pc = (register_file->get_pc() + offset) & ~0x1;
}
//instructionDecoderInterface.h
...
JAL_OP = 0b1101111,
JALR_OP = 0b1100111,
...
JALR_FN3 = 0b000,
...
偷偷跟你们说个秘密:
前天有只猴子犯了小小的错误,
我偷偷笑了很久,很缺德,现在在反省,
这件事不要跟其他人说喔!
前面的部分省略,
因为把 imm 设定为 -2,乘 2 之後就是前一道指令,
可以看到 JAL 执行之後又跳回 SH 再执行一次。
$ make run
...
LHU
rs1: 0
rd: 8
addr: 4
value: 33043
SH
rs1: 0
rs2: 7
addr: 1024
JAL
rd: 0
new_pc: 48
SH
rs1: 0
rs2: 7
addr: 1024
JAL
rd: 0
new_pc: 48
对於初学着来说(像我本人),一开始如果没先了解布局的运作容易在布局的编排上产生问题,所以第一篇先来介...
AlertDialog与Toast皆可用於显示讯息, 但与Toast不同的是, AlertDialo...
前面我们谈了,coroutine的coroutineScope、继承、异常处理和取消,也在文中提到了...
引言 我们终於快完成 General Skills 了, 最後的时间我们会来解解其他六大类的题目。...
前言 相信很多刚开始接触CSS或想对切版有所认识,都知道Amos老师吧!! 非常非常感谢老师的系列教...