[南大ICS-PA2] 指令集实现
[南大ICS-PA2] 指令集实现
- 指令布局和寄存器
- 指令集实现
- `li/addi/mv`
- `auipc rd, immediate x[rd] = pc + sext(immediate[31:12] << 12)`
- `jal`
- `sp`寄存器初始化问题
- `jalr/ret`
- `add`
- `sub`
- `sltiu/seqz`
- `beq/beqz`
- `bne/bnez`
- `sltu/snez`
- `xor rd, rs1, rs2 x[rd] = x[rs1] ^ x[rs2]`
- `xori rd, rs1, immediate x[rd] = x[rs1] ^ sext(immediate)`
- `or rd, rs1, rs2 x[rd] = x[rs1] | 𝑥[𝑟𝑠2]`
- `ori rd, rs1, immediate x[rd] = x[rs1] | sext(immediate)`
- 中文书籍错误纠正
- `sh rs2, offset(rs1) M[x[rs1] + sext(offset) = x[rs2][15: 0]`
- `sra rd, rs1, rs2 x[rd] = (x[rs1] ≫𝑠 x[rs2])`
- `srai rd, rs1, shamt x[rd] = (x[rs1] ≫𝑠 shamt)`
- `lb rd, offset(rs1) x[rd] = sext(M[x[rs1] + sext(offse`
- `lbu rd, offset(rs1) x[rd] = M[x[rs1] + sext(offset)][7:0]`
- `sll rd, rs1, rs2 x[rd] = x[rs1] ≪ x[rs2]`
- `slli rd, rs1, shamt x[rd] = x[rs1] ≪ shamt`
- `and rd, rs1, rs2 x[rd] = x[rs1] & x[rs2]`
- `andi rd, rs1, immediate x[rd] = x[rs1] & sext(immediate)`
- `bge/blez/ble/bgez rs1, rs2, offset if (rs1 ≥s rs2) pc += sext(offset)`
- `mul rd, rs1, rs2 x[rd] = x[rs1] × x[rs2]`
- `mulh rd, rs1, rs2 x[rd] = (x[rs1] 𝑠 ×𝑠 x[rs2]) ≫𝑠 XLEN`
- `div rd, rs1, rs2 x[rd] = x[rs1] ÷s x[rs2] `
- `divu rd,rs1,rs2 x[rd] = x[rs1] ÷u x[rs2]`
- `rem rd, rs1, rs2 x[rd] = x[rs1] %𝑠 x[rs2]`
- `remu rd,rs1,rs2 x[rd] = x[rs1] %𝑢 x[rs2]`
- `sb rs2, offset(rs1) M[x[rs1] + sext(offset) = x[rs2][7: 0]`
- `blt rs1,rs2,offset if (rs1
- `bltu rs1, rs2, offset if (rs1
- `slt rd, rs1, rs2 x[rd] = (x[rs1] <𝑠 x[rs2])`
- `lh rd, offset(rs1) x[rd] = sext(M[x[rs1] + sext(offset)][15:0])`
- `lhu rd, offset(rs1) x[rd] = M[x[rs1] + sext(offset)][15:0]`
- `srl rd, rs1, rs2 x[rd] = (x[rs1] ≫𝑢 x[rs2])`
- `srli rd, rs1, shamt x[rd] = (x[rs1] ≫𝑢 shamt)`
- `bge rs1, rs2, offset if (rs1 ≥s rs2) pc += sext(offset)`
- `bgeu rs1, rs2, offset if (rs1 ≥u rs2) pc += sext(offset)`
- `bltu rs1, rs2, offset if (rs1
- 内存地址超出
指令布局和寄存器


指令集实现
-
make ARCH=riscv32-nemu ALL=dummy run汇编代码如下:
Disassembly of section .text:80000000 <_start>: 80000000: 00000413 li s0,0 80000004: 00009117 auipc sp,0x9 80000008: ffc10113 addi sp,sp,-4 # 80009000 <_end> 8000000c: 00c000ef jal ra,80000018 <_trm_init>80000010:宏immU已经将指令中得20位移到结果中得高位了 80000010: 00000513 li a0,0 80000014: 00008067 ret80000018 <_trm_init>: 80000018: 80000537 lui a0,0x80000 8000001c: ff010113 addi sp,sp,-16 80000020: 03850513 addi a0,a0,56 # 80000038 <_end+0xffff7038> 80000024: 00112623 sw ra,12(sp) 80000028: fe9ff0ef jal ra,80000010 8000002c: 00050513 mv a0,a0 80000030: 00100073 ebreak 80000034: 0000006f j 80000034 <_trm_init+0x1c>
li/addi/mv
00000413 li s0,0
二进制代码为0000 0000 0000 0000 0000 0100 0001 0011
在RISC-V中文版手册中P25中没有找打指令li,于是分析指令得二进制接口,看是否能和其中得某条指令重合。
opcode为0010011,在I型指令中,符合addi,slti,sltiu,xori,ori,andi,slli,srli,srai- 7到11位是目的寄存器编号,
rd=8,查阅寄存器名称可知。s0寄存器得位置正是8; - 12到14位为0,对应I型指令的
addi
INSTPAT("??????? ????? ????? 000 ????? 00100 11", li , I, R(dest) = src1 + imm); // 也即addi
同理,ffc10113 addi sp,sp,-4 # 80009000 <_end>和这是同一个pattern
auipc rd, immediate x[rd] = pc + sext(immediate[31:12] << 12)
PC 加立即数 (Add Upper Immediate to PC). U-type, RV32I and RV64I. 把符号位扩展的 20 位(左移 12 位)立即数加到 pc 上,结果写入 x[rd]。immediate[31:12] rd 0010111
00009117 auipc sp,0x9
将寄存器的高20位加上立即数。把符号位扩展的 20 位(左移 12 位)立即数加到 pc 上,结果写入 x[rd]。
INSTPAT("??????? ????? ????? ??? ????? 00101 11", auipc , U, R(dest) = s->pc + imm); // 宏immU已经将指令中得20位移到结果中得高位了,这里只要直接加上
jal
fe9ff0ef jal ra,80000010
跳转并链接指令具有双重功能。
- 若将下一条指令PC+4的地址保存到目标寄存器中,通常是返回地址寄存器ra,便可以用它来实现过程调用;
- 如果是用零寄存器(x0)替换ra作为目标寄存器,则可以实现无条件跳转,因为x0不能更改。
所以指令
- 第一步要将下一条执行的指令存入返回寄存器中,这里是
R(dest) = s->snpc,s->snpc代表下一条静态指令。此时dest=0也可以,因为在nemu中,这个函数最后会强制使得寄存器x0为0,R(0)=0。 - 将动态PC置为下一条要执行的指令。
// 31302928272625|2423222120|1918171615|141312|1110090807|06050403020100
// 31__________25|24______20|19______15|14__12|11______07|06__________00
// imm[20|10:1|11|19:12] | rd | 110 1111 J jal
#define immJ() do { *imm = (SEXT(BITS(i, 31, 31), 1) << 20) | BITS(i, 30, 21) << 1 \| BITS(i, 20, 20) << 11 | BITS(i, 19, 12) << 12 ; } while(0)case TYPE_J: immJ(); break;INSTPAT("??????? ????? ????? ??? ????? 11011 11", jal , J, R(dest) = s->snpc; s->dnpc = s->pc + imm); // 要更新动态pc
sp寄存器初始化问题
由于初始时,sp寄存器中的值为0。在此测试程序中,向高20位加9,然后就是几个减法操作,这样,sp中的数仍然小于0x80000000,导致访问内存时出错,所以sp在程序开始时应该置为0x800000000。在哪里做这件事呢?重新查看PA1,看看初始化寄存器时应该怎么做。PA1中也没有说。继续查看运行程序时的寄存器初始化部分代码。
在src/isa/riscv32/init.c中,restart函数原始代码如下
static void restart() {/* Set the initial program counter. */问题cpu.pc = RESET_VECTOR;/* The zero register is always 0. */cpu.gpr[0] = 0;
}
将其修改,使得适应riscv32架构,他的初始sp=0x80000000,修改后代码如下
static void restart() {/* Set the initial program counter. */cpu.pc = RESET_VECTOR;/* The zero register is always 0. */cpu.gpr[0] = 0;// sp 初始应该为0x80000000cpu.gpr[2] = CONFIG_MBASE;
}
jalr/ret
jalr rd, offset(rs1) t =pc+4; pc=(x[rs1]+sext(offset))&~1; x[rd]=t
把 pc 设置为 x[rs1] + sign-extend(offset),把计算出的地址的最低有效位设为 0,并将原 pc+4 的值写入 f[rd]。rd 默认为 x1。
// 把 pc 设置为 x[rs1] + sign-extend(offset),把计算出的地址的最低有效位设为 0,并将原 pc+4 的值写入 f[rd]。rd 默认为 x1。
INSTPAT("??????? ????? ????? 000 ????? 11001 11", jalr , I, R(dest) = s->snpc; s->dnpc = src1 + imm); // 也即ret
add
把寄存器 x[rs2]加到寄存器 x[rs1]上,结果写入 x[rd]。忽略算术溢出。R型
0000000 rs2 rs1 000 Rd 0110011
INSTPAT("0000000 ????? ????? 000 ????? 01100 11", add , R, R(dest) = src1 + src2);
sub
x[rs1]减去 x[rs2],结果写入 x[rd]。忽略算术溢出。R型
0100000 rs2 rs1 000 rd 0110011
INSTPAT("0100000 ????? ????? 000 ????? 01100 11", sub , R, R(dest) = src1 - src2);
sltiu/seqz
sltiu: 无符号小于立即数则置位(Set if Less Than Immediate, Unsigned). I-type, RV32I and RV64I. 比较 x[rs1]和有符号扩展的 immediate,比较时视为无符号数。如果 x[rs1]更小,向 x[rd]写入 1,否则写入 0。
seqz: 等于 0 则置位(Set if Equal to Zero). 伪指令(Pseudoinstruction), RV32I and RV64I. 如果 x[rs1]等于 0,向 x[rd]写入 1,否则写入 0。实际被扩展为 sltiu rd, rs1, 1。
00153513 seqz a0,a0
0000 0000 0001 0101 0011 0101 0001 0011
immediate[11:0] rs1 011 rd 0010011
// 无符号数比较,比较时,两数均视作无符号数
INSTPAT("??????? ????? ????? 011 ????? 00100 11", sltiu , I, R(dest) = src1 < imm ? 1 : 0); // sltiu/seqz
beq/beqz
beqz: rs1, offset if (rs1 == 0) pc += sext(offset)
等于零时分支 (Branch if Equal to Zero). 伪指令(Pesudoinstruction), RV32I and RV64I. 可视为 beq rs1, x0, offset
beq: rs1, rs2, offset if (rs1 == rs2) pc += sext(offset)
相等时分支 (Branch if Equal). B-type, RV32I and RV64I. 若寄存器 x[rs1]和寄存器 x[rs2]的值相等,把 pc 的值设为当前值加上符号位扩展的偏移 offset。
offset[12|10:5] rs2 rs1 000 offset[4:1|11] 1100011
即下一条指令的值为当前pc+offset
// 31302928272625|2423222120|1918171615|141312|1110090807|06050403020100
// 31__________25|24______20|19______15|14__12|11______07|06__________00
// imm[12|10:5] | rs2 | rs1 | 000 |imm[4:1|11]| 110 0011 B beq
#define immB() do { *imm = (SEXT(BITS(i, 31, 31), 1) << 12) | BITS(i, 7, 7) << 11 \| BITS(i, 30, 25) << 5 | BITS(i, 11, 8) << 1; } while (0) // 如果两个寄存器中的值相等,把下一条要执行的指令设置为当前pc+imm
INSTPAT("??????? ????? ????? 000 ????? 11000 11", beq , B, s->dnpc = (src1 == src2 ? s->pc + imm : s->dnpc)); // beq/beqz以为卖不动的仰望U8,在48小时订单13000余辆。截止4月19日晚 8 点,仰望U8订单累计破3万了,两天的订单达到3万台,预计成交金额可达到329.4亿元
bne/bnez
-
bne: rs1, rs2, offset if (rs1 ≠ rs2) pc += sext(offset)
不相等时分支 (Branch if Not Equal). B-type, RV32I and RV64I. 若寄存器 x[rs1]和寄存器 x[rs2]的值不相等,把 pc 的值设为当前值加上符号位扩展的偏移 offset。
-
bnez: rs1, offset if (rs1 ≠ 0) pc += sext(offset)
不等于零时分支 (Branch if Not Equal to Zero). 伪指令(Pesudoinstruction), RV32I and RV64I. 可视为 bne rs1, x0, offset.
offset[12|10:5] rs2 rs1 001 offset[4:1|11] 1100011
// 小于时分支,无符号数 INSTPAT("??????? ????? ????? 110 ????? 11000 11", bltu , B, s->dnpc = (src1 < src2 ? s->pc + imm : s->dnpc)); // bne/bnez
sltu/snez
sltu rd, rs1, rs2 x[rd] = (x[rs1] <𝑢 x[rs2])
无符号小于则置位(Set if Less Than, Unsigned). R-type, RV32I and RV64I.
比较 x[rs1]和 x[rs2],比较时视为无符号数。如果 x[rs1]更小,向 x[rd]写入 1,否则写入 0。
// 无符号小于则置位
INSTPAT("0000000 ????? ????? 011 ????? 01100 11", sltu , R, R(dest) = (src1 < src2 ? 1 : 0));
snez: 不等于 0 则置位(Set if Not Equal to Zero). 伪指令(Pseudoinstruction), RV32I and RV64I. 如果 x[rs1]不等于 0,向 x[rd]写入 1,否则写入 0。实际扩展为 sltu rd, x0, rs2。
xor rd, rs1, rs2 x[rd] = x[rs1] ^ x[rs2]
异或(Exclusive-OR). R-type, RV32I and RV64I. x[rs1]和 x[rs2]按位异或,结果写入 x[rd]。0000000 rs2 rs1 100 rd 0110011
// 按位异或
INSTPAT("0000000 ????? ????? 100 ????? 01100 11", xor , R, R(dest) = src1 ^ src2);
xori rd, rs1, immediate x[rd] = x[rs1] ^ sext(immediate)
立即数异或(Exclusive-OR Immediate). I-type, RV32I and RV64I. x[rs1]和有符号扩展的 immediate 按位异或,结果写入 x[rd]。immediate[11:0] rs1 100 rd 0010011
// 立即数异或
INSTPAT("??????? ????? ????? 100下的所有 js 文件都会被 coc 当作插 ????? 00100 11", xori , I, R(dest) = src1 ^ imm);
not rd, rs1 x[rd] = ~x[rs1]: 取反(NOT). 伪指令(Pseudoinstruction), RV32I and RV64I. 把寄存器 x[rs1]对于 1 的补码(即按位取反的值)写入 x[rd]。实际被扩展为 xori rd, rs1, -1。
or rd, rs1, rs2 x[rd] = x[rs1] | 𝑥[𝑟𝑠2]
取或(OR). R-type, RV32I and RV64I. 把寄存器 x[rs1]和寄存器 x[rs2]按位取或,结果写入 x[rd]。0000000 rs2 rs1 110 rd 0110011
INSTPAT("0000000 ????? ????? 110 ????? 01100 11", or , R, R(dest) = src1 | src2);
ori rd, rs1, immediate x[rd] = x[rs1] | sext(immediate)
立即数取或(OR Immediate). R-type, RV32I and RV64I. 把寄存器 x[rs1]和有符号扩展的立即数 immediate 按位取或,结果写入 x[rd]。 Immediate[11:0] rs2 rs1 110 rd 0010011
中文书籍错误纠正
那本中文翻译的书,关于ori指令错误,应该为 Immediate[11:0] rs1 110 rd 0010011,且应该是I型指令
INSTPAT("??????? ????? ????? 110 ????? 00100 11", ori , I, R(dest) = src1 | imm);
sh rs2, offset(rs1) M[x[rs1] + sext(offset) = x[rs2][15: 0]
存半字(Store Halfword). S-type, RV32I and RV64I. 将 x[rs2]的低位 2 个字节存入内存地址 x[rs1]+sign-extend(offset)。offset[11:5] rs2 rs1 001 offset[4:0] 0100011
INSTPAT("??????? ????? ????? 001 ????? 01000 11", sh , S, Mw(src1 + imm, 2, src2 & 0xffff));
sra rd, rs1, rs2 x[rd] = (x[rs1] ≫𝑠 x[rs2])
算术右移(Shift Right Arithmetic). R-type, RV32I and RV64I. 把寄存器 x[rs1]右移 x[rs2]位,空位用 x[rs1]的最高位填充,结果写入 x[rd]。x[rs2]的低 5 位 (如果是 RV64I 则是低 6 位)为移动位数,高位则被忽略。0100000 rs2 rs1 101 rd 0110011
INSTPAT("0100000 ????? ????? 101 ????? 01100 11", sra , R, R(dest) = (word_t)(((int32_t)src1) >> (src2 & 0x1f)));
srai rd, rs1, shamt x[rd] = (x[rs1] ≫𝑠 shamt)
立即数算术右移(Shift Right Arithmetic Immediate). I-type, RV32I and RV64I. 把寄存器 x[rs1]右移 shamt 位,空位用 x[rs1]的最高位填充,结果写入 x[rd]。对于 RV32I, 仅当 shamt[5]=0 时指令有效。010000 shamt rs1 101 rd 0010011
INSTPAT("0100000 ????? ????? 101 ????? 00100 11", srai , I, R(dest) = (word_t)(((int32_t)src1) >> (imm & 0x1f)));
lb rd, offset(rs1) x[rd] = sext(M[x[rs1] + sext(offse
字节加载 (Load Byte). I-type, RV32I and RV64I. 从地址 x[rs1] + sign-extend(offset)读取一个字节,经符号位扩展后写入 x[rd]。offset[11:0] rs1 000 rd 0000011
INSTPAT("??????? ????? ????? 000 ????? 00000 11", lb , I, R(dest) = SEXT(Mr(src1 + imm, 1), 8));
lbu rd, offset(rs1) x[rd] = M[x[rs1] + sext(offset)][7:0]
无符号字节加载 (Load Byte, Unsigned). I-type, RV32I and RV64I. 从地址 x[rs1] + sign-extend(offset)读取一个字节,经零扩展后写入 x[rd]。offset[11:0] rs1 100 rd 0000011
INSTPAT("??????? ????? ????? 100 ????? 00000 11", lbu , I, R(dest) = Mr(src1 + imm, 1));
sll rd, rs1, rs2 x[rd] = x[rs1] ≪ x[rs2]
逻辑左移(Shift Left Logical). R-type, RV32I and RV64I. 把寄存器 x[rs1]左移 x[rs2]位,空出的位置填入 0,结果写入 x[rd]。x[rs2]的低 5 位(如果是 RV64I 则是低 6 位)代表移动位数,其高位则被忽略。0000000 rs2 rs1 001 rd 0110011
INSTPAT("0000000 ????? ????? 001 ????? 01100 11", sll , R, R(dest) = src1 << (src2 & 0x1f));
slli rd, rs1, shamt x[rd] = x[rs1] ≪ shamt
立即数逻辑左移(Shift Left Logical Immediate). I-type, RV32I and RV64I. 把寄存器x[rs1]左移shamt位,空出的位置填入0,结果写入x[rd]。对于RV32I,仅当shamt[5]=0 时,指令才是有效的。000000 shamt rs1 001 rd 0010011
INSTPAT("0000000 ????? ????? 001 ????? 00100 11", slli , I, R(dest) = src1 << (imm & 0x1f));
and rd, rs1, rs2 x[rd] = x[rs1] & x[rs2]
与 (And). R-type, RV32I and RV64I. 将寄存器 x[rs1]和寄存器 x[rs2]位与的结果写入 x[rd]。0000000 rs2 rs1 111 rd 0110011
INSTPAT("0000000 ????? ????? 111 ????? 01100 11", and , R, R(dest) = src1 & src2);
andi rd, rs1, immediate x[rd] = x[rs1] & sext(immediate)
与立即数 (And Immediate). I-type, RV32I and RV64I. 把符号位扩展的立即数和寄存器 x[rs1]上的值进行位与,结果写入 x[rd]。immediate[11:0] rs1 111 rd 0010011
INSTPAT("??????? ????? ????? 111 ????? 00100 11", andi , R, R(dest) = src1 & imm);
这是一条**I**指令,但是我第一次实现时,变成一条R指令,因为直接复制了and的部分。在执行goldbath中一直错误,并且不是指令没有实现得错误,而是指令错误,按照汇编代码一条一条指令得调试,花了四五个小时,才排查出是一条andi指令错了。保留上面得错误实现,将正确实现展示如下:
INSTPAT("??????? ????? ????? 111 ????? 00100 11", andi , I, R(dest) = src1 & imm);
bge/blez/ble/bgez rs1, rs2, offset if (rs1 ≥s rs2) pc += sext(offset)
大于等于时分支 (Branch if Greater Than or Equal). B-type, RV32I and RV64I. 若寄存器 x[rs1]的值大于等于寄存器 x[rs2]的值(均视为二进制补码),把 pc 的值设为当前 值加上符号位扩展的偏移 offset。offset[12|10:5] rs2 rs1 101 offset[4:1|11] 1100011
INSTPAT("??????? ????? ????? 101 ????? 11000 11", bge , B, s->dnpc = ((int32_t)src1 >= (int32_t)src2 ? s->pc + imm : s->dnpc));
blez rs2, offset if (rs2 ≤s 0) pc += sext(offset) 小于等于零时分支 (Branch if Less Than or Equal to Zero). 伪指令(Pesudoinstruction), RV32I and RV64I 可视为 bge x0, rs2, offset.
ble rs1, rs2, offset if (rs1 ≤s rs2) pc += sext(offset) 小于等于时分支 (Branch if Less Than or Equal). 伪指令(Pesudoinstruction), RV32I and RV64I. 可视为 bge rs2, rs1, offset.
bgez rs1, offset if (rs1 ≥s 0) pc += sext(offset) 大于等于零时分支 (Branch if Greater Than or Equal to Zero). 伪指令(Pesudoinstruction), RV32I and RV64I. 可视为 bge rs1, x0, offset
mul rd, rs1, rs2 x[rd] = x[rs1] × x[rs2]
乘(Multiply). R-type, RV32M and RV64M. 把寄存器 x[rs2]乘到寄存器 x[rs1]上,乘积写入 x[rd]。忽略算术溢出。0000001 rs2 rs1 000 rd 0110011
INSTPAT("0000001 ????? ????? 000 ????? 01100 11", mul , R, R(dest) = src1 * src2);
mulh rd, rs1, rs2 x[rd] = (x[rs1] 𝑠 ×𝑠 x[rs2]) ≫𝑠 XLEN
高位乘(Multiply High). R-type, RV32M and RV64M. 把寄存器 x[rs2]乘到寄存器 x[rs1]上,都视为 2 的补码,将乘积的高位写入 x[rd]。0000001 rs2 rs1 001 rd 0110011
XLEN为整数寄存器得宽度(以位为单位),所以此条指令,右移了XLEN位,实际在RV32中是32位,所以前面的乘法必然得到一个64位的符号扩展结果。
INSTPAT("0000001 ????? ????? 001 ????? 01100 11", mulh , R, R(dest) = ((SEXT(src1, 32) * SEXT(src2, 32)) >> 32));
div rd, rs1, rs2 x[rd] = x[rs1] ÷s x[rs2]
除法(Divide). R-type, RV32M and RV64M. 用寄存器 x[rs1]的值除以寄存器 x[rs2]的值,向零舍入,将这些数视为二进制补码,把商写 入 x[rd]。0000001 rs2 rs1 100 rd 0110011
INSTPAT("0000001 ????? ????? 100 ????? 01100 11", div_ricsv32, R, R(dest) = (int32_t)src1 / (int32_t)src2);
本条指令中的div_ricsv32是因为div是一个系统函数,所以改了一下,避免命名冲突。
divu rd,rs1,rs2 x[rd] = x[rs1] ÷u x[rs2]
无符号除法(Divide, Unsigned). R-type, RV32M and RV64M. 用寄存器 x[rs1]的值除以寄存器 x[rs2]的值,向零舍入,将这些数视为无符号数,把商写入 x[rd]。0000001 rs2 rs1 101 rd 0110011
INSTPAT("0000001 ????? ????? 101 ????? 01100 11", divu_ricsv32 , R, R(dest) = src1 / src2);
rem rd, rs1, rs2 x[rd] = x[rs1] %𝑠 x[rs2]
求余数(Remainder). R-type, RV32M and RV64M. x[rs1]除以 x[rs2],向 0 舍入,都视为 2 的补码,余数写入 x[rd]。0000001 rs2 rs1 110 rd 0110011
// 求余数
INSTPAT("0000001 ????? ????? 110 ????? 01100 11", rem , R, R(dest) = (word_t)((int32_t)src1 % (int32_t)src2));
remu rd,rs1,rs2 x[rd] = x[rs1] %𝑢 x[rs2]
求无符号数的余数(Remainder, Unsigned). R-type, RV32M and RV64M. x[rs1]除以 x[rs2],向 0 舍入,都视为无符号数,余数写入 x[rd]。0000001 rs2 rs1 111 rd 0110011
INSTPAT("0000001 ????? ????? 111 ????? 01100 11", remu , R, R(dest) = src1 % src2);
sb rs2, offset(rs1) M[x[rs1] + sext(offset) = x[rs2][7: 0]
存字节(Store Byte). S-type, RV32I and RV64I. 将 x[rs2]的低位字节存入内存地址 x[rs1]+sign-extend(offset)。 offset[11:5] rs2 rs1 000 offset[4:0] 0100011
INSTPAT("??????? ????? ????? 000 ????? 01000 11", sb , S, Mw(src1 + imm, 1, src2)); // 原框架代码
blt rs1,rs2,offset if (rs1
小于时分支 (Branch if Less Than). B-type, RV32I and RV64I. 若寄存器 x[rs1]的值小于寄存器 x[rs2]的值(均视为二进制补码),把 pc 的值设为当前值加 上符号位扩展的偏移 offset。offset[12|10:5] rs2 rs1 100 offset[4:1|11] 1100011
INSTPAT("??????? ????? ????? 100 ????? 11000 11", blt , B, s->dnpc = ((int32_t)src1 < (int32_t)src2 ? s->pc + imm : s->dnpc)); // bne/bnez
bltu rs1, rs2, offset if (rs1
无符号小于时分支 (Branch if Less Than, Unsigned). B-type, RV32I and RV64I. 若寄存器 x[rs1]的值小于寄存器 x[rs2]的值(均视为无符号数),把 pc 的值设为当前值加上 符号位扩展的偏移 offset。offset[12|10:5] rs2 rs1 110 offset[4:1|11] 1100011
INSTPAT("??????? ????? ????? 110 ????? 11000 11", bltu , B, s->dnpc = (src1 < src2 ? s->pc + imm : s->dnpc)); // bne/bnez
slt rd, rs1, rs2 x[rd] = (x[rs1] <𝑠 x[rs2])
小于则置位(Set if Less Than). R-type, RV32I and RV64I. 比较 x[rs1]和 x[rs2]中的数,如果 x[rs1]更小,向 x[rd]写入 1,否则写入 0。0000000 rs2 rs1 010 rd 0110011
INSTPAT("0000000 ????? ????? 010 ????? 01100 11", slt , R, R(dest) = ((int32_t)src1 < (int32_t)src2 ? 1 : 0));
lh rd, offset(rs1) x[rd] = sext(M[x[rs1] + sext(offset)][15:0])
半字加载 (Load Halfword). I-type, RV32I and RV64I. 从地址 x[rs1] + sign-extend(offset)读取两个字节,经符号位扩展后写入 x[rd]。offset[11:0] rs1 001 rd 0000011
INSTPAT("??????? ????? ????? 001 ????? 00000 11", lh , I, R(dest) = SEXT(Mr(src1 + imm, 2), 16));
lhu rd, offset(rs1) x[rd] = M[x[rs1] + sext(offset)][15:0]
无符号半字加载 (Load Halfword, Unsigned). I-type, RV32I and RV64I. 从地址 x[rs1] + sign-extend(offset)读取两个字节,经零扩展后写入 x[rd]。offset[11:0] rs1 101 rd 0000011
INSTPAT("??????? ????? ????? 101 ????? 00000 11", lhu , I, R(dest) = Mr(src1 + imm, 2));
srl rd, rs1, rs2 x[rd] = (x[rs1] ≫𝑢 x[rs2])
逻辑右移(Shift Right Logical). R-type, RV32I and RV64I. 把寄存器 x[rs1]右移 x[rs2]位,空出的位置填入 0,结果写入 x[rd]。x[rs2]的低 5 位(如果是 RV64I 则是低 6 位)代表移动位数,其高位则被忽略。0000000 rs2 rs1 101 rd 0110011
INSTPAT("0000000 ????? ????? 101 ????? 01100 11", srl , R, R(dest) = src1 >> (src2 & 0x1f));
srli rd, rs1, shamt x[rd] = (x[rs1] ≫𝑢 shamt)
立即数逻辑右移(Shift Right Logical Immediate). I-type, RV32I and RV64I. 把寄存器x[rs1]右移shamt位,空出的位置填入0,结果写入x[rd]。对于RV32I,仅当shamt[5]=0 时,指令才是有效的。000000 shamt rs1 101 rd 0010011
INSTPAT("0000000 ????? ????? 101 ????? 00100 11", srli , I, R(dest) = src1 >> (imm & 0x1f));
bge rs1, rs2, offset if (rs1 ≥s rs2) pc += sext(offset)
大于等于时分支 (Branch if Greater Than or Equal). B-type, RV32I and RV64I. 若寄存器 x[rs1]的值大于等于寄存器 x[rs2]的值(均视为二进制补码),把 pc 的值设为当前 值加上符号位扩展的偏移 offset。offset[12|10:5] rs2 rs1 101 offset[4:1|11] 1100011
INSTPAT("??????? ????? ????? 101 ????? 11000 11", bge , B, s->dnpc = ((int32_t)src1 >= (int32_t)src2 ? s->pc + imm : s->dnpc)); // ble
bgeu rs1, rs2, offset if (rs1 ≥u rs2) pc += sext(offset)
无符号大于等于时分支 (Branch if Greater Than or Equal, Unsigned). B-type, RV32I and RV64I. 若寄存器 x[rs1]的值大于等于寄存器 x[rs2]的值(均视为无符号数),把 pc 的值设为当前值 加上符号位扩展的偏移 offset。offset[12|10:5] rs2 rs1 111 offset[4:1|11] 1100011
// 无符号大于等于时分支
INSTPAT("??????? ????? ????? 111 ????? 11000 11", bgeu , B, s->dnpc = (src1 >= src2 ? s->pc + imm : s->dnpc)); // bleu
内存地址超出
在框架代码中,内存的大小为#define CONFIG_MSIZE 0x8000000,又由于基址为#define CONFIG_MBASE 0x80000000,所以能传入的地址最大值为
m a x _ a d d r e s s = 0 x 8000 , 0000 + 0 x 800 , 0000 − 1 = 0 x 8800 , 0000 − 1 = 0 x 87 f f , f f f f \rm max\_address=0x8000,0000+0x800,0000-1=0x8800,0000 - 1 = 0x87ff,ffff max_address=0x8000,0000+0x800,0000−1=0x8800,0000−1=0x87ff,ffff
然而在测试代码hello-str中,查看其汇编代码,其中部分如下所示
800000e4 :
800000e4: a00007b7 lui a5,0xa0000
800000e8: 3ea78c23 sb a0,1016(a5) # a00003f8 <_end+0x1fff73f8>
800000ec: 00008067 ret
可以看出,无论a5原先是什么值,在pc=800000e4后,a5=0xa000,0000,最终写入的地址为adress=a5+1016=0xa00003f8,显然,这个地址超出了框架代码中能访问的地址最大值。
综上所述,我修改内存的大小,使得这个地址是合法的,不然怎么修改代码都没用。修改为
#define CONFIG_MSIZE 0x30000000
在第一阶段中,由于string和hello-str还需要实现额外的内容才能运行(具体在后续小节介绍), 目前可以先使用其它测试用例进行测试。所以还是改回来,仍用原来的值。
define CONFIG_MSIZE 0x8000000
这个最后还是没有修改了,因为这个错误是因为klib中的函数没实现,在这里先不测试这个。
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
