要实现协处理器的设计,必然会涉及到新的指令。比如蜂鸟书籍《手把手教你设计CPU——RISC-V处理器篇》[1]第十六章中实现3*3矩阵的行列和运算时就定义了三条指令,分别是载入数组、存储数组以及求行列和指令。下面我将通过一个简单的例子,实现如下功能:
详见书籍《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 |
* |-----------------------------------------------------|
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;
}
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;
}
//求平方指令
`define INST_SQURE3'b110
//在其它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
//在其它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] 《手把手教你设计CPU——RISC-V处理器篇》,胡振波著
[2] https://www.riscv-mcu.com/column-topic-id-494.html