首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >riscv gcc中添加custom自定义指令

riscv gcc中添加custom自定义指令

作者头像
bigmagic
发布2021-10-09 16:05:05
3.8K0
发布2021-10-09 16:05:05
举报
文章被收录于专栏:嵌入式iot嵌入式iot嵌入式iot

riscv gcc中添加custom自定义指令

  • 1.概述
  • 2.riscv指令集基础
  • 3.利用.insn模板进行编程
  • 4.修改`binutils`让riscv gcc认识到这条指令
    • 4.1 利用riscv-opcodes生成对应的宏
    • 4.2 修改`binutils`
    • 4.3 编译与测试
  • 5.两种办法分析

1.概述

在riscv的处理器开发过程中,各家处理器往往都会涉及到自定义指令功能的添加。在处理器设计上,添加一些特定功能的指令是十分正常的,一般处理办法本文会讲述,让其识别客户自定义的指令。从现有的解决办法上来看,第一种是可以利用Kito Cheng提供的.insn模板进行开发,第二种则是修改binutils的方法。本文主要介绍这两种办法进行riscv custom指令的添加。

2.riscv指令集基础

要想设计一条自定义的riscv指令,必须了解riscv指令的构成。

从riscv指令集手册上来说,riscv的指令集被分成了R-type,I-type,S-type,B-type,U-type,J-type。

每一种类型的指令的格式都不相同,按照特定的机器码编排的指令有着特殊的用途。

在进行指令实验时,可以通过自定义一条基础整数指令开始。

按照划分,riscv的模块化指令集可以分成下列许多类型:

RV32I:整数基础指令集
RV32M:乘除法
RV32F:单精度浮点
RV32D:双精度浮点
RV32A:原子指令
RV32V:向量指令
RV32B:位操作
.
.
.

riscv基础指令集中,主要分析R-type,同时可以自定义一条custom指令。

custom的指令可以添加一条

比如自定义一条cube指令,该指令的作用是计算算数立方。

 *      func7      rs2      rs1    func3           rd        opcode
 * 31---------25--------19------15------12----------------6----------0
 * | 000110    | 00000  | *****  |  110  |    *****       |  1111011 |
 * |------------------------------------------------------|----------|

设计完成指令后,就可以实现该指令了。

3.利用.insn模板进行编程

在利用.insn模板进行编程时,不需要修改riscv的gcc任何代码,只需要用户根据指令编码设计模型。

对于R-type的指令模板构成,有下面的通用处理办法:

.insn r opcode, func3, func7, rd, rs1, rs2

从c内联汇编编程的编程方式,cube指令的实现可以通过下面的指令进行操作。

asm volatile(“.insn r 0x7b, 6, 6, %0, %1, x0” : “=r”(cube) : “r”(addr));

当然,也可以裸写汇编,a0,a1寄存器中存放的是函数调用时的两个参数。

.insn r 0x7b, 6, 6, a0, a1, x0

这样就完成了一条指令的功能。x0在riscv架构中,始终为0,所以该指令实际上就是讲a1的数据通过算数立方乘,将结果存放到a0寄存器。

通过手写C代码进行测试

static int custom_cube(int addr)
{
    int cube;
    asm volatile (
       ".insn r 0x7b, 6, 6, %0, %1, x0"
             :"=r"(cube)
             :"r"(addr)
     );
    return cube; 
}

反汇编后可以得到

a0002c74 <custom_cube>:
a0002c74: 7179                 addi sp,sp,-48
a0002c76: d622                 sw s0,44(sp)
a0002c78: 1800                 addi s0,sp,48
a0002c7a: fca42e23           sw a0,-36(s0)
a0002c7e: fdc42783           lw a5,-36(s0)
a0002c82: 0c07e7fb           0xc07e7fb
a0002c86: fef42623           sw a5,-20(s0)
a0002c8a: fec42783           lw a5,-20(s0)
a0002c8e: 853e                 mv a0,a5
a0002c90: 5432                 lw s0,44(sp)
a0002c92: 6145                 addi sp,sp,48
a0002c94: 8082                 ret

其中的0xc07e7fb,机器码交给实际的硬件进行解析,只要硬件设计指令按照指令规范即可。这样就能够实现算数立方的功能了。

