栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

浅析nice

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

浅析nice

本人在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工程与实践》

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/928615.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号