risc-v指令学习笔记(基于ch32v103)(代码片段)

Top嵌入式 Top嵌入式     2023-01-25     554

关键词:

文章目录

RISC-V 指令学习笔记(基于CH32V103)

最近学习 RISC-V 指令,参考书籍、博客:

学习过程中使用 CH32V103 RV32 架构的单片机进行一些指令的使用复现,加强记忆!

一、指令结构分类

以 32 位 RV 架构 CPU 为例子,从 CPU 级别看来,各种指令就是 32 位的一串数字,这 32 位的数字按照存储数据的位结构,具体可以分为 6 类:

关于 6 种指令的说明,参考博客内的总结:

  • R-typed

    R-typed 指令是最常用的运算指令,具有三个寄存器地址,每个都用 5bit 的数表示。指令的操作由 7 位的 opcode、7 位的 funct7 以及 3 位的 funct3 共同决定的。R-typed 是不包含立即数的所有整数计算指令,一般表示寄存器-寄存器操作的指令。

  • I-typed

    I-typed 具有两个寄存器地址和一个立即数,其中一个是源寄存器 rs1,一个是目的寄存器 rd,指令的高 12 位是立即数。指令的操作仅由 7 位的 opcode 和 3 位的funct3两者决定。值得注意的是,在执行运算时需要先把 12 位立即数扩展到 32 位之后再进行运算。I-typed 指令相当于将 R-typed 指令格式中的一个操作数改为立即数。一般表示短立即数和访存 load 操作的指令。

  • S-typed

    S-typed 的指令功能由 7 位 opcode 和 3 位 funct3 决定,指令中包含两个源寄存器和指令的imm[31:25]和 imm[11:7]构成的一个12位的立即数,在执行指令运算时需要把12 位立即数扩展到 32 位,然后再进行运算,S-typed 一般表示访存 store 操作指令,如存储字(sw)、半字(sh)、字节(sb)等指令。

  • B-typed

    B-typed 的指令操作由 7 位 opcode 和 3 位 funct3 决定,指令中具有两个源寄存器和一个 12 位的立即数,该立即数构成是指令的第32位是 imm[12]、第7位是imm[11]、25 到 30 是 imm[10:5]、8 到 11 位是 imm[4:1],同样的,在执行运算时需要把12 位立即数扩展到 32 位,然后再进行运算。B-typed 一般表示条件跳转操作指令,如相等(beq)、不相等(bne)、大于等于(bge)以及小于(blt)等跳转指令。

  • U-typed

    U-typed 的指令操作仅由 7 位 opcode 决定,指令中包括一个目的寄存器 rd 和高20 位表示的 20 位立即数。U-typed 一般表示长立即数操作指令,例如 lui 指令,将立即数左移 12 位,并将低 12 位置零,结果写回目的寄存器中。

  • J-typed

    J-typed 的指令操作由 7 位 opcode 决定,与 U-typed 一样只有一个目的寄存器 rd和一个 20 位的立即数,但是 20 位的立即数组成不同,即指令的 31 位是 imm[20]、 12 到 19 位是 imm[19:12]、20 位是 imm[11]、21 到 30 位是 imm[10:1],J-typed 一般表示无条件跳转指令,如 jal 指令。

二、寄存器功能

了解了 RV32 的指令结构分类后,我们来看一下 RV32 架构的寄存器分配情况,RV32 有 32 个寄存器,这 32 个寄存器的定义如下:其中 ABI 是寄存器的二进制接口的名称,可以在汇编中使用。

