深入剖析RISC-V指令:类型、编解码与工作原理

深入剖析RISC-V指令:类型、编解码与工作原理

什么是指令

本篇文章将以RISC-V 32位处理器为例,详细为大家介绍什么是指令、指令的类型、指令的编解码,揭示计算机工作的秘密。

我们在《程序之下:计算机的三层抽象与性能优化的底层逻辑》中提到过,想要让计算机能够执行目标任务,就必须用计算的话,告诉它该如何执行。我们所写的所有高级语言都会被转成计算机能够理解的语言–指令(Instruction)

指令是01字符串,它告诉计算机该执行什么操作(operation),以及操作的对象(operands)。

指令的类型

在开始之前,请聪明的你思考一个问题:假如你是一个机器人,你应该抽象出那些特质,才能够让你表现得和真实的人类一样?

在这里我给出我的个人看法:信息获取(眼睛、鼻子、耳朵、嘴巴)、信息处理与决策(大脑)、执行行动(四肢)。这个问题,其实很有启发性,因为**计算的工作,其实也可以抽象成一些列行为,这些行为的排列组合可以构成可表述的任意复杂的任务。**在这里我特别想强调可表述,这是因为一旦人类的特质,如创新、幽默、感性、情绪等可以用数学的公式表达。这就意味着,可以创建出具有人类的特质的机器人,那么那个时候或许离通用人工智能就不远了。

计算机的行为可以分为五大类,相应地也就对应了不同的指令类型,它们分别对应为:计算(Arithmetic)、数据搬运(Data Transfer)、逻辑运算(Logical)、移位(Shift)、决策(Conditional branch、 Unconditional branch)

下图为常见的RISC-V 汇编语言,知道了汇编语言其实也就知道了指令,不过在告诉你汇编与指令之间的转换, 即指令的编解码之前,咱还需要在知识的海洋里泡澡。

在这里插入图片描述

所有运算里,加法或许是最简单的的运算了,它们是对寄存器直接操作,从寄存器里面读取值,然后存放到寄存器里。

add x5, x6, x7  //x5= x6 + x7 ,先将寄存器x6位置的所存值与寄存器x7位置的值相加,然后在存入到寄存器x5

sub x19, x5, x6 //x19 = x5-x6 ,先将寄存器x5位置的所存值与寄存器x7位置的值相减,然后在存入到寄存器x19

编译器的工作是将程序变量与对应的寄存器相关联,以下面赋值的语句位列

​ f = (g+h)-(i+j)

我们假设分配给 f, g, h, i, j对应的寄存器分别为x19,x20,x21,x22,x23;x5,x6为两个临时变量寄存器。 那么编译后的RISC-V代码对应为

​ add x5, x20,x21 // x5 = g+h

​ add x6, x22, x23 //x6 =i+j

​ sub x19, x5,x6 //f=(g+h)-(i+j)

当然,除了基本的加减法运算,数据搬运型指令其实也非常重要,它们负责寄存器和内存之间数据的交换与传递。有两类常用的指令,分别对应为 ld(用于载入) 和 sd(用于存储)。它们将内存对应位置的数据载入到特定寄存器 或者 将寄存器的值存储在内存特定位置

假设变量 h存放在寄存器x21中,数组A的基址存放在寄存器x22中,那么

​ A[12] = h+A[8]

的汇编语言如下

​ ld x9, 64(x22) // 将A[8]处对应的载入到临时寄存器x9上

​ add x9, x9 x21 // x9 =h+A[8]

​ sd x9, 96(x22) //将h+A[8]的值载入到A[12]

提醒: 对内存的处理时(想想数组),常常用基地址(首地址)+偏移量 的方法来存放或者载入数据

​ 数组中对应的一个偏移量,对应一个字节的数据(8位),所以A[8]–>A[0]+8x8–> ld x9, 64(x22)

到这里,你已经登堂入室了,可以进入下一个部分:指令编解码。

指令编解码

生活中常常会出现这样一个场景:人群中,你听见有人叫你的名字,你立刻四处张望,试图确定是谁。那么这里就有一个很有意思的话题了:别人叫你的名字,你却答应了。这是否意味着,你的名字等于你呢?换言之,你等于你的名字。

从哲学的角度,或许会扯到一大堆符号学让人红温的东西。简单来说,你的名字是对你的编码,,它是你的标签,每当有人使用这个标签的时候,你下意识地认为是你。我们也可以举一个程序员都懂的东西,你在赋予变量的值的时候,你实际上是在对变量贴上了一个标签。

指令其实也是一样,它是将字符串拆分成不同有效字段,不同有效字段被赋予不同的含义

下图就是RISC-V R型指令的格式, 该指主要用于对寄存器经行操作
在这里插入图片描述

  • opcode :操作码,用于表示指令操作和指令格式

  • rd : 目的操作数寄存器,用来存放操作结果

  • funct3 :辅助操作码,用于辅助识别操作类型

  • rs1 :第一个源操作寄存器

  • rs2 : 第二个源操作寄存器

  • funct7 : 另外一个操作码字段

说明

  1. 7 + 5 +5+3+5+7 =32; 对应了32bit处理器

  2. 2^5 =32; 源操作寄存器以及目的操作寄存器的数据来去方向覆盖到32个寄存器

  3. 操作码,funct3,funct7 一起决定了指令的类型和操作方式, 而 rs1,rs2, rd 则表示操作的对象

在这里插入图片描述

这里举一个列子,演示从汇编到机器码的过程

add x9,x21,x9

  1. 指令为 add, 所以 funct7 = 0000000, funct3=000 两者一起表示操作类型; opcode=0110011(表示指令类型)
  2. 目的操作寄存器为 x9, 也就是第9个寄存器, rd= (9)10 = (01001)2
  3. 同理 rs1=(21)10=(10101)2, rs2= (9)10 = (01001)2
  4. 汇编转换成机器语言为: 0000000_01001_10101_000_01001_0110011(此处使用下划线为了方便阅读,也可以将32位二进制转为8位16进制)

针对不同的操作,需求不一样,因而指令的类型也不一样,对于立即数加法(add)和装载(ld),其格式如下

在这里插入图片描述

在这里插入图片描述

再举一个列子

数组A的首地址存在x10中,

​ x9 = A[30] 对应的机器码为?

  1. 对应的汇编语言为 ld x9, 240(x10)
  2. I型指令,opcode=0000011
  3. 载入数据, funct3=011
  4. 原操作寄存器x10, rs1=(10)10=(01010)2,目的操寄存器作x9, rd=(9)10=(01001)2
  5. 偏移地址 (240)10=(000011110000)2
  6. 所以机器码为 : 000011110000_01010_011_01001_0000011(此处使用下划线为了方便阅读,也可以将32位二进制转为8位16进制)

指令告诉了计算机执行什么操作,以及操作的对象,相信你看了这两个例子深有感触。

下面图为常见的指令类型以及对应的编码

在这里插入图片描述

n.a 表示 not applicable,这意味着对于某些指令或操作码,这些字段或值并不相关或不需要填写

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JINX的诅咒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值