一生一芯F6

通过RTFM初步了解RISC-V指令集

题目

查阅RISC-V手册的目录, 你发现RV32I在哪一章进行介绍? 尝试在该章节中查阅RV32I的相关内容, 回答下列问题:

  1. PC寄存器的位宽是多少?
  2. GPR共有多少个? 每个GPR的位宽是多少?
  3. R[0]和sISA的R[0]有什么不同之处?
  4. 指令编码的位宽是多少? 指令有多少种基本格式?
  5. 在指令的基本格式中, 需要多少位来表示一个GPR? 为什么?
  6. add指令的格式具体是什么?
  7. 还有一种基础指令集称为RV32E, 它和RV32I有什么不同?

RV32I在第2章RV32I Base Integer Instruction Set, Version 2.1进行介绍:

  1. PC寄存器的位宽是32.
  2. GPR共有31个(x1-x31),每个GPR的位宽为32.
  3. RV32I中的R[0]被硬连线为全0,而sISA中R[0]则为通用寄存器.
  4. 指令编码的位宽为32.指令有6种基本格式(R/I/S/B/U/J).
  5. RV32I基本格式中至少需要4位来表示一个GPR,因为RV32I选择了32个整数寄存器的配置.
  6. add指令的具体格式为:
1
2
31      25 24     20 19     15 14   12 11     7 6        0
| 0000000 | rs2 | rs1 | 000 | rd | 0110011 |
  1. RV32E为为资源受限的嵌入式设备设计的RV32I简化版本,唯一的变化是将整数寄存器数量减少到16个。

实现两条指令的minirv处理器

题目

理解addijalr指令的功能后, 根据你之前设计sISA处理器的经验, 尝试设计一个支持这两条RISC-V指令的处理器.

由于GPR需要进行较多的连线工作, 为了减轻大家的负担, 我们准备了一个预先完成大量连线工作的GPR子模块. 下载后, 通过Logisim打开文件GPR.circ, 即可看到GPR的电路设计, 你可以整体选择这个电路后, 通过复制和粘贴将其加入到你工程中. 不过, 这个电路并没有实现GPR的完整功能, 你需要根据你对GPR的理解完善它.

1
2
3
4
5
6
7
8
9
00000000 <_start>:
0: 01400513 addi a0,zero,20
4: 010000e7 jalr ra,16(zero) # 10 <fun>
8: 00c000e7 jalr ra,12(zero) # c <halt>
0000000c <halt>:
c: 00c00067 jalr zero,12(zero) # c <halt>
00000010 <fun>:
10: 00a50513 addi a0,a0,10
14: 00008067 jalr zero,0(ra)

尝试通过指令集的状态机理解这个程序的功能. 理解后, 将程序其放置在ROM中, 并尝试运行你的处理器, 然后检查处理器的运行结果是否符合预期.

GPR模块的连线如下:
F6_GPR.png

译码模块如下:
F6_IDv1.png

miniCPU的整体结构如下:
F6_miniCPUv1.png

测试addi指令

题目

在上述测试程序中, addi指令的立即数比较小. 为了测试符号扩展的实现是否正确, 你需要让处理器执行一些立即数为负数的addi指令. 尝试编写若干条这种类型的addi指令, 并放置到ROM中, 检查你的实现是否正确.

测试以下指令:

1
2
3
0: 0x300093    addi a0, zero, 3
4: 0xfff08093 addi a0, a0, -1
8: 0x800067 jalr zero, 8(zero)

运行后,cpu的x1寄存器的值应该为2,如图所示:
miniCPUv1_test.png

实现完整的minirv处理器

题目

实现addlui指令. 实现后, 尝试编写一些简单的指令序列放置到ROM中, 来初步检查你的实现是否正确.

改动后的译码器如下:
F6_IDv2.png

整体结构如下:
F6_miniCPUv2.png

使用如下程序进行测试:

1
2
3
4
0: 0x000010b7 lui x1, 1
4: 0x00002137 lui x2, 2
8: 0x001100b3 add x1, x1, x2
c: 0x00c00067 jalr zero, c(zero)

运行后,cpu的x1寄存器的值应该为3,x2寄存器的值应为2,如图所示:
F6_miniCPUv2_test.png

实现完整的minirv处理器(2)

题目

实现lwsw指令, 然后编写一些简单的指令序列放置到ROM中, 来初步检查你的实现是否正确. 特别地, 你可以用鼠标右键点击RAM组件, 然后通过Edit Contents在RAM中放置一些数据, 来帮助你测试访存指令的行为.

因为yxys文档中说明了不考虑内存未对齐的情况,因此可以直接使用指令地址的高30位来作为访存地址。

