riscv gcc中添加自定义的csr支持
- 1.概述
- 2.不改变编译工具链实现CSR添加
- 3.在编译器中集成CSR名称
- 4.编译开发riscv gcc相关
- 5.小结
1.概述
由于RISCV的模块化的指令集的定义,各家都有着自己的实现方式。从当前看来,除了标准的CSR外,很多都实现了自己的CSR指令扩展。如何自定义CSR并且让编译器能够识别,本文将进行一定的分析,同时从riscv gcc开发的角度出发,来分析编译器开发的流程。
2.不改变编译工具链实现CSR添加
如果想读写CSR,可以使用汇编指令进行操作。
代码语言:javascript复制void test_csr()
{
int a = 0x00200;
asm volatile("csrw 0x307,%0"::"r"(a));
}
通过反汇编
代码语言:javascript复制riscv32-unknown-elf-objdump -D virt_test.elf > 1.txt
得到该函数的汇编代码
由于编号为0x307
的CSR没有标准定义,所以会直接会体现在汇编函数中。但是这样并不会影响功能的使用。
对于这些非标准的CSR的读写操作,RISCV GCC并不会根据特定的编码序号生成相应的名称。
比如芯来科技为其eclic定义的一条CSR。
如果用标准的GCC进行编译,那么需要给定CSR地址(0x7ed)。
这样的实现,在写汇编函数的时候,给定CSR的地址即可,不能写名字。比如:
代码语言:javascript复制csrw 0x7ed,zero
通过上述的方式,可以完成不改变编译器的情况下,实现非标准的CSR的操作。
3.在编译器中集成CSR名称
往往通过汇编去查看时,若没有CSR名称,直接去查看CSR的地址,有时候还需要通过CSR的地址,查阅相关的寄存器手册,才能明白读写哪个寄存器。为了简化这种操作,可以将CSR的名称添加到gcc中。这里借用玄铁C906上的扩展指令进行实验。
如果不想改变gcc,那么实现上直接可以通过内联汇编。
代码语言:javascript复制void test_csr()
{
int a = 0x00200;
asm volatile("csrw 0x7c0,%0"::"r"(a));
}
那么最后反汇编得到结果如下:
这显然还是不够直观。
为了让其更加的直观,可以修改RISCV工具链。
这里使用的是riscv-gnu-toolchain
,目前做编译器开发需要对该仓库的使用非常熟悉。
https://github.com/riscv/riscv-gnu-toolchain.git
将代码进行clone后,更新子模块。
代码语言:javascript复制git submodule init
git submodule update
其中需要修改的是riscv-gnu-toolchain/riscv-binutils/include/opcode/riscv-opc.h
同时将MXSTATUS的地址添加进去。
这样就能够,编译工具链,用生成的编译工具链去编译代码,就能够正常的看到反汇编的执行效果了。
这样,无论是传递地址csrw 0x7c0,zero
,还是传递名称csrw mxstatus,zero
,汇编器都能够正常的输出CSR名称。
通过对编译器的修改,也是非常的简单的实现了这个功能。
4.编译开发riscv gcc相关
如果要研究riscv gcc的开发工作,搭建环境,加快编译效率很关键。由于编译工具链很大,涉及到的模块很多,而且编译的过程很复杂,为了节省编译的时间,可以通过编译选项来进行约束。
通常情况下,编译全功能的gcc,可以采用
代码语言:javascript复制./configure --prefix=$PREFIX --enable-multilib --with-cmodel=medany
当使能了--enable-multilib
,就能够编译很多版本的lib库。
./riscv64-unknown-elf-gcc --print-multi-lib
.;
rv32i/ilp32;@march=rv32i@mabi=ilp32
rv32ic/ilp32;@march=rv32ic@mabi=ilp32
rv32im/ilp32;@march=rv32im@mabi=ilp32
rv32imc/ilp32;@march=rv32imc@mabi=ilp32
rv32iac/ilp32;@march=rv32iac@mabi=ilp32
rv32imac/ilp32;@march=rv32imac@mabi=ilp32
rv32imafc/ilp32f;@march=rv32imafc@mabi=ilp32f
rv32imafdc/ilp32d;@march=rv32imafdc@mabi=ilp32d
rv32e/ilp32e;@march=rv32e@mabi=ilp32e
rv32ec/ilp32e;@march=rv32ec@mabi=ilp32e
rv32emc/ilp32e;@march=rv32emc@mabi=ilp32e
rv32ema/ilp32e;@march=rv32ema@mabi=ilp32e
rv32emac/ilp32e;@march=rv32emac@mabi=ilp32e
rv32eac/ilp32e;@march=rv32eac@mabi=ilp32e
rv64i/lp64;@march=rv64i@mabi=lp64
rv64im/lp64;@march=rv64im@mabi=lp64
rv64imc/lp64;@march=rv64imc@mabi=lp64
rv64imac/lp64;@march=rv64imac@mabi=lp64
rv64imafc/lp64f;@march=rv64imafc@mabi=lp64f
这样实际上也是消耗了很多时间,为了解决这个问题,可以指定特定的march和mabi进行编译和测试,待开发完成后,发布时在进行整体编译。这样开发效率会高出很多。
代码语言:javascript复制make clean
./configure --prefix=$RISCV/0727/rv32 --with-arch=rv32gc --with-abi=ilp32d --disable-gdb
make -j8
例如上面的命令,只编译march为rv32gc,abi为ilp32d的gcc模型。省略掉很多库的编译,让编译的时间缩短了不少。
5.小结
为自己的处理器添加特定的CSR支持,一种情况下可以使用CSR的地址进行,另外一种就是修改汇编器,前一种方法比较直观,适合使用riscv通用编译器的开发方式进行开发,但是不够直观。后一种则需要修改汇编器,并且需要自己编译工具链,比较复杂,但是使用起来直观,简单。