本人在22集创赛芯来riscv杯打酱油,分享下芯来官方的demo的理解
首先导入工程,打开helloworld的例程,将application的main.c删除,用nuclei-board-labs-master中的nice_demo中的.c进行替代
int main(void)
{
int i=100;
int arr[4]={1,2,3,4};
unsigned int array[ROW_LEN][COL_LEN]=
{{10,20,30},
{20,30,40},
{30,40,50}
};
unsigned int begin_instret, end_instret, instret_normal, instret_nice;
unsigned int begin_cycle, end_cycle, cycle_normal, cycle_nice;
//不使用nice核
begin_instret = __get_rv_instret();
begin_cycle = __get_rv_cycle();
normal_case(array);
end_instret = __get_rv_instret();
end_cycle = __get_rv_cycle();
instret_normal = end_instret - begin_instret;
cycle_normal = end_cycle - begin_cycle;
//调用nice核
begin_instret = __get_rv_instret();
begin_cycle = __get_rv_cycle();
nice_case(array);
end_instret = __get_rv_instret();
end_cycle = __get_rv_cycle();
instret_nice = end_instret - begin_instret;
cycle_nice = end_cycle - begin_cycle;
}
main函数首先初始化了一个矩阵,然后分别在使用和不使用nice核的情况下,对其进行了行、列求和,并计算了两次处理所使用的时间
这里重点关注使用nice核的情况
在insn.c中,nice_case函数部分代码如下。
int nice_case(unsigned int array[ROW_LEN][COL_LEN])
{
volatile unsigned char i, j;
volatile unsigned int col_sum[COL_LEN]={0};
volatile unsigned int row_sum[ROW_LEN]={0};
volatile unsigned int init_buf[3]={0};
custom_lbuf((int)init_buf);
for (i = 0; i < ROW_LEN; i++)
{
row_sum[i] = custom_rowsum((int)array[i]);
}
custom_sbuf((int)col_sum);
return 0;
}
首先是初始化了三个数组用于存储,然后依次执行custom_lbuf,custom_rowsum,custom_sbuf函数。这三个函数在insn.h中定义,分别实现了加载memory的数据到row_buf;实现累加值计算并写回结果;加载row_buf中的数据到内存。
custom_lbuf,custom_rowsum,custom_sbuf三个函数均使用的内联汇编的方法进行编写,利用e203指令保留的Custom实现了自定义nice(lbuf、sbuf、rowsum)指令。
NICE核(R-type)的指令形式如下,其中{xd,xs1,xs2}也可以为funct3
每个field对应的作用如下
下以custom_lbuf函数为例,简单介绍一些内联汇编的操作
__STATIC_FORCEINLINE void custom_lbuf(int addr)
{
int zero = 0;
asm volatile (
".insn r 0x7b, 2, 1, x0, %1, x0"
:"=r"(zero)
:"r"(addr)
);
}
__STATIC_FORCEINLINE是内联函数的关键字;
首先创建一个int类型的0,用于填充。
gcc内联汇编基本格式如下:
asm volatile ( 汇编指令列表 : 输出操作数 // 非必需 : 输入操作数 // 非必需 : 可能影响的寄存器或存储器 // 非必需 );
asm: 是GCC的关键字,表示“在C/C++代码中嵌入内联汇编程序”。
volatile:也是GCC的关键字,表示“不要对这段代码进行任何编译优化”,如果没有添加此关键字,GCC编译器可能将这段代码误优化掉。
.insn :这是GNU中的一个编译指示,意思是在当前位置插入一段**机器码。**每一个输入、输出操作数都由字符名、限制字符串、和C/C++表达式组成
:(冒号):第一个冒号后是输出操作数,第二个冒号后是输入操作数,第三个冒号(这里的代码省略没写)后声明在这段汇编代码中可能影响到的寄存器或者存储器。
“=r”、“r”、“m”:这里的r是指register寄存器,m是指memory内存。等号**=是对输出**操作数的约束,意思是输出的新值会替换旧值。
在 custom_lbuf中".insn r 0x7b, 2, 1, x0, %1, x0" r表示这条指令是R型指令。0x7b是指操作码opcode是1111011,表示这是custom3指令组。func3码(xd-xs1-xs2)是010,表示使用第一源寄存器。func7码是0000001。RD目标寄存器是x0。RS1是%1,即下文输入输出寄存器列表中的第二个寄存器。RS2是x0。
自定义的nice指令会经过如下过程再传给nice协处理器:
nice指令被经过主处理器decode之后,在EXU中对Opcode进行译码,发现其属于Custom指令组,继续根据指令编码的xs1于xs2判断是否需要读取寄存器。如需要读取,则在EXU级中读取regfile(通用寄存器)读出源操作数。最后在通过nice接口的请求通道派发给nice协处理器,包括指令的编码(funct3与funct7)、两个源操作数的值。
以上是本人对nice-demo的c语言的一些肤浅的理解,有误之处,望不吝赐教。
参考资料《手把手教你 RISC-V CPU工程与实践》