寄存器编号寄存器 ABI 名称寄存器功能
x0zero全0寄存器
x1ra跳转返回指针
x2sp栈指针
x3gp全局指针
x4tp线程指针
x5t0临时存储器
x6t1临时存储器
x7t2临时存储器
x8s0/fp存储寄存器,框架指针
x9s1存储寄存器
x10a0函数参数寄存器(可用于返回值)
x11a1函数参数寄存器(可用于返回值)
x12a2函数参数寄存器
x13a3函数参数寄存器
x14a4函数参数寄存器
x15a5函数参数寄存器
x16a6函数参数寄存器
x17a7函数参数寄存器
x18s2存储寄存器
x19s3存储寄存器
x20s4存储寄存器
x21s5存储寄存器
x22s6存储寄存器
x23s7存储寄存器
x24s8存储寄存器
x25s9存储寄存器
x26s10存储寄存器
x27s11存储寄存器
x28t3临时存储器
x29t4临时存储器
x30t5临时存储器
x31t6临时存储器

除了上面的寄存器外,还有个 pc 指针指向程序运行地址!了解完指令结构和架构寄存器的分工后,我们了解一下指令的功能分类~

三、加载存储指令

加载存储指令用于对寄存器进行数据的加载和保存操作,主要有以下几个

  • lb rd,offset(rs1):从地址为寄存器rs1的值加offset的主存中读一个字节,符号扩展后存入rd
  • lh rd,offset(rs1):从地址为寄存器rs1的值加offset的主存中读半个字,符号扩展后存入rd
  • lw rd,offset(rs1):从地址为寄存器rs1的值加offset的主存中读一个字,符号扩展后存入rd
  • lbu rd,offset(rs1):从地址为寄存器rs1的值加offset的主存中读一个无符号的字节,零扩展后存入rd
  • lhu rd,offset(rs1):从地址为寄存器rs1的值加offset的主存中读半个无符号的字,零扩展后存入rd
  • lwu rd,offset(rs1):从地址为寄存器rs1的值加offset的主存中读一个无符号的字,零扩展后存入rd
  • sb rs1,offset(rs2):把寄存器rs1的值存入地址为寄存器rs2的值加offset的主存中,保留最右端的8位
  • sh rs1,offset(rs2):把寄存器rs1的值存入地址为寄存器rs2的值加offset的主存中,保留最右端的16位
  • sw rs1,offset(rs2):把寄存器rs1的值存入地址为寄存器rs2的值加offset的主存中,保留最右端的32位

编程示例:

my_test:
	li t0, 0x20000000
	li t4, 0x12345678
	sw t4, 0x0(t0)
	lb t1, 0x0(t0)
	lh t2, 0x0(t0)
	lw t3, 0(t0)
	sb t4, 4(t0)
	sh t4, 8(t0)
	sw t4, 12(t0)
	j .

读取立即数 0x20000000 地址 (SRAM 地址 )到 t0,把 0x12345678 赋值给 t4 ,把 t4 的值保存到以 t0 值位地址的位置上,依次调用 lb、lh、lw 来读取,然后在调用 sb sh sw 将数据存放到 SRAM 上。

li 是立即数操作伪指令,因为立即数的操作是一些指令的合成,因为立即数操作使用频次较高,所以编译器将其缩减为 li,关于伪指令我在末尾会提到。

实验结果如下:

寄存器值:

Name : t0
	Hex:0x20000000
	Decimal:536870912
	Octal:04000000000
	Binary:100000000000000000000000000000
	Default:536870912

Name : t1
	Hex:0x78
	Decimal:120
	Octal:0170
	Binary:1111000
	Default:120

Name : t2
	Hex:0x5678
	Decimal:22136
	Octal:053170
	Binary:101011001111000
	Default:22136

Name : t3
	Hex:0x12345678
	Decimal:305419896
	Octal:02215053170
	Binary:10010001101000101011001111000
	Default:305419896

SRAM 存放值:

因为 CH32 是大端存储,高字节在低地址,所以每个 byte 存储的数据位置相反,关于大端小端可以参考我之前的文章:内存大小端

四、算数运算指令

首先是寄存运算功能:加减乘除

  • add rd,rs1,rs2:将寄存器rs1与rs2的值相加并写入寄存器rd

编写代码测试指令:

	li t1, 12
	addi t2, t1, 4

