\documentclass[12pt]{ctexart} \usepackage[utf8]{inputenc} \usepackage[margin=2.5cm]{geometry} \usepackage{graphicx} \usepackage{amsmath} \usepackage{listings} \usepackage{xcolor} \usepackage{hyperref} \hypersetup{ colorlinks=true, linkcolor=black, citecolor=green, urlcolor=blue, filecolor=magenta, pdftitle={实验二:RISC-V CPU 中断处理机制设计与实现}, pdfauthor={朱梓涵}, pdfsubject={RISC-V CPU 设计与实现报告}, pdfkeywords={RISC-V, CPU, Chisel, 中断, CLINT, CSR, 实验报告} } \usepackage{fancyhdr} \usepackage{enumitem} % --- 页眉页脚设置 --- \setlength{\headheight}{15pt} \pagestyle{fancy} \fancyhf{} \fancyhead[L]{\MakeUppercase{实验二:RISC-V CPU 中断处理机制设计与实现}} \fancyfoot[C]{\thepage} \renewcommand{\headrulewidth}{0.4pt} \renewcommand{\footrulewidth}{0.4pt} % --- 标题信息 --- \title{\vspace{-2cm}\textbf{实验二:RISC-V CPU 中断处理机制设计与实现}} \author{朱梓涵 \\ 学号:24325356} \date{\today} % --- 代码高亮风格定义 (Solarized-light) --- \definecolor{sol-base03}{HTML}{002b36} \definecolor{sol-base02}{HTML}{073642} \definecolor{sol-base01}{HTML}{586e75} \definecolor{sol-base00}{HTML}{657b83} \definecolor{sol-base0}{HTML}{839496} \definecolor{sol-base1}{HTML}{93a1a1} \definecolor{sol-base2}{HTML}{eee8d5} \definecolor{sol-base3}{HTML}{fdf6e3} \definecolor{sol-yellow}{HTML}{b58900} \definecolor{sol-orange}{HTML}{cb4b16} \definecolor{sol-red}{HTML}{dc322f} \definecolor{sol-magenta}{HTML}{d33682} \definecolor{sol-violet}{HTML}{6c71c4} \definecolor{sol-blue}{HTML}{268bd2} \definecolor{sol-cyan}{HTML}{2aa198} \definecolor{sol-green}{HTML}{859900} \lstdefinestyle{ScalaChiselStyle}{commentstyle=\color{sol-base01}\itshape,keywordstyle=\color{sol-green}\bfseries,stringstyle=\color{sol-cyan},basicstyle=\ttfamily\small,breakatwhitespace=false,breaklines=true,captionpos=b,keepspaces=true,numbers=none,showspaces=false,showstringspaces=false,showtabs=false,tabsize=2,frame=single,rulecolor=\color{black},morekeywords={when, Mux, MuxLookup, IndexedSeq, U, io, :=, object, val, def, class, override, package, import, extends, with, Bits, UInt, SInt},extendedchars=false,literate={:}{{\color{sol-base02}:}}1} \lstdefinestyle{CStyle}{commentstyle=\color{sol-base01}\itshape,keywordstyle=\color{sol-orange}\bfseries,stringstyle=\color{sol-cyan},basicstyle=\ttfamily\small,breakatwhitespace=false,breaklines=true,captionpos=b,keepspaces=true,numbers=none,showspaces=false,showstringspaces=false,showtabs=false,tabsize=2,frame=single,rulecolor=\color{black},extendedchars=false} \lstset{style=ScalaChiselStyle} % --- 图片计数器与章节联动 --- \counterwithin{figure}{section} \counterwithin{table}{section} % --- 文档开始 --- \begin{document} \maketitle \thispagestyle{empty} \section{实验目的} 本实验旨在为已实现的单周期 RISC-V CPU 增加中断与异常处理功能,深入理解现代处理器如何响应并处理非顺序控制流事件。实验目标包括: \begin{enumerate}[label=\arabic*.] \item 设计并实现控制状态寄存器模块,支持 CSR 指令的读写操作。 \item 设计并实现核本地中断控制器,使其能够正确处理外部硬件中断和内部软件中断以及中断返回指令。 \item 理解中断处理流程中 \texttt{mstatus}, \texttt{mepc}, \texttt{mcause}, \texttt{mtvec} 等关键寄存器的作用与变化。 \item 学习通过 Chisel 测试用例和波形图分析,验证复杂的中断处理逻辑的正确性。 \end{enumerate} \section{实验环境} \begin{itemize} \item \textbf{操作系统}: Windows 11 \item \textbf{开发工具}: IntelliJ IDEA \item \textbf{构建工具}: SBT \item \textbf{仿真与测试}: Verilator, GTKWave, MSYS2 \end{itemize} \section{模块实现与分析} 本次实验的核心是新增 \texttt{CSR} 和 \texttt{CLINT} 模块,并对 \texttt{Execute} 模块进行扩展以支持 CSR 指令。 \subsection{CSR 模块} CSR 模块是 CPU 的状态管理中心,负责存储和更新如 \texttt{mstatus}, \texttt{mepc} 等关键状态寄存器。 \begin{itemize} \item \textbf{实现要点}: \begin{enumerate} \item \textbf{独立寄存器与查找表}:将重要的 CSR(如 \texttt{mstatus})实现为独立的物理寄存器,并通过一个查找表 \texttt{regLUT} 响应读请求,设计清晰且高效。 \item \textbf{写操作优先级}:写入逻辑必须处理来自 CLINT(中断/异常事件)和 Execute(CSR指令)的并发写请求。设计中,CLINT 的写入拥有最高优先级,确保了中断处理的原子性。 \item \textbf{数据旁路}:为解决数据冒险,设计了旁路机制。CSR 模块会预计算出下一拍寄存器的值并立即提供给 CLINT,使其能在当前周期就基于最新的 CPU 状态做出正确决策。 \end{enumerate} \end{itemize} \subsubsection{代码实现} \begin{lstlisting}[caption={CSR 模块的写入优先级与旁路逻辑}, label={lst:csr_logic}] val mstatus_next = Mux(io.reg_write_enable_id && io.reg_write_address_id === CSRRegister.MSTATUS, io.reg_write_data_ex, mstatus) val mepc_next = Mux(io.reg_write_enable_id && io.reg_write_address_id === CSRRegister.MEPC, io.reg_write_data_ex, mepc) io.clint_access_bundle.mstatus := mstatus_next io.clint_access_bundle.mepc := mepc_next when(io.clint_access_bundle.direct_write_enable) { mstatus := io.clint_access_bundle.mstatus_write_data mepc := io.clint_access_bundle.mepc_write_data mcause := io.clint_access_bundle.mcause_write_data }.elsewhen(io.reg_write_enable_id) { when(io.reg_write_address_id === CSRRegister.MSTATUS) { mstatus := io.reg_write_data_ex } } \end{lstlisting} \subsection{CLINT 模块} CLINT 是中断处理的决策中心,负责监视 CPU 状态,判断中断与异常事件,并生成控制信号来改变 CPU 的执行流和状态。 \begin{itemize} \item \textbf{实现要点}: \begin{enumerate} \item \textbf{事件检测}:通过组合逻辑判断外部中断信号 (\texttt{interrupt\_flag}) 和特定指令(\texttt{mret}, \texttt{ecall}, \texttt{ebreak})的发生。 \item \textbf{状态更新计算}:根据发生的事件类型,精确计算 \texttt{mepc}, \texttt{mcause}, \texttt{mstatus} 这三个 CSR 寄存器需要更新成的新值。 \item \textbf{PC 重定向}:当陷阱 (trap) 发生或 \texttt{mret} 执行时,置位 \texttt{interrupt\_assert} 标志,并提供新的 PC 地址(中断时为 \texttt{mtvec},返回时为 \texttt{mepc})。 \item \textbf{处理优先级}:通过一个 \texttt{when-elsewhen-otherwise} 结构明确了事件处理的优先级:外部硬件中断 > \texttt{mret} > \texttt{ecall} > \texttt{ebreak}。 \end{enumerate} \end{itemize} \subsubsection{代码实现} \begin{lstlisting}[caption={CLINT 模块处理硬件中断的核心逻辑}, label={lst:clint_logic}] val interrupt_enable = io.csr_bundle.mstatus(3) val instruction_address = Mux( io.jump_flag, io.jump_address, io.instruction_address + 4.U ) val mstatus = io.csr_bundle.mstatus val mie = mstatus(3) when(io.interrupt_flag =/= InterruptCode.None && interrupt_enable) { io.interrupt_assert := true.B io.csr_bundle.direct_write_enable := true.B io.interrupt_handler_address := io.csr_bundle.mtvec io.csr_bundle.mepc_write_data := instruction_address io.csr_bundle.mcause_write_data := "h80000007".U val new_mpie = mie << 7 io.csr_bundle.mstatus_write_data := (mstatus & (~(1.U << 3)).asUInt) | new_mpie | (3.U << 11) }.elsewhen(io.instruction === InstructionsRet.mret) { } \end{lstlisting} \section{测试与结果分析} \subsection{CLINTCSRTest: 硬件中断测试分析} \subsubsection{测试机制简述} 该测试旨在验证 CPU 对外部硬件中断的响应是否正确。测试用例通过 \texttt{chiseltest} 框架,模拟外部定时器中断信号,并验证 CLINT 模块在不同场景下的中断处理行为。 \textbf{输入信号:} \begin{itemize} \item \texttt{interrupt\_flag}: 外部中断标志,测试中使用值为 \texttt{0x1} 的定时器中断 \item \texttt{instruction}: 当前执行的指令 \item \texttt{instruction\_address}: 当前指令地址,表示被中断时的 PC 值 \item \texttt{jump\_flag}: 指示当前指令是否为跳转指令 \item \texttt{jump\_address}: 跳转目标地址 \item \texttt{csr\_bundle.mtvec}: 预设的中断向量表基地址 \texttt{0x1144} \item \texttt{csr\_bundle.mstatus}: 初始值 \texttt{0x1888},MIE=1, MPIE=1,使能中断 \end{itemize} \textbf{测试的 CLINT 功能:} \begin{enumerate} \item \textbf{中断检测}:检测外部中断信号 (\texttt{interrupt\_flag}) 并判断是否应该响应(检查 \texttt{mstatus.MIE} 位) \item \textbf{上下文保存}:将中断发生时的关键状态保存到 CSR 寄存器: \begin{itemize} \item \texttt{MEPC} $\leftarrow$ PC + 4(非跳转)或跳转目标地址(跳转) \item \texttt{MCAUSE} $\leftarrow$ 中断原因编码(\texttt{0x80000007} 表示定时器中断) \item \texttt{MSTATUS} $\leftarrow$ 更新状态(MIE $\leftarrow$ 0, MPIE $\leftarrow$ 原 MIE 值) \end{itemize} \item \textbf{中断跳转}:跳转到中断处理程序(\texttt{mtvec} 中存储的地址 \texttt{0x1144}) \item \textbf{中断返回}:执行 \texttt{mret} 指令时恢复现场(PC $\leftarrow$ \texttt{MEPC},MIE $\leftarrow$ MPIE) \item \textbf{跳转与非跳转指令的差异}:验证在跳转指令执行期间发生中断时,\texttt{MEPC} 保存的是跳转目标地址而非 PC + 4 \end{enumerate} \subsubsection{波形图分析:非跳转指令下的硬件中断处理} 本次测试选用 \texttt{CLINTCSRTest.scala} 中的硬件中断测试(\texttt{handle external interrupt})。测试通过手动向 CPU 输入一个外部硬件中断标志 \texttt{io\_interrupt\_flag},并观察 CLINT 是否正确生成中断、是否能在非跳转指令下按照 RISC-V 标准流程完成一次完整的中断处理。 \textbf{测试输入信号及作用:} \begin{table}[htbp] \centering \begin{tabular}{|l|p{8cm}|} \hline \textbf{信号} & \textbf{作用} \\ \hline \texttt{io\_interrupt\_flag = 1} & 触发一次硬件中断(定时器中断) \\ \hline \texttt{io\_instruction = 0x13} & 当前执行指令(NOP,0x00000013) \\ \hline \texttt{io\_instruction\_address = 0x1900} & 当前 PC 值 \\ \hline \texttt{io\_jump\_flag = 0} & 非跳转指令标志 \\ \hline \texttt{mtvec}、\texttt{mstatus} 初始写入 & 配置中断入口(0x1144)及打开 MIE \\ \hline \end{tabular} \caption{硬件中断测试输入信号} \end{table} 此测试用来验证 CLINT 是否能完成以下功能: \begin{itemize} \item 在非跳转指令下正确响应硬件中断 \item 正确生成中断断点(\texttt{mepc = PC + 4}) \item 正确写入中断原因(\texttt{mcause = 0x80000007}) \item 自动清除 MIE 并保存到 MPIE(\texttt{mstatus} 更新) \item 正确跳转到中断处理入口(\texttt{mtvec}) \end{itemize} \begin{figure}[htbp] \centering \includegraphics[width=\textwidth]{1.png} \caption{硬件中断处理过程波形图 - 非跳转指令场景(8ps $\sim$ 12ps)} \label{fig:external_interrupt_waveform} \end{figure} \textbf{波形图关键信号说明:} \begin{itemize} \item \texttt{io\_interrupt\_flag[31:0]}: 外部中断标志输入 \item \texttt{io\_instruction[31:0]}: 当前执行的指令 \item \texttt{io\_instruction\_address[31:0]}: 当前指令地址(PC) \item \texttt{io\_jump\_flag}: 跳转指令标志 \item \texttt{io\_jump\_address[31:0]}: 跳转目标地址 \item \texttt{io\_interrupt\_assert}: CLINT 输出的中断响应信号 \item \texttt{io\_interrupt\_handler\_address[31:0]}: 中断跳转目标地址 \item \texttt{mtvec[31:0]}: 中断向量表基地址 \item \texttt{mepc[31:0]}: 中断返回地址 \item \texttt{mcause[31:0]}: 中断原因寄存器 \item \texttt{mstatus[31:0]}: 机器状态寄存器 \end{itemize} 如图所示,本测试一次完整的硬件中断处理过程(波形截图区间约为 8ps $\sim$ 12ps)。以下挑选关键信号说明中断发生与处理的整个过程: \paragraph{(1) 初始化阶段(约 6ps)} 在中断发生前,测试代码已通过 CSR 写指令完成初始化: \begin{itemize} \item \texttt{mtvec = 0x00001144}:中断处理程序入口地址已配置 \item \texttt{mstatus = 0x00001888}:全局中断已使能 \begin{itemize} \item 二进制表示为 \texttt{...0001\_1000\_1000\_1000} \item bit[3] MIE = 1(全局中断使能) \item bit[7] MPIE = 1(中断前的 MIE 备份) \end{itemize} \end{itemize} \paragraph{(2) 中断发生时刻(约 9ps)} 此时 CPU 正在执行一条普通的 NOP 指令,外部中断请求到来: \begin{itemize} \item \texttt{io\_instruction\_address = 0x00001900}:当前 PC \item \texttt{io\_instruction = 0x00000013}:NOP 指令 \item \texttt{io\_jump\_flag = 0}:非跳转指令 \item \texttt{io\_interrupt\_flag}:从 \texttt{0x00000000} $\rightarrow$ \texttt{0x00000001}(Timer0 中断) \end{itemize} \paragraph{(3) 同周期中断响应(约 9ps)} CLINT 模块检测到 \texttt{interrupt\_flag = 1} 且 \texttt{mstatus.MIE = 1} 后,立即通过组合逻辑响应中断: \begin{itemize} \item \texttt{io\_interrupt\_assert = 1}:触发中断信号 \item \texttt{io\_interrupt\_handler\_address = 0x00001144}:指示 CPU 跳转到 \texttt{mtvec} \item 波形图上可观察到 \texttt{io\_interrupt\_assert} 出现一个脉冲 \item \texttt{io\_interrupt\_handler\_address} 短暂输出 \texttt{0x1144} 后恢复为 \texttt{0} \end{itemize} 这表示 CPU 将在下一时钟周期跳转到中断处理程序入口。 \paragraph{(4) CSR 自动更新(约 10ps)} 在时钟上升沿,CSR 模块根据 CLINT 的 \texttt{direct\_write\_enable} 信号自动更新关键寄存器: \begin{itemize} \item \texttt{mepc}:从 \texttt{0x00000000} $\rightarrow$ \texttt{0x00001904} \begin{itemize} \item 保存的是 PC + 4(\texttt{0x1900 + 4 = 0x1904}) \item 这是被中断指令的下一条指令地址,中断返回后将从此处继续执行 \end{itemize} \item \texttt{mcause}:从 \texttt{0x00000000} $\rightarrow$ \texttt{0x80000007} \begin{itemize} \item bit[31] = 1:表示这是异步硬件中断(而非同步异常) \item 低位 = 7:对应 Timer 中断编码 \end{itemize} \item \texttt{mstatus}:从 \texttt{0x00001888} $\rightarrow$ \texttt{0x00001880} \begin{itemize} \item \texttt{0x1888} = \texttt{...0001\_1000\_1000\_1000}(MIE=1, MPIE=1) \item \texttt{0x1880} = \texttt{...0001\_1000\_1000\_0000}(MIE=0, MPIE=1) \item MIE 被清 0:关闭全局中断,防止中断嵌套 \item MPIE 保存了先前的 MIE 值(=1) \end{itemize} \end{itemize} 这些变化完全符合 RISC-V 特权架构手册中定义的中断进入流程。 \paragraph{(5) 中断标志清除(约 12ps 后)} 波形图显示 \texttt{io\_interrupt\_flag} 在约 12ps 后从 \texttt{1} 恢复为 \texttt{0},这是测试代码模拟中断处理程序清除外设中断标志的行为。 \vspace{1em} \noindent\textbf{波形分析总结:} 通过以上波形图分析,可以验证 CLINT 模块在非跳转指令场景下正确实现了以下功能: \begin{itemize} \item \texttt{mepc} 正确保存了 PC + 4(\texttt{0x1904}),确保中断返回后从下一条指令继续执行 \item \texttt{mcause} 正确记录了中断原因(\texttt{0x80000007}),bit[31]=1 标识为异步中断 \item \texttt{mstatus} 自动完成 MIE 清零和 MPIE 备份,实现中断嵌套保护机制 \item 从中断检测到 CSR 更新的整个过程由硬件自动完成,无需软件干预 \end{itemize} 测试还包括跳转指令场景的验证:当 \texttt{jump\_flag=1}, \texttt{jump\_address=0x1990} 时发生中断,\texttt{mepc} 应保存跳转目标 \texttt{0x1990} 而非 \texttt{PC+4},且 \texttt{mcause=0x8000000B}。这确保了 CLINT 能正确处理各种执行场景下的中断。 \subsection{CPUTest: SimpleTrapTest 分析} \subsubsection{测试目的} 本测试通过执行 \texttt{csrc/simpletest.c} 中的测试程序,验证 CPU 是否能够按照 RISC-V 标准正确处理中断,包括中断触发、保存现场、跳转到中断处理程序、执行处理逻辑以及返回主程序等完整流程。 \subsubsection{测试程序的中断验证机制} \texttt{simpletest.c} 通过以下过程验证 CPU 的中断处理正确性: \begin{lstlisting}[style=CStyle, caption={simpletest.c 测试程序源码}] extern void enable_interrupt(); void trap_handler(void *epc, unsigned int cause){ *((int*)0x4) = 0x2022; } int main(){ *((int*)0x4) = 0xDEADBEEF; enable_interrupt(); for(;;); } \end{lstlisting} \paragraph{主程序初始化阶段} 程序首先向内存地址 \texttt{0x4} 写入标记值 \texttt{0xDEADBEEF},用于表示"尚未处理中断"的初始状态。随后调用 \texttt{enable\_interrupt()} 函数配置中断环境: \begin{itemize} \item 将 \texttt{trap\_handler} 的地址写入 \texttt{mtvec} 寄存器 \item 设置 \texttt{mstatus.MIE = 1},使能全局中断 \end{itemize} \paragraph{等待中断触发} 程序进入无限循环 \texttt{for(;;)},持续等待中断到来。测试框架 \texttt{TestTopModule} 在仿真过程中通过 \texttt{io.interrupt\_flag.poke(0x1)} 向 CPU 注入外部中断请求。 \paragraph{中断处理程序执行} 当中断触发后,CPU 自动跳转到 \texttt{trap\_handler} 函数。该函数执行唯一的操作:将内存地址 \texttt{0x4} 的值修改为 \texttt{0x2022}。这一修改作为中断处理程序成功执行的关键证据。 \paragraph{中断返回与验证} \texttt{trap\_handler} 执行完毕后,通过 \texttt{mret} 指令返回主程序。测试代码随后读取内存地址 \texttt{0x4} 和相关 CSR 寄存器,验证: \begin{itemize} \item 内存值已从 \texttt{0xDEADBEEF} 变为 \texttt{0x2022} \item \texttt{mstatus} 恢复为 \texttt{0x1888}(中断返回后状态) \item \texttt{mcause} 保持 \texttt{0x80000007}(记录中断原因) \end{itemize} \vspace{0.5em} \noindent 该验证机制的核心在于:若无限循环能够被中断打断,且内存值发生预期变化,则证明 CPU 确实跳转到了 \texttt{trap\_handler},中断处理程序正确执行,且 \texttt{mret} 返回机制正常工作。这种通过可观测副作用验证复杂流程的方法,是嵌入式系统测试的典型手段。 \subsubsection{波形图分析} 本实验从波形图中截取了两段关键片段,分别展示中断触发与进入处理、以及中断处理程序修改内存的核心过程。 \paragraph{(1) 中断触发与进入中断处理} \begin{figure}[htbp] \centering \includegraphics[width=\textwidth]{2-1.png} \caption{SimpleTrapTest 中断触发与进入处理过程波形图(约 2000ps)} \label{fig:simpletrap_interrupt} \end{figure} 如图所示,展示了中断触发至 CPU 进入中断处理程序的完整时序。波形图中的关键信号及其变化如下: \begin{itemize} \item \texttt{io\_interrupt\_flag}: 在约 2000ps 从 \texttt{0} $\rightarrow$ \texttt{0x00000001},表示外部中断请求到来,随后恢复为 \texttt{0} \item \texttt{io\_interrupt\_assert}: 随即产生一个脉冲,说明 CLINT 已检测到中断并产生中断响应信号 \item \texttt{mepc}: 更新为 \texttt{0x000011C4},即被中断时刻 PC 的下一条指令地址,保存了中断返回点 \item \texttt{mcause}: 被写入 \texttt{0x00000007},记录中断原因为定时器中断(外部中断编码) \item \texttt{mstatus}: 从 \texttt{0x00001888} $\rightarrow$ \texttt{0x00001880},MIE 位(bit[3])被自动清零,MPIE 位(bit[7])保存了先前的 MIE 值,符合 RISC-V 中断进入时的标准行为 \item \texttt{io\_pc\_debug\_read}: 从原执行地址跳转到 \texttt{0x00001050} 附近,该地址为 \texttt{mtvec} 指向的 \texttt{trap\_handler} 函数入口,随后 PC 逐条递增执行中断处理程序指令 \end{itemize} 这一段波形完整展示了中断触发、CSR 自动保存、PC 跳转到 \texttt{trap\_handler} 的全过程。 \paragraph{(2) 中断处理程序修改内存值} \begin{figure}[htbp] \centering \includegraphics[width=\textwidth]{2-2.png} \caption{SimpleTrapTest 中断处理程序修改内存标志(约 2330ps $\sim$ 2390ps)} \label{fig:simpletrap_memory} \end{figure} 如图所示,展示了验证程序成功执行的最关键时刻。该时段内 CPU 正在执行 \texttt{trap\_handler} 函数体,波形图清晰记录了以下信号变化: \begin{itemize} \item \texttt{io\_bundle\_write\_enable = 1}: CPU 正在执行内存写操作 \item \texttt{io\_bundle\_address = 0x00000004}: 写入的目标地址正是 \texttt{simpletest.c} 中指定的内存地址 \texttt{0x4} \item \texttt{io\_bundle\_write\_data = 0x00002022}: 写入的数据为 \texttt{0x2022},即 \texttt{trap\_handler} 函数中期望写入的新值 \item \texttt{mstatus}: 保持 \texttt{0x00001880},说明此时仍处于中断处理状态,MIE 位为 0 \item \texttt{mcause}: 保持 \texttt{0x80000007},中断原因记录未变 \item \texttt{mepc}: 保持 \texttt{0x000011C4},中断返回地址保持不变 \item \texttt{io\_pc\_debug\_read}: 在 \texttt{trap\_handler} 函数内部的指令地址间递增,表明 CPU 正在顺序执行中断处理程序 \end{itemize} 这一瞬间表明 CPU 已成功进入 \texttt{trap\_handler},并执行了中断处理中最核心的操作——将内存地址 \texttt{0x4} 的值从 \texttt{0xDEADBEEF} 替换为 \texttt{0x2022}。这是 SimpleTrapTest 验证的直接依据,也是实验要求中明确指出的"程序成功执行"的关键信号。 \paragraph{波形分析总结} 通过以上两段波形图分析,可以验证: \begin{itemize} \item CPU 能够在外部中断到来时正确进入中断处理流程 \item CSR 寄存器(\texttt{mepc}、\texttt{mcause}、\texttt{mstatus})均按 RISC-V 标准顺序自动更新 \item PC 正确跳转到 \texttt{mtvec} 指向的 \texttt{trap\_handler} 函数 \item 中断处理函数成功执行,完成了将内存地址 \texttt{0x4} 的值从 \texttt{0xDEADBEEF} 修改为 \texttt{0x2022} 的操作 \item 测试代码后续验证了 \texttt{mret} 返回后 \texttt{mstatus} 恢复为 \texttt{0x1888},中断返回机制正常工作 \end{itemize} 综上所述,本次测试成功验证了 CPU 中断处理机制的完整性与正确性。 \subsection{CPU 与操作系统协作处理定时器中断的机制} 假设本实验设计的 CPU 上运行着一个简单的操作系统(如嵌入式实时操作系统),当定时器中断发生时,硬件(CPU 与 CLINT)和软件(操作系统内核)将协同完成中断处理的全过程。 \subsubsection{操作系统初始化阶段} 操作系统在启动过程中,执行特权指令(如 \texttt{csrrw})来初始化中断处理机制: \begin{itemize} \item 向 \texttt{mtvec} 寄存器写入中断分发程序 (interrupt dispatcher) 的入口地址 \item 配置定时器硬件,设置定时周期和中断使能位 \item 设置 \texttt{mstatus} 寄存器的 MIE 位为 1,使能全局中断 \end{itemize} \subsubsection{硬件自动响应} 定时器硬件在倒计时结束后,向 CPU 发送中断信号。CPU 的 CLINT 模块检测到该信号且 \texttt{mstatus.MIE = 1} 时,硬件自动执行以下原子操作: \begin{enumerate} \item 将 \texttt{mstatus.MIE} 位的值备份到 \texttt{mstatus.MPIE} 位,然后将 \texttt{mstatus.MIE} 清零,防止中断嵌套 \item 将当前 PC 的下一条指令地址存入 \texttt{mepc} 寄存器 \item 根据中断源,在 \texttt{mcause} 寄存器中写入原因码(定时器中断为 \texttt{0x80000007}) \item 读取 \texttt{mtvec} 寄存器的值,将 PC 强制设置为该地址,跳转到中断分发程序 \end{enumerate} \subsubsection{操作系统软件处理} CPU 跳转到操作系统预设的中断分发程序,该程序执行以下操作: \begin{enumerate} \item \textbf{保存上下文}:将所有通用寄存器(\texttt{x1}$\sim$\texttt{x31})压栈,保存到内核栈中 \item \textbf{识别中断源}:读取 \texttt{mcause} 寄存器,判断中断类型 \item \textbf{分发处理}:根据中断类型调用相应的中断服务例程。对于定时器中断,调用定时器中断服务例程 \item \textbf{执行中断服务例程}:定时器中断服务例程执行核心任务: \begin{itemize} \item 更新系统时钟计数器 \item 检查并唤醒睡眠超时的任务 \item 执行任务调度算法,决定是否需要任务切换 \item 重新配置定时器,设置下一次中断 \end{itemize} \item \textbf{恢复上下文}:从内核栈中恢复所有通用寄存器 \item \textbf{执行 \texttt{mret}}:返回被中断的程序 \end{enumerate} \subsubsection{硬件恢复} 执行 \texttt{mret} 指令时,CPU 硬件自动执行以下操作: \begin{enumerate} \item 将 \texttt{mstatus.MPIE} 的值恢复到 \texttt{mstatus.MIE},重新使能全局中断 \item 将 \texttt{mepc} 中保存的地址加载到 PC,返回被中断的程序 \end{enumerate} 至此,CPU 无缝返回到被中断的用户程序继续执行。 \section{实验结论} 本次实验,我成功地为单周期 CPU 添加了完整的中断处理功能。通过设计 CSR 和 CLINT 模块,我深入学习了 RISC-V 的特权架构和中断处理流程,对 \texttt{mstatus}, \texttt{mepc}, \texttt{mcause} 等核心 CSR 的作用有了实践层面的深刻理解。解决 Windows 环境配置难题和调试复杂中断逻辑的过程,极大地锻炼了我分析问题和解决问题的能力。通过将理论知识与硬件实现、软件测试相结合,我不仅验证了 CPU 设计的正确性,也对操作系统与硬件的交互机制有了更具体的认识,为未来更深入的系统级学习打下了坚实的基础。 \end{document}