Initial commit

This commit is contained in:
2025-06-17 00:35:40 +08:00
commit 07c0b18e4c
33 changed files with 2300 additions and 0 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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