给 t1 寄存器赋值 12,加上立即数 4 到 t2,编译运行查看结果

  • addi rd,rs1,imm:将寄存器rs1的值与立即数imm相加并存入寄存器rd

在将 t2 寄存器的值和 t1 寄存器相加,结果保存到 t2:

	add t2, t2, t1

运行结果:

  • sub rd,rs1,rs2:将寄存器rs1与rs2的值相减并写入寄存器rd
	li t3, 4
	sub t2, t2, t3

然后给 t3 赋值 4,使用 t2 寄存器的值减去 t3,结果保存到 t2,运行结果:

  • mul rd,rs1,rs2:将寄存器rs1与rs2的值相乘并写入寄存器rd
	mul t1, t1, t3

将 t1 和 t3 的值相乘存储到 t1,运行现象:

  • div rd,rs1,rs2:将寄存器rs1除以寄存器rs2的值,向零舍入并写入寄存器rd
	div t4, t1, t3

再将 t1 除以 t3,结果保存到 t4,运行现象:

  • rem rd, rs1, rs2:寄存器 rs1 除以寄存器 rs2 的值,向 0 舍入,都视为 2 的补码,余数写入 rd 寄存器
	rem t4, t1, t3

将 t1 除以 t3 取余数,运行现象:

  • neg rd, rs2:把寄存器 rs2 的值的二进制补码写入 rd 寄存器
	li t1, 0xF1
	neg t2, t1

将 t1 的补码保存到 t2,运行结果:

五、移位指令

  • sll rd,rs1,rs2:将寄存器rs1的值左移寄存器rs2的值这么多位,并写入寄存器rd

    li t2, 0x000F0000
    li t3, 4
    sll t1, t2, t3

将 0x000F0000 逻辑左移四位:

  • slli rd,rs1,imm:将寄存器rs1的值左移立即数imm的值这么多位,并写入寄存器rd
slli t1, t2, 8

将 0x000F0000 逻辑左移8位:

  • srl rd,rs1,rs2:将寄存器rs1的值逻辑右移寄存器rs2的值这么多位,并写入寄存器rd
srl t1, t2, t3

将 0x000F0000 逻辑右移4位:

  • srli rd,rs1,imm:将寄存器rs1的值逻辑右移立即数imm的值这么多位,并写入寄存器rd
srli t1, t2, 8

将 0x000F0000 逻辑右移8位:

  • sra rd,rs1,rs2:将寄存器rs1的值算数右移寄存器rs2的值这么多位,并写入寄存器rd

算数偏移,往左相对于乘以 2,往右相对于除以 2,该方式下会将符号位带入计算

li t2, -128
sra t1, t2, t3

逻辑右移 4 位,相对于除以 16,符号位不变,得到 -8:

  • srai rd,rs1,imm:将寄存器rs1的值算数右移立即数imm的值这么多位,并写入寄存器rd
srai t1, t2, 8

逻辑右移 8 位,相对于除以 128,符号位不变,得到 -1:

六、逻辑操作指令

  • and rd,rs1,rs2:将寄存器rs1与rs2的值按位与并写入寄存器rd
	li t1, 0b0A # 0000 1010
	li t2, 0x06 # 0000 0110
	and t3, t1, t2

执行结果:

  • andi rd,rs1,imm:将寄存器rs1的值与立即数imm的值按位与并写入寄存器rd
	andi t3, t1, 0x0F

执行结果:

  • or rd,rs1,rs2:将寄存器rs1与rs2的值按位或并写入寄存器rd
	or t3, t1, t2

执行结果:

  • ori rd,rs1,imm:将寄存器rs1的值与立即数imm的值按位或并写入寄存器rd
	ori t3, t1, 0x0F

执行结果:

  • xor rd,rs1,rs2:将寄存器rs1与rs2的值按位异或(相异为1,相同为0)并写入寄存器rd
	xor t3, t1, t2

