页首HTML代码

ASM@RISC-V

CPU寄存器

Register ABI Discription Attribution
x0 zero 硬件常数0 N/A
x1 ra 返回地址 Caller
x2 sp 栈指针 Callee
x3 gp 全局指针 -
x4 tp 线程指针 -
x5-x7 t0-t2 临时变量 Caller
x8 s0/fp 保存寄存器/帧指针 Callee
x9 s1 保存寄存器 Callee
x10-x11 a0-a1 函数参数/返回值 Caller
x12-x17 a2-a7 函数参数 Caller
x18-x27 s2-s11 保存寄存器 Callee
x28-x31 t3-t6 临时变量 Caller
f0-f7 ft0-ft7 浮点临时变量 Caller
f8-f9 fs0-fs1 浮点保存寄存器 Callee
f10-f11 fa0-fa1 浮点函数参数/返回值 Caller
f12-f17 fa2-fa7 浮点函数参数 Caller
f18-f27 fs2-fs11 浮点保存寄存器 Callee
f28-f31 ft8-ft11 浮点临时变量 Caller

32位RISC-V(RV32)指令集概述

典型汇编语句

label: opcode operands comment
标签 操作码 操作数 注释
  • 标签:表示当前指令的位置标记
  • 操作码:操作码主要有以下几种形式
    • RISC-V标准指令
    • 伪指令
    • 用户自定义宏
  • 操作数:
    • 操作码所需的操作,与操作码之间以空格分开
    • 符号、常量、或是符号与常量组成的表达式

指令机器码格式

  • I型指令

    imm[11:0] rs1 funct3 rd opcode
    [31:20] [19:15] [14:12] [11:7] [6:0]
  • U型指令

    imm[31:12] rd opcode
    [31:12] [11:7] [6:0]
  • S型指令

    imm[11:5] rs2 rs1 imm[4:0] rd opcode
    [31:25] [24:20] [19:15] [14:12] [11:7] [6:0]
  • R型指令

    funct7 rs2 rs1 funct3 rd opcode
    [31:25] [24:20] [19:15] [14:12] [11:7] [6:0]
  • B型指令

  • J型指令

RV32I基本指令集

说明:为了展示指令之间的层次结构,在这里使用了多级无序列表,每个指令后的括号即该指令的英文全称,方便理解相似指令在不同环境下的运用,而大写部分则是指令缩写所提取的字母,另外划有删除线代表本身不存在但具有分支应用的指令;后续也可能会画一个树表作为补充。

算术指令

  • add

    ADD:
    add rd,rs
    

    等效于rd = rd + rs

    • addi(add immediate)
      ADDI:
      addi rd,rs1,imm
      
  • or

    • ori(OR Immediate)
  • xor(eXclusive OR)

    • xori(eXclusive OR Immediate)
  • shift

    • sll(Shift Left Logical)
      • slli(Shift Left Logical Immediate)
    • sra(Shift Right Arithmetic)
      • srai(Shift Right Arithmetic Immediate)
    • srl(Shift Right Logical)
      • srli(Shift Right Logical Immediate)
  • sub(SUBtract)

  • lui(Load Upper Immediate)
    将一个16位的立即数填入寄存器的高16位中,低16位则补零

  • auipc(Add Upper Immediate to PC)

  • slt(Set Less Than)

    • slti(Set Less Than Immediate)
      • sltiu(Set Less Than Immediate Unsigned)

加载指令

  • load

    • lb(Load Byte)
      • lbu(Load Byte Unsigned)
    • lh(Load Halfword)
      读16位,符号位拓展后写入寄存器rd中
      • lhu(Load Halfword Unsigned)
        读16位,高位补0,写入寄存器rd中
    • lw(Load Word)
      从存储器中读回一个32位的数据,写入寄存器rd中
  • store

    • sb(store Byte)
    • sh(store Halfword)
    • sw(store Word)

