1.目的:
掌握RISC CPU与内存数据交换的方法,使用verilog设计一个多周期cpu,根据所给的mips指令集来实现
以数字电路为基础,设计基础组件
以组成原理为基础,各组件为原料,构造Minisys-1 CPU
能够使用vivado等软件通过verilog HDL语言实现cpu模型机的设计
五级流水线:取指,译码,执行,访存,回写
2.基本简况:
处理机机器字长32位,指令字长32位,定长指令,多周期
指令集:R,I,J三类
R型指令 op为0
Op6位 |
Rs 5位 |
Rt 5位 |
Rd 5位 |
Shamt 5位(op2) |
Funct6位(op3) |
I型指令
Op 6位 |
Rs 5位 |
Rt 5位 |
Immediate16位 |
J型指令
Op 6位 |
address26位 |
op:为操作码;
rs:为第1个源操作数寄存器,寄存器地址(编号)是00000~11111,00~1F;
rt:为第2个源操作数寄存器,或目的操作数寄存器,寄存器地址(同上);
rd:为目的操作数寄存器,寄存器地址(同上);
sa:为位移量(shift amt),移位指令用于指定移多少位;
funct:为功能码,在寄存器类型指令中(R类型)用来指定指令的功能;
immediate:为16位立即数,用作无符号的逻辑操作数、有符号的算术操作数、数据加载(Load)/数据保存(Store)指令的数据地址字节偏移量和分支指令中相对程序计数器(PC)的有符号偏移量;
address:为地址。
3.涉及到的指令
助记符 |
指 令 格 式 |
示 例 |
示例含义 |
操作及解释 |
|||||
BIT # |
[31:26] |
[25:21] |
[20:16] |
[15:11] |
[10:6] |
[5:0] |
|
|
|
R-类型 |
op |
rs |
rt |
rd |
shamt |
func |
|
|
|
add |
000000 |
rs |
rt |
rd |
00000 |
100000 |
add $1,$2,$3 |
$1=$2+S3 |
(rd)←(rs)+(rt); rs=$2,rt=$3,rd=$1 |
addu |
000000 |
rs |
rt |
rd |
00000 |
100001 |
addu $1,$2,$3 |
$1=$2+S3 |
(rd)←(rs)+(rt); rs=$2,rt=$3,rd=$1,无符号数 |
sub |
000000 |
rs |
rt |
rd |
00000 |
100010 |
sub $1,$2,$3 |
$1=$2-S3 |
(rd)←(rs)-(rt); rs=$2,rt=$3,rd=$1 |
subu |
000000 |
rs |
rt |
rd |
00000 |
100011 |
subu $1,$2,$3 |
$1=$2-S3 |
(rd)←(rs)-(rt); rs=$2,rt=$3,rd=$1,无符号数 |
and |
000000 |
rs |
rt |
rd |
00000 |
100100 |
and $1,$2,$3 |
$1=$2&S3 |
(rd)←(rs)&(rt); rs=$2,rt=$3,rd=$1 |
or |
000000 |
rs |
rt |
rd |
00000 |
100101 |
or $1,$2,$3 |
$1=$2|S3 |
(rd)←(rs) | (rt); rs=$2,rt=$3,rd=$1 |
xor |
000000 |
rs |
rt |
rd |
00000 |
100110 |
xor $1,$2,$3 |
$1=$2^S3 |
(rd)←(rs)^(rt); rs=$2,rt=$3,rd=$1 |
000000 |
00000 |
rt |
rd |
00000 |
000000 |
sll $1,$2,10 |
S1 = s2 <<10 |
S1(rd)←(rt)<<shamt |
|
nor |
000000 |
rs |
rt |
rd |
00000 |
100111 |
nor $1,$2,$3 |
$1= ~($2 | S3) |
(rd)←~((rs) | (rt)); rs=$2,rt=$3,rd=$1 |
I-类型 |
op |
rs |
rt |
immediate |
|
||||
addi |
001000 |
rs |
rt |
immediate |
addi $1,$2,10 |
$1=$2+10 |
(rt)←(rs)+(sign-extend)immediate,rt=$1,rs=$2 |
||
addiu |
001001 |
rs |
rt |
immediate |
addiu $1,$2,10 |
$1=$2+10 |
(rt)←(rs)+(sign-extend)immediate,rt=$1,rs=$2 |
||
andi |
001100 |
rs |
rt |
immediate |
andi $1,$2,10 |
$1=$2&10 |
(rt)←(rs)&(zero-extend)immediate,rt=$1,rs=$2 |
||
ori |
001101 |
rs |
rt |
immediate |
ori $1,$2,10 |
$1=$2|10 |
(rt)←(rs)|(zero-extend)immediate,rt=$1,rs=$2 |
||
xori |
001110 |
rs |
rt |
immediate |
xori $1,$2,10 |
$1=$2^10 |
(rt)←(rs)^(zero-extend)immediate,rt=$1,rs=$2 |
||
lui |
001111 |
00000 |
rt |
immediate |
lui $1,10 |
$1=10*65536 |
(rt)←immediate<<16 & 0FFFF0000H,将16位立即数放到目的寄存器高16位,目的寄存器的低16位填0 |
||
lw |
100011 |
rs |
rt |
offset |
lw $1,10($2) |
$1=Memory[ $2+10] |
(rt)←Memory[(rs)+(sign_extend)offset],rt=$1,rs=$2 |
||
sw |
101011 |
rs |
rt |
offset |
sw $1,10($2) |
Memory[ $2+10] =$1 |
Memory[(rs)+(sign_extend)offset]←(rt), rt=$1,rs=$2 |
||
beq |
000100 |
rs |
rt |
offset |
beq $1,$2,40 |
if($1=$2) |
if ((rt)=(rs)) then (PC)←(PC)+4+( (Sign-Extend) offset<<2), rs=$1, rt=$2 |
||
J-类型 |
op |
address |
|
||||||
j |
000010 |
address |
j 10000 |
goto 10000 |
(PC)←( (Zero-Extend) address<<2), address=10000/4 |
||||
jal |
000011 |
address |
jal 10000 |
$31=PC+4 goto 10000 |
($31)←(PC)+4; (PC)←( (Zero-Extend) address<<2),address=10000/4 |
4程序测试结果
测试代码
5系统结构图
主要构成部分:
pc模块,pc,if_id,control,de_ex,ex,ex_mem,memory,mem_writeback,rf,im,dm,mips_c,mipstop,激励文件
由于代码太多,就不一一粘贴了,只放核心代码了
控制器:
(1)基本描述
对来自去取指阶段的指令进行译码,分配读取寄存器数据,分配操作码
(2)模块接口
Inst_i |
In |
来自中间寄存器的指令码 |
Pc_i |
In |
指令地址 |
Reg1_data_i 1/2 |
In |
从通用寄存器组rf传来的数据 |
Aluop_o |
Out |
译码阶段产生的运算子类型 |
Alusel_op |
Out |
译码阶段产生的运算类型 |
Branch_flag_o |
Out |
跳转标志 |
Branc_target_address_o |
Out |
跳转地址 |
Inst_o |
Out |
指令 |
Link_addr_o |
Out |
要存储的指令地址(jal) |
Reg1(2)_addr_o |
Out |
读取寄存器号 |
Reg1(2)_o |
Out |
操作数数据,传递给下一个中间寄存器 |
Reg1(2)_read_o |
Out |
读使能信号,传递给通用寄存器组 |
Wd_o |
Out |
写寄存器的寄存器号 |
Wreg_o |
Out |
是否写寄存器 |
(3)功能定义
将指令进行译码,并从通用寄存器组读取所需要的数据,将来自上一阶段的部分数据传递到下一阶段,所有模块的核心
(4)模块描述
`include "base.v"
module controlmoudle(
input wire rst,
input wire[`InstAddrBus] pc_i,
input wire[`InstBus] inst_i,
input wire[`RegBus] reg1_data_i,
input wire[`RegBus] reg2_data_i,
//传�?�到RF
output reg reg1_read_o,
output reg reg2_read_o,
output reg[`RegAddrBus] reg1_addr_o,
output reg[`RegAddrBus] reg2_addr_o,
//传�?�到下一阶段
output reg[`AluOpBus] aluop_o,
output reg[`AluSelBus] alusel_o,
output reg[`RegBus] reg1_o,
output reg[`RegBus] reg2_o,
output reg[`RegAddrBus] wd_o,
output reg wreg_o,
output wire[`RegBus] inst_o,
output reg branch_flag_o, //分支标志
output reg[`RegBus] branch_target_address_o,//分支跳转目标
output reg[`RegBus] link_addr_o
);
wire[5:0] op = inst_i[31:26]; //顶级区分op
wire[4:0] op2 = inst_i[10:6]; //次级sa
wire[5:0] op3 = inst_i[5:0]; //低级funct
reg[`RegBus] imm;
reg instvalid;
wire[`RegBus] pc_plus_4;
wire[`RegBus] imm_sll_2_signedext;
assign pc_plus_4 = pc_i +4; //下一条指令
assign imm_sll_2_signedext = {{14{inst_i[15]}}, inst_i[15:0], 2'b00 }; //立即数符号位扩展,左移2位
assign inst_o = inst_i;
always @ (*) begin
if (rst == `RstEnable) begin
aluop_o <= `EXE_NOP_OP;
alusel_o <= `EXE_RES_NOP;
wd_o <= `NOPRegAddr;
wreg_o <= `WriteDisable;
reg1_read_o <= 1'b0;
reg2_read_o <= 1'b0;
reg1_addr_o <= `NOPRegAddr;
reg2_addr_o <= `NOPRegAddr;
imm <= 32'h0;
link_addr_o <= `ZeroWord;
branch_target_address_o <= `ZeroWord;
branch_flag_o <= `NotBranch;
end else begin
aluop_o <= `EXE_NOP_OP;
alusel_o <= `EXE_RES_NOP;
wd_o <= inst_i[15:11];
wreg_o <= `WriteDisable;
instvalid <= `InstInvalid;
reg1_read_o <= 1'b0;
reg2_read_o <= 1'b0;
reg1_addr_o <= inst_i[25:21];
reg2_addr_o <= inst_i[20:16];
imm <= `ZeroWord;
link_addr_o <= `ZeroWord;
branch_target_address_o <= `ZeroWord;
branch_flag_o <= `NotBranch;
case (op)
6'b000000: begin
case (op2)
5'b00000: begin
case (op3)
`EXE_OR: begin
wreg_o <= `WriteEnable; aluop_o <= `EXE_OR_OP;
alusel_o <= `EXE_RES_LOGIC; reg1_read_o <= 1'b1; reg2_read_o <= 1'b1;
end
`EXE_AND: begin
wreg_o <= `WriteEnable; aluop_o <= `EXE_AND_OP;
alusel_o <= `EXE_RES_LOGIC; reg1_read_o <= 1'b1; reg2_read_o <= 1'b1;
end
`EXE_XOR: begin
wreg_o <= `WriteEnable; aluop_o <= `EXE_XOR_OP;
alusel_o <= `EXE_RES_LOGIC; reg1_read_o <= 1'b1; reg2_read_o <= 1'b1;
end
`EXE_NOR: begin
wreg_o <= `WriteEnable; aluop_o <= `EXE_NOR_OP;
alusel_o <= `EXE_RES_LOGIC; reg1_read_o <= 1'b1; reg2_read_o <= 1'b1;
end
`EXE_ADD: begin
wreg_o <= `WriteEnable; aluop_o <= `EXE_ADD_OP;
alusel_o <= `EXE_RES_ARITHMETIC; reg1_read_o <= 1'b1; reg2_read_o <= 1'b1;
end
`EXE_ADDU: begin
wreg_o <= `WriteEnable; aluop_o <= `EXE_ADDU_OP;
alusel_o <= `EXE_RES_ARITHMETIC; reg1_read_o <= 1'b1; reg2_read_o <= 1'b1;
end
`EXE_SUB: begin
wreg_o <= `WriteEnable; aluop_o <= `EXE_SUB_OP;
alusel_o <= `EXE_RES_ARITHMETIC; reg1_read_o <= 1'b1; reg2_read_o <= 1'b1;
end
`EXE_SUBU: begin
wreg_o <= `WriteEnable; aluop_o <= `EXE_SUBU_OP; sel_o <= `EXE_RES_ARITHMETIC; reg1_read_o <= 1'b1; reg2_read_o <= 1'b1;
end
default: begin
end
endcase
end
default: begin
end
endcase
end
`EXE_ORI: begin //ORIָ
wreg_o <= `WriteEnable; aluop_o <= `EXE_OR_OP;
alusel_o <= `EXE_RES_LOGIC; reg1_read_o <= 1'b1; reg2_read_o <= 1'b0;
imm <= {16'h0, inst_i[15:0]}; wd_o <= inst_i[20:16];
end
`EXE_ANDI: begin
wreg_o <= `WriteEnable; aluop_o <= `EXE_AND_OP;
alusel_o <= `EXE_RES_LOGIC; reg1_read_o <= 1'b1; reg2_read_o <= 1'b0;
imm <= {16'h0, inst_i[15:0]}; wd_o <= inst_i[20:16];
end
`EXE_XORI: begin
wreg_o <= `WriteEnable; aluop_o <= `EXE_XOR_OP;
alusel_o <= `EXE_RES_LOGIC; reg1_read_o <= 1'b1; reg2_read_o <= 1'b0;
imm <= {16'h0, inst_i[15:0]}; wd_o <= inst_i[20:16];
end
`EXE_LUI: begin
wreg_o <= `WriteEnable; aluop_o <= `EXE_OR_OP;
alusel_o <= `EXE_RES_LOGIC; reg1_read_o <= 1'b1; reg2_read_o <= 1'b0;
imm <= {inst_i[15:0], 16'h0}; wd_o <= inst_i[20:16]; end
`EXE_ADDI: begin
wreg_o <= `WriteEnable; aluop_o <= `EXE_ADDI_OP;
alusel_o <= `EXE_RES_ARITHMETIC; reg1_read_o <= 1'b1; reg2_read_o <= 1'b0;
imm <= {{16{inst_i[15]}}, inst_i[15:0]}; wd_o <= inst_i[20:16]; end
`EXE_J: begin
wreg_o <= `WriteDisable; aluop_o <= `EXE_J_OP;
alusel_o <= `EXE_RES_JUMP_BRANCH; reg1_read_o <= 1'b0; reg2_read_o <= 1'b0;
link_addr_o <= `ZeroWord;
branch_target_address_o <= {pc_plus_4[31:28], inst_i[25:0], 2'b00};
branch_flag_o <= `Branch;
end
`EXE_JAL: begin
wreg_o <= `WriteEnable; aluop_o <= `EXE_JAL_OP;
alusel_o <= `EXE_RES_JUMP_BRANCH; reg1_read_o <= 1'b0; reg2_read_o <= 1'b0;
wd_o <= 5'b11111;
link_addr_o <= pc_plus_4 + 4 ;
branch_target_address_o <= {pc_plus_4[31:28], inst_i[25:0], 2'b00};
branch_flag_o <= `Branch;
end
`EXE_BEQ: begin
wreg_o <= `WriteDisable; aluop_o <= `EXE_BEQ_OP;
alusel_o <= `EXE_RES_JUMP_BRANCH; reg1_read_o <= 1'b1; reg2_read_o <= 1'b1;
if(reg1_o == reg2_o) begin
branch_target_address_o <= pc_plus_4 + imm_sll_2_signedext+4;
branch_flag_o <= `Branch;
end
end
`EXE_LW: begin
wreg_o <= `WriteEnable; aluop_o <= `EXE_LW_OP;
alusel_o <= `EXE_RES_LOAD_STORE; reg1_read_o <= 1'b1; reg2_read_o <= 1'b0;
wd_o <= inst_i[20:16];
end
`EXE_SW: begin
wreg_o <= `WriteDisable; aluop_o <= `EXE_SW_OP;
reg1_read_o <= 1'b1; reg2_read_o <= 1'b1;
alusel_o <= `EXE_RES_LOAD_STORE;
end
default: begin
end
endcase //case op
if ((inst_i[31:21] == 11'b00000000000) && inst_i[5:0] == 6'b000000)begin
if (op3 == `EXE_SLL) begin
wreg_o <= `WriteEnable; aluop_o <= `EXE_SLL_OP;
alusel_o <= `EXE_RES_SHIFT; reg1_read_o <= 1'b0; reg2_read_o <= 1'b1;
imm[4:0] <= inst_i[10:6]; wd_o <= inst_i[15:11];
end
end
end //if
end //always
// 1
always @ (*) begin
if(rst == `RstEnable) begin
reg1_o <= `ZeroWord;
end else if(reg1_read_o == 1'b1) begin
reg1_o <= reg1_data_i;
end else if(reg1_read_o == 1'b0) begin
reg1_o <= imm;
end else begin
reg1_o <= `ZeroWord;
end
end
//2
always @ (*) begin
if(rst == `RstEnable) begin
reg2_o <= `ZeroWord;
end else if(reg2_read_o == 1'b1) begin
reg2_o <= reg2_data_i;
end else if(reg2_read_o == 1'b0) begin
reg2_o <= imm;
end else begin
reg2_o <= `ZeroWord;
end
end
endmodule
顶层文件
mipstop模块
(1)基本描述
(2)模块接口
Clk |
In |
时钟信号 |
Rst |
In |
复位信号 |
(3)功能定义
初始化dm,mips_c,im模块,传递激励信号
(4)模块描述
`include "base.v"
module mipstop(
input wire clk,
input wire rst
);
wire[`InstAddrBus] inst_addr;
wire[`InstBus] inst;
wire rom_ce;
wire[`RegBus] mem_addr_i;
wire[`RegBus] mem_data_i;
wire[`RegBus] mem_data_o;
wire mem_ce_i;
mips_C mipsc(
.clk(clk),
.rst(rst),
.rom_addr_o(inst_addr),
.rom_data_i(inst),
.rom_ce_o(rom_ce),
.ram_addr_o(mem_addr_i),
.ram_data_o(mem_data_i),
.ram_data_i(mem_data_o),
.ram_ce_o(mem_ce_i)
);
IM U_IM(
.ce(rom_ce),
.addr(inst_addr),
.inst(inst)
);
DM U_DM(
.clk(clk),
.addr(mem_addr_i),
.data_i(mem_data_i),
.data_o(mem_data_o),
.ce(mem_ce_i)
);
endmodule
搭建的环境:vivado 2017