[笔记二] 学习开发一个RISC-V上的操作系统

本文详细介绍了如何在RISC-V架构上学习开发操作系统,包括汇编语言的基础概念、指令操作对象、算术、逻辑、移位运算、内存访问、条件分支、无条件跳转、函数调用约定以及C与汇编的混合编程。
摘要由CSDN通过智能技术生成

在这里插入图片描述

[笔记二] 学习开发一个RISC-V上的操作系统

资料来源

B站视频链接
RISC-V 开放架构设计之道

RISC-V 汇编语言

  • 汇编语言(Assembly Language)是一种"低级"语言
  • 汇编语言的缺点:难读、难写、难移植
  • 汇编语言的优点:灵活、强大
  • 汇编语言的应用场景
    • 需要直接访问底层硬件的地方
    • 需要对性能执行极致优化的地方

汇编语言语法介绍(GNU版本)

  • 一个完整的 RISC-V 汇编程序有多条语句(statement)组成
  • 一条典型的 RISC-V 汇编语句由 3 部分组成:
    • [label:] [operation] [comment]
    • label(标号):GNU汇编中,任何以冒号结尾的标识符都被认为是一个标号
    • operation可以有以下多种类型:
      • instruction(指令):直接对应二进制机器指令的字符串
      • pseudo-instruction(伪指令):为了提高编写代码的效率,可以用一条伪指令指示汇编器产生多条实际的指令
      • directive(指示/伪操作):.通过类似指令的形式(以"."开头),通知汇编器如何控制代码的产生等,不对应具体的指令
      • macro:采用.macro/.endn自定义宏
    • comment(注释):常用形式,"#"开始到当前行结束

RISC-V 汇编指令操作对象

  • 寄存器
    • 32个通用寄存器,x0~x31(RV32I的通用寄存器组)
    • 在RISC-V中,Hart在执行算数逻辑运算时所操作的数据必须直接来自寄存器
  • 内存
    • Hart可以执行在寄存器和内存之间的数据读写操作
    • 读写操作使用字节(Byte)为基本单位进行寻址
    • RV32可以访问最多2^32个字节的内存空间
  • x0比较特殊,只读,返回永远是0,写没有任何意义

RISC-V 汇编指令总览

RISC-V 汇编指令编码格式
在这里插入图片描述

  • 指令长度:ILEN1 = 32 bits(RV32I)
  • 指令对齐:IALIGN = 32 bits(RV32I)
  • 32个bit划分成不同的"域(field)"
  • funct3/funct7和opcode一起决定最终的指令类型
  • 没必要去记每条指令,只要知道在哪查即可
  • 指令在内存中按照小端序排列
    • 主机字节序(HBO - Host Byte Order)
    • 一个多字节整数 在计算机内存中存储的字节顺序称主机字节序或本地字节序
    • 不同类型CPU的HBO不同,这与CPU的设计有关,分为大端序(Big-Endian)和小端序(Little-Endian)
    • 大端(Big-Endian):数据的高位字节存放在内存的低地址
    • 小端(Little-Endian):数据的低位字节存放在内存的低地址
  • 6种指令格式:
    • 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)

RISC-V 汇编指令详解

算术运算指令(Arithmetic Instructions)
  • add、sub、addi(ADD immediate)、LUI(Load Upper Immediate)
指令语法描述eg
ADDADD RD, RS1, RS2RS1和RS2的值相加,结果保存到RDadd x5, x6, x7
SUBSUB RD, RS1, RS2RS1的值减去RS2的值,结果保存到RDsub x5, x6, x7
ADDIADDI RD, RS1, IMMRS1的值和IMM相加,结果保存到RDaddi x5, x6, 100
LUILUI RD, IMM构造一个32位的数,高20位存放IMM,低12位清零。结果保存到RDlui x5, 0x12345
AUIPCAUIPC RD, IMM构造一个32位的数,高20位存放IMM,低12位清零。结果和PC 相加后保存到RDauipc x5, 0x12345
  • 有符号数在计算机中的表示:二进制补码(two’s complement),eg:-4(2b’1111100)
  • 符号拓展(sign extension) vs 零扩展(Zero extension)
  • 基于算术运算指令实现其他伪指令
伪指令语法等价指令指令描述eg
LILI RD, IMMLUI和ADDI的组合将立即数IMM加载到RD中li x5, 0x12345678
LALA RD, LABELAUIPC和ADDI的组合为RD加载一个地址值la x5 label
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+ADDI来为寄存器加载一个大数据0x12345FFF
    • lui x1,0x12346
    • addi x1,x1,-1
  • LI(Load Immediate):构造出的为绝对地址
  • AUIPC:构造出的为相对地址,相比LI + PC
  • LA(Load Address):函数或变量指针赋值
