基于 FPGA 的单周期 CPU 设计与实现
设计单周期 CPU,支持下图中的所有指令
详细的项目需求和实现说明请参阅:
应教师要求,CPU 运行的测试程序不能为“指令覆盖型”(说人话就是不能太简单..)。于是重写了一个程序,实现了4个元素的冒泡排序,并附带自校验。下面介绍程序的运行原理。
RAM 布局:
| 地址范围 | 变量名 | 说明 |
|---|---|---|
| MEM[0x10] | N | 数组长度=4 |
| MEM[0x11] | checksum_before | 排序前求和 |
| MEM[0x12] | checksum_after | 排序后求和 |
| MEM[0x13] | i | 外层循环计数 |
| MEM[0x14] | j | 内层循环计数 |
| MEM[0x15] | limit | 内层循环上界=3-i |
| MEM[0x20..0x23] | A[0..3] | 数组元素 |
寄存器布局:
| 寄存器 | 用途及说明 |
|---|---|
| R0 | 基址 |
| R1 | 数组指针 ptr(指向 0x20 起) |
| R2 | 通用:N / i / j / a / checksum / 常量 |
| R3 | 通用:sum / b / 比较标志 / 临时混合值 |
- 阶段 1:在 RAM 写入 N 与 4 个待排序数:[7, -3, 12, 0]
- 写入 N=4
- 向 R2 写入 4
- 向 MEM[0x10](数组长度)写入 R2
- 写入数组元素
- 向 R3 写入 7
- 向 MEM[0x20](数组元素 A[0])写入 R3
- 向 R3 写入 -3
- 向 MEM[0x21](数组元素 A[1])写入 R3
- 向 R3 写入 12
- 向 MEM[0x22](数组元素 A[2])写入 R3
- 向 R3 写入 0
- 向 MEM[0x23](数组元素 A[3])写入 R3
- 【此时 A = [0x0007, 0xFFFD, 0x000C, 0x0000](即 [7, -3, 12, 0])】
- 写入 N=4
- 阶段 2:计算排序前的 checksum
- sum 清零
- 向 R3 写入 0
- 遍历 4 次累加
- 将 R1 置为 0x20(数组起始地址)
- 读取 A[0] 到 R2
- R3 加上 R2
- R1 加 1(指向下一个元素)
- 读取 A[1] 到 R2
- R3 加上 R2
- R1 加 1(指向下一个元素)
- 读取 A[2] 到 R2
- R3 加上 R2
- R1 加 1(指向下一个元素)
- 读取 A[3] 到 R2
- R3 加上 R2
- 保存 checksum_before
- 将 R3(checksum)写入 MEM[0x11]
- 【此时 checksum_before = 16】
- sum 清零
- 阶段 3:冒泡排序(按有符号升序排序)
- 初始化 i=0
- 向 R2 写入 0(i 初始化)
- 向 MEM[0x13] 写入 i
- 外层循环
- 读取 i 到 R3
- 向 R2 写入 3
- 若 i==3 跳到 0x33(51)(排序完成)
- 向 R2 写入 3
- R2 减 i
- 向 MEM[0x15] 写入 R2
- 初始化 j=0
- 向 R2 写入 0(j 初始化)
- 向 MEM[0x14] 写入 j
- 将 R1 置为 0x20(数组起始地址)
- 内层循环
- 读取 A[j] 到 R2
- 读取 A[j+1] 到 R3
- 若 A[j]>A[j+1] 跳到 0x27(39) 执行交换
- 跳过交换:无条件跳到 0x29(41)
- 交换 A[j] 与 A[j+1]
- 向 MEM[ptr] 写入 R3
- 向 MEM[ptr+1] 写入 R2
- 指针与计数推进
- R1 加 1(ptr++)
- 读取 j,j 加 1 后写回 MEM[0x14]
- 读取 limit,若 j != limit 跳回 0x23(35)
- 外层循环推进
- 读取 i,i 加 1 后写回 MEM[0x13]
- 无条件跳回 0x1A(26)
- 排序结果:A = [-3, 0, 7, 12]
- 初始化 i=0
- 阶段 4:计算排序后的 checksum_after
- 将 R1 置为 0x20(数组起始地址)
- sum2 清零(R3=0)
- 依次累加 A[0..3] 至 R3
- 将 checksum_after 写入 MEM[0x12]
- 阶段 5:校验 checksum 一致性
- 读取 checksum_before 与 checksum_after
- 若不相等跳转 FAIL
- 阶段 6:校验数组有序性(相邻元素比较)
- 依次读取 (A[0],A[1])、(A[1],A[2])、(A[2],A[3])
- 使用 slt 判断 b < a,则跳转 FAIL
- 阶段 7:混合指令覆盖(不影响结果,用于检测剩余指令是否能成功运作)
- 以 sum2 为输入,执行 and/andi/or/sllv/srlv/srav/sub
- 阶段 8:输出结果并停机
- PASS:输出 0x0AAA 后 halt
- FAIL:输出 0x0EEE 后 halt
本项目使用开源许可证 - 详见 LICENSE
