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