逻辑运算指令(Logical Instructions)
  • and、or、xor(eXclusive OR)、andi、ori、xori
指令格式语法描述eg
ANDR-typeAND RD,RS1,RS2RD=RS1&RS2and x5,x6,x7
ORR-typeOR RD,RS1,RS2RD=RS1|RS2or x5,x6,x7
XORR-typeXOR RD,RS1,RS2RD=RS1^RS2xor x5,x6,x7
ANDII-typeANDI RD,RS1,IMMRD=RS1&IMMandi x5,x6,20
ORII-typeORI RD,RS1,IMMRD=RS1|IMMor x5,x6,20
XORII-typeXORI RD,RS1,IMMRD=RS1^IMMxor x5,x6,20
伪指令语法等价指令描述eg
NOTNOT RD,RSXORI RD,RS,-1对RS的值按位取反,结果存放在RD中not x5,x6
移位运算指令(Shifting Instructions)

逻辑移位

指令格式语法描述eg
SLLR-typeSLL RD,RS1,RS2逻辑左移(Shift Left Logical)RD=RS1<<RS2sll x5,x6,x7
SRLR-typeSRL RD,RS1,RS2逻辑右移(Shift Right Logical)RD=RS1>>RS2逻辑左移立即数(Shift Left Logical Immediate)RD=RS1<<IMM
SLLII-typeSLLI RD,RS1,IMM逻辑左移立即数(Shift Left Logical Immediate)RD=RS1<<IMMslli x5,x6,3
SRLII-typeSRLI RD,RS1,IMM逻辑右移立即数(Shift Right Logical Immediate)RD=RS1>>IMMsrli x5,x6,3
  • 无论是逻辑左移还是右移,补足的都是0

算术移位

指令格式语法描述eg
SRAR-typeSRA RD,RS1,RS2算术右移(Shift Right Arithmetic)RD=RS1>>RS2sra x5,x6,x7
SRAII-typeSRAI RD,RS1,IMM算术右移立即数(Shift Right Arithmetic Immediate)RD=RS1>>IMMsrai x5,x6,3
  • 算术右移时按照符号位值补足,没有算术左移
内存读写指令(Load and Store Instructions)
  • 内存读指令:Load,将数据从内存读入寄存器
  • 内存写指令:Store,将数据从寄存器写出到内存
