RISCV汇编讲解

2024-08-25 14:12
文章标签 讲解 汇编 riscv

本文主要是介绍RISCV汇编讲解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

第一章 引言

为什么要讲riscv?

         riscv的特点:

        -诞生于顶尖学术机构:诞生于加州大学伯克利分校的体系结构研究院。吸引了大批的顶尖企业参与(e.g. 谷歌、华为、高通、阿里巴巴为rsicv的发展提供了大量的资金支持和贡献了技术和人才)

        -精简指令集:指令相对精简,指令格式规整,易于实现和理解。只包含了最基本和常用的指令,避免了复杂和特殊用途的指令,使得处理器的硬件设计更加简洁高效。比如说长度固定(32位/64位)。

        -模块化设计:可以根据不同的应用需求选择不同的模块进行组合。这使得riscv可以适应从嵌入式系统到高性能服务器等不同的应用场景,具有很高的灵活性。

        -开源:例如有多个开源的riscv编译器(如gcc和llvm)可供选择。

        -低功耗:指令集设计间接,处理器的硬件实现相对简单,有助于降低功耗。

命名规范

        RV[###][abc......xyz]

        [###]表示的是寄存器的位宽,后面的字母标识处理器支持的指令集模块集合。

        例如:RV32MA、RV64GC。

模块化        

        模块化ISA:由1个基本整数指令集+多个可选的指令集组成。基础指令是固定的,永远不会改变。设计cpu的可以像插件一样去堆模块。

        RISC-V ISA = 1个基本整数指令集 + 多个可选的扩展指令集

        基本整数指令集:唯一强制要求实现的基础指令集,其他指令集都是可选的扩展模块。

        扩展模块指令集:RISC-V允许在视线中以可选的形式实现其他标准化和非标准化的指令集扩展。特定组合“IMAFD”被称为“通用”组合,用英文字母G表示。

扩展指令集描述
M整式乘法与除法指令集
A存储器原子指令集
F单精度浮点指令集
D双精度浮点指令集
C压缩指令集
......其它标准化和非标准化指令集
HART

        hart = hardware thread(个人理解为超线程)

        一个hart就是一个虚拟的cpu。一个hart可以不受外界干扰的自主地去获取和执行risc-v指令。

特权级别
levelencodingname
000user/applicationU
101supervisorS
210reserved
311machineM

        当运行在用户态时,就是说cpu运行在user级别,进入到内核的时候就是supervisor级别。固件就是就是machine级别。运行在machine模式是不开虚拟地址的,全部运行在物理地址。

        risc-v芯片一上电首先是进入到machine模式,再进入到supervisor模式,此时也叫保护模式,虚拟地址打开。

        进程的实现依赖于虚拟地址,虚拟地址的实现需要MMU硬件支持。

Control and Status Register(CSR)

        不同的特权级别下时分布对应各自的一套CSR,用于控制和获取响应level下的处理器工作状态。

        高级别的特权可以访问低级别的CSR。比如说machine级别可以访问user级别的csr。

        rsicv定义了专门用于操作csr的指令。

        如果是用户程序,不太需要跟csr打交道。

异常和中断

        异常:主动触发,cpu给你一次改过自新的机会,去执行一段挽救程序;当执行到非法指令的时候,cpu会停掉此指令流跳到一个特殊的地址去执行一段特殊的程序(自己写的程序,想对异常做的处理),执行完之后回到之前的指令再次运行;比如说除0异常;

        中断:被动触发;cpu停掉当前程序,跳转到执行中断处理程序,执行完返回到下一条指令去执行;比如说外设通知你发生一件什么事情,跑去执行别的指令,执行完再回到下条指令,就像中断没有发生过一样。

 ELF介绍

        ELF(Executable Format)是一种unix-like系统上的二进制文件格式标准。

        ELF标准中定义的采用ELF格式的文件分为4类:

ELF文件类型说明实例
可重定位文件(relocatable file)内容包含了代码和数据,可以被链接成可执行文件或共享目标文件linux上的.o文件
可执行文件可以直接执行的文件linux上的a.out文件
共享目标文件内容包含了代码和数据,可以作为链接器的输入,在链接阶段和其他的relacatable file或者shared object file一起链接成新的object file;或者在运行阶段,作为动态链接器的输入,和可执行文件结合,作为进程的一部分来运行。

linux上的.so文件

核心转储文件(core dump file)进行意外终止时,系统可以将该进程的部分内容和终止时的其他状态信息保存到该文件中以供调试分析。linux上的core文件

        ELF文件格式:

ELF Header
Program Header Table运行视图
.text程序指令
.init做初始化的一些指令
.data数据:全局变量等
.bss
......

Section Header Table

从链接角度去描述了这个文件的内容

        .text和.init等这些信息放置的时候都会section(节)对齐,但是在运行的时候会合在一起以节省空间,于是有了segment(段)概念,segment的信息存放于program header table中。segment fault就是因为出错的时候在内存中失败。

        ELF文件处理相关工具:

       Binutils官网地址:https://www.gnu.org/software/binutils/

        ar:归档文件,将多个文件打包成一个大文件。

        as:被gcc调用,输入汇编文件,输出目标文件工链接器ld连接。

        ld:gnu链接器,被gcc调用,它把目标文件和各种库文件结合在一起,重定位数据,并链接符号引用。

        objcopy:执行文件格式转换。

        objdump:显示ELF文件的信息。

        readelf:显示更多ELF格式文件的信息(包括DWARF调试信息)

        示例1:

gcc -c hello.c
readelf -h hello.o  // 查看头信息readelf -S hello.o  // 查看section信息
readelf -SW hello.o

        实例2:

gcc -g -c hello.c
objdump -S hello.o // 显示汇编指令
 嵌入式开发

        嵌入式开发是一种比较综合性的技术,它不单指纯粹的软件开发技术,也不单是一种硬件配置技术;它是在特定的硬件环境下针对某款硬件进行开发,是一种系统级别的与硬件结合比较紧密的软件开发技术。程序并不是运行在本地,而是运行在特殊的硬件上。

        参与编译和运行的机器根据其角色可以分成以下三类:

        -build系统:生成编译器可执行程序的计算机。编译器在build系统上编译出来的。 

        -host系统:运行编译器可执行程序,编译链接应用程序的计算机系统。

        -target系统:运行应用程序的计算机系统。

        根据build/host/target的不同组合我们可以得到如下的编译方式分类:

        -本地(native)编译:build==host==target

        -交叉(cross)编译:build==host!=target

QEMU

        QEMU是一套由(Fabrice Bellard)编写的以GPL许可证分发源码的计算机系统模拟软件,在GNU/Linux平台上使用广泛。 

        QEMU,支持多种体系架构。譬如:IA-32(x86),AMD 64, MIPS 32/64,RISC-V 32/64等等。

        QEMU有两种主要运作模式:

        -user mode: 直接运行应用程序 (比如说hello.o运行直接输出“hello”)

        -system mode:模拟整个计算机系统,包括中央处理器及其他周边设备。

第二章 汇编语言编程

 基本组成

        汇编文件一般后缀为.S或.s,.S包含了预处理的语句,.s就是纯粹的汇编语句。

        一个完整的RISC-V汇编程序有多条语句(statement)组成。一条典型的RISC-V汇编语言由3部分组成:

[label:] [operation] [comment]

        打方括号表示可选。

        -label表示一个标号,必须以":"结尾。label相当于一个地址,给这个地址起了个名字。是这条指令存放在内存的地址。

        -operation可以由以下多种类型:

                -instruction(指令):直接对应二进制机器指令的字符串

                -preudo-instruction(伪指令):为了提高编写代码的效率,可以用一条伪指令指示汇编器产生多条实际的指令(instruction)。(要在汇编器的手册里查看定义)

                -directive(指示/伪操作):通过类似指令的形式(以“.”开头),通知汇编器如何控制代码的产生等,不对应具体的指令。属于汇编器自己定义的一些语法。(要在汇编器的手册里查看定义)

                -macro:采用.macro/.endm自定义的宏

        例子:


.macro do_nothing      #directivenop            #preudo-instructionnop            #preudo-instruction
.endm.text              #directive  告诉大家生成的指令要放到text的section中.global _start     #directive  _start是个全局变量,外部可见,有点像extern_start:                 #labelli x6, 5            #preudo-instructionli x7, 4            #preudo-instructionadd x5, x6, x7      #instructiondo_nothing          #calling macro.end                #End of file
RISC-V汇编指令操作对象

        寄存器:

        -32个通用寄存器,x0~x31;

        -在RISC-V中,Hart在执行算术逻辑运算时所操作的数据必须直接来自寄存器

        内存:

        -Hart可以执行在寄存器和内存之间的数据读写操作;

        -读写操作使用字节(Byte)为基本单位进行寻址;

        -RV32可以访问最多2^32个字节的内存空间。

        XLEN指的是寄存器的长度32/64。

        x0寄存器是zero寄存器,里面读出来永远是0,只读不写。

        pc寄存器是外界不可见的。 

 RISC-V汇编指令类型

        参考riscv-spec-20191213.pdf文件中的第24章

        -指令长度;ILEN1=32 bits(RV32I)

        -指令对齐:IALIGN=32bits(RV32I),指的是在内存中对齐。地址对齐32byte。

        -32个bit划分成不同的“域(field)”

         -funct3/funct7和opcode一起决定最终的指令类型。

         -指令在内存中按照小端序排列。

31-2524-2019-1514-1211-76-0
funct7rs2rs1funct3rdopcodeR-type
imm[]imm[]rs1funct3rdopcodeI-type
imm[]rs2rs1funct3imm[4:0]opcodeS-type
imm[]rs2rs1funct3imm[4:1[11]]opcodeB-type
imm[]imm[]imm[]imm[]rdopcodeU-type
imm[]imm[]imm[]imm[]rdopcodeJ-type

        R-type:(register),每条指令中有三个fields,用于指定3个寄存器参数。

         I-type:(Immediate),每条指令除了带有两个寄存器参数外,还带有一个立即数参数(宽度为12bits)。

        S-type: (Store),每条指令除了带有两个寄存器参数外,还带有一个立即数参数(宽度为12bits,但fields的组织方式不同于I-type)。(用来访问内存的指令)

        B-type: (Branch),每条指令除了带有两个寄存器参数外,还带有一个立即数参数(宽度为12bits,但取值为2的倍数)。(跟分支跳转有关)

        U-type:(Upper),每条指令含有一个寄存器参数再加上一个立即数参数(宽度为20bits,用于表示一个立即数的高20位)。

        J-type:(Jump),每条指令含有一个寄存器参数再加上一个立即数参数(宽度为20bits)。

小端序的概念

        -主机字节序(HBO - Host Byte Order),默认小端序。

        -一个多字节整数在计算机内存中存储的字节顺序称为主机字节序(HBO-Host Byte Order,或者叫本地字节序);

        -不同类型CPU的HBO不同,这与CPU的设计有关。分为大端序和小端序。

         riscv是小端序编指令。

算数指令

基于算术运算指令实现的其他伪指令
伪指令语法等价指令指令描述例子
NEGNEG RD, RSSUB RD, X0, RS对RS中的值取反并将结果存放在RD中neg x5, x6
MVMV RD, RSADDI RD, RS, 0将RS中的值拷贝到RD中mv x5, x6
NOPNOPADDI x0, x0, 0什么也不做nop
LUI(Load Upper Immediate)
语法LUI RD, IMM
例子lui x5, 0x12345x5 = 0x12345 << 12

         LUI指令会构造一个32bits的立即数,这个立即数的高20位对应指令中的imm,低12位清零。这个立即数作为结果存放在RD中。

        例子

        -利用LUI+ADDI来为寄存器加载一个大数0x12345678

        lui        x1, 0x12345        # x1 = 0x12345000

        addi    x1, x1, 0x678       # x1 = 0x12345678

        -利用LUI+ADDI来为寄存器加载一个大数0x12345FFF

        由于addi里的立即数会被符号扩展,所以不能直接加上FFF。

        lui        x1, 0x12346      # x1 = 0x12346000

        addi    x1, x1, -1            # x1 = 0x12345FFF

LI(Load Immediate)

AUIPC
语法AUIPC RD, IMM
例子auipc x5, 0x12345x5 = 0x12345 << 12 + PC

         auipc指令采用U-type

        和LUI指令类似,AUIPC指令也会构造一个32bits的立即数,这个立即数的高20位对应指令中的imm,低12位清零。但和LUI不同的是,AUIPC会先将这个立即数和PC值相加,将相加的结果放在RD中。

        应用场景:动态库地址的加载。

LA(Load Address)
语法LA RD, LABEL
例子la x5, foo

        LA是一个伪指令

        具体编程时给出需要加载的label,编译器会根据实际情况利用auipc和其他指令自动生成正确的指令序列。

        常用语加载一个函数或者变量的地址。

        例子

_start:la x5, _start    # x5 = _startjr x5

        反汇编出来很可能就是一条auipc指令。

逻辑运算指令

移位运算指令​​​​​​​​​​​​​​

内存读写指令

        

条件分支指令

        x1寄存器用来保存返回地址。

指令寻址模式总结

 函数调用过程概述  

      ​​​​​​​

        当caller和callee不是一个人写的,这个时候就需要制定一套规定

        当然也可以在调用函数的时候把所有寄存器都存到栈内,但这样效率太低了,于是需要分批。

​​​​​​​   

        栈帧的大小是编译阶段就确定了的?函数起始部分和函数退出部分都是编译器帮我们实现的。

        例子(尾调用):

例子(非尾调用):

        函数调用的汇编代码由三部分组成:开场代码,主体代码,退场代码。 大概得逻辑就是父函数调用子函数,参数传递用a0~ax寄存器,返回值默认用a0寄存器。通过a字号寄存器做交互。函数的中间值保存在s字号寄存器。所以编译器每次都要评估此函数会用到多少个s号寄存器,把需要用的s号寄存器先sw起来,如果不是尾调用还要sw ra寄存器。子函数调用完,结果写到a0,复原s号寄存器,照着返回地址返回到父函数。记住,函数与函数之间是通过寄存器交互的。 

        stack_start和stack_end之间是对栈的定义

RISC-V编程与C混合编程 

         遵循ABI(Abstract Binary Interface)的规定

        -数据类型的大小,布局和对齐

        -函数调用约定(calling convention)

        -系统调用约定

        RISC-V函数调用约定规定:

        -函数参数采用寄存器a0~a7传递

        -函数返回值采用a0和a1传递

        例子 (汇编调用C)

        例子(C调用汇编),方括号内的内容是可选的,加个volatile意思是让编译器不要优化。 

        简化版本,用顺序来表示映射关系

这篇关于RISCV汇编讲解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1105779

相关文章

计算机毕业设计 大学志愿填报系统 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试

🍊作者:计算机编程-吉哥 🍊简介:专业从事JavaWeb程序开发,微信小程序开发,定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事,生活就是快乐的。 🍊心愿:点赞 👍 收藏 ⭐评论 📝 🍅 文末获取源码联系 👇🏻 精彩专栏推荐订阅 👇🏻 不然下次找不到哟~Java毕业设计项目~热门选题推荐《1000套》 目录 1.技术选型 2.开发工具 3.功能

笔记整理—内核!启动!—kernel部分(2)从汇编阶段到start_kernel

kernel起始与ENTRY(stext),和uboot一样,都是从汇编阶段开始的,因为对于kernel而言,还没进行栈的维护,所以无法使用c语言。_HEAD定义了后面代码属于段名为.head .text的段。         内核起始部分代码被解压代码调用,前面关于uboot的文章中有提到过(eg:zImage)。uboot启动是无条件的,只要代码的位置对,上电就工作,kern

ispunct函数讲解 <ctype.h>头文件函数

目录 1.头文件函数 2.ispunct函数使用  小心!VS2022不可直接接触,否则..!没有这个必要,方源一把抓住VS2022,顷刻 炼化! 1.头文件函数 以上函数都需要包括头文件<ctype.h> ,其中包括 ispunct 函数 #include<ctype.h> 2.ispunct函数使用 简述: ispunct函数一种判断字符是否为标点符号的函

深度学习速通系列:深度学习算法讲解

深度学习算法是一系列基于人工神经网络的算法,它们通过模拟人脑处理信息的方式来学习和解决复杂问题。这些算法在图像识别、语音识别、自然语言处理、游戏等领域取得了显著的成就。以下是一些流行的深度学习算法及其基本原理: 1. 前馈神经网络(Feedforward Neural Networks, FNN) 原理:FNN 是最基本的神经网络结构,它由输入层、隐藏层和输出层组成。信息从输入层流向隐藏层,最

C#设计模式(1)——单例模式(讲解非常清楚)

一、引言 最近在学设计模式的一些内容,主要的参考书籍是《Head First 设计模式》,同时在学习过程中也查看了很多博客园中关于设计模式的一些文章的,在这里记录下我的一些学习笔记,一是为了帮助我更深入地理解设计模式,二同时可以给一些初学设计模式的朋友一些参考。首先我介绍的是设计模式中比较简单的一个模式——单例模式(因为这里只牵涉到一个类) 二、单例模式的介绍 说到单例模式,大家第一

[项目][CMP][直接向堆申请页为单位的大块内存]详细讲解

目录 1.系统调用 1.系统调用 Windows和Linux下如何直接向堆申请页为单位的大块内存: VirtualAllocbrk和mmap // 直接去堆上按页申请空间static inline void *SystemAlloc(size_t kpage){#ifdef _WIN32void *ptr = VirtualAlloc(0, kpage << 13,

汇编:嵌入式软件架构学习资源

成为嵌入式软件架构设计师需要掌握多方面的知识,包括嵌入式系统、实时操作系统、硬件接口、软件设计模式等。 以下是一些推荐的博客和网站,可以帮助你深入学习嵌入式软件架构设计: ### 1. **Embedded.com**    - **网址**: [Embedded.com](https://www.embedded.com/)    - **简介**: 这是一个专注于嵌入式系统设计的专业网

高斯平面直角坐标讲解,以及地理坐标转换高斯平面直角坐标

高斯平面直角坐标系(Gauss-Krüger 坐标系)是基于 高斯-克吕格投影 的一种常见的平面坐标系统,主要用于地理信息系统 (GIS)、测绘和工程等领域。该坐标系将地球表面的经纬度(地理坐标)通过一种投影方式转换为平面直角坐标,以便在二维平面中进行距离、面积和角度的计算。 一 投影原理 高斯平面直角坐标系使用的是 高斯-克吕格投影(Gauss-Krüger Projection),这是 横

车险该怎么买?行业人讲解车险

很多车主对汽车保险知识不了解,稀里糊涂的买了车辆保险,但是出险时发现很多不赔的,还有很多对自己来说没什么用的保险,花了不少钱,还没买到自己想要的,殊不知只要多了解点汽车保险知识就能轻松省下一大笔钱并且买到自己真正想要的,何乐而不为呢! 因为卖保险的或者4S店,都是按照常规情况给你推荐保险,具体用车情况,只有你自己最清楚,所以保险是个个性化定制的产品,需要什么买什么,不需要的就没必要购买了。 一般

VB和51单片机串口通信讲解(只针对VB部分)

标记:该篇文章全部搬自如下网址:http://www.crystalradio.cn/thread-321839-1-1.html,谢谢啦            里面关于中文接收的部分,大家可以好好学习下,题主也在研究中................... Commport;设置或返回串口号。 SettingS:以字符串的形式设置或返回串口通信参数。 Portopen:设置或返回串口