mirror of
https://github.com/handsomezhuzhu/2025-yatcpu.git
synced 2026-02-20 20:10:14 +00:00
204 lines
8.4 KiB
TeX
204 lines
8.4 KiB
TeX
\section{测试文件}
|
||
\label{sec:testing}
|
||
|
||
CPUTest.scala 测试了整个 CPU 运行三个程序的正确性。本节选择其中两个程序的运行进行详细分析,查看 csrc 文件夹下对应 C 文件的内容,概述该程序做了什么,执行结果如何在 Chisel 测试中被检查;在测试波形图上简单分析其执行,并说明波形图最后几个周期检查执行结果时的波形。
|
||
|
||
\subsection{整体测试框架}
|
||
|
||
CPUTest.scala 定义了三个主要的测试类:FibonacciTest、QuicksortTest 和 ByteAccessTest。每个测试都基于 ChiselTest 框架,通过 TestTopModule 作为顶层模块来驱动 CPU 执行相应的程序。测试框架的核心结构如下:
|
||
|
||
\begin{lstlisting}[caption={CPUTest.scala 测试框架结构}, style=ScalaChiselStyle]
|
||
class TestTopModule(exeFilename: String) extends Module {
|
||
val io = IO(new Bundle {
|
||
val mem_debug_read_address = Input(UInt(Parameters.AddrWidth))
|
||
val regs_debug_read_address = Input(UInt(Parameters.PhysicalRegisterAddrWidth))
|
||
val regs_debug_read_data = Output(UInt(Parameters.DataWidth))
|
||
val mem_debug_read_data = Output(UInt(Parameters.DataWidth))
|
||
})
|
||
// 实例化内存、指令ROM、CPU等模块
|
||
// 提供调试接口用于验证执行结果
|
||
}
|
||
\end{lstlisting}
|
||
|
||
测试框架通过调试接口读取内存和寄存器的值,与预期结果进行比较,从而验证 CPU 执行程序的正确性。
|
||
|
||
\subsection{斐波那契数列计算测试}
|
||
|
||
\subsubsection{程序功能分析}
|
||
|
||
fibonacci.c 程序实现了一个递归计算斐波那契数列的功能。程序代码如下:
|
||
|
||
\begin{lstlisting}[language=C, caption={fibonacci.c 程序代码}, style=CStyle]
|
||
int fib(int a) {
|
||
if (a == 1 || a == 2) return 1;
|
||
return fib(a - 1) + fib(a - 2);
|
||
}
|
||
|
||
int main() {
|
||
*(int *)(4) = fib(10);
|
||
}
|
||
\end{lstlisting}
|
||
|
||
该程序通过递归算法计算斐波那契数列的第 10 项(fib(10)),并将结果存储到内存地址 0x4 处。斐波那契数列的递归定义为:
|
||
\begin{itemize}
|
||
\item fib(1) = 1
|
||
\item fib(2) = 1
|
||
\item fib(n) = fib(n-1) + fib(n-2) (n > 2)
|
||
\end{itemize}
|
||
|
||
因此,fib(10) 的计算结果为 55。
|
||
|
||
\subsubsection{测试结果检查}
|
||
|
||
在 FibonacciTest 中,测试代码通过以下方式检查结果:
|
||
|
||
\begin{lstlisting}[caption={斐波那契测试的结果检查}, style=ScalaChiselStyle]
|
||
c.io.mem_debug_read_address.poke(4.U)
|
||
c.clock.step()
|
||
c.io.mem_debug_read_data.expect(55.U)
|
||
\end{lstlisting}
|
||
|
||
测试首先执行 50,000 个时钟周期(50 × 1000),确保递归计算完成。然后通过内存调试接口读取地址 0x4 的值,验证是否为预期的 55。
|
||
|
||
\subsubsection{波形图分析}
|
||
|
||
基于 VCD 文件分析,斐波那契程序执行过程中的关键信号包括:
|
||
|
||
\begin{itemize}
|
||
\item \texttt{cpu\_io\_instruction\_address}:程序计数器值,显示指令执行流程
|
||
\item \texttt{mem\_io\_bundle\_write\_enable}:内存写使能信号,标识数据写入操作
|
||
\item \texttt{mem\_io\_bundle\_address}:内存访问地址
|
||
\item \texttt{mem\_io\_bundle\_write\_data}:要写入内存的数据
|
||
\item \texttt{io\_mem\_debug\_read\_data}:调试接口读取的内存数据
|
||
\end{itemize}
|
||
|
||
在程序执行的最后阶段,可以观察到:
|
||
\begin{enumerate}
|
||
\item \texttt{mem\_io\_bundle\_write\_enable} 信号置高,表示正在进行内存写入
|
||
\item \texttt{mem\_io\_bundle\_address} 显示为 0x00000004,写入地址为 4
|
||
\item \texttt{mem\_io\_bundle\_write\_data} 显示为 0x00000037(十进制 55),即 fib(10) 的计算结果
|
||
\item 调试接口 \texttt{io\_mem\_debug\_read\_data} 最终稳定显示 0x00000037
|
||
\end{enumerate}
|
||
|
||
\subsection{快速排序算法测试}
|
||
|
||
\subsubsection{程序功能分析}
|
||
|
||
quicksort.c 程序实现了快速排序算法,对 10 个整数进行排序:
|
||
|
||
\begin{lstlisting}[language=C, caption={quicksort.c 程序代码}, style=CStyle]
|
||
void quicksort(int *arr, int l, int r) {
|
||
if (l >= r) return;
|
||
int pivot = arr[l];
|
||
int i = l, j = r;
|
||
while (i < j) {
|
||
while(arr[j] >= pivot && i < j) --j;
|
||
arr[i] = arr[j];
|
||
while(arr[i] < pivot && i < j) ++i;
|
||
arr[j] = arr[i];
|
||
}
|
||
arr[i] = pivot;
|
||
quicksort(arr, l, i - 1);
|
||
quicksort(arr, i + 1, r);
|
||
}
|
||
|
||
int main() {
|
||
int nums[10];
|
||
// 初始化数组:[6, 2, 4, 5, 3, 1, 0, 9, 7, 8]
|
||
nums[0] = 6; nums[1] = 2; nums[2] = 4; nums[3] = 5;
|
||
nums[4] = 3; nums[5] = 1; nums[6] = 0; nums[7] = 9;
|
||
nums[8] = 7; nums[9] = 8;
|
||
|
||
quicksort(nums, 0, 9);
|
||
|
||
// 将排序结果写入内存地址 4, 8, 12, ..., 40
|
||
for (int i = 1; i <= 10; ++i) {
|
||
*(int *)(i * 4) = nums[i - 1];
|
||
}
|
||
}
|
||
\end{lstlisting}
|
||
|
||
程序将初始数组 [6, 2, 4, 5, 3, 1, 0, 9, 7, 8] 排序为 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],然后将结果依次写入内存地址 4, 8, 12, ..., 40。
|
||
|
||
\subsubsection{测试结果检查}
|
||
|
||
在 QuicksortTest 中,测试代码验证排序结果:
|
||
|
||
\begin{lstlisting}[caption={快速排序测试的结果检查}, style=ScalaChiselStyle]
|
||
for (i <- 1 to 10) {
|
||
c.io.mem_debug_read_address.poke((4 * i).U)
|
||
c.clock.step()
|
||
c.io.mem_debug_read_data.expect((i - 1).U)
|
||
}
|
||
\end{lstlisting}
|
||
|
||
测试循环读取内存地址 4, 8, 12, ..., 40 的值,期望得到 0, 1, 2, ..., 9,验证排序的正确性。
|
||
|
||
\subsubsection{波形图分析}
|
||
|
||
快速排序程序执行过程中的关键信号变化:
|
||
\begin{itemize}
|
||
\item 多次内存写入操作,\texttt{mem\_io\_bundle\_write\_enable} 信号周期性置高
|
||
\item \texttt{mem\_io\_bundle\_address} 依次显示 0x00000004, 0x00000008, ..., 0x00000028
|
||
\item \texttt{mem\_io\_bundle\_write\_data} 依次显示 0x00000000, 0x00000001, ..., 0x00000009
|
||
\item 调试接口验证阶段,\texttt{io\_mem\_debug\_read\_data} 依次显示正确的排序值
|
||
\end{itemize}
|
||
|
||
\subsection{字节访问测试}
|
||
|
||
\subsubsection{程序功能分析}
|
||
|
||
ByteAccessTest 测试 CPU 对单个字节的存储和加载功能,验证字节级内存访问的正确性。该测试使用 sb.asmbin 程序,主要测试 Store Byte (sb) 和 Load Byte (lb) 指令。
|
||
|
||
\subsubsection{测试结果检查}
|
||
|
||
测试通过寄存器调试接口验证字节操作的正确性:
|
||
|
||
\begin{lstlisting}[caption={字节访问测试的结果检查}, style=ScalaChiselStyle]
|
||
c.io.regs_debug_read_address.poke(5.U)
|
||
c.io.regs_debug_read_data.expect(0xDEADBEEFL.U)
|
||
c.io.regs_debug_read_address.poke(6.U)
|
||
c.io.regs_debug_read_data.expect(0xEF.U)
|
||
c.io.regs_debug_read_address.poke(1.U)
|
||
c.io.regs_debug_read_data.expect(0x15EF.U)
|
||
\end{lstlisting}
|
||
|
||
测试验证了字节存储和加载操作对寄存器值的影响,确保字节级内存访问的正确实现。
|
||
|
||
\subsection{波形图截取建议}
|
||
|
||
基于 VCD 文件分析,建议截取以下关键波形图:
|
||
|
||
\begin{enumerate}
|
||
\item \textbf{斐波那契程序执行结果写入阶段}
|
||
\begin{itemize}
|
||
\item 信号:\texttt{mem\_io\_bundle\_write\_enable}, \texttt{mem\_io\_bundle\_address}, \texttt{mem\_io\_bundle\_write\_data}
|
||
\item 时间点:程序最后几个周期,fib(10) 结果写入内存地址 0x4
|
||
\item 特征:写使能置高,地址为 0x00000004,数据为 0x00000037
|
||
\end{itemize}
|
||
|
||
\item \textbf{快速排序程序内存写入序列}
|
||
\begin{itemize}
|
||
\item 信号:\texttt{mem\_io\_bundle\_write\_enable}, \texttt{mem\_io\_bundle\_address}, \texttt{mem\_io\_bundle\_write\_data}
|
||
\item 时间点:排序完成后,结果依次写入内存的阶段
|
||
\item 特征:连续的写操作,地址递增,数据为排序后的序列 0-9
|
||
\end{itemize}
|
||
|
||
\item \textbf{调试接口读取阶段}
|
||
\begin{itemize}
|
||
\item 信号:\texttt{io\_mem\_debug\_read\_address}, \texttt{io\_mem\_debug\_read\_data}
|
||
\item 时间点:测试的最后阶段,验证执行结果
|
||
\item 特征:调试地址变化,对应数据正确显示
|
||
\end{itemize}
|
||
\end{enumerate}
|
||
|
||
\subsection{测试总结}
|
||
|
||
CPUTest.scala 通过三个不同复杂度的测试程序全面验证了单周期 CPU 的功能:
|
||
\begin{itemize}
|
||
\item \textbf{斐波那契测试}验证了 CPU 的递归函数调用能力,包括栈操作、参数传递和返回值处理
|
||
\item \textbf{快速排序测试}验证了 CPU 的数组处理、循环控制和复杂算法执行能力
|
||
\item \textbf{字节访问测试}验证了 CPU 对不同类型内存访问指令的支持
|
||
\end{itemize}
|
||
|
||
所有测试都通过内存和寄存器调试接口验证执行结果,确保 CPU 能够正确执行各种程序。波形图分析显示了程序执行过程中的关键信号变化,验证了数据通路的正确性和控制信号的时序准确性。 |