指令格式语法描述eg
LBI-typeLB RD,IMM(RS1)Load Byte,从内存中读取一个8bits的数据到RD中,内存地址=RS1+IMM,数据在保存到RD之前会执行sign-extended(符号扩展)lb x5,40(x6)
LBUI-typeLBU RD,IMM(RS1)Load Byte Unsigned,从内存中读取一个8bits的数据到RD中,内存地址=RS1+IMM,数据在保存到RD之前会执行zero-extended(零扩展)lbu x5,40(x6)
LHI-typeLH RD,IMM(RS1)Load Halfword,从内存中读取一个16bits的数据到RD中,内存地址=RS1+IMM,数据在保存到RD之前会执行sign-extendedlh x5,40(x6)
LHUI-typeLHU RD,IMM(RS1)Load Halfword Unsigned,从内存中读取一个16bits的数据到RD中,内存地址=RS1+IMM,数据在保存到RD之前会执行zero-extendedlhu x5,40(x6)
LWI-typeLW RD,IMM(RS1)Load Word,从内存中读取一个32bits的数据到RD中,内存地址=RS1+IMMlw x5,40(x6)
SBS-typeSB RS2,IMM(RS1)Store Byte,将RS2寄存器中低8bits的数据写出到内存中,内存地址=RS1+IMMsb x5,40(x6)
SHS-typeSH RS2,IMM(RS1Store Halfword,将RS2寄存器中低16bits的数据写到内存中,内存地址=RS1+IMMsh x5,40(x6)
SWS-typeSW RS2,IMM(RS1)Store word,将RS2寄存器中低32bits的数据写到内存中,内存地址=RS1+IMMsh x5,40(x6)
  • IMM给出的偏移量范围为[-2^11,2^11-1]
  • 为何对于load要区分无符号方式和有符号方式,而store不区分?
条件分支指令(Condition Branch Instructions)
指令格式语法描述eg
BEQB-typeBEQ RS1,RS2,IMMBranch if EQual。比较RS1和RS2的值,如果相等,则执行路径跳转到一个新的地址beq x5, x6, 100
BNEB-typeBNE RS1,RS2,IMMBranch if Not Equal。比较RS1和RS2的值,如果不相等,则执行路径跳转到一个新的地址bne x5, x6, 100
BLTB-typeBLT RS1,RS2,IMMBranch if Less Than。按照有符号方式比较RS1和RS2的值,如果RS1<RS2,则执行路径跳转到一个新的地址blt x5, x6, 100
BLTUB-typeBLTU RS1,RS2,IMMBranch if Less Than (Unsigned)。按照无符号方式比较RS1和RS2的值,如果RS1<RS2,则执行路径跳转到一个新的地址bltu x5, x6, 100
BGEB-typeBGE RS1,RS2,IMMBranch if Greater than or Equal。按照有符号方式比较RS1和RS2的值,如果RS1>=RS2,则执行路径跳转到一个新的地址bge x5, x6, 100
BGEUB-typeBGEU RS1,RS2,IMMBranch if Greater than or Equal (Unsigned)。按照无符号方式比较RS1和RS2的值,如果RS1>=RS2,则执行路径跳转到一个新的地址bgeu x5, x6, 100
  • 跳转的目标地址计算方法:先将 IMM x 2,符号扩展后和 PC 值相加得到最终的目标地址,所以跳转范围是以 PC 为基准,+/- 4KB 左右 ([-4096, 4094])
  • 具体编程时,不会直接写 IMM,而是用标号代替,交由链接器来最终决定 IMM 的值
伪指令语法等价指令描述
BLEBLE RS, RT, OFFSETBGE RT, RS, OFFSETBranch if Less & Equal,有符号方式比较,如果 RS <= RT,跳转到OFFSET
BLEUBLEU RS, RT, OFFSETBGEU RT, RS, OFFSETBranch if Less or Equal Unsigned,无符号方式比较,如果 RS <= RT,跳转到OFFSET
BGTBGT RS, RT, OFFSETBLT RT, RS, OFFSETBranch if Greater Than,有符号方式比较,如果 RS > RT,跳转到OFFSET
BGTUBGTU RS, RT, OFFSETBLTU RT, RS, OFFSETBranch if Greater Than Unsigned,无符号方式比较,如果 RS > RT,跳转到OFFSET
BEQZBEQZ RS, OFFSETBEQ RS, x0, OFFSETBranch if EQual Zero, 如果RS == 0,跳转到OFFSET
BNEZBNEZ RS, OFFSETBNE RS, x0, OFFSETBranch if Not Equal Zero, 如果RS != 0,跳转到OFFSET
BLTZBLTZ RS, OFFSETBLT RS, x0, OFFSETBranch if Less Than Zero, 如果RS < 0,跳转到OFFSET
BLEZBLEZ RS, OFFSETBGE x0, RS, OFFSETBranch if Less or Equal Zero, 如果RS <= 0,跳转到OFFSET
BGTZBGTZ RS, OFFSETBLT x0, RS, OFFSETBranch if Greater Than Zero,如果RS > 0,跳转到OFFSET
BGEZBGEZ RS, OFFSETBGE RS, x0, OFFSETBranch if Greater or Equal Zero, 如果RS >= 0,跳转到OFFSET
无条件跳转指令(Unconditional Jump Instructions)

JAL(Jump And Link)

语法例子
JAL RD, LABELjal x1, label
  • JAL 指令使用 J-type 编码格式
  • JAL 指令用于调用子过程 (subroutine/function)
  • 子过程的地址计算方法:首先对 20 bits 宽的 IMM x 2 后进行 sign-extended,然后将符号扩展后的值和 PC 的值相加。因此该函数跳转的范围是以 PC 为基准,上下~+/- 1MB
  • JAL 指令的下一条指令的地址写入 RD,保存为返回地址
  • 实际编程时,用 label 给出跳转的目标,具体 IMM 值由编译器和链接器最终负责生成

JALR(Jump And Link Register )

语法例子
JALR RD, IMM(RS1)jalr x0, 0(x5)
  • JALR 指令使用 I-type 编码格式

  • JALR 指令用于调用子过程 (subroutine/function)

  • 子过程的地址计算方法:首先对 12 bits 宽的 IMM 进行 sign-extended,然后将符号扩展后的值和 RS1 的值相加,得到最终的结果后将其最低位设置为 0(确保地址按 2 字节对齐)。因此该函数跳转的范围是以 RS1 为基准,上下~+/- 2KB

  • JALR 指令的下一条指令的地址写入 RD,保存为返回地址

  • 如何解决更远距离的跳转?

    • AUIPC x6,IMM-20
    • JALR x1,x6,IMM-12
  • 如果跳转后不需要返回,可以利用 x0 代替 JAL 和 JALR 中的 RD

伪指令语法等价指令例子
JJ OFFSETJAL X0, OFFSETj leap
JRJR RSJALR X0, 0(RS)jr x2
RISC-V指令寻址模式总结

寻址即指令中定位操作数(oprand)或者地址的方式

寻址模式解释例子
立即数寻址操作数是指令本身的一部分addi x5, x6,20
寄存器寻址操作数存放在寄存器中,指令中指定访问的寄存器从而获取该操作数add x5, x6, x7
基址寻址操作数在内存中,指令中通过指定寄存器(基址base)和立即数(偏移量offset),通过base + offset 的方式获得操作数在内存中的地址从而获取该操作数sw x5, 40(x6)
PC相对寻址在指令中通过PC 和指令中的立即数相加获得目标地址的值beq x5, x6, 100
RISC-V 函数调用的编程约定(Calling Conventions)

Caller 调用者 vs Callee 被调用者

  • 有关寄存器的编程约定
寄存器名ABI名(编程用名)用途约定谁负责在函数调用过程中维护这些寄存器
x0zero读取时总为0,写入时不起任何效果N/A
x1ra存放函数地址的返回值(return address)Caller
x2sp存放栈指针(stack pointer)Callee
x5~x7, x28~x31t0~t2, t3~t6临时(temporaries)寄存器,Callee 可能会使用这些寄存器,所以Callee 不保证这些寄存器中的值在函数调用过程中保持不变,这意味着对于Caller 来说,如果需要的话,Caller 需要自己在调用Callee 之前保存临时寄存器中的值Caller
x8, x9, x18~x27s0, s1, s2~s11保存(saved)寄存器,Callee 需要保证这些寄存器的值在函数返回后仍然维持函数调用之前的原值,所以一旦 Callee 在自己的函数中会用到这些寄存器则需要在栈中备份并在退出函数时进行恢复Callee
x10 , x11a0 , a1参数(argument)寄存器,用于在函数调用过程中保存第一个和第二个参数,以及在函数返回时传递返回值Caller
x12 ~ x17a2 ~ a7参数(argument)寄存器,如果函数调用时需要传递更多的参数,则可以用这些寄存器,但注意用于传递参数的寄存器最多只有 8 个(a0 ~ a7),如果还有更多的参数则要利用栈Caller
  • 函数跳转和返回指令的编程约定
伪指令等价指令描述例子
jal offsetjal x1, offset跳转到offset 制定位置,返回地址保存在x1 (ra)jal foo
jalr rsjalr x1, 0(rs)跳转到rs 中值所指定的位置,返回地址保存在x1 (ra)jalr s1
j offsetjal x0, offset跳转到offset 制定位置,不保存返回地址j loop
jr rsjalr x0, 0(rs)跳转到rs 中值所指定的位置,不保存返回地址jr s1
call offsetauipc x1, offset[31 : 12] + offset[11] jalr x1, offset[11:0](x1)长跳转调用函数call foo
tail offsetauipc x6, offset[31 : 12] + offset[11] jalr x0, offset[11:0](x6)长跳转尾调用tail foo
retjalr x0, 0(x1)从Callee 返回ret

函数调用过程中实现被调用函数的编程约定

  • 函数起始部分(Prologue)
    • 减少sp 的值,根据本函数中使用saved 寄存器的情况以及local 变量的多少开辟栈空间
    • 将saved 寄存器的值保存到栈中
    • 如果函数中还会调用其他的函数,则将ra 寄存器的值保存到栈中
  • 函数执行体
  • 函数退出部分(Epilogue)
    • 从栈中恢复saved 寄存器
    • 如果需要的话,从栈中恢复ra 寄存器
    • 增加sp 的值,恢复到进入本函数之前的状态
    • 调用ret 返回
RISC-V 汇编 与 C 混合编程

遵守 ABI (Abstract Binary Interface)的规定• 数据类型的大小,布局和对齐

  • 函数调用约定(Calling Convention)
  • 系统调用约定

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

  • 函数参数采用寄存器a0 ~ a7 传递
  • 函数返回值采用寄存器a0 和a1 传递

C 函数中嵌入 RISC-V 汇编

asm [volatile] (
        “汇编指令”
        : 输出操作数列表(可选)
        : 输入操作数列表(可选)
        : 可能影响的寄存器或者存储器(可选)
);
int foo(int a, int b)
{
        int c;
        asm volatile (
                "add %[sum], %[add1], %[add2]" 
                :[sum]"=r"(c)
                :[add1]"r"(a), [add2]"r"(b)
        );
        return c;
}

int foo(int a, int b)
{
        int c;
        asm volatile (
                "add %0, %1, %2" 
                :"=r"(c)
                :"r"(a), "r"(b)
        );
        return c;
}
  • 汇编指令用双引号括起来,多条指令之间用";" 或者"\n" 分隔
  • “输出操作数列表” 和“输入操作数列表” 用于将需要操作的C 变量和汇编指令的操作数对应起来,多个操作数之间用“,” 分隔
  • “可能影响的寄存器或者存储器” 用于告知编译器当前嵌入的汇编语句可能修改的寄存器或者内存,方便编译器执行优化
  • 16
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值