执行结果:

  • xori rd,rs1,imm:将寄存器rs1的值与立即数imm的值按位异或并写入寄存器rd
	xori t3, t1, 0x0F

执行结果:

七、跳转指令

7.1 条件跳转

条件跳转是满足设置条件的情况下进行跳转:

  • beq rs1,rs2,lable:若rs1的值等于rs2的值,程序跳转到lable处继续执行

  • bne rs1,rs2,lable:若rs1的值不等于rs2的值,程序跳转到lable处继续执行

  • blt rs1,rs2,lable:若rs1的值小于rs2的值,程序跳转到lable处继续执行

  • bge rs1,rs2,lable:若rs1的值大于等于rs2的值,程序跳转到lable处继续执行

  • bltu rs1,rs2,lable:blt 无符号版

  • bgeu rs1,rs2,lable:bge 无符号版

写一段 c 语言方便理解:

if (t1 == t2) 
    fun1();
 else if (t1 < t2) 
    fun2();
 else if (t1 >= t2) 
    fun3();

对应的汇编:

	beq t1, t2, fun1
	blt t1, t2, fun2
	bge t1, t2, fun3

另一个版本

if (t1 != t2) 
    fun1();

对应的:

	bne t1, t2, fun1

7.2 无条件跳转

无条件跳转没有设置条件,可直接进行跳转

  • j label:程序直接跳转到lable处继续执行
  • jal rd,label:把下一条指令的地址保存在rd中(通常用x1),然后跳转到label处继续执行
  • jalr rd,offset(rs):把下一条指令的地址存到rd中,然后跳转到rs+offset地址处的指令继续执行。若 rd 为全 0 寄存器,则相当于 j

jal 和 jalr 常用于函数跳转和返回

八、比较判断

  • slt rd,rs1,rs2:若rs1的值小于rs2,rd置为1,否则置为0
  • slti rd,rs1,imm:若rs1的值小于立即数imm,rd置为1,否则置为0
  • sltu rd,rs1,rs2:若rs1的值小于rs1的值,rd置为1,否则置为0
  • sltiu rd,rs1,imm:若rs1的值小于立即数imm,rd置为1,否则置为0

九、CSR 操作指令

RISC - V 中有 8 个重要的 CSR 寄存器,寄存器如下:

  1. mtvec(Machine Trap Vector)它保存发生异常时处理器需要跳转到的地址
  2. mepc(Machine Exception PC)它指向发生异常的指令
  3. mcause(Machine Exception Cause)它指示发生异常的种类
  4. mie(Machine Interrupt Enable)它指出处理器目前能处理和必须忽略的中断
  5. mip(Machine Interrupt Pending)它列出目前正准备处理的中断
  6. mtval(Machine Trap Value)它保存了陷入(trap)的附加信息:地址例外中出错
    的地址、发生非法指令例外的指令本身,对于其他异常,它的值为 0
  7. mscratch(Machine Scratch)它暂时存放一个字大小的数据
  8. mstatus(Machine Status)它保存全局中断使能,以及许多其他的状态,如图所示

这些寄存器控制着中断的使能,同时可以用于异常的捕获,当异常发生时:异常指令的 PC 被保存在 mepc 中,PC 被设置为 mtvec(对于同步异常,mepc 指向导致异常的指令;对于中断,它指向中断处理后应该恢复执行的位置。)根据异常来源设置 mcause,并将 mtval 设置为出错的地址或者其它适用于特定异常的信息字,在机器模式(M 模式,对硬件有 %100 的控制权限)下,异常信息字设置如下:

异常类型分为 5 种:

  • 访问错误异常 当物理内存的地址不支持访问类型时发生(例如尝试写入 ROM)
  • 断点异常 在执行 ebreak 指令,或者地址或数据与调试触发器匹配时发生
  • 环境调用异常 在执行 ecall 指令时发生
  • 非法指令异常 在译码阶段发现无效操作码时发生
  • 非对齐地址异常 在有效地址不能被访问大小整除时发生

