目录
本实验包含:
简易结构图:
各部件代码或实现:
控制器:
寄存器堆:
ALU:
数据存储器:
指令存储器:
CPU:
tp(仿真文件):
仿真结果:
单周期CPU压缩包下载
本实验包含:
指令存储器和数据存储器的ip核调用,控制器,寄存器堆,ALU,单周期CPU的实现。
简易结构图:
各部件代码或实现:
控制器:
控制器有13条指令,需要可以再加,照着之前格式注释加就行了,对于同RAM相关的指令未测试
R: | |||||||
指令 | [31:26] | [25:21] | [20:16] | [15:11] | [10:6] | [5:0] | 功能 |
add | 000000 | rs | rt | rd | 000000 | 100000 | 寄存器加 |
sub | 000000 | rs | rt | rd | 000000 | 100010 | 寄存器减 |
and | 000000 | rs | rt | rd | 000000 | 100100 | 寄存器与 |
or | 000000 | rs | rt | rd | 000000 | 100101 | 寄存器或 |
nor | 000000 | rs | rt | rd | 000000 | 100111 | 寄存器或非 |
sll | 000000 | rs | 000000 | rd | sa | 000000 | 逻辑左移 |
srl | 000000 | rs | 000000 | rd | sa | 000010 | 逻辑右移 |
sra | 000000 | rs | 000000 | rd | sa | 100111 | 算术右移 |
I: | |||||||
指令 | [31:26] | [25:21] | [20:16] | [15:0] | 功能 | ||
addi | 001000 | rs | rt | immediate | 立即数加 | ||
lw | 100011 | rs | rt | immediate | 取字数据 | ||
sw | 101011 | rs | rt | immediate | 存字数据 | ||
beq | 000100 | rs | rt | immediate | 相等转移 | ||
J: | |||||||
指令 | [31:26] | [25:21] | [20:16] | [15;0] | 功能 | ||
j | 000010 | 00000 | 00000 | immediate | 转移 |
输入:op,func
输出:MemtoReg,MemWrite,Branch,ALUOP,ALUSrc,RegWrite,RegDst
//
//创建日期:2022/12/19 10:46:36
//设计名称:控制器
//课程名称:Controler
//说明:
//输入:op,func
//输出:MemtoReg,MemWrite,Branch,ALUOP,ALUSrc,RegWrite,RegDst
//依赖项:
//
//版次:
//版本0.01-文件已创建
//其他注释:
//
//
module Controler(op,func,MemtoReg,MemWrite,Branch,ALUOP,ALUSrc,RegWrite,RegDst);
input [5:0] op;
input [5:0] func;
output MemtoReg;
output MemWrite;
output Branch;
output [11:0] ALUOP;
output ALUSrc;
output RegWrite;
output RegDst;
reg MemtoReg,MemWrite,Branch,ALUSrc,RegWrite,RegDst;
reg [11:0] ALUOP;
// R:
// 指令 [31:26] [25:21] [20:16] [15:11] [10:6] [5:0] 功能
// add 000000 rs rt rd 000000 100000 寄存器加
// sub 000000 rs rt rd 000000 100010 寄存器减
// and 000000 rs rt rd 000000 100100 寄存器与
// or 000000 rs rt rd 000000 100101 寄存器或
// nor 000000 rs rt rd 000000 100111 寄存器或非
// sll 000000 rs 000000 rd sa 000000 逻辑左移
// srl 000000 rs 000000 rd sa 000010 逻辑右移
// sra 000000 rs 000000 rd sa 000011 算术右移
//I:
// 指令 [31:26] [25:21] [20:16] [15:0] 功能
// addi 001000 rs rt immediate 立即数加
// lw 100011 rs rt immediate 取字数据
// sw 101011 rs rt immediate 存字数据
// beq 000100 rs rt immediate 相等转移
//J:
// 指令 [31:26] [25:21] [20:16] [15:0] 功能
// j 000010 00000 00000 immediate 转移
always @(*)
begin
case(op)
6'b000000://寄存器操作
begin
MemtoReg=0;//输出ALU的输出
MemWrite=0;//数据存储器不写入
Branch=0;//正常PC
ALUSrc=0;//ALU输入2选择寄存器输出
RegWrite=1;//寄存器写入
RegDst=1;//有rd
case(func) //控制ALU操作
6'b100000:// 寄存器加
ALUOP=12'b010000000000;
6'b100010:// 寄存器减
ALUOP=12'b100000000000;
6'b100100:// 寄存器与
ALUOP=12'b000010000000;
6'b100101:// 寄存器或
ALUOP=12'b000000100000;
6'b100111:// 寄存器或非
ALUOP=12'b000001000000;
6'b100100:// 逻辑左移
ALUOP=12'b000000001000;
6'b100101:// 逻辑右移
ALUOP=12'b000000000100;
6'b100111:// 算术右移
ALUOP=12'b000000000010;
default:ALUOP=12'b010000000000;
endcase
end
6'b001000:// 立即数加
begin
MemtoReg=0;//输出ALU结果
MemWrite=0;//数据存储器不写入
Branch=0;//正常PC
ALUOP=12'b010000000000;//ALU加操作
ALUSrc=1;//数据2选择立即数输出
RegWrite=1;//寄存器写入
RegDst=0;//无rd选择rt
end
6'b100011:// 取字数据
begin
MemtoReg=1;//输出数据存储器结果
MemWrite=0;//数据存储器不写入
Branch=0;//正常PC
ALUOP=12'b000000000000;//ALU无操作,输出第一个输入
ALUSrc=1;//数据2随意
RegWrite=1;//寄存器写入
RegDst=0;//无rd选择rt
end
6'b101011:// 存字数据
begin
MemtoReg=1;//输出随意
MemWrite=1;//数据存储器写入
Branch=0;//正常PC
ALUOP=12'b000000000000;//ALU无操作,输出第一个输入
ALUSrc=1;//数据2随意
RegWrite=0;//寄存器不写入
RegDst=0;//不写入随意
end
6'b000100:// 相等转移
begin
MemtoReg=1;//输出随意
MemWrite=0;//数据存储器不写入
Branch=1;//PC可能改变
ALUOP=12'b000000000000;//ALU无操作,输出第一个输入
ALUSrc=0;//ALU输入2选择寄存器输出
RegWrite=0;//寄存器不写入
RegDst=0;//不写入随意
end
6'b000010://跳转
begin
MemtoReg=1;//输出随意
MemWrite=0;//数据存储器不写入
Branch=1;//PC可能改变
ALUOP=12'b000000000000;//ALU无操作,输出第一个输入
ALUSrc=0;//数据2选择寄存器输出
RegWrite=0;//寄存器不写入
RegDst=0;//不写入随意
end
default:
begin
MemtoReg=0;
MemWrite=0;
Branch=0;
ALUOP = 12'b000000000000;//ALU无操作,输出第一个输入
ALUSrc=0;
RegWrite=1;
RegDst=1;
end
endcase
end
endmodule
寄存器堆:
采用之前的寄存器堆(短版)代码,没有初始化,不影响使用,需要的话加上就行
//
//
//创建日期:2022/10/16 21:37:00
//设计名称:寄存器堆
//课程名称:regfile
//说明:
// 实现 32 个寄存器, 其中 0 号寄存器读出的值恒为 0,
// 寄存器堆为异步读同步写,
// 共有 1 个写端口和 2 个读端口
//依赖项:
//
//版次:
//版本0.01-文件已创建
//其他注释:
//
module regfile(
input clk, // 时钟
input wen, // 写使能
input [4 :0] raddr1, // 读地址1
input [4 :0] raddr2, // 读地址2
input [4 :0] waddr, // 写地址
input [31:0] wdata, // 写数据
output reg [31:0] rdata1, // 读到的数据1
output reg [31:0] rdata2, // 读到的数据2
input [4 :0] test_addr, // 测试读端口
output reg [31:0] test_data // 测试输出
);
reg [31:0] rf[31:0]; // 定义32个32位的寄存器
always @(posedge clk) // 时钟上升沿
begin
if (wen) // 如果写使能wen为1则写入寄存器
begin
rf[waddr] <= wdata;
end
end
//读端口 1
always @(*)
begin
if (raddr1==5'd0)
rdata1 <= 32'd0;
else
rdata1 <= rf[raddr1];
end
//读端口 2
always @(*)
begin
if (raddr2==5'd0)
rdata2 <= 32'd0;
else
rdata2 <= rf[raddr2];
end
//测试读端口
always @(*)
begin
if (test_addr==5'd0)
test_data <= 32'd0;
else
test_data <= rf[test_addr];
end
endmodule
ALU:
对照之前的ALU增加了比较相等的输出,用于PC的跳转,采用独热编码,相当于13种简易运算。
//
//创建日期:2022/11/6 20:06:00
//设计名称:ALU算术逻辑单元
//课程名称:alu
//说明:
//输入: [11:0] alu_control; // ALU控制信号
// [31:0] alu_src1; // ALU操作数1
// [31:0] alu_src2; // ALU操作数2
//输出: [31:0] alu_result; // ALU结果
// Equal 两个输入是否相等
//依赖项:
//
//版次:
//版本0.01-文件已创建
//其他注释:
//
//
module alu(alu_control,alu_src1,alu_src2,alu_result,Equal);
input [11:0] alu_control; // ALU控制信号
input [31:0] alu_src1; // ALU操作数1
input [31:0] alu_src2; // ALU操作数2
output [31:0] alu_result; // ALU结果
output Equal; //相等
wire Equal;
reg [31:0] alu_result;
// 控制信号为独热编码
assign Equal = alu_src1==alu_src2;
always @(*)
begin
case(alu_control) // 下面的1,2指操作数1,操作数2
12'b000000000001:alu_result<=alu_src1<<16; // 高位加载 1
12'b000000000010:alu_result<=alu_src1>>>alu_src2; // 算术右移 2
12'b000000000100:alu_result<=alu_src1>>alu_src2; // 逻辑右移 4
12'b000000001000:alu_result<=alu_src1<<alu_src2; // 逻辑左移 8
12'b000000010000:alu_result<=alu_src1^alu_src2; // 按位异或 16
12'b000000100000:alu_result<=alu_src1|alu_src2;// 按位或 32
12'b000001000000:alu_result<=~(alu_src1|alu_src2); // 按位或非 64
12'b000010000000:alu_result<=alu_src1&alu_src2; // 按位与 128
12'b000100000000:alu_result<=alu_src1<alu_src2?32'd1:32'd0;// 无符号比较,小于置位 256
12'b001000000000:alu_result<=$signed(alu_src1)<$signed(alu_src2)?32'd1:32'd0;// 有符号比较,小于置位 512
12'b010000000000:alu_result<=alu_src1+alu_src2;// 1加 1024
12'b100000000000:alu_result<=alu_src1-alu_src2;// 1减 2048
default: alu_result<=alu_src1;
endcase
end
endmodule
数据存储器:
采用IP核实现:
没有测试,功能或许有问题
指令存储器:
采用IP核实现:
第四张图的ROM.coe数据如下:
memory_initialization_radix=2;
memory_initialization_vector=
00100000000000010000000000001000
00100000000000100000000000000010
00100000000000110000000000000000
00000000010000110001100000100000
00010000001000110000000000000111
00001000000000000000000000000011
这是一段测试用的指令段,具体功能在tp文件种有注释
这个文件什么名字和位置都可以,后缀是.coe就行。
CPU:
//
//创建日期:2022/12/19 16:32:56
//设计名称:CPU
//课程名称:CPU
//说明:
//调用各个部件,进行运算
//依赖项:
// 控制器,寄存器,ALU
//版次:
//版本0.01-文件已创建
//其他注释:
module CPU(clk);
input clk;
// PC
reg [7:0] PC=8'd0;//PC从第0条指令开始
wire[31:0] SignImm;//指令后16位扩展结果
wire PCSrc;//是否跳转
always@(posedge clk)//上升沿
begin
if (PCSrc == 0)
PC = PC+1;
else
PC = SignImm[7:0];
end
// 指令存储器
wire [31:0] instructions;//指令存储器输出
ROM_D IROM(
.a(PC),//地址
.spo(instructions));//指令输出
wire[5:0] op,func;//控制器输入
wire[4:0] rs,rt,rd;//三个寄存器地址
assign op = instructions[31:26];
assign func = instructions[5:0];
assign rs = instructions[25:21];
assign rt = instructions[20:16];
assign rd = instructions[15:11];
assign SignImm = {{(16){instructions[15]}},instructions[15:0]};
// 控制器
wire MemtoReg,MemWrite,Branch,ALUSrc,RegWrite,RegDst;//控制器输出控制信号
wire[11:0] ALUOP;//ALU所做的操作
Controler Contr(op,func,MemtoReg,MemWrite,Branch,ALUOP,ALUSrc,RegWrite,RegDst);
// 寄存器堆
wire[31:0] R1,R2,WriteBackData;//寄存器输出和数据输入
wire[4:0] reg_w;//寄存器写地址
assign reg_w = RegDst?rd:rt;
regfile regfile_(clk,RegWrite,rs,rt,reg_w,WriteBackData,R1,R2);
// ALU
wire[31:0] srcB,ALUResult;//ALU第二个数据输入和数据输出
wire Equal;//输入是否相等
assign srcB = ALUSrc?SignImm:R2;
alu ALU(ALUOP,R1,srcB,ALUResult,Equal);
assign PCSrc = Branch⩵
// 数据存储器
wire [31:0] ReadData;//数据存储器输出
data_RAM DRM(
.clka (clk ),
.wea (MemWrite ),
.addra (ALUResult[7:0]),
.dina (R2 ),
.douta (ReadData ));
assign WriteBackData = MemWrite?ReadData:ALUResult;
endmodule
tp(仿真文件):
就一个clk和CPU的调用,所用指令段的注释。
`timescale 1ns / 1ps
//001000 00000 00001 0000000000001000 第0个寄存器和8相加存入第1个寄存器
//001000 00000 00010 0000000000000010 第0个寄存器和2相加存入第2个寄存器
//001000 00000 00011 0000000000000000 第0个寄存器和0相加存入第3个寄存器
//000000 00010 00011 00011 00000 100000 第3个寄存器和第2个寄存器相加,结果存入第3个寄存器
//000100 00001 00011 0000000000000111 第1个寄存器和第3个相等转移到7
//000010 00000 00000 0000000000000011 转移到3
//相当于以下程序:
// reg[1] = 8
// reg[2] = 2
// reg[3] = 0
//M: reg[3] = reg[3]+reg[2]
// if reg[1] == reg[3]: goto N
// goto M
//N:
module tp;
reg clk=0;
CPU cpu_(clk);
always #10 clk = ~clk;
endmodule
仿真结果:
单周期CPU压缩包下载
开了动态调分,初始积分是0.