[南大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)`
  • 内存地址超出

指令布局和寄存器

在这里插入图片描述

在这里插入图片描述

指令集实现

  • 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,于是分析指令得二进制接口,看是否能和其中得某条指令重合。

  • opcode0010011,在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->snpcs->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

beqzrs1, 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,00001=0x8800,00001=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

在第一阶段中,由于stringhello-str还需要实现额外的内容才能运行(具体在后续小节介绍), 目前可以先使用其它测试用例进行测试。所以还是改回来,仍用原来的值。

define CONFIG_MSIZE 0x8000000

这个最后还是没有修改了,因为这个错误是因为klib中的函数没实现,在这里先不测试这个。


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章