自学内容网 自学内容网

Chisel芯片开发入门系列 -- 16. CPU芯片开发和解释5(指令序列的构造和测试)

上篇已经看到了RISC-V的一整套指令集的Chisel代码实现,本文针对这一套RISC-V CPU指令集的Chisel代码实现,构造指令序列(也就是CPU上的用户程序!!!)进行测试。这需要对指令集格式有细致的了解才能构造出来,所参考的是RISC-V的32位基础指令集RV32I。构造出来的代码,还需要在代表CPU芯片的Chisel代码上进行运行及调试。

RISC-V的基础指令集是32位的基础指令集RV32I。这个基础指令集包括的指令条数为47,包括:

--移位(Shifts)指令6条。累计6。

--算术运算(Arithmetic)指令5条。累计11。

--逻辑运算(Logic)指令6条。累计17。

--比较(Compare)指令4条。累计21。

--条件跳转(Branches)指令6条。累计27。

--无条件跳转(Jump)指令2条。累计29。

--同步(Synch)指令2条。累计31。

--环境(Environment)指令2条。累计33。

--控制和状态寄存器(CSR)指令6条。累计39。

--内存读写(LoadStore)指令8条。累计47。

这些指令集有6种格式,如下所示。每一条指令使用哪一种格式,如上表中的Fmt列。比如Store指令(3个变种)采用S-type的格式,Load指令(5个变种)采用I-type的格式。

本系列的前面的文章着重介绍了Load/Store指令(实际上只介绍了它们中的LW/SW两条指令),以及ADD指令作为理解“计算”的代表。重点把这3条指令看一下。

其中ADD指令是一个R-Type格式的指令,这种格式的指令还有其他几个,汇总列表如下:

----RV32I defines several arithmetic R-type operations. All operations read the rs1 and rs2 registers  as source operands and write the result into register rd. The funct7 and funct3 fields select the type of operation

----ADD performs the addition of rs1 and rs2.

----SUB performs the subtraction of rs2 from rs1.

----SLT and SLTU perform signed and unsigned compares respectively, writing 1 to rd if rs1<rs2, 0 otherwise.

----AND, OR, and XOR perform bitwise logical operations.

----SLL, SRL, and SRA perform logical left, logical right, and arithmetic right shifts on the value in  register rs1 by the shift amount held in the lower 5 bits of register rs2.

而Load/Store指令分别是I-Type和S-Type,如下:

----Load and store instructions transfer a value between the registers and memory. Loads are encoded in the I-type format and stores are S-type.

----The effective memory address is obtained by adding register rs1 to the sign-extended 12-bit offset. //内存地址的计算(不管是Load还是Store),都是用rs1 + imm_s_ext。这正是我们前面看到的操作。rs1被称为base而imm则被称为offset,它们共同构成memory address。

----Loads copy a value from memory to register rd.

----Stores copy the value in register rs2 to memory.

基于上面介绍的R-type、I-Type、S-Type类型的指令,设计一个测试例20250731-1。

测试例的C代码(仅看逻辑):

int main()

{

    int counter = 0;

    counter += 11;

    counter += 22;

    counter += 33;

    counter += 44;

    counter += 55;

}

测试例的RISC-V机器码:

DMmem(Data, 共6个Words/24个字节):

0, 11, 22, 33, 44, 55  //存放在Memory.scala中的mem变量的地址96/100/104/108/112/116处!!这个示例中并没有真正物理上区分开的IMem和DMem!在Core.scala文件中,regfile(24) = 96就是设置base=96,相应地在Load指令中使用的base=24,再加上offset=0/4/8/12/16/20!见下面。
 

IMem(Instructions,共12条指令/48个字节) : 

//从hex文件读取后存放在Memory.scala中的mem变量的0/4/8等地址处!!

//所有这些指令的构造,都是对照上面的指令格式完成的!需要细心和耐心。

//参照1:val LW      = BitPat("b?????????????????010?????0000011")

//参照2:val ADD     = BitPat("b0000000??????????000?????0110011")

//参照3:val SW      = BitPat("b?????????????????010?????0100011")

//参照4:LW/ADD/SW的其他各字段的填写,参考上面的格式图片!

Load Word (offset = 0 * 4, base = 24, dest = regfile04) //0x000, 0xC2,  0x2, 0x03

Load Word (offset = 1 * 4, base = 24, dest = regfile06) //0x004, 0xC2,  0x3, 0x03

