RISC-V MCU中文社区

【分享】 GCC内联汇编

发表于 开源蜂鸟E203 2023-05-10 20:01:58
0
2923
2

1 队伍介绍

报名编号:CICC1907
团队名称:Hollow-SEKIRO-ARCAEA
学校名称:东南大学
指导老师:刘昊
团队成员:申烁、徐轶凡、林昊

这是我们第七篇分享文章。

2 GCC内联汇编

在蜂鸟内核的NICE协处理器扩展demo的insn.h文件中存在下面一段指令,用于定义对协处理器调用指令,demo中协处理器支持三条指令:lbuf从内存中load数据至行数据缓存;sbuf从行数据缓存中store数据至内存;以及rowsum实现行累加值的计算并通过结果寄存器返回累加值。
NICE协处理器的操作需要用到RISC-V的自定义指令,不能通过GCC直接编译生成,所以需要用到C/C++中的内联汇编语法asm volatile(“instructions”) 以在C代码中嵌入汇编以执行自定义指令集。
__STATIC_FORCEINLINE void custom_lbuf(unsigned long* addr)
{
int zero = 0;

asm volatile(".insn r 0x5b, 2, 1, x0, %1, x0" : "=r"(zero) : "r"(addr));

}

/* custom nice instruction sbuf /
__STATIC_FORCEINLINE void custom_sbuf(unsigned long* addr)
{
int zero = 0;

asm volatile(".insn r 0x5b, 2, 2, x0, %1, x0" : "=r"(zero) : "r"(addr));

}

/* custom nice instruction rowsum /
__STATIC_FORCEINLINE int custom_rowsum(unsigned long* addr)
{
int rowsum;

asm volatile(".insn r 0x5b, 6, 6, %0, %1, x0" : "=r"(rowsum) : "r"(addr));

return rowsum;

}

2.1 内联汇编语法

内嵌汇编命令asm volatile(“instructions”)中asm表示后面的代码为内嵌汇编,volatile表示禁止编译器优化修改后面的汇编代码,括号内则是汇编指令。
内嵌汇编的语法为asm(汇编语句模板: 输出部分: 输入部分: 破坏描述部分),一共包括汇编语句、输出、输入、破坏描述四个部分,各部分之间使用“:”隔开,不需要定义对应内容属实相应位置为空即可。

语法规则:
这部分可以由一句或多句汇编语句构成,可以写标准的RISC-V指令集汇编码格式,也可以按照一定的模板编写一些特殊功能的指令,就比如demo中按.insn模板添加的NICE指令。多条指令之间使用“;”、“/n”或者“/n/t”分开。
指令中的操作数可以通过占位符引用C语言代码中定义的变量,比如demo中定义的“%0、%1”,操作数在C语言中总被视为32bit的long型数据,但是在像汇编中传递时根据汇编指令类型传递16bit或者8bit,默认传递低字节,也可以自己指定例如“%h1”表示传递高字节,“%b1”表示传递低字节。
0% 表示第一个参数, 1%这种数字表示第二个参数,依次类推
图片alt

2.2 输入输出

内嵌汇编的第二部分列出输出操作数与第三部分列出输入操作数,若存在多个输入或输出操作数,则不同操作数之间用“,”隔开,罗列完毕后用“:”结束。输入输出的操作数对应于汇编语句中的占位符,并按顺序一一对应,如asm volatile(“.insn r 0x5b, 6, 6, %0, %1, x0” : “=r”(rowsum) : “r”(addr))中,内%0对应输出参操作数rowsum,%1对应输入操作数addr。
上述例句输入输出操作数部分中,““内为限制字符串用于限制操作数变量的属性,”=“表示变量用作输出,”r”表示该操作数对应一个寄存器。“()”中写操作数在C/C++代码中对应的变量或者表达式。

2.3 .insn模板

在自行设置指令编码时可以利用.insn模板进行编程,不需要修改RISC-V的GCC代码,例子中的代码就是使用.insn的R-type指令模板设置的指令编码,模板如下:
.insn r opcode, func3, func7, rd, rs1, rs2
图片alt
根据蜂鸟e203官方文档中对NICE指令的解释,opcode为1011011表示其使用的是Custom-2指令组,fun3为110说明其需要用到目的寄存器rd1和源寄存器rs1,func7为本demo中定义的指令功能表示,0000001代表custom_lbuf,0000010代表sbuf,000110代表rowsum。

下面以Verilog仿真作为示例
图片alt
图片alt
Nice_demo.c的例程首先是初始化了三个数组用于存储,然后依次执行custom_lbuf,custom_rowsum,custom_sbuf函数。这三个函数在insn.h中定义,分别实现了加载memory的数据到row_buf;实现累加值计算并写回结果;加载row_buf中的数据到内存。
Vivado红色标出的指令对应nice中自定义的custom_lbuf指令,可以看到在指令发出后ready信号一直置高后等待valid来到,此时指令和valid同时到达,ince_req_inst = 0000_0010_0000_0111_1010_0000_0111_1011,根据nice指令格式,我们可以将其填充至如下指令单元
图片alt
图片alt
再来看SDK中对应代码,init_buf = 01111,再根据上文提到的指令模板替换原则,可知opcode = 0x7b,func3=2,funcc7=1,rd=0,rs1=01111,rs2=0可知与仿真结果一致
图片alt
图片alt

3 总结

本文介绍的内容是为了完成对蜂鸟E203 RISC-V内核进行运算算子(譬如加解密算法、浮点运算、矢量运算等)的扩展,我们计划通过NICE协处理器接口添加一套专用于计算神经网络卷积计算功能的指令,实现更高的性能。

喜欢2
用户评论
Lri-SKY

Lri-SKY 实名认证

懒的都不写签名

积分
问答
粉丝
关注
  • RV-STAR 开发板
  • RISC-V处理器设计系列课程
  • 培养RISC-V大学土壤 共建RISC-V教育生态
RV-STAR 开发板