RISC-V MCU中文社区

【分享】 (一):蜂鸟自定义指令软件讲解和内联汇编

发表于 全国大学生集成电路创新创业大赛 2023-05-13 18:26:00
0
2692
5

一、团队介绍

报名编号:CICC1327
团队名称:301小队

二、内联汇编介绍

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

(1)GCC内联汇编
“asm”是 GCC 的关键字,表示进行内联汇编操作,关键字“volatile”要求编译器对后续括号内添加的汇编程序不进行任何优化以保持其原状;如果没有添加此关键字,则编译器可能会将某些汇编指令优化掉。
可以使用“ %数字”的方式进行隐含指定。 “数字”从 0 开始,依次表示输出操作数和输入操作数。假设包含“输出操作数”列表中有 2 个操作数,“输入操作数”列表中有 2 个操作数,则汇编程序中%0 表示第一个输出操作数, %1 表示第二个输出操数, %2 表示第一个输入操作数, %3 表示第二个输入操作数。字母“r”表示使用编译器自动分配的寄存器来存储该操作数变量;(如果不需要使用操作数,则直接分配x0寄存器即可)字母“m” 表示使用内存地址来存储该操作数变量。如果同时指明“ rm”,则编译器自动选择最优方案。对于“输出操作数”而言,“=”代表输出变量用作输出,原来的值会被新值替换;“+”代表输出变量不仅作为输出,而且作为输入。

(2)蜂鸟定义的汇编指令
此协处理器模块为计算3行3列的矩阵值,首先加载矩阵每行的首个元素的地址,连续加载三次进行计算。lbuf为读内存中的数据加载到缓存中;sbuf为将缓存中的数据写入内存中;rowsum为累加指令,将结果写回目的寄存器。

RISC-V架构中的汇编代码中用户自定义指令需要通过伪指令.insn来实现,
对于R类型指令,“.insn”的使用格式如下:
.insn r opcode, func3, func7, rd, rs1, rs2

(1):三个可扩展指令都使用custom3,因此opcode为0x7b;
(2):接着的func3,最低位func3[0]表示读取rs2的值;func3[1]表示读取rs1的值;func3[2]表示需要写回目的寄存器;
(3):func7指示同一个custom的不同指令类型,可以实现2的七次方的不同指令的组合

lbuf 指令格式

__STATIC_FORCEINLINE void custom_lbuf(unsigned long* addr)
{

int zero = 0;
asm volatile(“.insn r 0x7b, 2, 1, x0, %1, x0” : “=r”(zero) : “r”(addr));
输入的addr即为数据在内存中的地址,把的地址通过rs1加载到寄存器,编译器会自动分配rs1用哪一个寄存器
}

sbuf指令格式

__STATIC_FORCEINLINE void custom_sbuf(unsigned long* addr)
{

int zero = 0;
asm volatile(“.insn r 0x7b, 2, 2, x0, %1, x0” : “=r”(zero) : “r”(addr));
与上一个类似
}

rowsum指令格式

__STATIC_FORCEINLINE int custom_rowsum(unsigned long* addr)
{

int rowsum;
asm volatile(“.insn r 0x7b, 6, 6, %0, %1, x0” : “=r”(rowsum) : “r”(addr));
return rowsum; //对于需要写回的指令,需要有返回值
}

demo_nice代码解读

通过调用汇编的形式使用汇编

void nice_case(unsigned int array[ROW_LEN][COL_LEN], unsigned int col_sum[COL_LEN], unsigned int row_sum[ROW_LEN])
{
int i;
//将init_buf数组的每一行元素首个地址分配到寄存器中
unsigned int init_buf[COL_LEN] = {0,0,0};

通过读取寄存器中的地址,将内存中对应的数据读取到buf中。
通过*符号,将数组init_buf的首个元素的地址传递给custom_lbuf
custom_lbuf((unsigned long*)init_buf);

//把每一行元素的三个数据的累加结果写回目的寄存器。
//通过for循环三次,计算每行的求和结果

for (i = 0; i < ROW_LEN; i++) {
    row_sum[i] = custom_rowsum((unsigned long*)array[i]);
}
//将计算出的结果储存到rs1对应寄存器值所表示的地址中
custom_sbuf((unsigned long*)col_sum);

}
通过使用“输出操作数”和“输入操作数”部分的指定,可以
将 C/C++中的变量或者表达式映射到汇编指令中充当操作数。在此过程中,程序员无须关心真正执行的汇编指令具体使用的寄存器索引(譬如到底是 x1 还是 x2 等),编译器会根据引号中指定的操作数约束,按照编译优化的原则来分配合理的寄存器索引号。程序员仅需要关心操作数和变量的映射,无须关心操作数会映射到处理器具体的哪个通用寄存器,这样能使软件程序员能够从底层硬件的细节中解放出来。

喜欢5
用户评论
星河

星河 实名认证

懒的都不写签名

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