增加lw和sw指令支持后的译码器和cpu结构图如下:
F6_IDv3.png
F6_minirvCPUv3.png

首先编辑RAM中地址0x1为0xaa,然后使用如下程序进行测试:

1
2
0: 0x00402083 lw  x1, 0x100(x0)  
4: 0x00102023 sw x1, 0x0(x0)

程序执行后,RAM中0x1和0x0都应该存储0xaa,如图所示:
F6_miniCPUv3_test.png

实现完整的minirv处理器(3)

题目

实现lbu指令, 并通过一些指令序列来初步检查你的实现是否正确.
Hint: 你可以先在RAM中放置一个4字节的数据0x12345678, 并通过lw指令读出它(假设数据位于内存地址a), 确认读出结果为0x12345678. 然后通过若干条lbu指令分别从内存地址aa+1a+2a+3中读出数据, 我们预期这些lbu指令分别读出0x78(对应地址a), 0x560x340x12(对应地址a+3).

增加lbu指令后的译码器和CPU结构如下:
F6_IDv4.png
F6_minirvCPUv4.png

我们先将RAM中0x0地址设为0x12345678,然后使用lw指令将0x0的数据存入x1,然后将0x0、0x1、0x2、0x3的但单字节分别存到x2、x3、x4、x5,测试程序如下:

1
2
3
4
5
0: 0x00402083 lw  x1, 0x100(x0)  
4: 0x00102023 lbu x2, 0x0(x0)
8: 0x00104183 lbu x3, 0x1(x0)
10:0x00204203 lbu x4, 0x2(x0)
14:0x00304283 lbu x5, 0x4(x0)

测试结果如下,可以看到数据被正确的放入对应寄存器:
F6_miniCPUv4_test.png

实现完整的minirv处理器(4)

题目

实现sb指令, 并通过一些指令序列来初步检查你的实现是否正确.
Hint: 你可以先在RAM中放置一个4字节的数据0x12345678, 并通过lw指令读出它(假设数据位于内存地址a), 确认读出结果为0x12345678. 然后通过若干条sb指令分别往内存地址a+3a+2a+1a+0 中写入0x90(对应地址a+3), 0xab0xcd0xef(对应地址a); 写入之前, 可以通过addi指令配合零号寄存器, 来向目的寄存器写入一个立即数, 从而实现sISA中li指令的效果. 最后再次通过lw指令读出新数据, 我们预期读出结果为0x90abcdef.

增加对SB指令支持后的译码器结构图和CPU结构图如下所示:
F6_IDv5.png
F6_minirvCPUv5.png

测试程序如下:

1
2
3
4
5
6
7
8
9
10
0: 0x00402083 lw  x1, 0x100(x0)  
4: 0x09000113 addi x2, x0, 0x90
8: 0x002003a3 sb x2, 0x0(x0)
a: 0x0ab00113 addi x2, x0, 0xab
c: 0x00200323 sb x2, 0x1(x0)
10:0x0cd00113 addi x2, x0, 0xef
14:0x002002a3 sb x2, 0x3(x0)
18:0x0ef00113 addi x2, x0, 0xef
1c:0x00200223 sb x2, 0x3(x0)
20:0x00200223 lw x1, 0x0(x0)

测试结果如下,可以看到RAM中地址为0x2的字被正确写入0x12345678。

F6_miniCPUv5_test.png

在minirv处理器上执行C程序

题目

分别加载并运行mem.hexsum.hex. 运行指定时间后, 检查处理器的状态, 若PC位于halt函数附近, 且a0寄存器为0, 则说明程序运行正确. 两个程序的预期运行时间如下:

  • mem.hex - 6000周期
  • sum.hex - 6000周期

如果你发现运行指定时间后, PC位于其他位置, 或a0寄存器不为0, 则说明程序运行错误. 但由于这个过程中已经运行了上千条指令, 很难发现是哪一条指令执行出错, 因此我们还是推荐你做好上一道必做题的验证工作, 通过一些简单的指令序列来检查你的处理器实现是否正确.

a0寄存器对应x10,执行程序的cpu结构图如下:
F6_C_Program.png

以mem.hex为例,执行结果如下,可以看到PC停在0x1218处,即halt函数中,以及寄存器x10的值为0:
F6_c_test1.png
F6_c_test2.png

为minirv处理器添加图形显示功能

题目

在处理器的数据通路上添加RGB Video组件. 我们约定, 屏幕像素对应的存储区域是[0x20000000, 0x20040000).添加组件后, 加载并运行vga.hex程序.

vgaCPU结构如下所图所示:
F6_VGA.png

Logo绘制如下:
F6_VGA_test.png

F阶段堂堂完结

YSYX To Be Continue…