Load Word (offset = 2 * 4, base = 24, dest = regfile08) //0x008, 0xC2,  0x4, 0x03

Load Word (offset = 3 * 4, base = 24, dest = regfile10) //0x00C, 0xC2,  0x5, 0x03

Load Word (offset = 4 * 4, base = 24, dest = regfile12) //0x010, 0xC2,  0x6, 0x03

Load Word (offset = 5 * 4, base = 24, dest = regfile14) //0x014, 0xC2,  0x7, 0x03

ADD(src2 = regfile06, src1 = regfile04, dst = regfile04) //0x00, 0x6, 0x2, 0x0, 0x2, 0x33

ADD(src2 = regfile08, src1 = regfile04, dst = regfile04) //0x00, 0x8, 0x2, 0x0, 0x2, 0x33

ADD(src2 = regfile10, src1 = regfile04, dst = regfile04) //0x00, 0xA, 0x2, 0x0, 0x2, 0x33

ADD(src2 = regfile12, src1 = regfile04, dst = regfile04) //0x00, 0xC, 0x2, 0x0, 0x2, 0x33

ADD(src2 = regfile14, src1 = regfile04, dst = regfile04) //0x00, 0xE, 0x2, 0x0, 0x2, 0x33

Store Word(offset = 16 * 4, src = regfile04, base = 0) //0x04, 0x4, 0x0, 0x2, 0x0, 0x23

修改的文件列表如下:

$ git status
On branch master
Your branch is up to date with 'origin/master'.

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   src/hex/rv32ui-p-sw.hex
        modified:   src/main/scala/05_riscvtests/Core.scala
        modified:   src/main/scala/05_riscvtests/Memory.scala
        modified:   src/main/scala/common/Consts.scala
 

将Dmem的内容体现在Memory.scala源代码文件中。如下:

$ git diff src/main/scala/05_riscvtests/Memory.scala
diff --git a/chisel-template/src/main/scala/05_riscvtests/Memory.scala b/chisel-template/src/main/scala/05_riscvtests/Memory.scala
index 150b62b..6394382 100644
--- a/chisel-template/src/main/scala/05_riscvtests/Memory.scala
+++ b/chisel-template/src/main/scala/05_riscvtests/Memory.scala
@@ -34,6 +34,12 @@ class Memory extends Module {
     mem(index) := lineBits
     //chisel3.printf(p"fromFile: Index: ${idxBits}, Content: ${lineBits}, mem: ${mem(index)}\n")
   }