条件分支指令

  • branch
    • beq(branch equal)
      比较两个寄存器内部的值,若相等便跳转至指定标签位置,否则继续顺序执行。
    • bne(branch not equal)
      比较两个寄存器内部的值,如果结果不相等,则跳转至指定标签位置,反之继续顺序执行。
    • bge(Branch Greater than or Equal)
      当前操作数大于等于后者时,跳转到分支标签内容,否则继续执行顺序内容。
      • bgeu(Branch Greater than or Equal Unsigned)
        对参数以无符号格式进行比较,其他部分逻辑与bge指令相同
    • blt(Branch Less Than)
      当前操作数小于(但不等于)后者时,跳转到分支标签内容,不然继续执行顺序内容。
      • bltu(Branch Less Than Unsigned)

小于等于、大于判断可以通过颠倒操作数位置来实现,注意逻辑和边际问题即可。
站在芯片运行优化的角度,设计的比较条件应该尽可能倾向于程序顺序执行,以减少在判断跳转之间的时间消耗。

BRANCH:
opcode rs1,rs2,LABEL;

跳转控制指令

  • jal(Jump And Link)
    • jalr(Jump And Link Register)

寄存器屏障指令

  • fence

  • break

    • ebreak
  • call

    • ecall
  • csr

    • csrrc(Control Status Register Read & Clear bit)
      • csrrci(Control Status Register Read & Clear bit Immediate)
    • csrrs(Control Status Register Read & Set bit)
      • csrrsi(Control Status Register Read & Set bit Immediate)
    • csrrw(Control Status Register Read & Write bit)
      • csrrwi(Control Status Register Read & Write bit Immediate)

RV32标准拓展指令

RV32M标准拓展指令

  • mul
    • mulh
      • mulhsu
      • mulhu
  • div
    • divu
  • rem
    • remu

RV32A标准拓展指令

  • lr.w
  • sc.w
  • amo
    • amoswap.w
    • amoadd.w
    • amoxor.w
    • amoand.w
    • amoor.w
    • amomin.w
    • amomax.w
    • amominu.w
    • amomaxu.w

RV32F标准拓展指令

  • flw
  • fsw
  • fmadd.s
  • fmsub.s
  • fnmsub.s
  • snmadd.s
  • fadd.s
  • fsub.s
  • fmul.s
  • fdiv.s
  • fsqrt.s
  • fsgnj.s
    • fsgnjn.s
    • fsgnjx.s
  • fmin.s
  • fmax.s
  • fcvt.w.s
    • fcvt.wu.s
  • fmv.x.s
  • feq.s
  • flt.s
  • fclass.s
  • fcvt.s.w
  • fcvt.s.wu
  • fmv.s.x

伪指令

实际上是由基本指令组合而成

加载伪指令

  • la(Load Address)
    加载地址
  • ld(LoaD)
    加载全局参数

算术伪指令

  • nop
      NOP:
      nop
    
    执行一个空操作
  • li(Load Immediate)
      LI:
      li rd,imm;
    
    将imm代表的立即数直接赋值rd寄存器
  • mv(MOV)
  • not
  • neg
  • negw
  • sext
  • seqz
  • snez
  • sltz
  • sgtz

浮点算术伪指令

  • fmv.s
  • fabs.s
  • fneg.s
  • fmv.d
  • fabs.d
  • feng.d

条件分支伪指令

  • beqz
  • bnez
  • blez
  • bgez
  • bltz
  • bgtz

跳转控制伪指令

  • j
  • jal
  • jr
  • jalr
  • ret
  • tail

