8位CPU在FPGA上实现(2)

Posted on 2023-01-29 20:54  _Chapman  阅读(181)  评论(0编辑  收藏  举报

所用指令

该CPU采用的指令参考MISP指令作出修改,采用ADDU(无符号相加)、SUBU(无符号相减)、SLL(左移)、ORI(扩展到32位)、LW(地址加偏移一个字读取,即读取一个字的数据)、SW(地址加偏移一个字写入,即写入一个字的数据)、BEQ(比较寄存器的数值,再进行跳转)、J(在256MB范围内跳转)来实现一个简单CPU功能电路。

指令详解

image
上图是MISP的32位指令结构简图,这种指令分为R、I、J类型。R类型,从两个寄存器中得到需要运算的数值,具体操作由op和func决定。rs和rt是两个寄存器编号,sa只有在位移操作中使用。I指令是立即数指令,具体是低16位为一个立即数,op为操作指令,rs和rt是两个用于操作的寄存器。J指令一般指跳转,高6位为操作码,后续为操作地址。

ADDU

R指令的一种,关于R指令,31~26位均为0。
image
将寄存器中数值相加:rs+rt,放到rd寄存器中。

SUBU

R指令的一种
image
将寄存器中数值相减:rs-rt,放到rd寄存器中。

SLL

R指令的一种,左移指令。
image
将rt中的数值进行左移shamt位,再赋值到rd里面。

ORI

一个I指令
image
将立即数扩展到32位,或上rs寄存器的数值,然后赋值到rt。

LW

一个I指令
image
将指定地址一个字的数据加载到指定寄存器中。
立即数为地址,然后加上偏移量rs,读出数据加载到rt里面。

SW

一个I指令
image
将指定地址一个字的数据写入到指定寄存器中。
立即数为地址,然后加上偏移量rs,rt里面的数据写入该地址。

BEQ

一个I指令
image
将立即数先左移两位,如果rs等于rt的数值,则PC跳转到PC+4+立即数左移两位的地址。

J

一个J指令
image
直接跳转到256M以内的地址。

功能模块详解

指令译码器

再PC读取出来的指令被送入指令译码器。根据上述的指令,可以分为R指令和非R指令,因为R指令开头6位均为0。
假如输入信号为:
input [31:0] inst
input zero

输出为:
output IM_R,
output M6,
output M5,
output M4,
output M3,
output M2,
output M1,
output [2:0] ALUC,
output RF_W,
output DM_CS,
output DM_R,
output DM_w,
output signed16
output DM_R,

定义一个wire [5:0] func = inst[5:0];//用来读取低6位功能
定义一个wire [5:0] op = inst[31:26];//用来读取指令高6位是否全部为零
定义全部状态:
wire R_type;
wire addu;
wire subu;
wire sll;
wire ori;
wire lw;
wire sw;
wire beq;
wire j;

assign R_type = ~op[5]& ~op[4]& ~op[3]& ~op[2]& ~op[1]& ~op[0];
//如果是R指令,R_type为1
assign addu = R_type& func[5]& ~func[4]& ~func[3]& ~func[2]& ~func[1]& func[0];
//如果是addu指令,addu为1;
assign subu = R_type& func[5]& ~func[4]& ~func[3]& ~func[2]& func[1]& func[0];
//如果是subu指令,subu为1;
assign sll = R_type& ~func[5]& ~func[4]& ~func[3]& ~func[2]& ~func[1]& ~func[0];
//如果是sll指令,sll为1;
assign ori = ~R_type& ~op[5]& ~op[4]& op[3]& p[2]& ~op[1]& op[0];
//如果是ori指令,则ori为1;
assign lw = ~R_type& op[5]& ~op[4]& ~op[3]& ~op[2]& op[1]& op[0];
//如果是lw指令,则lw为1;
assign sw = ~R_type& op[5]& ~op[4]& op[3]& ~op[2]& op[1]& op[0];
//如果是sw指令,则sw为1;
assign beq = ~R_type& ~op[5]& ~op[4]& ~op[3]& op[2]& ~op[1]& ~op[0];
//如果是beq指令,则beq为1;
assign j = ~R_type& ~op[5]& ~op[4]& ~op[3]& ~op[2]& op[1]& ~op[0];
//如果是j指令,则j为1;

//定义寄存器写信号
//因为ADDU、SUBU、SLL、ORI、LW都要将数据写入rt寄存器,所以该位需要激活
assign RF_W = addu|subu|sll|ori|lw;
//从存储器读取指令一直使能就可以
assign IM_R = 1;
//如果操作存储器,则使能操作
//(注意,数据存储器和指令存储器是在同一从存储器)
assign DM_CS = lw|sw;
//如果操作读取存储器数据,则使能操作
//(注意,如果要间存储器数据写入寄存器,则需要激活此位)
assign DM_R = lw;
//如果操作写入存储器数据,则使能操作
//(注意,如果要间存储器数据写入寄存器,则需要激活此位)
assign DM_W = sw;

//ALU控制端,给了三个输入口
ALUC[0] = 0;//这一位我暂时不知道为什么一直拉低
ALUC[1] = ori|sll;//位移和逻辑操作
ALUC[2] = sll|subu|beq;//含有加减操作

//mux控制选择器
assign M1 = addu|subu|sll|ori|lw|sw|beq;//不含有跳转操作
assign M2 = addu|subu|sll|ori|j|sw|beq;//含有跳转操作
assign M3 = addu|subu|ori|beq|sw|lw;
assign M4 = ori|sw|lw;
assign M5 = zero & beq;
assign M6 = ori|lw;

//16位有无符号拓展
assign signed16 = sw|lw;

可能有点乱,下面整理一个译码器真值表:
image