如何往binutils中加入一条riscv指令

riscv支持自定义指令,但是自定义指令,在gcc工具链中,是不支持的,因此需要自己修改gcc工具链,以支持自己定义的指令。

gcc工具链中,gcc工具负责将高级语言,转换为汇编程序。binutils中的as工具,负责将汇编程序,转化为二进制程序。

我们增加的是汇编指令,因此只需要修改binutils代码,在其中,加入我们定义的指令。

一、需要加的指令

我们需要加的指令,如下图所示:

这条指令,比较特殊的是,最后7个bit为全1。

如果按照riscv-spec的规定,那么这条指令,是属于80bit指令。

但是,这里,只是借用了80bit指令的编码部分,指令还是32bit。这就需要在binutils中,做一些处理。

二、下载binutils

从github上,下载riscv-binutils的源代码。

git clone https://github.com/riscv/riscv-binutils-gdb.git

下载完毕后,在源代码根目录,创建build目录,用于之后编译binutils。

我们首先编译binutils工具。

cd build
../configure -with-arch=rv64imafdc --with-abi=lp64d --target=riscv64-unkown-elf --prefix=/home/lujun/tools/riscv-gcc2
make
make install

安装完毕后,得到如下一些工具:指定将来工具,安装在 /home/lujun/tool/riscv-gcc2, 这样就不需要root权限。

我们在这里,主要修改的是as工具和objdump工具。

三、生成encoding.h

riscv的相关工具,比如binutils,spike等,均依赖于encoding.h这个头文件,而这个头文件,由riscv-opcodes这个工具来生成。

从github上,下载riscv-opcodes开源项目:

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

创建,opcodes-own文件,加入如下内容:

p.add16   rd rs1 rs2  31..25=0x20  14..12=0  6..2=0x1f  1..0=3

最前面一列,是指令的助记符,后续跟指令的参数。

接着是各个bit的描述,这个比较重要。要注意描述,从高bit描述到低bit。

修改Makefile,增加如下代码:

gen:
    cat opcodes-own  | python3 ./parse-opcodes -c > encoding.h

执行make gen,生成新的 encoding.h文件。在这个文件中,我们关注如下信息:

#define MATCH_P_ADD16 0x4000007f
#define MASK_P_ADD16  0xfe00707f
DECLARE_INSN(p_add16, MATCH_P_ADD16, MASK_P_ADD16)

定义该条指令的MASK和MATCH,并且声明一条该指令。

四、修改riscv-opc.h文件

在binutils源代码根目录下的include/opcode/riscv-opc.h 文件,其实就来源于上面生成的encoding.h文件。

该文件,对riscv的指令和架构进行了描述。不过不能直接从上一步生成的encoding.h文件,拷贝覆盖该文件,否则编译会报错。

将上一步得到的信息,拷贝到该文件中。

加入MASK和MATCH。

加入DECLARE_INSN。

五、修改riscv-opc.c文件

binutils源代码根目录下,opcdes/riscv-opc.c 文件,定义了riscv的指令集。

结构体数组,riscv_opodes,保存了riscv的指令。因此要在这个数组中,加入我们自定义的指令,这样才能够实现编译。

在数组的最后,加入如下内容:

{"p.add16", 0, {"I", 0}, "d,s,t", MATCH_P_ADD16, MASK_P_ADD16, match_opcode, 0},

如下图所示:

第一个是汇编指令的助记符

第二个是指令依赖的XLEN,0表示不指定

第三个是指令所属的指令集,这里暂定为I指令集

第四个是指令的参数,d表示rd,s表示rs1,t表示rs2。

第五个是指令的MATCH参数,在enconding.h文件中生成

第六个是指令的MASK参数,在encoding.h文件中生成

第七个是指令的match函数,直接使用默认的match_opcode函数

第八个填0即可。

以上的格式,可以参考目前已经有的指令,进行修改。可以考虑找一条和当前指令很相似的指令,然后参考修改。

六、修改riscv.h

在binutils源代码根目录,include/opcode/riscv.h中,有riscv_insn_length函数,该函数,用来判断指令的位宽。

在代码中,加入80bit指令的判断。如果是80bit指令,当做是32bit指令,返回4。

static inline unsigned int riscv_insn_length (insn_t insn)
{
if ((insn & 0x3) != 0x3) /* RVC.  */
return 2;
if ((insn & 0x1f) != 0x1f) /* Base ISA and extensions in 32-bit space.  */
return 4;
if ((insn & 0x3f) == 0x1f) /* 48-bit extensions.  */
return 6;
if ((insn & 0x7f) == 0x3f) /* 64-bit extensions.  */
return 8;
if ((insn & 0x7f) == 0x7f) /* 80bit, reserved as 32bit */
return 4;
/* Longer instructions not supported at the moment.  */
return 2;
}

七、编译binutils

修改完毕后,需要对binutils重新编译。

cd build
make -j8
make install

八、测试

编写如下汇编程序,命名为 a.s

.text
p.add16 x5, x1, x2
p.add16 x2, x2, x3
p.add16 x6, x5, x8
p.add16 x4, x7, x9

使用刚刚编译得到的  riscv64-unknown-elf-as 工具,编译该程序,得到 a.out。说明,as工具,可以编译我们自定义的指令。

使用 riscv64-unknow-elf-objdump -d a.out, 查看反汇编:

从反汇编可以看出,我们自定义的指令,objdump工具也能认识,能做反汇编。

此条目发表在RISCV分类目录,贴了标签。将固定链接加入收藏夹。

发表评论

电子邮件地址不会被公开。