启动

  • .global(.globl)
    伪操作,用于定义全局符号,使链接过程中能被其他程序文件可见,类似于extern

  • .local
    伪操作,用于定义局部符号,仅当前程序文件可见

  • .file
    伪操作,用于指示汇编器该汇编程序的逻辑文件名

    opcode oprands
    .file "filename"
  • .section

    • .text
    • .data
    • .rodata
    • .bss
  • .type

  • .align

    • .balign
  • .zero

  • .byte

    • 2byte
    • 4byte
    • 8byte
  • .float

    • .double
  • .word

    • .half
    • .dword
  • .string

  • .equ

  • .weak
    伪操作,同时创建符号并定义为弱属性。如果该符号已定义,则设置此符号为弱属性

    • 在汇编程序中,符号的默认属性为强(strong)

    如果符号的属性为弱,则可无需定义具体内容,常用于预留空符号并通过汇编语法检查。当另外存在一个属性为强的同名符号实体时会自动覆盖并链接

  • .pushsection - .popsection

  • .marco - .endm

  • .comm(.common)

  • .option

注释

以符号 # 或 ; 作为分隔号,当行分隔号以后的部分会被视作注释

代指

  • rd(Register Destination)

指目标寄存器,实际代码中可以是t0、t1等

代码格式(C语言嵌套汇编)

在C语言文件中嵌入汇编代码,除对固定地址寄存器并直接进行操作外,需要将变量等参数传入汇编程序中,才能保证操作有效,想要传入参数,需要通过操作符列表来完成

关键字

  • asm
    为GCC的关键字,表示进行内联汇编操作,也可以使用前后各带两个下划线的__asm___asm_是GCC关键字asm的宏定义。

  • volatile
    或者_volatile__volatile_volatile,均为宏定义。如果添加了该关键字,则通知编译器避免对后续括号内添加汇编程序进行任何优化,以保持其原状

  • 汇编指令列表
    即需要嵌入的汇编指令,每条指令必须被双引号括起来(作为字符串),两条指令之前必须以“\n”或者“;”作为分隔符。如果没有添加分隔符,相连的字符串将会被合并成为一个字符串。内部语法与汇编程序一样

标记符号

  • %
    后面跟标签则为指定,若为数字则是从操作符列表顺序选择

  • [ ]
    方括号中的符号名,用于将内联汇编程序中使用的操作数和此操作符通过名称绑定起来。标签名称随意,但不能重复

  • “ ”
    引号中的限制字符串,用于约束此操作数变量的属性,常用的约束如:

    • r
      代表使用编译器自动分配的寄存器来存储该操作数变量

    • m
      字母“m”代表使用内存地址来存储该操作数变量,如果同时指明“rm”则编译器自动选择最优方案

    • =
      对于输出操作数而言,等号代表输出变量用作输出,原来的值会被新值替换,此约束不适用于输入操作数

    • +
      代表输出变量不仅作为输出,还作为输入,此约束不适用于输入操作数

  • ( )
    圆括号中填入C/C++变量或者表达式

输出操作符之间需使用逗号分割

操作符列表

  • 输出资源表
    : [ ]"=r"( ), [ ]"=r"( ), ···

  • 输入资源表
    : [ ]"r"( ), [ ]"r"( ), ···

  • 影响资源表
    : " ", "", ···

用于告知编译器当前内联汇编语句可能会对某些寄存器或内存进行修改,使得编译器在优化时将其因素考虑进去

  • 隐式表
    除了使用%[字符]中明确的符号命名指定之外,还可以使用%数字的方式进行隐含指定。从0开始,依次表示操作符列表中所有的输出操作数与输入操作数。

范例

int abc = 10,cba = 20;   
printf("%d & %d\n",abc,cba);

asm volatile(
    "mv %[da],%3\n"
    "add %2,%1,%0\n"
    "li %3,12\n"

    : [da]"=r"(abc), "=r"(cba)                 // Working
    : "r"(abc), [db]"r"(cba)                 // Working
    : "t1", "t2"//, "x7"
);

printf("%d & %d\n",abc,cba);

输出结果为:

10 20   (修改前)
40 12   (修改后)
posted on 2022-06-07 14:52  常隐  阅读(1866)  评论(0编辑  收藏  举报



页脚HTML代码