commit 07c0b18e4c2ed48b41496f98637600c7126d23be Author: Launchcore Date: Tue Jun 17 00:35:40 2025 +0800 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f880961 --- /dev/null +++ b/.gitignore @@ -0,0 +1,216 @@ +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +### Python Patch ### +# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration +poetry.toml + +# ruff +.ruff_cache/ + +# LSP config files +pyrightconfig.json + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history +.ionide + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk diff --git a/Hardware/.gitignore b/Hardware/.gitignore new file mode 100644 index 0000000..64a5d42 --- /dev/null +++ b/Hardware/.gitignore @@ -0,0 +1,66 @@ +### Vivado ### +######################################################################################################### +## This is an example .gitignore file for Vivado, please treat it as an example as +## it might not be complete. In addition, XAPP 1165 should be followed. +######### +#Exclude all +* +!*/ +!.gitignore +########################################################################### +## VIVADO +#Source files: +#Do NOT ignore VHDL, Verilog, block diagrams or EDIF files. +!*.vhd +!*.v +!*.sv +!*.bd +!*.edif +#IP files +#.xci: synthesis and implemented not possible - you need to return back to the previous version to generate output products +#.xci + .dcp: implementation possible but not re-synthesis +#*.xci(www.spiritconsortium.org) +!*.xci +#.xcix: Core container file +#.xcix: https://www.xilinx.com/support/documentation/sw_manuals/xilinx2016_2/ug896-vivado-ip.pdf (Page 41) +!*.xcix +#*.dcp(checkpoint files) +!*.dcp +!*.vds +!*.pb +#All bd comments and layout coordinates are stored within .ui +!*.ui +!*.ooc +#System Generator +!*.mdl +!*.slx +!*.bxml +#Simulation logic analyzer +!*.wcfg +!*.coe +#MIG +!*.prj +!*.mem +#Project files +#XPR + *.XML ? XPR (Files are merged into a single XPR file for 2014.1 version) +#Do NOT ignore *.xpr files +!*.xpr +#Include *.xml files for 2013.4 or earlier version +!*.xml +#Constraint files +#Do NOT ignore *.xdc files +!*.xdc +#TCL - files +!*.tcl +#Journal - files +!*.jou +#Reports +!*.rpt +!*.txt +!*.vdi +#C-files +!*.c +!*.h +!*.elf +!*.bmm +!*.xmp diff --git a/Hardware/LA32R.cache/wt/webtalk_pa.xml b/Hardware/LA32R.cache/wt/webtalk_pa.xml new file mode 100644 index 0000000..5053921 --- /dev/null +++ b/Hardware/LA32R.cache/wt/webtalk_pa.xml @@ -0,0 +1,60 @@ + + + + +
+ + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
diff --git a/Hardware/LA32R.ip_user_files/README.txt b/Hardware/LA32R.ip_user_files/README.txt new file mode 100644 index 0000000..023052c --- /dev/null +++ b/Hardware/LA32R.ip_user_files/README.txt @@ -0,0 +1 @@ +The files in this directory structure are automatically generated and managed by Vivado. Editing these files is not recommended. diff --git a/Hardware/LA32R.sim/sim_1/behav/xsim/cpu_tb.tcl b/Hardware/LA32R.sim/sim_1/behav/xsim/cpu_tb.tcl new file mode 100644 index 0000000..1094e45 --- /dev/null +++ b/Hardware/LA32R.sim/sim_1/behav/xsim/cpu_tb.tcl @@ -0,0 +1,11 @@ +set curr_wave [current_wave_config] +if { [string length $curr_wave] == 0 } { + if { [llength [get_objects]] > 0} { + add_wave / + set_property needs_save false [current_wave_config] + } else { + send_msg_id Add_Wave-1 WARNING "No top level signals found. Simulator will start without a wave window. If you want to open a wave window go to 'File->New Waveform Configuration' or type 'create_wave_config' in the TCL console." + } +} + +run 1000ns diff --git a/Hardware/LA32R.sim/sim_1/behav/xsim/cpu_tb_vlog.prj b/Hardware/LA32R.sim/sim_1/behav/xsim/cpu_tb_vlog.prj new file mode 100644 index 0000000..ff790c7 --- /dev/null +++ b/Hardware/LA32R.sim/sim_1/behav/xsim/cpu_tb_vlog.prj @@ -0,0 +1,17 @@ +# compile verilog/system verilog design source files +verilog xil_defaultlib \ +"../../../../LA32R.srcs/sources_1/new/alu.v" \ +"../../../../LA32R.srcs/sources_1/new/control_unit.v" \ +"../../../../LA32R.srcs/sources_1/new/cpu_top.v" \ +"../../../../LA32R.srcs/sources_1/new/data_memory.v" \ +"../../../../LA32R.srcs/sources_1/new/imm_extender.v" \ +"../../../../LA32R.srcs/sources_1/new/instruction_memory.v" \ +"../../../../LA32R.srcs/sources_1/new/pc.v" \ +"../../../../LA32R.srcs/sources_1/new/register_file.v" \ +"../../../../LA32R.srcs/sim_1/new/cpu_tb.v" \ + +# compile glbl module +verilog xil_defaultlib "glbl.v" + +# Do not sort compile order +nosort diff --git a/Hardware/LA32R.sim/sim_1/behav/xsim/glbl.v b/Hardware/LA32R.sim/sim_1/behav/xsim/glbl.v new file mode 100644 index 0000000..be64233 --- /dev/null +++ b/Hardware/LA32R.sim/sim_1/behav/xsim/glbl.v @@ -0,0 +1,71 @@ +// $Header: /devl/xcs/repo/env/Databases/CAEInterfaces/verunilibs/data/glbl.v,v 1.14 2010/10/28 20:44:00 fphillip Exp $ +`ifndef GLBL +`define GLBL +`timescale 1 ps / 1 ps + +module glbl (); + + parameter ROC_WIDTH = 100000; + parameter TOC_WIDTH = 0; + +//-------- STARTUP Globals -------------- + wire GSR; + wire GTS; + wire GWE; + wire PRLD; + tri1 p_up_tmp; + tri (weak1, strong0) PLL_LOCKG = p_up_tmp; + + wire PROGB_GLBL; + wire CCLKO_GLBL; + wire FCSBO_GLBL; + wire [3:0] DO_GLBL; + wire [3:0] DI_GLBL; + + reg GSR_int; + reg GTS_int; + reg PRLD_int; + +//-------- JTAG Globals -------------- + wire JTAG_TDO_GLBL; + wire JTAG_TCK_GLBL; + wire JTAG_TDI_GLBL; + wire JTAG_TMS_GLBL; + wire JTAG_TRST_GLBL; + + reg JTAG_CAPTURE_GLBL; + reg JTAG_RESET_GLBL; + reg JTAG_SHIFT_GLBL; + reg JTAG_UPDATE_GLBL; + reg JTAG_RUNTEST_GLBL; + + reg JTAG_SEL1_GLBL = 0; + reg JTAG_SEL2_GLBL = 0 ; + reg JTAG_SEL3_GLBL = 0; + reg JTAG_SEL4_GLBL = 0; + + reg JTAG_USER_TDO1_GLBL = 1'bz; + reg JTAG_USER_TDO2_GLBL = 1'bz; + reg JTAG_USER_TDO3_GLBL = 1'bz; + reg JTAG_USER_TDO4_GLBL = 1'bz; + + assign (strong1, weak0) GSR = GSR_int; + assign (strong1, weak0) GTS = GTS_int; + assign (weak1, weak0) PRLD = PRLD_int; + + initial begin + GSR_int = 1'b1; + PRLD_int = 1'b1; + #(ROC_WIDTH) + GSR_int = 1'b0; + PRLD_int = 1'b0; + end + + initial begin + GTS_int = 1'b1; + #(TOC_WIDTH) + GTS_int = 1'b0; + end + +endmodule +`endif diff --git a/Hardware/LA32R.sim/sim_1/behav/xsim/webtalk.jou b/Hardware/LA32R.sim/sim_1/behav/xsim/webtalk.jou new file mode 100644 index 0000000..2507de1 --- /dev/null +++ b/Hardware/LA32R.sim/sim_1/behav/xsim/webtalk.jou @@ -0,0 +1,12 @@ +#----------------------------------------------------------- +# Webtalk v2018.1 (64-bit) +# SW Build 2188600 on Wed Apr 4 18:40:38 MDT 2018 +# IP Build 2185939 on Wed Apr 4 20:55:05 MDT 2018 +# Start of session at: Tue Jun 17 00:19:34 2025 +# Process ID: 36764 +# Current directory: D:/Schoolwork/ComputerComposition/LA32R/Hardware/LA32R.sim/sim_1/behav/xsim +# Command line: wbtcv.exe -mode batch -source D:/Schoolwork/ComputerComposition/LA32R/Hardware/LA32R.sim/sim_1/behav/xsim/xsim.dir/cpu_tb_snapshot/webtalk/xsim_webtalk.tcl -notrace +# Log file: D:/Schoolwork/ComputerComposition/LA32R/Hardware/LA32R.sim/sim_1/behav/xsim/webtalk.log +# Journal file: D:/Schoolwork/ComputerComposition/LA32R/Hardware/LA32R.sim/sim_1/behav/xsim\webtalk.jou +#----------------------------------------------------------- +source D:/Schoolwork/ComputerComposition/LA32R/Hardware/LA32R.sim/sim_1/behav/xsim/xsim.dir/cpu_tb_snapshot/webtalk/xsim_webtalk.tcl -notrace diff --git a/Hardware/LA32R.sim/sim_1/behav/xsim/webtalk_16180.backup.jou b/Hardware/LA32R.sim/sim_1/behav/xsim/webtalk_16180.backup.jou new file mode 100644 index 0000000..885483f --- /dev/null +++ b/Hardware/LA32R.sim/sim_1/behav/xsim/webtalk_16180.backup.jou @@ -0,0 +1,12 @@ +#----------------------------------------------------------- +# Webtalk v2018.1 (64-bit) +# SW Build 2188600 on Wed Apr 4 18:40:38 MDT 2018 +# IP Build 2185939 on Wed Apr 4 20:55:05 MDT 2018 +# Start of session at: Tue Jun 17 00:19:31 2025 +# Process ID: 16180 +# Current directory: D:/Schoolwork/ComputerComposition/LA32R/Hardware/LA32R.sim/sim_1/behav/xsim +# Command line: wbtcv.exe -mode batch -source D:/Schoolwork/ComputerComposition/LA32R/Hardware/LA32R.sim/sim_1/behav/xsim/xsim.dir/cpu_tb_snapshot/webtalk/xsim_webtalk.tcl -notrace +# Log file: D:/Schoolwork/ComputerComposition/LA32R/Hardware/LA32R.sim/sim_1/behav/xsim/webtalk.log +# Journal file: D:/Schoolwork/ComputerComposition/LA32R/Hardware/LA32R.sim/sim_1/behav/xsim\webtalk.jou +#----------------------------------------------------------- +source D:/Schoolwork/ComputerComposition/LA32R/Hardware/LA32R.sim/sim_1/behav/xsim/xsim.dir/cpu_tb_snapshot/webtalk/xsim_webtalk.tcl -notrace diff --git a/Hardware/LA32R.sim/sim_1/behav/xsim/xelab.pb b/Hardware/LA32R.sim/sim_1/behav/xsim/xelab.pb new file mode 100644 index 0000000..48159e5 Binary files /dev/null and b/Hardware/LA32R.sim/sim_1/behav/xsim/xelab.pb differ diff --git a/Hardware/LA32R.sim/sim_1/behav/xsim/xsim.dir/cpu_tb_snapshot/Compile_Options.txt b/Hardware/LA32R.sim/sim_1/behav/xsim/xsim.dir/cpu_tb_snapshot/Compile_Options.txt new file mode 100644 index 0000000..b46ddf1 --- /dev/null +++ b/Hardware/LA32R.sim/sim_1/behav/xsim/xsim.dir/cpu_tb_snapshot/Compile_Options.txt @@ -0,0 +1 @@ +--debug "typical" --snapshot "cpu_tb_snapshot" "xil_defaultlib.cpu_tb" -log "elaborate.log" diff --git a/Hardware/LA32R.sim/sim_1/behav/xsim/xsim.dir/cpu_tb_snapshot/TempBreakPointFile.txt b/Hardware/LA32R.sim/sim_1/behav/xsim/xsim.dir/cpu_tb_snapshot/TempBreakPointFile.txt new file mode 100644 index 0000000..fdbc612 --- /dev/null +++ b/Hardware/LA32R.sim/sim_1/behav/xsim/xsim.dir/cpu_tb_snapshot/TempBreakPointFile.txt @@ -0,0 +1 @@ +Breakpoint File Version 1.0 diff --git a/Hardware/LA32R.sim/sim_1/behav/xsim/xsim.dir/cpu_tb_snapshot/xsim.mem b/Hardware/LA32R.sim/sim_1/behav/xsim/xsim.dir/cpu_tb_snapshot/xsim.mem new file mode 100644 index 0000000..769a9b4 Binary files /dev/null and b/Hardware/LA32R.sim/sim_1/behav/xsim/xsim.dir/cpu_tb_snapshot/xsim.mem differ diff --git a/Hardware/LA32R.sim/sim_1/behav/xsim/xsim.dir/cpu_tb_snapshot/xsim_script.tcl b/Hardware/LA32R.sim/sim_1/behav/xsim/xsim.dir/cpu_tb_snapshot/xsim_script.tcl new file mode 100644 index 0000000..7295638 --- /dev/null +++ b/Hardware/LA32R.sim/sim_1/behav/xsim/xsim.dir/cpu_tb_snapshot/xsim_script.tcl @@ -0,0 +1 @@ +xsim {cpu_tb_snapshot} -autoloadwcfg -runall diff --git a/Hardware/LA32R.sim/sim_1/behav/xsim/xsim.jou b/Hardware/LA32R.sim/sim_1/behav/xsim/xsim.jou new file mode 100644 index 0000000..215a6fd --- /dev/null +++ b/Hardware/LA32R.sim/sim_1/behav/xsim/xsim.jou @@ -0,0 +1,13 @@ +#----------------------------------------------------------- +# xsim v2018.1 (64-bit) +# SW Build 2188600 on Wed Apr 4 18:40:38 MDT 2018 +# IP Build 2185939 on Wed Apr 4 20:55:05 MDT 2018 +# Start of session at: Tue Jun 17 00:19:32 2025 +# Process ID: 35112 +# Current directory: D:/Schoolwork/ComputerComposition/LA32R/Hardware/LA32R.sim/sim_1/behav/xsim +# Command line: xsim.exe -log ..\..\..\..\..\simulation.log -mode tcl -source {xsim.dir/cpu_tb_snapshot/xsim_script.tcl} +# Log file: D:/Schoolwork/ComputerComposition/LA32R/Hardware/LA32R.sim/sim_1/behav/xsim/../../../../../simulation.log +# Journal file: D:/Schoolwork/ComputerComposition/LA32R/Hardware/LA32R.sim/sim_1/behav/xsim\xsim.jou +#----------------------------------------------------------- +source xsim.dir/cpu_tb_snapshot/xsim_script.tcl +run -all diff --git a/Hardware/LA32R.sim/sim_1/behav/xsim/xvlog.pb b/Hardware/LA32R.sim/sim_1/behav/xsim/xvlog.pb new file mode 100644 index 0000000..44e8f36 Binary files /dev/null and b/Hardware/LA32R.sim/sim_1/behav/xsim/xvlog.pb differ diff --git a/Hardware/LA32R.srcs/sim_1/new/cpu_tb.v b/Hardware/LA32R.srcs/sim_1/new/cpu_tb.v new file mode 100644 index 0000000..ad859da --- /dev/null +++ b/Hardware/LA32R.srcs/sim_1/new/cpu_tb.v @@ -0,0 +1,101 @@ +`timescale 1ns / 1ps + +/******************************************************************************* +** Company: Nantong University +** Engineer: あやせももこ +** +** Create Date: 2025-06-16 +** Design Name: LA32R Single Cycle CPU Testbench +** Module Name: cpu_tb +** Project Name: Computer Architecture Course Design +** Target Devices: Any +** Tool Versions: Vivado 2018.1 +** Description: +** A self-checking testbench for the LA32R single-cycle CPU. +** - Generates clock and reset signals. +** - Instantiates the cpu_top module. +** - The instruction_memory module (instantiated within cpu_top) will load +** the machine code from "program.hex". +** - Monitors and displays the state of the PC and register file. +** +** Revision: +** Revision 0.01 - File Created +** Additional Comments: +** - Place this file and "program.hex" in the simulation directory. +** +*******************************************************************************/ + +`timescale 1ns / 1ps + +module cpu_tb; + + // --- 信号声明 (Signal Declarations) --- + reg clk; + reg rst; + + // --- 实例化待测设计 (Instantiate the Design Under Test) --- + cpu_top uut ( + .clk(clk), + .rst(rst) + ); + + // --- 时钟生成器 (Clock Generator) --- + localparam CLK_PERIOD = 10; // 时钟周期为10ns (Clock period is 10ns) + initial begin + clk = 0; + forever #(CLK_PERIOD / 2) clk = ~clk; + end + + // --- 仿真控制 (Simulation Control) --- + initial begin + // 1. 复位CPU (Reset the CPU) + rst = 1; + #(CLK_PERIOD * 2); // 保持复位2个周期 (Hold reset for 2 cycles) + rst = 0; + $display("------------------------------------------------------------"); + $display(" CPU Simulation Started. Reset is released. "); + $display("------------------------------------------------------------"); + + // 2. 运行一段时间后停止仿真 + // Stop the simulation after a certain amount of time. + // The test program has an infinite loop at the end, + // so we need to manually stop the simulation. + #500; // 运行500ns (Run for 500ns) + + // 3. 打印最终的寄存器状态 + // Print the final state of the registers + $display("\n------------------------------------------------------------"); + $display(" Simulation Finished. Final Register State: "); + $display("------------------------------------------------------------"); + // 使用$display来显示寄存器的值。注意路径需要正确。 + // Use $display to show register values. Note the path must be correct. + for (integer i = 0; i < 32; i = i + 1) begin + // 检查寄存器值是否非零,以简化输出 + // Check if register value is non-zero to simplify output + if (uut.u_reg_file.registers[i] != 32'h00000000) begin + $display("Register R%0d: 0x%08h", i, uut.u_reg_file.registers[i]); + end + end + $display("※Please note that registers with value zero are hidden.※"); + $display("------------------------------------------------------------"); + + $finish; // 结束仿真 (End simulation) + end + + // --- 监控和显示 (Monitoring and Display) --- + // 在每个时钟周期的下降沿打印信息,确保所有信号稳定 + // Display info at the falling edge of the clock to ensure all signals are stable. + always @(negedge clk) begin + if (!rst) begin + $display("Time: %0t ns | PC: 0x%08h | Instruction: 0x%08h | R4=0x%h R5=0x%h R6=0x%h", + $time, + uut.pc_out, + uut.instr, + uut.u_reg_file.registers[4], + uut.u_reg_file.registers[5], + uut.u_reg_file.registers[6] + ); + end + end + +endmodule diff --git a/Hardware/LA32R.srcs/sources_1/new/alu.v b/Hardware/LA32R.srcs/sources_1/new/alu.v new file mode 100644 index 0000000..6b8c561 --- /dev/null +++ b/Hardware/LA32R.srcs/sources_1/new/alu.v @@ -0,0 +1,80 @@ +`timescale 1ns / 1ps + +/******************************************************************************* +** Company: Nantong University +** Engineer: あやせももこ +** +** Create Date: 2025-06-16 +** Design Name: LA32R Single Cycle CPU +** Module Name: alu +** Project Name: Computer Architecture Course Design +** Target Devices: Any FPGA +** Tool Versions: Vivado 2018.1 +** Description: +** This module implements the Arithmetic Logic Unit (ALU) for the LA32R CPU. +** +** [FIXED] The implementation of SLT now uses the $signed() system function +** to correctly handle comparisons that could cause overflow, such as +** comparing a positive and a negative number. +** +** Revision: +** Revision 0.02 - Corrected SLT implementation using $signed(). +** Revision 0.01 - File Created +** +*******************************************************************************/ + +module alu ( + input wire [31:0] a, // 操作数 A (Operand A) + input wire [31:0] b, // 操作数 B (Operand B) + input wire [3:0] alu_op, // ALU 操作控制码 (ALU Operation Control Code) + output reg [31:0] result, // 运算结果 (Result) + output wire zero, // 零标志位 (Zero Flag) + output wire lt // 小于标志位 (Less Than Flag for BLT) +); + + // 定义ALU操作码的参数,增强可读性 + // Define parameters for ALU operations to enhance readability + localparam ALU_ADD = 4'b0000; + localparam ALU_SUB = 4'b0001; + localparam ALU_AND = 4'b0010; + localparam ALU_OR = 4'b0011; + localparam ALU_NOR = 4'b0100; + localparam ALU_SLT = 4'b0101; + localparam ALU_SLTU = 4'b0110; + + // 减法结果的临时线网 + // Temporary wire for subtraction result + wire [31:0] sub_result = a - b; + + // [FIX] 使用$signed()进行稳健的有符号比较 + // [FIX] Use $signed() for robust signed comparison + wire slt_res = ($signed(a) < $signed(b)); + + // 无符号比较 + // Unsigned comparison + wire sltu_res = (a < b); + + // 主组合逻辑: 根据alu_op计算结果 + // Main combinational logic: calculate result based on alu_op + always @(*) begin + case (alu_op) + ALU_ADD: result = a + b; // 加法 (Addition) + ALU_SUB: result = sub_result; // 减法 (Subtraction) + ALU_AND: result = a & b; // 与 (AND) + ALU_OR: result = a | b; // 或 (OR) + ALU_NOR: result = ~(a | b); // 或非 (NOR) + ALU_SLT: result = {31'b0, slt_res}; // 有符号小于比较 (Set on Less Than, Signed) + ALU_SLTU: result = {31'b0, sltu_res}; // 无符号小于比较 (Set on Less Than, Unsigned) + default: result = 32'hxxxxxxxx; // 默认情况,输出不定态 (Default case, output undefined) + endcase + end + + // 零标志位输出: 当减法结果为0时置1,用于BEQ指令 + // Zero flag output: set to 1 when the subtraction result is zero, for BEQ instruction + assign zero = (sub_result == 32'h00000000); + + // 小于标志位输出: 用于BLT指令 + // Less Than flag output: for BLT instruction + assign lt = slt_res; + +endmodule \ No newline at end of file diff --git a/Hardware/LA32R.srcs/sources_1/new/control_unit.v b/Hardware/LA32R.srcs/sources_1/new/control_unit.v new file mode 100644 index 0000000..bcb295b --- /dev/null +++ b/Hardware/LA32R.srcs/sources_1/new/control_unit.v @@ -0,0 +1,166 @@ +`timescale 1ns / 1ps + +/******************************************************************************* +** Company: Nantong University +** Engineer: あやせももこ +** +** Create Date: 2025-06-16 +** Design Name: LA32R Single Cycle CPU +** Module Name: control_unit +** Project Name: Computer Architecture Course Design +** Target Devices: Any FPGA +** Tool Versions: Vivado 2018.1 +** Description: +** This is the main control unit for the single-cycle LA32R CPU. It decodes +** the instruction's opcode and function fields to generate all necessary +** control signals for the datapath. +** +** [FIXED] Restructured the case statement to correctly handle instructions +** that share the same opcode, such as LD.W and ST.W. +** +** Revision: +** Revision 0.02 - Corrected case statement logic for shared opcodes. +** Revision 0.01 - File Created +** +*******************************************************************************/ + +module control_unit ( + input wire [31:0] instr, // 指令输入 (Instruction Input) + input wire zero_flag, // 来自ALU的零标志位 (Zero flag from ALU) + input wire lt_flag, // 来自ALU的小于标志位 (Less-than flag from ALU) + + output reg reg_write_en, // 寄存器写使能 (Register Write Enable) + output reg mem_to_reg, // 选择写回寄存器的数据源 (Selects data source for register write-back) + output reg mem_write_en, // 存储器写使能 (Memory Write Enable) + output reg alu_src, // 选择ALU的第二操作数源 (Selects ALU's second operand source) + output reg src_reg, // 选择寄存器堆的第二读地址源 (Selects Register File's second read address source) + output reg [2:0] ext_op, // 立即数扩展控制 (Immediate extender control) + output reg [3:0] alu_op, // ALU操作控制 (ALU operation control) + output wire pcsource // PC下一个地址来源选择 (PC next address source selection) +); + + // 提取指令中的关键字段 + // Extract key fields from the instruction + wire [5:0] opcode = instr[31:26]; + wire [1:0] func2 = instr[21:20]; // for 3R-type per ISA document + wire [4:0] func5 = instr[19:15]; // for 3R-type per ISA document + wire [3:0] func4 = instr[25:22]; // for 2RI12-type + + // 定义指令操作码 (Opcode Definitions) + localparam OP_GROUP_00 = 6'b000000; // Contains 3R instructions + localparam OP_ADDI_W = 6'b000010; + localparam OP_LUI12I = 6'b000101; + localparam OP_GROUP_0A = 6'b001010; // Contains LD.W, ST.W + localparam OP_B = 6'b010100; + localparam OP_BEQ = 6'b010110; + localparam OP_BLT = 6'b011000; + + // 定义ALU操作码 (ALU Operation Definitions) + localparam ALU_ADD = 4'b0000; + localparam ALU_SUB = 4'b0001; + localparam ALU_AND = 4'b0010; + localparam ALU_OR = 4'b0011; + localparam ALU_NOR = 4'b0100; + localparam ALU_SLT = 4'b0101; + localparam ALU_SLTU = 4'b0110; + + // 定义立即数扩展类型 (Immediate Extension Type Definitions) + localparam EXT_SI12 = 3'b001; + localparam EXT_SI16 = 3'b010; + localparam EXT_UI20 = 3'b011; + localparam EXT_SI26 = 3'b100; + + // 主译码逻辑 (Main Decoding Logic) + always @(*) begin + // --- 控制信号默认值,防止生成锁存器 --- + // Default values for control signals to prevent latches + reg_write_en = 1'b0; + mem_to_reg = 1'b0; + mem_write_en = 1'b0; + alu_src = 1'b0; + src_reg = 1'b0; + ext_op = 3'bxxx; + alu_op = 4'bxxxx; + + case (opcode) + OP_GROUP_00: begin + // Differentiate based on func2 field + if (func2 == 2'b01) begin // This is a 3R-type arithmetic/logic instruction + reg_write_en = 1'b1; + alu_src = 1'b0; // B operand comes from register + src_reg = 1'b0; // Second read address comes from rk field + // Further decode based on func5 + case(func5) + 5'b00000: alu_op = ALU_ADD; + 5'b00010: alu_op = ALU_SUB; + 5'b00100: alu_op = ALU_SLT; + 5'b00101: alu_op = ALU_SLTU; + 5'b01000: alu_op = ALU_NOR; + 5'b01001: alu_op = ALU_AND; + 5'b01010: alu_op = ALU_OR; + default: alu_op = 4'bxxxx; + endcase + end + end + OP_ADDI_W: begin + // ADDI.W has its own opcode + reg_write_en = 1'b1; + alu_src = 1'b1; // B operand comes from immediate + ext_op = EXT_SI12; + alu_op = ALU_ADD; + end + OP_LUI12I: begin + reg_write_en = 1'b1; + alu_src = 1'b1; + mem_to_reg = 1'b0; // ALU result writes back + ext_op = EXT_UI20; + alu_op = ALU_ADD; // ALU adds immediate to zero + end + OP_GROUP_0A: begin + // Differentiate LD.W and ST.W based on func4 + if (func4 == 4'b0010) begin // LD.W + reg_write_en = 1'b1; + mem_to_reg = 1'b1; // Data from memory writes back + alu_src = 1'b1; + ext_op = EXT_SI12; + alu_op = ALU_ADD; // Calculate address + end + else if (func4 == 4'b0110) begin // ST.W + mem_write_en = 1'b1; + alu_src = 1'b1; + src_reg = 1'b1; // Second read address comes from rd field + ext_op = EXT_SI12; + alu_op = ALU_ADD; // Calculate address + end + end + OP_B: begin + // Unconditional branch + ext_op = EXT_SI26; + end + OP_BEQ: begin + alu_src = 1'b0; + src_reg = 1'b1; // Second read address comes from rd field + ext_op = EXT_SI16; + alu_op = ALU_SUB; // Compare + end + OP_BLT: begin + alu_src = 1'b0; + src_reg = 1'b1; // Second read address comes from rd field + ext_op = EXT_SI16; + alu_op = ALU_SUB; // Compare + end + default: begin + // All signals keep their default values + end + endcase + end + + // PC下一个地址来源的逻辑 + // Logic for PC's next address source + wire beq_cond = (opcode == OP_BEQ) && zero_flag; + wire blt_cond = (opcode == OP_BLT) && lt_flag; + wire b_cond = (opcode == OP_B); + + assign pcsource = beq_cond || blt_cond || b_cond; + +endmodule diff --git a/Hardware/LA32R.srcs/sources_1/new/cpu_top.v b/Hardware/LA32R.srcs/sources_1/new/cpu_top.v new file mode 100644 index 0000000..ecc238d --- /dev/null +++ b/Hardware/LA32R.srcs/sources_1/new/cpu_top.v @@ -0,0 +1,144 @@ +`timescale 1ns / 1ps + +/******************************************************************************* +** Company: Nantong University +** Engineer: あやせももこ +** +** Create Date: 2025-06-16 +** Design Name: LA32R Single Cycle CPU +** Module Name: cpu_top +** Project Name: Computer Architecture Course Design +** Target Devices: Any FPGA +** Tool Versions: Vivado 2018.1 +** Description: +** This is the top-level module of the single-cycle LA32R CPU. It instantiates +** and connects all sub-modules including PC, memories, register file, ALU, +** immediate extender, and the control unit. The connections follow the +** datapath diagram provided in the course design guide. +** +** Revision: +** Revision 0.01 - File Created +** Additional Comments: +** - This file integrates the entire design. +** +*******************************************************************************/ + +`timescale 1ns / 1ps + +module cpu_top ( + input wire clk, + input wire rst +); + + // --- 内部连线声明 (Internal Wire Declarations) --- + wire [31:0] pc_out; + wire [31:0] instr; + wire [31:0] imm_ext; + wire [31:0] read_data1; + wire [31:0] read_data2; + wire [31:0] alu_result; + wire [31:0] mem_read_data; + wire [31:0] write_back_data; + wire [31:0] alu_b_operand; + + wire [4:0] reg_write_addr; + wire [4:0] reg_read_addr1; + wire [4:0] reg_read_addr2; + wire [4:0] reg_read_addr2_final; + + // 控制信号 + // Control Signals + wire reg_write_en; + wire mem_to_reg; + wire mem_write_en; + wire alu_src; + wire src_reg; + wire pcsource; + wire zero_flag; + wire lt_flag; + wire [2:0] ext_op; + wire [3:0] alu_op; + + + // --- 模块实例化 (Module Instantiation) --- + + // PC (程序计数器) + pc u_pc ( + .clk (clk), + .rst (rst), + .pcsource (pcsource), + .imm_ext (imm_ext), + .pc_out (pc_out) + ); + + // Instruction Memory (指令存储器) + instruction_memory u_inst_mem ( + .addr (pc_out), + .instr (instr) + ); + + // Control Unit (控制单元) + control_unit u_ctrl_unit ( + .instr (instr), + .zero_flag (zero_flag), + .lt_flag (lt_flag), + .reg_write_en (reg_write_en), + .mem_to_reg (mem_to_reg), + .mem_write_en (mem_write_en), + .alu_src (alu_src), + .src_reg (src_reg), + .ext_op (ext_op), + .alu_op (alu_op), + .pcsource (pcsource) + ); + + // Immediate Extender (立即数扩展单元) + imm_extender u_imm_ext ( + .instr (instr), + .ext_op (ext_op), + .imm_ext (imm_ext) + ); + + // MUX for Register File's second read address (src_reg_mux) + assign reg_read_addr2_final = src_reg ? instr[4:0] : instr[14:10]; + + // Register File (寄存器堆) + register_file u_reg_file ( + .clk (clk), + .rst (rst), + .reg_write_en (reg_write_en), + .read_addr1 (instr[9:5]), + .read_addr2 (reg_read_addr2_final), + .write_addr (instr[4:0]), + .write_data (write_back_data), + .read_data1 (read_data1), + .read_data2 (read_data2) + ); + + // MUX for ALU's second operand (alu_src_mux) + assign alu_b_operand = alu_src ? imm_ext : read_data2; + + // ALU (算术逻辑单元) + alu u_alu ( + .a (read_data1), + .b (alu_b_operand), + .alu_op (alu_op), + .result (alu_result), + .zero (zero_flag), + .lt (lt_flag) + ); + + // Data Memory (数据存储器) + data_memory u_data_mem ( + .clk (clk), + .mem_write_en (mem_write_en), + .addr (alu_result), + .write_data (read_data2), // ST.W指令的数据来自第二个读端口(rd) + .read_data (mem_read_data) + ); + + // MUX for write-back data (mem_to_reg_mux) + assign write_back_data = mem_to_reg ? mem_read_data : alu_result; + + +endmodule diff --git a/Hardware/LA32R.srcs/sources_1/new/data_memory.v b/Hardware/LA32R.srcs/sources_1/new/data_memory.v new file mode 100644 index 0000000..0ae5cbf --- /dev/null +++ b/Hardware/LA32R.srcs/sources_1/new/data_memory.v @@ -0,0 +1,32 @@ +`timescale 1ns / 1ps + +/******************************************************************************* +** Data Memory Module +*******************************************************************************/ + +module data_memory ( + input wire clk, // 时钟 (Clock) + input wire mem_write_en, // 写使能 (Write Enable) + input wire [31:0] addr, // 地址输入 (Address input) + input wire [31:0] write_data, // 待写数据 (Write data) + output wire [31:0] read_data // 读出数据 (Read data) +); + // 在FPGA中,这会综合成一个同步写的RAM + // In an FPGA, this synthesizes into a synchronous-write RAM. + + reg [31:0] mem [0:1023]; // 示例: 1KB数据空间 (Example: 1KB data space) + + // 同步写 + // Synchronous write + always @(posedge clk) begin + if (mem_write_en) begin + mem[addr[11:2]] <= write_data; + end + end + + // 异步读 + // Asynchronous read + assign read_data = mem[addr[11:2]]; + +endmodule + diff --git a/Hardware/LA32R.srcs/sources_1/new/imm_extender.v b/Hardware/LA32R.srcs/sources_1/new/imm_extender.v new file mode 100644 index 0000000..e0200e4 --- /dev/null +++ b/Hardware/LA32R.srcs/sources_1/new/imm_extender.v @@ -0,0 +1,76 @@ +`timescale 1ns / 1ps + +/******************************************************************************* +** Company: Nantong University +** Engineer: あやせももこ +** +** Create Date: 2025-06-16 +** Design Name: LA32R Single Cycle CPU +** Module Name: imm_extender +** Project Name: Computer Architecture Course Design +** Target Devices: Any FPGA +** Tool Versions: Vivado 2018.1 +** Description: +** This module handles the sign/zero extension of immediate values found in +** various instruction formats of the LA32R architecture. It generates a 32-bit +** immediate value based on the instruction and the control signal `ext_op`. +** +** Supported Extensions: +** - 12-bit signed immediate (si12) for ADDI.W, LD.W, ST.W +** - 20-bit immediate (si20) for LUI12I.W (zero-extended low) +** - 16-bit signed offset for BEQ, BLT +** - 26-bit signed offset for B +** +** Revision: +** Revision 0.01 - File Created +** Additional Comments: +** - This is a purely combinational logic module. +** +*******************************************************************************/ + +module imm_extender ( + input wire [31:0] instr, // 32位指令输入 (32-bit instruction input) + input wire [2:0] ext_op, // 扩展操作控制信号 (Extension operation control signal) + output reg [31:0] imm_ext // 32位扩展后的立即数输出 (32-bit extended immediate output) +); + + // 定义立即数扩展类型的参数 + // Define parameters for immediate extension types + localparam EXT_SI12 = 3'b001; // 12-bit signed immediate for I-type + localparam EXT_SI16 = 3'b010; // 16-bit signed offset for branches + localparam EXT_UI20 = 3'b011; // 20-bit immediate for LUI + localparam EXT_SI26 = 3'b100; // 26-bit signed offset for jump + + // 提取指令中不同格式的立即数 + // Extract immediate values from different instruction formats + wire [11:0] si12 = instr[21:10]; + wire [15:0] si16 = instr[25:10]; + wire [19:0] si20 = instr[24:5]; + wire [25:0] si26 = {instr[9:0], instr[25:10]}; // B指令的offs[25:16]在[9:0], offs[15:0]在[25:10] + + // 组合逻辑: 根据ext_op选择不同的扩展方式 + // Combinational logic: select extension method based on ext_op + always @(*) begin + case (ext_op) + EXT_SI12: + // 对si12进行符号位扩展 + // Sign-extend si12 + imm_ext = {{20{si12[11]}}, si12}; + EXT_SI16: + // 对si16进行符号位扩展并左移两位 + // Sign-extend si16 and shift left by 2 + imm_ext = {{14{si16[15]}}, si16, 2'b00}; + EXT_UI20: + // 对si20进行高位加载,低12位补0 + // Load si20 to high bits, pad low 12 bits with 0 + imm_ext = {si20, 12'b0}; + EXT_SI26: + // 对si26进行符号位扩展并左移两位 + // Sign-extend si26 and shift left by 2 + imm_ext = {{4{si26[25]}}, si26, 2'b00}; + default: + imm_ext = 32'hxxxxxxxx; // 默认情况,输出不定态 + endcase + end + +endmodule diff --git a/Hardware/LA32R.srcs/sources_1/new/instruction_memory.v b/Hardware/LA32R.srcs/sources_1/new/instruction_memory.v new file mode 100644 index 0000000..5628837 --- /dev/null +++ b/Hardware/LA32R.srcs/sources_1/new/instruction_memory.v @@ -0,0 +1,31 @@ +`timescale 1ns / 1ps + +/******************************************************************************* +** Instruction Memory Module +*******************************************************************************/ + +module instruction_memory ( + input wire [31:0] addr, // 地址输入 (Address input) + output wire [31:0] instr // 指令输出 (Instruction output) +); + // 指令存储器通常是只读的 (Instruction memory is typically read-only) + // 在FPGA中,这会综合成一个ROM + // In an FPGA, this synthesizes into a ROM. + + // 仿真时,使用reg数组和$readmemh加载程序 + // For simulation, use a reg array and $readmemh to load the program. + reg [31:0] mem [0:1023]; // 示例: 1024条指令空间 (Example: 1024 instruction space) + + initial begin + // 从文件中加载指令 + // Load instructions from a file. + // 你需要创建一个名为 "program.hex" 的文件 + // You need to create a file named "program.hex". + $readmemh("../../../../../Software/program.hex", mem); + end + + // 字节地址转换为字地址 + // Convert byte address to word address. + assign instr = mem[addr[11:2]]; + +endmodule \ No newline at end of file diff --git a/Hardware/LA32R.srcs/sources_1/new/pc.v b/Hardware/LA32R.srcs/sources_1/new/pc.v new file mode 100644 index 0000000..79b3032 --- /dev/null +++ b/Hardware/LA32R.srcs/sources_1/new/pc.v @@ -0,0 +1,56 @@ +`timescale 1ns / 1ps + +/******************************************************************************* +** Company: Nantong University +** Engineer: あやせももこ +** +** Create Date: 2025-06-16 +** Design Name: LA32R Single Cycle CPU +** Module Name: pc +** Project Name: Computer Architecture Course Design +** Target Devices: Any FPGA +** Tool Versions: Vivado 2018.1 +** Description: +** This module implements the Program Counter (PC) and its update logic. +** The PC is a 32-bit register that holds the address of the instruction +** to be fetched. It updates on every clock cycle. +** +** Update Logic: +** - If no branch/jump: PC_next = PC_current + 4 +** - If branch/jump taken: PC_next = Branch/Jump Target Address +** +** Revision: +** Revision 0.01 - File Created +** +*******************************************************************************/ + +module pc ( + input wire clk, // 时钟 (Clock) + input wire rst, // 复位 (Reset) + input wire pcsource, // PC下一个地址来源选择 (PC next address source selection) + input wire [31:0] imm_ext, // 来自立即数扩展单元的偏移量 (Offset from immediate extender) + output reg [31:0] pc_out // 当前PC值 (Current PC value) +); + + wire [31:0] pc_plus_4; + wire [31:0] pc_branch; + wire [31:0] pc_next; + + // PC寄存器 + // PC register + always @(posedge clk or posedge rst) begin + if (rst) begin + pc_out <= 32'h00000000; // 复位到0地址 + end else begin + pc_out <= pc_next; + end + end + + // PC更新逻辑 + // PC update logic + assign pc_plus_4 = pc_out + 32'd4; + assign pc_branch = pc_out + imm_ext; // 偏移量已经左移两位 + + assign pc_next = pcsource ? pc_branch : pc_plus_4; + +endmodule diff --git a/Hardware/LA32R.srcs/sources_1/new/register_file.v b/Hardware/LA32R.srcs/sources_1/new/register_file.v new file mode 100644 index 0000000..d876b9d --- /dev/null +++ b/Hardware/LA32R.srcs/sources_1/new/register_file.v @@ -0,0 +1,79 @@ +`timescale 1ns / 1ps + +/******************************************************************************* +** Company: Nantong University +** Engineer: あやせももこ +** +** Create Date: 2025-06-16 +** Design Name: LA32R Single Cycle CPU +** Module Name: register_file +** Project Name: Computer Architecture Course Design +** Target Devices: Any FPGA +** Tool Versions: Vivado 2018.1 +** Description: +** This module implements the 32x32-bit Register File for the LA32R CPU. +** It features two asynchronous read ports and one synchronous write port. +** A defensive design is implemented to ensure R0 is always zero. +** +** Features: +** - 32 general-purpose registers, each 32 bits wide. +** - Asynchronous read: Read ports reflect content immediately. +** - Synchronous write: Write operation occurs on the rising edge of the clock. +** - R0 Hardwired to Zero: Cannot be written to, and reading it always returns 0. +** +** Revision: +** Revision 0.01 - File Created +** Additional Comments: +** - Reset logic is included to initialize all registers to zero, which is good practice for simulation and synthesis. +** +*******************************************************************************/ + +module register_file ( + input wire clk, // 时钟 (Clock) + input wire rst, // 复位 (Reset) + input wire reg_write_en, // 写使能 (Write Enable) + input wire [4:0] read_addr1, // 读地址1 (Read Address 1) + input wire [4:0] read_addr2, // 读地址2 (Read Address 2) + input wire [4:0] write_addr, // 写地址 (Write Address) + input wire [31:0] write_data, // 写数据 (Write Data) + output wire [31:0] read_data1, // 读数据1 (Read Data 1) + output wire [31:0] read_data2 // 读数据2 (Read Data 2) +); + + // 声明32个32位的寄存器阵列 + // Declare an array of 32 registers, each 32 bits wide. + reg [31:0] registers[0:31]; + + integer i; + + // 同步写操作 (时钟上升沿触发) + // Synchronous write operation (triggered on the rising edge of the clock) + always @(posedge clk or posedge rst) begin + if (rst) begin + // 复位时,将所有寄存器清零 + // On reset, clear all registers to zero. + for (i = 0; i < 32; i = i + 1) begin + registers[i] <= 32'b0; + end + end else if (reg_write_en) begin + // 写使能有效时,执行写操作 + // When write enable is active, perform the write operation. + // 防御性设计:确保不写入0号寄存器 + // Defensive design: ensure register R0 is not written to. + if (write_addr != 5'd0) begin + registers[write_addr] <= write_data; + end + end + end + + // 异步读操作1 + // Asynchronous read port 1 + // 防御性设计:确保读取0号寄存器时返回0 + // Defensive design: ensure reading from R0 always returns zero. + assign read_data1 = (read_addr1 == 5'd0) ? 32'b0 : registers[read_addr1]; + + // 异步读操作2 + // Asynchronous read port 2 + assign read_data2 = (read_addr2 == 5'd0) ? 32'b0 : registers[read_addr2]; + +endmodule diff --git a/Hardware/LA32R.xpr b/Hardware/LA32R.xpr new file mode 100644 index 0000000..6e1158e --- /dev/null +++ b/Hardware/LA32R.xpr @@ -0,0 +1,190 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Reference/支持5种类型共计14条指令的单周期LA32R CPU逻辑电路图.png b/Reference/支持5种类型共计14条指令的单周期LA32R CPU逻辑电路图.png new file mode 100644 index 0000000..6216152 Binary files /dev/null and b/Reference/支持5种类型共计14条指令的单周期LA32R CPU逻辑电路图.png differ diff --git a/Reference/设计方案.md b/Reference/设计方案.md new file mode 100644 index 0000000..af75aa4 --- /dev/null +++ b/Reference/设计方案.md @@ -0,0 +1,528 @@ +# LA32R 单周期 CPU 模型机课程设计详细实现方案 + +## 前言 + +本报告旨在为“LA32R 架构模型机硬件系统”课程设计项目提供一份全面、详尽的总体设计与分步实现方案。报告将遵循专业的硬件设计流程,从指令集体系结构(ISA)的深度分析入手,逐步构建数据通路、设计控制逻辑,并最终提出一套完整的、基于 Verilog HDL 的模块化实现与自动化验证策略。本方案不仅涵盖了课程设计指导书中提出的所有要求,更融入了业界标准的设计思想与最佳实践,旨在帮助设计者不仅完成项目,更能深刻理解计算机体系结构的核心原理。 + +--- + +## 第一部分:体系结构基础与数据通路设计 + +在着手编写任何硬件描述语言(HDL)代码之前,首要任务是深入理解目标处理器的体系结构,并在此基础上构建一个能够支持所有指令执行的逻辑数据通路。这一阶段是整个设计的基石,其正确性与完备性直接决定了后续工作的成败。 + +### 1.1 LA32R 指令集体系结构(ISA)深度分析 + +对指令集体系结构(ISA)的分析是 CPU 设计的起点。LA32R 指令集采用哈佛结构,包含独立的指令存储器和数据存储器,其设计的 14 条指令根据编码格式和功能,可以进行系统性的解构。 + +#### 1.1.1 指令格式分类与解析 + +根据指导书中的表 2,14 条指令可归纳为五种不同的格式。对这些格式的透彻理解是设计译码器和数据通路的前提。 + +1. **1RI20 型 (LUI12I.W):** 用于将一个 20 位的立即数加载到寄存器的高位。其格式为 `opcode (6) | 0 (1) | si20 (20) | rd (5)`。 +2. **3R 型 (ADD.W, SUB.W, SLT, SLTU, NOR, AND, OR):** 这是典型的寄存器-寄存器操作指令。其格式为 `opcode (6) | 0000 (4) | func (2) | 00000 (5) | rk (5) | rj (5) | rd (5)`。这类指令从两个源寄存器(`rj`, `rk`)读取操作数,并将结果写入目标寄存器(`rd`)。 +3. **2RI12 型 (ADDI.W, LD.W, ST.W):** 这是寄存器-立即数操作指令。其格式为 `opcode (6) | func (4) | si12 (12) | rj (5) | rd (5)`。这类指令使用一个源寄存器(`rj`)和一个 12 位立即数(`si12`)作为操作数。 +4. **I26 型 (B):** 无条件跳转指令。其格式为 `opcode (6) | offs[15:0] (16) | offs[25:16] (10)`。它包含一个 26 位的偏移量。 +5. **2RI16 型 (BEQ, BLT):** 条件分支指令。其格式为 `opcode (6) | offs[15:0] (16) | rj (5) | rd (5)`。它使用两个寄存器(`rj`, `rd`)进行比较,并包含一个 16 位的偏移量。 + +#### 1.1.2 指令格式对数据通路设计的启示 + +对指令格式的深入分析,能够揭示出许多隐含的设计简化信息,从而指导我们构建更高效、更简洁的数据通路。 + +一个关键的发现是指令中 **寄存器地址域的高度规整性** 。在所有需要读写寄存器的指令中(3R 型、2RI12 型、2RI16 型),目标寄存器地址`rd`始终位于指令的`[4:0]`位,第一个源寄存器地址`rj`始终位于`[9:5]`位,第二个源寄存器地址`rk`始终位于`[14:10]`位。 + +这种设计并非偶然,它极大地简化了寄存器堆(Register File)的连接。这意味着: + +- 指令的`[9:5]`位可以直接连接到寄存器堆的第一个读地址端口(`ReadRegister1`)。 +- 指令的`[14:10]`位可以直接连接到寄存器堆的第二个读地址端口(`ReadRegister2`)。 +- 指令的`[4:0]`位可以直接连接到寄存器堆的写地址端口(`WriteRegister`)。 + +这样的设计避免了在寄存器堆的地址输入端使用复杂的选择器(MUX),将选择的复杂性转移到了数据通路的其他部分(例如,选择哪个数据写回寄存器堆)。然而,对于 ST、BEQ、BLT 等指令,它们的第二个源操作数来自`rd`字段,因此在寄存器堆的第二个读地址端口前仍需一个 MUX 进行选择。 + +与此相对, **立即数字段的位置和长度则呈现多样性** 。`LUI12I.W`使用 20 位立即数`si20`,`ADDI.W`等使用 12 位`si12`,`B`使用 26 位`offs26`,而`BEQ`等使用 16 位`offs16`。这预示着 **立即数扩展单元(Sign/Immediate Extension Unit)** 将是一个不可忽视的关键组合逻辑模块。该模块需要根据指令的`opcode`,从指令寄存器(IR)的不同位置提取出正确长度的立即数字段,并根据指令功能说明(例如`ADDI.W`需要符号扩展,而`LUI12I.W`是逻辑拼接)进行相应的处理。这个单元是初学者设计中常见的错误来源,必须给予足够重视。 + +### 1.2 设计单周期数据通路 + +单周期 CPU 的特点是每条指令都在一个时钟周期内完成取指、译码、执行、访存和写回。这意味着数据通路必须包含一个时钟周期内执行任何一条指令所需的所有硬件单元和连接。我们将以指导书中的参考图(图 1)为基础,逐步构建并完善数据通路。 + +#### 1.2.1 核心硬件组件 + +数据通路主要由以下功能模块通过导线和多路选择器连接而成: + +- **程序计数器 (Program Counter, PC):** 32 位寄存器,存放当前待取指令的地址。 +- **指令存储器 (Instruction Memory):** 根据 PC 提供的地址,输出对应的 32 位指令。 +- **寄存器堆 (Register File):** 包含 32 个 32 位通用寄存器(GR)。它具有两个异步读端口和一个同步写端口,能够在一个周期内同时读取两个操作数并写入一个结果。 +- **算术逻辑单元 (Arithmetic Logic Unit, ALU):** 执行算术(加、减)和逻辑(与、或、或非)运算,以及比较操作。 +- **数据存储器 (Data Memory):** 用于`LD.W`(加载)和`ST.W`(存储)指令,与处理器进行数据交换。 +- **立即数扩展单元 (Immediate Extender):** 根据指令类型,对指令中的立即数字段进行提取、拼接和符号扩展。 +- **控制单元 (Control Unit):** 核心的译码部件,根据指令的`opcode`生成控制数据通路中所有选择器和功能单元的控制信号。 +- **多路选择器 (Multiplexers, MUX):** 在数据通路的关键节点,根据控制信号选择正确的数据来源。 + +#### 1.2.2 关键指令的数据流追踪 + +通过追踪几条代表性指令的数据流,可以清晰地展示数据通路各组件如何协同工作。 + +1. **R 型指令 (以`ADD.W rd, rj, rk`为例):** + - **取指 (IF):** PC 的值送入指令存储器地址端口,指令存储器输出`ADD.W`指令。PC 更新为`PC+4`。 + - **译码/读寄存器 (ID):** 指令被送入控制单元和寄存器堆。控制单元进行译码。寄存器堆根据指令中的`rj`(`instr[9:5]`)和`rk`(`instr[14:10]`)地址,读出两个 32 位操作数。 + - **执行 (EX):** 两个操作数被送入 ALU。控制单元生成的`ALUOp`信号指示 ALU 执行加法操作。 + - **写回 (WB):** ALU 的计算结果通过一个选择器(`MemtoReg` MUX,此时选择 ALU 结果)送回寄存器堆的写数据端口。控制单元使能`RegWrite`信号,将结果写入由`rd`(`instr[4:0]`)指定的寄存器。 +2. **加载指令 (`LD.W rd, rj, si12`):** + - **IF & ID:** 与 R 型指令类似,但此时寄存器堆只读取`rj`的值。同时,指令中的`si12`字段被送入立即数扩展单元进行符号扩展。 + - **EX:** `rj`寄存器的值和符号扩展后的`si12`被送入 ALU,进行加法运算以计算内存地址`Addr`。 + - **访存 (MEM):** ALU 计算出的地址`Addr`被送入数据存储器的地址端口。控制单元使能`MemRead`信号,数据存储器根据地址读出数据。 + - **WB:** 从数据存储器读出的数据通过`MemtoReg` MUX(此时选择内存数据)送回寄存器堆,并写入`rd`寄存器。 +3. **条件分支指令 (`BEQ rj, rd, offs16`):** + - **IF & ID:** 与 R 型指令类似,读取`rj`和`rd`寄存器的值。同时,`offs16`字段被立即数扩展单元进行符号扩展并左移两位(因为指令地址是字节对齐的,而偏移量通常以字为单位)。 + - **EX:** `rj`和`rd`的值被送入 ALU 进行减法操作。如果两者相等,ALU 的`Zero`标志位输出为 1。 + - **PC 更新:** 控制单元生成`Branch`信号。该信号与 ALU 的`Zero`标志位进行逻辑与运算。如果结果为 1,则表示分支条件成立,一个专门用于 PC 更新的选择器会选择由`PC+4`和扩展后的偏移量相加得到的跳转目标地址来更新 PC;否则,PC 正常更新为`PC+4`。 + +#### 1.2.3 单周期设计的核心权衡:性能与简洁性 + +单周期设计的最大特点是其 **简洁性** 。每条指令的控制流程固定,没有复杂的状态转换,使得控制单元的设计相对直接。然而,这种简洁性是以牺牲**性能**为代价的。 + +在单周期模型中,时钟周期必须足够长,以容纳**最长路径**指令的完整执行。通过上述数据流追踪,我们可以确定`LD.W`(加载)指令是关键路径最长的指令之一。其完整执行需要经历以下串行延迟: + +$$T_{cycle}​=T_{PC_{r​ead}}​+T_{IMem_{a​ccess}}​+T_{RegFile_{r​ead}}​+T_{ALU_{c​alc}}​+T_{DMem_{a​ccess}}​+T_{MUX}​+T_{RegFile_{s​etup}}​$$ + +相比之下,一条简单的`ADD.W`指令并不需要访问数据存储器,而一条`B`(无条件跳转)指令甚至不需要 ALU 进行数据运算。然而,在单周期设计中,这些快速指令必须等待与`LD.W`同样长的时钟周期,造成了巨大的时钟资源浪费。 + +在最终的设计报告中,对这一核心权衡的分析是必不可少的。它不仅展示了设计者对项目本身的完成度,更体现了对计算机体系结构基本设计原则的深刻理解。这为后续学习更高效的多周期和流水线处理器设计奠定了理论基础。 + +--- + +## 第二部分:核心功能模块设计 (Verilog) + +将概念性的数据通路图转化为可综合的硬件描述语言代码,是项目的核心实践环节。采用自底向上的模块化设计方法,可以有效管理复杂性,提高代码的可重用性和可测试性。 + +### 2.1 程序计数器(PC)及 PC 更新模块 + +- **功能说明:** PC 模块用于存储当前指令的地址,并在每个时钟周期更新以获取下一条指令。在顺序执行时,PC 递增 4;在遇到跳转或分支指令时,PC 根据偏移量更新。 +- **接口信号:** + - 输入: `clk` (时钟), `rst` (复位), `PCSrc` (下一 PC 来源控制信号), `branch_addr` (分支目标地址)。 + - 输出: `PC` (当前指令地址)。 +- **内部实现:** PC 本质是一个 32 位寄存器,在时钟上升沿根据`PCSrc`信号选择更新。`PCSrc=0`时,`PC_next = PC + 4`;`PCSrc=1`时,`PC_next = branch_addr`。`branch_addr`由一个加法器计算得出,通常为`PC + SignExt(offset << 2)`。 + +### 2.2 指令与数据存储器 + +- **指令存储器 (Instruction Memory):** + - **功能:** 只读存储器,根据 PC 提供的地址输出 32 位指令。在 FPGA 上可利用分布式 ROM 或块 RAM(Block RAM)实现。 + - **实现:** 在仿真中,可使用 Verilog 的`reg`数组和`$readmemh`系统任务从外部`.hex`文件加载机器码,以方便测试程序的更换与管理。 +- **数据存储器 (Data Memory):** + - **功能:** 支持字(32 位)读写的随机存取存储器(RAM),用于`LD.W`和`ST.W`指令。 + - **实现:** 可用同步双端口 RAM 实现。写操作在时钟上升沿根据`MemWrite`信号执行;读操作可设计为异步(组合逻辑读),即根据当前地址立即输出数据。地址索引需注意字节地址到字地址的转换(如 + `addr[31:2]`)。 + +### 2.3 寄存器堆 (Register File) + +寄存器堆是 CPU 中用于临时存储数据的高速存储单元。对于 LA32R,需要一个包含 32 个 32 位寄存器的阵列。 + +#### 2.3.1 寄存器堆接口与行为 + +- **接口:** + - `input clk, rst;` // 时钟和复位信号 + - `input RegWrite;` // 写使能信号 + - `input [4:0] ReadRegister1, ReadRegister2;` // 两个 5 位读地址 + - `input [4:0] WriteRegister;` // 一个 5 位写地址 + - `input [31:0] WriteData;` // 32 位写数据 + - `output [31:0] ReadData1, ReadData2;` // 两个 32 位读数据 +- **行为:** + - **写操作:** 同步的。当`RegWrite`为高电平,在时钟的上升沿,`WriteData`被写入`WriteRegister`指定的寄存器中。 + - **读操作:** 异步(组合逻辑)的。`ReadData1`和`ReadData2`的输出实时反映由`ReadRegister1`和`ReadRegister2`地址指定的寄存器的内容。 + +#### 2.3.2 零号寄存器 (R0) 的防御性设计 + +在许多 RISC 架构中,零号寄存器(R0)被硬性规定为常数 0。它不可被写入,读取时永远返回 0。实现这一特性需要“防御性”的设计策略。 + +一个简单的实现是在写操作时增加一个判断条件: + +`if (RegWrite && (WriteRegister!= 5'd0)) registers <= WriteData;` + +这可以阻止对 R0 的写入 11。然而,这种方法并非万无一失。例如,如果系统复位不彻底,或者存在某些设计缺陷, + +`registers`的物理存储单元可能并非为 0。此时,如果读逻辑仅仅是`assign ReadData1 = registers;`,那么当`ReadRegister1`为 0 时,可能会读出一个非零值,导致灾难性的错误。 + +一个更**稳健、更具防御性**的设计方法是**在读写两端同时施加约束**。 + +1. **写端防护:** 如上所述,阻止对地址 0 的任何写操作。 +2. 读端强制: 在读端口的输出逻辑中,明确地处理地址 0 的情况。 + `assign ReadData1 = (ReadRegister1 == 5'd0)? 32'b0 : registers;` + `assign ReadData2 = (ReadRegister2 == 5'd0)? 32'b0 : registers;` + +这种双重保险的设计,确保了无论内部物理存储状态如何,对 R0 的读取操作永远返回 0。这体现了专业的硬件设计思想:模块的接口行为应是明确且不受内部意外状态影响的。 + +#### 2.3.3 寄存器堆 Verilog 代码框架 + +```verilog +// 寄存器堆模块 (32个32位寄存器, 2个读端口, 1个写端口) +module register_file ( + input wire clk, // 时钟 + input wire rst, // 复位 + input wire reg_write_en, // 写使能 + input wire [4:0] read_addr1, // 读地址1 + input wire [4:0] read_addr2, // 读地址2 + input wire [4:0] write_addr, // 写地址 + input wire [31:0] write_data, // 写数据 + output wire [31:0] read_data1, // 读数据1 + output wire [31:0] read_data2 // 读数据2 +); + + // 声明32个32位的寄存器阵列 + reg [31:0] registers[0:31]; + + // 同步写操作 (时钟上升沿触发) + always @(posedge clk) begin + if (rst) begin + // 复位时,将所有寄存器清零 (可选,但良好实践) + for (integer i = 0; i < 32; i = i + 1) begin + registers[i] <= 32'b0; + end + end else if (reg_write_en) begin + // 写使能有效时,执行写操作 + // 防御性设计:确保不写入0号寄存器 + if (write_addr!= 5'd0) begin + registers[write_addr] <= write_data; + end + end + end + + // 异步读操作1 + // 防御性设计:确保读取0号寄存器时返回0 + assign read_data1 = (read_addr1 == 5'd0)? 32'b0 : registers[read_addr1]; + + // 异步读操作2 + assign read_data2 = (read_addr2 == 5'd0)? 32'b0 : registers[read_addr2]; + +endmodule +``` + +### 2.4 算术逻辑单元 (ALU) + +ALU 是 CPU 的计算核心,负责执行指令指定的算术和逻辑运算。 + +#### 2.4.1 ALU 模块接口与功能 + +- **接口:** + - `input [31:0] A, B;` // 两个 32 位操作数输入 + - `input [3:0] ALUOp;` // 4 位操作控制信号 + - `output reg [31:0] Result;` // 32 位运算结果输出 + - `output Zero;` // 零标志位输出,用于分支指令 +- **功能:** 根据`ALUOp`信号,执行`ADD.W`, `SUB.W`, `SLT`, `SLTU`, `NOR`, `AND`, `OR`等操作。此外,ALU 也用于计算内存地址和分支比较。 +- **实现:** 使用`always @(*)`块和`case`语句是实现 ALU 组合逻辑最清晰、最直接的方式。 + `Zero`标志位可以通过比较`Result`是否为全 0 来生成:`assign Zero = (Result == 32'h00000000);`。 + +#### 2.4.2 `SLT`与`SLTU`的稳健实现 + +ISA 要求 ALU 同时支持有符号比较(`SLT`)和无符号比较(`SLTU`)^3^。这为 ALU 的设计带来了一个有趣的挑战。 + +一种直接的方法是利用 Verilog 的语言特性。在比较时,将输入操作数强制转换为`signed`类型: + +`Result = ($signed(A) < $signed(B))? 32'd1 : 32'd0;` + +对于无符号比较,则使用默认的无符号行为。这种方法虽然简单,但高度依赖于 Verilog 的类型系统,可能会在不同仿真或综合工具间存在细微差异,且未能体现底层硬件的实现原理。 + +一种更为稳健和体现硬件本质的方法,是基于减法运算的标志位来实现比较。 + +1. **执行减法:** 对于`A < B`的比较,ALU 首先计算`S = A - B`。 +2. **无符号比较 (SLTU):** 在无符号数中,`A < B`等价于`A - B`会产生借位。在二进制补码加法器中,这通常表现为最高位的进位输出(`CarryOut`)为 0。因此,`SLTU`的逻辑可以实现为:如果`A-B`的结果的最高位进位为 0,则`A < B`。 +3. **有符号比较 (SLT):** 在二进制补码中,情况更为复杂,需要考虑溢出(Overflow)。有符号数`A < B`的条件是: **符号标志位 (Sign Flag) 不等于 溢出标志位 (Overflow Flag)** ,即 `Result = (SignFlag ^ OverflowFlag)? 32'd1 : 32'd0;`。其中,`SignFlag`是减法结果的最高位,`OverflowFlag`可以通过`(A & ~B & ~S) | (~A & B & S)`来计算。 + +在设计报告中,提供并比较这两种实现方式,并最终选择基于算术标志位的方法,能显著展示设计者对二进制补码运算和硬件实现的深刻理解。 + +#### 2.4.3 ALU Verilog 代码框架 + +```verilog +// ALU模块 +module alu ( + input wire [31:0] a, // 操作数A + input wire [31:0] b, // 操作数B + input wire [3:0] alu_op, // ALU操作控制码 + output reg [31:0] result, // 运算结果 + output wire zero, // 零标志位 + output wire negative // 负标志位 +); + + // 临时线网,用于SLT/SLTU + wire slt_result = ($signed(a) < $signed(b)); + wire sltu_result = (a < b); + + // 主组合逻辑 + always @(*) begin + case (alu_op) + // 具体操作码需根据控制单元设计确定 + 4'b0000: result = a + b; // ADD.W + 4'b0001: result = a - b; // SUB.W + 4'b0010: result = a & b; // AND + 4'b0011: result = a | b; // OR + 4'b0100: result = ~(a | b); // NOR + 4'b0101: result = {31'b0, slt_result}; // SLT + 4'b0110: result = {31'b0, sltu_result}; // SLTU + default: result = 32'hxxxxxxxx; // 未定义操作,输出不定态 + endcase + end + + // 标志位输出 + assign zero = (result == 32'h00000000); + assign negative = result[1]; + +endmodule +``` + +### 2.5 立即数扩展单元 (Immediate Extender) + +- **功能说明:** 根据指令类型,将指令中的立即数字段(如 12 位、16 位、20 位、26 位)转换为 32 位值,供 ALU 或 PC 更新模块使用。处理方式包括符号扩展、零扩展和拼接。 +- **接口信号:** + - 输入: `instr` (32 位指令), `ImmType` (扩展类型控制信号)。 + - 输出: `imm_ext` (32 位扩展后立即数)。 +- **内部实现:** 这是一个纯组合逻辑模块。使用`case`语句根据`ImmType`选择对应的处理逻辑。例如,对 12 位有符号立即数`si12`,可使用位拼接实现符号扩展:`imm_ext = {{20{si12}}, si12}`。对于分支偏移量,还需在扩展后左移两位(即末尾补两个 0)。 + +### 2.6 多路选择器 (Multiplexers) + +多路选择器(MUX)在数据通路中扮演着关键的“交通枢纽”角色,根据控制信号选择正确的数据来源。 + +- **ALU 第二操作数选择 (ALUSrc MUX):** 在 ALU 的 B 输入端,根据`ALUSrc`信号选择数据来源:是来自寄存器堆的第二个读端口,还是来自立即数扩展单元的输出。 +- **写回数据选择 (MemtoReg MUX):** 决定写入寄存器堆的数据来源。根据`MemtoReg`信号选择:是来自 ALU 的计算结果,还是来自数据存储器的读出值。 +- **PC 更新选择 (PCSrc MUX):** 决定下一个 PC 的值。根据`PCSrc`信号选择:是顺序执行的`PC+4`,还是分支/跳转的目标地址。 +- **寄存器堆第二读地址选择 (srcReg MUX):** 对于`ST.W`和条件分支指令,其第二个源操作数寄存器地址位于`rd`字段(`instr[4:0]`),而非`rk`字段。因此需要一个 MUX 根据`srcReg`信号,为寄存器堆的第二个读地址端口选择`instr[14:10]`(rk)或`instr[4:0]`(rd)。 + +### 2.7 顶层 CPU 模块集成 + +顶层`cpu_top`模块是整个设计的骨架。它不包含复杂的逻辑,其主要任务是: + +1. **实例化**所有子模块:PC、存储器、寄存器堆、ALU、立即数扩展单元、控制单元以及所有需要的 MUX。 +2. **连接**这些模块。根据数据通路图,将一个模块的输出连接到另一个模块的输入。例如,将寄存器堆的读数据端口连接到 ALU 的输入,将 ALU 的输出连接到数据存储器的地址端口或写回 MUX 的输入。 +3. 将控制单元生成的**控制信号**分发到数据通路中对应的 MUX 选择端和功能单元的使能端。 + +这个过程是细致且易错的,必须严格对照数据通路图进行。模块化的设计使得这一过程条理清晰,大大降低了出错的概率。 + +--- + +## 第三部分:指令级数据通路与控制信号分析 + +本部分详细分析 LA32R 指令集中五类指令在单周期 CPU 中的数据通路走向,以及执行时各主要控制信号的取值情况。通过对比可以看出,不同类型指令在数据通路中激活的部件各异,但整个 CPU 架构需统一容纳这些数据流。 + +### 3.1 1RI20 型指令 (LUI12I.W) + +- **功能:** `GR[rd] ← si20 | | 12’b0`,将 20 位立即数加载到目标寄存器高位,低 12 位补零。 +- **数据通路:** 立即数扩展单元提取`si20`并拼接 12 个 0,生成 32 位常量。该常量通过`ALUSrc` MUX 送入 ALU(另一输入可为 0),ALU 执行加法(等效于传递)后,结果通过`MemtoReg` MUX 写回`rd`寄存器。 +- 控制信号: + +| 控制信号 | LU12I.W (1RI20) | +| -------- | -------------------------- | +| RegWrite | 1 (写使能) | +| ALUSrc | 1 (ALU 第二操作数选立即数) | +| MemtoReg | 0 (ALU 结果写回) | +| MemRead | 0 | +| MemWrite | 0 | +| PCSrc | 0 (顺序执行) | + +### 3.2 3R 型指令 (ADD.W, SUB.W, SLT, SLTU, NOR, AND, OR) + +- **功能:** `GR[rd] ← GR[rj] op GR[rk]`,执行两个寄存器间的算术或逻辑运算。 +- **数据通路:** 寄存器堆读出`rj`和`rk`的值,送入 ALU。ALU 根据`AluCtrl`信号执行相应运算,结果通过`MemtoReg` MUX 写回`rd`寄存器。 +- 控制信号: + +| 控制信号 | 3R 类型指令 | +| -------- | ---------------------------- | +| RegWrite | 1 | +| ALUSrc | 0 (ALU 第二操作数来自寄存器) | +| MemtoReg | 0 | +| MemRead | 0 | +| MemWrite | 0 | +| srcReg | 0 (第二读地址选 rk 字段) | +| PCSrc | 0 | +| AluCtrl | 根据 func 译码 | + +### 3.3 2RI12 型指令 (ADDI.W, LD.W, ST.W) + +这类指令格式相同,但数据通路和控制信号有显著差异。 + +- **ADDI.W rd, rj, si12:** `GR[rd] ← GR[rj] + SignExtend(si12)`。数据通路类似 3R 型,但 ALU 的第二操作数来自符号扩展后的 + `si12`。 +- **LD.W rd, rj, si12:** `GR[rd] ← M + SignExtend(si12)]`。ALU 计算地址,数据存储器根据该地址读出数据,该数据通过 + `MemtoReg` MUX 写回`rd`寄存器。 +- **ST.W rd, rj, si12:** `M + SignExtend(si12)] ← GR[rd]`。ALU 计算地址,寄存器堆读出 + `rd`的值作为待写数据,写入数据存储器。此指令不写回寄存器。 +- 控制信号: + +| 控制信号 | ADDI.W | LD.W | ST.W | +| -------- | ------ | ---- | ---- | +| RegWrite | 1 | 1 | 0 | +| ALUSrc | 1 | 1 | 1 | +| MemtoReg | 0 | 1 | X | +| MemRead | 0 | 1 | 0 | +| MemWrite | 0 | 0 | 1 | +| srcReg | X | X | 1 | +| AluCtrl | ADD | ADD | ADD | +| PCSrc | 0 | 0 | 0 | + +### 3.4 I26 型指令 (B) + +- **功能:** `PC ← PC + SignExtend(offs26 | | 2’b0)`,无条件跳转。 +- **数据通路:** 立即数扩展单元处理`offs26`得到跳转偏移,与 PC 相加后得到目标地址。该地址通过`PCSrc` MUX 更新到 PC 寄存器。数据通路的主要部分(寄存器堆、ALU、数据存储器)均不参与有效运算。 +- **控制信号:** `PCSrc`置为 1,强制选择跳转地址。其他数据通路控制信号(`RegWrite`, `MemWrite`等)均为 0 或无关(X)。 + +### 3.5 2RI16 型指令 (BEQ, BLT) + +- **功能:** `if (condition) PC ← PC + SignExtend(offs16 | | 2’b0)`,条件分支。 +- **数据通路:** 寄存器堆读出`rj`和`rd`的值送入 ALU 进行减法比较。ALU 的`Zero`(用于 BEQ)或`Negative`(用于 BLT)标志位与控制单元生成的`Branch`信号共同决定`PCSrc`的取值。若条件成立,`PCSrc`为 1,PC 更新为跳转目标地址;否则为 0,PC 更新为`PC+4`。 +- 控制信号: + +| 控制信号 | BEQ / BLT | +| -------- | ------------------------ | +| RegWrite | 0 | +| ALUSrc | 0 | +| MemRead | 0 | +| MemWrite | 0 | +| srcReg | 1 | +| AluCtrl | SUB (减法比较) | +| PCSrc | Branch & (Zero/Negative) | + +--- + +## 第四部分:控制逻辑、验证与测试 + +如果说数据通路是 CPU 的“肌肉和骨骼”,那么控制单元就是其“大脑和神经系统”。设计完备的控制逻辑并建立一套行之有效的验证体系,是确保 CPU 功能正确性的关键。 + +### 4.1 控制单元设计 + +控制单元是一个有限状态机(FSM),但在单周期 CPU 中,这个 FSM 只有一个状态。因此,它退化为一个纯组合逻辑电路,其输出完全由其输入(即指令的`opcode`和`func`字段)决定。 + +#### 4.1.1 主控制单元与 ALU 控制单元 + +为了使设计更加模块化,控制逻辑通常被分为两部分: + +1. **主控制单元 (Main Control Unit):** + - **输入:** 指令的`opcode`字段(`instr[31:26]`)及其他功能位。 + - **输出:** 数据通路中除 ALU 操作外的所有控制信号,如`RegWrite`、`ALUSrc`、`MemRead`、`MemWrite`、`MemtoReg`、`Branch`等。此外,它还会生成一个 2 位的`ALUOp`信号,用于粗略地指示 ALU 需要执行的操作类型。 + - **实现:** 使用一个大的`case`语句或硬布线逻辑,根据`opcode`为每种指令类型生成一组固定的控制信号。 +2. **ALU 控制单元 (ALU Control Unit):** + - **输入:** 主控制单元生成的`ALUOp`信号,以及指令的`func`字段(对于 R 型指令)。 + - **输出:** 最终驱动 ALU 的 4 位`ALUOperation`信号。 + - **实现:** 这种两级控制结构使得主控制单元不必关心 R 型指令的具体运算,只需告诉 ALU 控制单元“这是一条 R 型指令”。ALU 控制单元再根据`func`字段来确定具体的运算。这遵循了“关注点分离”的设计原则。 + +#### 4.1.2 控制信号真值表:设计的核心文档 + +在编写控制单元的 Verilog 代码之前,强烈建议先创建一个 **控制信号真值表** 。这张表是连接 ISA 规范和硬件实现的桥梁,其重要性不可低估。第三部分的指令分析表即是此真值表的详细体现。 + +- **价值:** + 1. **强制性分析:** 迫使设计者在编码前就理清所有逻辑细节。 + 2. **设计规范:** 作为控制单元最精确、无歧义的设计规范。 + 3. **调试黄金标准:** 在后续仿真调试阶段,可将实际控制信号与表中期望值比对,快速定位问题。 + +### 4.2 验证策略与测试程序开发 + +验证是硬件设计中耗时最长、也最重要的环节。一个结构化的验证策略和精心设计的测试程序是必不可少的。 + +- **策略:** 遵循“增量测试”和“覆盖关键路径”的原则。测试程序不应是随机指令的堆砌,而应是一个逻辑清晰、能逐步验证 CPU 功能的序列。 +- **测试程序序列示例:** + 1. **寄存器加载:** 首先使用`LUI12I.W`和`ADDI.W`向多个寄存器加载已知初始值。 + 2. **算术/逻辑运算:** 依次执行所有 3R 型指令,操作数来自已加载的寄存器,结果存入新寄存器。 + 3. **内存写/读验证:** 使用`ST.W`将已知值存入内存,再用`LD.W`读回并比较。 + 4. **分支逻辑测试:** 分别测试条件成立(跳转)和不成立(不跳转)两种情况。 + 5. **无条件跳转:** 使用`B`指令跳转到特定位置。 + 6. **测试结束标志:** 程序最后应有明确的结束标志,如无限循环或向特定寄存器/内存地址写入“魔法数字”,供测试平台判断。 + +### 4.3 实现自检查测试平台 (Self-Checking Testbench) + +手动观察波形来验证 CPU 功能的做法效率低下且极易出错。一个专业的测试平台(Testbench)应该是自动化的、能够自行判断测试结果的。 + +#### 4.3.1 测试平台架构 + +一个强大的自检查测试平台应包含以下组件: + +- **DUT 实例化:** `cpu uut (...)`。 +- **时钟与复位生成器:** `always #5 clk = ~clk;` 和一个`initial`块来控制复位信号。 +- **指令存储器模型:** 一个`reg`数组,使用`$readmemh("test_program.hex", instruction_memory);`从外部文件加载测试程序的机器码。 +- **数据存储器模型:** 另一个`reg`数组,用于模拟数据存储器。 +- **结果验证逻辑:** 这是自检查的核心。 + +#### 4.3.2 基于“黄金参考模型”的自动化验证 + +自检查的关键在于“与谁比较?”。最强大的方法之一是使用一个“黄金参考模型”。对于本课程设计,这个模型可以简化为对最终寄存器状态的期望值。 + +1. **生成黄金参考:** 手动或通过脚本模拟执行测试程序,计算出程序结束时所有寄存器的 **最终期望值** 。 +2. **存储黄金参考:** 将期望值存储在测试平台的`golden_reg_file`数组中。 +3. **执行与比对:** 仿真结束时,测试平台用`for`循环逐一比较 DUT 内部寄存器值和`golden_reg_file`中的值。 +4. **报告结果:** 如果匹配,打印`"TEST PASSED"`;否则,打印详细错误信息和`"TEST FAILED"`。 + +这种方法将验证过程从主观的波形观察,转变为客观、自动化的**PASS/FAIL**判断,是工业界验证方法学的入门实践。 + +--- + +## 第五部分:仿真、调试与报告撰写 + +这一部分关注于如何利用 EDA 工具(Vivado)进行实际的仿真调试,并总结了设计过程中常见的陷阱以及如何撰写一份高质量的设计报告。 + +### 5.1 Vivado 仿真与波形分析 + +在 Vivado 中运行仿真后,波形查看器是主要的调试工具。为了高效地追踪问题,应重点观察以下关键信号: + +- **时钟与复位:** `clk`, `rst` +- **PC 与指令:** `pc`, `instruction` +- **寄存器写操作:** `reg_write_en`, `write_addr`, `write_data` +- **ALU 操作:** `alu_input_a`, `alu_input_b`, `alu_op`, `alu_result` +- **内存访问:** `mem_address`, `mem_write_data`, `mem_read_data`, `mem_write_en`, `mem_read_en` + +### 5.2 主动调试与常见陷阱规避 + +对于初学者而言,Verilog 的某些特性可能会导致与直觉相悖的结果。了解并规避这些常见陷阱是成功的关键。 + +1. **思维模型错位 (Mental Model Mismatch):** 最根本的错误是把 Verilog 当作一种顺序执行的编程语言。必须牢记: + **Verilog 是硬件描述语言** 。正确的思维方式是:“ **先画出电路图,再用 Verilog 描述它** ”。 +2. **阻塞赋值 (`=`) vs. 非阻塞赋值 (`<=`):** 这是 Verilog 中最常见也最致命的错误之一。 + - **铁律:** + - 在 **时序逻辑** (`always @(posedge clk)`)中,**永远**使用 **非阻塞赋值 (`<=`)** 。 + - 在 **组合逻辑** (`always @(*)`)中,**永远**使用 **阻塞赋值 (`=`)** 。 +3. **意外推断出锁存器 (Inferred Latches):** 在组合逻辑块中,如果某些条件下输出没有被赋值(如`if`缺少`else`,`case`缺少`default`),综合器会推断出锁存器。 + - **解决方法:** 确保在组合逻辑的`always`块中,所有输出信号在所有代码路径中都被赋值,或在块开头赋默认值。 +4. **不完整的敏感列表 (Incomplete Sensitivity List):** 在 Verilog-1995 标准中,遗漏敏感列表中的输入会推断出锁存器。 + - **解决方法:** **始终使用`always @(*)`** (Verilog-2001 标准),它能自动推断敏感列表。 + +### 5.3 进阶调试:Vivado ILA 硬件在线调试 + +仿真环境是理想的,而实际硬件中可能会出现仿真无法复现的问题。Vivado 的 **集成逻辑分析仪 (Integrated Logic Analyzer, ILA)** 核是一个可以植入到设计中的“片上示波器”,允许你在 FPGA 运行时实时捕获内部信号的状态。 + +- **基本流程:** + 1. **标记信号:** 在综合后的网表视图中,右键点击关心的内部信号,选择“Mark Debug”。 + 2. **设置 ILA:** 运行“Set Up Debug”向导,Vivado 会自动创建 ILA 核并连接探针。 + 3. **重新实现与生成比特流:** 包含 ILA 核的新设计需要重新实现并生成`.bit`文件。 + 4. **硬件调试:** 在 Hardware Manager 中连接开发板,下载比特流,然后像使用逻辑分析仪一样设置触发条件并捕获波形。 + +向学生介绍 ILA 的使用,是将其从纯粹的模拟仿真引向了专业的硬件调试领域,极大地提升了其实践技能。 + +### 5.4 命名规范与报告撰写 + +#### 5.4.1 命名规范建议 + +良好的命名规范可以提高代码的可读性和维护性。建议统一使用简洁的英文缩写或单词来命名,并遵循一致的风格(如驼峰式或下划线式)。 + +- **模块命名:** `CpuTop` 或 `cpu_top`。 +- **信号命名:** `pc_next`, `reg_write`, `mem_to_reg`。 +- **常量:** 使用全大写,如 `parameter ALU_ADD = 4'b0000;`。 +- **注释:** 在模块定义和关键逻辑处添加注释,说明信号作用和设计意图。 + +#### 5.4.2 最终课程设计报告结构 + +一份结构清晰、内容详实的报告是展示项目成果的关键。建议报告遵循以下结构: + +1. **引言:** 项目背景、目标、LA32R 架构简介、报告结构概述。 +2. **总体设计描述:** CPU 总体架构、模块划分、数据通路概览图及说明。 +3. **模块设计说明:** + - 分小节详细描述每个模块(PC、寄存器堆、ALU 等)。 + - 为每个模块提供接口表(信号名、位宽、方向、含义)。 + - 描述内部实现原理,可附上关键 Verilog 代码片段和注释。 +4. **数据通路与控制分析:** + - 为五类指令分别绘制简化的数据通路图(可在总图上高亮)。 + - 提供一张完整的控制信号总表,对比所有指令的控制信号取值,并进行分析。 +5. **实现细节和仿真结果:** + - 附上关键模块的核心 Verilog 代码。 + - 展示关键指令执行时的仿真波形图,并结合数据通路进行分析,证明其正确性。尤其要展示分支跳转成功和失败的对比波形。 + - 附上自检查测试平台的最终 PASS/FAIL 测试结果截图和日志。 +6. **结论与展望:** 总结项目完成情况,分析单周期设计的优缺点,并对可能的改进方向(如多周期、流水线)进行展望。 +7. **附录:** 完整的 Verilog 源代码、测试程序的汇编代码和机器码。 + +--- + +## 总结 + +本报告为 LA32R 单周期 CPU 模型机的设计与实现提供了一个系统化、工程化的完整方案。通过融合两种设计思路的精华,本方案不仅详细阐述了从架构分析到模块化实现,再到自动化验证的完整设计流程,还深入探讨了硬件设计的核心思想、常见陷阱、高级调试技术以及专业报告的撰写规范。遵循此综合方案,设计者不仅能够成功构建一个功能正确的 CPU,更能够在此过程中建立起坚实的计算机体系结构知识基础和专业的硬件工程实践能力。 diff --git a/Reference/课程设计指导书.md b/Reference/课程设计指导书.md new file mode 100644 index 0000000..be963c8 --- /dev/null +++ b/Reference/课程设计指导书.md @@ -0,0 +1,41 @@ +# 计算机组成原理 (LoongArch之LA32R版)课程设计指导书 + +## 五、课程设计任务 + +设计并实现LA32R架构的模型机硬件系统。要求在EDA工具(Vivado)仿真环境下完成电路的建模设计、仿真调试与运行。该模型机的指令系统包括以下14条指令,指令的格式如表2所示,指令的功能如表3所示。 + +
表2 指令格式说明
+ + + + + + + + + + + + +
序号 类型指令6位1位3位7位5位5位5位
31 …262524… 2221 2019…1514…109… 54… 0
11RI20LUI12I.W0001010si20rd
23RADD.W00000000000100000rkrjrd
33RSUB.W00000000000100010rkrjrd
43RSLT00000000000100100rkrjrd
53RSLTU00000000000100101rkrjrd
63RNOR00000000000101000rkrjrd
73RAND00000000000101001rkrjrd
83ROR00000000000101010rkrjrd
92RI12ADDI.W0000001010si12rjrd
102RI12LD. W0010100010si12rjrd
112RI12ST.W0010100110si12rjrd
12I26B010100offs[15:0]offs [25:16]
132RI16BEQ010110offs[15:0]rjrd
142RI16BLT011000offs [15:0]rjrd
+ +
表3 指令功能说明
+ + + +
序号类型指令功能说明
11RI20LU12I.W rd,si20GR[rd] ⟵si20 || 12’b0①GR[rd]的高20位为si20,低12位为0
②符号||表示拼接
23RADD.W rd, rj, rkGR[rd]⟵GR[rj]+GR[rk]加法
33RSUB.W rd, rj, rkGR[rd]⟵GR[rj]-GR[rk]减法
43RSLT rd, rj, rkif (GR[rj]<GR[rk]) GR[rd]⟵1 else GR[rd]⟵0带符号数的大小比较
53RSLTU rd, rj, rkif (GR[rj]<GR[rk]) GR[rd]⟵1 else GR[rd]⟵0无符号数的大小比较
63RNOR rd, rj, rkGR[rd]⟵(GR[rj]∨GR[rk]) ̅或非
73RAND rd, rj, rkGR[rd]⟵GR[rj] ∧ GR[rk]
83ROR rd, rj, rkGR[rd]⟵GR[rj] ∨ GR[rk]
92RI12ADDI.W rd, rj, si12GR[rd]⟵GR[rj]+ SignExtend(si12)①立即数加法
②si12是12位立即数,进行符号位扩展后与GR[rj]相加
102RI12LD.W rd ,rj,si12Addr⟵GR[rj] + SignExtend (si12) ,GR[rd] ⟵M[Addr]①将内存Addr单元的值取出后存入R[rd]
②si12是12位立即数,进行符号位扩展后与GR[rj]相加后得到内存单元的地址Addr
112RI12ST.W rd, rj, si12Addr⟵GR[rj] + SignExtend (si12) , M[Addr]⟵GR[rd]①把GR[rd]的值存入内存Addr单元
②内存单元的地址Addr的计算方法与LD.W指令相同
12I26B offs26PC⟵PC+ SignExtend (offs26||2’b0)无条件跳转到目标地址
132RI16BEQ rj, rd, offs16if (GR[rj]=GR[rd])
PC⟵PC+ SignExtend (offs16||2’b0)
①符号||表示拼接
②GR[rj]=GR[rd]时,跳转到目标地址
142RI16BLT rj, rd, offs16if (signed(GR[rj])<signed(GR[rd]))
PC⟵PC+ SignExtend (offs16||2’b0)
①符号||表示拼接
②GR[rj]与GR[rd]视作带符号数
③GR[rj]<GR[rd]时,跳转到目标地址
+ +参考逻辑电路图如下。设计方案没有唯一答案,下图所示电路仅供参考。 + +![LA32R参考逻辑电路图](./支持5种类型共计14条指令的单周期LA32R%20CPU逻辑电路图.png) + +
图1 支持5种类型共计14条指令的单周期LA32R CPU逻辑电路图
+ +## 六、课程设计步骤 + +1. 对指令格式和功能进行分析,绘制不同类型指令的数据通路; +2. 分析各类型指令执行阶段控制信号,绘制控制信号取值分析表; +3. 设计总框图,进行各逻辑部件之间的互相连接,完成模型机顶层模块的设计,使得由指令系统所要求的数据通路都能实现; +4. 编写机器语言应用程序(测试程序),存入模型机指令存储器; +5. 编写模型机顶层模块的仿真激励文件; +6. 运行仿真,分析仿真结果,撰写设计报告。 diff --git a/Software/assembler.py b/Software/assembler.py new file mode 100644 index 0000000..39b8731 --- /dev/null +++ b/Software/assembler.py @@ -0,0 +1,130 @@ +# -*- coding: utf-8 -*- +import re + +""" +LA32R Simple Assembler +- Converts a simplified assembly language into 32-bit hexadecimal machine code. +- Supports all 14 instructions required by the course design. +- Handles register names like '$r4', '$r12' and immediate values. +- Outputs a 'program.hex' file suitable for Verilog's $readmemh. +""" + +# 指令编码定义 +# Instruction encoding definitions +OPCODES = { + 'add.w': '000000', 'sub.w': '000000', 'slt': '000000', 'sltu': '000000', + 'nor': '000000', 'and': '000000', 'or': '000000', + 'addi.w': '000010', 'lu12i.w': '000101', 'ld.w': '001010', 'st.w': '001010', + 'b': '010100', 'beq': '010110', 'blt': '011000' +} + +FUNC_3R = { + # 'opcode': ('func2', 'func5') + 'add.w': ('01', '00000'), 'sub.w': ('01', '00010'), 'slt': ('01', '00100'), + 'sltu': ('01', '00101'), 'nor': ('01', '01000'), 'and': ('01', '01001'), + 'or': ('01', '01010') +} + +FUNC_2RI12 = { + # 'opcode': 'func4' + 'addi.w': '1010', + 'ld.w': '0010', + 'st.w': '0110' +} + + +def to_binary(value, bits): + """Converts an integer to a two's complement binary string of specified length.""" + if value >= 0: + return format(value, 'b').zfill(bits) + else: + return format((1 << bits) + value, 'b') + + +def parse_register(reg_str): + """Parses register string '$rX' to a 5-bit binary string.""" + return to_binary(int(reg_str.strip('$r')), 5) + + +def assemble_line(line): + """Assembles a single line of assembly code into a 32-bit binary string.""" + line = line.lower().strip() + parts = re.split(r'[\s,]+', line) + op = parts[0] + + if op in FUNC_3R: # 3R-type: add.w $r3, $r1, $r2 + rd, rj, rk = parse_register(parts[1]), parse_register(parts[2]), parse_register(parts[3]) + opcode = OPCODES[op] + func2, func5 = FUNC_3R[op] + return f"{opcode}0000{func2}{func5}{rk}{rj}{rd}" + + elif op in ['addi.w', 'ld.w', 'st.w']: # 2RI12-type + rd = parse_register(parts[1]) + rj = parse_register(parts[2]) + # FIX: Use int(str, 0) to auto-detect base (e.g., '0x' for hex) + imm = to_binary(int(parts[3], 0), 12) + opcode = OPCODES[op] + func4 = FUNC_2RI12[op] + return f"{opcode}{func4}{imm}{rj}{rd}" + + elif op == 'lu12i.w': # 1RI20-type: lu12i.w $r1, 0x12345 + rd = parse_register(parts[1]) + imm = to_binary(int(parts[2], 0), 20) # Support hex (0x) and dec + opcode = OPCODES[op] + return f"{opcode}0{imm}{rd}" + + elif op in ['beq', 'blt']: # 2RI16-type: beq $r1, $r2, -4 + rj = parse_register(parts[1]) + rd = parse_register(parts[2]) + # FIX: Use int(str, 0) to auto-detect base + offset = int(parts[3], 0) >> 2 # Offset is in bytes, convert to word offset for encoding + imm = to_binary(offset, 16) + opcode = OPCODES[op] + return f"{opcode}{imm}{rj}{rd}" + + elif op == 'b': # I26-type: b -8 + # FIX: Use int(str, 0) to auto-detect base + offset = int(parts[1], 0) >> 2 # Convert to word offset + imm = to_binary(offset, 26) + offs_25_16 = imm[0:10] + offs_15_0 = imm[10:26] + opcode = OPCODES[op] + return f"{opcode}{offs_15_0}{offs_25_16}" + + else: + raise ValueError(f"Unknown instruction: {op}") + + +def main(): + """Main function to read assembly file and write hex file.""" + # 使用这个测试程序来验证CPU的功能 + # Use this test program to verify CPU functionality + assembly_code = open("../program.asm", "r").read() + + output_filename = "program.hex" + with open(output_filename, "w") as f: + print(f"Assembling code into {output_filename}...") + for line in assembly_code.split('\n'): + line = line.strip() + # 忽略注释和空行 + # Ignore comments and empty lines + if not line or line.startswith('//'): + continue + + try: + # 移除行内注释 + # Remove inline comments + line = line.split('//')[0].strip() + binary_code = assemble_line(line) + hex_code = f"{int(binary_code, 2):08x}" + f.write(hex_code + '\n') + print(f" {line:<30} -> {hex_code}") + except Exception as e: + print(f"Error assembling line: '{line}'") + print(f" > {e}") + return + print("Assembly finished successfully.") + + +if __name__ == "__main__": + main() diff --git a/Software/program.hex b/Software/program.hex new file mode 100644 index 0000000..eece3ec --- /dev/null +++ b/Software/program.hex @@ -0,0 +1,21 @@ +14246004 +0a915884 +15110005 +0abffca5 +00101486 +00111487 +00121488 +00129489 +0014948a +0015148b +00142d4c +0a819001 +29800024 +29801025 +2880002d +2880102e +580009a4 +0a80040f +600008e6 +0a800410 +53ffffff diff --git a/program.asm b/program.asm new file mode 100644 index 0000000..4996901 --- /dev/null +++ b/program.asm @@ -0,0 +1,35 @@ +// 1. 寄存器加载测试 (Register Loading Test) +lu12i.w $r4, 0x12300 // r4 = 0x12300000 +addi.w $r4, $r4, 0x456 // r4 = 0x12300456 +lu12i.w $r5, 0x88800 // r5 = 0x88800000 (negative high part) +addi.w $r5, $r5, -1 // r5 = 0x887FFFFF + +// 2. 算术/逻辑运算测试 (Arithmetic/Logic Test) +add.w $r6, $r4, $r5 // r6 = 0x12300456 + 0x887FFFFF = 0x9AB00455 +sub.w $r7, $r4, $r5 // r7 = 0x12300456 - 0x887FFFFF = 0x89B00457 +slt $r8, $r4, $r5 // r4 > r5 (signed), r8 = 0 +sltu $r9, $r4, $r5 // r4 < r5 (unsigned), r9 = 1 +and $r10, $r4, $r5 // r10 = 0x12300456 & 0x887FFFFF = 0x00300456 +or $r11, $r4, $r5 // r11 = 0x12300456 | 0x887FFFFF = 0x9A7FFFFF +nor $r12, $r10, $r11 // r12 = ~(r10 | r11) = ~0x9A7FFFFF = 0x65800000 + +// 3. 内存访问测试 (Memory Access Test) +addi.w $r1, $r0, 100 // r1 = 100 (Address base) +st.w $r4, $r1, 0 // M[100] = r4 (0x12300456) +st.w $r5, $r1, 4 // M[104] = r5 (0x887FFFFF) +ld.w $r13, $r1, 0 // r13 = M[100] +ld.w $r14, $r1, 4 // r14 = M[104] + +// 4. 分支指令测试 (Branching Test) +// BEQ: r13 == r4 is true, should jump +beq $r13, $r4, 8 // Branch to 'skip' label (PC+8) +addi.w $r15, $r0, 1 // This should NOT be executed +// 'skip:' label is here +// BLT: r7 is negative, r6 is positive, r7 < r6 is true, should jump +blt $r7, $r6, 8 // Branch to 'end' label (PC+8) +addi.w $r16, $r0, 1 // This should NOT be executed + +// 'end:' label is here +// 5. 无条件跳转: 创建一个无限循环来结束程序 +// Unconditional Jump: Create an infinite loop to end the program +b -4 // Infinite loop: jump to itself \ No newline at end of file diff --git a/run.bat b/run.bat new file mode 100644 index 0000000..fbbb91a --- /dev/null +++ b/run.bat @@ -0,0 +1,108 @@ +@echo off +:: 设置窗口标题 +TITLE LA32R CPU Simulation Automation + +:: ============================================================================ +:: LA32R 单周期 CPU 自动化仿真脚本 (适配Vivado项目结构) +:: +:: 功能: +:: 1. 定义项目文件路径。 +:: 2. 在正确的仿真目录下清理、执行所有步骤。 +:: 3. 运行 Python 汇编器生成 program.hex (已修正工作目录)。 +:: 4. 编译、链接并运行仿真 (已修正脚本提前退出的问题)。 +:: +:: 使用方法: +:: - 将此脚本放在项目的根目录下。 +:: - 确保 Vivado 的 bin 目录已添加到系统的 PATH 环境变量中。 +:: - 直接双击运行此脚本。 +:: ============================================================================ + +:: --- 步骤 0: 定义项目路径 --- +:: %~dp0 会获取脚本所在的目录,作为我们的项目根目录 +set "PROJECT_ROOT=%~dp0" +set "SOFTWARE_DIR=%PROJECT_ROOT%Software" +set "DESIGN_SRC_DIR=%PROJECT_ROOT%Hardware\LA32R.srcs\sources_1\new" +set "SIM_SRC_DIR=%PROJECT_ROOT%Hardware\LA32R.srcs\sim_1\new" +set "SIM_RUN_DIR=%PROJECT_ROOT%Hardware\LA32R.sim\sim_1\behav\xsim" + +echo Project Root: %PROJECT_ROOT% +echo Simulation Run Directory: %SIM_RUN_DIR% +echo. + +:: --- 准备工作:切换到仿真运行目录 --- +:: 创建目录(如果不存在)并进入 +if not exist "%SIM_RUN_DIR%" ( mkdir "%SIM_RUN_DIR%" ) +cd /d "%SIM_RUN_DIR%" + +:: --- 步骤 1: 清理环境 --- +echo [STEP 1] Cleaning up previous simulation files... +if exist xsim.dir ( rd /s /q xsim.dir ) +if exist *.log ( del *.log ) +if exist *.jou ( del *.jou ) +if exist verilog_files.f ( del verilog_files.f ) +if exist webtalk*.xml ( del webtalk*.xml ) +if exist webtalk*.tcl ( del webtalk*.tcl ) +echo. + +:: --- 步骤 2: 运行Python汇编器 --- +echo [STEP 2] Assembling test program... +:: [FIX] 使用 pushd/popd 临时切换目录以保证python脚本的工作目录正确 +pushd "%SOFTWARE_DIR%" +python assembler.py +popd + +:: 检查 program.hex 是否成功生成 +if not exist "%SOFTWARE_DIR%\program.hex" ( + echo [ERROR] Failed to generate program.hex. Halting script. + goto end +) +:: [FIX] 移除文件复制步骤,因为Verilog已配置为使用相对路径 +echo Assembly complete. Verilog will read program.hex from its relative path. +echo. + +:: --- 步骤 3: 创建文件列表并编译Verilog --- +echo [STEP 3] Creating file list and compiling Verilog sources... +:: 创建一个文件列表,包含所有设计和仿真源文件的绝对路径 +( + echo "%DESIGN_SRC_DIR%\data_memory.v" + echo "%DESIGN_SRC_DIR%\instruction_memory.v" + echo "%DESIGN_SRC_DIR%\alu.v" + echo "%DESIGN_SRC_DIR%\pc.v" + echo "%DESIGN_SRC_DIR%\imm_extender.v" + echo "%DESIGN_SRC_DIR%\register_file.v" + echo "%DESIGN_SRC_DIR%\control_unit.v" + echo "%DESIGN_SRC_DIR%\cpu_top.v" + echo "%SIM_SRC_DIR%\cpu_tb.v" +) > verilog_files.f + +:: [FIX] 添加 'call' 命令确保执行后控制权返回脚本 +call xvlog -sv --work xil_defaultlib -f verilog_files.f +if %errorlevel% neq 0 ( + echo [ERROR] Verilog compilation failed. Check xvlog.log for details. + goto end +) +echo Verilog compilation successful. +echo. + +:: --- 步骤 4: 链接和构建仿真快照 --- +echo [STEP 4] Elaborating the design with xelab... +:: [FIX] 添加 'call' 命令 +call xelab --debug typical --snapshot cpu_tb_snapshot xil_defaultlib.cpu_tb -log elaborate.log +if %errorlevel% neq 0 ( + echo [ERROR] Design elaboration failed. Check elaborate.log for details. + goto end +) +echo Design elaboration successful. +echo. + +:: --- 步骤 5: 运行仿真 --- +echo [STEP 5] Running simulation with xsim... +echo ======================= SIMULATION OUTPUT START ======================= +:: [FIX] 添加 'call' 命令 +call xsim cpu_tb_snapshot --runall --log ..\..\..\..\..\simulation.log +echo ======================== SIMULATION OUTPUT END ======================== +echo. + +:end +echo Script finished. Press any key to exit. +pause > nul