Refactor: Update comments to Chinese and standardize.
This commit updates all comments in Verilog source files, the Python assembler, and the batch run script to Chinese. Key changes include: - Verilog files (.v): - Header comments (Description, Features, Revision, Additional Comments) translated and updated. - Inline comments translated to Chinese. - Removed [FIX], [NEW] tags, incorporating relevant information into the comments. - assembler.py: - Main docstring and inline comments translated to Chinese. - Removed [FIX] style tags, with details moved to a revision history in the docstring. - Ensured no functional code changes were made. - run.bat: - All user-facing `echo` prompts translated to Chinese. - Comments (::) reviewed and standardized in Chinese. The codebase's logic and functionality remain unchanged. This effort improves readability and maintainability for Chinese-speaking developers by providing comprehensive and standardized documentation directly within the code. Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
This commit is contained in:
@@ -11,89 +11,90 @@
|
||||
** 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.
|
||||
**
|
||||
** 本文件是为LA32R单周期CPU设计的一个自校验测试平台 (testbench)。
|
||||
** 主要功能包括:
|
||||
** - 生成时钟 (clk) 和复位 (rst) 信号。
|
||||
** - 实例化顶层CPU模块 (cpu_top)。
|
||||
** - CPU内部的指令存储器模块将从 "program.hex" 文件加载机器码。
|
||||
** - 监控并显示程序计数器 (PC) 和寄存器堆的状态,以供分析。
|
||||
** Features:
|
||||
** - 产生周期性的时钟信号。
|
||||
** - 提供可控的复位序列,用于初始化CPU。
|
||||
** - 实例化待测试的CPU顶层模块 (uut)。
|
||||
** - 通过预设的仿真时间控制仿真流程。
|
||||
** - 在仿真结束时,打印所有非零寄存器的最终状态。
|
||||
** - 在每个时钟周期的下降沿(确保信号稳定后)显示关键信号,如PC值、当前指令和特定寄存器的值。
|
||||
** Revision:
|
||||
** Revision 0.01 - File Created
|
||||
** Revision 0.01 - 文件创建及基本测试流程实现。
|
||||
** Additional Comments:
|
||||
** - Place this file and "program.hex" in the simulation directory.
|
||||
**
|
||||
** - 请确保此测试平台文件和包含机器码的 "program.hex" 文件位于Vivado仿真工作目录下。
|
||||
** - "program.hex" 的路径在 `instruction_memory.v` 模块中指定。
|
||||
*******************************************************************************/
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
module cpu_tb;
|
||||
|
||||
// --- 信号声明 (Signal Declarations) ---
|
||||
reg clk;
|
||||
reg rst;
|
||||
// --- 信号声明 ---
|
||||
reg clk; // 时钟信号
|
||||
reg rst; // 复位信号
|
||||
|
||||
// --- 实例化待测设计 (Instantiate the Design Under Test) ---
|
||||
// --- 实例化待测设计 (DUT - Design Under Test) ---
|
||||
// 将cpu_top模块实例化为uut (unit under test)
|
||||
cpu_top uut (
|
||||
.clk(clk),
|
||||
.rst(rst)
|
||||
);
|
||||
|
||||
// --- 时钟生成器 (Clock Generator) ---
|
||||
localparam CLK_PERIOD = 10; // 时钟周期为10ns (Clock period is 10ns)
|
||||
// --- 时钟生成逻辑 ---
|
||||
localparam CLK_PERIOD = 10; // 定义时钟周期为10纳秒
|
||||
initial begin
|
||||
clk = 0;
|
||||
forever #(CLK_PERIOD / 2) clk = ~clk;
|
||||
clk = 0; // 初始化时钟为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;
|
||||
// 1. 初始化并施加复位信号
|
||||
rst = 1; // 断言复位信号
|
||||
#(CLK_PERIOD * 2); // 保持复位状态持续2个时钟周期,以确保CPU完全复位
|
||||
rst = 0; // 撤销复位信号,CPU开始正常执行
|
||||
$display("------------------------------------------------------------");
|
||||
$display(" CPU Simulation Started. Reset is released. ");
|
||||
$display(" CPU仿真开始。复位信号已释放。 ");
|
||||
$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)
|
||||
// 2. 设定仿真运行时间后停止
|
||||
// 由于测试程序末尾通常是无限循环,因此需要手动设置仿真停止时间。
|
||||
#500; // 仿真运行500纳秒
|
||||
|
||||
// 3. 打印最终的寄存器状态
|
||||
// Print the final state of the registers
|
||||
// 3. 仿真结束前,打印寄存器堆的最终状态
|
||||
$display("\n------------------------------------------------------------");
|
||||
$display(" Simulation Finished. Final Register State: ");
|
||||
$display(" 仿真结束。最终寄存器状态如下: ");
|
||||
$display("------------------------------------------------------------");
|
||||
// 使用$display来显示寄存器的值。注意路径需要正确。
|
||||
// Use $display to show register values. Note the path must be correct.
|
||||
// 使用循环遍历寄存器堆,并通过$display显示其值。注意hierarchical path的正确性。
|
||||
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]);
|
||||
$display("寄存器 R%0d: 0x%08h", i, uut.u_reg_file.registers[i]);
|
||||
end
|
||||
end
|
||||
$display("※Please note that registers with value zero are hidden.※");
|
||||
$display("※请注意:值为零的寄存器已被隐藏,未在此处显示。※");
|
||||
$display("------------------------------------------------------------");
|
||||
|
||||
$finish; // 结束仿真 (End simulation)
|
||||
$finish; // 调用$finish系统任务来结束仿真过程
|
||||
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]
|
||||
if (!rst) begin //仅在非复位状态下显示
|
||||
$display("时间: %0t ns | PC: 0x%08h | 指令: 0x%08h | R4=0x%h R5=0x%h R6=0x%h",
|
||||
$time, // 当前仿真时间
|
||||
uut.pc_out, // PC的当前值
|
||||
uut.instr, // 当前PC指向的指令
|
||||
uut.u_reg_file.registers[4], // R4寄存器的值
|
||||
uut.u_reg_file.registers[5], // R5寄存器的值
|
||||
uut.u_reg_file.registers[6] // R6寄存器的值
|
||||
);
|
||||
end
|
||||
end
|
||||
|
||||
@@ -11,70 +11,66 @@
|
||||
** 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.
|
||||
**
|
||||
** 本模块为LA32R CPU实现算术逻辑单元(ALU)。
|
||||
** 它负责执行CPU指令所规定的算术运算(如加法、减法)和逻辑运算(如与、或、或非)。
|
||||
** 同时,它也处理比较操作(如有符号小于、无符号小于)并产生相应的标志位。
|
||||
** Features:
|
||||
** - 支持加法、减法、与、或、或非等基本运算。
|
||||
** - 支持有符号小于比较 (SLT) 和无符号小于比较 (SLTU)。
|
||||
** - SLT 操作使用 $signed() 以确保对有符号数的正确比较。
|
||||
** - 输出运算结果以及零标志位和小于标志位。
|
||||
** Revision:
|
||||
** Revision 0.02 - Corrected SLT implementation using $signed().
|
||||
** Revision 0.01 - File Created
|
||||
**
|
||||
** Revision 0.02 - 修正了SLT指令的实现,使用$signed()处理有符号比较,确保结果的正确性。
|
||||
** Revision 0.01 - 文件创建。
|
||||
** Additional Comments:
|
||||
** 无。
|
||||
*******************************************************************************/
|
||||
|
||||
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)
|
||||
input wire [31:0] a, // 操作数A
|
||||
input wire [31:0] b, // 操作数B
|
||||
input wire [3:0] alu_op, // ALU操作控制信号
|
||||
output reg [31:0] result, // ALU运算结果
|
||||
output wire zero, // 零标志位,用于判断结果是否为零
|
||||
output wire lt // 小于标志位,用于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;
|
||||
// 定义ALU支持的各种操作码,使用localparam增强代码的可读性和可维护性
|
||||
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
|
||||
// 执行减法操作,结果存储在临时线网sub_result中
|
||||
wire [31:0] sub_result = a - b;
|
||||
|
||||
// [FIX] 使用$signed()进行稳健的有符号比较
|
||||
// [FIX] Use $signed() for robust signed comparison
|
||||
// 执行有符号小于比较,使用$signed()确保对负数的正确处理
|
||||
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
|
||||
// ALU核心逻辑:根据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)
|
||||
ALU_ADD: result = a + b; // 执行加法
|
||||
ALU_SUB: result = sub_result; // 执行减法
|
||||
ALU_AND: result = a & b; // 执行逻辑与
|
||||
ALU_OR: result = a | b; // 执行逻辑或
|
||||
ALU_NOR: result = ~(a | b); // 执行逻辑或非
|
||||
ALU_SLT: result = {31'b0, slt_res}; // 执行有符号小于比较,结果为0或1
|
||||
ALU_SLTU: result = {31'b0, sltu_res}; // 执行无符号小于比较,结果为0或1
|
||||
default: result = 32'hxxxxxxxx; // 默认行为:当alu_op无效时,输出不确定值
|
||||
endcase
|
||||
end
|
||||
|
||||
// 零标志位输出: 当减法结果为0时置1,用于BEQ指令
|
||||
// Zero flag output: set to 1 when the subtraction result is zero, for BEQ instruction
|
||||
// 零标志位(zero flag)的生成逻辑:当减法结果(通常用于比较指令)为全零时,zero置1
|
||||
assign zero = (sub_result == 32'h00000000);
|
||||
|
||||
// 小于标志位输出: 用于BLT指令
|
||||
// Less Than flag output: for BLT instruction
|
||||
// 小于标志位(less than flag)的生成逻辑:直接使用有符号比较的结果slt_res
|
||||
assign lt = slt_res;
|
||||
|
||||
endmodule
|
||||
@@ -11,118 +11,150 @@
|
||||
** 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.
|
||||
** [FIXED] ADDI.W decoding now correctly uses opcode 000000 + func4.
|
||||
** [FIXED] Added new control signal 'ALUAsrc' to force ALU's A-operand to 0
|
||||
** for LUI12I.W instruction.
|
||||
** [FINAL FIX] Corrected the logic within the opcode '000000' group.
|
||||
** The previous version incorrectly evaluated func fields, causing 3R-type
|
||||
** instructions to fail after the ADDI.W fix. This version ensures both
|
||||
** ADDI.W and all 3R instructions are decoded correctly according to the ISA.
|
||||
**
|
||||
** 本模块是LA32R单周期CPU的主控制单元。它负责对指令的操作码(opcode)和功能码(func)字段
|
||||
** 进行译码,并据此生成数据通路所需的所有控制信号。
|
||||
** 该单元能够正确处理具有相同主操作码的指令(如LD.W和ST.W),
|
||||
** 以及特殊指令(如ADDI.W, LUI12I.W)和所有3R类型指令的译码。
|
||||
** Features:
|
||||
** - 译码LA32R指令集中的所有指令。
|
||||
** - 生成寄存器写使能(reg_write_en)、存储器到寄存器(mem_to_reg)、存储器写使能(mem_write_en)等控制信号。
|
||||
** - 控制ALU的操作(alu_op)和操作数来源(alu_src, alu_asrc)。
|
||||
** - 控制立即数扩展器的操作类型(ext_op)。
|
||||
** - 根据指令类型和ALU标志(zero_flag, lt_flag)确定程序计数器(PC)的下一个来源(pcsource)。
|
||||
** - 为LUI12I.W指令提供特殊的ALU第一操作数选择信号(alu_asrc)。
|
||||
** Revision:
|
||||
** Revision 0.04 - Fixed logic for 3R-type instructions.
|
||||
** Revision 0.03 - Fixed ADDI.W decoding and shared opcode logic.
|
||||
** Revision 0.02 - Corrected case statement logic for shared opcodes.
|
||||
** Revision 0.01 - File Created
|
||||
**
|
||||
** Revision 0.04 - 修正了3R类型指令的译码逻辑,确保与ADDI.W指令的区分和正确执行。
|
||||
** Revision 0.03 - 修正了ADDI.W的译码逻辑,并处理了共享操作码指令的译码问题。引入alu_asrc信号。
|
||||
** Revision 0.02 - 调整了case语句逻辑以正确处理共享操作码。
|
||||
** Revision 0.01 - 文件创建。
|
||||
** Additional Comments:
|
||||
** 控制信号的默认值在always块的开始处设置,以确保在未指定时信号处于安全状态。
|
||||
*******************************************************************************/
|
||||
|
||||
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)
|
||||
input wire [31:0] instr, // 输入的32位指令
|
||||
input wire zero_flag, // ALU运算结果的零标志位输入
|
||||
input wire lt_flag, // ALU运算结果的小于标志位输入 (用于BLT)
|
||||
|
||||
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 reg alu_asrc, // [NEW] 选择ALU第一操作数源 (Selects ALU's first operand source)
|
||||
output wire pcsource // PC下一个地址来源选择 (PC next address source selection)
|
||||
output reg reg_write_en, // 寄存器文件写使能信号
|
||||
output reg mem_to_reg, // 数据选择信号:选择写入寄存器的数据来源 (0: ALU结果, 1: 存储器数据)
|
||||
output reg mem_write_en, // 数据存储器写使能信号
|
||||
output reg alu_src, // 数据选择信号:选择ALU的B操作数来源 (0: 寄存器, 1: 立即数)
|
||||
output reg src_reg, // 数据选择信号:选择寄存器堆的第二个读地址来源 (0: instr[19:15], 1: instr[24:20])
|
||||
output reg [2:0] ext_op, // 立即数扩展单元操作控制信号
|
||||
output reg [3:0] alu_op, // ALU操作类型控制信号
|
||||
output reg alu_asrc, // 数据选择信号:选择ALU的A操作数来源 (0: 寄存器, 1: PC / 0 for LUI12I)
|
||||
output wire pcsource // PC下一个地址来源选择信号 (0: PC+4, 1: 分支/跳转目标地址)
|
||||
);
|
||||
|
||||
// 从指令中提取操作码字段
|
||||
wire [5:0] opcode = instr[31:26];
|
||||
|
||||
// -- 功能码字段 --
|
||||
wire [3:0] func_2ri12 = instr[25:22]; // For ADDI.W, LD.W, ST.W
|
||||
wire [1:0] func_3r_f2 = instr[21:20]; // For 3R-type
|
||||
wire [4:0] func_3r_f5 = instr[19:15]; // For 3R-type
|
||||
// -- 从指令中提取功能码字段 --
|
||||
wire [3:0] func_2ri12 = instr[25:22]; // 用于 ADDI.W, LD.W, ST.W 等指令类型
|
||||
wire [1:0] func_3r_f2 = instr[21:20]; // 用于 3R 类型指令的辅助功能码
|
||||
wire [4:0] func_3r_f5 = instr[19:15]; // 用于 3R 类型指令的主要功能码
|
||||
|
||||
// -- 操作码定义 --
|
||||
localparam OP_GROUP_00 = 6'b000000; // Contains 3R & ADDI.W
|
||||
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;
|
||||
// -- LA32R指令集操作码定义 (部分) --
|
||||
localparam OP_GROUP_00 = 6'b000000; // 包含3R类型指令 (如ADD, SUB) 和 ADDI.W 指令
|
||||
localparam OP_LUI12I = 6'b000101; // LUI12I.W 指令的操作码
|
||||
localparam OP_GROUP_0A = 6'b001010; // 包含 LD.W 和 ST.W 指令
|
||||
localparam OP_B = 6'b010100; // 无条件分支 B 指令
|
||||
localparam OP_BEQ = 6'b010110; // 条件分支 BEQ 指令 (相等则跳转)
|
||||
localparam OP_BLT = 6'b011000; // 条件分支 BLT 指令 (有符号小于则跳转)
|
||||
|
||||
// -- ALU操作控制码定义 (与alu模块一致) --
|
||||
localparam ALU_ADD = 4'b0000, ALU_SUB = 4'b0001, ALU_AND = 4'b0010,
|
||||
ALU_OR = 4'b0011, ALU_NOR = 4'b0100, ALU_SLT = 4'b0101,
|
||||
ALU_SLTU= 4'b0110;
|
||||
|
||||
localparam EXT_SI12 = 3'b001, EXT_SI16 = 3'b010, EXT_UI20 = 3'b011, EXT_SI26 = 3'b100;
|
||||
// -- 立即数扩展操作类型定义 --
|
||||
localparam EXT_SI12 = 3'b001, EXT_SI16 = 3'b010, EXT_UI20 = 3'b011, EXT_SI26 = 3'b100; // SI:有符号, UI:无符号
|
||||
|
||||
// 主控制逻辑:根据操作码和功能码生成所有控制信号
|
||||
always @(*) begin
|
||||
// -- 默认值 --
|
||||
reg_write_en = 1'b0; mem_to_reg = 1'b0; mem_write_en = 1'b0;
|
||||
alu_src = 1'b0; src_reg = 1'b0; alu_asrc = 1'b0;
|
||||
ext_op = 3'bxxx; alu_op = 4'bxxxx;
|
||||
// -- 控制信号默认值设定 --
|
||||
reg_write_en = 1'b0; // 默认不写入寄存器
|
||||
mem_to_reg = 1'b0; // 默认ALU结果写入寄存器
|
||||
mem_write_en = 1'b0; // 默认不写入存储器
|
||||
alu_src = 1'b0; // 默认ALU第二操作数来自寄存器
|
||||
src_reg = 1'b0; // 默认寄存器堆第二读地址来自instr[19:15] (rd)
|
||||
alu_asrc = 1'b0; // 默认ALU第一操作数来自寄存器
|
||||
ext_op = 3'bxxx; // 默认立即数扩展操作无效
|
||||
alu_op = 4'bxxxx; // 默认ALU操作无效
|
||||
|
||||
case (opcode)
|
||||
OP_GROUP_00: begin
|
||||
// 3R 和 ADDI.W 共享主操作码 000000
|
||||
if (func_2ri12 == 4'b1010) begin // ADDI.W
|
||||
reg_write_en = 1'b1;
|
||||
alu_src = 1'b1;
|
||||
ext_op = EXT_SI12;
|
||||
alu_op = ALU_ADD;
|
||||
OP_GROUP_00: begin // 处理主操作码为 000000 的指令 (3R类型 和 ADDI.W)
|
||||
// 3R 和 ADDI.W 指令共享此主操作码,需通过功能码进一步区分
|
||||
if (func_2ri12 == 4'b1010) begin // ADDI.W 指令 (instr[25:22] == 4'b1010)
|
||||
reg_write_en = 1'b1; // 需要写回寄存器
|
||||
alu_src = 1'b1; // ALU第二操作数为立即数
|
||||
ext_op = EXT_SI12; // 12位有符号立即数扩展
|
||||
alu_op = ALU_ADD; // ALU执行加法
|
||||
end
|
||||
// 对于3R指令, func_2ri12 ([25:22]) 字段为 '0000'
|
||||
// 并且 func_3r_f2 ([21:20]) 字段为 '01'
|
||||
else if (instr[25:22] == 4'b0000 && func_3r_f2 == 2'b01) begin // 3R-type
|
||||
reg_write_en = 1'b1;
|
||||
alu_src = 1'b0;
|
||||
src_reg = 1'b0;
|
||||
case(func_3r_f5)
|
||||
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;
|
||||
// 对于3R类型指令, instr[25:22] (func_2ri12) 应为 '0000'
|
||||
// 且 instr[21:20] (func_3r_f2) 应为 '01'
|
||||
else if (instr[25:22] == 4'b0000 && func_3r_f2 == 2'b01) begin // 3R类型指令
|
||||
reg_write_en = 1'b1; // 需要写回寄存器
|
||||
alu_src = 1'b0; // ALU第二操作数来自寄存器
|
||||
src_reg = 1'b0; // 寄存器堆第二读地址来自instr[19:15] (源操作数2)
|
||||
case(func_3r_f5) // 根据 instr[19:15] (func_3r_f5) 决定具体ALU操作
|
||||
5'b00000: alu_op = ALU_ADD; // ADD
|
||||
5'b00010: alu_op = ALU_SUB; // SUB
|
||||
5'b00100: alu_op = ALU_SLT; // SLT
|
||||
5'b00101: alu_op = ALU_SLTU; // SLTU
|
||||
5'b01000: alu_op = ALU_NOR; // NOR
|
||||
5'b01001: alu_op = ALU_AND; // AND
|
||||
5'b01010: alu_op = ALU_OR; // OR
|
||||
default: alu_op = 4'bxxxx; // 未定义功能码则ALU操作无效
|
||||
endcase
|
||||
end
|
||||
end
|
||||
OP_LUI12I: begin
|
||||
reg_write_en = 1'b1; alu_src = 1'b1; alu_asrc = 1'b1;
|
||||
ext_op = EXT_UI20; alu_op = ALU_ADD;
|
||||
OP_LUI12I: begin // LUI12I.W 指令 (高12位立即数加载)
|
||||
reg_write_en = 1'b1; // 需要写回寄存器
|
||||
alu_src = 1'b1; // ALU第二操作数为立即数
|
||||
alu_asrc = 1'b1; // ALU第一操作数特殊处理 (对于LUI,通常是将立即数左移,另一输入为0)
|
||||
ext_op = EXT_UI20; // 20位无符号立即数扩展 (实际使用高12位)
|
||||
alu_op = ALU_ADD; // ALU执行加法 (0 + 扩展后的立即数)
|
||||
end
|
||||
OP_GROUP_0A: begin
|
||||
if (func_2ri12 == 4'b0010) begin // LD.W
|
||||
reg_write_en = 1'b1; mem_to_reg = 1'b1;
|
||||
alu_src = 1'b1; ext_op = EXT_SI12; alu_op = ALU_ADD;
|
||||
OP_GROUP_0A: begin // 处理主操作码为 001010 的指令 (LD.W 和 ST.W)
|
||||
if (func_2ri12 == 4'b0010) begin // LD.W 指令 (加载字)
|
||||
reg_write_en = 1'b1; // 需要写回寄存器
|
||||
mem_to_reg = 1'b1; // 数据来自存储器
|
||||
alu_src = 1'b1; // ALU第二操作数为立即数 (地址偏移)
|
||||
ext_op = EXT_SI12; // 12位有符号立即数扩展 (地址偏移)
|
||||
alu_op = ALU_ADD; // ALU计算基地址+偏移
|
||||
end
|
||||
else if (func_2ri12 == 4'b0110) begin // ST.W
|
||||
mem_write_en = 1'b1; alu_src = 1'b1; src_reg = 1'b1;
|
||||
ext_op = EXT_SI12; alu_op = ALU_ADD;
|
||||
else if (func_2ri12 == 4'b0110) begin // ST.W 指令 (存储字)
|
||||
mem_write_en = 1'b1; // 需要写入存储器
|
||||
alu_src = 1'b1; // ALU第二操作数为立即数 (地址偏移)
|
||||
src_reg = 1'b1; // 寄存器堆第二读地址来自instr[24:20] (源数据寄存器)
|
||||
ext_op = EXT_SI12; // 12位有符号立即数扩展 (地址偏移)
|
||||
alu_op = ALU_ADD; // ALU计算基地址+偏移
|
||||
end
|
||||
end
|
||||
OP_B: ext_op = EXT_SI26;
|
||||
OP_BEQ: begin alu_src = 1'b0; src_reg = 1'b1; ext_op = EXT_SI16; alu_op = ALU_SUB; end
|
||||
OP_BLT: begin alu_src = 1'b0; src_reg = 1'b1; ext_op = EXT_SI16; alu_op = ALU_SUB; end
|
||||
default: begin end
|
||||
OP_B: ext_op = EXT_SI26; // 无条件分支指令,设置26位有符号立即数扩展
|
||||
OP_BEQ: begin // 相等则分支指令
|
||||
alu_src = 1'b0; // ALU比较两个寄存器的值
|
||||
src_reg = 1'b1; // 寄存器堆第二读地址来自instr[24:20]
|
||||
ext_op = EXT_SI16; // 16位有符号立即数扩展 (分支偏移)
|
||||
alu_op = ALU_SUB; // ALU执行减法以判断是否相等 (结果送zero_flag)
|
||||
end
|
||||
OP_BLT: begin // 小于则分支指令
|
||||
alu_src = 1'b0; // ALU比较两个寄存器的值
|
||||
src_reg = 1'b1; // 寄存器堆第二读地址来自instr[24:20]
|
||||
ext_op = EXT_SI16; // 16位有符号立即数扩展 (分支偏移)
|
||||
alu_op = ALU_SUB; // ALU执行减法以判断是否小于 (结果送lt_flag)
|
||||
end
|
||||
default: begin end // 其他未定义操作码,不产生任何有效控制信号(使用默认值)
|
||||
endcase
|
||||
end
|
||||
|
||||
wire beq_cond = (opcode == OP_BEQ) && zero_flag;
|
||||
wire blt_cond = (opcode == OP_BLT) && lt_flag;
|
||||
wire b_cond = (opcode == OP_B);
|
||||
// 分支条件逻辑:根据操作码和ALU标志位判断是否进行分支
|
||||
wire beq_cond = (opcode == OP_BEQ) && zero_flag; // BEQ 指令且零标志位为1
|
||||
wire blt_cond = (opcode == OP_BLT) && lt_flag; // BLT 指令且小于标志位为1
|
||||
wire b_cond = (opcode == OP_B); // B 指令无条件跳转
|
||||
|
||||
// PC来源控制信号:如果任一分支条件满足,则pcsource为1,选择分支目标地址;否则为0,选择PC+4
|
||||
assign pcsource = beq_cond || blt_cond || b_cond;
|
||||
endmodule
|
||||
@@ -11,43 +11,63 @@
|
||||
** 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.
|
||||
**
|
||||
** [FIXED] Added a multiplexer for the ALU's first operand (A-operand) to
|
||||
** support the LUI12I.W instruction by allowing it to select 0.
|
||||
**
|
||||
** 本模块是LA32R单周期CPU的顶层模块。它实例化并连接了所有子模块,
|
||||
** 包括程序计数器(PC)、指令存储器、数据存储器、寄存器堆、算术逻辑单元(ALU)、
|
||||
** 立即数扩展器和控制单元。模块间的连接遵循课程设计指导书中提供的单周期CPU数据通路图。
|
||||
** 为了支持LUI12I.W指令,ALU的第一个操作数(A操作数)增加了一个多路选择器,允许选择0作为输入。
|
||||
** Features:
|
||||
** - 集成CPU所有核心组件:PC、指令存储器、数据存储器、寄存器文件、ALU、立即数扩展器、控制单元。
|
||||
** - 实现单周期数据通路,连接各模块以执行指令。
|
||||
** - 根据控制单元产生的信号,协调数据流动和操作执行。
|
||||
** - 支持LUI12I.W指令,通过alu_asrc信号控制ALU的A操作数选择。
|
||||
** Revision:
|
||||
** Revision 0.02 - Added ALUAsrc control signal to support LUI12I.W instruction.
|
||||
** Revision 0.01 - File Created
|
||||
** Revision 0.02 - 添加了alu_asrc控制信号及相应的数据通路修改,以支持LUI12I.W指令。
|
||||
** Revision 0.01 - 文件创建。
|
||||
** Additional Comments:
|
||||
** - This file integrates the entire design.
|
||||
**
|
||||
** - 本文件是整个CPU设计的集成核心。
|
||||
** - 所有主要的子模块都在此文件中实例化和互连。
|
||||
*******************************************************************************/
|
||||
|
||||
module cpu_top (
|
||||
input wire clk,
|
||||
input wire rst
|
||||
input wire clk, // 时钟信号输入
|
||||
input wire rst // 复位信号输入
|
||||
);
|
||||
|
||||
// --- 内部连线声明 (Internal Wire Declarations) ---
|
||||
wire [31:0] pc_out, instr, imm_ext, read_data1, read_data2, alu_result,
|
||||
mem_read_data, write_back_data, alu_a_operand, alu_b_operand;
|
||||
// --- 内部信号线声明 ---
|
||||
// 数据通路信号
|
||||
wire [31:0] pc_out; // PC的输出,即当前指令地址
|
||||
wire [31:0] instr; // 从指令存储器读出的指令
|
||||
wire [31:0] imm_ext; // 立即数扩展器输出的扩展后立即数
|
||||
wire [31:0] read_data1; // 从寄存器堆读出的第一个操作数 (rs)
|
||||
wire [31:0] read_data2; // 从寄存器堆读出的第二个操作数 (rt / store data)
|
||||
wire [31:0] alu_result; // ALU的运算结果
|
||||
wire [31:0] mem_read_data; // 从数据存储器读出的数据
|
||||
wire [31:0] write_back_data; // 写回寄存器堆的数据
|
||||
wire [31:0] alu_a_operand; // ALU的A操作数
|
||||
wire [31:0] alu_b_operand; // ALU的B操作数
|
||||
|
||||
// 控制信号 (Control Signals)
|
||||
wire reg_write_en, mem_to_reg, mem_write_en, alu_src, src_reg,
|
||||
alu_asrc, pcsource, zero_flag, lt_flag;
|
||||
wire [2:0] ext_op;
|
||||
wire [3:0] alu_op;
|
||||
// 控制信号线
|
||||
wire reg_write_en; // 寄存器写使能
|
||||
wire mem_to_reg; // 选择写回寄存器的数据来源 (ALU结果或内存数据)
|
||||
wire mem_write_en; // 存储器写使能
|
||||
wire alu_src; // ALU第二操作数来源选择 (寄存器或立即数)
|
||||
wire src_reg; // 寄存器堆第二读地址来源选择
|
||||
wire alu_asrc; // ALU第一操作数来源选择 (支持LUI)
|
||||
wire pcsource; // PC下一地址来源选择 (PC+4或分支目标)
|
||||
wire zero_flag; // ALU零标志位输出
|
||||
wire lt_flag; // ALU小于标志位输出 (用于BLT)
|
||||
wire [2:0] ext_op; // 立即数扩展操作控制
|
||||
wire [3:0] alu_op; // ALU操作控制
|
||||
|
||||
// --- 模块实例化 (Module Instantiation) ---
|
||||
// --- 各子模块实例化 ---
|
||||
|
||||
// 程序计数器 (PC)
|
||||
pc u_pc (.clk(clk), .rst(rst), .pcsource(pcsource), .imm_ext(imm_ext), .pc_out(pc_out));
|
||||
|
||||
// 指令存储器
|
||||
instruction_memory u_inst_mem (.addr(pc_out), .instr(instr));
|
||||
|
||||
// 控制单元
|
||||
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),
|
||||
@@ -55,33 +75,46 @@ module cpu_top (
|
||||
.alu_asrc(alu_asrc), .pcsource(pcsource)
|
||||
);
|
||||
|
||||
// 立即数扩展器
|
||||
imm_extender u_imm_ext (.instr(instr), .ext_op(ext_op), .imm_ext(imm_ext));
|
||||
|
||||
wire [4:0] reg_read_addr2_final = src_reg ? instr[4:0] : instr[14:10];
|
||||
// 决定寄存器堆的第二个读取地址 (用于某些指令格式,如ST.W,其中rt是源数据)
|
||||
wire [4:0] reg_read_addr2_final = src_reg ? instr[4:0] : instr[14:10]; // src_reg=0: rd=instr[19:15] rt=instr[14:10]; src_reg=1: rd=instr[24:20] rt=instr[4:0]
|
||||
|
||||
// 寄存器堆
|
||||
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)
|
||||
.read_addr1(instr[9:5]), // rs寄存器地址
|
||||
.read_addr2(reg_read_addr2_final),// rt寄存器地址 (根据src_reg选择)
|
||||
.write_addr(instr[4:0]), // rd寄存器地址 (目标寄存器)
|
||||
.write_data(write_back_data), // 写回的数据
|
||||
.read_data1(read_data1), // 读出的rs寄存器数据
|
||||
.read_data2(read_data2) // 读出的rt寄存器数据
|
||||
);
|
||||
|
||||
// [NEW] MUX for ALU's first operand (A)
|
||||
// ALU的第一个操作数 (A) 的多路选择器,由alu_asrc控制
|
||||
// 当alu_asrc为1 (如LUI指令),A操作数为0;否则为寄存器堆的read_data1
|
||||
assign alu_a_operand = alu_asrc ? 32'b0 : read_data1;
|
||||
|
||||
// MUX for ALU's second operand (B)
|
||||
// ALU的第二个操作数 (B) 的多路选择器,由alu_src控制
|
||||
// 当alu_src为1,B操作数为立即数扩展器的输出;否则为寄存器堆的read_data2
|
||||
assign alu_b_operand = alu_src ? imm_ext : read_data2;
|
||||
|
||||
// 算术逻辑单元 (ALU)
|
||||
alu u_alu (
|
||||
.a(alu_a_operand), .b(alu_b_operand), .alu_op(alu_op),
|
||||
.result(alu_result), .zero(zero_flag), .lt(lt_flag)
|
||||
);
|
||||
|
||||
// 数据存储器
|
||||
data_memory u_data_mem (
|
||||
.clk(clk), .mem_write_en(mem_write_en), .addr(alu_result),
|
||||
.write_data(read_data2), .read_data(mem_read_data)
|
||||
.clk(clk), .mem_write_en(mem_write_en), .addr(alu_result), // 地址来自ALU计算结果
|
||||
.write_data(read_data2), // 写入的数据来自寄存器堆的read_data2 (例如ST.W指令)
|
||||
.read_data(mem_read_data) // 读出的数据
|
||||
);
|
||||
|
||||
// 写回寄存器的数据选择多路选择器,由mem_to_reg控制
|
||||
// 当mem_to_reg为1 (如LD.W指令),写回数据来自数据存储器;否则来自ALU的运算结果
|
||||
assign write_back_data = mem_to_reg ? mem_read_data : alu_result;
|
||||
|
||||
endmodule
|
||||
|
||||
@@ -1,31 +1,56 @@
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
/*******************************************************************************
|
||||
** Data Memory Module
|
||||
** Company: Nantong University
|
||||
** Engineer: あやせももこ
|
||||
**
|
||||
** Create Date: 2025-06-16
|
||||
** Design Name: LA32R Single Cycle CPU
|
||||
** Module Name: data_memory
|
||||
** Project Name: Computer Architecture Course Design
|
||||
** Target Devices: Any FPGA
|
||||
** Tool Versions: Vivado 2018.1
|
||||
** Description:
|
||||
** 本模块为LA32R CPU实现数据存储器。它用于存储和加载CPU执行过程中需要读写的数据。
|
||||
** 该存储器设计为字节寻址,但在此单周期CPU实现中,主要进行字(32位)的读写操作。
|
||||
** 存储器采用同步写、异步读的方式工作。
|
||||
** Features:
|
||||
** - 提供1024个32位存储单元 (总共4KB)。
|
||||
** - 支持同步写操作:在时钟上升沿根据写使能信号写入数据。
|
||||
** - 支持异步读操作:根据地址信号直接读出数据,无需时钟同步。
|
||||
** - 地址通过addr[11:2]进行选择,对应于32位字对齐的地址。
|
||||
** Revision:
|
||||
** Revision 0.01 - 文件创建及基本功能实现。
|
||||
** Additional Comments:
|
||||
** - 本模块在FPGA综合时,通常会被实现为块RAM (BRAM)。
|
||||
** - 地址线的低两位(addr[1:0])被忽略,因为内存按字(4字节)对齐访问。
|
||||
*******************************************************************************/
|
||||
|
||||
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)
|
||||
input wire clk, // 时钟信号输入
|
||||
input wire mem_write_en, // 存储器写使能信号 (高有效)
|
||||
input wire [31:0] addr, // 数据读写地址输入
|
||||
input wire [31:0] write_data, // 待写入存储器的数据
|
||||
output wire [31:0] read_data // 从存储器读出的数据
|
||||
);
|
||||
// 在FPGA中,这会综合成一个同步写的RAM
|
||||
// In an FPGA, this synthesizes into a synchronous-write RAM.
|
||||
// 此模块在FPGA实现中,通常会综合成一个同步写、异步读的RAM资源。
|
||||
// 例如,在Xilinx FPGA中,这可以映射到BRAM。
|
||||
|
||||
reg [31:0] mem [0:1023]; // 示例: 1KB数据空间 (Example: 1KB data space)
|
||||
// 定义一个1024深度、32位宽度的存储阵列,总容量为 1024 * 4 Bytes = 4KB。
|
||||
// 访问时使用地址的高10位 addr[11:2] 作为索引。
|
||||
reg [31:0] mem [0:1023]; // 存储阵列,共1024个字
|
||||
|
||||
// 同步写
|
||||
// Synchronous write
|
||||
// 同步写逻辑:仅在时钟上升沿且写使能有效时,才将数据写入指定地址。
|
||||
always @(posedge clk) begin
|
||||
if (mem_write_en) begin
|
||||
// 使用地址信号的高10位 (addr[11:2]) 作为存储器阵列的索引,
|
||||
// 因为存储器是字寻址的 (32位 = 4字节)。
|
||||
mem[addr[11:2]] <= write_data;
|
||||
end
|
||||
end
|
||||
|
||||
// 异步读
|
||||
// Asynchronous read
|
||||
// 异步读逻辑:读操作是组合逻辑,直接根据地址索引从存储阵列中获取数据。
|
||||
// 同样使用地址的高10位作为索引。
|
||||
assign read_data = mem[addr[11:2]];
|
||||
|
||||
endmodule
|
||||
|
||||
@@ -11,65 +11,69 @@
|
||||
** 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
|
||||
** 本模块负责处理LA32R架构中各种指令格式中出现的立即数的符号扩展或零扩展。
|
||||
** 它根据输入的指令和控制信号 `ext_op` 生成一个32位的立即数值。
|
||||
**
|
||||
** 支持的扩展类型:
|
||||
** - 12位有符号立即数 (si12):用于 ADDI.W, LD.W, ST.W 等I型指令。
|
||||
** - 20位立即数 (ui20):用于 LUI12I.W 指令 (高20位,低12位补零)。
|
||||
** - 16位有符号偏移量 (si16):用于 BEQ, BLT 等分支指令 (扩展后需左移两位)。
|
||||
** - 26位有符号偏移量 (si26):用于 B 等无条件跳转指令 (扩展后需左移两位)。
|
||||
** Features:
|
||||
** - 根据ext_op控制信号选择执行的扩展操作。
|
||||
** - 为I型指令(如ADDI.W)提供12位立即数的符号扩展。
|
||||
** - 为LUI12I.W指令提供20位立即数的高位加载(低位补零)。
|
||||
** - 为条件分支指令(如BEQ, BLT)提供16位立即数的符号扩展和左移两位。
|
||||
** - 为无条件跳转指令(B)提供26位立即数的符号扩展和左移两位。
|
||||
** - 输出固定为32位的扩展后立即数。
|
||||
** Revision:
|
||||
** Revision 0.01 - File Created
|
||||
** Revision 0.01 - 文件创建及基本功能实现。
|
||||
** Additional Comments:
|
||||
** - This is a purely combinational logic module.
|
||||
**
|
||||
** - 这是一个纯组合逻辑模块,输出仅取决于当前输入。
|
||||
** - 扩展操作的正确性对CPU指令的正确执行至关重要。
|
||||
*******************************************************************************/
|
||||
|
||||
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)
|
||||
input wire [31:0] instr, // 32位指令字输入
|
||||
input wire [2:0] ext_op, // 立即数扩展操作类型控制信号
|
||||
output reg [31:0] imm_ext // 输出的32位扩展后立即数
|
||||
);
|
||||
|
||||
// 定义立即数扩展类型的参数
|
||||
// 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
|
||||
// 定义用于指示不同立即数扩展操作的参数常量
|
||||
localparam EXT_SI12 = 3'b001; // 扩展类型:12位有符号立即数 (用于I型指令)
|
||||
localparam EXT_SI16 = 3'b010; // 扩展类型:16位有符号立即数 (用于BEQ, BLT等分支指令的偏移量)
|
||||
localparam EXT_UI20 = 3'b011; // 扩展类型:20位无符号立即数 (用于LUI12I.W指令)
|
||||
localparam EXT_SI26 = 3'b100; // 扩展类型:26位有符号立即数 (用于B指令的偏移量)
|
||||
|
||||
// 提取指令中不同格式的立即数
|
||||
// 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]
|
||||
// 从指令字中根据不同指令格式提取原始立即数字段
|
||||
wire [11:0] si12 = instr[21:10]; // I型指令的12位立即数 imm[11:0]
|
||||
wire [15:0] si16 = instr[25:10]; // 分支指令的16位偏移量 offs[15:0]
|
||||
wire [19:0] si20 = instr[24:5]; // LUI12I.W指令的20位立即数 imm[19:0]
|
||||
// B指令的26位偏移量由两部分拼接而成: offs[25:16] 位于 instr[9:0], offs[15:0] 位于 instr[25:10]
|
||||
wire [25:0] si26 = {instr[9:0], instr[25:10]};
|
||||
|
||||
// 组合逻辑: 根据ext_op选择不同的扩展方式
|
||||
// Combinational logic: select extension method based on ext_op
|
||||
// 组合逻辑:根据ext_op控制信号选择相应的立即数扩展方式
|
||||
always @(*) begin
|
||||
case (ext_op)
|
||||
EXT_SI12:
|
||||
// 对si12进行符号位扩展
|
||||
// Sign-extend si12
|
||||
// 对12位立即数si12进行符号扩展至32位。
|
||||
// 即将si12的最高位(si12[11])复制填充到结果的高20位。
|
||||
imm_ext = {{20{si12[11]}}, si12};
|
||||
EXT_SI16:
|
||||
// 对si16进行符号位扩展并左移两位
|
||||
// Sign-extend si16 and shift left by 2
|
||||
// 对16位立即数si16进行符号扩展并左移两位,以生成32位字地址偏移。
|
||||
// 即将si16的最高位(si16[15])复制填充到结果的高14位,低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
|
||||
// 对20位立即数si20进行处理,用于LUI12I.W指令。
|
||||
// 将si20作为结果的高20位,低12位补零。
|
||||
imm_ext = {si20, 12'b0};
|
||||
EXT_SI26:
|
||||
// 对si26进行符号位扩展并左移两位
|
||||
// Sign-extend si26 and shift left by 2
|
||||
// 对26位立即数si26进行符号扩展并左移两位,以生成32位字地址偏移。
|
||||
// 即将si26的最高位(si26[25])复制填充到结果的高4位,低2位补零。
|
||||
imm_ext = {{4{si26[25]}}, si26, 2'b00};
|
||||
default:
|
||||
imm_ext = 32'hxxxxxxxx; // 默认情况,输出不定态
|
||||
// 若ext_op不匹配任何已知类型,则输出不确定值。
|
||||
imm_ext = 32'hxxxxxxxx;
|
||||
endcase
|
||||
end
|
||||
|
||||
|
||||
@@ -1,31 +1,55 @@
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
/*******************************************************************************
|
||||
** Instruction Memory Module
|
||||
** Company: Nantong University
|
||||
** Engineer: あやせももこ
|
||||
**
|
||||
** Create Date: 2025-06-16
|
||||
** Design Name: LA32R Single Cycle CPU
|
||||
** Module Name: instruction_memory
|
||||
** Project Name: Computer Architecture Course Design
|
||||
** Target Devices: Any FPGA
|
||||
** Tool Versions: Vivado 2018.1
|
||||
** Description:
|
||||
** 本模块为LA32R CPU实现指令存储器。它负责根据程序计数器(PC)提供的地址
|
||||
** 输出相应的32位指令。在实际硬件中,这通常是一个只读存储器(ROM)。
|
||||
** 在仿真环境中,它通过一个寄存器数组实现,并使用`$readmemh`系统任务
|
||||
** 从外部十六进制文件加载指令。
|
||||
** Features:
|
||||
** - 提供1024个32位存储单元,可存储1024条指令。
|
||||
** - 根据输入的32位字节地址(addr)获取指令,实际通过addr[11:2]选择字。
|
||||
** - 在FPGA实现中通常综合为ROM。
|
||||
** - 仿真时支持从外部文件加载指令。
|
||||
** Revision:
|
||||
** Revision 0.01 - 文件创建及基本功能实现。
|
||||
** Additional Comments:
|
||||
** - 仿真时,指令从位于"../../../../../Software/program.hex"的十六进制文件加载。
|
||||
** - 确保该路径相对于仿真工作目录是正确的。
|
||||
** - 硬件实现中,指令内容通常在FPGA配置时被编程到ROM中。
|
||||
*******************************************************************************/
|
||||
|
||||
module instruction_memory (
|
||||
input wire [31:0] addr, // 地址输入 (Address input)
|
||||
output wire [31:0] instr // 指令输出 (Instruction output)
|
||||
input wire [31:0] addr, // 输入的指令地址 (来自PC)
|
||||
output wire [31:0] instr // 输出的32位指令
|
||||
);
|
||||
// 指令存储器通常是只读的 (Instruction memory is typically read-only)
|
||||
// 在FPGA中,这会综合成一个ROM
|
||||
// In an FPGA, this synthesizes into a ROM.
|
||||
// 指令存储器在硬件实现中通常是只读存储器 (ROM)。
|
||||
// 在FPGA综合时,此模块将被实现为一个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)
|
||||
// 为了进行仿真,我们使用一个寄存器数组来模拟指令存储器,
|
||||
// 并使用 $readmemh 系统任务从一个十六进制文件中加载程序指令。
|
||||
reg [31:0] mem [0:1023]; // 定义一个可存储1024条32位指令的存储阵列 (4KB容量)
|
||||
|
||||
// `initial`块仅在仿真开始时执行一次。
|
||||
initial begin
|
||||
// 从文件中加载指令
|
||||
// Load instructions from a file.
|
||||
// 你需要创建一个名为 "program.hex" 的文件
|
||||
// You need to create a file named "program.hex".
|
||||
// 从指定的十六进制文件中加载指令到mem数组中。
|
||||
// 文件路径是相对于仿真执行目录的相对路径。
|
||||
// 用户需要创建一个名为 "program.hex" 的文件,其中包含机器码。
|
||||
$readmemh("../../../../../Software/program.hex", mem);
|
||||
end
|
||||
|
||||
// 字节地址转换为字地址
|
||||
// Convert byte address to word address.
|
||||
assign instr = mem[addr[11:2]];
|
||||
// CPU发出的地址是字节地址,而指令存储器是按字(32位)组织的。
|
||||
// 因此,需要将字节地址转换为字地址索引。addr[1:0]被忽略。
|
||||
// 例如,地址0x00, 0x04, 0x08 分别对应 mem[0], mem[1], mem[2]。
|
||||
assign instr = mem[addr[11:2]]; // 使用地址的高10位作为mem数组的索引
|
||||
|
||||
endmodule
|
||||
@@ -16,41 +16,52 @@
|
||||
** 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
|
||||
**
|
||||
** - 若无分支或跳转 (pcsource=0): PC_next = PC_current + 4
|
||||
** - 若发生分支或跳转 (pcsource=1): PC_next = PC_current + imm_ext (分支/跳转目标地址)
|
||||
** (其中 imm_ext 是已经过符号扩展和适当左移的偏移量)
|
||||
** Features:
|
||||
** - 32位程序计数器。
|
||||
** - 同步复位功能,复位时PC置为0x00000000。
|
||||
** - 在每个时钟上升沿更新PC值。
|
||||
** - 支持顺序执行 (PC + 4)。
|
||||
** - 支持基于立即数偏移量的分支和跳转。
|
||||
** Revision:
|
||||
** Revision 0.01 - File Created
|
||||
**
|
||||
** Revision 0.01 - 文件创建及基本功能实现。
|
||||
** Additional Comments:
|
||||
** - `imm_ext` 输入假定已经由立即数扩展单元处理过(例如,对于分支指令,已经左移两位)。
|
||||
*******************************************************************************/
|
||||
|
||||
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)
|
||||
input wire clk, // 时钟信号输入
|
||||
input wire rst, // 复位信号输入 (高有效)
|
||||
input wire pcsource, // PC下一地址来源选择信号 (0: PC+4; 1: 分支/跳转目标)
|
||||
input wire [31:0] imm_ext, // 来自立即数扩展单元的32位扩展后立即数 (用作偏移量)
|
||||
output reg [31:0] pc_out // 输出当前的PC值 (即当前指令地址)
|
||||
);
|
||||
|
||||
wire [31:0] pc_plus_4;
|
||||
wire [31:0] pc_branch;
|
||||
wire [31:0] pc_next;
|
||||
wire [31:0] pc_plus_4; // 存储PC + 4的结果
|
||||
wire [31:0] pc_branch; // 存储分支或跳转目标地址
|
||||
wire [31:0] pc_next; // 存储下一个PC的值
|
||||
|
||||
// PC寄存器
|
||||
// PC register
|
||||
// PC寄存器逻辑:在时钟上升沿或复位信号有效时更新
|
||||
always @(posedge clk or posedge rst) begin
|
||||
if (rst) begin
|
||||
pc_out <= 32'h00000000; // 复位到0地址
|
||||
pc_out <= 32'h00000000; // 系统复位时,PC清零
|
||||
end else begin
|
||||
pc_out <= pc_next;
|
||||
pc_out <= pc_next; // 否则,PC更新为pc_next的值
|
||||
end
|
||||
end
|
||||
|
||||
// PC更新逻辑
|
||||
// PC update logic
|
||||
// PC下一状态逻辑 (组合逻辑)
|
||||
// 计算PC顺序递增4的值 (指向下一条指令)
|
||||
assign pc_plus_4 = pc_out + 32'd4;
|
||||
assign pc_branch = pc_out + imm_ext; // 偏移量已经左移两位
|
||||
// 计算分支/跳转目标地址。imm_ext是带符号的偏移量,
|
||||
// 对于分支指令,imm_extender模块已经将其处理为乘以4之后的值(即左移两位)。
|
||||
assign pc_branch = pc_out + imm_ext;
|
||||
|
||||
// 根据pcsource信号选择下一个PC值:
|
||||
// 如果pcsource为1 (表示发生分支或跳转),则pc_next为计算出的目标地址pc_branch。
|
||||
// 如果pcsource为0 (表示顺序执行),则pc_next为pc_plus_4。
|
||||
assign pc_next = pcsource ? pc_branch : pc_plus_4;
|
||||
|
||||
endmodule
|
||||
|
||||
@@ -16,64 +16,59 @@
|
||||
** 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.
|
||||
** - 包含32个通用寄存器,每个寄存器宽度为32位。
|
||||
** - 异步读:两个读端口可以立即反映所选寄存器的内容,无需时钟同步。
|
||||
** - 同步写:写操作在时钟的上升沿进行。
|
||||
** - R0硬连线为零:寄存器R0不可写入,读取R0始终返回0。
|
||||
**
|
||||
** Revision:
|
||||
** Revision 0.01 - File Created
|
||||
** Revision 0.01 - 文件创建及基本功能实现。
|
||||
** Additional Comments:
|
||||
** - Reset logic is included to initialize all registers to zero, which is good practice for simulation and synthesis.
|
||||
**
|
||||
** - 包含了复位逻辑,在复位时将所有寄存器初始化为零,这对于仿真和综合是一个良好的实践。
|
||||
** - R0始终为零的设计是MIPS等RISC架构的常见特性。
|
||||
*******************************************************************************/
|
||||
|
||||
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)
|
||||
input wire clk, // 时钟信号输入
|
||||
input wire rst, // 复位信号输入 (高有效)
|
||||
input wire reg_write_en, // 寄存器写使能信号 (高有效)
|
||||
input wire [4:0] read_addr1, // 第一个读端口的寄存器地址 (5位选择32个寄存器之一)
|
||||
input wire [4:0] read_addr2, // 第二个读端口的寄存器地址
|
||||
input wire [4:0] write_addr, // 写端口的寄存器地址
|
||||
input wire [31:0] write_data, // 待写入寄存器的数据
|
||||
output wire [31:0] read_data1, // 第一个读端口读出的数据
|
||||
output wire [31:0] read_data2 // 第二个读端口读出的数据
|
||||
);
|
||||
|
||||
// 声明32个32位的寄存器阵列
|
||||
// Declare an array of 32 registers, each 32 bits wide.
|
||||
// 声明一个包含32个32位寄存器的存储阵列。
|
||||
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.
|
||||
// 当复位信号有效时,将所有寄存器(包括R0)初始化为0。
|
||||
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.
|
||||
// 当写使能信号有效时,并且目标地址不是R0(5'd0),才执行写操作。
|
||||
// 这是为了确保R0始终为0。
|
||||
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.
|
||||
// 异步读端口1的逻辑:
|
||||
// 如果读取地址为R0 (5'd0),则输出32'b0。
|
||||
// 否则,输出对应地址寄存器的内容。
|
||||
assign read_data1 = (read_addr1 == 5'd0) ? 32'b0 : registers[read_addr1];
|
||||
|
||||
// 异步读操作2
|
||||
// Asynchronous read port 2
|
||||
// 异步读端口2的逻辑:
|
||||
// 与读端口1类似,确保读取R0时返回0。
|
||||
assign read_data2 = (read_addr2 == 5'd0) ? 32'b0 : registers[read_addr2];
|
||||
|
||||
endmodule
|
||||
|
||||
Reference in New Issue
Block a user