\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 能够正确执行各种程序。波形图分析显示了程序执行过程中的关键信号变化，验证了数据通路的正确性和控制信号的时序准确性。