实验报告检查

This commit is contained in:
2025-12-21 23:11:21 +08:00
parent 078b2c0b37
commit 52ecd7743b
85 changed files with 8477 additions and 16 deletions

View File

@@ -45,6 +45,7 @@ class Forwarding extends Module {
val ex_mem_hazard_rs2 = io.reg_write_enable_mem && io.rd_mem =/= 0.U && (io.rd_mem === io.rs2_ex)
val ex_wb_hazard_rs1 = io.reg_write_enable_wb && io.rd_wb =/= 0.U && (io.rd_wb === io.rs1_ex)
val ex_wb_hazard_rs2 = io.reg_write_enable_wb && io.rd_wb =/= 0.U && (io.rd_wb === io.rs2_ex)
io.reg1_forward_ex := Mux(ex_mem_hazard_rs1, ForwardingType.ForwardFromMEM,
Mux(ex_wb_hazard_rs1, ForwardingType.ForwardFromWB, ForwardingType.NoForward))
io.reg2_forward_ex := Mux(ex_mem_hazard_rs2, ForwardingType.ForwardFromMEM,
@@ -61,3 +62,6 @@ class Forwarding extends Module {
// Lab3(Final) End
}

View File

@@ -0,0 +1,365 @@
\documentclass[12pt]{ctexart} % 使用 ctexart 文档类支持中文12pt 字号
\usepackage[utf8]{inputenc} % 输入编码,保持兼容性
\usepackage[margin=2.5cm]{geometry} % 设置页边距
\usepackage{graphicx} % 导入图片
\usepackage{amsmath} % 支持数学公式
\usepackage{listings} % 代码块高亮
\usepackage{xcolor} % 用于代码高亮颜色
\usepackage{hyperref} % 目录、交叉引用可点击生成PDF书签
\hypersetup{
colorlinks=true, % 这是关键,它会让链接文本以颜色显示,而不是边框
linkcolor=black, % 内部链接(如目录、章节引用)的颜色设为黑色
citecolor=green, % 引用文献的颜色(如果用不到可以忽略或设为黑色)
urlcolor=blue, % URL链接的颜色如果用不到可以忽略或设为黑色
filecolor=magenta, % 文件链接的颜色(如果用不到可以忽略或设为黑色)
% 可以添加更多其他 PDF 元数据,让 PDF 文件信息更完整
pdftitle={实验一:单周期 RISC-V CPU 设计与实现},
pdfauthor={朱梓涵},
pdfsubject={RISC-V CPU 设计与实现报告},
pdfkeywords={RISC-V, CPU, Chisel, 单周期, 实验报告}
}
% 目录、交叉引用可点击生成PDF书签
\usepackage{fancyhdr} % 自定义页眉页脚
\usepackage{enumitem} % 列表项自定义
\usepackage{ifthen} % 条件判断(用于图片占位)
% --- 图片缺失占位宏 ---
\newcommand{\includegraphicsorplaceholder}[2][]{%
\IfFileExists{#2}{\includegraphics[#1]{#2}}{\fbox{\parbox[c][0.2\textheight][c]{0.9\textwidth}{\centering Missing image: #2}}}%
}
% --- 页眉页脚设置 ---
\pagestyle{fancy}
\fancyhf{} % 清除所有页眉页脚字段
\fancyhead[L]{\MakeUppercase{实验一:单周期 RISC-V CPU 设计与实现}} % 左侧页眉:大写实验名称
\fancyfoot[C]{\thepage} % 居中页脚:页码
\renewcommand{\headrulewidth}{0.4pt} % 页眉下方的横线粗细
\renewcommand{\footrulewidth}{0.4pt} % 页脚上方的横线粗细
% 解决 fancyhdr 提示的 \headheight 偏小问题
\setlength{\headheight}{15pt}
% --- 标题信息 ---
\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},
% 添加 Chisel 相关关键字
morekeywords={when, Mux, MuxLookup, IndexedSeq, U, io, :=, object, val, def, class, override, package, import, extends, with, Bits, UInt, SInt},
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},
}
\lstset{style=ScalaChiselStyle} % 默认代码风格为 Scala/Chisel
% --- 图片计数器与章节联动 ---
\counterwithin{figure}{section}
\counterwithin{table}{section}
% --- 文档开始 ---
\begin{document}
\maketitle % 生成标题
\thispagestyle{empty} % 标题页无页码
% --- 正文开始 ---
\section{实验目的}
本实验旨在深入理解 RISC-V 指令集架构 (ISA) 与单周期 CPU 的工作原理。实验目标包括:
\begin{enumerate}[label=\arabic*.] % 使用 enumerate 环境创建编号列表
\item 设计并实现一个支持部分 RV32I 指令集的单周期 CPU。
\item 掌握 CPU 经典五阶段(取指、译码、执行、访存、写回)在单周期模型下的数据通路和控制逻辑设计。
\item 学习使用 Chisel 硬件描述语言进行模块化硬件设计。
\item 通过编写单元测试和集成测试,利用 \texttt{chiseltest} 和 GTKWave 波形图验证 CPU 各模块及整体功能的正确性。
\end{enumerate}
\section{实验环境}
\begin{itemize}
\item \textbf{操作系统}: Windows 11
\item \textbf{开发工具}: IntelliJ IDEA
\item \textbf{构建工具}: SBT
\item \textbf{仿真与测试}: Verilator, GTKWave
\end{itemize}
\section{阶段功能划分}
本单周期 RISC-V CPU 依照经典的五级流水线概念进行设计,虽然是单周期实现,但其内部模块划分仍遵循五阶段的逻辑,确保数据通路和控制逻辑清晰。
\begin{itemize}
\item \textbf{取指 (Instruction Fetch, IF)}: 程序计数器 (PC) 提供指令地址,指令存储器根据该地址输出 32 位指令。PC 在每个时钟周期默认加 4若遇跳转或分支成功则更新为目标地址。
\item \textbf{译码 (Instruction Decode, ID)}: 译码单元解析指令,提取操作数(寄存器地址 \texttt{rs1}, \texttt{rs2})、目标寄存器地址 (\texttt{rd}) 和立即数。主控制器根据指令操作码生成后续阶段所需的所有控制信号。
\item \textbf{执行 (Execute, EX)}: 算术逻辑单元 (ALU) 根据译码阶段生成的控制信号,对来自寄存器文件或立即数生成器的操作数进行计算。同时,分支跳转的地址计算和条件判断也在此阶段完成。
\item \textbf{访存 (Memory Access, MEM)}: 根据译码阶段生成的访存控制信号与数据存储器进行交互。Load 指令从此阶段读取数据Store 指令则向此阶段写入数据。
\item \textbf{写回 (Write Back, WB)}: 将执行结果或从数据存储器中读取的数据写回寄存器文件。写回的数据来源由控制信号决定。
\end{itemize}
\section{模块实现与分析}
\subsection{取指}
\begin{itemize}
\item \textbf{功能}: 根据 \texttt{PC} 的当前值从指令存储器中取出指令,并计算下一周期的 \texttt{PC} 值。
\item \textbf{实现要点}: \texttt{PC} 的更新逻辑是核心。它由跳转标志 \texttt{jump\_flag\_id} 控制:若跳转发生,\texttt{PC} 更新为 \texttt{jump\_address\_id};否则,顺序执行,\texttt{PC} 更新为 \texttt{PC + 4}
\end{itemize}
\subsubsection{代码实现}
\begin{lstlisting}[caption={取指模块PC更新逻辑}, label={lst:if_pc}]
// lab1(InstructionFetch)
when(io.jump_flag_id) {
pc := io.jump_address_id
}.otherwise {
pc := pc + 4.U
}
// lab1(InstructionFetch) end
\end{lstlisting}
\subsubsection{波形图}
\begin{figure}[htbp]
\centering
\includegraphicsorplaceholder[width=0.9\textwidth]{b1.png}
\caption{取指模块 (\texttt{InstructionFetch}) 波形图}
\label{fig:if_waveform}
\end{figure}
\subsection{译码}
\begin{itemize}
\item \textbf{功能}: 解析指令,生成立即数,并为后续阶段提供控制信号。
\item \textbf{实现要点}: 控制信号的生成逻辑是关键。例如,\texttt{memory\_read\_enable} 仅在 L-type 指令时有效,\texttt{wb\_reg\_write\_source} 根据指令类型选择写回数据是来自 ALU、数据存储器还是 PC+4。
\end{itemize}
\subsubsection{代码实现}
\begin{lstlisting}[caption={译码模块控制信号生成逻辑}, label={lst:id_control}]
// lab1(InstructionDecode)
io.ex_aluop2_source := Mux(opcode === InstructionTypes.RM,
ALUOp2Source.Register,
ALUOp2Source.Immediate
)
io.memory_read_enable := opcode === InstructionTypes.L
io.memory_write_enable := opcode === InstructionTypes.S
io.wb_reg_write_source := MuxLookup(
opcode,
RegWriteSource.ALUResult
)(
IndexedSeq(
InstructionTypes.L -> RegWriteSource.Memory,
Instructions.jal -> RegWriteSource.NextInstructionAddress,
Instructions.jalr -> RegWriteSource.NextInstructionAddress
)
)
// lab1(InstructionDecode) end
\end{lstlisting}
\subsubsection{波形图}
\begin{figure}[htbp]
\centering
\includegraphicsorplaceholder[width=0.9\textwidth]{b2.png}
\caption{译码模块 (\texttt{InstructionDecode}) 波形图}
\label{fig:id_waveform}
\end{figure}
\subsection{执行}
\begin{itemize}
\item \textbf{功能}: 执行 ALU 运算,并处理分支和跳转逻辑。
\item \textbf{实现要点}: ALU 的两个操作数 \texttt{op1}\texttt{op2} 的来源由译码模块生成的 \texttt{aluop*\_source} 信号决定,实现了操作数的灵活选择。
\end{itemize}
\subsubsection{波形图}
\begin{figure}[htbp]
\centering
\includegraphicsorplaceholder[width=0.9\textwidth]{b3.png}
\caption{执行模块 (\texttt{Execute}) 波形图}
\label{fig:ex_waveform}
\end{figure}
\subsubsection{代码实现}
\begin{lstlisting}[caption={执行模块ALU操作数选择逻辑}, label={lst:ex_aluops}]
// lab1(Execute)
alu.io.func := alu_ctrl.io.alu_funct
alu.io.op1 := Mux(
io.aluop1_source === ALUOp1Source.Register,
io.reg1_data,
io.instruction_address
)
alu.io.op2 := Mux(
io.aluop2_source === ALUOp2Source.Register,
io.reg2_data,
io.immediate
)
// lab1(Execute) end
\end{lstlisting}
\subsection{CPU 模块}
\begin{itemize}
\item \textbf{功能}: 实例化取指、译码、执行等所有子模块,并根据数据通路图将它们正确连接起来。
\item \textbf{实现要点}: 确保数据流和控制流在模块间的正确传递是顶层连接的关键。
\end{itemize}
\subsubsection{代码实现}
\begin{lstlisting}[caption={CPU模块部分连接逻辑}, label={lst:cpu_top}]
// lab1(cpu)
ex.io.instruction := inst_fetch.io.instruction
ex.io.instruction_address := inst_fetch.io.instruction_address
ex.io.reg1_data := regs.io.read_data1
ex.io.reg2_data := regs.io.read_data2
ex.io.immediate := id.io.ex_immediate
ex.io.aluop1_source := id.io.ex_aluop1_source
ex.io.aluop2_source := id.io.ex_aluop2_source
// lab1(cpu) end
\end{lstlisting}
\section{测试与结果分析}
\subsection{单元测试InstructionDecoderTest 分析}
\subsubsection{L-Type 指令测试分析}
测试用例首先向译码模块的 \texttt{io.instruction} 端口输入一条 L-Type 指令。随后,测试代码使用 \texttt{.expect()} 方法断言模块的各个输出端口信号值是否与预设的期望值一致。
对于 \texttt{lw} 指令,其核心功能是从内存读取数据并写回寄存器。因此:
\begin{itemize}
\item \texttt{memory\_read\_enable} 必须为 \texttt{true},以启动访存阶段的数据读取。
\item \texttt{reg\_write\_enable} 必须为 \texttt{true},以允许最终结果写入寄存器。
\item \texttt{wb\_reg\_write\_source} 必须是 \texttt{RegWriteSource.Memory},因为写回的数据源于数据存储器。
\item \texttt{ex\_immediate} 必须是指令编码中包含的偏移量(此例中为 32用于地址计算 \texttt{rs1 + imm}
\end{itemize}
这些 \texttt{expect} 值精确地定义了 L-Type 指令在译码阶段应生成的正确行为模式,确保了控制通路的正确性。
\subsubsection{波形图分析}
\begin{figure}[htbp]
\centering
\includegraphicsorplaceholder[width=0.9\textwidth]{b2.png}
\caption{\texttt{L-Type} 指令 \texttt{lw x3, 0(x8)} 的译码波形图}
\label{fig:lw_decode_waveform}
\end{figure}
如图所示,波形图展示了指令 lw x3, 0(x8)(机器码 0x00404183的译码过程。在时钟上升沿之后
\begin{itemize}
\item \verb|id_io_instruction| 端口稳定为 \texttt{0x00404183},对应 I-type 的 Load 指令 \texttt{lw x3, 0(x8)}
\item \verb|id_io_memory_read_enable| 和 \verb|id_io_reg_write_enable| 信号被置为高电平1表示该指令需要从存储器读取数据并写回寄存器。
\item \verb|id_io_wb_reg_write_source| 信号值为 \texttt{01} (二进制),表示写回数据的来源是 Memory。
\item \verb|id_io_ex_immediate| 的值为 \texttt{0x00000000},即偏移量 0。
\end{itemize}
波形验证了译码模块对 I-type Load指令的控制信号输出与 \texttt{InstructionDecoderTest.scala} 中的 \texttt{expect} 语句一致,说明译码单元能够正确识别 Load 指令并产生相应控制信号。
\subsection{整体测试CPUTest (以 \texttt{FibonacciTest} 为例)}
\subsubsection{\texttt{fibonacci.c} 程序分析}
\begin{itemize}
\item \textbf{程序功能}:
该 C 语言程序定义了一个递归函数 \texttt{fib(int a)},用于计算斐波那契数列的第 \texttt{a} 项。其逻辑为:如果 \texttt{a} 等于 1 或 2则返回 1否则返回 \texttt{fib(a-1)}\texttt{fib(a-2)} 之和。\texttt{main} 函数是程序的入口点,它调用 \texttt{fib(10)} 来计算斐波那契数列的第 10 项,并将最终结果(即 55存储到内存地址 \texttt{0x4}
\begin{lstlisting}[language=C, caption={Fibonacci 递归程序 \texttt{fibonacci.c}}, label={lst:fib_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); // 将 fib(10) 的结果写入内存地址 0x4
}
\end{lstlisting}
\item \textbf{Chisel 测试检查方式}:
测试框架首先将 \texttt{fibonacci.asmbin} 文件加载到 CPU 的指令存储器和数据存储器中。然后,它驱动 CPU 执行足够多的时钟周期(通过 \texttt{c.clock.step(1000)} 大循环),以确保递归计算和内存写入操作全部完成。在仿真结束时,测试代码通过读取数据存储器的调试端口:
\begin{lstlisting}[caption={Chisel 测试代码片段}, label={lst:chisel_test}]
c.io.mem_debug_read_address.poke(4.U) // 将调试地址设置为 0x4
c.clock.step() // 步进一个时钟周期
c.io.mem_debug_read_data.expect(55.U) // 期望从地址 0x4 读出的数据为 55
\end{lstlisting}
上述语句首先将调试端口的读取地址 \texttt{mem\_debug\_read\_address} 设置为 \texttt{0x4},然后步进一个时钟周期以便数据稳定,最后断言 \texttt{mem\_debug\_read\_data} 的值是否等于预期的 \texttt{55}。如果相等,则测试通过,验证了 CPU 能够正确执行递归计算并将结果写入指定内存。
\end{itemize}
\subsubsection{波形图分析}
\begin{figure}[htbp]
\centering
\includegraphicsorplaceholder[width=0.9\textwidth]{b41.png}
\caption{Fibonacci 程序执行末期及结果写入内存的波形图}
\label{fig:fibonacci_waveform}
\end{figure}
基于 VCD 文件分析,斐波那契程序执行过程中的关键信号包括:
\begin{itemize}
\item \verb|cpu_io_instruction_address|程序计数器PC显示指令的执行流程
\item \verb|mem_io_bundle_write_enable|:内存写使能信号,用于标识数据写入操作;
\item \verb|mem_io_bundle_address|:内存访问地址;
\item \verb|mem_io_bundle_write_data|:写入内存的数据;
\item \verb|io_mem_debug_read_data|:调试接口读取的内存数据,用于结果验证。
\end{itemize}
所选测试程序为斐波那契数列求解程序,其功能是计算第 10 项的值(即 55并在程序结束时将结果写入数据存储器地址 \texttt{0x00000004}。在 \texttt{CPUTest.scala} 的测试中,系统通过监测访存信号或从调试接口读取该地址的值,验证处理器输出是否正确。
在程序执行的最后阶段,可以在波形中观察到以下现象:
\begin{enumerate}
\item \verb|mem_io_bundle_write_enable| 信号置高,表示 CPU 正在执行 \verb|sw| 指令;
\item \verb|mem_io_bundle_address| 显示为 \verb|0x00000004|,即目标内存地址为 4
\item \verb|mem_io_bundle_write_data| 显示为 \verb|0x00000037|(十进制 55\verb|fib(10)| 的计算结果;
\item 随后,调试接口 \verb|io_mem_debug_read_data| 读出并稳定为相同的值 \verb|0x00000037|。
\end{enumerate}
由此可知CPU 成功将计算结果 55 写入目标地址,测试检查结果正确,验证了数据通路及访存阶段的功能实现。
\section{遇到的问题与改进建议}
\begin{enumerate}[label=\arabic*.]
\item \textbf{问题:立即数生成规则复杂,指导文档未详细展开。}
在实现译码模块时RISC-V 不同指令格式I/S/B/U/J的立即数拼接方式各不相同实验指导对此描述较为简略导致初期实现困难。
\begin{itemize}
\item \textbf{建议}:在实验指导中为每种指令格式提供一个具体的立即数拼接图示或示例代码,能极大帮助理解。
\end{itemize}
\item \textbf{问题:缺少对波形图调试的引导案例。}
初次使用 GTKWave 进行硬件调试时,面对海量的信号线,难以快速定位关键信号。
\begin{itemize}
\item \textbf{建议}:实验文档中可以附加一个简单的调试案例,例如追踪一条 \texttt{add} 指令的数据流,展示如何从取指 PC 开始,逐步添加 `instruction`, `reg\_read\_data`, `alu\_result`, `reg\_write\_data` 等信号,并解释它们在波形图上的时序关系。
\end{itemize}
\end{enumerate}
\section{实验结论}
通过本次实验,我成功设计并实现了一个功能基本完备的 RISC-V 单周期 CPU。在实现过程中我深入理解了数据通路与控制信号在指令执行过程中的协同作用并掌握了模块化的硬件设计思想。通过编写和分析单元测试与集成测试我学会了如何利用波形图对硬件设计进行验证和调试。本次实验极大地加深了我对计算机组成原理中理论知识的实践理解为后续更复杂的处理器设计打下了坚实的基础。
\end{document}

View File

@@ -0,0 +1,44 @@
\relax
\providecommand\hyper@newdestlabel[2]{}
\providecommand*\HyPL@Entry[1]{}
\HyPL@Entry{0<</S/D>>}
\@writefile{toc}{\contentsline {section}{\numberline {1}实验目的}{1}{section.1}\protected@file@percent }
\@writefile{toc}{\contentsline {section}{\numberline {2}实验环境}{1}{section.2}\protected@file@percent }
\@writefile{toc}{\contentsline {section}{\numberline {3}流水线结构与功能划分}{1}{section.3}\protected@file@percent }
\@writefile{toc}{\contentsline {subsection}{\numberline {3.1}三级流水线结构}{1}{subsection.3.1}\protected@file@percent }
\@writefile{toc}{\contentsline {subsection}{\numberline {3.2}五级流水线结构}{2}{subsection.3.2}\protected@file@percent }
\@writefile{toc}{\contentsline {subsection}{\numberline {3.3}缩短分支延迟的五级流水线}{2}{subsection.3.3}\protected@file@percent }
\@writefile{toc}{\contentsline {section}{\numberline {4}模块实现与分析}{3}{section.4}\protected@file@percent }
\@writefile{toc}{\contentsline {subsection}{\numberline {4.1}流水线寄存器PipelineRegister}{3}{subsection.4.1}\protected@file@percent }
\@writefile{toc}{\contentsline {subsubsection}{\numberline {4.1.1}功能}{3}{subsubsection.4.1.1}\protected@file@percent }
\@writefile{toc}{\contentsline {subsubsection}{\numberline {4.1.2}代码实现}{3}{subsubsection.4.1.2}\protected@file@percent }
\newlabel{lst:pipeline_reg}{{1}{3}{流水线寄存器实现}{lstlisting.1}{}}
\@writefile{lol}{\contentsline {lstlisting}{\numberline {1}流水线寄存器实现}{3}{lstlisting.1}\protected@file@percent }
\@writefile{toc}{\contentsline {subsubsection}{\numberline {4.1.3}设计要点}{4}{subsubsection.4.1.3}\protected@file@percent }
\@writefile{toc}{\contentsline {subsection}{\numberline {4.2}控制单元Control}{4}{subsection.4.2}\protected@file@percent }
\@writefile{toc}{\contentsline {subsubsection}{\numberline {4.2.1}功能}{4}{subsubsection.4.2.1}\protected@file@percent }
\@writefile{toc}{\contentsline {subsubsection}{\numberline {4.2.2}代码实现(最终版本)}{4}{subsubsection.4.2.2}\protected@file@percent }
\newlabel{lst:control_final}{{2}{4}{控制单元实现(缩短分支延迟版本)}{lstlisting.2}{}}
\@writefile{lol}{\contentsline {lstlisting}{\numberline {2}控制单元实现(缩短分支延迟版本)}{4}{lstlisting.2}\protected@file@percent }
\@writefile{toc}{\contentsline {subsubsection}{\numberline {4.2.3}设计要点}{5}{subsubsection.4.2.3}\protected@file@percent }
\@writefile{toc}{\contentsline {subsection}{\numberline {4.3}转发单元Forwarding}{6}{subsection.4.3}\protected@file@percent }
\@writefile{toc}{\contentsline {subsubsection}{\numberline {4.3.1}功能}{6}{subsubsection.4.3.1}\protected@file@percent }
\@writefile{toc}{\contentsline {subsubsection}{\numberline {4.3.2}代码实现(最终版本)}{6}{subsubsection.4.3.2}\protected@file@percent }
\newlabel{lst:forward_final}{{3}{6}{转发单元实现(缩短分支延迟版本)}{lstlisting.3}{}}
\@writefile{lol}{\contentsline {lstlisting}{\numberline {3}转发单元实现(缩短分支延迟版本)}{6}{lstlisting.3}\protected@file@percent }
\@writefile{toc}{\contentsline {subsubsection}{\numberline {4.3.3}设计要点}{8}{subsubsection.4.3.3}\protected@file@percent }
\@writefile{toc}{\contentsline {subsection}{\numberline {4.4}译码单元InstructionDecode}{8}{subsection.4.4}\protected@file@percent }
\@writefile{toc}{\contentsline {subsubsection}{\numberline {4.4.1}关键代码}{9}{subsubsection.4.4.1}\protected@file@percent }
\newlabel{lst:id_jump}{{4}{9}{ID 段跳转逻辑}{lstlisting.4}{}}
\@writefile{lol}{\contentsline {lstlisting}{\numberline {4}ID 段跳转逻辑}{9}{lstlisting.4}\protected@file@percent }
\@writefile{toc}{\contentsline {subsubsection}{\numberline {4.4.2}设计要点}{10}{subsubsection.4.4.2}\protected@file@percent }
\@writefile{toc}{\contentsline {section}{\numberline {5}CSR 指令的冒险分析}{10}{section.5}\protected@file@percent }
\@writefile{toc}{\contentsline {subsection}{\numberline {5.1}数据冒险}{10}{subsection.5.1}\protected@file@percent }
\@writefile{toc}{\contentsline {subsection}{\numberline {5.2}控制冒险}{11}{subsection.5.2}\protected@file@percent }
\@writefile{toc}{\contentsline {subsection}{\numberline {5.3}结论}{11}{subsection.5.3}\protected@file@percent }
\@writefile{toc}{\contentsline {section}{\numberline {6}测试结果}{11}{section.6}\protected@file@percent }
\@writefile{toc}{\contentsline {section}{\numberline {7}遇到的问题与改进建议}{12}{section.7}\protected@file@percent }
\@writefile{toc}{\contentsline {subsection}{\numberline {7.1}遇到的问题}{12}{subsection.7.1}\protected@file@percent }
\@writefile{toc}{\contentsline {subsection}{\numberline {7.2}改进建议}{12}{subsection.7.2}\protected@file@percent }
\@writefile{toc}{\contentsline {section}{\numberline {8}实验结论}{13}{section.8}\protected@file@percent }
\gdef \@abspage@last{13}

Binary file not shown.

View File

@@ -0,0 +1,507 @@
\documentclass[12pt]{ctexart} % 使用 ctexart 文档类支持中文12pt 字号
\usepackage[utf8]{inputenc} % 输入编码,保持兼容性
\usepackage[margin=2.5cm]{geometry} % 设置页边距
\usepackage{graphicx} % 导入图片
\usepackage{amsmath} % 支持数学公式
\usepackage{listings} % 代码块高亮
\usepackage{xcolor} % 用于代码高亮颜色
\usepackage{hyperref} % 目录、交叉引用可点击生成PDF书签
\hypersetup{
colorlinks=true, % 这是关键,它会让链接文本以颜色显示,而不是边框
linkcolor=black, % 内部链接(如目录、章节引用)的颜色设为黑色
citecolor=green, % 引用文献的颜色(如果用不到可以忽略或设为黑色)
urlcolor=blue, % URL链接的颜色如果用不到可以忽略或设为黑色
filecolor=magenta, % 文件链接的颜色(如果用不到可以忽略或设为黑色)
% 可以添加更多其他 PDF 元数据,让 PDF 文件信息更完整
pdftitle={实验三:流水线 CPU 设计与实现},
pdfauthor={朱梓涵},
pdfsubject={流水线 RISC-V CPU 设计与实现报告},
pdfkeywords={RISC-V, CPU, Chisel, 流水线, 实验报告}
}
% 目录、交叉引用可点击生成PDF书签
\usepackage{fancyhdr} % 自定义页眉页脚
\usepackage{enumitem} % 列表项自定义
\usepackage{ifthen} % 条件判断(用于图片占位)
% --- 图片缺失占位宏 ---
\newcommand{\includegraphicsorplaceholder}[2][]{%
\IfFileExists{#2}{\includegraphics[#1]{#2}}{\fbox{\parbox[c][0.2\textheight][c]{0.9\textwidth}{\centering Missing image: #2}}}%
}
% --- 页眉页脚设置 ---
\pagestyle{fancy}
\fancyhf{} % 清除所有页眉页脚字段
\fancyhead[L]{\MakeUppercase{实验三:流水线 CPU 设计与实现}} % 左侧页眉:大写实验名称
\fancyfoot[C]{\thepage} % 居中页脚:页码
\renewcommand{\headrulewidth}{0.4pt} % 页眉下方的横线粗细
\renewcommand{\footrulewidth}{0.4pt} % 页脚上方的横线粗细
% 解决 fancyhdr 提示的 \headheight 偏小问题
\setlength{\headheight}{15pt}
% --- 标题信息 ---
\title{\vspace{-2cm}\textbf{实验三:流水线 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},
% 添加 Chisel 相关关键字
morekeywords={when, Mux, MuxLookup, IndexedSeq, U, io, :=, object, val, def, class, override, package, import, extends, with, Bits, UInt, SInt, elsewhen, otherwise, Wire, Bool, RegInit, asSInt, asUInt},
literate={:}{{\color{sol-base02}:}}1
}
\lstset{style=ScalaChiselStyle} % 默认代码风格为 Scala/Chisel
% --- 图片计数器与章节联动 ---
\counterwithin{figure}{section}
\counterwithin{table}{section}
% --- 文档开始 ---
\begin{document}
\maketitle % 生成标题
\thispagestyle{empty} % 标题页无页码
% --- 正文开始 ---
\section{实验目的}
本实验旨在深入理解流水线技术在 CPU 设计中的应用,通过实现三级和五级流水线 CPU掌握流水线中竞争冒险的处理方法。实验目标包括
\begin{enumerate}[label=\arabic*.] % 使用 enumerate 环境创建编号列表
\item 理解流水线寄存器的作用,并实现支持阻塞和清空功能的流水线寄存器。
\item 设计并实现三级流水线 CPU掌握控制冒险的处理方法。
\item 设计并实现五级流水线 CPU学习使用阻塞和转发技术处理数据冒险。
\item 将分支跳转提前到译码阶段,进一步缩短分支延迟,优化流水线性能。
\item 通过编写测试用例,验证流水线 CPU 各模块及整体功能的正确性。
\end{enumerate}
\section{实验环境}
\begin{itemize}
\item \textbf{操作系统}: Windows 11
\item \textbf{开发工具}: IntelliJ IDEA
\item \textbf{构建工具}: SBT
\item \textbf{仿真与测试}: Verilator, chiseltest
\end{itemize}
\section{流水线结构与功能划分}
\subsection{三级流水线结构}
三级流水线将单周期 CPU 的组合逻辑切分为三个阶段:
\begin{itemize}
\item \textbf{取指 (IF)}: 根据 PC 从指令存储器取出指令。
\item \textbf{译码 (ID)}: 解码指令,生成控制信号,从寄存器组读取操作数。
\item \textbf{执行 (EX)}: 执行 ALU 运算、访存和写回操作。
\end{itemize}
在三级流水线中,主要需要处理控制冒险。当 EX 段执行跳转或分支指令时IF 和 ID 段的两条指令需要被清空。
\subsection{五级流水线结构}
五级流水线在三级流水线的基础上,将 EX 段进一步细分:
\begin{itemize}
\item \textbf{取指 (IF)}: 根据 PC 从指令存储器取出指令。
\item \textbf{译码 (ID)}: 解码指令,生成控制信号,从寄存器组读取操作数。
\item \textbf{执行 (EX)}: 执行 ALU 运算。
\item \textbf{访存 (MEM)}: 访问数据存储器。
\item \textbf{写回 (WB)}: 将结果写回寄存器组。
\end{itemize}
五级流水线引入了更复杂的数据冒险,需要使用阻塞和转发技术进行处理。
\subsection{缩短分支延迟的五级流水线}
在最终版本的五级流水线中,将分支和跳转指令的执行从 EX 段提前到 ID 段:
\begin{itemize}
\item 在 ID 段增加加法器,用于计算跳转目标地址。
\item 在 ID 段进行分支条件判断,使用转发逻辑从 MEM 和 WB 段获取操作数。
\item 如果依赖的数据还未产生,则进行阻塞。
\end{itemize}
这样做可以将分支延迟从两个时钟周期减少到一个时钟周期。
\section{模块实现与分析}
\subsection{流水线寄存器PipelineRegister}
\subsubsection{功能}
流水线寄存器是流水线 CPU 的核心组件,用于在相邻流水段之间缓存数据和控制信号。它支持三种操作:
\begin{itemize}
\item \textbf{清空 (flush)}: 将寄存器值重置为默认值,用于清除错误路径上的指令。
\item \textbf{阻塞 (stall)}: 保持当前寄存器值不变,用于暂停流水线。
\item \textbf{正常更新}: 在时钟上升沿将输入值写入寄存器。
\end{itemize}
\subsubsection{代码实现}
\begin{lstlisting}[caption={流水线寄存器实现}, label={lst:pipeline_reg}]
class PipelineRegister(width: Int = Parameters.DataBits, defaultValue: UInt = 0.U) extends Module {
val io = IO(new Bundle {
val stall = Input(Bool())
val flush = Input(Bool())
val in = Input(UInt(width.W))
val out = Output(UInt(width.W))
})
// Lab3(PipelineRegister)
val register = RegInit(defaultValue)
when(io.flush) {
register := defaultValue
}.elsewhen(io.stall) {
}.otherwise {
register := io.in
}
io.out := register
// Lab3(PipelineRegister) End
}
\end{lstlisting}
\subsubsection{设计要点}
\begin{itemize}
\item 优先级:\texttt{flush} 信号优先级最高,其次是 \texttt{stall},最后是正常更新。
\item\texttt{flush} 为高时,寄存器被清空为默认值(通常是 NOP 指令或 0
\item\texttt{stall} 为高时,寄存器保持当前值不变。
\item 其他情况下,寄存器在时钟上升沿更新为输入值。
\end{itemize}
\subsection{控制单元Control}
\subsubsection{功能}
控制单元负责检测流水线中的冒险,并生成相应的控制信号:
\begin{itemize}
\item 检测数据冒险,生成阻塞信号。
\item 检测控制冒险,生成清空信号。
\end{itemize}
\subsubsection{代码实现(最终版本)}
\begin{lstlisting}[caption={控制单元实现(缩短分支延迟版本)}, label={lst:control_final}]
class Control extends Module {
val io = IO(new Bundle {
val jump_flag = Input(Bool())
val jump_instruction_id = Input(Bool())
val rs1_id = Input(UInt(Parameters.PhysicalRegisterAddrWidth))
val rs2_id = Input(UInt(Parameters.PhysicalRegisterAddrWidth))
val memory_read_enable_ex = Input(Bool())
val rd_ex = Input(UInt(Parameters.PhysicalRegisterAddrWidth))
val memory_read_enable_mem = Input(Bool())
val rd_mem = Input(UInt(Parameters.PhysicalRegisterAddrWidth))
val if2id_flush = Output(Bool())
val id2ex_flush = Output(Bool())
val pc_stall = Output(Bool())
val if2id_stall = Output(Bool())
})
// Lab3(Final)
val stall = Wire(Bool())
val load_use_hazard = io.memory_read_enable_ex && io.rd_ex =/= 0.U &&
(io.rd_ex === io.rs1_id || io.rd_ex === io.rs2_id)
val id_jump_needs_ex_alu = io.jump_instruction_id && io.rd_ex =/= 0.U &&
!io.memory_read_enable_ex &&
(io.rd_ex === io.rs1_id || io.rd_ex === io.rs2_id)
val id_jump_needs_mem_load = io.jump_instruction_id && io.memory_read_enable_mem && io.rd_mem =/= 0.U &&
(io.rd_mem === io.rs1_id || io.rd_mem === io.rs2_id)
stall := load_use_hazard || id_jump_needs_ex_alu || id_jump_needs_mem_load
val flush = io.jump_flag && !stall
io.pc_stall := stall
io.if2id_stall := stall
io.if2id_flush := flush
io.id2ex_flush := stall
// Lab3(Final) End
}
\end{lstlisting}
\subsubsection{设计要点}
在缩短分支延迟的版本中,由于分支和跳转指令在 ID 段执行,控制单元需要处理以下几种情况:
\begin{enumerate}
\item \textbf{Load-use 冒险}: 当 ID 段的指令需要使用 EX 段 load 指令的结果时,必须阻塞一个周期,等待数据从 MEM 段产生后通过转发获取。
\item \textbf{跳转指令依赖 EX 段 ALU 结果}: 当 ID 段的跳转/分支指令需要使用 EX 段的 ALU 计算结果时,需要阻塞一个周期。虽然 EX 段的结果会在下个周期进入 MEM 段,可以通过转发提供给 ID 段,但由于跳转判断需要在 ID 段完成,因此必须等待一个周期。
\item \textbf{跳转指令依赖 MEM 段 load 结果}: 当 ID 段的跳转/分支指令需要使用 MEM 段 load 指令的结果时,需要阻塞一个周期,等待数据进入 WB 段后通过转发获取。
\item \textbf{控制冒险}: 当跳转确实发生时(\texttt{jump\_flag} 为真且无阻塞),需要清空 IF2ID 流水线寄存器,丢弃已取出的错误路径指令。
\item \textbf{阻塞时插入气泡}: 当发生阻塞时,需要清空 ID2EX 流水线寄存器,在 EX 段插入一条 NOP 指令(气泡),防止 ID 段的指令被重复执行。
\end{enumerate}
\subsection{转发单元Forwarding}
\subsubsection{功能}
转发单元负责检测数据冒险,并生成转发控制信号,使得 EX 或 ID 段可以直接从流水线寄存器中获取所需数据,而不必等待数据写回寄存器组,从而减少流水线阻塞。
\subsubsection{代码实现(最终版本)}
\begin{lstlisting}[caption={转发单元实现(缩短分支延迟版本)}, label={lst:forward_final}]
object ForwardingType {
val NoForward = 0.U(2.W)
val ForwardFromMEM = 1.U(2.W)
val ForwardFromWB = 2.U(2.W)
}
class Forwarding extends Module {
val io = IO(new Bundle() {
val rs1_id = Input(UInt(Parameters.PhysicalRegisterAddrWidth))
val rs2_id = Input(UInt(Parameters.PhysicalRegisterAddrWidth))
val rs1_ex = Input(UInt(Parameters.PhysicalRegisterAddrWidth))
val rs2_ex = Input(UInt(Parameters.PhysicalRegisterAddrWidth))
val rd_mem = Input(UInt(Parameters.PhysicalRegisterAddrWidth))
val reg_write_enable_mem = Input(Bool())
val rd_wb = Input(UInt(Parameters.PhysicalRegisterAddrWidth))
val reg_write_enable_wb = Input(Bool())
val reg1_forward_id = Output(UInt(2.W))
val reg2_forward_id = Output(UInt(2.W))
val reg1_forward_ex = Output(UInt(2.W))
val reg2_forward_ex = Output(UInt(2.W))
})
// Lab3(Final)
val ex_mem_hazard_rs1 = io.reg_write_enable_mem && io.rd_mem =/= 0.U && (io.rd_mem === io.rs1_ex)
val ex_mem_hazard_rs2 = io.reg_write_enable_mem && io.rd_mem =/= 0.U && (io.rd_mem === io.rs2_ex)
val ex_wb_hazard_rs1 = io.reg_write_enable_wb && io.rd_wb =/= 0.U && (io.rd_wb === io.rs1_ex)
val ex_wb_hazard_rs2 = io.reg_write_enable_wb && io.rd_wb =/= 0.U && (io.rd_wb === io.rs2_ex)
io.reg1_forward_ex := Mux(ex_mem_hazard_rs1, ForwardingType.ForwardFromMEM,
Mux(ex_wb_hazard_rs1, ForwardingType.ForwardFromWB, ForwardingType.NoForward))
io.reg2_forward_ex := Mux(ex_mem_hazard_rs2, ForwardingType.ForwardFromMEM,
Mux(ex_wb_hazard_rs2, ForwardingType.ForwardFromWB, ForwardingType.NoForward))
val id_mem_hazard_rs1 = io.reg_write_enable_mem && io.rd_mem =/= 0.U && (io.rd_mem === io.rs1_id)
val id_mem_hazard_rs2 = io.reg_write_enable_mem && io.rd_mem =/= 0.U && (io.rd_mem === io.rs2_id)
val id_wb_hazard_rs1 = io.reg_write_enable_wb && io.rd_wb =/= 0.U && !id_mem_hazard_rs1 && (io.rd_wb === io.rs1_id)
val id_wb_hazard_rs2 = io.reg_write_enable_wb && io.rd_wb =/= 0.U && !id_mem_hazard_rs2 && (io.rd_wb === io.rs2_id)
io.reg1_forward_id := Mux(id_mem_hazard_rs1, ForwardingType.ForwardFromMEM,
Mux(id_wb_hazard_rs1, ForwardingType.ForwardFromWB, ForwardingType.NoForward))
io.reg2_forward_id := Mux(id_mem_hazard_rs2, ForwardingType.ForwardFromMEM,
Mux(id_wb_hazard_rs2, ForwardingType.ForwardFromWB, ForwardingType.NoForward))
// Lab3(Final) End
}
\end{lstlisting}
\subsubsection{设计要点}
转发单元需要同时处理到 EX 段和到 ID 段的转发:
\begin{enumerate}
\item \textbf{EX 段转发}:
\begin{itemize}
\item 如果 EX 段的源寄存器与 MEM 段的目标寄存器相同,从 MEM 段转发。
\item 如果 EX 段的源寄存器与 WB 段的目标寄存器相同,从 WB 段转发。
\item MEM 段转发优先级高于 WB 段转发(保证获取最新的值)。
\end{itemize}
\item \textbf{ID 段转发}:
\begin{itemize}
\item 由于跳转指令在 ID 段执行,需要将 MEM 和 WB 段的结果转发到 ID 段。
\item 如果 ID 段的源寄存器与 MEM 段的目标寄存器相同,从 MEM 段转发。
\item 如果 ID 段的源寄存器与 WB 段的目标寄存器相同(且不与 MEM 段冲突),从 WB 段转发。
\item MEM 段转发优先级同样高于 WB 段转发。
\end{itemize}
\item \textbf{寄存器 x0 的特殊处理}:
\begin{itemize}
\item RISC-V 中寄存器 x0 恒为 0写入 x0 的结果会被丢弃。
\item 因此,转发逻辑中需要检查目标寄存器是否为 0\texttt{rd =/= 0.U}),避免不必要的转发。
\end{itemize}
\end{enumerate}
\subsection{译码单元InstructionDecode}
在缩短分支延迟的版本中,译码单元需要在 ID 段完成分支和跳转指令的执行。
\subsubsection{关键代码}
\begin{lstlisting}[caption={ID 段跳转逻辑}, label={lst:id_jump}]
val reg1_data = MuxLookup(io.reg1_forward, io.reg1_data)(
Seq(
ForwardingType.ForwardFromMEM -> io.forward_from_mem,
ForwardingType.ForwardFromWB -> io.forward_from_wb
)
)
val reg2_data = MuxLookup(io.reg2_forward, io.reg2_data)(
Seq(
ForwardingType.ForwardFromMEM -> io.forward_from_mem,
ForwardingType.ForwardFromWB -> io.forward_from_wb
)
)
val is_jump_instruction = opcode === Instructions.jal || opcode === Instructions.jalr || opcode === InstructionTypes.B
io.ctrl_jump_instruction := is_jump_instruction
val jump_condition_met =
(opcode === Instructions.jal) ||
(opcode === Instructions.jalr) ||
(opcode === InstructionTypes.B) && MuxLookup(
funct3,
false.B,
IndexedSeq(
InstructionsTypeB.beq -> (reg1_data === reg2_data),
InstructionsTypeB.bne -> (reg1_data =/= reg2_data),
InstructionsTypeB.blt -> (reg1_data.asSInt < reg2_data.asSInt),
InstructionsTypeB.bge -> (reg1_data.asSInt >= reg2_data.asSInt),
InstructionsTypeB.bltu -> (reg1_data < reg2_data),
InstructionsTypeB.bgeu -> (reg1_data >= reg2_data)
)
)
val jump_address = Mux(
opcode === Instructions.jalr,
(reg1_data.asSInt + io.ex_immediate.asSInt).asUInt,
(io.instruction_address.asSInt + io.ex_immediate.asSInt).asUInt
) & (~1.U(Parameters.DataWidth)).asUInt
io.if_jump_flag := jump_condition_met || io.interrupt_assert
io.if_jump_address := Mux(
io.interrupt_assert,
io.interrupt_handler_address,
jump_address
)
\end{lstlisting}
\subsubsection{设计要点}
\begin{itemize}
\item 在 ID 段使用转发逻辑获取寄存器数据,确保使用的是最新的值。
\item 根据指令类型jal/jalr/分支)判断跳转条件是否满足。
\item 在 ID 段计算跳转目标地址,无需等到 EX 段。
\item 跳转地址需要与 \texttt{\textasciitilde 1} 进行与运算确保地址为偶数RISC-V 要求)。
\end{itemize}
\section{CSR 指令的冒险分析}
\subsection{数据冒险}
CSRControl and Status Register指令用于读写控制和状态寄存器。在本实验的实现中CSR 指令可能产生以下数据冒险:
\begin{enumerate}
\item \textbf{RAWRead After Write冒险}:
\begin{itemize}
\item 当后续指令需要读取 CSR 指令写入的通用寄存器时,会发生 RAW 冒险。
\item 例如:\texttt{csrrw x1, mstatus, x2} 后跟 \texttt{add x3, x1, x4}
\item 解决方法与普通指令相同通过转发或阻塞解决。CSR 指令在 WB 段将结果写回通用寄存器,可以通过 MEM-EX 和 WB-EX 转发路径提供数据。
\end{itemize}
\item \textbf{CSR 寄存器的 RAW 冒险}:
\begin{itemize}
\item 当连续的 CSR 指令访问同一个 CSR 寄存器时,后续指令可能读取到过时的值。
\item 例如:\texttt{csrrw x1, mstatus, x2} 后跟 \texttt{csrrs x3, mstatus, x4}
\item 本实验未专门处理 CSR 寄存器间的冒险。在实际实现中,可以通过以下方式处理:
\begin{itemize}
\item 检测 CSR 地址冲突,插入阻塞。
\item 为 CSR 单元添加转发逻辑。
\item 简化方案CSR 指令较少出现连续访问,可以通过编译器重排指令避免。
\end{itemize}
\end{itemize}
\end{enumerate}
\subsection{控制冒险}
CSR 指令本身不产生控制冒险(它们不是跳转或分支指令)。但是,某些 CSR 指令(如 \texttt{mret})会改变程序执行流:
\begin{itemize}
\item \texttt{mret} 指令用于从异常处理返回,会跳转到 \texttt{mepc} 寄存器指定的地址。
\item 这类指令在本实验中被当作特殊的跳转指令处理,会触发流水线清空。
\end{itemize}
\subsection{结论}
\begin{itemize}
\item CSR 指令写入通用寄存器的数据冒险可以通过现有的转发和阻塞机制解决。
\item CSR 寄存器间的 RAW 冒险在本实验中未专门处理,实际应用中需要额外的检测和阻塞逻辑。
\item \texttt{mret} 等特殊 CSR 指令产生的控制冒险可以通过流水线清空解决。
\end{itemize}
\section{测试结果}
本实验通过了所有测试用例,包括:
\begin{itemize}
\item \textbf{流水线寄存器测试}: 验证了流水线寄存器的阻塞和清空功能。
\item \textbf{三级流水线 CPU 测试}: 验证了控制冒险的处理,包括递归计算斐波那契数列、快速排序、单字节加载存储等。
\item \textbf{五级流水线阻塞CPU 测试}: 验证了使用阻塞解决数据冒险的正确性。
\item \textbf{五级流水线转发CPU 测试}: 验证了使用转发减少阻塞的优化效果。
\item \textbf{五级流水线缩短分支延迟CPU 测试}: 验证了将跳转提前到 ID 段的优化。
\end{itemize}
所有测试均在 60 秒内完成,共 20 个测试用例全部通过,验证了流水线 CPU 各阶段实现的正确性。
\section{遇到的问题与改进建议}
\subsection{遇到的问题}
\begin{enumerate}[label=\arabic*.]
\item \textbf{问题:转发逻辑的优先级容易混淆。}
在实现转发单元时,需要处理 MEM 段和 WB 段同时满足转发条件的情况。根据流水线原理,应优先使用 MEM 段的数据(更新),但在编写代码时容易写反优先级。
\begin{itemize}
\item \textbf{解决方法}:仔细分析数据流,绘制流水线状态图,明确各阶段数据的新旧关系。使用嵌套的 \texttt{Mux} 语句时,外层 \texttt{Mux} 判断优先级更高的条件。
\end{itemize}
\item \textbf{问题:跳转指令提前到 ID 段后,冒险情况增多。}
将跳转判断从 EX 段提前到 ID 段后,需要考虑 ID 段的跳转指令与 EX、MEM 段指令的依赖关系,冒险检测逻辑变得更加复杂。
\begin{itemize}
\item \textbf{解决方法}:按照实验指导的提示,列出所有可能的冒险组合表格,逐一分析每种情况,确保覆盖所有冒险场景。
\end{itemize}
\item \textbf{问题:调试困难,波形图信号繁多。}
五级流水线 CPU 信号众多,在 GTKWave 中查看波形时很难快速定位错误信号。
\begin{itemize}
\item \textbf{解决方法}:使用 Chisel 的 \texttt{printf} 调试功能,在关键部件(如寄存器文件、控制单元)添加打印语句,输出关键信号的值。结合简单的测试程序(如 \texttt{sb.S}),逐步排查错误。
\end{itemize}
\end{enumerate}
\subsection{改进建议}
\begin{enumerate}[label=\arabic*.]
\item \textbf{建议:提供更多调试案例和方法指导。}
实验指导中的调试部分较为简略,建议增加具体的调试案例,例如如何追踪一条指令在流水线中的完整执行过程,如何分析波形图定位冒险问题等。
\item \textbf{建议:增加可视化工具。}
流水线状态可视化对理解和调试非常有帮助。建议提供或推荐一些工具,能够将流水线各阶段的指令和数据流以图形化方式展示。
\item \textbf{建议:补充 CSR 指令冒险处理的说明。}
实验指导中未详细讨论 CSR 指令的冒险处理,建议在后续版本中补充相关内容,或明确说明本实验中 CSR 指令的简化假设。
\end{enumerate}
\section{实验结论}
通过本次实验,我成功实现了三级和五级流水线 CPU深入理解了流水线技术和竞争冒险的处理方法。在实现过程中我掌握了
\begin{itemize}
\item 流水线寄存器的设计与实现。
\item 控制冒险的检测与清空机制。
\item 数据冒险的阻塞和转发解决方案。
\item 分支延迟优化技术(将跳转提前到 ID 段)。
\end{itemize}
通过编写和分析测试用例,我学会了如何验证流水线 CPU 的正确性,并掌握了使用打印和波形图进行调试的方法。本次实验使我对计算机组成原理中的流水线技术有了更深入的实践理解,为后续更复杂的处理器设计奠定了坚实的基础。
\end{document}

BIN
lab3/朱梓涵24325356.rar Normal file

Binary file not shown.

View File

@@ -0,0 +1,69 @@
// Copyright 2021 Howard Lau
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package riscv.core
import chisel3._
import chisel3.experimental.ChiselEnum
import chisel3.util._
import riscv.Parameters
object ALUFunctions extends ChiselEnum {
val zero, add, sub, sll, slt, xor, or, and, srl, sra, sltu = Value
}
class ALU extends Module {
val io = IO(new Bundle {
val func = Input(ALUFunctions())
val op1 = Input(UInt(Parameters.DataWidth))
val op2 = Input(UInt(Parameters.DataWidth))
val result = Output(UInt(Parameters.DataWidth))
})
io.result := 0.U
switch(io.func) {
is(ALUFunctions.add) {
io.result := io.op1 + io.op2
}
is(ALUFunctions.sub) {
io.result := io.op1 - io.op2
}
is(ALUFunctions.sll) {
io.result := io.op1 << io.op2(4, 0)
}
is(ALUFunctions.slt) {
io.result := io.op1.asSInt < io.op2.asSInt
}
is(ALUFunctions.xor) {
io.result := io.op1 ^ io.op2
}
is(ALUFunctions.or) {
io.result := io.op1 | io.op2
}
is(ALUFunctions.and) {
io.result := io.op1 & io.op2
}
is(ALUFunctions.srl) {
io.result := io.op1 >> io.op2(4, 0)
}
is(ALUFunctions.sra) {
io.result := (io.op1.asSInt >> io.op2(4, 0)).asUInt
}
is(ALUFunctions.sltu) {
io.result := io.op1 < io.op2
}
}
}

View File

@@ -0,0 +1,87 @@
// Copyright 2021 Howard Lau
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package riscv.core
import chisel3._
import chisel3.util._
import riscv.core.threestage.{InstructionTypes, Instructions, InstructionsTypeI, InstructionsTypeR}
class ALUControl extends Module {
val io = IO(new Bundle {
val opcode = Input(UInt(7.W))
val funct3 = Input(UInt(3.W))
val funct7 = Input(UInt(7.W))
val alu_funct = Output(ALUFunctions())
})
io.alu_funct := ALUFunctions.zero
switch(io.opcode) {
is(InstructionTypes.I) {
io.alu_funct := MuxLookup(
io.funct3,
ALUFunctions.zero,
IndexedSeq(
InstructionsTypeI.addi -> ALUFunctions.add,
InstructionsTypeI.slli -> ALUFunctions.sll,
InstructionsTypeI.slti -> ALUFunctions.slt,
InstructionsTypeI.sltiu -> ALUFunctions.sltu,
InstructionsTypeI.xori -> ALUFunctions.xor,
InstructionsTypeI.ori -> ALUFunctions.or,
InstructionsTypeI.andi -> ALUFunctions.and,
InstructionsTypeI.sri -> Mux(io.funct7(5), ALUFunctions.sra, ALUFunctions.srl)
),
)
}
is(InstructionTypes.RM) {
io.alu_funct := MuxLookup(
io.funct3,
ALUFunctions.zero,
IndexedSeq(
InstructionsTypeR.add_sub -> Mux(io.funct7(5), ALUFunctions.sub, ALUFunctions.add),
InstructionsTypeR.sll -> ALUFunctions.sll,
InstructionsTypeR.slt -> ALUFunctions.slt,
InstructionsTypeR.sltu -> ALUFunctions.sltu,
InstructionsTypeR.xor -> ALUFunctions.xor,
InstructionsTypeR.or -> ALUFunctions.or,
InstructionsTypeR.and -> ALUFunctions.and,
InstructionsTypeR.sr -> Mux(io.funct7(5), ALUFunctions.sra, ALUFunctions.srl)
),
)
}
is(InstructionTypes.B) {
io.alu_funct := ALUFunctions.add
}
is(InstructionTypes.L) {
io.alu_funct := ALUFunctions.add
}
is(InstructionTypes.S) {
io.alu_funct := ALUFunctions.add
}
is(Instructions.jal) {
io.alu_funct := ALUFunctions.add
}
is(Instructions.jalr) {
io.alu_funct := ALUFunctions.add
}
is(Instructions.lui) {
io.alu_funct := ALUFunctions.add
}
is(Instructions.auipc) {
io.alu_funct := ALUFunctions.add
}
}
}

View File

@@ -0,0 +1,40 @@
// Copyright 2022 Howard Lau
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package riscv.core
import chisel3._
import riscv.ImplementationType
import riscv.core.fivestage_final.{CPU => FiveStageCPUFinal}
import riscv.core.fivestage_forward.{CPU => FiveStageCPUForward}
import riscv.core.fivestage_stall.{CPU => FiveStageCPUStall}
import riscv.core.threestage.{CPU => ThreeStageCPU}
class CPU(val implementation: Int = ImplementationType.FiveStageFinal) extends Module {
val io = IO(new CPUBundle)
implementation match {
case ImplementationType.ThreeStage =>
val cpu = Module(new ThreeStageCPU)
cpu.io <> io
case ImplementationType.FiveStageStall =>
val cpu = Module(new FiveStageCPUStall)
cpu.io <> io
case ImplementationType.FiveStageForward =>
val cpu = Module(new FiveStageCPUForward)
cpu.io <> io
case _ =>
val cpu = Module(new FiveStageCPUFinal)
cpu.io <> io
}
}

View File

@@ -0,0 +1,30 @@
// Copyright 2022 Howard Lau
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package riscv.core
import chisel3._
import peripheral.RAMBundle
import riscv.Parameters
class CPUBundle extends Bundle {
val instruction_address = Output(UInt(Parameters.AddrWidth))
val instruction = Input(UInt(Parameters.DataWidth))
val instruction_valid = Input(Bool())
val memory_bundle = Flipped(new RAMBundle)
val device_select = Output(UInt(Parameters.SlaveDeviceCountBits.W))
val interrupt_flag = Input(UInt(Parameters.InterruptFlagWidth))
val debug_read_address = Input(UInt(Parameters.PhysicalRegisterAddrWidth))
val debug_read_data = Output(UInt(Parameters.DataWidth))
}

View File

@@ -0,0 +1,99 @@
// Copyright 2021 Howard Lau
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package riscv.core
import chisel3._
import chisel3.util._
import riscv.Parameters
import riscv.core.threestage.CSRDirectAccessBundle
object CSRRegister {
// Refer to Spec. Vol.II Page 8-10
val MSTATUS = 0x300.U(Parameters.CSRRegisterAddrWidth)
val MIE = 0x304.U(Parameters.CSRRegisterAddrWidth)
val MTVEC = 0x305.U(Parameters.CSRRegisterAddrWidth)
val MSCRATCH = 0x340.U(Parameters.CSRRegisterAddrWidth)
val MEPC = 0x341.U(Parameters.CSRRegisterAddrWidth)
val MCAUSE = 0x342.U(Parameters.CSRRegisterAddrWidth)
val CycleL = 0xc00.U(Parameters.CSRRegisterAddrWidth)
val CycleH = 0xc80.U(Parameters.CSRRegisterAddrWidth)
}
class CSR extends Module {
val io = IO(new Bundle {
val reg_read_address_id = Input(UInt(Parameters.CSRRegisterAddrWidth))
val reg_write_enable_ex = Input(Bool())
val reg_write_address_ex = Input(UInt(Parameters.CSRRegisterAddrWidth))
val reg_write_data_ex = Input(UInt(Parameters.DataWidth))
val id_reg_read_data = Output(UInt(Parameters.DataWidth))
val clint_access_bundle = Flipped(new CSRDirectAccessBundle)
})
val mstatus = RegInit(UInt(Parameters.DataWidth), 0.U)
val mie = RegInit(UInt(Parameters.DataWidth), 0.U)
val mtvec = RegInit(UInt(Parameters.DataWidth), 0.U)
val mscratch = RegInit(UInt(Parameters.DataWidth), 0.U)
val mepc = RegInit(UInt(Parameters.DataWidth), 0.U)
val mcause = RegInit(UInt(Parameters.DataWidth), 0.U)
val cycles = RegInit(UInt(64.W), 0.U)
val regLUT =
IndexedSeq(
CSRRegister.MSTATUS -> mstatus,
CSRRegister.MIE -> mie,
CSRRegister.MTVEC -> mtvec,
CSRRegister.MSCRATCH -> mscratch,
CSRRegister.MEPC -> mepc,
CSRRegister.MCAUSE -> mcause,
CSRRegister.CycleL -> cycles(31, 0),
CSRRegister.CycleH -> cycles(63, 32),
)
cycles := cycles + 1.U
// If the pipeline and the CLINT are going to read and write the CSR at the same time, let the pipeline write first.
// This is implemented in a single cycle by passing reg_write_data_ex to clint and writing the data from the CLINT to the CSR.
io.id_reg_read_data := MuxLookup(io.reg_read_address_id, 0.U, regLUT)
io.clint_access_bundle.mstatus := Mux(io.reg_write_enable_ex && io.reg_write_address_ex === CSRRegister.MSTATUS, io.reg_write_data_ex, mstatus)
io.clint_access_bundle.mtvec := Mux(io.reg_write_enable_ex && io.reg_write_address_ex === CSRRegister.MTVEC, io.reg_write_data_ex, mtvec)
io.clint_access_bundle.mcause := Mux(io.reg_write_enable_ex && io.reg_write_address_ex === CSRRegister.MCAUSE, io.reg_write_data_ex, mcause)
io.clint_access_bundle.mepc := Mux(io.reg_write_enable_ex && io.reg_write_address_ex === CSRRegister.MEPC, io.reg_write_data_ex, mepc)
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_ex) {
when(io.reg_write_address_ex === CSRRegister.MSTATUS) {
mstatus := io.reg_write_data_ex
}.elsewhen(io.reg_write_address_ex === CSRRegister.MEPC) {
mepc := io.reg_write_data_ex
}.elsewhen(io.reg_write_address_ex === CSRRegister.MCAUSE) {
mcause := io.reg_write_data_ex
}
}
when(io.reg_write_enable_ex) {
when(io.reg_write_address_ex === CSRRegister.MIE) {
mie := io.reg_write_data_ex
}.elsewhen(io.reg_write_address_ex === CSRRegister.MTVEC) {
mtvec := io.reg_write_data_ex
}.elsewhen(io.reg_write_address_ex === CSRRegister.MSCRATCH) {
mscratch := io.reg_write_data_ex
}
}
}

View File

@@ -0,0 +1,39 @@
// Copyright 2021 Howard Lau
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package riscv.core
import chisel3._
import riscv.Parameters
class PipelineRegister(width: Int = Parameters.DataBits, defaultValue: UInt = 0.U) extends Module {
val io = IO(new Bundle {
val stall = Input(Bool())
val flush = Input(Bool())
val in = Input(UInt(width.W))
val out = Output(UInt(width.W))
})
// Lab3(PipelineRegister)
val register = RegInit(defaultValue)
when(io.flush) {
register := defaultValue
}.elsewhen(io.stall) {
}.otherwise {
register := io.in
}
io.out := register
// Lab3(PipelineRegister) End
}

View File

@@ -0,0 +1,78 @@
// Copyright 2021 Howard Lau
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package riscv.core
import chisel3._
import chisel3.util._
import riscv.Parameters
object Registers extends Enumeration {
type Register = Value
val zero,
ra, sp, gp, tp,
t0, t1, t2, fp,
s1,
a0, a1, a2, a3, a4, a5, a6, a7,
s2, s3, s4, s5, s6, s7, s8, s9, s10, s11,
t3, t4, t5, t6 = Value
}
class RegisterFile extends Module {
val io = IO(new Bundle {
val write_enable = Input(Bool())
val write_address = Input(UInt(Parameters.PhysicalRegisterAddrWidth))
val write_data = Input(UInt(Parameters.DataWidth))
val read_address1 = Input(UInt(Parameters.PhysicalRegisterAddrWidth))
val read_address2 = Input(UInt(Parameters.PhysicalRegisterAddrWidth))
val read_data1 = Output(UInt(Parameters.DataWidth))
val read_data2 = Output(UInt(Parameters.DataWidth))
val debug_read_address = Input(UInt(Parameters.PhysicalRegisterAddrWidth))
val debug_read_data = Output(UInt(Parameters.DataWidth))
})
val registers = Reg(Vec(Parameters.PhysicalRegisters, UInt(Parameters.DataWidth)))
when(!reset.asBool) {
when(io.write_enable && io.write_address =/= 0.U) {
registers(io.write_address) := io.write_data
}
}
io.read_data1 := MuxCase(
registers(io.read_address1),
IndexedSeq(
(io.read_address1 === 0.U) -> 0.U,
(io.read_address1 === io.write_address && io.write_enable) -> io.write_data
)
)
io.read_data2 := MuxCase(
registers(io.read_address2),
IndexedSeq(
(io.read_address2 === 0.U) -> 0.U,
(io.read_address2 === io.write_address && io.write_enable) -> io.write_data
)
)
io.debug_read_data := MuxCase(
registers(io.debug_read_address),
IndexedSeq(
(io.debug_read_address === 0.U) -> 0.U,
(io.debug_read_address === io.write_address && io.write_enable) -> io.write_data
)
)
}

View File

@@ -0,0 +1,101 @@
// Copyright 2021 Howard Lau
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package riscv.core.fivestage_final
import chisel3._
import chisel3.util.MuxLookup
import riscv.Parameters
object InterruptStatus {
val None = 0x0.U(8.W)
val Timer0 = 0x1.U(8.W)
val Ret = 0xFF.U(8.W)
}
class CSRDirectAccessBundle extends Bundle {
val mstatus = Input(UInt(Parameters.DataWidth))
val mepc = Input(UInt(Parameters.DataWidth))
val mcause = Input(UInt(Parameters.DataWidth))
val mtvec = Input(UInt(Parameters.DataWidth))
val mstatus_write_data = Output(UInt(Parameters.DataWidth))
val mepc_write_data = Output(UInt(Parameters.DataWidth))
val mcause_write_data = Output(UInt(Parameters.DataWidth))
val direct_write_enable = Output(Bool())
}
// Core Local Interrupt Controller
class CLINT extends Module {
val io = IO(new Bundle {
val interrupt_flag = Input(UInt(Parameters.InterruptFlagWidth))
val instruction_id = Input(UInt(Parameters.InstructionWidth))
val instruction_address_if = Input(UInt(Parameters.AddrWidth))
val jump_flag = Input(Bool())
val jump_address = Input(UInt(Parameters.AddrWidth))
val id_interrupt_handler_address = Output(UInt(Parameters.AddrWidth))
val id_interrupt_assert = Output(Bool())
val csr_bundle = new CSRDirectAccessBundle
})
val interrupt_enable = io.csr_bundle.mstatus(3)
val instruction_address = Mux(
io.jump_flag,
io.jump_address,
io.instruction_address_if,
)
val mstatus_disable_interrupt = io.csr_bundle.mstatus(31, 4) ## 0.U(1.W) ## io.csr_bundle.mstatus(2, 0)
val mstatus_recover_interrupt = io.csr_bundle.mstatus(31, 4) ## io.csr_bundle.mstatus(7) ## io.csr_bundle.mstatus(2, 0)
when(io.instruction_id === InstructionsEnv.ecall || io.instruction_id === InstructionsEnv.ebreak) {
io.csr_bundle.mstatus_write_data := mstatus_disable_interrupt
io.csr_bundle.mepc_write_data := instruction_address
io.csr_bundle.mcause_write_data := MuxLookup(
io.instruction_id,
10.U,
IndexedSeq(
InstructionsEnv.ecall -> 11.U,
InstructionsEnv.ebreak -> 3.U,
)
)
io.csr_bundle.direct_write_enable := true.B
io.id_interrupt_assert := true.B
io.id_interrupt_handler_address := io.csr_bundle.mtvec
}.elsewhen(io.interrupt_flag =/= InterruptStatus.None && interrupt_enable) {
io.csr_bundle.mstatus_write_data := mstatus_disable_interrupt
io.csr_bundle.mepc_write_data := instruction_address
io.csr_bundle.mcause_write_data := Mux(io.interrupt_flag(0), 0x80000007L.U, 0x8000000BL.U)
io.csr_bundle.direct_write_enable := true.B
io.id_interrupt_assert := true.B
io.id_interrupt_handler_address := io.csr_bundle.mtvec
}.elsewhen(io.instruction_id === InstructionsRet.mret) {
io.csr_bundle.mstatus_write_data := mstatus_recover_interrupt
io.csr_bundle.mepc_write_data := io.csr_bundle.mepc
io.csr_bundle.mcause_write_data := io.csr_bundle.mcause
io.csr_bundle.direct_write_enable := true.B
io.id_interrupt_assert := true.B
io.id_interrupt_handler_address := io.csr_bundle.mepc
}.otherwise {
io.csr_bundle.mstatus_write_data := io.csr_bundle.mstatus
io.csr_bundle.mepc_write_data := io.csr_bundle.mepc
io.csr_bundle.mcause_write_data := io.csr_bundle.mcause
io.csr_bundle.direct_write_enable := false.B
io.id_interrupt_assert := false.B
io.id_interrupt_handler_address := 0.U
}
}

View File

@@ -0,0 +1,169 @@
// Copyright 2021 Howard Lau
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package riscv.core.fivestage_final
import chisel3._
import riscv.Parameters
import riscv.core.{CPUBundle, CSR, RegisterFile}
class CPU extends Module {
val io = IO(new CPUBundle)
val ctrl = Module(new Control)
val regs = Module(new RegisterFile)
val inst_fetch = Module(new InstructionFetch)
val if2id = Module(new IF2ID)
val id = Module(new InstructionDecode)
val id2ex = Module(new ID2EX)
val ex = Module(new Execute)
val ex2mem = Module(new EX2MEM)
val mem = Module(new MemoryAccess)
val mem2wb = Module(new MEM2WB)
val wb = Module(new WriteBack)
val forwarding = Module(new Forwarding)
val clint = Module(new CLINT)
val csr_regs = Module(new CSR)
ctrl.io.jump_flag := id.io.if_jump_flag
ctrl.io.jump_instruction_id := id.io.ctrl_jump_instruction
ctrl.io.rs1_id := id.io.regs_reg1_read_address
ctrl.io.rs2_id := id.io.regs_reg2_read_address
ctrl.io.memory_read_enable_ex := id2ex.io.output_memory_read_enable
ctrl.io.rd_ex := id2ex.io.output_regs_write_address
ctrl.io.memory_read_enable_mem := ex2mem.io.output_memory_read_enable
ctrl.io.rd_mem := ex2mem.io.output_regs_write_address
regs.io.write_enable := mem2wb.io.output_regs_write_enable
regs.io.write_address := mem2wb.io.output_regs_write_address
regs.io.write_data := wb.io.regs_write_data
regs.io.read_address1 := id.io.regs_reg1_read_address
regs.io.read_address2 := id.io.regs_reg2_read_address
regs.io.debug_read_address := io.debug_read_address
io.debug_read_data := regs.io.debug_read_data
io.instruction_address := inst_fetch.io.instruction_address
inst_fetch.io.stall_flag_ctrl := ctrl.io.pc_stall
inst_fetch.io.jump_flag_id := id.io.if_jump_flag
inst_fetch.io.jump_address_id := id.io.if_jump_address
inst_fetch.io.rom_instruction := io.instruction
inst_fetch.io.instruction_valid := io.instruction_valid
if2id.io.stall := ctrl.io.if2id_stall
if2id.io.flush := ctrl.io.if2id_flush
if2id.io.instruction := inst_fetch.io.id_instruction
if2id.io.instruction_address := inst_fetch.io.instruction_address
if2id.io.interrupt_flag := io.interrupt_flag
id.io.instruction := if2id.io.output_instruction
id.io.instruction_address := if2id.io.output_instruction_address
id.io.reg1_data := regs.io.read_data1
id.io.reg2_data := regs.io.read_data2
id.io.forward_from_mem := mem.io.forward_data
id.io.forward_from_wb := wb.io.regs_write_data
id.io.reg1_forward := forwarding.io.reg1_forward_id
id.io.reg2_forward := forwarding.io.reg2_forward_id
id.io.interrupt_assert := clint.io.id_interrupt_assert
id.io.interrupt_handler_address := clint.io.id_interrupt_handler_address
id2ex.io.flush := ctrl.io.id2ex_flush
id2ex.io.instruction := if2id.io.output_instruction
id2ex.io.instruction_address := if2id.io.output_instruction_address
id2ex.io.reg1_data := regs.io.read_data1
id2ex.io.reg2_data := regs.io.read_data2
id2ex.io.regs_reg1_read_address := id.io.regs_reg1_read_address
id2ex.io.regs_reg2_read_address := id.io.regs_reg2_read_address
id2ex.io.regs_write_enable := id.io.ex_reg_write_enable
id2ex.io.regs_write_address := id.io.ex_reg_write_address
id2ex.io.regs_write_source := id.io.ex_reg_write_source
id2ex.io.immediate := id.io.ex_immediate
id2ex.io.aluop1_source := id.io.ex_aluop1_source
id2ex.io.aluop2_source := id.io.ex_aluop2_source
id2ex.io.csr_write_enable := id.io.ex_csr_write_enable
id2ex.io.csr_address := id.io.ex_csr_address
id2ex.io.memory_read_enable := id.io.ex_memory_read_enable
id2ex.io.memory_write_enable := id.io.ex_memory_write_enable
id2ex.io.csr_read_data := csr_regs.io.id_reg_read_data
ex.io.instruction := id2ex.io.output_instruction
ex.io.instruction_address := id2ex.io.output_instruction_address
ex.io.reg1_data := id2ex.io.output_reg1_data
ex.io.reg2_data := id2ex.io.output_reg2_data
ex.io.immediate := id2ex.io.output_immediate
ex.io.aluop1_source := id2ex.io.output_aluop1_source
ex.io.aluop2_source := id2ex.io.output_aluop2_source
ex.io.csr_read_data := id2ex.io.output_csr_read_data
ex.io.forward_from_mem := mem.io.forward_data
ex.io.forward_from_wb := wb.io.regs_write_data
ex.io.reg1_forward := forwarding.io.reg1_forward_ex
ex.io.reg2_forward := forwarding.io.reg2_forward_ex
ex2mem.io.regs_write_enable := id2ex.io.output_regs_write_enable
ex2mem.io.regs_write_source := id2ex.io.output_regs_write_source
ex2mem.io.regs_write_address := id2ex.io.output_regs_write_address
ex2mem.io.instruction_address := id2ex.io.output_instruction_address
ex2mem.io.funct3 := id2ex.io.output_instruction(14, 12)
ex2mem.io.reg2_data := ex.io.mem_reg2_data
ex2mem.io.memory_read_enable := id2ex.io.output_memory_read_enable
ex2mem.io.memory_write_enable := id2ex.io.output_memory_write_enable
ex2mem.io.alu_result := ex.io.mem_alu_result
ex2mem.io.csr_read_data := id2ex.io.output_csr_read_data
mem.io.alu_result := ex2mem.io.output_alu_result
mem.io.reg2_data := ex2mem.io.output_reg2_data
mem.io.memory_read_enable := ex2mem.io.output_memory_read_enable
mem.io.memory_write_enable := ex2mem.io.output_memory_write_enable
mem.io.funct3 := ex2mem.io.output_funct3
mem.io.regs_write_source := ex2mem.io.output_regs_write_source
mem.io.csr_read_data := ex2mem.io.output_csr_read_data
io.device_select := mem.io.bundle.address(Parameters.AddrBits - 1, Parameters.AddrBits - Parameters.SlaveDeviceCountBits)
io.memory_bundle <> mem.io.bundle
io.memory_bundle.address := 0.U(Parameters.SlaveDeviceCountBits.W) ## mem.io.bundle.address(Parameters.AddrBits - 1 - Parameters.SlaveDeviceCountBits, 0)
mem2wb.io.instruction_address := ex2mem.io.output_instruction_address
mem2wb.io.alu_result := ex2mem.io.output_alu_result
mem2wb.io.regs_write_enable := ex2mem.io.output_regs_write_enable
mem2wb.io.regs_write_source := ex2mem.io.output_regs_write_source
mem2wb.io.regs_write_address := ex2mem.io.output_regs_write_address
mem2wb.io.memory_read_data := mem.io.wb_memory_read_data
mem2wb.io.csr_read_data := ex2mem.io.output_csr_read_data
wb.io.instruction_address := mem2wb.io.output_instruction_address
wb.io.alu_result := mem2wb.io.output_alu_result
wb.io.memory_read_data := mem2wb.io.output_memory_read_data
wb.io.regs_write_source := mem2wb.io.output_regs_write_source
wb.io.csr_read_data := mem2wb.io.output_csr_read_data
forwarding.io.rs1_id := id.io.regs_reg1_read_address
forwarding.io.rs2_id := id.io.regs_reg2_read_address
forwarding.io.rs1_ex := id2ex.io.output_regs_reg1_read_address
forwarding.io.rs2_ex := id2ex.io.output_regs_reg2_read_address
forwarding.io.rd_mem := ex2mem.io.output_regs_write_address
forwarding.io.reg_write_enable_mem := ex2mem.io.output_regs_write_enable
forwarding.io.rd_wb := mem2wb.io.output_regs_write_address
forwarding.io.reg_write_enable_wb := mem2wb.io.output_regs_write_enable
clint.io.instruction_address_if := inst_fetch.io.instruction_address
clint.io.instruction_id := if2id.io.output_instruction
clint.io.jump_flag := id.io.clint_jump_flag
clint.io.jump_address := id.io.clint_jump_address
clint.io.interrupt_flag := if2id.io.output_interrupt_flag
clint.io.csr_bundle <> csr_regs.io.clint_access_bundle
csr_regs.io.reg_read_address_id := id.io.ex_csr_address
csr_regs.io.reg_write_enable_ex := id2ex.io.output_csr_write_enable
csr_regs.io.reg_write_address_ex := id2ex.io.output_csr_address
csr_regs.io.reg_write_data_ex := ex.io.csr_write_data
}

View File

@@ -0,0 +1,62 @@
// Copyright 2021 Howard Lau
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package riscv.core.fivestage_final
import chisel3._
import riscv.Parameters
class Control extends Module {
val io = IO(new Bundle {
val jump_flag = Input(Bool())
val jump_instruction_id = Input(Bool())
val rs1_id = Input(UInt(Parameters.PhysicalRegisterAddrWidth))
val rs2_id = Input(UInt(Parameters.PhysicalRegisterAddrWidth))
val memory_read_enable_ex = Input(Bool())
val rd_ex = Input(UInt(Parameters.PhysicalRegisterAddrWidth))
val memory_read_enable_mem = Input(Bool())
val rd_mem = Input(UInt(Parameters.PhysicalRegisterAddrWidth))
val if2id_flush = Output(Bool())
val id2ex_flush = Output(Bool())
val pc_stall = Output(Bool())
val if2id_stall = Output(Bool())
})
// Lab3(Final)
val stall = Wire(Bool())
val load_use_hazard = io.memory_read_enable_ex && io.rd_ex =/= 0.U &&
(io.rd_ex === io.rs1_id || io.rd_ex === io.rs2_id)
val id_jump_needs_ex_alu = io.jump_instruction_id && io.rd_ex =/= 0.U &&
!io.memory_read_enable_ex &&
(io.rd_ex === io.rs1_id || io.rd_ex === io.rs2_id)
val id_jump_needs_mem_load = io.jump_instruction_id && io.memory_read_enable_mem && io.rd_mem =/= 0.U &&
(io.rd_mem === io.rs1_id || io.rd_mem === io.rs2_id)
stall := load_use_hazard || id_jump_needs_ex_alu || id_jump_needs_mem_load
val flush = io.jump_flag && !stall
io.pc_stall := stall
io.if2id_stall := stall
io.if2id_flush := flush
io.id2ex_flush := stall
// Lab3(Final) End
}

View File

@@ -0,0 +1,108 @@
// Copyright 2022 Canbin Huang
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package riscv.core.fivestage_final
import chisel3._
import riscv.Parameters
import riscv.core.PipelineRegister
class EX2MEM extends Module {
val io = IO(new Bundle() {
val regs_write_enable = Input(Bool())
val regs_write_source = Input(UInt(2.W))
val regs_write_address = Input(UInt(Parameters.AddrWidth))
val instruction_address = Input(UInt(Parameters.AddrWidth))
val funct3 = Input(UInt(3.W))
val reg2_data = Input(UInt(Parameters.DataWidth))
val memory_read_enable = Input(Bool())
val memory_write_enable = Input(Bool())
val alu_result = Input(UInt(Parameters.DataWidth))
val csr_read_data = Input(UInt(Parameters.DataWidth))
val output_regs_write_enable = Output(Bool())
val output_regs_write_source = Output(UInt(2.W))
val output_regs_write_address = Output(UInt(Parameters.AddrWidth))
val output_instruction_address = Output(UInt(Parameters.AddrWidth))
val output_funct3 = Output(UInt(Parameters.DataWidth))
val output_reg2_data = Output(UInt(Parameters.DataWidth))
val output_memory_read_enable = Output(Bool())
val output_memory_write_enable = Output(Bool())
val output_alu_result = Output(UInt(Parameters.DataWidth))
val output_csr_read_data = Output(UInt(Parameters.DataWidth))
})
val stall = false.B
val flush = false.B
val regs_write_enable = Module(new PipelineRegister(1))
regs_write_enable.io.in := io.regs_write_enable
regs_write_enable.io.stall := stall
regs_write_enable.io.flush := flush
io.output_regs_write_enable := regs_write_enable.io.out
val regs_write_source = Module(new PipelineRegister(2))
regs_write_source.io.in := io.regs_write_source
regs_write_source.io.stall := stall
regs_write_source.io.flush := flush
io.output_regs_write_source := regs_write_source.io.out
val regs_write_address = Module(new PipelineRegister(Parameters.PhysicalRegisterAddrBits))
regs_write_address.io.in := io.regs_write_address
regs_write_address.io.stall := stall
regs_write_address.io.flush := flush
io.output_regs_write_address := regs_write_address.io.out
val instruction_address = Module(new PipelineRegister(Parameters.AddrBits))
instruction_address.io.in := io.instruction_address
instruction_address.io.stall := stall
instruction_address.io.flush := flush
io.output_instruction_address := instruction_address.io.out
val funct3 = Module(new PipelineRegister(3))
funct3.io.in := io.funct3
funct3.io.stall := stall
funct3.io.flush := flush
io.output_funct3 := funct3.io.out
val reg2_data = Module(new PipelineRegister())
reg2_data.io.in := io.reg2_data
reg2_data.io.stall := stall
reg2_data.io.flush := flush
io.output_reg2_data := reg2_data.io.out
val alu_result = Module(new PipelineRegister())
alu_result.io.in := io.alu_result
alu_result.io.stall := stall
alu_result.io.flush := flush
io.output_alu_result := alu_result.io.out
val memory_read_enable = Module(new PipelineRegister(1))
memory_read_enable.io.in := io.memory_read_enable
memory_read_enable.io.stall := stall
memory_read_enable.io.flush := flush
io.output_memory_read_enable := memory_read_enable.io.out
val memory_write_enable = Module(new PipelineRegister(1))
memory_write_enable.io.in := io.memory_write_enable
memory_write_enable.io.stall := stall
memory_write_enable.io.flush := flush
io.output_memory_write_enable := memory_write_enable.io.out
val csr_read_data = Module(new PipelineRegister())
csr_read_data.io.in := io.csr_read_data
csr_read_data.io.stall := stall
csr_read_data.io.flush := flush
io.output_csr_read_data := csr_read_data.io.out
}

View File

@@ -0,0 +1,93 @@
// Copyright 2021 Howard Lau
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package riscv.core.fivestage_final
import chisel3._
import chisel3.util._
import riscv.Parameters
import riscv.core.{ALU, ALUControl}
class Execute extends Module {
val io = IO(new Bundle {
val instruction = Input(UInt(Parameters.InstructionWidth))
val instruction_address = Input(UInt(Parameters.AddrWidth))
val reg1_data = Input(UInt(Parameters.DataWidth))
val reg2_data = Input(UInt(Parameters.DataWidth))
val immediate = Input(UInt(Parameters.DataWidth))
val aluop1_source = Input(UInt(1.W))
val aluop2_source = Input(UInt(1.W))
val csr_read_data = Input(UInt(Parameters.DataWidth))
val forward_from_mem = Input(UInt(Parameters.DataWidth))
val forward_from_wb = Input(UInt(Parameters.DataWidth))
val reg1_forward = Input(UInt(2.W))
val reg2_forward = Input(UInt(2.W))
val mem_alu_result = Output(UInt(Parameters.DataWidth))
val mem_reg2_data = Output(UInt(Parameters.DataWidth))
val csr_write_data = Output(UInt(Parameters.DataWidth))
})
val opcode = io.instruction(6, 0)
val funct3 = io.instruction(14, 12)
val funct7 = io.instruction(31, 25)
val uimm = io.instruction(19, 15)
val alu = Module(new ALU)
val alu_ctrl = Module(new ALUControl)
alu_ctrl.io.opcode := opcode
alu_ctrl.io.funct3 := funct3
alu_ctrl.io.funct7 := funct7
alu.io.func := alu_ctrl.io.alu_funct
val reg1_data = MuxLookup(
io.reg1_forward,
io.reg1_data,
IndexedSeq(
ForwardingType.ForwardFromMEM -> io.forward_from_mem,
ForwardingType.ForwardFromWB -> io.forward_from_wb
)
)
alu.io.op1 := Mux(
io.aluop1_source === ALUOp1Source.InstructionAddress,
io.instruction_address,
reg1_data
)
val reg2_data = MuxLookup(
io.reg2_forward,
io.reg2_data,
IndexedSeq(
ForwardingType.ForwardFromMEM -> io.forward_from_mem,
ForwardingType.ForwardFromWB -> io.forward_from_wb
)
)
alu.io.op2 := Mux(
io.aluop2_source === ALUOp2Source.Immediate,
io.immediate,
reg2_data
)
io.mem_alu_result := alu.io.result
io.mem_reg2_data := reg2_data
io.csr_write_data := MuxLookup(funct3, 0.U, IndexedSeq(
InstructionsTypeCSR.csrrw -> reg1_data,
InstructionsTypeCSR.csrrc -> io.csr_read_data.&((~reg1_data).asUInt),
InstructionsTypeCSR.csrrs -> io.csr_read_data.|(reg1_data),
InstructionsTypeCSR.csrrwi -> Cat(0.U(27.W), uimm),
InstructionsTypeCSR.csrrci -> io.csr_read_data.&((~Cat(0.U(27.W), uimm)).asUInt),
InstructionsTypeCSR.csrrsi -> io.csr_read_data.|(Cat(0.U(27.W), uimm)),
))
}

View File

@@ -0,0 +1,67 @@
// Copyright 2022 Canbin Huang
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package riscv.core.fivestage_final
import chisel3._
import riscv.Parameters
object ForwardingType {
val NoForward = 0.U(2.W)
val ForwardFromMEM = 1.U(2.W)
val ForwardFromWB = 2.U(2.W)
}
class Forwarding extends Module {
val io = IO(new Bundle() {
val rs1_id = Input(UInt(Parameters.PhysicalRegisterAddrWidth))
val rs2_id = Input(UInt(Parameters.PhysicalRegisterAddrWidth))
val rs1_ex = Input(UInt(Parameters.PhysicalRegisterAddrWidth))
val rs2_ex = Input(UInt(Parameters.PhysicalRegisterAddrWidth))
val rd_mem = Input(UInt(Parameters.PhysicalRegisterAddrWidth))
val reg_write_enable_mem = Input(Bool())
val rd_wb = Input(UInt(Parameters.PhysicalRegisterAddrWidth))
val reg_write_enable_wb = Input(Bool())
val reg1_forward_id = Output(UInt(2.W))
val reg2_forward_id = Output(UInt(2.W))
val reg1_forward_ex = Output(UInt(2.W))
val reg2_forward_ex = Output(UInt(2.W))
})
// Lab3(Final)
val ex_mem_hazard_rs1 = io.reg_write_enable_mem && io.rd_mem =/= 0.U && (io.rd_mem === io.rs1_ex)
val ex_mem_hazard_rs2 = io.reg_write_enable_mem && io.rd_mem =/= 0.U && (io.rd_mem === io.rs2_ex)
val ex_wb_hazard_rs1 = io.reg_write_enable_wb && io.rd_wb =/= 0.U && (io.rd_wb === io.rs1_ex)
val ex_wb_hazard_rs2 = io.reg_write_enable_wb && io.rd_wb =/= 0.U && (io.rd_wb === io.rs2_ex)
io.reg1_forward_ex := Mux(ex_mem_hazard_rs1, ForwardingType.ForwardFromMEM,
Mux(ex_wb_hazard_rs1, ForwardingType.ForwardFromWB, ForwardingType.NoForward))
io.reg2_forward_ex := Mux(ex_mem_hazard_rs2, ForwardingType.ForwardFromMEM,
Mux(ex_wb_hazard_rs2, ForwardingType.ForwardFromWB, ForwardingType.NoForward))
val id_mem_hazard_rs1 = io.reg_write_enable_mem && io.rd_mem =/= 0.U && (io.rd_mem === io.rs1_id)
val id_mem_hazard_rs2 = io.reg_write_enable_mem && io.rd_mem =/= 0.U && (io.rd_mem === io.rs2_id)
val id_wb_hazard_rs1 = io.reg_write_enable_wb && io.rd_wb =/= 0.U && !id_mem_hazard_rs1 && (io.rd_wb === io.rs1_id)
val id_wb_hazard_rs2 = io.reg_write_enable_wb && io.rd_wb =/= 0.U && !id_mem_hazard_rs2 && (io.rd_wb === io.rs2_id)
io.reg1_forward_id := Mux(id_mem_hazard_rs1, ForwardingType.ForwardFromMEM,
Mux(id_wb_hazard_rs1, ForwardingType.ForwardFromWB, ForwardingType.NoForward))
io.reg2_forward_id := Mux(id_mem_hazard_rs2, ForwardingType.ForwardFromMEM,
Mux(id_wb_hazard_rs2, ForwardingType.ForwardFromWB, ForwardingType.NoForward))
// Lab3(Final) End
}

View File

@@ -0,0 +1,163 @@
// Copyright 2021 Howard Lau
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package riscv.core.fivestage_final
import chisel3._
import riscv.Parameters
import riscv.core.PipelineRegister
class ID2EX extends Module {
val io = IO(new Bundle {
val flush = Input(Bool())
val instruction = Input(UInt(Parameters.InstructionWidth))
val instruction_address = Input(UInt(Parameters.AddrWidth))
val regs_reg1_read_address = Input(UInt(Parameters.PhysicalRegisterAddrWidth))
val regs_reg2_read_address = Input(UInt(Parameters.PhysicalRegisterAddrWidth))
val regs_write_enable = Input(Bool())
val regs_write_address = Input(UInt(Parameters.PhysicalRegisterAddrWidth))
val regs_write_source = Input(UInt(2.W))
val reg1_data = Input(UInt(Parameters.DataWidth))
val reg2_data = Input(UInt(Parameters.DataWidth))
val immediate = Input(UInt(Parameters.DataWidth))
val aluop1_source = Input(UInt(1.W))
val aluop2_source = Input(UInt(1.W))
val csr_write_enable = Input(Bool())
val csr_address = Input(UInt(Parameters.CSRRegisterAddrWidth))
val memory_read_enable = Input(Bool())
val memory_write_enable = Input(Bool())
val csr_read_data = Input(UInt(Parameters.DataWidth))
val output_instruction = Output(UInt(Parameters.DataWidth))
val output_instruction_address = Output(UInt(Parameters.AddrWidth))
val output_regs_reg1_read_address = Output(UInt(Parameters.PhysicalRegisterAddrWidth))
val output_regs_reg2_read_address = Output(UInt(Parameters.PhysicalRegisterAddrWidth))
val output_regs_write_enable = Output(Bool())
val output_regs_write_address = Output(UInt(Parameters.PhysicalRegisterAddrWidth))
val output_regs_write_source = Output(UInt(2.W))
val output_reg1_data = Output(UInt(Parameters.DataWidth))
val output_reg2_data = Output(UInt(Parameters.DataWidth))
val output_immediate = Output(UInt(Parameters.DataWidth))
val output_aluop1_source = Output(UInt(1.W))
val output_aluop2_source = Output(UInt(1.W))
val output_csr_write_enable = Output(Bool())
val output_csr_address = Output(UInt(Parameters.CSRRegisterAddrWidth))
val output_memory_read_enable = Output(Bool())
val output_memory_write_enable = Output(Bool())
val output_csr_read_data = Output(UInt(Parameters.DataWidth))
})
val stall = false.B
val instruction = Module(new PipelineRegister(defaultValue = InstructionsNop.nop))
instruction.io.in := io.instruction
instruction.io.stall := stall
instruction.io.flush := io.flush
io.output_instruction := instruction.io.out
val instruction_address = Module(new PipelineRegister(defaultValue = ProgramCounter.EntryAddress))
instruction_address.io.in := io.instruction_address
instruction_address.io.stall := stall
instruction_address.io.flush := io.flush
io.output_instruction_address := instruction_address.io.out
val regs_reg1_read_address = Module(new PipelineRegister(Parameters.PhysicalRegisterAddrBits))
regs_reg1_read_address.io.in := io.regs_reg1_read_address
regs_reg1_read_address.io.stall := stall
regs_reg1_read_address.io.flush := io.flush
io.output_regs_reg1_read_address := regs_reg1_read_address.io.out
val regs_reg2_read_address = Module(new PipelineRegister(Parameters.PhysicalRegisterAddrBits))
regs_reg2_read_address.io.in := io.regs_reg2_read_address
regs_reg2_read_address.io.stall := stall
regs_reg2_read_address.io.flush := io.flush
io.output_regs_reg2_read_address := regs_reg2_read_address.io.out
val regs_write_enable = Module(new PipelineRegister(1))
regs_write_enable.io.in := io.regs_write_enable
regs_write_enable.io.stall := stall
regs_write_enable.io.flush := io.flush
io.output_regs_write_enable := regs_write_enable.io.out
val regs_write_address = Module(new PipelineRegister(Parameters.PhysicalRegisterAddrBits))
regs_write_address.io.in := io.regs_write_address
regs_write_address.io.stall := stall
regs_write_address.io.flush := io.flush
io.output_regs_write_address := regs_write_address.io.out
val regs_write_source = Module(new PipelineRegister(2))
regs_write_source.io.in := io.regs_write_source
regs_write_source.io.stall := stall
regs_write_source.io.flush := io.flush
io.output_regs_write_source := regs_write_source.io.out
val reg1_data = Module(new PipelineRegister())
reg1_data.io.in := io.reg1_data
reg1_data.io.stall := stall
reg1_data.io.flush := io.flush
io.output_reg1_data := reg1_data.io.out
val reg2_data = Module(new PipelineRegister())
reg2_data.io.in := io.reg2_data
reg2_data.io.stall := stall
reg2_data.io.flush := io.flush
io.output_reg2_data := reg2_data.io.out
val immediate = Module(new PipelineRegister())
immediate.io.in := io.immediate
immediate.io.stall := stall
immediate.io.flush := io.flush
io.output_immediate := immediate.io.out
val aluop1_source = Module(new PipelineRegister(1))
aluop1_source.io.in := io.aluop1_source
aluop1_source.io.stall := stall
aluop1_source.io.flush := io.flush
io.output_aluop1_source := aluop1_source.io.out
val aluop2_source = Module(new PipelineRegister(1))
aluop2_source.io.in := io.aluop2_source
aluop2_source.io.stall := stall
aluop2_source.io.flush := io.flush
io.output_aluop2_source := aluop2_source.io.out
val csr_write_enable = Module(new PipelineRegister(1))
csr_write_enable.io.in := io.csr_write_enable
csr_write_enable.io.stall := stall
csr_write_enable.io.flush := io.flush
io.output_csr_write_enable := csr_write_enable.io.out
val csr_address = Module(new PipelineRegister(Parameters.CSRRegisterAddrBits))
csr_address.io.in := io.csr_address
csr_address.io.stall := stall
csr_address.io.flush := io.flush
io.output_csr_address := csr_address.io.out
val memory_read_enable = Module(new PipelineRegister(1))
memory_read_enable.io.in := io.memory_read_enable
memory_read_enable.io.stall := stall
memory_read_enable.io.flush := io.flush
io.output_memory_read_enable := memory_read_enable.io.out
val memory_write_enable = Module(new PipelineRegister(1))
memory_write_enable.io.in := io.memory_write_enable
memory_write_enable.io.stall := stall
memory_write_enable.io.flush := io.flush
io.output_memory_write_enable := memory_write_enable.io.out
val csr_read_data = Module(new PipelineRegister())
csr_read_data.io.in := io.csr_read_data
csr_read_data.io.stall := stall
csr_read_data.io.flush := io.flush
io.output_csr_read_data := csr_read_data.io.out
}

View File

@@ -0,0 +1,51 @@
// Copyright 2021 Howard Lau
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package riscv.core.fivestage_final
import chisel3._
import riscv.Parameters
import riscv.core.PipelineRegister
class IF2ID extends Module {
val io = IO(new Bundle {
val stall = Input(Bool())
val flush = Input(Bool())
val instruction = Input(UInt(Parameters.InstructionWidth))
val instruction_address = Input(UInt(Parameters.AddrWidth))
val interrupt_flag = Input(UInt(Parameters.InterruptFlagWidth))
val output_instruction = Output(UInt(Parameters.DataWidth))
val output_instruction_address = Output(UInt(Parameters.AddrWidth))
val output_interrupt_flag = Output(UInt(Parameters.InterruptFlagWidth))
})
val instruction = Module(new PipelineRegister(defaultValue = InstructionsNop.nop))
instruction.io.in := io.instruction
instruction.io.stall := io.stall
instruction.io.flush := io.flush
io.output_instruction := instruction.io.out
val instruction_address = Module(new PipelineRegister(defaultValue = ProgramCounter.EntryAddress))
instruction_address.io.in := io.instruction_address
instruction_address.io.stall := io.stall
instruction_address.io.flush := io.flush
io.output_instruction_address := instruction_address.io.out
val interrupt_flag = Module(new PipelineRegister(Parameters.InterruptFlagBits))
interrupt_flag.io.in := io.interrupt_flag
interrupt_flag.io.stall := io.stall
interrupt_flag.io.flush := io.flush
io.output_interrupt_flag := interrupt_flag.io.out
}

View File

@@ -0,0 +1,274 @@
// Copyright 2021 Howard Lau
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package riscv.core.fivestage_final
import chisel3._
import chisel3.util._
import riscv.Parameters
object InstructionTypes {
val L = "b0000011".U
val I = "b0010011".U
val S = "b0100011".U
val RM = "b0110011".U
val B = "b1100011".U
}
object Instructions {
val lui = "b0110111".U
val nop = "b0000001".U
val jal = "b1101111".U
val jalr = "b1100111".U
val auipc = "b0010111".U
val csr = "b1110011".U
val fence = "b0001111".U
}
object InstructionsTypeL {
val lb = "b000".U
val lh = "b001".U
val lw = "b010".U
val lbu = "b100".U
val lhu = "b101".U
}
object InstructionsTypeI {
val addi = 0.U
val slli = 1.U
val slti = 2.U
val sltiu = 3.U
val xori = 4.U
val sri = 5.U
val ori = 6.U
val andi = 7.U
}
object InstructionsTypeS {
val sb = "b000".U
val sh = "b001".U
val sw = "b010".U
}
object InstructionsTypeR {
val add_sub = 0.U
val sll = 1.U
val slt = 2.U
val sltu = 3.U
val xor = 4.U
val sr = 5.U
val or = 6.U
val and = 7.U
}
object InstructionsTypeM {
val mul = 0.U
val mulh = 1.U
val mulhsu = 2.U
val mulhum = 3.U
val div = 4.U
val divu = 5.U
val rem = 6.U
val remu = 7.U
}
object InstructionsTypeB {
val beq = "b000".U
val bne = "b001".U
val blt = "b100".U
val bge = "b101".U
val bltu = "b110".U
val bgeu = "b111".U
}
object InstructionsTypeCSR {
val csrrw = "b001".U
val csrrs = "b010".U
val csrrc = "b011".U
val csrrwi = "b101".U
val csrrsi = "b110".U
val csrrci = "b111".U
}
object InstructionsNop {
val nop = 0x00000013L.U(Parameters.DataWidth)
}
object InstructionsRet {
val mret = 0x30200073L.U(Parameters.DataWidth)
val ret = 0x00008067L.U(Parameters.DataWidth)
}
object InstructionsEnv {
val ecall = 0x00000073L.U(Parameters.DataWidth)
val ebreak = 0x00100073L.U(Parameters.DataWidth)
}
object ALUOp1Source {
val Register = 0.U(1.W)
val InstructionAddress = 1.U(1.W)
}
object ALUOp2Source {
val Register = 0.U(1.W)
val Immediate = 1.U(1.W)
}
object RegWriteSource {
val ALUResult = 0.U(2.W)
val Memory = 1.U(2.W)
val CSR = 2.U(2.W)
val NextInstructionAddress = 3.U(2.W)
}
class InstructionDecode extends Module {
val io = IO(new Bundle {
val instruction = Input(UInt(Parameters.InstructionWidth))
val instruction_address = Input(UInt(Parameters.AddrWidth))
val reg1_data = Input(UInt(Parameters.DataWidth))
val reg2_data = Input(UInt(Parameters.DataWidth))
val forward_from_mem = Input(UInt(Parameters.DataWidth))
val forward_from_wb = Input(UInt(Parameters.DataWidth))
val reg1_forward = Input(UInt(2.W))
val reg2_forward = Input(UInt(2.W))
val interrupt_assert = Input(Bool())
val interrupt_handler_address = Input(UInt(Parameters.AddrWidth))
val regs_reg1_read_address = Output(UInt(Parameters.PhysicalRegisterAddrWidth))
val regs_reg2_read_address = Output(UInt(Parameters.PhysicalRegisterAddrWidth))
val ex_immediate = Output(UInt(Parameters.DataWidth))
val ex_aluop1_source = Output(UInt(1.W))
val ex_aluop2_source = Output(UInt(1.W))
val ex_memory_read_enable = Output(Bool())
val ex_memory_write_enable = Output(Bool())
val ex_reg_write_source = Output(UInt(2.W))
val ex_reg_write_enable = Output(Bool())
val ex_reg_write_address = Output(UInt(Parameters.PhysicalRegisterAddrWidth))
val ex_csr_address = Output(UInt(Parameters.CSRRegisterAddrWidth))
val ex_csr_write_enable = Output(Bool())
val ctrl_jump_instruction = Output(Bool())
val clint_jump_flag = Output(Bool())
val clint_jump_address = Output(UInt(Parameters.AddrWidth))
val if_jump_flag = Output(Bool())
val if_jump_address = Output(UInt(Parameters.AddrWidth))
})
val opcode = io.instruction(6, 0)
val funct3 = io.instruction(14, 12)
val funct7 = io.instruction(31, 25)
val rd = io.instruction(11, 7)
val rs1 = io.instruction(19, 15)
val rs2 = io.instruction(24, 20)
// Lab3(Final) ID rs
val rs1_used = opcode =/= Instructions.lui && opcode =/= Instructions.auipc && opcode =/= Instructions.jal
val rs2_used = opcode === InstructionTypes.RM || opcode === InstructionTypes.S || opcode === InstructionTypes.B
io.regs_reg1_read_address := Mux(rs1_used, rs1, 0.U)
io.regs_reg2_read_address := Mux(rs2_used, rs2, 0.U)
// Lab3(Final) ID rs End
io.ex_immediate := MuxLookup(
opcode,
Cat(Fill(20, io.instruction(31)), io.instruction(31, 20)),
IndexedSeq(
InstructionTypes.I -> Cat(Fill(21, io.instruction(31)), io.instruction(30, 20)),
InstructionTypes.L -> Cat(Fill(21, io.instruction(31)), io.instruction(30, 20)),
Instructions.jalr -> Cat(Fill(21, io.instruction(31)), io.instruction(30, 20)),
InstructionTypes.S -> Cat(Fill(21, io.instruction(31)), io.instruction(30, 25), io.instruction(11, 7)),
InstructionTypes.B -> Cat(Fill(20, io.instruction(31)), io.instruction(7), io.instruction(30, 25), io.instruction(11, 8), 0.U(1.W)),
Instructions.lui -> Cat(io.instruction(31, 12), 0.U(12.W)),
Instructions.auipc -> Cat(io.instruction(31, 12), 0.U(12.W)),
Instructions.jal -> Cat(Fill(12, io.instruction(31)), io.instruction(19, 12), io.instruction(20), io.instruction(30, 21), 0.U(1.W))
)
)
io.ex_aluop1_source := Mux(
opcode === Instructions.auipc || opcode === InstructionTypes.B || opcode === Instructions.jal,
ALUOp1Source.InstructionAddress,
ALUOp1Source.Register
)
io.ex_aluop2_source := Mux(
opcode === InstructionTypes.RM,
ALUOp2Source.Register,
ALUOp2Source.Immediate
)
io.ex_memory_read_enable := opcode === InstructionTypes.L
io.ex_memory_write_enable := opcode === InstructionTypes.S
io.ex_reg_write_source := MuxLookup(
opcode,
RegWriteSource.ALUResult,
IndexedSeq(
InstructionTypes.L -> RegWriteSource.Memory,
Instructions.csr -> RegWriteSource.CSR,
Instructions.jal -> RegWriteSource.NextInstructionAddress,
Instructions.jalr -> RegWriteSource.NextInstructionAddress
)
)
// Lab3(Final) ID rd
val rd_used = opcode === InstructionTypes.RM || opcode === InstructionTypes.I || opcode === InstructionTypes.L ||
opcode === Instructions.lui || opcode === Instructions.auipc || opcode === Instructions.jal ||
opcode === Instructions.jalr || opcode === Instructions.csr
io.ex_reg_write_enable := rd_used
io.ex_reg_write_address := Mux(rd_used, rd, 0.U)
// Lab3(Final) ID rd End
io.ex_csr_address := io.instruction(31, 20)
io.ex_csr_write_enable := (opcode === Instructions.csr) && (
funct3 === InstructionsTypeCSR.csrrw || funct3 === InstructionsTypeCSR.csrrwi ||
funct3 === InstructionsTypeCSR.csrrs || funct3 === InstructionsTypeCSR.csrrsi ||
funct3 === InstructionsTypeCSR.csrrc || funct3 === InstructionsTypeCSR.csrrci
)
// Lab3(Final)
val reg1_data = MuxLookup(io.reg1_forward, io.reg1_data)(
Seq(
ForwardingType.ForwardFromMEM -> io.forward_from_mem,
ForwardingType.ForwardFromWB -> io.forward_from_wb
)
)
val reg2_data = MuxLookup(io.reg2_forward, io.reg2_data)(
Seq(
ForwardingType.ForwardFromMEM -> io.forward_from_mem,
ForwardingType.ForwardFromWB -> io.forward_from_wb
)
)
val is_jump_instruction = opcode === Instructions.jal || opcode === Instructions.jalr || opcode === InstructionTypes.B
io.ctrl_jump_instruction := is_jump_instruction
val jump_condition_met =
(opcode === Instructions.jal) ||
(opcode === Instructions.jalr) ||
(opcode === InstructionTypes.B) && MuxLookup(
funct3,
false.B,
IndexedSeq(
InstructionsTypeB.beq -> (reg1_data === reg2_data),
InstructionsTypeB.bne -> (reg1_data =/= reg2_data),
InstructionsTypeB.blt -> (reg1_data.asSInt < reg2_data.asSInt),
InstructionsTypeB.bge -> (reg1_data.asSInt >= reg2_data.asSInt),
InstructionsTypeB.bltu -> (reg1_data < reg2_data),
InstructionsTypeB.bgeu -> (reg1_data >= reg2_data)
)
)
val jump_address = Mux(
opcode === Instructions.jalr,
(reg1_data.asSInt + io.ex_immediate.asSInt).asUInt,
(io.instruction_address.asSInt + io.ex_immediate.asSInt).asUInt
) & (~1.U(Parameters.DataWidth)).asUInt // Ensure address is even
io.clint_jump_flag := jump_condition_met
io.clint_jump_address := jump_address
io.if_jump_flag := jump_condition_met || io.interrupt_assert
io.if_jump_address := Mux(
io.interrupt_assert,
io.interrupt_handler_address,
jump_address
)
// Lab3(Final) End
}

View File

@@ -0,0 +1,48 @@
// Copyright 2021 Howard Lau
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package riscv.core.fivestage_final
import chisel3._
import chisel3.util.MuxCase
import riscv.Parameters
object ProgramCounter {
val EntryAddress = Parameters.EntryAddress
}
class InstructionFetch extends Module {
val io = IO(new Bundle {
val stall_flag_ctrl = Input(Bool())
val jump_flag_id = Input(Bool())
val jump_address_id = Input(UInt(Parameters.AddrWidth))
val rom_instruction = Input(UInt(Parameters.DataWidth))
val instruction_valid = Input(Bool())
val instruction_address = Output(UInt(Parameters.AddrWidth))
val id_instruction = Output(UInt(Parameters.InstructionWidth))
})
val pc = RegInit(ProgramCounter.EntryAddress)
pc := MuxCase(
pc + 4.U,
IndexedSeq(
(io.jump_flag_id && !io.stall_flag_ctrl) -> io.jump_address_id,
(io.stall_flag_ctrl || !io.instruction_valid) -> pc
)
)
io.instruction_address := pc
io.id_instruction := Mux(io.instruction_valid, io.rom_instruction, InstructionsNop.nop)
}

View File

@@ -0,0 +1,83 @@
// Copyright 2022 Canbin Huang
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package riscv.core.fivestage_final
import chisel3._
import riscv.Parameters
import riscv.core.PipelineRegister
class MEM2WB extends Module {
val io = IO(new Bundle() {
val instruction_address = Input(UInt(Parameters.AddrWidth))
val alu_result = Input(UInt(Parameters.DataWidth))
val regs_write_enable = Input(Bool())
val regs_write_source = Input(UInt(2.W))
val regs_write_address = Input(UInt(Parameters.AddrWidth))
val memory_read_data = Input(UInt(Parameters.DataWidth))
val csr_read_data = Input(UInt(Parameters.DataWidth))
val output_instruction_address = Output(UInt(Parameters.AddrWidth))
val output_alu_result = Output(UInt(Parameters.DataWidth))
val output_regs_write_enable = Output(Bool())
val output_regs_write_source = Output(UInt(2.W))
val output_regs_write_address = Output(UInt(Parameters.AddrWidth))
val output_memory_read_data = Output(UInt(Parameters.DataWidth))
val output_csr_read_data = Output(UInt(Parameters.DataWidth))
})
val stall = false.B
val flush = false.B
val alu_result = Module(new PipelineRegister())
alu_result.io.in := io.alu_result
alu_result.io.stall := stall
alu_result.io.flush := flush
io.output_alu_result := alu_result.io.out
val memory_read_data = Module(new PipelineRegister())
memory_read_data.io.in := io.memory_read_data
memory_read_data.io.stall := stall
memory_read_data.io.flush := flush
io.output_memory_read_data := memory_read_data.io.out
val regs_write_enable = Module(new PipelineRegister(1))
regs_write_enable.io.in := io.regs_write_enable
regs_write_enable.io.stall := stall
regs_write_enable.io.flush := flush
io.output_regs_write_enable := regs_write_enable.io.out
val regs_write_source = Module(new PipelineRegister(2))
regs_write_source.io.in := io.regs_write_source
regs_write_source.io.stall := stall
regs_write_source.io.flush := flush
io.output_regs_write_source := regs_write_source.io.out
val regs_write_address = Module(new PipelineRegister(Parameters.PhysicalRegisterAddrBits))
regs_write_address.io.in := io.regs_write_address
regs_write_address.io.stall := stall
regs_write_address.io.flush := flush
io.output_regs_write_address := regs_write_address.io.out
val instruction_address = Module(new PipelineRegister(Parameters.InstructionBits))
instruction_address.io.in := io.instruction_address
instruction_address.io.stall := stall
instruction_address.io.flush := flush
io.output_instruction_address := instruction_address.io.out
val csr_read_data = Module(new PipelineRegister())
csr_read_data.io.in := io.csr_read_data
csr_read_data.io.stall := stall
csr_read_data.io.flush := flush
io.output_csr_read_data := csr_read_data.io.out
}

View File

@@ -0,0 +1,109 @@
// Copyright 2022 Canbin Huang
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package riscv.core.fivestage_final
import chisel3._
import chisel3.util._
import peripheral.RAMBundle
import riscv.Parameters
class MemoryAccess extends Module {
val io = IO(new Bundle() {
val alu_result = Input(UInt(Parameters.DataWidth))
val reg2_data = Input(UInt(Parameters.DataWidth))
val memory_read_enable = Input(Bool())
val memory_write_enable = Input(Bool())
val funct3 = Input(UInt(3.W))
val regs_write_source = Input(UInt(2.W))
val csr_read_data = Input(UInt(Parameters.DataWidth))
val wb_memory_read_data = Output(UInt(Parameters.DataWidth))
val forward_data = Output(UInt(Parameters.DataWidth))
val bundle = Flipped(new RAMBundle)
})
val mem_address_index = io.alu_result(log2Up(Parameters.WordSize) - 1, 0).asUInt
io.bundle.write_enable := io.memory_write_enable
io.bundle.write_data := 0.U
io.bundle.address := io.alu_result
io.bundle.write_strobe := VecInit(Seq.fill(Parameters.WordSize)(false.B))
io.wb_memory_read_data := 0.U
when(io.memory_read_enable) {
val data = io.bundle.read_data
io.wb_memory_read_data := MuxLookup(
io.funct3,
0.U,
IndexedSeq(
InstructionsTypeL.lb -> MuxLookup(
mem_address_index,
Cat(Fill(24, data(31)), data(31, 24)),
IndexedSeq(
0.U -> Cat(Fill(24, data(7)), data(7, 0)),
1.U -> Cat(Fill(24, data(15)), data(15, 8)),
2.U -> Cat(Fill(24, data(23)), data(23, 16))
)
),
InstructionsTypeL.lbu -> MuxLookup(
mem_address_index,
Cat(Fill(24, 0.U), data(31, 24)),
IndexedSeq(
0.U -> Cat(Fill(24, 0.U), data(7, 0)),
1.U -> Cat(Fill(24, 0.U), data(15, 8)),
2.U -> Cat(Fill(24, 0.U), data(23, 16))
)
),
InstructionsTypeL.lh -> Mux(
mem_address_index === 0.U,
Cat(Fill(16, data(15)), data(15, 0)),
Cat(Fill(16, data(31)), data(31, 16))
),
InstructionsTypeL.lhu -> Mux(
mem_address_index === 0.U,
Cat(Fill(16, 0.U), data(15, 0)),
Cat(Fill(16, 0.U), data(31, 16))
),
InstructionsTypeL.lw -> data
)
)
}.elsewhen(io.memory_write_enable) {
io.bundle.write_data := io.reg2_data
io.bundle.write_strobe := VecInit(Seq.fill(Parameters.WordSize)(false.B))
when(io.funct3 === InstructionsTypeS.sb) {
io.bundle.write_strobe(mem_address_index) := true.B
io.bundle.write_data := io.reg2_data(Parameters.ByteBits, 0) << (mem_address_index << log2Up(Parameters.ByteBits).U)
}.elsewhen(io.funct3 === InstructionsTypeS.sh) {
when(mem_address_index === 0.U) {
for (i <- 0 until Parameters.WordSize / 2) {
io.bundle.write_strobe(i) := true.B
}
io.bundle.write_data := io.reg2_data(Parameters.WordSize / 2 * Parameters.ByteBits, 0)
}.otherwise {
for (i <- Parameters.WordSize / 2 until Parameters.WordSize) {
io.bundle.write_strobe(i) := true.B
}
io.bundle.write_data := io.reg2_data(Parameters.WordSize / 2 * Parameters.ByteBits, 0) << (Parameters
.WordSize / 2 * Parameters.ByteBits)
}
}.elsewhen(io.funct3 === InstructionsTypeS.sw) {
for (i <- 0 until Parameters.WordSize) {
io.bundle.write_strobe(i) := true.B
}
}
}
io.forward_data := Mux(io.regs_write_source === RegWriteSource.CSR, io.csr_read_data, io.alu_result)
}

View File

@@ -0,0 +1,40 @@
// Copyright 2022 Canbin Huang
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package riscv.core.fivestage_final
import chisel3._
import chisel3.util._
import riscv.Parameters
class WriteBack extends Module {
val io = IO(new Bundle() {
val instruction_address = Input(UInt(Parameters.AddrWidth))
val alu_result = Input(UInt(Parameters.DataWidth))
val memory_read_data = Input(UInt(Parameters.DataWidth))
val regs_write_source = Input(UInt(2.W))
val csr_read_data = Input(UInt(Parameters.DataWidth))
val regs_write_data = Output(UInt(Parameters.DataWidth))
})
io.regs_write_data := MuxLookup(
io.regs_write_source,
io.alu_result,
IndexedSeq(
RegWriteSource.Memory -> io.memory_read_data,
RegWriteSource.CSR -> io.csr_read_data,
RegWriteSource.NextInstructionAddress -> (io.instruction_address + 4.U)
)
)
}

View File

@@ -0,0 +1,103 @@
// Copyright 2021 Howard Lau
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package riscv.core.fivestage_forward
import chisel3._
import chisel3.util.MuxLookup
import riscv.Parameters
object InterruptStatus {
val None = 0x0.U(8.W)
val Timer0 = 0x1.U(8.W)
val Ret = 0xFF.U(8.W)
}
class CSRDirectAccessBundle extends Bundle {
val mstatus = Input(UInt(Parameters.DataWidth))
val mepc = Input(UInt(Parameters.DataWidth))
val mcause = Input(UInt(Parameters.DataWidth))
val mtvec = Input(UInt(Parameters.DataWidth))
val mstatus_write_data = Output(UInt(Parameters.DataWidth))
val mepc_write_data = Output(UInt(Parameters.DataWidth))
val mcause_write_data = Output(UInt(Parameters.DataWidth))
val direct_write_enable = Output(Bool())
}
// Core Local Interrupt Controller
class CLINT extends Module {
val io = IO(new Bundle {
val interrupt_flag = Input(UInt(Parameters.InterruptFlagWidth))
val instruction_ex = Input(UInt(Parameters.InstructionWidth))
val instruction_address_if = Input(UInt(Parameters.AddrWidth))
val instruction_address_id = Input(UInt(Parameters.AddrWidth))
val jump_flag = Input(Bool())
val jump_address = Input(UInt(Parameters.AddrWidth))
val ex_interrupt_handler_address = Output(UInt(Parameters.AddrWidth))
val ex_interrupt_assert = Output(Bool())
val csr_bundle = new CSRDirectAccessBundle
})
val interrupt_enable = io.csr_bundle.mstatus(3)
val jumpping = RegNext(io.jump_flag || io.ex_interrupt_assert)
val instruction_address = Mux(
io.jump_flag,
io.jump_address,
Mux(jumpping, io.instruction_address_if, io.instruction_address_id)
)
val mstatus_disable_interrupt = io.csr_bundle.mstatus(31, 4) ## 0.U(1.W) ## io.csr_bundle.mstatus(2, 0)
val mstatus_recover_interrupt = io.csr_bundle.mstatus(31, 4) ## io.csr_bundle.mstatus(7) ## io.csr_bundle.mstatus(2, 0)
when(io.instruction_ex === InstructionsEnv.ecall || io.instruction_ex === InstructionsEnv.ebreak) {
io.csr_bundle.mstatus_write_data := mstatus_disable_interrupt
io.csr_bundle.mepc_write_data := instruction_address
io.csr_bundle.mcause_write_data := MuxLookup(
io.instruction_ex,
10.U,
IndexedSeq(
InstructionsEnv.ecall -> 11.U,
InstructionsEnv.ebreak -> 3.U,
)
)
io.csr_bundle.direct_write_enable := true.B
io.ex_interrupt_assert := true.B
io.ex_interrupt_handler_address := io.csr_bundle.mtvec
}.elsewhen(io.interrupt_flag =/= InterruptStatus.None && interrupt_enable) {
io.csr_bundle.mstatus_write_data := mstatus_disable_interrupt
io.csr_bundle.mepc_write_data := instruction_address
io.csr_bundle.mcause_write_data := Mux(io.interrupt_flag(0), 0x80000007L.U, 0x8000000BL.U)
io.csr_bundle.direct_write_enable := true.B
io.ex_interrupt_assert := true.B
io.ex_interrupt_handler_address := io.csr_bundle.mtvec
}.elsewhen(io.instruction_ex === InstructionsRet.mret) {
io.csr_bundle.mstatus_write_data := mstatus_recover_interrupt
io.csr_bundle.mepc_write_data := io.csr_bundle.mepc
io.csr_bundle.mcause_write_data := io.csr_bundle.mcause
io.csr_bundle.direct_write_enable := true.B
io.ex_interrupt_assert := true.B
io.ex_interrupt_handler_address := io.csr_bundle.mepc
}.otherwise {
io.csr_bundle.mstatus_write_data := io.csr_bundle.mstatus
io.csr_bundle.mepc_write_data := io.csr_bundle.mepc
io.csr_bundle.mcause_write_data := io.csr_bundle.mcause
io.csr_bundle.direct_write_enable := false.B
io.ex_interrupt_assert := false.B
io.ex_interrupt_handler_address := 0.U
}
}

View File

@@ -0,0 +1,158 @@
// Copyright 2021 Howard Lau
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package riscv.core.fivestage_forward
import chisel3._
import riscv.Parameters
import riscv.core.{CPUBundle, CSR, RegisterFile}
class CPU extends Module {
val io = IO(new CPUBundle)
val ctrl = Module(new Control)
val regs = Module(new RegisterFile)
val inst_fetch = Module(new InstructionFetch)
val if2id = Module(new IF2ID)
val id = Module(new InstructionDecode)
val id2ex = Module(new ID2EX)
val ex = Module(new Execute)
val ex2mem = Module(new EX2MEM)
val mem = Module(new MemoryAccess)
val mem2wb = Module(new MEM2WB)
val wb = Module(new WriteBack)
val forwarding = Module(new Forwarding)
val clint = Module(new CLINT)
val csr_regs = Module(new CSR)
ctrl.io.jump_flag := ex.io.if_jump_flag
ctrl.io.rs1_id := id.io.regs_reg1_read_address
ctrl.io.rs2_id := id.io.regs_reg2_read_address
ctrl.io.memory_read_enable_ex := id2ex.io.output_memory_read_enable
ctrl.io.rd_ex := id2ex.io.output_regs_write_address
regs.io.write_enable := mem2wb.io.output_regs_write_enable
regs.io.write_address := mem2wb.io.output_regs_write_address
regs.io.write_data := wb.io.regs_write_data
regs.io.read_address1 := id.io.regs_reg1_read_address
regs.io.read_address2 := id.io.regs_reg2_read_address
regs.io.debug_read_address := io.debug_read_address
io.debug_read_data := regs.io.debug_read_data
io.instruction_address := inst_fetch.io.instruction_address
inst_fetch.io.stall_flag_ctrl := ctrl.io.pc_stall
inst_fetch.io.jump_flag_id := ex.io.if_jump_flag
inst_fetch.io.jump_address_id := ex.io.if_jump_address
inst_fetch.io.rom_instruction := io.instruction
inst_fetch.io.instruction_valid := io.instruction_valid
if2id.io.stall := ctrl.io.if2id_stall
if2id.io.flush := ctrl.io.if2id_flush
if2id.io.instruction := inst_fetch.io.id_instruction
if2id.io.instruction_address := inst_fetch.io.instruction_address
if2id.io.interrupt_flag := io.interrupt_flag
id.io.instruction := if2id.io.output_instruction
id2ex.io.flush := ctrl.io.id2ex_flush
id2ex.io.instruction := if2id.io.output_instruction
id2ex.io.instruction_address := if2id.io.output_instruction_address
id2ex.io.reg1_data := regs.io.read_data1
id2ex.io.reg2_data := regs.io.read_data2
id2ex.io.regs_reg1_read_address := id.io.regs_reg1_read_address
id2ex.io.regs_reg2_read_address := id.io.regs_reg2_read_address
id2ex.io.regs_write_enable := id.io.ex_reg_write_enable
id2ex.io.regs_write_address := id.io.ex_reg_write_address
id2ex.io.regs_write_source := id.io.ex_reg_write_source
id2ex.io.immediate := id.io.ex_immediate
id2ex.io.aluop1_source := id.io.ex_aluop1_source
id2ex.io.aluop2_source := id.io.ex_aluop2_source
id2ex.io.csr_write_enable := id.io.ex_csr_write_enable
id2ex.io.csr_address := id.io.ex_csr_address
id2ex.io.memory_read_enable := id.io.ex_memory_read_enable
id2ex.io.memory_write_enable := id.io.ex_memory_write_enable
id2ex.io.csr_read_data := csr_regs.io.id_reg_read_data
ex.io.instruction := id2ex.io.output_instruction
ex.io.instruction_address := id2ex.io.output_instruction_address
ex.io.reg1_data := id2ex.io.output_reg1_data
ex.io.reg2_data := id2ex.io.output_reg2_data
ex.io.immediate_id := id2ex.io.output_immediate
ex.io.aluop1_source_id := id2ex.io.output_aluop1_source
ex.io.aluop2_source_id := id2ex.io.output_aluop2_source
ex.io.csr_read_data_id := id2ex.io.output_csr_read_data
ex.io.forward_from_mem := mem.io.forward_data
ex.io.forward_from_wb := wb.io.regs_write_data
ex.io.reg1_forward := forwarding.io.reg1_forward_ex
ex.io.reg2_forward := forwarding.io.reg2_forward_ex
ex.io.interrupt_assert_clint := clint.io.ex_interrupt_assert
ex.io.interrupt_handler_address_clint := clint.io.ex_interrupt_handler_address
ex2mem.io.regs_write_enable := id2ex.io.output_regs_write_enable
ex2mem.io.regs_write_source := id2ex.io.output_regs_write_source
ex2mem.io.regs_write_address := id2ex.io.output_regs_write_address
ex2mem.io.instruction_address := id2ex.io.output_instruction_address
ex2mem.io.funct3 := id2ex.io.output_instruction(14, 12)
ex2mem.io.reg2_data := ex.io.mem_reg2_data
ex2mem.io.memory_read_enable := id2ex.io.output_memory_read_enable
ex2mem.io.memory_write_enable := id2ex.io.output_memory_write_enable
ex2mem.io.alu_result := ex.io.mem_alu_result
ex2mem.io.csr_read_data := id2ex.io.output_csr_read_data
mem.io.alu_result := ex2mem.io.output_alu_result
mem.io.reg2_data := ex2mem.io.output_reg2_data
mem.io.memory_read_enable := ex2mem.io.output_memory_read_enable
mem.io.memory_write_enable := ex2mem.io.output_memory_write_enable
mem.io.funct3 := ex2mem.io.output_funct3
mem.io.regs_write_source := ex2mem.io.output_regs_write_source
mem.io.csr_read_data := ex2mem.io.output_csr_read_data
io.device_select := mem.io.bundle.address(Parameters.AddrBits - 1, Parameters.AddrBits - Parameters.SlaveDeviceCountBits)
io.memory_bundle <> mem.io.bundle
io.memory_bundle.address := 0.U(Parameters.SlaveDeviceCountBits.W) ## mem.io.bundle.address(Parameters.AddrBits - 1 - Parameters.SlaveDeviceCountBits, 0)
mem2wb.io.instruction_address := ex2mem.io.output_instruction_address
mem2wb.io.alu_result := ex2mem.io.output_alu_result
mem2wb.io.regs_write_enable := ex2mem.io.output_regs_write_enable
mem2wb.io.regs_write_source := ex2mem.io.output_regs_write_source
mem2wb.io.regs_write_address := ex2mem.io.output_regs_write_address
mem2wb.io.memory_read_data := mem.io.wb_memory_read_data
mem2wb.io.csr_read_data := ex2mem.io.output_csr_read_data
wb.io.instruction_address := mem2wb.io.output_instruction_address
wb.io.alu_result := mem2wb.io.output_alu_result
wb.io.memory_read_data := mem2wb.io.output_memory_read_data
wb.io.regs_write_source := mem2wb.io.output_regs_write_source
wb.io.csr_read_data := mem2wb.io.output_csr_read_data
forwarding.io.rs1_ex := id2ex.io.output_regs_reg1_read_address
forwarding.io.rs2_ex := id2ex.io.output_regs_reg2_read_address
forwarding.io.rd_mem := ex2mem.io.output_regs_write_address
forwarding.io.reg_write_enable_mem := ex2mem.io.output_regs_write_enable
forwarding.io.rd_wb := mem2wb.io.output_regs_write_address
forwarding.io.reg_write_enable_wb := mem2wb.io.output_regs_write_enable
clint.io.instruction_address_if := inst_fetch.io.instruction_address
clint.io.instruction_address_id := if2id.io.output_instruction_address
clint.io.instruction_ex := id2ex.io.output_instruction
clint.io.jump_flag := ex.io.clint_jump_flag
clint.io.jump_address := ex.io.clint_jump_address
clint.io.interrupt_flag := if2id.io.output_interrupt_flag
clint.io.csr_bundle <> csr_regs.io.clint_access_bundle
csr_regs.io.reg_read_address_id := id.io.ex_csr_address
csr_regs.io.reg_write_enable_ex := id2ex.io.output_csr_write_enable
csr_regs.io.reg_write_address_ex := id2ex.io.output_csr_address
csr_regs.io.reg_write_data_ex := ex.io.csr_write_data
}

View File

@@ -0,0 +1,44 @@
// Copyright 2021 Howard Lau
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package riscv.core.fivestage_forward
import chisel3._
import riscv.Parameters
class Control extends Module {
val io = IO(new Bundle {
val jump_flag = Input(Bool())
val rs1_id = Input(UInt(Parameters.PhysicalRegisterAddrWidth))
val rs2_id = Input(UInt(Parameters.PhysicalRegisterAddrWidth))
val memory_read_enable_ex = Input(Bool())
val rd_ex = Input(UInt(Parameters.PhysicalRegisterAddrWidth))
val if2id_flush = Output(Bool())
val id2ex_flush = Output(Bool())
val pc_stall = Output(Bool())
val if2id_stall = Output(Bool())
})
// Lab3(Forward)
val load_use_hazard = io.memory_read_enable_ex &&
(io.rd_ex === io.rs1_id || io.rd_ex === io.rs2_id) &&
io.rd_ex =/= 0.U
val flush = io.jump_flag
io.pc_stall := load_use_hazard && !flush
io.if2id_stall := load_use_hazard && !flush
io.if2id_flush := flush
io.id2ex_flush := flush || load_use_hazard
// Lab3(Forward) End
}

View File

@@ -0,0 +1,108 @@
// Copyright 2022 Canbin Huang
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package riscv.core.fivestage_forward
import chisel3._
import riscv.Parameters
import riscv.core.PipelineRegister
class EX2MEM extends Module {
val io = IO(new Bundle() {
val regs_write_enable = Input(Bool())
val regs_write_source = Input(UInt(2.W))
val regs_write_address = Input(UInt(Parameters.AddrWidth))
val instruction_address = Input(UInt(Parameters.AddrWidth))
val funct3 = Input(UInt(3.W))
val reg2_data = Input(UInt(Parameters.DataWidth))
val memory_read_enable = Input(Bool())
val memory_write_enable = Input(Bool())
val alu_result = Input(UInt(Parameters.DataWidth))
val csr_read_data = Input(UInt(Parameters.DataWidth))
val output_regs_write_enable = Output(Bool())
val output_regs_write_source = Output(UInt(2.W))
val output_regs_write_address = Output(UInt(Parameters.AddrWidth))
val output_instruction_address = Output(UInt(Parameters.AddrWidth))
val output_funct3 = Output(UInt(Parameters.DataWidth))
val output_reg2_data = Output(UInt(Parameters.DataWidth))
val output_memory_read_enable = Output(Bool())
val output_memory_write_enable = Output(Bool())
val output_alu_result = Output(UInt(Parameters.DataWidth))
val output_csr_read_data = Output(UInt(Parameters.DataWidth))
})
val stall = false.B
val flush = false.B
val regs_write_enable = Module(new PipelineRegister(1))
regs_write_enable.io.in := io.regs_write_enable
regs_write_enable.io.stall := stall
regs_write_enable.io.flush := flush
io.output_regs_write_enable := regs_write_enable.io.out
val regs_write_source = Module(new PipelineRegister(2))
regs_write_source.io.in := io.regs_write_source
regs_write_source.io.stall := stall
regs_write_source.io.flush := flush
io.output_regs_write_source := regs_write_source.io.out
val regs_write_address = Module(new PipelineRegister(Parameters.PhysicalRegisterAddrBits))
regs_write_address.io.in := io.regs_write_address
regs_write_address.io.stall := stall
regs_write_address.io.flush := flush
io.output_regs_write_address := regs_write_address.io.out
val instruction_address = Module(new PipelineRegister(Parameters.AddrBits))
instruction_address.io.in := io.instruction_address
instruction_address.io.stall := stall
instruction_address.io.flush := flush
io.output_instruction_address := instruction_address.io.out
val funct3 = Module(new PipelineRegister(3))
funct3.io.in := io.funct3
funct3.io.stall := stall
funct3.io.flush := flush
io.output_funct3 := funct3.io.out
val reg2_data = Module(new PipelineRegister())
reg2_data.io.in := io.reg2_data
reg2_data.io.stall := stall
reg2_data.io.flush := flush
io.output_reg2_data := reg2_data.io.out
val alu_result = Module(new PipelineRegister())
alu_result.io.in := io.alu_result
alu_result.io.stall := stall
alu_result.io.flush := flush
io.output_alu_result := alu_result.io.out
val memory_read_enable = Module(new PipelineRegister(1))
memory_read_enable.io.in := io.memory_read_enable
memory_read_enable.io.stall := stall
memory_read_enable.io.flush := flush
io.output_memory_read_enable := memory_read_enable.io.out
val memory_write_enable = Module(new PipelineRegister(1))
memory_write_enable.io.in := io.memory_write_enable
memory_write_enable.io.stall := stall
memory_write_enable.io.flush := flush
io.output_memory_write_enable := memory_write_enable.io.out
val csr_read_data = Module(new PipelineRegister())
csr_read_data.io.in := io.csr_read_data
csr_read_data.io.stall := stall
csr_read_data.io.flush := flush
io.output_csr_read_data := csr_read_data.io.out
}

View File

@@ -0,0 +1,121 @@
// Copyright 2021 Howard Lau
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package riscv.core.fivestage_forward
import chisel3._
import chisel3.util._
import riscv.Parameters
import riscv.core.{ALU, ALUControl}
class Execute extends Module {
val io = IO(new Bundle {
val instruction = Input(UInt(Parameters.InstructionWidth))
val instruction_address = Input(UInt(Parameters.AddrWidth))
val reg1_data = Input(UInt(Parameters.DataWidth))
val reg2_data = Input(UInt(Parameters.DataWidth))
val immediate_id = Input(UInt(Parameters.DataWidth))
val aluop1_source_id = Input(UInt(1.W))
val aluop2_source_id = Input(UInt(1.W))
val csr_read_data_id = Input(UInt(Parameters.DataWidth))
val forward_from_mem = Input(UInt(Parameters.DataWidth))
val forward_from_wb = Input(UInt(Parameters.DataWidth))
val reg1_forward = Input(UInt(2.W))
val reg2_forward = Input(UInt(2.W))
val interrupt_assert_clint = Input(Bool())
val interrupt_handler_address_clint = Input(UInt(Parameters.AddrWidth))
val mem_alu_result = Output(UInt(Parameters.DataWidth))
val mem_reg2_data = Output(UInt(Parameters.DataWidth))
val csr_write_data = Output(UInt(Parameters.DataWidth))
val if_jump_flag = Output(Bool())
val if_jump_address = Output(UInt(Parameters.AddrWidth))
val clint_jump_flag = Output(Bool())
val clint_jump_address = Output(UInt(Parameters.AddrWidth))
})
val opcode = io.instruction(6, 0)
val funct3 = io.instruction(14, 12)
val funct7 = io.instruction(31, 25)
val uimm = io.instruction(19, 15)
// ALU compute
val alu = Module(new ALU)
val alu_ctrl = Module(new ALUControl)
alu_ctrl.io.opcode := opcode
alu_ctrl.io.funct3 := funct3
alu_ctrl.io.funct7 := funct7
alu.io.func := alu_ctrl.io.alu_funct
// Lab3(Forward)
val reg1_data = MuxLookup(io.reg1_forward, io.reg1_data)(
Seq(
ForwardingType.ForwardFromMEM -> io.forward_from_mem,
ForwardingType.ForwardFromWB -> io.forward_from_wb
)
)
val reg2_data = MuxLookup(io.reg2_forward, io.reg2_data)(
Seq(
ForwardingType.ForwardFromMEM -> io.forward_from_mem,
ForwardingType.ForwardFromWB -> io.forward_from_wb
)
)
// Lab3(Forward) End
alu.io.op1 := Mux(
io.aluop1_source_id === ALUOp1Source.InstructionAddress,
io.instruction_address,
reg1_data
)
alu.io.op2 := Mux(
io.aluop2_source_id === ALUOp2Source.Immediate,
io.immediate_id,
reg2_data
)
io.mem_alu_result := alu.io.result
io.mem_reg2_data := reg2_data
io.csr_write_data := MuxLookup(funct3, 0.U, IndexedSeq(
InstructionsTypeCSR.csrrw -> reg1_data,
InstructionsTypeCSR.csrrc -> io.csr_read_data_id.&((~reg1_data).asUInt),
InstructionsTypeCSR.csrrs -> io.csr_read_data_id.|(reg1_data),
InstructionsTypeCSR.csrrwi -> Cat(0.U(27.W), uimm),
InstructionsTypeCSR.csrrci -> io.csr_read_data_id.&((~Cat(0.U(27.W), uimm)).asUInt),
InstructionsTypeCSR.csrrsi -> io.csr_read_data_id.|(Cat(0.U(27.W), uimm)),
))
// jump and interrupt
val instruction_jump_flag = (opcode === Instructions.jal) ||
(opcode === Instructions.jalr) ||
(opcode === InstructionTypes.B) && MuxLookup(
funct3,
false.B,
IndexedSeq(
InstructionsTypeB.beq -> (reg1_data === reg2_data),
InstructionsTypeB.bne -> (reg1_data =/= reg2_data),
InstructionsTypeB.blt -> (reg1_data.asSInt < reg2_data.asSInt),
InstructionsTypeB.bge -> (reg1_data.asSInt >= reg2_data.asSInt),
InstructionsTypeB.bltu -> (reg1_data.asUInt < reg2_data.asUInt),
InstructionsTypeB.bgeu -> (reg1_data.asUInt >= reg2_data.asUInt)
)
)
io.clint_jump_flag := instruction_jump_flag
io.clint_jump_address := alu.io.result
io.if_jump_flag := io.interrupt_assert_clint || instruction_jump_flag
io.if_jump_address := Mux(io.interrupt_assert_clint,
io.interrupt_handler_address_clint,
alu.io.result
)
}

View File

@@ -0,0 +1,56 @@
// Copyright 2022 Canbin Huang
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package riscv.core.fivestage_forward
import chisel3._
import riscv.Parameters
object ForwardingType {
val NoForward = 0.U(2.W)
val ForwardFromMEM = 1.U(2.W)
val ForwardFromWB = 2.U(2.W)
}
class Forwarding extends Module {
val io = IO(new Bundle() {
val rs1_ex = Input(UInt(Parameters.PhysicalRegisterAddrWidth))
val rs2_ex = Input(UInt(Parameters.PhysicalRegisterAddrWidth))
val rd_mem = Input(UInt(Parameters.PhysicalRegisterAddrWidth))
val reg_write_enable_mem = Input(Bool())
val rd_wb = Input(UInt(Parameters.PhysicalRegisterAddrWidth))
val reg_write_enable_wb = Input(Bool())
// Forwarding Type
val reg1_forward_ex = Output(UInt(2.W))
val reg2_forward_ex = Output(UInt(2.W))
})
// Lab3(Forward)
// io.reg1_forward_ex := 0.U
// io.reg2_forward_ex := 0.U
io.reg1_forward_ex := ForwardingType.NoForward
io.reg2_forward_ex := ForwardingType.NoForward
when(io.reg_write_enable_mem && io.rd_mem =/= 0.U && io.rd_mem === io.rs1_ex) {
io.reg1_forward_ex := ForwardingType.ForwardFromMEM
}.elsewhen(io.reg_write_enable_wb && io.rd_wb =/= 0.U && io.rd_wb === io.rs1_ex) {
io.reg1_forward_ex := ForwardingType.ForwardFromWB
}
when(io.reg_write_enable_mem && io.rd_mem =/= 0.U && io.rd_mem === io.rs2_ex) {
io.reg2_forward_ex := ForwardingType.ForwardFromMEM
}.elsewhen(io.reg_write_enable_wb && io.rd_wb =/= 0.U && io.rd_wb === io.rs2_ex) {
io.reg2_forward_ex := ForwardingType.ForwardFromWB
}
// Lab3(Forward) End
}

View File

@@ -0,0 +1,164 @@
// Copyright 2021 Howard Lau
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package riscv.core.fivestage_forward
import chisel3._
import riscv.Parameters
import riscv.core.PipelineRegister
class ID2EX extends Module {
val io = IO(new Bundle {
val flush = Input(Bool())
val instruction = Input(UInt(Parameters.InstructionWidth))
val instruction_address = Input(UInt(Parameters.AddrWidth))
val regs_reg1_read_address = Input(UInt(Parameters.PhysicalRegisterAddrWidth))
val regs_reg2_read_address = Input(UInt(Parameters.PhysicalRegisterAddrWidth))
val regs_write_enable = Input(Bool())
val regs_write_address = Input(UInt(Parameters.PhysicalRegisterAddrWidth))
val regs_write_source = Input(UInt(2.W))
val reg1_data = Input(UInt(Parameters.DataWidth))
val reg2_data = Input(UInt(Parameters.DataWidth))
val immediate = Input(UInt(Parameters.DataWidth))
val aluop1_source = Input(UInt(1.W))
val aluop2_source = Input(UInt(1.W))
val csr_write_enable = Input(Bool())
val csr_address = Input(UInt(Parameters.CSRRegisterAddrWidth))
val memory_read_enable = Input(Bool())
val memory_write_enable = Input(Bool())
val csr_read_data = Input(UInt(Parameters.DataWidth))
val output_instruction = Output(UInt(Parameters.DataWidth))
val output_instruction_address = Output(UInt(Parameters.AddrWidth))
val output_regs_reg1_read_address = Output(UInt(Parameters.PhysicalRegisterAddrWidth))
val output_regs_reg2_read_address = Output(UInt(Parameters.PhysicalRegisterAddrWidth))
val output_regs_write_enable = Output(Bool())
val output_regs_write_address = Output(UInt(Parameters.PhysicalRegisterAddrWidth))
val output_regs_write_source = Output(UInt(2.W))
val output_reg1_data = Output(UInt(Parameters.DataWidth))
val output_reg2_data = Output(UInt(Parameters.DataWidth))
val output_immediate = Output(UInt(Parameters.DataWidth))
val output_aluop1_source = Output(UInt(1.W))
val output_aluop2_source = Output(UInt(1.W))
val output_csr_write_enable = Output(Bool())
val output_csr_address = Output(UInt(Parameters.CSRRegisterAddrWidth))
val output_memory_read_enable = Output(Bool())
val output_memory_write_enable = Output(Bool())
val output_csr_read_data = Output(UInt(Parameters.DataWidth))
})
val stall = false.B
val instruction = Module(new PipelineRegister(defaultValue = InstructionsNop.nop))
instruction.io.in := io.instruction
instruction.io.stall := stall
instruction.io.flush := io.flush
io.output_instruction := instruction.io.out
val instruction_address = Module(new PipelineRegister(defaultValue = ProgramCounter.EntryAddress))
instruction_address.io.in := io.instruction_address
instruction_address.io.stall := stall
instruction_address.io.flush := io.flush
io.output_instruction_address := instruction_address.io.out
val regs_reg1_read_address = Module(new PipelineRegister(Parameters.PhysicalRegisterAddrBits))
regs_reg1_read_address.io.in := io.regs_reg1_read_address
regs_reg1_read_address.io.stall := stall
regs_reg1_read_address.io.flush := io.flush
io.output_regs_reg1_read_address := regs_reg1_read_address.io.out
val regs_reg2_read_address = Module(new PipelineRegister(Parameters.PhysicalRegisterAddrBits))
regs_reg2_read_address.io.in := io.regs_reg2_read_address
regs_reg2_read_address.io.stall := stall
regs_reg2_read_address.io.flush := io.flush
io.output_regs_reg2_read_address := regs_reg2_read_address.io.out
val regs_write_enable = Module(new PipelineRegister(1))
regs_write_enable.io.in := io.regs_write_enable
regs_write_enable.io.stall := stall
regs_write_enable.io.flush := io.flush
io.output_regs_write_enable := regs_write_enable.io.out
val regs_write_address = Module(new PipelineRegister(Parameters.PhysicalRegisterAddrBits))
regs_write_address.io.in := io.regs_write_address
regs_write_address.io.stall := stall
regs_write_address.io.flush := io.flush
io.output_regs_write_address := regs_write_address.io.out
val regs_write_source = Module(new PipelineRegister(2))
regs_write_source.io.in := io.regs_write_source
regs_write_source.io.stall := stall
regs_write_source.io.flush := io.flush
io.output_regs_write_source := regs_write_source.io.out
val reg1_data = Module(new PipelineRegister())
reg1_data.io.in := io.reg1_data
reg1_data.io.stall := stall
reg1_data.io.flush := io.flush
io.output_reg1_data := reg1_data.io.out
val reg2_data = Module(new PipelineRegister())
reg2_data.io.in := io.reg2_data
reg2_data.io.stall := stall
reg2_data.io.flush := io.flush
io.output_reg2_data := reg2_data.io.out
val immediate = Module(new PipelineRegister())
immediate.io.in := io.immediate
immediate.io.stall := stall
immediate.io.flush := io.flush
io.output_immediate := immediate.io.out
val aluop1_source = Module(new PipelineRegister(1))
aluop1_source.io.in := io.aluop1_source
aluop1_source.io.stall := stall
aluop1_source.io.flush := io.flush
io.output_aluop1_source := aluop1_source.io.out
val aluop2_source = Module(new PipelineRegister(1))
aluop2_source.io.in := io.aluop2_source
aluop2_source.io.stall := stall
aluop2_source.io.flush := io.flush
io.output_aluop2_source := aluop2_source.io.out
val csr_write_enable = Module(new PipelineRegister(1))
csr_write_enable.io.in := io.csr_write_enable
csr_write_enable.io.stall := stall
csr_write_enable.io.flush := io.flush
io.output_csr_write_enable := csr_write_enable.io.out
val csr_address = Module(new PipelineRegister(Parameters.CSRRegisterAddrBits))
csr_address.io.in := io.csr_address
csr_address.io.stall := stall
csr_address.io.flush := io.flush
io.output_csr_address := csr_address.io.out
val memory_read_enable = Module(new PipelineRegister(1))
memory_read_enable.io.in := io.memory_read_enable
memory_read_enable.io.stall := stall
memory_read_enable.io.flush := io.flush
io.output_memory_read_enable := memory_read_enable.io.out
val memory_write_enable = Module(new PipelineRegister(1))
memory_write_enable.io.in := io.memory_write_enable
memory_write_enable.io.stall := stall
memory_write_enable.io.flush := io.flush
io.output_memory_write_enable := memory_write_enable.io.out
val csr_read_data = Module(new PipelineRegister())
csr_read_data.io.in := io.csr_read_data
csr_read_data.io.stall := stall
csr_read_data.io.flush := io.flush
io.output_csr_read_data := csr_read_data.io.out
}

View File

@@ -0,0 +1,51 @@
// Copyright 2021 Howard Lau
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package riscv.core.fivestage_forward
import chisel3._
import riscv.Parameters
import riscv.core.PipelineRegister
class IF2ID extends Module {
val io = IO(new Bundle {
val stall = Input(Bool())
val flush = Input(Bool())
val instruction = Input(UInt(Parameters.InstructionWidth))
val instruction_address = Input(UInt(Parameters.AddrWidth))
val interrupt_flag = Input(UInt(Parameters.InterruptFlagWidth))
val output_instruction = Output(UInt(Parameters.DataWidth))
val output_instruction_address = Output(UInt(Parameters.AddrWidth))
val output_interrupt_flag = Output(UInt(Parameters.InterruptFlagWidth))
})
val instruction = Module(new PipelineRegister(defaultValue = InstructionsNop.nop))
instruction.io.in := io.instruction
instruction.io.stall := io.stall
instruction.io.flush := io.flush
io.output_instruction := instruction.io.out
val instruction_address = Module(new PipelineRegister(defaultValue = ProgramCounter.EntryAddress))
instruction_address.io.in := io.instruction_address
instruction_address.io.stall := io.stall
instruction_address.io.flush := io.flush
io.output_instruction_address := instruction_address.io.out
val interrupt_flag = Module(new PipelineRegister(Parameters.InterruptFlagBits))
interrupt_flag.io.in := io.interrupt_flag
interrupt_flag.io.stall := io.stall
interrupt_flag.io.flush := io.flush
io.output_interrupt_flag := interrupt_flag.io.out
}

View File

@@ -0,0 +1,218 @@
// Copyright 2021 Howard Lau
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package riscv.core.fivestage_forward
import chisel3._
import chisel3.util._
import riscv.Parameters
object InstructionTypes {
val L = "b0000011".U
val I = "b0010011".U
val S = "b0100011".U
val RM = "b0110011".U
val B = "b1100011".U
}
object Instructions {
val lui = "b0110111".U
val nop = "b0000001".U
val jal = "b1101111".U
val jalr = "b1100111".U
val auipc = "b0010111".U
val csr = "b1110011".U
val fence = "b0001111".U
}
object InstructionsTypeL {
val lb = "b000".U
val lh = "b001".U
val lw = "b010".U
val lbu = "b100".U
val lhu = "b101".U
}
object InstructionsTypeI {
val addi = 0.U
val slli = 1.U
val slti = 2.U
val sltiu = 3.U
val xori = 4.U
val sri = 5.U
val ori = 6.U
val andi = 7.U
}
object InstructionsTypeS {
val sb = "b000".U
val sh = "b001".U
val sw = "b010".U
}
object InstructionsTypeR {
val add_sub = 0.U
val sll = 1.U
val slt = 2.U
val sltu = 3.U
val xor = 4.U
val sr = 5.U
val or = 6.U
val and = 7.U
}
object InstructionsTypeM {
val mul = 0.U
val mulh = 1.U
val mulhsu = 2.U
val mulhum = 3.U
val div = 4.U
val divu = 5.U
val rem = 6.U
val remu = 7.U
}
object InstructionsTypeB {
val beq = "b000".U
val bne = "b001".U
val blt = "b100".U
val bge = "b101".U
val bltu = "b110".U
val bgeu = "b111".U
}
object InstructionsTypeCSR {
val csrrw = "b001".U
val csrrs = "b010".U
val csrrc = "b011".U
val csrrwi = "b101".U
val csrrsi = "b110".U
val csrrci = "b111".U
}
object InstructionsNop {
val nop = 0x00000013L.U(Parameters.DataWidth)
}
object InstructionsRet {
val mret = 0x30200073L.U(Parameters.DataWidth)
val ret = 0x00008067L.U(Parameters.DataWidth)
}
object InstructionsEnv {
val ecall = 0x00000073L.U(Parameters.DataWidth)
val ebreak = 0x00100073L.U(Parameters.DataWidth)
}
object ALUOp1Source {
val Register = 0.U(1.W)
val InstructionAddress = 1.U(1.W)
}
object ALUOp2Source {
val Register = 0.U(1.W)
val Immediate = 1.U(1.W)
}
object RegWriteSource {
val ALUResult = 0.U(2.W)
val Memory = 1.U(2.W)
val CSR = 2.U(2.W)
val NextInstructionAddress = 3.U(2.W)
}
class InstructionDecode extends Module {
val io = IO(new Bundle {
val instruction = Input(UInt(Parameters.InstructionWidth))
val regs_reg1_read_address = Output(UInt(Parameters.PhysicalRegisterAddrWidth))
val regs_reg2_read_address = Output(UInt(Parameters.PhysicalRegisterAddrWidth))
val ex_immediate = Output(UInt(Parameters.DataWidth))
val ex_aluop1_source = Output(Bool())
val ex_aluop2_source = Output(Bool())
val ex_memory_read_enable = Output(Bool())
val ex_memory_write_enable = Output(Bool())
val ex_reg_write_source = Output(UInt(2.W))
val ex_reg_write_enable = Output(Bool())
val ex_reg_write_address = Output(UInt(Parameters.PhysicalRegisterAddrWidth))
val ex_csr_address = Output(UInt(Parameters.CSRRegisterAddrWidth))
val ex_csr_write_enable = Output(Bool())
})
val opcode = io.instruction(6, 0)
val funct3 = io.instruction(14, 12)
val funct7 = io.instruction(31, 25)
val rd = io.instruction(11, 7)
val rs1 = io.instruction(19, 15)
val rs2 = io.instruction(24, 20)
// Lab3(Forward) ID rs
// io.regs_reg1_read_address := rs1
// io.regs_reg2_read_address := rs2
val rs1_used = opcode =/= Instructions.lui && opcode =/= Instructions.auipc && opcode =/= Instructions.jal
val rs2_used = opcode === InstructionTypes.RM || opcode === InstructionTypes.S || opcode === InstructionTypes.B
io.regs_reg1_read_address := Mux(rs1_used, rs1, 0.U)
io.regs_reg2_read_address := Mux(rs2_used, rs2, 0.U)
// Lab3(Forward) ID rs End
io.ex_immediate := MuxLookup(
opcode,
Cat(Fill(20, io.instruction(31)), io.instruction(31, 20)),
IndexedSeq(
InstructionTypes.I -> Cat(Fill(21, io.instruction(31)), io.instruction(30, 20)),
InstructionTypes.L -> Cat(Fill(21, io.instruction(31)), io.instruction(30, 20)),
Instructions.jalr -> Cat(Fill(21, io.instruction(31)), io.instruction(30, 20)),
InstructionTypes.S -> Cat(Fill(21, io.instruction(31)), io.instruction(30, 25), io.instruction(11, 7)),
InstructionTypes.B -> Cat(Fill(20, io.instruction(31)), io.instruction(7), io.instruction(30, 25), io.instruction(11, 8), 0.U(1.W)),
Instructions.lui -> Cat(io.instruction(31, 12), 0.U(12.W)),
Instructions.auipc -> Cat(io.instruction(31, 12), 0.U(12.W)),
Instructions.jal -> Cat(Fill(12, io.instruction(31)), io.instruction(19, 12), io.instruction(20), io.instruction(30, 21), 0.U(1.W))
)
)
io.ex_aluop1_source := Mux(
opcode === Instructions.auipc || opcode === InstructionTypes.B || opcode === Instructions.jal,
ALUOp1Source.InstructionAddress,
ALUOp1Source.Register
)
io.ex_aluop2_source := Mux(
opcode === InstructionTypes.RM,
ALUOp2Source.Register,
ALUOp2Source.Immediate
)
io.ex_memory_read_enable := opcode === InstructionTypes.L
io.ex_memory_write_enable := opcode === InstructionTypes.S
io.ex_reg_write_source := MuxLookup(
opcode,
RegWriteSource.ALUResult,
IndexedSeq(
InstructionTypes.L -> RegWriteSource.Memory,
Instructions.csr -> RegWriteSource.CSR,
Instructions.jal -> RegWriteSource.NextInstructionAddress,
Instructions.jalr -> RegWriteSource.NextInstructionAddress
)
)
// Lab3(Forward) ID rd
// io.ex_reg_write_enable := false.B
// io.ex_reg_write_address := rd
val rd_used = opcode === InstructionTypes.RM || opcode === InstructionTypes.I || opcode === InstructionTypes.L ||
opcode === Instructions.lui || opcode === Instructions.auipc || opcode === Instructions.jal ||
opcode === Instructions.jalr || opcode === Instructions.csr
io.ex_reg_write_enable := rd_used
io.ex_reg_write_address := Mux(rd_used, rd, 0.U)
// Lab3(Forward) ID rd End
io.ex_csr_address := io.instruction(31, 20)
io.ex_csr_write_enable := (opcode === Instructions.csr) && (
funct3 === InstructionsTypeCSR.csrrw || funct3 === InstructionsTypeCSR.csrrwi ||
funct3 === InstructionsTypeCSR.csrrs || funct3 === InstructionsTypeCSR.csrrsi ||
funct3 === InstructionsTypeCSR.csrrc || funct3 === InstructionsTypeCSR.csrrci
)
}

View File

@@ -0,0 +1,48 @@
// Copyright 2021 Howard Lau
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package riscv.core.fivestage_forward
import chisel3._
import chisel3.util.MuxCase
import riscv.Parameters
object ProgramCounter {
val EntryAddress = Parameters.EntryAddress
}
class InstructionFetch extends Module {
val io = IO(new Bundle {
val stall_flag_ctrl = Input(Bool())
val jump_flag_id = Input(Bool())
val jump_address_id = Input(UInt(Parameters.AddrWidth))
val rom_instruction = Input(UInt(Parameters.DataWidth))
val instruction_valid = Input(Bool())
val instruction_address = Output(UInt(Parameters.AddrWidth))
val id_instruction = Output(UInt(Parameters.InstructionWidth))
})
val pc = RegInit(ProgramCounter.EntryAddress)
pc := MuxCase(
pc + 4.U,
IndexedSeq(
(io.jump_flag_id && !io.stall_flag_ctrl) -> io.jump_address_id,
(io.stall_flag_ctrl || !io.instruction_valid) -> pc
)
)
io.instruction_address := pc
io.id_instruction := Mux(io.instruction_valid, io.rom_instruction, InstructionsNop.nop)
}

View File

@@ -0,0 +1,83 @@
// Copyright 2022 Canbin Huang
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package riscv.core.fivestage_forward
import chisel3._
import riscv.Parameters
import riscv.core.PipelineRegister
class MEM2WB extends Module {
val io = IO(new Bundle() {
val instruction_address = Input(UInt(Parameters.AddrWidth))
val alu_result = Input(UInt(Parameters.DataWidth))
val regs_write_enable = Input(Bool())
val regs_write_source = Input(UInt(2.W))
val regs_write_address = Input(UInt(Parameters.AddrWidth))
val memory_read_data = Input(UInt(Parameters.DataWidth))
val csr_read_data = Input(UInt(Parameters.DataWidth))
val output_instruction_address = Output(UInt(Parameters.AddrWidth))
val output_alu_result = Output(UInt(Parameters.DataWidth))
val output_regs_write_enable = Output(Bool())
val output_regs_write_source = Output(UInt(2.W))
val output_regs_write_address = Output(UInt(Parameters.AddrWidth))
val output_memory_read_data = Output(UInt(Parameters.DataWidth))
val output_csr_read_data = Output(UInt(Parameters.DataWidth))
})
val stall = false.B
val flush = false.B
val alu_result = Module(new PipelineRegister())
alu_result.io.in := io.alu_result
alu_result.io.stall := stall
alu_result.io.flush := flush
io.output_alu_result := alu_result.io.out
val memory_read_data = Module(new PipelineRegister())
memory_read_data.io.in := io.memory_read_data
memory_read_data.io.stall := stall
memory_read_data.io.flush := flush
io.output_memory_read_data := memory_read_data.io.out
val regs_write_enable = Module(new PipelineRegister(1))
regs_write_enable.io.in := io.regs_write_enable
regs_write_enable.io.stall := stall
regs_write_enable.io.flush := flush
io.output_regs_write_enable := regs_write_enable.io.out
val regs_write_source = Module(new PipelineRegister(2))
regs_write_source.io.in := io.regs_write_source
regs_write_source.io.stall := stall
regs_write_source.io.flush := flush
io.output_regs_write_source := regs_write_source.io.out
val regs_write_address = Module(new PipelineRegister(Parameters.PhysicalRegisterAddrBits))
regs_write_address.io.in := io.regs_write_address
regs_write_address.io.stall := stall
regs_write_address.io.flush := flush
io.output_regs_write_address := regs_write_address.io.out
val instruction_address = Module(new PipelineRegister(Parameters.InstructionBits))
instruction_address.io.in := io.instruction_address
instruction_address.io.stall := stall
instruction_address.io.flush := flush
io.output_instruction_address := instruction_address.io.out
val csr_read_data = Module(new PipelineRegister())
csr_read_data.io.in := io.csr_read_data
csr_read_data.io.stall := stall
csr_read_data.io.flush := flush
io.output_csr_read_data := csr_read_data.io.out
}

View File

@@ -0,0 +1,109 @@
// Copyright 2022 Canbin Huang
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package riscv.core.fivestage_forward
import chisel3._
import chisel3.util._
import peripheral.RAMBundle
import riscv.Parameters
class MemoryAccess extends Module {
val io = IO(new Bundle() {
val alu_result = Input(UInt(Parameters.DataWidth))
val reg2_data = Input(UInt(Parameters.DataWidth))
val memory_read_enable = Input(Bool())
val memory_write_enable = Input(Bool())
val funct3 = Input(UInt(3.W))
val regs_write_source = Input(UInt(2.W))
val csr_read_data = Input(UInt(Parameters.DataWidth))
val wb_memory_read_data = Output(UInt(Parameters.DataWidth))
val forward_data = Output(UInt(Parameters.DataWidth))
val bundle = Flipped(new RAMBundle)
})
val mem_address_index = io.alu_result(log2Up(Parameters.WordSize) - 1, 0).asUInt
io.bundle.write_enable := io.memory_write_enable
io.bundle.write_data := 0.U
io.bundle.address := io.alu_result
io.bundle.write_strobe := VecInit(Seq.fill(Parameters.WordSize)(false.B))
io.wb_memory_read_data := 0.U
when(io.memory_read_enable) {
val data = io.bundle.read_data
io.wb_memory_read_data := MuxLookup(
io.funct3,
0.U,
IndexedSeq(
InstructionsTypeL.lb -> MuxLookup(
mem_address_index,
Cat(Fill(24, data(31)), data(31, 24)),
IndexedSeq(
0.U -> Cat(Fill(24, data(7)), data(7, 0)),
1.U -> Cat(Fill(24, data(15)), data(15, 8)),
2.U -> Cat(Fill(24, data(23)), data(23, 16))
)
),
InstructionsTypeL.lbu -> MuxLookup(
mem_address_index,
Cat(Fill(24, 0.U), data(31, 24)),
IndexedSeq(
0.U -> Cat(Fill(24, 0.U), data(7, 0)),
1.U -> Cat(Fill(24, 0.U), data(15, 8)),
2.U -> Cat(Fill(24, 0.U), data(23, 16))
)
),
InstructionsTypeL.lh -> Mux(
mem_address_index === 0.U,
Cat(Fill(16, data(15)), data(15, 0)),
Cat(Fill(16, data(31)), data(31, 16))
),
InstructionsTypeL.lhu -> Mux(
mem_address_index === 0.U,
Cat(Fill(16, 0.U), data(15, 0)),
Cat(Fill(16, 0.U), data(31, 16))
),
InstructionsTypeL.lw -> data
)
)
}.elsewhen(io.memory_write_enable) {
io.bundle.write_data := io.reg2_data
io.bundle.write_strobe := VecInit(Seq.fill(Parameters.WordSize)(false.B))
when(io.funct3 === InstructionsTypeS.sb) {
io.bundle.write_strobe(mem_address_index) := true.B
io.bundle.write_data := io.reg2_data(Parameters.ByteBits, 0) << (mem_address_index << log2Up(Parameters.ByteBits).U)
}.elsewhen(io.funct3 === InstructionsTypeS.sh) {
when(mem_address_index === 0.U) {
for (i <- 0 until Parameters.WordSize / 2) {
io.bundle.write_strobe(i) := true.B
}
io.bundle.write_data := io.reg2_data(Parameters.WordSize / 2 * Parameters.ByteBits, 0)
}.otherwise {
for (i <- Parameters.WordSize / 2 until Parameters.WordSize) {
io.bundle.write_strobe(i) := true.B
}
io.bundle.write_data := io.reg2_data(Parameters.WordSize / 2 * Parameters.ByteBits, 0) << (Parameters
.WordSize / 2 * Parameters.ByteBits)
}
}.elsewhen(io.funct3 === InstructionsTypeS.sw) {
for (i <- 0 until Parameters.WordSize) {
io.bundle.write_strobe(i) := true.B
}
}
}
io.forward_data := Mux(io.regs_write_source === RegWriteSource.CSR, io.csr_read_data, io.alu_result)
}

View File

@@ -0,0 +1,40 @@
// Copyright 2022 Canbin Huang
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package riscv.core.fivestage_forward
import chisel3._
import chisel3.util._
import riscv.Parameters
class WriteBack extends Module {
val io = IO(new Bundle() {
val instruction_address = Input(UInt(Parameters.AddrWidth))
val alu_result = Input(UInt(Parameters.DataWidth))
val memory_read_data = Input(UInt(Parameters.DataWidth))
val regs_write_source = Input(UInt(2.W))
val csr_read_data = Input(UInt(Parameters.DataWidth))
val regs_write_data = Output(UInt(Parameters.DataWidth))
})
io.regs_write_data := MuxLookup(
io.regs_write_source,
io.alu_result,
IndexedSeq(
RegWriteSource.Memory -> io.memory_read_data,
RegWriteSource.CSR -> io.csr_read_data,
RegWriteSource.NextInstructionAddress -> (io.instruction_address + 4.U)
)
)
}

View File

@@ -0,0 +1,103 @@
// Copyright 2021 Howard Lau
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package riscv.core.fivestage_stall
import chisel3._
import chisel3.util.MuxLookup
import riscv.Parameters
object InterruptStatus {
val None = 0x0.U(8.W)
val Timer0 = 0x1.U(8.W)
val Ret = 0xFF.U(8.W)
}
class CSRDirectAccessBundle extends Bundle {
val mstatus = Input(UInt(Parameters.DataWidth))
val mepc = Input(UInt(Parameters.DataWidth))
val mcause = Input(UInt(Parameters.DataWidth))
val mtvec = Input(UInt(Parameters.DataWidth))
val mstatus_write_data = Output(UInt(Parameters.DataWidth))
val mepc_write_data = Output(UInt(Parameters.DataWidth))
val mcause_write_data = Output(UInt(Parameters.DataWidth))
val direct_write_enable = Output(Bool())
}
// Core Local Interrupt Controller
class CLINT extends Module {
val io = IO(new Bundle {
val interrupt_flag = Input(UInt(Parameters.InterruptFlagWidth))
val instruction_ex = Input(UInt(Parameters.InstructionWidth))
val instruction_address_if = Input(UInt(Parameters.AddrWidth))
val instruction_address_id = Input(UInt(Parameters.AddrWidth))
val jump_flag = Input(Bool())
val jump_address = Input(UInt(Parameters.AddrWidth))
val ex_interrupt_handler_address = Output(UInt(Parameters.AddrWidth))
val ex_interrupt_assert = Output(Bool())
val csr_bundle = new CSRDirectAccessBundle
})
val interrupt_enable = io.csr_bundle.mstatus(3)
val jumpping = RegNext(io.jump_flag || io.ex_interrupt_assert)
val instruction_address = Mux(
io.jump_flag,
io.jump_address,
Mux(jumpping, io.instruction_address_if, io.instruction_address_id)
)
val mstatus_disable_interrupt = io.csr_bundle.mstatus(31, 4) ## 0.U(1.W) ## io.csr_bundle.mstatus(2, 0)
val mstatus_recover_interrupt = io.csr_bundle.mstatus(31, 4) ## io.csr_bundle.mstatus(7) ## io.csr_bundle.mstatus(2, 0)
when(io.instruction_ex === InstructionsEnv.ecall || io.instruction_ex === InstructionsEnv.ebreak) {
io.csr_bundle.mstatus_write_data := mstatus_disable_interrupt
io.csr_bundle.mepc_write_data := instruction_address
io.csr_bundle.mcause_write_data := MuxLookup(
io.instruction_ex,
10.U,
IndexedSeq(
InstructionsEnv.ecall -> 11.U,
InstructionsEnv.ebreak -> 3.U,
)
)
io.csr_bundle.direct_write_enable := true.B
io.ex_interrupt_assert := true.B
io.ex_interrupt_handler_address := io.csr_bundle.mtvec
}.elsewhen(io.interrupt_flag =/= InterruptStatus.None && interrupt_enable) {
io.csr_bundle.mstatus_write_data := mstatus_disable_interrupt
io.csr_bundle.mepc_write_data := instruction_address
io.csr_bundle.mcause_write_data := Mux(io.interrupt_flag(0), 0x80000007L.U, 0x8000000BL.U)
io.csr_bundle.direct_write_enable := true.B
io.ex_interrupt_assert := true.B
io.ex_interrupt_handler_address := io.csr_bundle.mtvec
}.elsewhen(io.instruction_ex === InstructionsRet.mret) {
io.csr_bundle.mstatus_write_data := mstatus_recover_interrupt
io.csr_bundle.mepc_write_data := io.csr_bundle.mepc
io.csr_bundle.mcause_write_data := io.csr_bundle.mcause
io.csr_bundle.direct_write_enable := true.B
io.ex_interrupt_assert := true.B
io.ex_interrupt_handler_address := io.csr_bundle.mepc
}.otherwise {
io.csr_bundle.mstatus_write_data := io.csr_bundle.mstatus
io.csr_bundle.mepc_write_data := io.csr_bundle.mepc
io.csr_bundle.mcause_write_data := io.csr_bundle.mcause
io.csr_bundle.direct_write_enable := false.B
io.ex_interrupt_assert := false.B
io.ex_interrupt_handler_address := 0.U
}
}

View File

@@ -0,0 +1,148 @@
// Copyright 2021 Howard Lau
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package riscv.core.fivestage_stall
import chisel3._
import riscv.Parameters
import riscv.core.{CPUBundle, CSR, RegisterFile}
class CPU extends Module {
val io = IO(new CPUBundle)
val ctrl = Module(new Control)
val regs = Module(new RegisterFile)
val inst_fetch = Module(new InstructionFetch)
val if2id = Module(new IF2ID)
val id = Module(new InstructionDecode)
val id2ex = Module(new ID2EX)
val ex = Module(new Execute)
val ex2mem = Module(new EX2MEM)
val mem = Module(new MemoryAccess)
val mem2wb = Module(new MEM2WB)
val wb = Module(new WriteBack)
val clint = Module(new CLINT)
val csr_regs = Module(new CSR)
ctrl.io.jump_flag := ex.io.if_jump_flag
ctrl.io.rs1_id := id.io.regs_reg1_read_address
ctrl.io.rs2_id := id.io.regs_reg2_read_address
ctrl.io.rd_ex := id2ex.io.output_regs_write_address
ctrl.io.reg_write_enable_ex := id2ex.io.output_regs_write_enable
ctrl.io.rd_mem := ex2mem.io.output_regs_write_address
ctrl.io.reg_write_enable_mem := ex2mem.io.output_regs_write_enable
regs.io.write_enable := mem2wb.io.output_regs_write_enable
regs.io.write_address := mem2wb.io.output_regs_write_address
regs.io.write_data := wb.io.regs_write_data
regs.io.read_address1 := id.io.regs_reg1_read_address
regs.io.read_address2 := id.io.regs_reg2_read_address
regs.io.debug_read_address := io.debug_read_address
io.debug_read_data := regs.io.debug_read_data
io.instruction_address := inst_fetch.io.instruction_address
inst_fetch.io.stall_flag_ctrl := ctrl.io.pc_stall
inst_fetch.io.jump_flag_id := ex.io.if_jump_flag
inst_fetch.io.jump_address_id := ex.io.if_jump_address
inst_fetch.io.rom_instruction := io.instruction
inst_fetch.io.instruction_valid := io.instruction_valid
if2id.io.stall := ctrl.io.if2id_stall
if2id.io.flush := ctrl.io.if2id_flush
if2id.io.instruction := inst_fetch.io.id_instruction
if2id.io.instruction_address := inst_fetch.io.instruction_address
if2id.io.interrupt_flag := io.interrupt_flag
id.io.instruction := if2id.io.output_instruction
id2ex.io.flush := ctrl.io.id2ex_flush
id2ex.io.instruction := if2id.io.output_instruction
id2ex.io.instruction_address := if2id.io.output_instruction_address
id2ex.io.reg1_data := regs.io.read_data1
id2ex.io.reg2_data := regs.io.read_data2
id2ex.io.regs_reg1_read_address := id.io.regs_reg1_read_address
id2ex.io.regs_reg2_read_address := id.io.regs_reg2_read_address
id2ex.io.regs_write_enable := id.io.ex_reg_write_enable
id2ex.io.regs_write_address := id.io.ex_reg_write_address
id2ex.io.regs_write_source := id.io.ex_reg_write_source
id2ex.io.immediate := id.io.ex_immediate
id2ex.io.aluop1_source := id.io.ex_aluop1_source
id2ex.io.aluop2_source := id.io.ex_aluop2_source
id2ex.io.csr_write_enable := id.io.ex_csr_write_enable
id2ex.io.csr_address := id.io.ex_csr_address
id2ex.io.memory_read_enable := id.io.ex_memory_read_enable
id2ex.io.memory_write_enable := id.io.ex_memory_write_enable
id2ex.io.csr_read_data := csr_regs.io.id_reg_read_data
ex.io.instruction := id2ex.io.output_instruction
ex.io.instruction_address := id2ex.io.output_instruction_address
ex.io.reg1_data := id2ex.io.output_reg1_data
ex.io.reg2_data := id2ex.io.output_reg2_data
ex.io.immediate_id := id2ex.io.output_immediate
ex.io.aluop1_source_id := id2ex.io.output_aluop1_source
ex.io.aluop2_source_id := id2ex.io.output_aluop2_source
ex.io.csr_read_data_id := id2ex.io.output_csr_read_data
ex.io.interrupt_assert_clint := clint.io.ex_interrupt_assert
ex.io.interrupt_handler_address_clint := clint.io.ex_interrupt_handler_address
ex2mem.io.regs_write_enable := id2ex.io.output_regs_write_enable
ex2mem.io.regs_write_source := id2ex.io.output_regs_write_source
ex2mem.io.regs_write_address := id2ex.io.output_regs_write_address
ex2mem.io.instruction_address := id2ex.io.output_instruction_address
ex2mem.io.funct3 := id2ex.io.output_instruction(14, 12)
ex2mem.io.reg2_data := ex.io.mem_reg2_data
ex2mem.io.memory_read_enable := id2ex.io.output_memory_read_enable
ex2mem.io.memory_write_enable := id2ex.io.output_memory_write_enable
ex2mem.io.alu_result := ex.io.mem_alu_result
ex2mem.io.csr_read_data := id2ex.io.output_csr_read_data
mem.io.alu_result := ex2mem.io.output_alu_result
mem.io.reg2_data := ex2mem.io.output_reg2_data
mem.io.memory_read_enable := ex2mem.io.output_memory_read_enable
mem.io.memory_write_enable := ex2mem.io.output_memory_write_enable
mem.io.funct3 := ex2mem.io.output_funct3
mem.io.regs_write_source := ex2mem.io.output_regs_write_source
mem.io.csr_read_data := ex2mem.io.output_csr_read_data
io.device_select := mem.io.bundle.address(Parameters.AddrBits - 1, Parameters.AddrBits - Parameters.SlaveDeviceCountBits)
io.memory_bundle <> mem.io.bundle
io.memory_bundle.address := 0.U(Parameters.SlaveDeviceCountBits.W) ## mem.io.bundle.address(Parameters.AddrBits - 1 - Parameters.SlaveDeviceCountBits, 0)
mem2wb.io.instruction_address := ex2mem.io.output_instruction_address
mem2wb.io.alu_result := ex2mem.io.output_alu_result
mem2wb.io.regs_write_enable := ex2mem.io.output_regs_write_enable
mem2wb.io.regs_write_source := ex2mem.io.output_regs_write_source
mem2wb.io.regs_write_address := ex2mem.io.output_regs_write_address
mem2wb.io.memory_read_data := mem.io.wb_memory_read_data
mem2wb.io.csr_read_data := ex2mem.io.output_csr_read_data
wb.io.instruction_address := mem2wb.io.output_instruction_address
wb.io.alu_result := mem2wb.io.output_alu_result
wb.io.memory_read_data := mem2wb.io.output_memory_read_data
wb.io.regs_write_source := mem2wb.io.output_regs_write_source
wb.io.csr_read_data := mem2wb.io.output_csr_read_data
clint.io.instruction_address_if := inst_fetch.io.instruction_address
clint.io.instruction_address_id := if2id.io.output_instruction_address
clint.io.instruction_ex := id2ex.io.output_instruction
clint.io.jump_flag := ex.io.clint_jump_flag
clint.io.jump_address := ex.io.clint_jump_address
clint.io.interrupt_flag := if2id.io.output_interrupt_flag
clint.io.csr_bundle <> csr_regs.io.clint_access_bundle
csr_regs.io.reg_read_address_id := id.io.ex_csr_address
csr_regs.io.reg_write_enable_ex := id2ex.io.output_csr_write_enable
csr_regs.io.reg_write_address_ex := id2ex.io.output_csr_address
csr_regs.io.reg_write_data_ex := ex.io.csr_write_data
}

View File

@@ -0,0 +1,46 @@
// Copyright 2021 Howard Lau
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package riscv.core.fivestage_stall
import chisel3._
import riscv.Parameters
class Control extends Module {
val io = IO(new Bundle {
val jump_flag = Input(Bool())
val rs1_id = Input(UInt(Parameters.PhysicalRegisterAddrWidth))
val rs2_id = Input(UInt(Parameters.PhysicalRegisterAddrWidth))
val rd_ex = Input(UInt(Parameters.PhysicalRegisterAddrWidth))
val reg_write_enable_ex = Input(Bool())
val rd_mem = Input(UInt(Parameters.PhysicalRegisterAddrWidth))
val reg_write_enable_mem = Input(Bool())
val if2id_flush = Output(Bool())
val id2ex_flush = Output(Bool())
val pc_stall = Output(Bool())
val if2id_stall = Output(Bool())
})
// Lab3(Stall)
val hazard_from_ex = io.reg_write_enable_ex && io.rd_ex =/= 0.U && (io.rd_ex === io.rs1_id || io.rd_ex === io.rs2_id)
val hazard_from_mem = io.reg_write_enable_mem && io.rd_mem =/= 0.U && (io.rd_mem === io.rs1_id || io.rd_mem === io.rs2_id)
val stall = hazard_from_ex || hazard_from_mem
val flush = io.jump_flag
io.pc_stall := stall && !flush
io.if2id_stall := stall && !flush
io.if2id_flush := flush
io.id2ex_flush := flush || stall
// Lab3(Stall) End
}

View File

@@ -0,0 +1,108 @@
// Copyright 2022 Canbin Huang
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package riscv.core.fivestage_stall
import chisel3._
import riscv.Parameters
import riscv.core.PipelineRegister
class EX2MEM extends Module {
val io = IO(new Bundle() {
val regs_write_enable = Input(Bool())
val regs_write_source = Input(UInt(2.W))
val regs_write_address = Input(UInt(Parameters.AddrWidth))
val instruction_address = Input(UInt(Parameters.AddrWidth))
val funct3 = Input(UInt(3.W))
val reg2_data = Input(UInt(Parameters.DataWidth))
val memory_read_enable = Input(Bool())
val memory_write_enable = Input(Bool())
val alu_result = Input(UInt(Parameters.DataWidth))
val csr_read_data = Input(UInt(Parameters.DataWidth))
val output_regs_write_enable = Output(Bool())
val output_regs_write_source = Output(UInt(2.W))
val output_regs_write_address = Output(UInt(Parameters.AddrWidth))
val output_instruction_address = Output(UInt(Parameters.AddrWidth))
val output_funct3 = Output(UInt(Parameters.DataWidth))
val output_reg2_data = Output(UInt(Parameters.DataWidth))
val output_memory_read_enable = Output(Bool())
val output_memory_write_enable = Output(Bool())
val output_alu_result = Output(UInt(Parameters.DataWidth))
val output_csr_read_data = Output(UInt(Parameters.DataWidth))
})
val stall = false.B
val flush = false.B
val regs_write_enable = Module(new PipelineRegister(1))
regs_write_enable.io.in := io.regs_write_enable
regs_write_enable.io.stall := stall
regs_write_enable.io.flush := flush
io.output_regs_write_enable := regs_write_enable.io.out
val regs_write_source = Module(new PipelineRegister(2))
regs_write_source.io.in := io.regs_write_source
regs_write_source.io.stall := stall
regs_write_source.io.flush := flush
io.output_regs_write_source := regs_write_source.io.out
val regs_write_address = Module(new PipelineRegister(Parameters.PhysicalRegisterAddrBits))
regs_write_address.io.in := io.regs_write_address
regs_write_address.io.stall := stall
regs_write_address.io.flush := flush
io.output_regs_write_address := regs_write_address.io.out
val instruction_address = Module(new PipelineRegister(Parameters.AddrBits))
instruction_address.io.in := io.instruction_address
instruction_address.io.stall := stall
instruction_address.io.flush := flush
io.output_instruction_address := instruction_address.io.out
val funct3 = Module(new PipelineRegister(3))
funct3.io.in := io.funct3
funct3.io.stall := stall
funct3.io.flush := flush
io.output_funct3 := funct3.io.out
val reg2_data = Module(new PipelineRegister())
reg2_data.io.in := io.reg2_data
reg2_data.io.stall := stall
reg2_data.io.flush := flush
io.output_reg2_data := reg2_data.io.out
val alu_result = Module(new PipelineRegister())
alu_result.io.in := io.alu_result
alu_result.io.stall := stall
alu_result.io.flush := flush
io.output_alu_result := alu_result.io.out
val memory_read_enable = Module(new PipelineRegister(1))
memory_read_enable.io.in := io.memory_read_enable
memory_read_enable.io.stall := stall
memory_read_enable.io.flush := flush
io.output_memory_read_enable := memory_read_enable.io.out
val memory_write_enable = Module(new PipelineRegister(1))
memory_write_enable.io.in := io.memory_write_enable
memory_write_enable.io.stall := stall
memory_write_enable.io.flush := flush
io.output_memory_write_enable := memory_write_enable.io.out
val csr_read_data = Module(new PipelineRegister())
csr_read_data.io.in := io.csr_read_data
csr_read_data.io.stall := stall
csr_read_data.io.flush := flush
io.output_csr_read_data := csr_read_data.io.out
}

View File

@@ -0,0 +1,101 @@
// Copyright 2021 Howard Lau
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package riscv.core.fivestage_stall
import chisel3._
import chisel3.util._
import riscv.Parameters
import riscv.core.{ALU, ALUControl}
class Execute extends Module {
val io = IO(new Bundle {
val instruction = Input(UInt(Parameters.InstructionWidth))
val instruction_address = Input(UInt(Parameters.AddrWidth))
val reg1_data = Input(UInt(Parameters.DataWidth))
val reg2_data = Input(UInt(Parameters.DataWidth))
val immediate_id = Input(UInt(Parameters.DataWidth))
val aluop1_source_id = Input(UInt(1.W))
val aluop2_source_id = Input(UInt(1.W))
val csr_read_data_id = Input(UInt(Parameters.DataWidth))
val interrupt_assert_clint = Input(Bool())
val interrupt_handler_address_clint = Input(UInt(Parameters.AddrWidth))
val mem_alu_result = Output(UInt(Parameters.DataWidth))
val mem_reg2_data = Output(UInt(Parameters.DataWidth))
val csr_write_data = Output(UInt(Parameters.DataWidth))
val if_jump_flag = Output(Bool())
val if_jump_address = Output(UInt(Parameters.AddrWidth))
val clint_jump_flag = Output(Bool())
val clint_jump_address = Output(UInt(Parameters.AddrWidth))
})
val opcode = io.instruction(6, 0)
val funct3 = io.instruction(14, 12)
val funct7 = io.instruction(31, 25)
val uimm = io.instruction(19, 15)
// ALU compute
val alu = Module(new ALU)
val alu_ctrl = Module(new ALUControl)
alu_ctrl.io.opcode := opcode
alu_ctrl.io.funct3 := funct3
alu_ctrl.io.funct7 := funct7
alu.io.func := alu_ctrl.io.alu_funct
alu.io.op1 := Mux(
io.aluop1_source_id === ALUOp1Source.InstructionAddress,
io.instruction_address,
io.reg1_data
)
alu.io.op2 := Mux(
io.aluop2_source_id === ALUOp2Source.Immediate,
io.immediate_id,
io.reg2_data
)
io.mem_alu_result := alu.io.result
io.mem_reg2_data := io.reg2_data
io.csr_write_data := MuxLookup(funct3, 0.U, IndexedSeq(
InstructionsTypeCSR.csrrw -> io.reg1_data,
InstructionsTypeCSR.csrrc -> io.csr_read_data_id.&((~io.reg1_data).asUInt),
InstructionsTypeCSR.csrrs -> io.csr_read_data_id.|(io.reg1_data),
InstructionsTypeCSR.csrrwi -> Cat(0.U(27.W), uimm),
InstructionsTypeCSR.csrrci -> io.csr_read_data_id.&((~Cat(0.U(27.W), uimm)).asUInt),
InstructionsTypeCSR.csrrsi -> io.csr_read_data_id.|(Cat(0.U(27.W), uimm)),
))
// jump and interrupt
val instruction_jump_flag = (opcode === Instructions.jal) ||
(opcode === Instructions.jalr) ||
(opcode === InstructionTypes.B) && MuxLookup(
funct3,
false.B,
IndexedSeq(
InstructionsTypeB.beq -> (io.reg1_data === io.reg2_data),
InstructionsTypeB.bne -> (io.reg1_data =/= io.reg2_data),
InstructionsTypeB.blt -> (io.reg1_data.asSInt < io.reg2_data.asSInt),
InstructionsTypeB.bge -> (io.reg1_data.asSInt >= io.reg2_data.asSInt),
InstructionsTypeB.bltu -> (io.reg1_data.asUInt < io.reg2_data.asUInt),
InstructionsTypeB.bgeu -> (io.reg1_data.asUInt >= io.reg2_data.asUInt)
)
)
io.clint_jump_flag := instruction_jump_flag
io.clint_jump_address := alu.io.result
io.if_jump_flag := io.interrupt_assert_clint || instruction_jump_flag
io.if_jump_address := Mux(io.interrupt_assert_clint,
io.interrupt_handler_address_clint,
alu.io.result
)
}

View File

@@ -0,0 +1,164 @@
// Copyright 2021 Howard Lau
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package riscv.core.fivestage_stall
import chisel3._
import riscv.Parameters
import riscv.core.PipelineRegister
class ID2EX extends Module {
val io = IO(new Bundle {
val flush = Input(Bool())
val instruction = Input(UInt(Parameters.InstructionWidth))
val instruction_address = Input(UInt(Parameters.AddrWidth))
val regs_reg1_read_address = Input(UInt(Parameters.PhysicalRegisterAddrWidth))
val regs_reg2_read_address = Input(UInt(Parameters.PhysicalRegisterAddrWidth))
val regs_write_enable = Input(Bool())
val regs_write_address = Input(UInt(Parameters.PhysicalRegisterAddrWidth))
val regs_write_source = Input(UInt(2.W))
val reg1_data = Input(UInt(Parameters.DataWidth))
val reg2_data = Input(UInt(Parameters.DataWidth))
val immediate = Input(UInt(Parameters.DataWidth))
val aluop1_source = Input(UInt(1.W))
val aluop2_source = Input(UInt(1.W))
val csr_write_enable = Input(Bool())
val csr_address = Input(UInt(Parameters.CSRRegisterAddrWidth))
val memory_read_enable = Input(Bool())
val memory_write_enable = Input(Bool())
val csr_read_data = Input(UInt(Parameters.DataWidth))
val output_instruction = Output(UInt(Parameters.DataWidth))
val output_instruction_address = Output(UInt(Parameters.AddrWidth))
val output_regs_reg1_read_address = Output(UInt(Parameters.PhysicalRegisterAddrWidth))
val output_regs_reg2_read_address = Output(UInt(Parameters.PhysicalRegisterAddrWidth))
val output_regs_write_enable = Output(Bool())
val output_regs_write_address = Output(UInt(Parameters.PhysicalRegisterAddrWidth))
val output_regs_write_source = Output(UInt(2.W))
val output_reg1_data = Output(UInt(Parameters.DataWidth))
val output_reg2_data = Output(UInt(Parameters.DataWidth))
val output_immediate = Output(UInt(Parameters.DataWidth))
val output_aluop1_source = Output(UInt(1.W))
val output_aluop2_source = Output(UInt(1.W))
val output_csr_write_enable = Output(Bool())
val output_csr_address = Output(UInt(Parameters.CSRRegisterAddrWidth))
val output_memory_read_enable = Output(Bool())
val output_memory_write_enable = Output(Bool())
val output_csr_read_data = Output(UInt(Parameters.DataWidth))
})
val stall = false.B
val instruction = Module(new PipelineRegister(defaultValue = InstructionsNop.nop))
instruction.io.in := io.instruction
instruction.io.stall := stall
instruction.io.flush := io.flush
io.output_instruction := instruction.io.out
val instruction_address = Module(new PipelineRegister(defaultValue = ProgramCounter.EntryAddress))
instruction_address.io.in := io.instruction_address
instruction_address.io.stall := stall
instruction_address.io.flush := io.flush
io.output_instruction_address := instruction_address.io.out
val regs_reg1_read_address = Module(new PipelineRegister(Parameters.PhysicalRegisterAddrBits))
regs_reg1_read_address.io.in := io.regs_reg1_read_address
regs_reg1_read_address.io.stall := stall
regs_reg1_read_address.io.flush := io.flush
io.output_regs_reg1_read_address := regs_reg1_read_address.io.out
val regs_reg2_read_address = Module(new PipelineRegister(Parameters.PhysicalRegisterAddrBits))
regs_reg2_read_address.io.in := io.regs_reg2_read_address
regs_reg2_read_address.io.stall := stall
regs_reg2_read_address.io.flush := io.flush
io.output_regs_reg2_read_address := regs_reg2_read_address.io.out
val regs_write_enable = Module(new PipelineRegister(1))
regs_write_enable.io.in := io.regs_write_enable
regs_write_enable.io.stall := stall
regs_write_enable.io.flush := io.flush
io.output_regs_write_enable := regs_write_enable.io.out
val regs_write_address = Module(new PipelineRegister(Parameters.PhysicalRegisterAddrBits))
regs_write_address.io.in := io.regs_write_address
regs_write_address.io.stall := stall
regs_write_address.io.flush := io.flush
io.output_regs_write_address := regs_write_address.io.out
val regs_write_source = Module(new PipelineRegister(2))
regs_write_source.io.in := io.regs_write_source
regs_write_source.io.stall := stall
regs_write_source.io.flush := io.flush
io.output_regs_write_source := regs_write_source.io.out
val reg1_data = Module(new PipelineRegister())
reg1_data.io.in := io.reg1_data
reg1_data.io.stall := stall
reg1_data.io.flush := io.flush
io.output_reg1_data := reg1_data.io.out
val reg2_data = Module(new PipelineRegister())
reg2_data.io.in := io.reg2_data
reg2_data.io.stall := stall
reg2_data.io.flush := io.flush
io.output_reg2_data := reg2_data.io.out
val immediate = Module(new PipelineRegister())
immediate.io.in := io.immediate
immediate.io.stall := stall
immediate.io.flush := io.flush
io.output_immediate := immediate.io.out
val aluop1_source = Module(new PipelineRegister(1))
aluop1_source.io.in := io.aluop1_source
aluop1_source.io.stall := stall
aluop1_source.io.flush := io.flush
io.output_aluop1_source := aluop1_source.io.out
val aluop2_source = Module(new PipelineRegister(1))
aluop2_source.io.in := io.aluop2_source
aluop2_source.io.stall := stall
aluop2_source.io.flush := io.flush
io.output_aluop2_source := aluop2_source.io.out
val csr_write_enable = Module(new PipelineRegister(1))
csr_write_enable.io.in := io.csr_write_enable
csr_write_enable.io.stall := stall
csr_write_enable.io.flush := io.flush
io.output_csr_write_enable := csr_write_enable.io.out
val csr_address = Module(new PipelineRegister(Parameters.CSRRegisterAddrBits))
csr_address.io.in := io.csr_address
csr_address.io.stall := stall
csr_address.io.flush := io.flush
io.output_csr_address := csr_address.io.out
val memory_read_enable = Module(new PipelineRegister(1))
memory_read_enable.io.in := io.memory_read_enable
memory_read_enable.io.stall := stall
memory_read_enable.io.flush := io.flush
io.output_memory_read_enable := memory_read_enable.io.out
val memory_write_enable = Module(new PipelineRegister(1))
memory_write_enable.io.in := io.memory_write_enable
memory_write_enable.io.stall := stall
memory_write_enable.io.flush := io.flush
io.output_memory_write_enable := memory_write_enable.io.out
val csr_read_data = Module(new PipelineRegister())
csr_read_data.io.in := io.csr_read_data
csr_read_data.io.stall := stall
csr_read_data.io.flush := io.flush
io.output_csr_read_data := csr_read_data.io.out
}

View File

@@ -0,0 +1,51 @@
// Copyright 2021 Howard Lau
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package riscv.core.fivestage_stall
import chisel3._
import riscv.Parameters
import riscv.core.PipelineRegister
class IF2ID extends Module {
val io = IO(new Bundle {
val stall = Input(Bool())
val flush = Input(Bool())
val instruction = Input(UInt(Parameters.InstructionWidth))
val instruction_address = Input(UInt(Parameters.AddrWidth))
val interrupt_flag = Input(UInt(Parameters.InterruptFlagWidth))
val output_instruction = Output(UInt(Parameters.DataWidth))
val output_instruction_address = Output(UInt(Parameters.AddrWidth))
val output_interrupt_flag = Output(UInt(Parameters.InterruptFlagWidth))
})
val instruction = Module(new PipelineRegister(defaultValue = InstructionsNop.nop))
instruction.io.in := io.instruction
instruction.io.stall := io.stall
instruction.io.flush := io.flush
io.output_instruction := instruction.io.out
val instruction_address = Module(new PipelineRegister(defaultValue = ProgramCounter.EntryAddress))
instruction_address.io.in := io.instruction_address
instruction_address.io.stall := io.stall
instruction_address.io.flush := io.flush
io.output_instruction_address := instruction_address.io.out
val interrupt_flag = Module(new PipelineRegister(Parameters.InterruptFlagBits))
interrupt_flag.io.in := io.interrupt_flag
interrupt_flag.io.stall := io.stall
interrupt_flag.io.flush := io.flush
io.output_interrupt_flag := interrupt_flag.io.out
}

View File

@@ -0,0 +1,214 @@
// Copyright 2021 Howard Lau
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package riscv.core.fivestage_stall
import chisel3._
import chisel3.util._
import riscv.Parameters
object InstructionTypes {
val L = "b0000011".U
val I = "b0010011".U
val S = "b0100011".U
val RM = "b0110011".U
val B = "b1100011".U
}
object Instructions {
val lui = "b0110111".U
val nop = "b0000001".U
val jal = "b1101111".U
val jalr = "b1100111".U
val auipc = "b0010111".U
val csr = "b1110011".U
val fence = "b0001111".U
}
object InstructionsTypeL {
val lb = "b000".U
val lh = "b001".U
val lw = "b010".U
val lbu = "b100".U
val lhu = "b101".U
}
object InstructionsTypeI {
val addi = 0.U
val slli = 1.U
val slti = 2.U
val sltiu = 3.U
val xori = 4.U
val sri = 5.U
val ori = 6.U
val andi = 7.U
}
object InstructionsTypeS {
val sb = "b000".U
val sh = "b001".U
val sw = "b010".U
}
object InstructionsTypeR {
val add_sub = 0.U
val sll = 1.U
val slt = 2.U
val sltu = 3.U
val xor = 4.U
val sr = 5.U
val or = 6.U
val and = 7.U
}
object InstructionsTypeM {
val mul = 0.U
val mulh = 1.U
val mulhsu = 2.U
val mulhum = 3.U
val div = 4.U
val divu = 5.U
val rem = 6.U
val remu = 7.U
}
object InstructionsTypeB {
val beq = "b000".U
val bne = "b001".U
val blt = "b100".U
val bge = "b101".U
val bltu = "b110".U
val bgeu = "b111".U
}
object InstructionsTypeCSR {
val csrrw = "b001".U
val csrrs = "b010".U
val csrrc = "b011".U
val csrrwi = "b101".U
val csrrsi = "b110".U
val csrrci = "b111".U
}
object InstructionsNop {
val nop = 0x00000013L.U(Parameters.DataWidth)
}
object InstructionsRet {
val mret = 0x30200073L.U(Parameters.DataWidth)
val ret = 0x00008067L.U(Parameters.DataWidth)
}
object InstructionsEnv {
val ecall = 0x00000073L.U(Parameters.DataWidth)
val ebreak = 0x00100073L.U(Parameters.DataWidth)
}
object ALUOp1Source {
val Register = 0.U(1.W)
val InstructionAddress = 1.U(1.W)
}
object ALUOp2Source {
val Register = 0.U(1.W)
val Immediate = 1.U(1.W)
}
object RegWriteSource {
val ALUResult = 0.U(2.W)
val Memory = 1.U(2.W)
val CSR = 2.U(2.W)
val NextInstructionAddress = 3.U(2.W)
}
class InstructionDecode extends Module {
val io = IO(new Bundle {
val instruction = Input(UInt(Parameters.InstructionWidth))
val regs_reg1_read_address = Output(UInt(Parameters.PhysicalRegisterAddrWidth))
val regs_reg2_read_address = Output(UInt(Parameters.PhysicalRegisterAddrWidth))
val ex_immediate = Output(UInt(Parameters.DataWidth))
val ex_aluop1_source = Output(Bool())
val ex_aluop2_source = Output(Bool())
val ex_memory_read_enable = Output(Bool())
val ex_memory_write_enable = Output(Bool())
val ex_reg_write_source = Output(UInt(2.W))
val ex_reg_write_enable = Output(Bool())
val ex_reg_write_address = Output(UInt(Parameters.PhysicalRegisterAddrWidth))
val ex_csr_address = Output(UInt(Parameters.CSRRegisterAddrWidth))
val ex_csr_write_enable = Output(Bool())
})
val opcode = io.instruction(6, 0)
val funct3 = io.instruction(14, 12)
val funct7 = io.instruction(31, 25)
val rd = io.instruction(11, 7)
val rs1 = io.instruction(19, 15)
val rs2 = io.instruction(24, 20)
// Lab3(Stall) ID rs
val rs1_used = opcode =/= Instructions.lui && opcode =/= Instructions.auipc && opcode =/= Instructions.jal
val rs2_used = opcode === InstructionTypes.RM || opcode === InstructionTypes.S || opcode === InstructionTypes.B
io.regs_reg1_read_address := Mux(rs1_used, rs1, 0.U)
io.regs_reg2_read_address := Mux(rs2_used, rs2, 0.U)
// Lab3(Stall) ID rs End
io.ex_immediate := MuxLookup(
opcode,
Cat(Fill(20, io.instruction(31)), io.instruction(31, 20)),
IndexedSeq(
InstructionTypes.I -> Cat(Fill(21, io.instruction(31)), io.instruction(30, 20)),
InstructionTypes.L -> Cat(Fill(21, io.instruction(31)), io.instruction(30, 20)),
Instructions.jalr -> Cat(Fill(21, io.instruction(31)), io.instruction(30, 20)),
InstructionTypes.S -> Cat(Fill(21, io.instruction(31)), io.instruction(30, 25), io.instruction(11, 7)),
InstructionTypes.B -> Cat(Fill(20, io.instruction(31)), io.instruction(7), io.instruction(30, 25), io.instruction(11, 8), 0.U(1.W)),
Instructions.lui -> Cat(io.instruction(31, 12), 0.U(12.W)),
Instructions.auipc -> Cat(io.instruction(31, 12), 0.U(12.W)),
Instructions.jal -> Cat(Fill(12, io.instruction(31)), io.instruction(19, 12), io.instruction(20), io.instruction(30, 21), 0.U(1.W))
)
)
io.ex_aluop1_source := Mux(
opcode === Instructions.auipc || opcode === InstructionTypes.B || opcode === Instructions.jal,
ALUOp1Source.InstructionAddress,
ALUOp1Source.Register
)
io.ex_aluop2_source := Mux(
opcode === InstructionTypes.RM,
ALUOp2Source.Register,
ALUOp2Source.Immediate
)
io.ex_memory_read_enable := opcode === InstructionTypes.L
io.ex_memory_write_enable := opcode === InstructionTypes.S
io.ex_reg_write_source := MuxLookup(
opcode,
RegWriteSource.ALUResult,
IndexedSeq(
InstructionTypes.L -> RegWriteSource.Memory,
Instructions.csr -> RegWriteSource.CSR,
Instructions.jal -> RegWriteSource.NextInstructionAddress,
Instructions.jalr -> RegWriteSource.NextInstructionAddress
)
)
// Lab3(Stall) ID rd
val rd_used = opcode === InstructionTypes.RM || opcode === InstructionTypes.I || opcode === InstructionTypes.L ||
opcode === Instructions.lui || opcode === Instructions.auipc || opcode === Instructions.jal ||
opcode === Instructions.jalr || opcode === Instructions.csr
io.ex_reg_write_enable := rd_used
io.ex_reg_write_address := Mux(rd_used, rd, 0.U)
// Lab3(Stall) ID rd End
io.ex_csr_address := io.instruction(31, 20)
io.ex_csr_write_enable := (opcode === Instructions.csr) && (
funct3 === InstructionsTypeCSR.csrrw || funct3 === InstructionsTypeCSR.csrrwi ||
funct3 === InstructionsTypeCSR.csrrs || funct3 === InstructionsTypeCSR.csrrsi ||
funct3 === InstructionsTypeCSR.csrrc || funct3 === InstructionsTypeCSR.csrrci
)
}

View File

@@ -0,0 +1,48 @@
// Copyright 2021 Howard Lau
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package riscv.core.fivestage_stall
import chisel3._
import chisel3.util.MuxCase
import riscv.Parameters
object ProgramCounter {
val EntryAddress = Parameters.EntryAddress
}
class InstructionFetch extends Module {
val io = IO(new Bundle {
val stall_flag_ctrl = Input(Bool())
val jump_flag_id = Input(Bool())
val jump_address_id = Input(UInt(Parameters.AddrWidth))
val rom_instruction = Input(UInt(Parameters.DataWidth))
val instruction_valid = Input(Bool())
val instruction_address = Output(UInt(Parameters.AddrWidth))
val id_instruction = Output(UInt(Parameters.InstructionWidth))
})
val pc = RegInit(ProgramCounter.EntryAddress)
pc := MuxCase(
pc + 4.U,
IndexedSeq(
(io.jump_flag_id && !io.stall_flag_ctrl) -> io.jump_address_id,
(io.stall_flag_ctrl || !io.instruction_valid) -> pc
)
)
io.instruction_address := pc
io.id_instruction := Mux(io.instruction_valid, io.rom_instruction, InstructionsNop.nop)
}

View File

@@ -0,0 +1,83 @@
// Copyright 2022 Canbin Huang
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package riscv.core.fivestage_stall
import chisel3._
import riscv.Parameters
import riscv.core.PipelineRegister
class MEM2WB extends Module {
val io = IO(new Bundle() {
val instruction_address = Input(UInt(Parameters.AddrWidth))
val alu_result = Input(UInt(Parameters.DataWidth))
val regs_write_enable = Input(Bool())
val regs_write_source = Input(UInt(2.W))
val regs_write_address = Input(UInt(Parameters.AddrWidth))
val memory_read_data = Input(UInt(Parameters.DataWidth))
val csr_read_data = Input(UInt(Parameters.DataWidth))
val output_instruction_address = Output(UInt(Parameters.AddrWidth))
val output_alu_result = Output(UInt(Parameters.DataWidth))
val output_regs_write_enable = Output(Bool())
val output_regs_write_source = Output(UInt(2.W))
val output_regs_write_address = Output(UInt(Parameters.AddrWidth))
val output_memory_read_data = Output(UInt(Parameters.DataWidth))
val output_csr_read_data = Output(UInt(Parameters.DataWidth))
})
val stall = false.B
val flush = false.B
val alu_result = Module(new PipelineRegister())
alu_result.io.in := io.alu_result
alu_result.io.stall := stall
alu_result.io.flush := flush
io.output_alu_result := alu_result.io.out
val memory_read_data = Module(new PipelineRegister())
memory_read_data.io.in := io.memory_read_data
memory_read_data.io.stall := stall
memory_read_data.io.flush := flush
io.output_memory_read_data := memory_read_data.io.out
val regs_write_enable = Module(new PipelineRegister(1))
regs_write_enable.io.in := io.regs_write_enable
regs_write_enable.io.stall := stall
regs_write_enable.io.flush := flush
io.output_regs_write_enable := regs_write_enable.io.out
val regs_write_source = Module(new PipelineRegister(2))
regs_write_source.io.in := io.regs_write_source
regs_write_source.io.stall := stall
regs_write_source.io.flush := flush
io.output_regs_write_source := regs_write_source.io.out
val regs_write_address = Module(new PipelineRegister(Parameters.PhysicalRegisterAddrBits))
regs_write_address.io.in := io.regs_write_address
regs_write_address.io.stall := stall
regs_write_address.io.flush := flush
io.output_regs_write_address := regs_write_address.io.out
val instruction_address = Module(new PipelineRegister(Parameters.InstructionBits))
instruction_address.io.in := io.instruction_address
instruction_address.io.stall := stall
instruction_address.io.flush := flush
io.output_instruction_address := instruction_address.io.out
val csr_read_data = Module(new PipelineRegister())
csr_read_data.io.in := io.csr_read_data
csr_read_data.io.stall := stall
csr_read_data.io.flush := flush
io.output_csr_read_data := csr_read_data.io.out
}

View File

@@ -0,0 +1,106 @@
// Copyright 2022 Canbin Huang
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package riscv.core.fivestage_stall
import chisel3._
import chisel3.util._
import peripheral.RAMBundle
import riscv.Parameters
class MemoryAccess extends Module {
val io = IO(new Bundle() {
val alu_result = Input(UInt(Parameters.DataWidth))
val reg2_data = Input(UInt(Parameters.DataWidth))
val memory_read_enable = Input(Bool())
val memory_write_enable = Input(Bool())
val funct3 = Input(UInt(3.W))
val regs_write_source = Input(UInt(2.W))
val csr_read_data = Input(UInt(Parameters.DataWidth))
val wb_memory_read_data = Output(UInt(Parameters.DataWidth))
val bundle = Flipped(new RAMBundle)
})
val mem_address_index = io.alu_result(log2Up(Parameters.WordSize) - 1, 0).asUInt
io.bundle.write_enable := io.memory_write_enable
io.bundle.write_data := 0.U
io.bundle.address := io.alu_result
io.bundle.write_strobe := VecInit(Seq.fill(Parameters.WordSize)(false.B))
io.wb_memory_read_data := 0.U
when(io.memory_read_enable) {
val data = io.bundle.read_data
io.wb_memory_read_data := MuxLookup(
io.funct3,
0.U,
IndexedSeq(
InstructionsTypeL.lb -> MuxLookup(
mem_address_index,
Cat(Fill(24, data(31)), data(31, 24)),
IndexedSeq(
0.U -> Cat(Fill(24, data(7)), data(7, 0)),
1.U -> Cat(Fill(24, data(15)), data(15, 8)),
2.U -> Cat(Fill(24, data(23)), data(23, 16))
)
),
InstructionsTypeL.lbu -> MuxLookup(
mem_address_index,
Cat(Fill(24, 0.U), data(31, 24)),
IndexedSeq(
0.U -> Cat(Fill(24, 0.U), data(7, 0)),
1.U -> Cat(Fill(24, 0.U), data(15, 8)),
2.U -> Cat(Fill(24, 0.U), data(23, 16))
)
),
InstructionsTypeL.lh -> Mux(
mem_address_index === 0.U,
Cat(Fill(16, data(15)), data(15, 0)),
Cat(Fill(16, data(31)), data(31, 16))
),
InstructionsTypeL.lhu -> Mux(
mem_address_index === 0.U,
Cat(Fill(16, 0.U), data(15, 0)),
Cat(Fill(16, 0.U), data(31, 16))
),
InstructionsTypeL.lw -> data
)
)
}.elsewhen(io.memory_write_enable) {
io.bundle.write_data := io.reg2_data
io.bundle.write_strobe := VecInit(Seq.fill(Parameters.WordSize)(false.B))
when(io.funct3 === InstructionsTypeS.sb) {
io.bundle.write_strobe(mem_address_index) := true.B
io.bundle.write_data := io.reg2_data(Parameters.ByteBits, 0) << (mem_address_index << log2Up(Parameters.ByteBits).U)
}.elsewhen(io.funct3 === InstructionsTypeS.sh) {
when(mem_address_index === 0.U) {
for (i <- 0 until Parameters.WordSize / 2) {
io.bundle.write_strobe(i) := true.B
}
io.bundle.write_data := io.reg2_data(Parameters.WordSize / 2 * Parameters.ByteBits, 0)
}.otherwise {
for (i <- Parameters.WordSize / 2 until Parameters.WordSize) {
io.bundle.write_strobe(i) := true.B
}
io.bundle.write_data := io.reg2_data(Parameters.WordSize / 2 * Parameters.ByteBits, 0) << (Parameters
.WordSize / 2 * Parameters.ByteBits)
}
}.elsewhen(io.funct3 === InstructionsTypeS.sw) {
for (i <- 0 until Parameters.WordSize) {
io.bundle.write_strobe(i) := true.B
}
}
}
}

View File

@@ -0,0 +1,40 @@
// Copyright 2022 Canbin Huang
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package riscv.core.fivestage_stall
import chisel3._
import chisel3.util._
import riscv.Parameters
class WriteBack extends Module {
val io = IO(new Bundle() {
val instruction_address = Input(UInt(Parameters.AddrWidth))
val alu_result = Input(UInt(Parameters.DataWidth))
val memory_read_data = Input(UInt(Parameters.DataWidth))
val regs_write_source = Input(UInt(2.W))
val csr_read_data = Input(UInt(Parameters.DataWidth))
val regs_write_data = Output(UInt(Parameters.DataWidth))
})
io.regs_write_data := MuxLookup(
io.regs_write_source,
io.alu_result,
IndexedSeq(
RegWriteSource.Memory -> io.memory_read_data,
RegWriteSource.CSR -> io.csr_read_data,
RegWriteSource.NextInstructionAddress -> (io.instruction_address + 4.U)
)
)
}

View File

@@ -0,0 +1,88 @@
// Copyright 2021 Howard Lau
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package riscv.core.threestage
import chisel3._
import chisel3.util.MuxLookup
import riscv.Parameters
object InterruptStatus {
val None = 0x0.U(8.W)
val Timer0 = 0x1.U(8.W)
val Ret = 0xFF.U(8.W)
}
class CSRDirectAccessBundle extends Bundle {
val mstatus = Input(UInt(Parameters.DataWidth))
val mepc = Input(UInt(Parameters.DataWidth))
val mcause = Input(UInt(Parameters.DataWidth))
val mtvec = Input(UInt(Parameters.DataWidth))
val mstatus_write_data = Output(UInt(Parameters.DataWidth))
val mepc_write_data = Output(UInt(Parameters.DataWidth))
val mcause_write_data = Output(UInt(Parameters.DataWidth))
val direct_write_enable = Output(Bool())
}
// Core Local Interrupt Controller
class CLINT extends Module {
val io = IO(new Bundle {
val interrupt_flag = Input(UInt(Parameters.InterruptFlagWidth))
val instruction_ex = Input(UInt(Parameters.InstructionWidth))
val instruction_address_if = Input(UInt(Parameters.AddrWidth))
val instruction_address_id = Input(UInt(Parameters.AddrWidth))
val jump_flag = Input(Bool())
val jump_address = Input(UInt(Parameters.AddrWidth))
val ex_interrupt_handler_address = Output(UInt(Parameters.AddrWidth))
val ex_interrupt_assert = Output(Bool())
val csr_bundle = new CSRDirectAccessBundle
})
val interrupt_enable = io.csr_bundle.mstatus(3)
val jumpping = RegNext(io.jump_flag || io.ex_interrupt_assert)
val instruction_address = Mux(
io.jump_flag,
io.jump_address,
Mux(jumpping, io.instruction_address_if, io.instruction_address_id)
)
val mstatus_disable_interrupt = io.csr_bundle.mstatus(31, 4) ## 0.U(1.W) ## io.csr_bundle.mstatus(2, 0)
val mstatus_recover_interrupt = io.csr_bundle.mstatus(31, 4) ## io.csr_bundle.mstatus(7) ## io.csr_bundle.mstatus(2, 0)
val exception = io.instruction_ex === InstructionsEnv.ecall || io.instruction_ex === InstructionsEnv.ebreak
val interrupt = io.interrupt_flag =/= InterruptStatus.None && interrupt_enable
val mret = io.instruction_ex === InstructionsRet.mret
val interrupt_assert = exception || interrupt || mret
io.csr_bundle.mstatus_write_data := Mux(mret, mstatus_recover_interrupt, mstatus_disable_interrupt)
io.csr_bundle.mepc_write_data := instruction_address
io.csr_bundle.mcause_write_data := Mux(
exception,
MuxLookup(
io.instruction_ex,
10.U,
IndexedSeq(
InstructionsEnv.ecall -> 11.U,
InstructionsEnv.ebreak -> 3.U,
)
),
Mux(io.interrupt_flag(0), 0x80000007L.U, 0x8000000BL.U)
)
io.csr_bundle.direct_write_enable := interrupt_assert
io.ex_interrupt_assert := interrupt_assert
io.ex_interrupt_handler_address := Mux(mret, io.csr_bundle.mepc, io.csr_bundle.mtvec)
}

View File

@@ -0,0 +1,107 @@
// Copyright 2021 Howard Lau
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package riscv.core.threestage
import chisel3._
import riscv.Parameters
import riscv.core.{CPUBundle, CSR, RegisterFile}
class CPU extends Module {
val io = IO(new CPUBundle)
val ctrl = Module(new Control)
val regs = Module(new RegisterFile)
val inst_fetch = Module(new InstructionFetch)
val if2id = Module(new IF2ID)
val id = Module(new InstructionDecode)
val id2ex = Module(new ID2EX)
val ex = Module(new Execute)
val clint = Module(new CLINT)
val csr_regs = Module(new CSR)
// Lab3(ThreeStage)
// if2id.io.flush := false.B
// id2ex.io.flush := false.B
ctrl.io.jump_flag_ex := ex.io.if_jump_flag
if2id.io.flush := ctrl.io.if2id_flush
id2ex.io.flush := ctrl.io.id2ex_flush
// Lab3(ThreeStage) End
regs.io.write_enable := id2ex.io.output_regs_write_enable
regs.io.write_address := id2ex.io.output_regs_write_address
regs.io.write_data := ex.io.regs_write_data
regs.io.read_address1 := id.io.regs_reg1_read_address
regs.io.read_address2 := id.io.regs_reg2_read_address
regs.io.debug_read_address := io.debug_read_address
io.debug_read_data := regs.io.debug_read_data
io.instruction_address := inst_fetch.io.instruction_address
inst_fetch.io.jump_flag_ex := ex.io.if_jump_flag
inst_fetch.io.jump_address_ex := ex.io.if_jump_address
inst_fetch.io.rom_instruction := io.instruction
inst_fetch.io.instruction_valid := io.instruction_valid
if2id.io.instruction := inst_fetch.io.id_instruction
if2id.io.instruction_address := inst_fetch.io.instruction_address
if2id.io.interrupt_flag := io.interrupt_flag
id.io.instruction := if2id.io.output_instruction
id2ex.io.instruction := if2id.io.output_instruction
id2ex.io.instruction_address := if2id.io.output_instruction_address
id2ex.io.reg1_data := regs.io.read_data1
id2ex.io.reg2_data := regs.io.read_data2
id2ex.io.regs_write_enable := id.io.ex_reg_write_enable
id2ex.io.regs_write_address := id.io.ex_reg_write_address
id2ex.io.regs_write_source := id.io.ex_reg_write_source
id2ex.io.immediate := id.io.ex_immediate
id2ex.io.aluop1_source := id.io.ex_aluop1_source
id2ex.io.aluop2_source := id.io.ex_aluop2_source
id2ex.io.csr_write_enable := id.io.ex_csr_write_enable
id2ex.io.csr_address := id.io.ex_csr_address
id2ex.io.memory_read_enable := id.io.ex_memory_read_enable
id2ex.io.memory_write_enable := id.io.ex_memory_write_enable
id2ex.io.csr_read_data := csr_regs.io.id_reg_read_data
ex.io.instruction := id2ex.io.output_instruction
ex.io.instruction_address := id2ex.io.output_instruction_address
ex.io.reg1_data := id2ex.io.output_reg1_data
ex.io.reg2_data := id2ex.io.output_reg2_data
ex.io.csr_read_data := id2ex.io.output_csr_read_data
ex.io.immediate_id := id2ex.io.output_immediate
ex.io.aluop1_source_id := id2ex.io.output_aluop1_source
ex.io.aluop2_source_id := id2ex.io.output_aluop2_source
ex.io.memory_read_enable_id := id2ex.io.output_memory_read_enable
ex.io.memory_write_enable_id := id2ex.io.output_memory_write_enable
ex.io.regs_write_source_id := id2ex.io.output_regs_write_source
ex.io.interrupt_assert_clint := clint.io.ex_interrupt_assert
ex.io.interrupt_handler_address_clint := clint.io.ex_interrupt_handler_address
io.device_select := ex.io.memory_bundle.address(Parameters.AddrBits - 1, Parameters.AddrBits - Parameters.SlaveDeviceCountBits)
io.memory_bundle <> ex.io.memory_bundle
io.memory_bundle.address := 0.U(Parameters.SlaveDeviceCountBits.W) ## ex.io.memory_bundle.address(Parameters.AddrBits - 1 - Parameters.SlaveDeviceCountBits, 0)
clint.io.instruction_address_if := inst_fetch.io.instruction_address
clint.io.instruction_address_id := if2id.io.output_instruction_address
clint.io.instruction_ex := id2ex.io.output_instruction
clint.io.jump_flag := ex.io.clint_jump_flag
clint.io.jump_address := ex.io.clint_jump_address
clint.io.interrupt_flag := if2id.io.output_interrupt_flag
clint.io.csr_bundle <> csr_regs.io.clint_access_bundle
csr_regs.io.reg_read_address_id := id.io.ex_csr_address
csr_regs.io.reg_write_enable_ex := id2ex.io.output_csr_write_enable
csr_regs.io.reg_write_address_ex := id2ex.io.output_csr_address
csr_regs.io.reg_write_data_ex := ex.io.csr_write_data
}

View File

@@ -0,0 +1,30 @@
// Copyright 2021 Howard Lau
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package riscv.core.threestage
import chisel3._
class Control extends Module {
// Lab3(ThreeStage)
val io = IO(new Bundle {
val jump_flag_ex = Input(Bool())
val if2id_flush = Output(Bool())
val id2ex_flush = Output(Bool())
})
val flush = io.jump_flag_ex
io.if2id_flush := flush
io.id2ex_flush := flush
// Lab3(ThreeStage) End
}

View File

@@ -0,0 +1,191 @@
// Copyright 2021 Howard Lau
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package riscv.core.threestage
import chisel3._
import chisel3.util._
import peripheral.RAMBundle
import riscv.Parameters
import riscv.core.{ALU, ALUControl}
class Execute extends Module {
val io = IO(new Bundle {
val instruction = Input(UInt(Parameters.InstructionWidth))
val instruction_address = Input(UInt(Parameters.AddrWidth))
val reg1_data = Input(UInt(Parameters.DataWidth))
val reg2_data = Input(UInt(Parameters.DataWidth))
val csr_read_data = Input(UInt(Parameters.DataWidth))
val immediate_id = Input(UInt(Parameters.DataWidth))
val aluop1_source_id = Input(UInt(1.W))
val aluop2_source_id = Input(UInt(1.W))
val memory_read_enable_id = Input(Bool())
val memory_write_enable_id = Input(Bool())
val regs_write_source_id = Input(UInt(2.W))
val interrupt_assert_clint = Input(Bool())
val interrupt_handler_address_clint = Input(UInt(Parameters.AddrWidth))
val memory_bundle = Flipped(new RAMBundle)
val csr_write_data = Output(UInt(Parameters.DataWidth))
val regs_write_data = Output(UInt(Parameters.DataWidth))
val if_jump_flag = Output(Bool())
val if_jump_address = Output(UInt(Parameters.AddrWidth))
val clint_jump_flag = Output(Bool())
val clint_jump_address = Output(UInt(Parameters.AddrWidth))
})
val opcode = io.instruction(6, 0)
val funct3 = io.instruction(14, 12)
val funct7 = io.instruction(31, 25)
val uimm = io.instruction(19, 15)
// ALU compute
val alu = Module(new ALU)
val alu_ctrl = Module(new ALUControl)
alu_ctrl.io.opcode := opcode
alu_ctrl.io.funct3 := funct3
alu_ctrl.io.funct7 := funct7
alu.io.func := alu_ctrl.io.alu_funct
alu.io.op1 := Mux(
io.aluop1_source_id === ALUOp1Source.InstructionAddress,
io.instruction_address,
io.reg1_data
)
alu.io.op2 := Mux(
io.aluop2_source_id === ALUOp2Source.Immediate,
io.immediate_id,
io.reg2_data
)
io.csr_write_data := MuxLookup(
funct3,
0.U,
IndexedSeq(
InstructionsTypeCSR.csrrw -> io.reg1_data,
InstructionsTypeCSR.csrrc -> (io.csr_read_data & (~io.reg1_data).asUInt),
InstructionsTypeCSR.csrrs -> (io.csr_read_data | io.reg1_data),
InstructionsTypeCSR.csrrwi -> (0.U(27.W) ## uimm),
InstructionsTypeCSR.csrrci -> (io.csr_read_data & (~(0.U(27.W) ## uimm)).asUInt),
InstructionsTypeCSR.csrrsi -> (io.csr_read_data | 0.U(27.W) ## uimm),
)
)
// memory access
val mem_address_index = alu.io.result(log2Up(Parameters.WordSize) - 1, 0).asUInt
val mem_read_data = Wire(UInt(Parameters.DataWidth))
io.memory_bundle.write_enable := io.memory_write_enable_id
io.memory_bundle.write_data := 0.U
io.memory_bundle.address := alu.io.result
io.memory_bundle.write_strobe := VecInit(Seq.fill(Parameters.WordSize)(false.B))
mem_read_data := 0.U
when(io.memory_read_enable_id) {
val data = io.memory_bundle.read_data
mem_read_data := MuxLookup(
funct3,
0.U,
IndexedSeq(
InstructionsTypeL.lb -> MuxLookup(
mem_address_index,
Cat(Fill(24, data(31)), data(31, 24)),
IndexedSeq(
0.U -> Cat(Fill(24, data(7)), data(7, 0)),
1.U -> Cat(Fill(24, data(15)), data(15, 8)),
2.U -> Cat(Fill(24, data(23)), data(23, 16))
)
),
InstructionsTypeL.lbu -> MuxLookup(
mem_address_index,
Cat(Fill(24, 0.U), data(31, 24)),
IndexedSeq(
0.U -> Cat(Fill(24, 0.U), data(7, 0)),
1.U -> Cat(Fill(24, 0.U), data(15, 8)),
2.U -> Cat(Fill(24, 0.U), data(23, 16))
)
),
InstructionsTypeL.lh -> Mux(
mem_address_index === 0.U,
Cat(Fill(16, data(15)), data(15, 0)),
Cat(Fill(16, data(31)), data(31, 16))
),
InstructionsTypeL.lhu -> Mux(
mem_address_index === 0.U,
Cat(Fill(16, 0.U), data(15, 0)),
Cat(Fill(16, 0.U), data(31, 16))
),
InstructionsTypeL.lw -> data
)
)
}.elsewhen(io.memory_write_enable_id) {
io.memory_bundle.write_data := io.reg2_data
io.memory_bundle.write_strobe := VecInit(Seq.fill(Parameters.WordSize)(false.B))
when(funct3 === InstructionsTypeS.sb) {
io.memory_bundle.write_strobe(mem_address_index) := true.B
io.memory_bundle.write_data := io.reg2_data(Parameters.ByteBits, 0) << (mem_address_index << log2Up(Parameters.ByteBits).U)
}.elsewhen(funct3 === InstructionsTypeS.sh) {
when(mem_address_index === 0.U) {
for (i <- 0 until Parameters.WordSize / 2) {
io.memory_bundle.write_strobe(i) := true.B
}
io.memory_bundle.write_data := io.reg2_data(Parameters.WordSize / 2 * Parameters.ByteBits, 0)
}.otherwise {
for (i <- Parameters.WordSize / 2 until Parameters.WordSize) {
io.memory_bundle.write_strobe(i) := true.B
}
io.memory_bundle.write_data := io.reg2_data(Parameters.WordSize / 2 * Parameters.ByteBits, 0) << (Parameters
.WordSize / 2 * Parameters.ByteBits)
}
}.elsewhen(funct3 === InstructionsTypeS.sw) {
for (i <- 0 until Parameters.WordSize) {
io.memory_bundle.write_strobe(i) := true.B
}
}
}
// write back
io.regs_write_data := MuxLookup(
io.regs_write_source_id,
alu.io.result,
IndexedSeq(
RegWriteSource.Memory -> mem_read_data,
RegWriteSource.CSR -> io.csr_read_data,
RegWriteSource.NextInstructionAddress -> (io.instruction_address + 4.U)
)
)
// jump and interrupt
val instruction_jump_flag = (opcode === Instructions.jal) ||
(opcode === Instructions.jalr) ||
(opcode === InstructionTypes.B) && MuxLookup(
funct3,
false.B,
IndexedSeq(
InstructionsTypeB.beq -> (io.reg1_data === io.reg2_data),
InstructionsTypeB.bne -> (io.reg1_data =/= io.reg2_data),
InstructionsTypeB.blt -> (io.reg1_data.asSInt < io.reg2_data.asSInt),
InstructionsTypeB.bge -> (io.reg1_data.asSInt >= io.reg2_data.asSInt),
InstructionsTypeB.bltu -> (io.reg1_data.asUInt < io.reg2_data.asUInt),
InstructionsTypeB.bgeu -> (io.reg1_data.asUInt >= io.reg2_data.asUInt)
)
)
io.clint_jump_flag := instruction_jump_flag
io.clint_jump_address := alu.io.result
io.if_jump_flag := io.interrupt_assert_clint || instruction_jump_flag
io.if_jump_address := Mux(io.interrupt_assert_clint,
io.interrupt_handler_address_clint,
alu.io.result
)
}

View File

@@ -0,0 +1,148 @@
// Copyright 2021 Howard Lau
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package riscv.core.threestage
import chisel3._
import riscv.Parameters
import riscv.core.PipelineRegister
class ID2EX extends Module {
val io = IO(new Bundle {
val flush = Input(Bool())
val instruction = Input(UInt(Parameters.InstructionWidth))
val instruction_address = Input(UInt(Parameters.AddrWidth))
val regs_write_enable = Input(Bool())
val regs_write_address = Input(UInt(Parameters.PhysicalRegisterAddrWidth))
val regs_write_source = Input(UInt(2.W))
val reg1_data = Input(UInt(Parameters.DataWidth))
val reg2_data = Input(UInt(Parameters.DataWidth))
val immediate = Input(UInt(Parameters.DataWidth))
val aluop1_source = Input(UInt(1.W))
val aluop2_source = Input(UInt(1.W))
val csr_write_enable = Input(Bool())
val csr_address = Input(UInt(Parameters.CSRRegisterAddrWidth))
val memory_read_enable = Input(Bool())
val memory_write_enable = Input(Bool())
val csr_read_data = Input(UInt(Parameters.DataWidth))
val output_instruction = Output(UInt(Parameters.DataWidth))
val output_instruction_address = Output(UInt(Parameters.AddrWidth))
val output_regs_write_enable = Output(Bool())
val output_regs_write_address = Output(UInt(Parameters.PhysicalRegisterAddrWidth))
val output_regs_write_source = Output(UInt(2.W))
val output_reg1_data = Output(UInt(Parameters.DataWidth))
val output_reg2_data = Output(UInt(Parameters.DataWidth))
val output_immediate = Output(UInt(Parameters.DataWidth))
val output_aluop1_source = Output(UInt(1.W))
val output_aluop2_source = Output(UInt(1.W))
val output_csr_write_enable = Output(Bool())
val output_csr_address = Output(UInt(Parameters.CSRRegisterAddrWidth))
val output_memory_read_enable = Output(Bool())
val output_memory_write_enable = Output(Bool())
val output_csr_read_data = Output(UInt(Parameters.DataWidth))
})
val stall = false.B
val instruction = Module(new PipelineRegister(defaultValue = InstructionsNop.nop))
instruction.io.in := io.instruction
instruction.io.stall := stall
instruction.io.flush := io.flush
io.output_instruction := instruction.io.out
val instruction_address = Module(new PipelineRegister(defaultValue = ProgramCounter.EntryAddress))
instruction_address.io.in := io.instruction_address
instruction_address.io.stall := stall
instruction_address.io.flush := io.flush
io.output_instruction_address := instruction_address.io.out
val regs_write_enable = Module(new PipelineRegister(1))
regs_write_enable.io.in := io.regs_write_enable
regs_write_enable.io.stall := stall
regs_write_enable.io.flush := io.flush
io.output_regs_write_enable := regs_write_enable.io.out
val regs_write_address = Module(new PipelineRegister(Parameters.PhysicalRegisterAddrBits))
regs_write_address.io.in := io.regs_write_address
regs_write_address.io.stall := stall
regs_write_address.io.flush := io.flush
io.output_regs_write_address := regs_write_address.io.out
val regs_write_source = Module(new PipelineRegister(2))
regs_write_source.io.in := io.regs_write_source
regs_write_source.io.stall := stall
regs_write_source.io.flush := io.flush
io.output_regs_write_source := regs_write_source.io.out
val reg1_data = Module(new PipelineRegister())
reg1_data.io.in := io.reg1_data
reg1_data.io.stall := stall
reg1_data.io.flush := io.flush
io.output_reg1_data := reg1_data.io.out
val reg2_data = Module(new PipelineRegister())
reg2_data.io.in := io.reg2_data
reg2_data.io.stall := stall
reg2_data.io.flush := io.flush
io.output_reg2_data := reg2_data.io.out
val immediate = Module(new PipelineRegister())
immediate.io.in := io.immediate
immediate.io.stall := stall
immediate.io.flush := io.flush
io.output_immediate := immediate.io.out
val aluop1_source = Module(new PipelineRegister(1))
aluop1_source.io.in := io.aluop1_source
aluop1_source.io.stall := stall
aluop1_source.io.flush := io.flush
io.output_aluop1_source := aluop1_source.io.out
val aluop2_source = Module(new PipelineRegister(1))
aluop2_source.io.in := io.aluop2_source
aluop2_source.io.stall := stall
aluop2_source.io.flush := io.flush
io.output_aluop2_source := aluop2_source.io.out
val csr_write_enable = Module(new PipelineRegister(1))
csr_write_enable.io.in := io.csr_write_enable
csr_write_enable.io.stall := stall
csr_write_enable.io.flush := io.flush
io.output_csr_write_enable := csr_write_enable.io.out
val csr_address = Module(new PipelineRegister(Parameters.CSRRegisterAddrBits))
csr_address.io.in := io.csr_address
csr_address.io.stall := stall
csr_address.io.flush := io.flush
io.output_csr_address := csr_address.io.out
val memory_read_enable = Module(new PipelineRegister(1))
memory_read_enable.io.in := io.memory_read_enable
memory_read_enable.io.stall := stall
memory_read_enable.io.flush := io.flush
io.output_memory_read_enable := memory_read_enable.io.out
val memory_write_enable = Module(new PipelineRegister(1))
memory_write_enable.io.in := io.memory_write_enable
memory_write_enable.io.stall := stall
memory_write_enable.io.flush := io.flush
io.output_memory_write_enable := memory_write_enable.io.out
val csr_read_data = Module(new PipelineRegister())
csr_read_data.io.in := io.csr_read_data
csr_read_data.io.stall := stall
csr_read_data.io.flush := io.flush
io.output_csr_read_data := csr_read_data.io.out
}

View File

@@ -0,0 +1,52 @@
// Copyright 2021 Howard Lau
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package riscv.core.threestage
import chisel3._
import riscv.Parameters
import riscv.core.PipelineRegister
class IF2ID extends Module {
val io = IO(new Bundle {
val flush = Input(Bool())
val instruction = Input(UInt(Parameters.InstructionWidth))
val instruction_address = Input(UInt(Parameters.AddrWidth))
val interrupt_flag = Input(UInt(Parameters.InterruptFlagWidth))
val output_instruction = Output(UInt(Parameters.DataWidth))
val output_instruction_address = Output(UInt(Parameters.AddrWidth))
val output_interrupt_flag = Output(UInt(Parameters.InterruptFlagWidth))
})
val stall = false.B
val instruction = Module(new PipelineRegister(defaultValue = InstructionsNop.nop))
instruction.io.in := io.instruction
instruction.io.stall := stall
instruction.io.flush := io.flush
io.output_instruction := instruction.io.out
val instruction_address = Module(new PipelineRegister(defaultValue = ProgramCounter.EntryAddress))
instruction_address.io.in := io.instruction_address
instruction_address.io.stall := stall
instruction_address.io.flush := io.flush
io.output_instruction_address := instruction_address.io.out
val interrupt_flag = Module(new PipelineRegister(Parameters.InterruptFlagBits))
interrupt_flag.io.in := io.interrupt_flag
interrupt_flag.io.stall := stall
interrupt_flag.io.flush := io.flush
io.output_interrupt_flag := interrupt_flag.io.out
}

View File

@@ -0,0 +1,207 @@
// Copyright 2021 Howard Lau
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package riscv.core.threestage
import chisel3._
import chisel3.util._
import riscv.Parameters
object InstructionTypes {
val L = "b0000011".U
val I = "b0010011".U
val S = "b0100011".U
val RM = "b0110011".U
val B = "b1100011".U
}
object Instructions {
val lui = "b0110111".U
val nop = "b0000001".U
val jal = "b1101111".U
val jalr = "b1100111".U
val auipc = "b0010111".U
val csr = "b1110011".U
val fence = "b0001111".U
}
object InstructionsTypeL {
val lb = "b000".U
val lh = "b001".U
val lw = "b010".U
val lbu = "b100".U
val lhu = "b101".U
}
object InstructionsTypeI {
val addi = 0.U
val slli = 1.U
val slti = 2.U
val sltiu = 3.U
val xori = 4.U
val sri = 5.U
val ori = 6.U
val andi = 7.U
}
object InstructionsTypeS {
val sb = "b000".U
val sh = "b001".U
val sw = "b010".U
}
object InstructionsTypeR {
val add_sub = 0.U
val sll = 1.U
val slt = 2.U
val sltu = 3.U
val xor = 4.U
val sr = 5.U
val or = 6.U
val and = 7.U
}
object InstructionsTypeM {
val mul = 0.U
val mulh = 1.U
val mulhsu = 2.U
val mulhum = 3.U
val div = 4.U
val divu = 5.U
val rem = 6.U
val remu = 7.U
}
object InstructionsTypeB {
val beq = "b000".U
val bne = "b001".U
val blt = "b100".U
val bge = "b101".U
val bltu = "b110".U
val bgeu = "b111".U
}
object InstructionsTypeCSR {
val csrrw = "b001".U
val csrrs = "b010".U
val csrrc = "b011".U
val csrrwi = "b101".U
val csrrsi = "b110".U
val csrrci = "b111".U
}
object InstructionsNop {
val nop = 0x00000013L.U(Parameters.DataWidth)
}
object InstructionsRet {
val mret = 0x30200073L.U(Parameters.DataWidth)
val ret = 0x00008067L.U(Parameters.DataWidth)
}
object InstructionsEnv {
val ecall = 0x00000073L.U(Parameters.DataWidth)
val ebreak = 0x00100073L.U(Parameters.DataWidth)
}
object ALUOp1Source {
val Register = 0.U(1.W)
val InstructionAddress = 1.U(1.W)
}
object ALUOp2Source {
val Register = 0.U(1.W)
val Immediate = 1.U(1.W)
}
object RegWriteSource {
val ALUResult = 0.U(2.W)
val Memory = 1.U(2.W)
val CSR = 2.U(2.W)
val NextInstructionAddress = 3.U(2.W)
}
class InstructionDecode extends Module {
val io = IO(new Bundle {
val instruction = Input(UInt(Parameters.InstructionWidth))
val regs_reg1_read_address = Output(UInt(Parameters.PhysicalRegisterAddrWidth))
val regs_reg2_read_address = Output(UInt(Parameters.PhysicalRegisterAddrWidth))
val ex_immediate = Output(UInt(Parameters.DataWidth))
val ex_aluop1_source = Output(Bool())
val ex_aluop2_source = Output(Bool())
val ex_memory_read_enable = Output(Bool())
val ex_memory_write_enable = Output(Bool())
val ex_reg_write_source = Output(UInt(2.W))
val ex_reg_write_enable = Output(Bool())
val ex_reg_write_address = Output(UInt(Parameters.PhysicalRegisterAddrWidth))
val ex_csr_address = Output(UInt(Parameters.CSRRegisterAddrWidth))
val ex_csr_write_enable = Output(Bool())
})
val opcode = io.instruction(6, 0)
val funct3 = io.instruction(14, 12)
val funct7 = io.instruction(31, 25)
val rd = io.instruction(11, 7)
val rs1 = io.instruction(19, 15)
val rs2 = io.instruction(24, 20)
io.regs_reg1_read_address := Mux(opcode === Instructions.lui, 0.U(Parameters.PhysicalRegisterAddrWidth), rs1)
io.regs_reg2_read_address := rs2
io.ex_immediate := MuxLookup(
opcode,
Cat(Fill(20, io.instruction(31)), io.instruction(31, 20)),
IndexedSeq(
InstructionTypes.I -> Cat(Fill(21, io.instruction(31)), io.instruction(30, 20)),
InstructionTypes.L -> Cat(Fill(21, io.instruction(31)), io.instruction(30, 20)),
Instructions.jalr -> Cat(Fill(21, io.instruction(31)), io.instruction(30, 20)),
InstructionTypes.S -> Cat(Fill(21, io.instruction(31)), io.instruction(30, 25), io.instruction(11, 7)),
InstructionTypes.B -> Cat(Fill(20, io.instruction(31)), io.instruction(7), io.instruction(30, 25), io.instruction(11, 8), 0.U(1.W)),
Instructions.lui -> Cat(io.instruction(31, 12), 0.U(12.W)),
Instructions.auipc -> Cat(io.instruction(31, 12), 0.U(12.W)),
Instructions.jal -> Cat(Fill(12, io.instruction(31)), io.instruction(19, 12), io.instruction(20), io.instruction(30, 21), 0.U(1.W))
)
)
io.ex_aluop1_source := Mux(
opcode === Instructions.auipc || opcode === InstructionTypes.B || opcode === Instructions.jal,
ALUOp1Source.InstructionAddress,
ALUOp1Source.Register
)
io.ex_aluop2_source := Mux(
opcode === InstructionTypes.RM,
ALUOp2Source.Register,
ALUOp2Source.Immediate
)
io.ex_memory_read_enable := opcode === InstructionTypes.L
io.ex_memory_write_enable := opcode === InstructionTypes.S
io.ex_reg_write_source := MuxLookup(
opcode,
RegWriteSource.ALUResult,
IndexedSeq(
InstructionTypes.L -> RegWriteSource.Memory,
Instructions.csr -> RegWriteSource.CSR,
Instructions.jal -> RegWriteSource.NextInstructionAddress,
Instructions.jalr -> RegWriteSource.NextInstructionAddress
)
)
io.ex_reg_write_enable := (opcode === InstructionTypes.RM) || (opcode === InstructionTypes.I) ||
(opcode === InstructionTypes.L) || (opcode === Instructions.auipc) || (opcode === Instructions.lui) ||
(opcode === Instructions.jal) || (opcode === Instructions.jalr) || (opcode === Instructions.csr)
io.ex_reg_write_address := rd
io.ex_csr_address := io.instruction(31, 20)
io.ex_csr_write_enable := (opcode === Instructions.csr) && (
funct3 === InstructionsTypeCSR.csrrw || funct3 === InstructionsTypeCSR.csrrwi ||
funct3 === InstructionsTypeCSR.csrrs || funct3 === InstructionsTypeCSR.csrrsi ||
funct3 === InstructionsTypeCSR.csrrc || funct3 === InstructionsTypeCSR.csrrci
)
}

View File

@@ -0,0 +1,47 @@
// Copyright 2021 Howard Lau
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package riscv.core.threestage
import chisel3._
import chisel3.util.MuxCase
import riscv.Parameters
object ProgramCounter {
val EntryAddress = Parameters.EntryAddress
}
class InstructionFetch extends Module {
val io = IO(new Bundle {
val jump_flag_ex = Input(Bool())
val jump_address_ex = Input(UInt(Parameters.AddrWidth))
val rom_instruction = Input(UInt(Parameters.DataWidth))
val instruction_valid = Input(Bool())
val instruction_address = Output(UInt(Parameters.AddrWidth))
val id_instruction = Output(UInt(Parameters.InstructionWidth))
})
val pc = RegInit(ProgramCounter.EntryAddress)
pc := MuxCase(
pc,
IndexedSeq(
io.jump_flag_ex -> io.jump_address_ex,
io.instruction_valid -> (pc + 4.U)
)
)
io.instruction_address := pc
io.id_instruction := Mux(io.instruction_valid, io.rom_instruction, InstructionsNop.nop)
}

Binary file not shown.

184
lab3/要求.txt Normal file
View File

@@ -0,0 +1,184 @@
实验三 流水线 CPU🚁
在完成单周期 CPU 实验后,你已经对 CPU 的原理和结构已经有了基本的了解。但单周期的 CPU 设计中,关键路径太长,频率难以提升,并且每个时钟周期只能执行一条指令,指令吞吐率低。下面,我们将尝试使多条指令重叠执行(即流水线技术)来解决这个问题。
竞争冒险的处理是流水线 CPU 设计的难点和关键所在。在下面的实验中,我们首先设计一个简单的三级流水线 CPUIF、ID 和 EX 三级),它只涉及分支和跳转指令带来的控制冒险,处理起来较为简单;然后,我们再将三级流水线 CPU 的 EX 级继续切分为 EX、MEM 和 WB形成经典的五级流水线这样做带来的数据冒险需要使用阻塞和转发技术进行处理最后我们将分支和跳转提前到 ID 阶段,进一步缩短分支延迟。
本实验中五级流水线 CPU 的架构与 Computer Organization and Design, RISC-V Edition 中的流水线 CPU 架构基本相同,读者可同时参考此书第 4.5~4.8 节。
在本实验中,你将学习到:
使用流水线设计缩短关键路径
正确处理流水线阻塞与清空
使用转发逻辑减少流水线阻塞
不管使用 IDE 还是执行命令,根目录是 lab3 文件夹。
流水线寄存器🚁
流水线寄存器是在流水线中起缓存作用的寄存器,目的是切分组合逻辑,缩短关键路径。它的基本功能非常简单,在每一个时钟周期,根据复位(流水线清空)或阻塞(流水线暂停)的状态,将寄存器内容清空、保持或设置为新的值。寄存器的输出则是寄存器中保存的值。为了方便复用,我们可以定义一个带参数的 PipelineRegister 模块,用来实现不同数据位宽的流水线寄存器。
实验代码已经在 src/main/scala/riscv/core/PipelineRegister.scala 中定义好了模块接口stall 和 flush 分别为流水线寄存器的阻塞和清空信号in 和 out 分别为要写入寄存器的值和寄存器的当前值。
实验任务: 实现流水线寄存器
请你修改 // Lab3(PipelineRegister) 注释处的代码,使其能通过 PipelineRegisterTest 测试。
提示
在完成此题时你可以暂时抛开 CPU只需要用实验零中的基础知识完成上述功能即可最低所需代码不超过 7 行,请将它当作一道简单的开胃菜享用吧!
三级流水线🚁
下面是三级流水线 CPU 的结构图,数据通路用蓝线表示,控制信号用红线表示。
three_stage_pipelined_CPU_structure
我们用 IF2ID 和 ID2EX 这两组流水线寄存器将单周期 CPU 的组合逻辑部分切分为三个阶段:
取指Instruction FetchIF根据 PC 中的指令地址从内存中取出指令码;
译码Instruction DecodeID将指令码解码为控制信号并从寄存器组中读取操作数
执行ExecuteEX包括 ALU 运算、访问内存和结果写回。
这三个阶段的代码与单周期 CPU 大同小异,所以我已经帮你写好啦,接下来让我们把主要精力放在处理竞争冒险上。
解决控制冒险:清空🚁
在三级流水线中,由于所有数据处理操作都在 EX 阶段进行,因此不存在数据冒险,我们只需要处理程序跳转带来的控制冒险。有三种情况可能发生程序跳转:
EX 段执行到跳转指令
EX 段执行到分支指令且分支条件成立
发生中断EX 段收到 CLINT 发来的 InterruptAssert 信号,这相当于在 EX 段的指令之上叠加了一条跳转指令EX 段的指令继续执行IF 段和 ID 段的指令将被丢弃
无论哪种情况,都是由 EX 段向 IF 段发送跳转信号 jump_flag 和跳转的目标地址 jump_address但在 jump_address 写入 PC 并从该处取出指令前,流水线的 IF 和 ID 段已经各有两条不需要执行的指令,好在这两条指令的结果还没有写回,我们只需要清空对应的流水线寄存器,把它们变成两条空指令即可。
实验任务:支持三级流水线的控制冒险
我们用一个控制单元来检测控制冒险并清空流水线,模块定义在 src/main/scala/riscv/core/threestage/Control.scala为了避免此题过于简单我们没有提供模块接口请根据以上分析确定模块的输入输出在 // Lab3(Flush) 处将代码补充完整,并在 src/main/scala/riscv/core/threestage/CPU.scala 的 // Lab3(Flush) 处补充相关连线,使其能够通过 ThreeStageCPUTest 测试。
五级流水线🚁
在三级流水线中,执行阶段逻辑复杂,仍然可能导致较大的延迟。为了进一步缩短关键路径,我们可以扩展流水线级数,将执行阶段进一步分为 ALU 阶段、访存阶段以及写回阶段,如下图所示。
five_stage_pipelined_CPU_structure
把三级流水线进一步分割为五级流水线将带来更加复杂的数据冒险,下面我们将尝试使用阻塞的方式解决数据冒险,得到一个功能完整的五级流水线 CPU。接着我们可以使用旁路和将分支跳转提前到 ID 阶段进一步提升 CPU 效率,这两部分将作为拓展实验供同学们选做。注意,上面的 CPU 结构图是我们完成所有实验之后的结果,在完成“缩短分支延迟”实验之前,我们 CPU 的结构将与上图稍有不同。例如,我们紧接着讨论的五级流水线 CPU 在 EX 阶段判断程序是否发生跳转,而不是 ID 阶段。
解决数据冒险:阻塞🚁
当处于 ID 阶段的指令要读取的寄存器依赖于 EX 或 MEM 阶段的指令时,发生数据冒险。此时,我们可以保持 IF 和 ID 两个阶段状态不变,直到被依赖的指令执行完成,即 ID 段能够从寄存器组获得它所需要的数据,再继续执行。让我们考虑如下指令序列,并思考几个问题:
0000: add x1, x0, x0
0004: sub x2, x0, x1
0008: and x3, x1, x2
000C: jalr x4, x1, 0
0010: or x5, x3, x4
0014: xor x6, x4, x5
假设没有阻塞,它们在流水线中的状态如下:
时钟周期 0 1 2 3 4 5 6
IF add sub and jalr or xor
ID add sub and jalr or xor
EX add sub and jalr or
MEM add sub and jalr
WB add sub and
在第 2 个时钟周期,指令 sub x2, x0, x1 处于 ID 阶段,需要从寄存器组读出它的源操作数,但它的源操作数依赖于前一条指令,且前一条指令的结果还没有写回,需要阻塞;同理,在第 3 个时钟周期,指令 and x3, x1, x2 需要读取的源操作数依赖于前两条指令,也需要阻塞;但是这两条指令分别需要阻塞多少个时钟周期?
在第 4 个时钟周期,指令 jalr x4, x1, 0 处于 ID 阶段,它的源操作数依赖于处于 WB 阶段的指令的结果,此时需不需要阻塞?
最后两条指令的源操作数同样依赖于前面的指令,这两条指令需不需要阻塞?
请你先思考片刻,下面我将给出阻塞后的流水线状态并作分析:
时钟周期 0 1 2 3 4 5 6 7 8 9 10
IF add sub and and and jalr jalr jalr or xor add
ID add sub sub sub and and and jalr or nop
EX add nop nop sub nop nop and jalr nop
MEM add nop nop sub nop nop and jalr
WB add nop nop sub nop nop and
位于 ID 阶段的指令和位于 WB 阶段的指令之间不会发生数据冒险,这是因为我们的寄存器组模拟实现了 Double Pumping 功能,即 WB 阶段在前半个时钟周期向寄存器组写入数据ID 阶段在后半个时钟周期从寄存器组读出数据,请你打开 src/main/scala/riscv/core/RegisterFile 查看相关代码。因此,在上面的例子中,对 sub 和 and 指令的阻塞只需持续到它们依赖的指令进入 WB 阶段即可,而 jalr 指令无需阻塞。
值得注意的是,我们在阻塞 PC 和 IF2ID 寄存器以保持 IF 和 ID 阶段不变的同时,需要清空 ID2EX 寄存器以在 EX 阶段插入空指令(“气泡”),否则 ID 阶段的指令还是会进入 EX 阶段,这样就不是“阻塞”,而变成“重复”了。
另外jalr 是跳转指令,虽然它后面两条指令依赖于它写入的寄存器,但是它们本就不应该紧接着被执行,而是应该被清空,所以在第 10 个时钟周期应该清空 IF2ID 和 ID2EX 寄存器,而不是阻塞。(上表中加粗的 nop 表示因清空信号而插入的空指令,未加粗的 nop 表示从上一个流水段进入下一个流水段的空指令。)
特别提示:除了以上讨论的情况之外,寄存器 x0 在 risc-v 中具有特殊作用,以它为目标寄存器的指令的结果将被丢弃、以它为输入寄存器的指令也不会在相应输入寄存器结果上产生依赖。 也就是说,目标或输入寄存器为 x0 时,该寄存器的结果无需阻塞。
借用这一特点,我们对判断依赖和产生阻塞时约定:一条指令至多涉及两个输入寄存器 rs1 和 rs2 ,以及一个目标寄存器 rd 。若某一格式的指令不包含这些寄存器的某一个时,如 I-type 指令不含 rs2由 Decoder 输出相应寄存器地址为 0即 x0 。该约定不会影响指令的正确执行,其由 ALUOpSource 选择正确的输入,同时也不会使流水线控制错误产生阻塞。
实验任务:用阻塞解决数据冒险
我们在 src/main/scala/riscv/core/fivestage_stall/InstructionDecode.scala 中留下了相应接口,请根据上述约定使 ID 输出合适的寄存器地址,以供后续冒险分析。完成后应能通过 DecoderStallTest 测试。
我们用一个控制单元来检测并解决控制冒险和数据冒险,模块接口已经定义在 src/main/scala/riscv/core/fivestage_stall/Control.scala请根据以上分析修改 // Lab3(ThreeStage) 处的代码,使其能够通过 FiveStageCPUStallTest 测试。
拓展:使用旁路减少阻塞🚁
至此,我们已经解决完所有竞争冒险,做出一个功能基本完整的五级流水线 CPU 了!但是,对同一个寄存器的连续操作在程序中是非常常见的,如果只使用阻塞来解决数据冒险,将产生大量“气泡”,降低执行效率。
实际上,我们可以直接从流水线寄存器中直接获得指令的执行结果,并不需要阻塞直到前面的指令把结果写入寄存器组后再从寄存器组读取,即在 EX 段和 EX2MEM、MEM2WB 这两组流水线寄存器之间建立“旁路”,让 EX 段可以直接获取前面指令的执行结果。我们考虑如下指令序列:
0000: addi x1, x0, 1
0004: sub x2, x0, x1
0008: and x2, x1, x2
000C: lw x2, 4(x2)
0010: or x3, x1, x2
下表是建立了旁路后的流水线状态,我们增加了 EX2MEM 和 MEM2WB 两行用来表示暂存在流水线寄存器中的执行结果。
时钟周期 0 1 2 3 4 5 6 7
IF addi sub and lw or
ID addi sub and lw or
EX addi sub and lw or or
EX2MEM addi: x1 sub: x2 and: x2
MEM addi sub and lw nop
MEM2WB addi: x1 sub: x2 and: x2 lw: x2
WB addi sub and lw
虽然第二条指令 sub x2, x0, x1 依赖于前一条指令的执行结果,但是在第 3 个时钟周期,位于 EX 段的 sub 指令可以直接从 EX2MEM 寄存器中获取第一条指令执行后 x1 寄存器的值作为它的源操作数而无需阻塞;同样地,第三条指令依赖于前两条指令,在第 4 个时钟周期,位于 EX 段的 and 指令可以从 EX2MEM 和 MEM2WB 中获取它的源操作数。值得注意的是,在第 5 个时钟周期EX2MEM 和 MEM2WB 中都存有 x2 的值,此时我们应该取“最新”的那个,也就是位于 EX2MEM 中的值。在第 6 个时钟周期,情况有些不同,因为第四条指令是 load 指令,它需要等到 MEM 阶段结束才能得到结果,因此处于 EX 段的 or 指令无法从 EX2MEM 中获取它的源操作数,必须阻塞一个时钟周期后从 MEM2WB 中获取。
实验任务:用旁路减少阻塞
InstructionDecode中的实现与五级阻塞流水线CPU基本一致你可以复制前面填好的内容。
我们用一个控制单元来处理流水线的阻塞和清空,模块接口已经定义在 src/main/scala/riscv/core/fivestage_forward/Control.scala再用一个旁路单元来检测数据冒险并发出旁路控制信号模块接口已经定义在 src/main/scala/riscv/core/fivestage_forward/Forwarding.scala另外还需要在执行单元src/main/scala/riscv/core/fivestage_forward/Execute.scala中根据旁路单元的控制信号使用对应的旁路数据。请你根据以上分析修改上述三个模块中 // Lab3(Forward) 处的代码,使其能够通过 FiveStageCPUForward 测试。
提示:你可以复用上道题中 Control 模块的部分代码;如果对模块接口中某个信号的功能有疑惑,可参考上面的 CPU 结构图,或做出你认为合理的修改并简要记录在报告中。
拓展:缩短分支延迟🚁
我们已经利用旁路将数据冒险带来的损失降低到至多一个时钟周期了,但你也许对与目前流水线的效率还是不太满意,因为每次跳转都需要浪费两个时钟周期。接下来,我们往 CPU 中加入少量硬件,将分支/跳转指令的执行从 EX 段提前到 ID 段,进而把程序跳转的损失减少到一个时钟周期。
首先,我们需要把跳转的判断从 EX 段移到 ID 段;其次,跳转目标地址的计算是使用 EX 段的 ALU 进行的,因此我们需要给 ID 段增加一个加法器来计算目标地址;最后,我们需要添加额外的旁路逻辑,将前面指令的执行结果旁路到 ID 段给分支或跳转指令使用,如果所依赖的结果还没有产生,还需要进行阻塞。作为本实验的最后一题,我不再为你提供数据冒险的指令序列样例,请你自行考虑其他所有类型的指令与分支或跳转指令的搭配,找出其中的竞争冒险并进行解决。
实验任务:缩短分支延迟
我们已经为你定义好相关模块的接口并移除了 Execute 模块中分支和跳转的相关代码,请你修改 src/main/scala/riscv/core/fivestage_final 下的 InstructionDecode.scala、Control.scala 和 Forwarding.scala 中 // Lab3(Final) 处的代码,使其能够通过 FiveStageCPUFinal 测试。你可以复制上道题中相应代码并作修改。
提示:如何有效 debug🚁
五级流水线 CPU 已经较为复杂,实现过程中的小错误都有可能导致结果出错。本小节为您提供如何有效 debug 的提示。
debug 时应当从小和简单的内容开始逐步排除再考虑更复杂的内容。基础的检查包括检查是否有文件还没有填写实现代码如ID的代码未从前一步复制过来、检查是否有拼写错误或错用名字相近的信号线 等。
排除这些后,再考虑更复杂的可能性,其中重要的一步是 用最少的内容复现错误,这样可以排除更多的无关变量。在五级流水线三个小实验的 CPUTest 中,最简单的测试是 “store and load single byte” 测试,其代码在 csrc/sb.S仅十几条汇编指令。如果您的 CPU 在不能通过该测试,您可以通过这些方法排查错误:
打印🚁
在一些关键部件处进行打印,如
class RegisterFile extends Module {
// ......
// DEBUG
val debug_clk_cnt = RegInit(0.U(32.W))
debug_clk_cnt := debug_clk_cnt + 1.U
when (io.write_enable) {
printf(cf"[Reg] at clock ${debug_clk_cnt} write to register ${io.write_address}: 0x${io.write_data}%x\n\n")
}
}
其在寄存器写使能有效时,打印所写入的值。 其中 printf 为 Chisel3 提供的打印函数, cf 为字符串插值器,可以在字符串里直观地打印,详见 Printing in Chisel。
该方法需要找到某个容易看出错误根源的部件如寄存器、MEM 等。
TODO查看输出截图
查看波形图🚁
可以用 GTKWave 查看波形图并进行分析。TODOGTKWave 教程。
充分利用提示🚁
一般而言,本实验设计上不要求非常深入、复杂的 debug因而会在部件的输入信号上予以提示即不会引入多余的输入信号。您可以了解这些输入信号的含义并查看其是否被您的代码利用上。
实验没有提供 Control 或 Forwarding 的单元测试,这是因为它们逻辑并不复杂,提供单元测试相当于给出了答案。为找出错误,可以按照如下框架进行思考:首先,五级流水线中,当前指令处于 EX 时,至多依赖于前两条指令,前面第三条指令已经完成并退出流水线; 同时依赖于前两条指令时,两条指令的目标寄存器是否相同 。其次,被依赖的指令可能在 EX 阶段得到其结果,也可能在 MEM 阶段得到。综合这两个维度,您应考虑其组合下的所有情况:
考虑情况表
上表适用于 Stall 和 Forwarding 两个小实验,对于缩短分支延迟,由于又在 ID 阶段引入了依赖,上表还需翻倍,分别对当前时钟周期下 ID 和 EX 阶段的指令进行讨论。
烧板验证🚁
如果你已经完成了所有基础实验,那么你的 CPU 应该能够运行简单的程序,你可以通过 Top 模块中传入 CPU 模块构造函数的参数来选择你要使用的 CPU 版本:如果你只完成了基础实验,请选择 ImplementationType.FiveStageStall如果你完成了第一个拓展实验请选择 ImplementationType.FiveStageForward如果你完成了第二个拓展实验请选择 ImplementationType.FiveStageFinal。
实验报告🚁
简述您已实现的最复杂 CPU 的关键思路,包括 Contrl 和 Forwarding如果有的控制逻辑。
实验中没有特别讨论 CSR 指令的控制和数据冒险,请您简要分析 CSR 指令是否会产生上述冒险,并是否需要特别的处理措施。
说明您在完成实验的过程中,遇到的实验指导不足或改进建议。