4.修改binutils让riscv gcc认识到这条指令

采用.insn模板进行编程的缺点非常明显,就是非常的复杂难懂,编程人员还需要知道每条指令的机器码,这样不利于riscv编程使用者的开发体验。为了解决这样的问题,可以通过修改binutils来解决。

4.1 利用riscv-opcodes生成对应的宏

首先定义好cube指令的格式后。

 *      func7      rs2      rs1    func3           rd        opcode
 * 31---------25--------19------15------12----------------6----------0
 * | 000110    | 00000  | *****  |  110  |    *****       |  1111011 |
 * |------------------------------------------------------|----------|

下载riscv-opcodes

https://github.com/riscv/riscv-opcodes

可生成对应的指令模板。

首先新建一个opcodes-custom文件。

添加如下的内容

cube rd rs1 rs2 31..25=0x0c 14..12=0x6 6..2=0x1e 1..0=3

其中的格式是按照定义好的指令序列进行排布。

接着输入

cat opcodes-custom | python3 parse_opcodes -c > encoding.h

可看到encoding.h生成对应的文件

DECLARE_INSN(cube, MATCH_CUBE, MASK_CUBE)

还生成下面的宏定义

#define MATCH_CUBE 0x1800607b
#define MASK_CUBE  0xfe00707f

4.2 修改binutils

riscv-gnu-toolchain/riscv-binutils中,修改

include/opcode/riscv-opc.h

上述riscv-opcodes生成的三条宏定义放到该文件中。

然后修改opcodes/riscv-opc.c中的指令定义。

{"cube",       0, INSN_CLASS_I, "d,s,t",  MATCH_CUBE, MASK_CUBE, match_opcode, 0 },

修改完成后,这样就添加完成了。

4.3 编译与测试

对于单独编译binutils,可以直接进入到build-binutils-newlib

输入make -j8 && make install。不用全部重新编译riscv gcc效率比较高。

如果是第一次编译riscv-gnu-toolchain,则没有build-binutils-newlib,需要全部重新编译:

./configure --prefix=$RISCV --enable-multilib --with-cmodel=medany
make -j8

测试时,可以写内联汇编

static int custom_cube(int addr)
{
    int cube;
    asm volatile (
       "cube %0, %1, x0"
             :"=r"(cube)
             :"r"(addr)
     );
    return cube; 
}

很容易,也可以在汇编文件中写

cube a0,a1,zero

因为x0寄存器表示zero,所以这样写是等价的。

通过反汇编后可以看到解析代码如下:

a0002c74 <custom_cube>:
a0002c74: 7179                 addi sp,sp,-48
a0002c76: d622                 sw s0,44(sp)
a0002c78: 1800                 addi s0,sp,48
a0002c7a: fca42e23           sw a0,-36(s0)
a0002c7e: fdc42783           lw a5,-36(s0)
a0002c82: 1807e7fb           cube a5,a5,zero
a0002c86: fef42623           sw a5,-20(s0)
a0002c8a: fec42783           lw a5,-20(s0)
a0002c8e: 853e                 mv a0,a5
a0002c90: 5432                 lw s0,44(sp)
a0002c92: 6145                 addi sp,sp,48
a0002c94: 8082                 ret

直接写汇编,gcc已经可以识别到cube指令了。

5.两种办法分析

riscv添加新的自定义指令,利用.insn的好处是不用修改riscv gcc的代码,所有的riscv gcc均可进行编译,但是需要理解指令的操作码,对于应用程序编程来说比较复杂,更加适合硬件指令的功能验证。而采用修改binutils则需要单独维护一个与riscv gcc主线分离的版本,单独发布,更适合芯片方案厂商。虽然修改riscv gcc并不是一件很容易的事情,但是对用户来说,操作体验更好。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-09-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 嵌入式IoT 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • riscv gcc中添加custom自定义指令
    • 1.概述
      • 2.riscv指令集基础
        • 3.利用.insn模板进行编程
          • 4.修改binutils让riscv gcc认识到这条指令
            • 4.1 利用riscv-opcodes生成对应的宏
            • 4.2 修改binutils
            • 4.3 编译与测试
          • 5.两种办法分析
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档