异常发生时异常指令的 PC 被保存在 mepc 中,PC 被设置为 mtvec(对于同步异常,mepc
指向导致异常的指令;对于中断,它指向中断处理后应该恢复执行的位置。)根据异常来源设置 mcause,并将 mtval 设置为出错的地址或者其它适用于特定异常的信息字。把控制状态寄存器 mstatus 中的 MIE 位置零以禁用中断,并把先前的 MIE 值保留到 MPIE 中。发生异常之前的权限模式保留在 mstatus 的 MPP 域中,再把权限模式更改为 M。

相关的寄存器操作使用如下指令完成,csr 就是上面相关寄存器:

  • csrrw rd, csr, rs:是读后写控制状态寄存器,先将 csr 的值记为 t,把 rs 寄存器的值写入 csr,再将 t 写入 rd 中;
  • csrrwi rd, csr, zimm:是立即数读后写控制状态寄存器,将 csr 的值写入 rd 中,再将立即数写入 csr 中;
  • csrrs rd, csr, rs1:是读后 置位 控制状态寄存器,先将 csr 的值记为 t,让 t 和 rs1 取或并写入 csr,再将 t 写入 rd 中;
  • csrrsi rd, csr, zimm:是立即数读后 置位 控制状态寄存器,先将 csr 的值记为 t,把 t 和立即数 zimm 取或并写入 csr,再将 t 写入 rd 中;
  • csrrc rd, csr, rs1:是读后 清除位 控制状态寄存器,先将 csr 的值记 为 t,把 t 和 rs1 位与并写入 csr,再将 t 写入 rd 中;
  • csrrci rd, csr, zimm:是立即数读后 清除位 控制状态寄存器,csr 的值记为 t,把 t 和立即数 zimm 位与并写入 csr,再将 t 写入 rd 中。

沁恒ch32学习——risc-v架构学习笔记

作为新手第一次接触RISC-V架构时。我首先百度简单了解了ARM架构和RISC-V架构的区别,以我个人的理解是ARM架构代表之前的复杂指令集,而RISC-V架构代表着精简指令集。就比如说同样是一个舞蹈动作,复杂指令集可能会... 查看详情

risc-v学习笔记(代码片段)

RISC-V学习笔记(1)作者:夏风喃喃参考:计算机组成与设计:硬件/软件接口(RISC-V版)文章目录RISC-V学习笔记(1)第1章计算机抽象及相关技术1.6性能1.6.1性能的定义1.6.2性能的度量1.6.3CPU性能... 查看详情

risc-v学习笔记(代码片段)

