RISC-V扩展指令示例

自定义RISC-V扩展指令

要实现协处理器的设计,必然会涉及到新的指令。比如蜂鸟书籍《手把手教你设计CPU——RISC-V处理器篇》[1]第十六章中实现3*3矩阵的行列和运算时就定义了三条指令,分别是载入数组、存储数组以及求行列和指令。下面我将通过一个简单的例子,实现如下功能:

  1. 在C语言中嵌入汇编代码;
  2. 自定义RISC-V扩展指令,以求平方指令为例[2];
  3. 修改tinyriscv代码,增加求平方功能。
  4. 说明如何判断结果是否正确。

一、在C语言中嵌入汇编代码格式

​ 详见书籍《RISC-V架构与嵌入式开发快速入门》第十章第六节。

二、自定义RISC-V扩展指令

1、RISC-V指令集类型如下:

在这里插入图片描述

​ 求平方指令需要输入一个数,输出一个数,可使用R类型指令,将rs1对应寄存器值作为输入,rd寄存器的值作为输出,无需使用rs2,可将其值设为0。则该指令可在R类型指令中扩展,opcode不变,将func3设为110,将funct7设为0000110。其指令格式如下:

*     func7    rs2     rs1    func3     rd       opcode
* 31------25 24----19 18--15 14-12 11--------7 6--------0
* | 0000110 | 00000 | ***** | 110 |  *****    | 0110011 |
* |-----------------------------------------------------|
2、对应的c内联汇编代码如下:
asm volatile (
     ".insn r 0x33, 6, 6, %0, %1, x0"
        :"=r"(squre)
        :"r"(addr)
   );

将该汇编代码封装成函数,以便调用,提升可读性。其代码如下:

static int custom_squre(int addr)
{
  int squre;
  asm volatile (
     ".insn r 0x33, 6, 6, %0, %1, x0"
        :"=r"(squre)
        :"r"(addr)
   );
  return squre; 
}
3、在main函数中代码如下:
int main()
{
  int a = 3;
  int squre= 0;
  squre= custom_squre(a);

  if (a*a == squre)
     set_test_pass();
  else
     set_test_fail();
  return 0;
}

三、修改tinyriscv代码

1、在defines.v文件中定义求平方指令,其func3部分为110.
//求平方指令
`define INST_SQURE3'b110
2、在id.v文件中对应的指令INST_TYPE_R_M分支条件下加入如下代码:
//在其它funct7代码块后面继续写
end else if (funct7 == 7'b0000110) begin
    case (funct3)
        `INST_SQURE: begin
            reg_we_o = `WriteEnable;
            reg_waddr_o = rd;
            reg1_raddr_o = rs1;
            reg2_raddr_o = rs2;
            op1_o = reg1_rdata_i;
            op2_o = reg2_rdata_i;
        end
        default: begin
            reg_we_o = `WriteDisable;
            reg_waddr_o = `ZeroReg;
            reg1_raddr_o = `ZeroReg;
            reg2_raddr_o = `ZeroReg;
        end
    endcase
3、在ex.v文件对应的指令分支中加入如下代码:
//在其它funct7代码块后面继续写
end else if (funct7 == 7'b000110) begin
    case (funct3)
        `INST_SQURE: begin
            $display("---------suqre----------");
            jump_flag = `JumpDisable;
            hold_flag = `HoldDisable;
            jump_addr = `ZeroWord;
            mem_wdata_o = `ZeroWord;
            mem_rdata_o = `ZeroWord;
            mem_waddr_o = `ZeroWord;
            mem_we = `WriteDisable;
            reg_data = op1_i * op1_i;
            $display("%x = %x * %x",reg_wdata, op1_i, op1_i);
        end
        default: begin
            jump_flag = `JumpDisable;
            hold_flag = `HoldDisable;
            jump_addr = `ZeroWord;
            mem_wdata_o = `ZeroWord;
            mem_raddr_o = `ZeroWord;
            mem_waddr_o = `ZeroWord;
            mem_we = `WriteDisnable;
            reg_wdata = `ZeroWord;
        end
    endcase

最后执行结果如下:

四、结果分析

​ 在main文件中include包含的文件tinyriscv-master\tests\example\include\utils.h中存在宏定义如下:

#ifdef SIMULATION
#define set_test_pass() asm("li x27, 0x01")
#define set_test_fail() asm("li x27, 0x00")
#endif

​ 若结果输出正确,则将x27寄存器的值设置为1,否则为0.而在tinyriscv的仿真文件 tinyriscv-master\tb\compliance_test\tinyriscv_soc_tb.v中有如下代码

	if (x27 == 32'b1) begin
		$display("~~~~~~~~~~~~~~~~~~~ TEST_PASS ~~~~~~~~~~~~~~~~~~~");
        $display("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
        $display("~~~~~~~~~ #####   ##   ####   #### ~~~~~~~~~");
        $display("~~~~~~~~~ #   #  #  #  #    #   ~~~~~~~~~");
        $display("~~~~~~~~~ #   #  #   #  ####   #### ~~~~~~~~~");
        $display("~~~~~~~~~ #####  ######    #    #~~~~~~~~~");
        $display("~~~~~~~~~ #      #   #  #   #  #   #~~~~~~~~~");
        $display("~~~~~~~~~ #     #   #  ####   #### ~~~~~~~~~");
        $display("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
	end else begin
        $display("~~~~~~~~~~~~~~~~~~~ TEST_FAIL ~~~~~~~~~~~~~~~~~~~~");
        $display("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
        $display("~~~~~~~~~~######   ##    #   #   ~~~~~~~~~~");
        $display("~~~~~~~~~~#     #  #    #   #   ~~~~~~~~~~");
        $display("~~~~~~~~~~#####  #   #   #   #   ~~~~~~~~~~");
        $display("~~~~~~~~~~#    ######   #   #   ~~~~~~~~~~");
        $display("~~~~~~~~~~#    #   #   #   #   ~~~~~~~~~~");
        $display("~~~~~~~~~~#    #   #   #   ######~~~~~~~~~~");
        $display("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
        $display("fail testnum = %2d", x3);
        for (r = 0; r < 32; r = r + 1)
			$display("x%2d = 0x%x", r, tinyriscv_soc_top_0.u_tinyriscv.u_regs.regs[r]);
		End

若x27寄存器的值为1,则打印pass,表明结果正确,否则打印打印fail和各个寄存器的值。

五、感受

  1. 在参考网址[2]的时候,刚开始没看清该扩展指令的opcode跟tinyriscv代码中定义的R类型,导致一直出现结果不正确。
  2. 在ex.v模块中调试的时候可以增加一些打印语句,方便查看中间信息。
  3. 在c文件中,调用函数式直接传入参数,否则出错。

参考资料:

[1] 《手把手教你设计CPU——RISC-V处理器篇》,胡振波著

[2] https://www.riscv-mcu.com/column-topic-id-494.html

  • 8
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

skyer_lhb

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值