1553 字
8 分钟
【计组】期中考补充资料
2024-11-12

如何通过机器码快速确定RISC-V指令类型: 识别步骤:

  1. 观察指令的低7位:RISC-V指令的低7位是“opcode”(操作码),它可以帮助你确定指令的大类别,例如算术指令、加载/存储指令、分支指令等。

  2. 根据操作码分类

  • R类型:常见的R类型操作码为0110011,用于算术和逻辑运算(如加法、减法、逻辑操作等)。
  • I类型:I类型的操作码有多种,例如0000011(加载指令)、0010011(立即数运算指令)和1100111(跳转和链接寄存器指令)。
  • S类型:S类型的操作码通常是0100011,用于存储指令(如SW存储字指令)。
  • B类型:B类型的操作码为1100011,用于条件分支指令。
  • U类型:U类型的操作码一般是0010111(AUIPC)或0110111(LUI)。
  • J类型:J类型的操作码为1101111,用于无条件跳转指令(如JAL)。
  1. 进一步解析:在确定大类型后,可以通过解析不同的字段来进一步确定具体的指令。例如:
  • 在R类型指令中,funct3和funct7字段可以帮助区分具体的算术运算(如加法和乘法)。
  • 在I类型指令中,funct3字段可以区分不同的立即数操作(如加法立即数和加载立即数)。

总结来说,通过检查RISC-V机器码中的低7位操作码可以快速确定指令的类型,而根据其余字段可以进一步解析具体的指令内容。 [/media/45.png] [46.png]

[47.png] [48.png]

给32位大立即数地址取出其中内容(lui+lw)

要取出一个32位的大立即数地址的内容,常用的是先通过lui指令加载高16位,再通过lw指令加载低16位进行访问。这里是实现该过程的步骤:

示例步骤

假设我们要访问的32位地址为0xABCD1234。

  1. 加载高位:使用lui指令加载高16位。lui指令会将立即数加载到寄存器的高16位,低16位补零。
lui t0, 0xABCD   # 加载地址的高16位 0xABCD 到寄存器 t0
执行完这步后,t0寄存器的值是 0xABCD0000。
  1. 加载低位偏移:使用lw指令加载低16位偏移部分。
lw t1, 0x1234(t0) # 从地址 0xABCD1234 取出内容,存入寄存器 t1

执行完这两条指令后,t1中就会包含地址 0xABCD1234 处的数据。

guard、round、stick的作用: 在浮点数运算中,额外的三个位(guard、round、sticky)是为了在舍入过程中保持更高的精度,以下是它们的具体作用:

  1. Guard 位
  • 位置:紧挨着浮点数的有效位。
  • 作用:用于记录在浮点数截断后的下一个有效位,从而提供更精确的舍入信息。
  • 用途:在截断或舍入时帮助判断是否应该将最后一位舍入上去。
  1. Round 位
  • 位置:紧跟在 guard 位之后。
  • 作用:表示截断时的下一位,用于进一步判断是否需要进位。
  • 用途:配合 guard 位,进一步决定最终的舍入方向。
  1. Sticky 位
  • 位置:在 round 位之后。
  • 作用:用于记录 guard 位之后所有被截断的低位是否有至少一位是1。
  • 用途:如果截断的低位包含1,sticky 位会被设置为1,这意味着在截断过程中舍弃了有值的位。sticky 位的引入可以确保在计算舍入时,不会忽略这些被截断的位。

在浮点数运算中的操作流程 假设有一个浮点数需要舍入,步骤如下:

  1. 截断多余位:保留 guard、round 和 sticky 位,将其余低位全部丢弃。
  2. 舍入判断:根据 IEEE 754 舍入规则,判断是否需要进位:
  • 如果 round 位为1,并且 guard 位为1 或 sticky 位为1,则通常会向上舍入。
  • 如果 guard 位和 sticky 位为0,或满足最近偶数舍入,则不会进位。 这些额外的位在浮点运算中可以显著提高结果精度,并减少误差的累积。

转换为RISC-V指令汇编程序

void main(char *s,int *n){ 
	char c,ch; 
	c = '3'; 
	ch = '5'; 
	*n = replace(*s,c,ch); 
} 
int replace(char *u,char c,char ch){ 
	int i = 0; 
	while(u[i]!=0){ 
		if(u[i]==c){ 
			u[i]=ch; 
			break; 
		} 
		i++; 
	} 
	return i; 
}

解答: [49.png]

main:
    # 保存寄存器值
    addi sp, sp, -32         # 分配栈空间(64 位系统)
    sd ra, 24(sp)            # 保存返回地址
    sd s0, 16(sp)            # 保存 s0
    sd s1, 8(sp)             # 保存 s1

    # 加载 '3' 和 '5' 的 ASCII 值到寄存器
    la s0, c_val             # 加载 c 的地址
    lbu t0, 0(s0)            # 加载 '3' 的 ASCII 值到 t0
    la s1, ch_val            # 加载 ch 的地址
    lbu t1, 0(s1)            # 加载 '5' 的 ASCII 值到 t1

    # 调用 replace 函数
    mv a0, a0                # 参数 u = s
    mv a1, t0                # 参数 c = '3'
    mv a2, t1                # 参数 ch = '5'
    jal ra, replace          # 调用 replace 函数

    # 存储返回值到 *n
    sd a0, 0(a1)             # *n = 返回值

    # 恢复寄存器并返回
    ld ra, 24(sp)            # 恢复返回地址
    ld s0, 16(sp)            # 恢复 s0
    ld s1, 8(sp)             # 恢复 s1
    addi sp, sp, 32          # 恢复栈指针
    ret                      # 返回到调用函数

replace:
    # 保存寄存器值
    addi sp, sp, -24         # 分配栈空间
    sd ra, 16(sp)            # 保存返回地址
    sd s0, 8(sp)             # 保存 s0
    sd s1, 0(sp)             # 保存 s1

    # 初始化循环计数器 i = 0
    li s0, 0

loop:
    lbu t0, 0(a0)            # 加载 u[i] 到 t0
    beq t0, zero, end        # 如果 u[i] == 0,跳转到 end

    # 检查是否 u[i] == c
    beq t0, a1, replace_char # 如果 u[i] == c,跳转替换字符

    # 否则继续循环
    addi s0, s0, 1           # i++
    addi a0, a0, 1           # 移动到 u[i + 1]
    j loop                   # 继续循环

replace_char:
    sb a2, 0(a0)             # 将 u[i] 替换为 ch

end:
    mv a0, s0                # 返回值 i
    # 恢复寄存器并返回
    ld ra, 16(sp)            # 恢复返回地址
    ld s0, 8(sp)             # 恢复 s0
    ld s1, 0(sp)             # 恢复 s1
    addi sp, sp, 24          # 恢复栈指针
    ret                      # 返回到调用函数

[50.png]

RISC-V architecture 提供 32 个数据寄存器,分别命名为 x0 ~ x31 ,每个寄存器的大小是 64 位。在 RISC-V architecture 中,一个 word 为 32 位,一个 doubleword 为 64 位。

寄存器的每一个存储单元为1个字节。

【计组】期中考补充资料
https://tillyendless.github.io/posts/计组期中考补充资料/
作者
发布于
2024-11-12
许可协议
CC BY-NC-SA 4.0