摘要:本文介绍了计算机组成原理的实践学习,重点记录了使用Logisim设计简单CPU以及使用Verilog实现ALU和流水线模块的实验过程。通过Logisim,设计了一个8位简易CPU,包含完整的指令集和数据通路。使用Verilog实现了功能完备的ALU模块,支持多种算术逻辑运算,并构建了五级流水线CPU的基本框架。实验中遇到了时序问题、仿真错误和流水线冲突等挑战,通过合理设置时钟周期、检查寄存器初始化和实现转发机制等方法解决了这些问题。通过实践,加深了对计算机组成原理的理解,为后续学习打下了坚实基础。
关键词:计算机组成原理、Logisim、Verilog、CPU设计、ALU、流水线、实践
人工智能助手:DeepSeek
计算机组成原理作为计算机科学与技术专业的核心课程,不仅需要扎实的理论基础,更需要通过实践来深化对计算机硬件系统的理解。本学习笔记将重点记录使用Logisim设计简单CPU以及使用Verilog实现ALU和流水线模块的实验过程与心得体会。
通过硬件描述语言和电路仿真工具的实践操作,能够将抽象的计算机组成概念具体化,真正理解数据在计算机中的流动过程、指令的执行机制以及各硬件组件的协同工作方式。这种"做中学"的方式对于培养硬件设计思维和解决实际工程问题的能力至关重要。
Logisim是一款开源的数字电路仿真与设计工具,具有以下特点:
设计一个8位简易CPU,包含以下组件:
设计精简指令集(RISC风格),包含以下指令类型:
指令类型 | 操作码 | 功能说明 |
---|---|---|
ADD | 0000 | Rd ← Rs + Rt |
SUB | 0001 | Rd ← Rs - Rt |
AND | 0010 | Rd ← Rs & Rt |
OR | 0011 | Rd ← Rs |
LOAD | 0100 | Rd ← M[imm] |
STORE | 0101 | M[imm] ← Rs |
JMP | 0110 | PC ← imm |
BNE | 0111 | if Rs≠Rt then PC←PC+imm |
指令格式:
opcode(4) | rd(2) | rs(2) | rt(2)
opcode(4) | rd(2) | imm(6)
Logisim实现步骤:
支持以下操作:
功能表:
ALUop(2) | 功能 |
---|---|
00 | ADD |
01 | SUB |
10 | AND |
11 | OR |
Logisim实现:
根据指令操作码生成控制信号:
Logisim实现:
将各模块连接成完整数据通路:
编写简单汇编程序测试CPU功能:
LOAD R0, 5 // R0 = 5
LOAD R1, 3 // R1 = 3
ADD R2, R0, R1 // R2 = R0 + R1 = 8
STORE R2, 0x10 // M[16] = 8
BNE R0, R1, 2 // 跳转2条指令
SUB R3, R0, R1 // 不会执行
AND R3, R0, R1 // R3 = R0 & R1 = 1
转换为机器码并存入ROM。
module
和endmodule
input
, output
, inout
wire
, reg
assign
,过程赋值=
,<=
always
块使用initial
块生成测试激励:
initial begin
// 初始化输入
a = 0; b = 0; op = 0;
// 测试用例
#10 a = 8'h05; b = 8'h03; op = 2'b00; // ADD
#10 op = 2'b01; // SUB
#10 op = 2'b10; // AND
#10 op = 2'b11; // OR
#10 $finish;
end
module ALU (
input [7:0] a, // 操作数A
input [7:0] b, // 操作数B
input [1:0] op, // 操作码
output reg [7:0] out, // 运算结果
output zero // 零标志
);
always @(*) begin
case(op)
2'b00: out = a + b; // ADD
2'b01: out = a - b; // SUB
2'b10: out = a & b; // AND
2'b11: out = a | b; // OR
default: out = 8'b0;
endcase
end
assign zero = (out == 8'b0); // 零标志
`timescale 1ns / 1ps
module ALU(
input [7:0] a,
input [7:0] b,
input [1:0] op,
output reg [7:0] out,
output zero
);
always @(*) begin
case(op)
2'b00: out = a + b; // ADD
2'b01: out = a - b; // SUB
2'b10: out = a & b; // AND
2'b11: out = a | b; // OR
default: out = 8'b0;
endcase
end
assign zero = (out == 8'b0);
endmodule
module ALU_tb;
reg [7:0] a, b;
reg [1:0] op;
wire [7:0] out;
wire zero;
ALU uut (.a(a), .b(b), .op(op), .out(out), .zero(zero));
initial begin
$monitor("At time %t, a=%h b=%h op=%b => out=%h zero=%b",
$time, a, b, op, out, zero);
a = 8'h05; b = 8'h03; op = 2'b00; #10; // ADD: 5+3=8
op = 2'b01; #10; // SUB: 5-3=2
op = 2'b10; #10; // AND: 5&3=1
op = 2'b11; #10; // OR: 5|3=7
$finish;
end
endmodule
时间 | a | b | op | out | zero |
---|---|---|---|---|---|
10 | 5 | 3 | 00 | 08 | 0 |
20 | 5 | 3 | 01 | 02 | 0 |
30 | 5 | 3 | 10 | 01 | 0 |
40 | 5 | 3 | 11 | 07 | 0 |
设计测试用例覆盖:
相邻阶段间插入寄存器,保存中间结果和控制信号:
module IF_stage(
input clk,
input reset,
input PCSrc,
input [7:0] BranchTarget,
output [7:0] PC,
output [11:0] Instruction
);
reg [7:0] PC_reg;
wire [7:0] PC_next;
// 指令存储器(模拟)
reg [11:0] IMEM [0:255];
initial begin
$readmemb("program.bin", IMEM);
end
// PC更新逻辑
assign PC_next = PCSrc ? BranchTarget : (PC_reg + 1);
always @(posedge clk or posedge reset) begin
if (reset) PC_reg <= 8'b0;
else PC_reg <= PC_next;
end
assign PC = PC_reg;
assign Instruction = IMEM[PC_reg];
endmodule
module ID_stage(
input clk,
input [11:0] Instruction,
input RegWrite,
input [7:0] WriteData,
input [1:0] WriteReg,
output [7:0] ReadData1,
output [7:0] ReadData2,
output [1:0] Rd,
output [7:0] Imm,
output [1:0] ALUop
);
// 寄存器文件
reg [7:0] RegFile [0:3];
// 指令解码
wire [3:0] opcode = Instruction[11:8];
assign Rd = Instruction[7:6];
wire [1:0] Rs = Instruction[5:4];
wire [1:0] Rt = Instruction[3:2];
assign Imm = {2'b0, Instruction[5:0]};
// 控制信号生成
assign ALUop = (opcode[3:2] == 2'b00) ? 2'b00 : // ADD
(opcode[3:2] == 2'b01) ? 2'b01 : // SUB
(opcode[3:2] == 2'b10) ? 2'b10 : // AND
2'b11; // OR
// 寄存器读
assign ReadData1 = RegFile[Rs];
assign ReadData2 = RegFile[Rt];
// 寄存器写
always @(posedge clk) begin
if (RegWrite) RegFile[WriteReg] <= WriteData;
end
endmodule
module IF_ID_Reg(
input clk,
input [7:0] PC_in,
input [11:0] Instruction_in,
output reg [7:0] PC_out,
output reg [11:0] Instruction_out
);
always @(posedge clk) begin
PC_out <= PC_in;
Instruction_out <= Instruction_in;
end
endmodule
// 在EX阶段添加转发逻辑
wire [7:0] ALU_input1 = (ForwardA == 2'b01) ? EX_MEM_ALUResult :
(ForwardA == 2'b10) ? MEM_WB_WriteData :
ID_EX_ReadData1;
wire [7:0] ALU_input2 = (ForwardB == 2'b01) ? EX_MEM_ALUResult :
(ForwardB == 2'b10) ? MEM_WB_WriteData :
ID_EX_ReadData2;
// 简单分支处理
assign PCSrc = (ID_Branch & (ID_ReadData1 != ID_ReadData2));
assign BranchTarget = ID_PC + ID_Imm;
module Pipeline_CPU(
input clk,
input reset
);
// 阶段间寄存器
wire [7:0] IF_PC, ID_PC;
wire [11:0] IF_Instruction, ID_Instruction;
// 各阶段控制信号
wire ID_RegWrite, EX_RegWrite, MEM_RegWrite, WB_RegWrite;
wire [1:0] EX_ALUop;
wire EX_Branch;
// 数据通路信号
wire [7:0] ALUResult, MEM_ReadData, WB_WriteData;
wire [1:0] WB_WriteReg;
// 各阶段实例化
IF_stage IF(.clk(clk), .reset(reset), .PCSrc(PCSrc),
.BranchTarget(BranchTarget), .PC(IF_PC),
.Instruction(IF_Instruction));
IF_ID_Reg IF_ID(.clk(clk), .PC_in(IF_PC),
.Instruction_in(IF_Instruction),
.PC_out(ID_PC), .Instruction_out(ID_Instruction));
ID_stage ID(.clk(clk), .Instruction(ID_Instruction),
.RegWrite(WB_RegWrite), .WriteData(WB_WriteData),
.WriteReg(WB_WriteReg), /* 其他输出 */);
// 其他阶段类似...
endmodule
module Pipeline_CPU_tb;
reg clk, reset;
Pipeline_CPU cpu(.clk(clk), .reset(reset));
initial begin
clk = 0;
reset = 1;
#10 reset = 0;
// 运行足够时钟周期
#200 $finish;
end
always #5 clk = ~clk;
endmodule
通过本次实验,我实现了:
Logisim时序问题:
Verilog仿真错误:
流水线冲突:
扩展指令集:
增强流水线:
性能优化:
验证完善:
计算机组成原理实验让我深刻理解了:
这些实践经验不仅加深了对计算机组成原理的理解,也为后续学习计算机体系结构、操作系统等课程打下了坚实基础。