+  mem(96.U) := 0.U(WORD_LEN.W)
+  mem(100.U) := 11.U(WORD_LEN.W)
+  mem(104.U) := 22.U(WORD_LEN.W)
+  mem(108.U) := 33.U(WORD_LEN.W)
+  mem(112.U) := 44.U(WORD_LEN.W)
+  mem(116.U) := 55.U(WORD_LEN.W)

   io.imem.inst := Cat(
     mem(io.imem.addr + 3.U(WORD_LEN.W)),

将IMem的内容体现在riscv.hex文件中。如下:

$ cat src/hex/rv32ui-p-sw.hex
03
22
0C
00
03
23
4C
00
03
24
8C
00
03
25
CC
00
03
26
0C
01
03
27
4C
01
33
02
62
00
33
02
82
00
33
02
A2
00
33
02
C2
00
33
02
E2
00
23
20
40
04

运行结果如下(验证了Load/Add/Store这一连贯的指令执行):

$ sbt "testOnly riscvtests.RiscvTest"
io.imem.addr=0x00000000,  io.imem.inst=0x000c2203, io.dmem.addr=0x00000060, io.dmem.rdata=0x00000000.
******** op1_sel =  0, op1_data =          96 !!!
******** op2_sel =  2, op2_data =           0 !!!
******** exe_fun =   1, alu_out =          96 !!!
LW/ADD WriteBack: wb_addr =   4, regfile(wb_addr) =           0 !!!
io.pc      : 0x00000000
inst       : 0x000c2203,  inst_code:           1 (01 LW, 02 SW, 03 ADD)!
gp         :           1
rs1_addr(ADD.src1)(LW.base)(SW.base)     :  24
rs2_addr(ADD.src2)(LW.offset)(SW.src)    :   0
wb_addr(ADD.dest)(LW.dest)(SW.offset)    :   4
rs1_data   : 0x00000060
rs2_data   : 0x00000000
wb_data    : 0x00000000
dmem.addr  :          96
dmem.rdata :           0
**** regfiles:           0           0           0           0           0           0 ****
---------END

io.imem.addr=0x00000004,  io.imem.inst=0x004c2303, io.dmem.addr=0x00000064, io.dmem.rdata=0x0000000b.
******** op1_sel =  0, op1_data =          96 !!!
******** op2_sel =  2, op2_data =           4 !!!
******** exe_fun =   1, alu_out =         100 !!!
LW/ADD WriteBack: wb_addr =   6, regfile(wb_addr) =          11 !!!
io.pc      : 0x00000004
inst       : 0x004c2303,  inst_code:           1 (01 LW, 02 SW, 03 ADD)!
gp         :           1
rs1_addr(ADD.src1)(LW.base)(SW.base)     :  24
rs2_addr(ADD.src2)(LW.offset)(SW.src)    :   4
wb_addr(ADD.dest)(LW.dest)(SW.offset)    :   6
rs1_data   : 0x00000060
rs2_data   : 0x00000000
wb_data    : 0x0000000b
dmem.addr  :         100
dmem.rdata :          11
**** regfiles:           0           0           0           0           0           0 ****
---------END

io.imem.addr=0x00000008,  io.imem.inst=0x008c2403, io.dmem.addr=0x00000068, io.dmem.rdata=0x00000016.
******** op1_sel =  0, op1_data =          96 !!!
******** op2_sel =  2, op2_data =           8 !!!
******** exe_fun =   1, alu_out =         104 !!!
LW/ADD WriteBack: wb_addr =   8, regfile(wb_addr) =          22 !!!
io.pc      : 0x00000008
inst       : 0x008c2403,  inst_code:           1 (01 LW, 02 SW, 03 ADD)!
gp         :           1
rs1_addr(ADD.src1)(LW.base)(SW.base)     :  24
rs2_addr(ADD.src2)(LW.offset)(SW.src)    :   8
wb_addr(ADD.dest)(LW.dest)(SW.offset)    :   8
rs1_data   : 0x00000060
rs2_data   : 0x00000000
wb_data    : 0x00000016
dmem.addr  :         104
dmem.rdata :          22
**** regfiles:           0          11           0           0           0           0 ****
---------END

io.imem.addr=0x0000000c,  io.imem.inst=0x00cc2503, io.dmem.addr=0x0000006c, io.dmem.rdata=0x00000021.
******** op1_sel =  0, op1_data =          96 !!!
******** op2_sel =  2, op2_data =          12 !!!
******** exe_fun =   1, alu_out =         108 !!!
LW/ADD WriteBack: wb_addr =  10, regfile(wb_addr) =          33 !!!
io.pc      : 0x0000000c
inst       : 0x00cc2503,  inst_code:           1 (01 LW, 02 SW, 03 ADD)!
gp         :           1
rs1_addr(ADD.src1)(LW.base)(SW.base)     :  24
rs2_addr(ADD.src2)(LW.offset)(SW.src)    :  12
wb_addr(ADD.dest)(LW.dest)(SW.offset)    :  10
rs1_data   : 0x00000060
rs2_data   : 0x00000000
wb_data    : 0x00000021
dmem.addr  :         108
dmem.rdata :          33
**** regfiles:           0          11          22           0           0           0 ****
---------END

io.imem.addr=0x00000010,  io.imem.inst=0x010c2603, io.dmem.addr=0x00000070, io.dmem.rdata=0x0000002c.
******** op1_sel =  0, op1_data =          96 !!!
******** op2_sel =  2, op2_data =          16 !!!
******** exe_fun =   1, alu_out =         112 !!!
LW/ADD WriteBack: wb_addr =  12, regfile(wb_addr) =          44 !!!
io.pc      : 0x00000010
inst       : 0x010c2603,  inst_code:           1 (01 LW, 02 SW, 03 ADD)!
gp         :           1
rs1_addr(ADD.src1)(LW.base)(SW.base)     :  24
rs2_addr(ADD.src2)(LW.offset)(SW.src)    :  16
wb_addr(ADD.dest)(LW.dest)(SW.offset)    :  12
rs1_data   : 0x00000060
rs2_data   : 0x00000000
wb_data    : 0x0000002c
dmem.addr  :         112
dmem.rdata :          44
**** regfiles:           0          11          22          33           0           0 ****
---------END

io.imem.addr=0x00000014,  io.imem.inst=0x014c2703, io.dmem.addr=0x00000074, io.dmem.rdata=0x00000037.
******** op1_sel =  0, op1_data =          96 !!!
******** op2_sel =  2, op2_data =          20 !!!
******** exe_fun =   1, alu_out =         116 !!!
LW/ADD WriteBack: wb_addr =  14, regfile(wb_addr) =          55 !!!
io.pc      : 0x00000014
inst       : 0x014c2703,  inst_code:           1 (01 LW, 02 SW, 03 ADD)!
gp         :           1
rs1_addr(ADD.src1)(LW.base)(SW.base)     :  24
rs2_addr(ADD.src2)(LW.offset)(SW.src)    :  20
wb_addr(ADD.dest)(LW.dest)(SW.offset)    :  14
rs1_data   : 0x00000060
rs2_data   : 0x00000000
wb_data    : 0x00000037
dmem.addr  :         116
dmem.rdata :          55
**** regfiles:           0          11          22          33          44           0 ****
---------END

io.imem.addr=0x00000018,  io.imem.inst=0x00620233, io.dmem.addr=0x0000000b, io.dmem.rdata=0xcc250300.
******** op1_sel =  0, op1_data =           0 !!!
******** op2_sel =  1, op2_data =          11 !!!
******** exe_fun =   1, alu_out =          11 !!!
LW/ADD WriteBack: wb_addr =   4, regfile(wb_addr) =          11 !!!
io.pc      : 0x00000018
inst       : 0x00620233,  inst_code:           3 (01 LW, 02 SW, 03 ADD)!
gp         :           1
rs1_addr(ADD.src1)(LW.base)(SW.base)     :   4
rs2_addr(ADD.src2)(LW.offset)(SW.src)    :   6
wb_addr(ADD.dest)(LW.dest)(SW.offset)    :   4
rs1_data   : 0x00000000
rs2_data   : 0x0000000b
wb_data    : 0x0000000b
dmem.addr  :          11
dmem.rdata :  3424977664
**** regfiles:           0          11          22          33          44          55 ****
---------END

io.imem.addr=0x0000001c,  io.imem.inst=0x00820233, io.dmem.addr=0x00000021, io.dmem.rdata=0x3300a202.
******** op1_sel =  0, op1_data =          11 !!!
******** op2_sel =  1, op2_data =          22 !!!
******** exe_fun =   1, alu_out =          33 !!!
LW/ADD WriteBack: wb_addr =   4, regfile(wb_addr) =          33 !!!
io.pc      : 0x0000001c
inst       : 0x00820233,  inst_code:           3 (01 LW, 02 SW, 03 ADD)!
gp         :           1
rs1_addr(ADD.src1)(LW.base)(SW.base)     :   4
rs2_addr(ADD.src2)(LW.offset)(SW.src)    :   8
wb_addr(ADD.dest)(LW.dest)(SW.offset)    :   4
rs1_data   : 0x0000000b
rs2_data   : 0x00000016
wb_data    : 0x00000021
dmem.addr  :          33
dmem.rdata :   855679490
**** regfiles:          11          11          22          33          44          55 ****
---------END

io.imem.addr=0x00000020,  io.imem.inst=0x00a20233, io.dmem.addr=0x00000042, io.dmem.rdata=0x00000000.
******** op1_sel =  0, op1_data =          33 !!!
******** op2_sel =  1, op2_data =          33 !!!
******** exe_fun =   1, alu_out =          66 !!!
LW/ADD WriteBack: wb_addr =   4, regfile(wb_addr) =          66 !!!
io.pc      : 0x00000020
inst       : 0x00a20233,  inst_code:           3 (01 LW, 02 SW, 03 ADD)!
gp         :           1
rs1_addr(ADD.src1)(LW.base)(SW.base)     :   4
rs2_addr(ADD.src2)(LW.offset)(SW.src)    :  10
wb_addr(ADD.dest)(LW.dest)(SW.offset)    :   4
rs1_data   : 0x00000021
rs2_data   : 0x00000021
wb_data    : 0x00000042
dmem.addr  :          66
dmem.rdata :           0
**** regfiles:          33          11          22          33          44          55 ****
---------END

io.imem.addr=0x00000024,  io.imem.inst=0x00c20233, io.dmem.addr=0x0000006e, io.dmem.rdata=0x002c0000.
******** op1_sel =  0, op1_data =          66 !!!
******** op2_sel =  1, op2_data =          44 !!!
******** exe_fun =   1, alu_out =         110 !!!
LW/ADD WriteBack: wb_addr =   4, regfile(wb_addr) =         110 !!!
io.pc      : 0x00000024
inst       : 0x00c20233,  inst_code:           3 (01 LW, 02 SW, 03 ADD)!
gp         :           1
rs1_addr(ADD.src1)(LW.base)(SW.base)     :   4
rs2_addr(ADD.src2)(LW.offset)(SW.src)    :  12
wb_addr(ADD.dest)(LW.dest)(SW.offset)    :   4
rs1_data   : 0x00000042
rs2_data   : 0x0000002c
wb_data    : 0x0000006e
dmem.addr  :         110
dmem.rdata :     2883584
**** regfiles:          66          11          22          33          44          55 ****
---------END

io.imem.addr=0x00000028,  io.imem.inst=0x00e20233, io.dmem.addr=0x000000a5, io.dmem.rdata=0x00000000.
******** op1_sel =  0, op1_data =         110 !!!
******** op2_sel =  1, op2_data =          55 !!!
******** exe_fun =   1, alu_out =         165 !!!
LW/ADD WriteBack: wb_addr =   4, regfile(wb_addr) =         165 !!!
io.pc      : 0x00000028
inst       : 0x00e20233,  inst_code:           3 (01 LW, 02 SW, 03 ADD)!
gp         :           1
rs1_addr(ADD.src1)(LW.base)(SW.base)     :   4
rs2_addr(ADD.src2)(LW.offset)(SW.src)    :  14
wb_addr(ADD.dest)(LW.dest)(SW.offset)    :   4
rs1_data   : 0x0000006e
rs2_data   : 0x00000037
wb_data    : 0x000000a5
dmem.addr  :         165
dmem.rdata :           0
**** regfiles:         110          11          22          33          44          55 ****
---------END

io.imem.addr=0x0000002c,  io.imem.inst=0x04402023, io.dmem.addr=0x00000040, io.dmem.rdata=0x00000000.
io.dmem.addr=0x00000040, io.dmem.wdata=0x000000a5.
******** op1_sel =  0, op1_data =           0 !!!
******** op2_sel =  3, op2_data =          64 !!!
******** exe_fun =   1, alu_out =          64 !!!
Store(SW) wen/addr/wdata =          64  1         165!!!
io.pc      : 0x0000002c
inst       : 0x04402023,  inst_code:           2 (01 LW, 02 SW, 03 ADD)!
gp         :           1
rs1_addr(ADD.src1)(LW.base)(SW.base)     :   0
rs2_addr(ADD.src2)(LW.offset)(SW.src)    :   4
wb_addr(ADD.dest)(LW.dest)(SW.offset)    :   0
rs1_data   : 0x00000000
rs2_data   : 0x000000a5
wb_data    : 0x00000040
dmem.addr  :          64
dmem.rdata :           0
**** regfiles:         165          11          22          33          44          55 ****
---------END

[info] RiscvTest:
[info] mycpu
[info] - should work through hex
[info] Run completed in 1 second, 719 milliseconds.
[info] Total number of tests run: 1
[info] Suites: completed 1, aborted 0
[info] Tests: succeeded 1, failed 0, canceled 0, ignored 0, pending 0
[info] All tests passed.
[success] Total time: 6 s, completed 2025骞?鏈?1鏃?22:07:40
[0J
 

除了上面列出的要点之外,Core.scala代码中增加了不少用于调试代码的地方,其中之一是这个指令字典加了一个inst_code的代码,如下红框中的代码,提取出每个指令的编号并赋值给inst_code变量,便于后续打印调试信息。

代码已经push到代码仓!提交记录如下:

ChipCamp-riscv-chisel-book提交记录20250731 完整跑通Load--Add--Store的指令序列

请熟练使用gitcode并关注本项目,实际操作才能真正入门。此外,本文中提到的RV32I基础指令集及相关文档,同样收集到了gitcode的项目中,项目地址如下:

项目首页 - riscv-isa-documents:RISC-V指令集架构文档 RISC-V ISA Document - GitCode


原文地址:https://blog.csdn.net/weixin_47996968/article/details/149807361

免责声明:本站文章内容转载自网络资源,如侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!