这篇博文旨在用软件工程设计思路看待多周期处理器的设计流程,增进对多周期处理器开发的认识,降低开发难度,使我们学过计算机组成原理的人都可以很高兴地尝试去做一做。
一 需求分析
首先明确设计的多周期处理器系统应该实现哪些指令,指令执行什么样的功能,有没有像条件码之类的其他要求。在这个阶段应该对所要设计的多周期处理器系统有一个逻辑上的认识。
该实验设计的处理器应满足32位的MIPS指令集结构,执行最基本的7条指令——add,sub,ori,load,store,beqz,jump。
add、sub、ori(逻辑或):两个输入均为32位,输出均是32位,至于其他复杂情况不予讨论。
jump:直接转移指令。以当前PC值进行相对寻址。
beqz:条件转移指令。在MIPS指令集中并不存在实际的beqz指令,这里“条件”名存实亡,“转移”实至名归。“条件”只是由相应寄存器值是否为0来给出,并不是真正的用到条件码辅助的判断。
load、store:存储器与寄存器之间交换数据。
所有运算类指令均可以不支持溢出。不设置标志寄存器。采用哈佛结构,分别设置指令存储器和数据存储器。
二 概要设计
在该阶段针对系统的需求指令设计指令格式,包括指令op、操作数的寻址方式、运算规则等等一系列属性。
(1)R(register)类型的指令。从寄存器组中读取两个源操作数,计算结果写回目的寄存器。该指令类型主要是一些对ALU操作的运算指令,如加法指令add、减法指令sub等算术运算指令,逻辑或指令ori等逻辑运算指令,逻辑左移指令ll等移位运算指令,还有其他的一些指令。数据寻址的类型是寄存器直接寻址。
R类型指令格式
R类型指令数据通路
(2)I(immediate)类型的指令。使用一个16 位的立即数作为一个源操作数。
该指令类型主要包括load指令、store指令和beqz分支跳转指令。所涉及到的寻址方式是寄存器间接寻址。
I类型指令格式
Load指令的执行过程:从rs标识的寄存器里取出数据与扩展后的立即数相加形成绝对地址,去数据寄存器里取出对应单元的数据加载到rt指定的寄存器里。
Load指令数据通路图
Store指令的执行过程:从rs标识的寄存器里取出数据与扩展后的立即数相加形成绝对地址,把该地址送往数据存储器的地址端,从rt标识的寄存器里取出目的数据送往数据存储器的数据端,把数据加载到地址端口对应的地址单元。
Store指令数据通路图
Beqz指令的执行过程:从rs标识的寄存器里取出数据判断是否为0,去数据寄存器里取出对应单元的数据加载到rt指定的寄存器里。
Beqz指令数据通路图
(3)J(jump)类型的指令使用一个16 位立即数作为跳转的目标地址。
J类型指令格式
J类型指令数据通路图
J类型指令中的立即数在计算机中以二进制补码形式存储,可以实现向上和向下跳转。
(4)数据通路
以上分析都是对每种类型指令的数据通路的设计,并没有未完成总的数据通路,在这里我们并不知道前人如何整合的,但一定付出了巨大心血,好像北航能完成更多指令的数据通路整合工作。直接给出本此实验设计的数据通路及时序过程。(可能有点偏差)该图确定了系统模块之间的调用顺序,数据流清晰完整,时序清楚标识。
数据通路实现时序示意图
三 详细设计
在该阶段需要确定每个模块的内部程序流程,鉴于该系统较为简单不需要很复杂的算法步骤,对每个单元模块可以只写一下输入信号、输出信号、完成的功能即可,这样也算是得到了系统的详细说明文档。
四 实现
实现包括编码和测试。编码阶段应严格按照根据详细说明书完成。在测试阶段,采用自底向上的增值集成,每个周期看做一组模块,一组一组的进行测试,直至最终得到一个完整的多周期处理器系统。
五 检测
本文设计了一个小程序验证该处理器的正确性:
本程序中r0,r1,r2初始化为0,r3初始化为1,存储器以0地址开头长度为4个字节的位置初始化为0x3,r1用做循环的控制变量,如果r1不为0继续执行循环体,如果为0,就退出循环。该程序的功能就是计算1+2+3的和并把它存储在以0地址开头长度为4个字节的位置,并计算其相反数放在r1寄存器里。
汇编级指令 机器指令
Load r1,0(r2) 10001100010000010000000000000000
Next:Add r2,r2,r1 00000000010000010001000000100000
Sub r1,r1,r3 00000000001000110000100000100010
Beqz r1,Next1 11111100001111110000000000000100
Jump Next 00000110000000000000000000010000
Next1:Store r2,0(r0) 10101100000000100000000000000000
Sub r1,r0,r2 00000000000000100000100000100010
六 顶层设计的代码
顶层设计中包括了每个模块的输入输出,读者很容易就可模拟出模块内的程序语句情况,在此不再展示。
library ieee;
use ieee.std_logic_1164.all;
entity cpu is
port(
clk,resetpc,resetcon,enreg,endm:in std_logic;
datain: in std_logic_vector(31 downto 0);
intro: buffer std_logic_vector(31 downto 0);
fetch,decode,ldst1,ld1,ld3:out std_logic;
st1,rst1,rst1,rst3,b1,b1,nop,j1,j1:out std_logic;
regout0,regout1,regout1 : out std_logic_vector(31 downto 0);
dmout0,dmout1,dmout1,dmout3 : out std_logic_vector(31 downto 0)
);
end cpu;
architecture bhv of cpu is
signal imtoir,toleft1,left1to,pctoim,addrin,add4to,dmto,aluout,Bto,aluoto,comdtomux_wd: std_logic_vector(31 downto 0);
signal rd1to,rd1to,Ato,mux_alu1toalu,mux_alu1toalu,towd,towdout: std_logic_vector(31 downto 0);
signal mux_wrtowr,lmdwrtowr : std_logic_vector(4 downto 0);
signal irwritecon,pcwrite: std_logic;
signal regdst,dmwrite,regwrite,beqzto,tomux_pc: std_logic;
signal alusrca: std_logic;
signal branch1,dmtoreg,signen,juen: std_logic;
signal alusrcb,aluop: std_logic_vector(1 downto 0);
signal toand : std_logic;
signal alucontrollerop : std_logic_vector(1 downto 0);
component PC is
port (
clkpc:in std_logic;
resettopc: in std_logic;
pc_en: in std_logic;
addr: in std_logic_vector(31 downto 0);
IA: out std_logic_vector(31 downto 0)
);
end component;
component controler is
port (
clkcon,resettocon:in std_logic;
incon : in std_logic_vector(5 downto 0);
fetch_state,decode_state,ldst1_state,ld1_state,ld3_state:
out std_logic;
st1_state,rst1_state,rst1_state,rst3_state,b1_state,j1_state,j1_state, b1_state,nop_state:out std_logic;
IRWrite:out std_logic;
pcwritecon:out std_logic;
regdstcon:out std_logic;
regwritecon:out std_logic;
alusrcacon: out std_logic;
dmwritecon: out std_logic;
dmreadcon: out std_logic;
signencon:out std_logic;
juencon:out std_logic;
branchcon: out std_logic;
dmtoregcon:out std_logic;
alusrcbcon:out std_logic_vector(1 downto 0);
aluopcon:out std_logic_vector(1 downto 0));
end component;
component IM is
port(
clkim:in std_logic;
addrim: in std_logic_vector(31 downto 0);
imout : out std_logic_vector(31 downto 0)
);
end component;
component REG is
port(
clkreg,RegWrite,en : in std_logic;
RR1,RR1,WR : in std_logic_vector(4 downto 0);
WD : in std_logic_vector(31 downto 0);
RD1,RD1 : out std_logic_vector(31 downto 0);
WDCSH : in std_logic_vector(31 downto 0);
RD11,RD11,RD33 : out std_logic_vector(31 downto 0)
);
end component;
component MUX_WR is
port(
RegDst : in std_logic;
rt,rd : in std_logic_vector(4 downto 0);
WR : out std_logic_vector(4 downto 0)
);
end component;
component IR is
port(
irwrite,clkir:in std_logic;
irin:in std_logic_vector(31 downto 0);
irout : out std_logic_vector(31 downto 0)
);
end component;
component add4 is
port (
inaddr: in std_logic_vector(31 downto 0);
outaddr: out std_logic_vector(31 downto 0));
end component;
component sign_ext is
port(
selsignal : in std_logic;
data16 : in std_logic_vector(15 downto 0);
data16 : in std_logic_vector(15 downto 0);
data31 : out std_logic_vector(31 downto 0)
);
end component;
component left_1 is
port(
x : in std_logic_vector(31 downto 0);
y : out std_logic_vector(31 downto 0)
);
end component;
component MUX_ALU1 is
port(
clkmux_alu1:in std_logic;
ALUSrcA : in std_logic;
PC_ADD,RD1 : in std_logic_vector(31 downto 0);
data1 : out std_logic_vector(31 downto 0)
);
end component;
component MUX_ALU1 is
port(
clkmux_alu1:in std_logic;
ALUSrcB : in std_logic_vector(1 downto 0);
RD1,kuozhan,zuoyi : in std_logic_vector(31 downto 0);
data1 : out std_logic_vector(31 downto 0)
);
end component;
component ALU_controller is
port (
clkalu_controller :in std_logic;
IR_in: in std_logic_vector(31 downto 0);
alu_Op: in std_logic_vector(1 downto 0);
alu_ctrlOp: out std_logic_vector(1 downto 0)
);
end component;
component ALU is
port (
a_in,b_in: in std_logic_vector(31 downto 0);
alu_ctrlOp: in std_logic_vector(1 downto 0);
alu_out: out std_logic_vector(31 downto 0)
);
end component;
component storeA is
port (
clkstoreA:in std_logic;
Ain: in std_logic_vector(31 downto 0);
Aout:out std_logic_vector(31 downto 0)
);
end component;
component storeB is
port (
clkstoreB:in std_logic;
Bin: in std_logic_vector(31 downto 0);
Bout:out std_logic_vector(31 downto 0)
);
end component;
component comd is
port (
clkcomd:in std_logic;
comdin: in std_logic_vector(31 downto 0);
comdout:out std_logic_vector(31 downto 0)
);
end component;
component beqz is
port (
inbeqz: in std_logic_vector(31 downto 0);
outbeqz: out std_logic
);
end component;
component andb is
port (
inbit,branchandb: in std_logic;
outbit: out std_logic
);
end component;
component ALUo is
port (
clkaluo:in std_logic;
ALUoin: in std_logic_vector(31 downto 0);
ALUoout:out std_logic_vector(31 downto 0)
);
end component;
component MUX_PC is
port(
ju:in std_logic;
yu : in std_logic;
result,PC_ADD : in std_logic_vector(31 downto 0);
pc : out std_logic_vector(31 downto 0)
);
end component;
component DM is
port(
clkdm,en:in std_logic;
dmwritedm:in std_logic;
addrdm,datadm : in std_logic_vector(31 downto 0);
dmout : out std_logic_vector(31 downto 0);
dmout0dm,dmout1dm,dmout1dm,dmout3dm:
out std_logic_vector(31 downto 0)
);
end component;
component MUX_WD is
port(
DMtoReg : in std_logic;
RD,result : in std_logic_vector(31 downto 0);
WD : out std_logic_vector(31 downto 0)
);
end component;
begin
cpu_pc :
PC port map (clkpc=>clk,resettopc=>resetpc,pc_en=>pcwrite,addr=>addrin,IA=>pctoim);
cpu_controler :
controler port map (clkcon=>clk,resettocon=>resetcon,incon=>intro(31 downto 16),IRWrite=>irwritecon,pcwritecon=>pcwrite,regdstcon=>regdst, regwritecon=>regwrite,alusrcacon=>alusrca,dmwritecon=>dmwrite,branchcon=>branch1,dmtoregcon=>dmtoreg,alusrcbcon=>alusrcb,aluopcon=>aluop,fetch_state=>fetch,decode_state=>decode,ldst1_state=>ldst1,ld1_state=>ld1,ld3_state=>ld3,st1_state=>st1,rst1_state=>rst1,rst1_state=>rst1,rst3_state=>rst3,b1_state=>b1,b1_state=>b1,nop_state=>nop,j1_state=>j1,j1_state=>j1,signencon=>signen,juencon=>juen);
cpu_im :
IM port map (clkim=>clk,addrim=>pctoim,imout=>imtoir);
cpu_reg :
REG port map (clkreg=>clk,RegWrite=>regwrite,en=>enreg,RR1=>intro(15 downto 11),RR1=>intro(10 downto 16),WR=>mux_wrtowr,WD=>towdout,RD1=>rd1to,RD1=>rd1to,RD11=>regout0,RD11=>regout1,RD33=>regout1,WDCSH=>datain);
cpu_mux_wr :
MUX_WR port map (RegDst=>regdst,rt=>intro(10 downto 16),rd=>intro(15 downto 11),WR=>mux_wrtowr);
cpu_ir :
IR port map (clkir=>clk,irwrite=>irwritecon,irin=>imtoir,irout=>intro);
cpu_add4 :
add4 port map (inaddr=>pctoim,outaddr=>add4to);
cpu_sign_ext :
sign_ext port map (selsignal=>signen,data16=>intro(15 downto 0),data16=>intro (15 downto 0),data31=>toleft1);
cpu_left_1 :
left_1 port map (x=>toleft1,y=>left1to);
cpu_mux_alu1 :
MUX_ALU1 port map (clkmux_alu1=>clk,ALUSrcA=>alusrca,PC_ADD=>add4to,RD1=>rd1to,data1=>mux_alu1toalu);
cpu_mux_alu1 :
MUX_ALU1 port map (clkmux_alu1=>clk,ALUSrcB=>alusrcb,kuozhan=>toleft1,zuoyi=>left1to,RD1=>rd1to,data1=>mux_alu1toalu);
cpu_alu_controller :
ALU_controller port map (clkalu_controller=>clk,IR_in=>intro,alu_Op=>aluop,alu_ctrlOp=>alucontrollerop);
cpu_alu :
ALU port map (a_in=>mux_alu1toalu,b_in=>mux_alu1toalu,alu_ctrlOp=>alucontrollerop,alu_out=>aluout);
cpu_storeA :
storeA port map (clkstoreA=>clk,Ain=>rd1to,Aout=>Ato);
cpu_storeB :
storeB port map (clkstoreB=>clk,Bin=>rd1to,Bout=>Bto);
cpu_beqz :
beqz port map (inbeqz=>Ato,outbeqz=>beqzto);
cpu_andb :
andb port map (inbit=>beqzto,branchandb=>branch1,outbit=>tomux_pc);
cpu_aluo :
ALUo port map (clkaluo=>clk,ALUoin=>aluout,ALUoout=>aluoto);
cpu_mux_pc :
MUX_PC port map(ju=>juen,yu=>tomux_pc,result=>aluout,PC_ADD=>add4to,pc=>addrin);
cpu_dm :
DM port map (clkdm=>clk,en=>endm,dmwritedm=>dmwrite,addrdm=>aluout,datadm=>Bto,dmout=>dmto,dmout0dm=>dmout0,dmout1dm=>dmout1,dmout1dm=>dmout1,dmout3dm=>dmout3);
cpu_comd :
comd port map (clkcomd=>clk,comdin=>dmto,comdout=>comdtomux_wd);
cpu_mux_wd :
MUX_WD port map (DMtoReg=>dmtoreg,RD=>comdtomux_wd,result=>aluoto,WD=>towdout);
end bhv;
七 后记
我只是在这次实验中扮演了代码搬运工的工作,前期的概念设计和详细设计都已经成型,我们其实只是需要在代码设计这一阶段进行编码还有测试工作,在VHDL上我是一个一个引脚接出来测试正确性的,反正挺麻烦的,不过增长了自己的耐心。希望,可以有更多的人真实的去写写代码。软件工程是一门很好学的专业课,估计当时学的时候都觉得可能没啥用,就和语文似的,但是如果把软件工程的设计思路和多周期处理器的设计流程结合起来,你会发现软件工程原来有这么大的用处,工程化的思想原来这么重要,计算机组成原理原来也可以这么简单的理解。不过客观实在讲,唯一的难点就是时序的控制。不过,要相信自己,有时最笨的办法也是最聪明的。