RISC-V学习笔记(2)作者:夏风喃喃参考:计算机组成与设计:硬件/软件接口(RISC-V版)文章目录RISC-V学习笔记(2)第2章计算机的语言2.1引言2.2计算机硬件的操作2.3计算机硬件的操作数(加... 查看详情

沁恒ch32v307单片机入门(01):基础说明与流程体验(代码片段)

...备使用南京沁恒的CH32V307为基础进行介绍,这是一款RISC-V架构内核的单片机。我看中它的主要是自带高速USBPHY和ETHPHY,同时价格也很便宜,某宝上散买十块出头。图片是R的,V的可用引脚要多些。这个芯片官方是有... 查看详情

沁恒ch32v208:ch32v208的储存结构,启动模式和时钟(代码片段)

这一篇简单说明CH32V208的片内存储结构和时钟的特点,以及通过SDK中的示例代码分析CH32V208的时钟设置目录沁恒CH32V208(一):CH32V208WBU6评估板上手报告和Win10环境配置沁恒CH32V208(二):CH32V208的储存结构,启动模式和时钟CH32V存储容量命名... 查看详情

沁恒ch32v307使用记录:gpio与exti(代码片段)

...说明。本文使用沁恒官方的开发板(CH32V307-EVT-R1沁恒RISC-V模块MCU赤兔评估板)进行演示。本文演示中需要用到开发板上的KEY和LED,默认只是引入接口到排针,并没有和芯片GPIO口相连,下文使用中需要手动用杜... 查看详情

汇编入门学习笔记——int指令port

...的暑假学习之 汇编入门学习笔记(十二)—— int指令、port參考:《汇编语言》王爽第13、14章一、int指令1.int指令引发的中断intn指令,相当于引发一个n号中断。运行过程相当于:(1)取中断类型吗n。(2)标志寄存器入... 查看详情

risc-v指令详解

...众号,不错过精彩内容作者| Vinson转载| 漫谈嵌入式RISC-V是当下热门的技术,值得大家学习,这里分享一份关于RISC-V指令的内容给大家。1.指令集1.1指令集指令集是一个CPU的基石,要实现CPU计算和控制功能,就... 查看详情

从risc到risc-v

参考技术A身处电子行业或电子类的学生对RISC-V不太陌生,这个词在2018年可谓是霸占了大量的篇幅啊。今天就让我们一起从它的字面意思入手看看这个RISC-V是个“神马玩意”首先我们从名字的第一个单词入手,RISC的较官方解释为... 查看详情

risc-v基本介绍

...寄存器)四、学习资料1.官方资料2.开发参考总结前言RISC-V作为目前大火的开源指令集架构有着自己的独特魅力,本文将对其基本情况进行介绍,给大家留一个初步印象。关于指令集的概念大家可以参看:什么是指... 查看详情

第十七届ch32v307多车组头尾双车摄像头传统扫线循迹

...理到实现循迹的大概过程。2.本开源博客的代码处理皆是基于逐飞科技提供的底层开源函数库二.摄像头如何采集到图像1.采集原始值/******图像处理函数* 查看详情

沁恒ch32v307单片机入门(01):基础说明与流程体验(代码片段)

...的单片机。基础说明芯片介绍这里准备使用南京沁恒的CH32V307为基 查看详情

读入优化~~~(个人学习笔记)

基本模板:inlineintread(){ intx=0,w=1;charch=0; while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)w=-1;ch=getchar();} while(ch>=‘0‘&&ch<=‘9‘)x=(x<<3)+(x<<1)+ch-‘0‘,ch=getchar(); returnx*w;}& 查看详情

沁恒ch32v307单片机入门(02):官方库与工程模板介绍(代码片段)

...大多数时候都是面向库开发的,这里将简单介绍下CH32V307的官方库。在开发过程中新建项目时通常会从某些模板开始,模板包含了库和初始化代码等内容,有一定的组织好的目录结构,使用模板可以加速开发过程... 查看详情

risc-v基本介绍

...寄存器)四、学习资料1.官方资料2.开发参考总结前言RISC-V作为目前大火的开源指令集架构有着自己的独特魅力,本文将对其基本情况进行介绍,给大家留一个初步印象。关于指令集的概念大家可以参看:什么是指... 查看详情

趋势观察:第五代精简指令集risc

参考技术A中国网/中国发展门户网讯RISC-V,即第五代精简指令集,是一种基于精简指令集计算机(RISC)原理的开源指令集架构(ISA),由美国加州大学伯克利分校研究团队于2010年设计。相对于X86指令集的完全封闭及ARM指令集高... 查看详情

spark机器学习读书笔记-ch05

5.2.从数据中提取合适的特征[[email protected]ch05]#sed1dtrain.tsv>train_noheader.tsv[[email protected]ch05]#lltotal42920-rw-r--r--1rootroot21972457Jan3115:03train_noheader.tsv-rw-r--r--1rootroot21972 查看详情

沁恒ch32v307单片机入门(02):官方库与工程模板介绍(代码片段)

...大多数时候都是面向库开发的,这里将简单介绍下CH32V307的官方库。在开发过程中新建项目时通常会从某些模板开始,模板包含了库和初始化代码等内容,有一定的组织好的目录结构,使用模板可以加速开发过程... 查看详情