《简化CPU设计》-
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
电子科技大学通信学院
简化CPU设计
2013年9月
一、课程设计要求
1.设计16位精简指令集CPU指令系统;
2.完成精简指令集CPU的结构设计和所有模块的代码编写,并仿真验证;
3.编写能够完成加法器﹑流水灯等功能的汇编程序,并翻译成二进制机器码;
4.设计CPU外围模块如分频器,存储器和IO接口,并在软件平台上仿真CPU
执行程序的完整过程;
5.下载工程到FPGA芯片,在硬件资源上实现。
二、设计思路
1、CPU指令集系统设计
本课程设计所设计的RISC_CPU指令长度为16位,能够处理16位数据,指令中需要操作符,寄存器地址和立即数等字段。
完成立即数数据载入操作需要如下指令:
mil:将立即数放在低8位
mih:将立即数放在高8位
因为一条指令无法载入完整16比特立即数数据,设计指令格式中用于存放立即数的字段为8bits,将16bits数据传递到通用寄存器需要2条指令,“mil R1,I(低8位)”将立即数I 的低8位传递给通用寄存器R1,“mih R1,I(高8位)”将立即数I的高8位传递给通用寄存器R1。
完成存储器或I/O数据载入与存储的操作需要如下指令:
lda:载入指定地址数据
sta:储存数据到指定地址
inp:从端口输入
oup:输出到端口
因为存储器中有些地址的数据可能是有工程意义的,对这些地址上的数据的处理是必不可少的。
“lda Rd Rs”将通用寄存器Rs的数据作为指定地址,将存储器中该地址上的数据载入到通用寄存器Rd中,“sta Rd Rs”将通用寄存器Rd的数据作为指定地址,将通用寄存器Rs的数据储存到存储器该地址上。
完成通用寄存器阵列内数据运算操作需要如下指令:
and:寄存器数据与操作
orr:寄存器数据或操作
not:寄存器数据非操作
shl:左移
shr:右移
add:寄存器数据相加
sub:寄存器数据相减
mul:寄存器数据相乘
cmp:寄存器数据相比较
这些是本CPU设计能够完成的数据处理操作,有3点需要注意:1、所有操作的数据必须储存于通用寄存器中2、乘法运算只能进行8比特数据相乘,溢出则取其低8位数据相乘3、cmp指令的结果会影响标志位,该标志位可作为分支操作的条件,但执行cmp指令之前建议先清除相关标志位。
完成对标志位的处理操作需要如下指令:
szf:对零标志位置1
czf:清除零标志位
scf:对进位标志位置1
ccf:清除进位标志位
设计这些指令为分支操作的执行创造了条件,还需要注意其它指令在执行过程中同样可能影响标志位的值。
完成指令跳转、分支操作需要如下指令:
spc:保存pc的值
jpa:跳转到指定位置
jpr:跳转到相关位置
brz:以零标志位为条件的分支
brc:以进位标志位为条件的分支
spc、jpa、jpr指令为程序的循环执行创造了基础,brz、brc指令为程序的分支执行创造了基础。
如果一个程序不只是顺序执行,那么这些指令是必不可少的。
“spc Rd I”将当前PC值与立即数I相加的结果送给通用寄存器Rd,“brc I”如果进位标志位C为1,将当前PC的值与立即数I相加的结果作为PC的值。
完成对窗口指针的操作需要如下指令:
cwp:清除窗口指针
awp:窗口指针与立即数相加
窗口指针用于存放通用寄存器的窗口偏移值,达到扩大通用寄存器数量的目的。
在最终的指令格式中通用寄存器地址(编号)只有两比特,也就是直接表示只能表示4个寄存器,经验证无法完成本课程设计所设计所要求的完成流水灯的程序编写,而加上窗口指针偏移值后,就可以表示更多的通用寄存器以达到课程设计要求。
完成无操作、中断需要如下指令:
nop:无操作
hlt:中断
建议本课程设计所设计的CPU的指令格式为:
15-12bit规定指令的类型;
11-8bit选择寄存器,共设有8个寄存器,前2位为目的寄存器,后2位为源寄存器;
7-0bit为立即数。
部分示例指令如下表2-1所示,其中D、S分别为目的、源寄存器,I为立即数。
表2-1 部分指令集系统
2、精简指令集CPU结构设计
CPU整体设计
RISC_CPU是一个复杂的数字逻辑电路。
图2-1给出了CPU的整体结构。
具体接口信息如表2-2所示。
图2-1 精简指令集CPU的接口
表2-2 CPU接口说明
课程设计所设计的精简指令集CPU包括数据通路和控制器,如图2-2所示。
其中:
数据通路部分主要由以下基本部件组成:寻址单元(由程序计数器和地址逻辑组成)、算术逻辑单元、通用寄存器阵列、窗口指针、指令寄存器、状态寄存器。
控制器是7状态的状态机,不同状态下向数据通路发送不同的控制信号。
图2-2 精简指令集CPU层次结构
控制器有7个状态,包括复位、读取、译码、执行、和中断操作。
控制器发出的信号控制逻辑单元的操作和数据通路的寄存器时钟触发。
用Verilog描述CPU时,采用图2-2所示的层次结构。
数据结构是单独的部分,把它里面的各个单元连起来构成数据通路。
控制器也是单独的Verilog模块。
在整个Verilog设计中,用连线把数据通路和控制器连接起来。
数据结构
这里用组合逻辑和时序电路描述CPU的数据结构。
算术逻辑单元(ALU)进行算术和逻辑操作,是组合逻辑。
这类单元的功能受控制器控制。
时序电路的功能包括载入数据、复位电路等,这些功能也受控制器控制。
寻址单元:包括程序计数器和地址逻辑两部分。
程序计数器是带使能和复位功能的简单寄存器,地址逻辑是个小型的算术单元,通过加法和递增来计算程序计数器的值或储存器的地址。
这个单元有两个输入和一个输出。
一个输入为16比特,来自寄存器阵列;另一个输入为8比特,来自指令寄存器;输出为8比特地址。
寻址单元的控制信号包括ResetPC,PCplusI,PCplus1,RplusI,Rplus0和PCenable,这些信号决定寻址单元的输出内容。
寻
址单元根据输入控制信号生成程序计数器的输入信号,通过PCout传递给程序计数器。
图2-3给出了寻址单元的结构,具体接口的信息如表2-4所示。
图2-3 寻址单元结构
如执行指令jpa “跳转到指定位置”,二进制码设为1111-D-11-I ,表达式为PC<=Rd+I。
指令码1111-D-11在控制器处于exec1状态时输入控制器,产生一系列控制信号,例如控制信号RplusI从无效变为有效,产生的结果是寄存器输出寄存器编号为D的数据到地址逻辑的RSide接口,立即数I被指令寄存器送到地址逻辑ISide接口,地址逻辑将RSide的值与ISide的值相加,结果作为输出地址发送给存储器,下一条指令便从那个地址的指令开始执行。
表2-4 寻址单元接口说明
算数逻辑单元:了增加可读性,根据功能对它的输入控制信号进行了宏定义。
例如,让ALU执行加法操作的输入控制信号为“0000001000”,这个值被定义为AaddBH。
ALU 的输入信号有B15to0,AandB,AorB,notB,shlB,shrB,AaddB,AsubB,AmulB和AcmpB,这些信号选择ALU进行的操作。
为了确保不生成无用的锁存器,在always语句的开头把ALU的所有输出设为他们的无效值。
在case语句中,根据输入控制信号选择ALU进行的操作,得到相应的aluout和输出标志位。
图2-4给出了算术逻辑单元的结构,具体接口信息如表2-5所示。
图2-4 算术逻辑单元结构
如执行指令mih“将立即数放到高8位”二进制码为1111-D-01-I,表达式为Rdl<={I,8’BZ}。
指令码1111-D-01在控制器处于exec1状态时输入控制器,产生一系列控制信号,例如控制信号B15to0,产生的结果是立即数I被治理寄存器从低8位放到高8位,并经过算术逻辑单元回到数据总线,写入寄存器阵列D寄存器的高8位。
表2-5 算术逻辑单元具体接口信息
算术逻辑单元代码示例:
//Arithmetic Logic Unit (ALU)
`timescale 1 ns /1 ns
`define B15to0H 10'b1000000000
`define AandBH 10'b010*******
`define AorBH 10'b0010000000
`define notBH 10'b0001000000
`define shlBH 10'b0000100000
`define shrBH 10'b0000010000
`define AaddBH 10'b0000001000
`define AsubBH 10'b0000000100
`define AmulBH 10'b0000000010
`define AcmpBH 10'b0000000001
module ArithmeticUnit (
A, B,
B15to0, AandB, AorB, notB, shlB, shrB, AaddB, AsubB, AmulB, AcmpB, aluout, cin, zout, cout
);
input [15:0] A, B;
input B15to0, AandB, AorB, notB, shlB, shrB, AaddB, AsubB, AmulB, AcmpB;
input cin;
output [15:0] aluout;
output zout, cout;
reg [15:0] aluout;
reg zout, cout;
always @(
A or
B or B15to0 or AandB or AorB or notB or shlB or shrB or AaddB or AsubB or AmulB or AcmpB or cin
)
begin
zout = 0; cout = 0; aluout = 0;
case ({B15to0, AandB, AorB, notB, shlB, shrB, AaddB, AsubB, AmulB, AcmpB}) `B15to0H:aluout = B;
`AandBH: aluout = A & B;
`AorBH: aluout = A | B;
`notBH: aluout = ~B;
`shlBH: aluout = {B[14:0], B[15]};
`shrBH: aluout = {B[0], B[15:1]};
`AaddBH: {cout, aluout} = A + B + cin;
`AsubBH: {cout, aluout} = A - B - cin;
`AmulBH: aluout = A[7:0] * B[7:0];
`AcmpBH: begin
aluout = A;
if (A> B) cout = 1; else cout = 0;
if (A==B) zout = 1; else zout = 0;
end
default: aluout = 0;
endcase
if (aluout == 0) zout = 1'b1;
end
endmodule
module test_ArithmeticUnit;
reg [15:0] A, B;
reg B15to0, AandB, AorB, notB, shlB, shrB, AaddB, AsubB, AmulB, AcmpB;
reg cin=0;
wire [15:0] aluout;
wire zout, cout;
ArithmeticUnit MTU(A, B, B15to0, AandB, AorB, notB, shlB, shrB, AaddB, AsubB, AmulB, AcmpB,
aluout, cin, zout, cout);
initial begin
#10 A=4'b1101;
#10 B=4'b1101;
#10 {B15to0, AandB, AorB, notB, shlB, shrB, AaddB, AsubB, AmulB, AcmpB}=`B15to0H;
#10 {B15to0, AandB, AorB, notB, shlB, shrB, AaddB, AsubB, AmulB, AcmpB}=`AandBH;
#10 {B15to0, AandB, AorB, notB, shlB, shrB, AaddB, AsubB, AmulB, AcmpB}=`AorBH;
#10 {B15to0, AandB, AorB, notB, shlB, shrB, AaddB, AsubB, AmulB, AcmpB}=`notBH;
#10 {B15to0, AandB, AorB, notB, shlB, shrB, AaddB, AsubB, AmulB, AcmpB}=`shlBH;
#10 {B15to0, AandB, AorB, notB, shlB, shrB, AaddB, AsubB, AmulB, AcmpB}=`shrBH;
#10 {B15to0, AandB, AorB, notB, shlB, shrB, AaddB, AsubB, AmulB, AcmpB}=`AaddBH;
#10 {B15to0, AandB, AorB, notB, shlB, shrB, AaddB, AsubB, AmulB, AcmpB}=`AsubBH;
#10 {B15to0, AandB, AorB, notB, shlB, shrB, AaddB, AsubB, AmulB, AcmpB}=`AmulBH;
#10 {B15to0, AandB, AorB, notB, shlB, shrB, AaddB, AsubB, AmulB, AcmpB}=`AcmpBH;
#50 $stop;
end
endmodule
指令寄存器是高电平使能的16比特寄存器。
它只有一个输入控制信号:IRload。
图2-5指令寄存器结构
如执行指令awp“窗口指针与立即数相加”,二进制码为0000-10-10-I表达式为WP<=WP+I。
指令码0000-10-10在控制器处于exec1状态时输入控制器,产生一系列控制信号,例如控制信号WPadd,产生的结果是指令寄存器将立即数I送到窗口指针寄存器,与原窗口偏移值相加,其结果作为新的窗口偏移值。
寄存器阵列是带移动窗口指针的双端口储存器。
从存储器读数据时,窗口指针的基地址(Base)加上左偏移地址或右偏移地址(Laddr或Raddr),构成左路地址和右路地址(Laddress 和Raddress)。
这样,适当的存储器内容被送到左路和右路的输出上(Lout和Rout)。
向储存器写内容时用左路地址指向写入地址,因为左路地址是指令的目的地址。
当控制信号RFLwrite的值为1时,数据写入寄存器阵列的低8位,当RFHwrite的值为1时,数据写入寄存器阵列的高8位。
当这两个值都为1时,数据被写入地址为(Laddress+Base)的16比特字中。
图2-5给出了通用寄存器阵列的结构,具体接口信息如表2-6示。
图2-6通用寄存器阵列结构图
表2-6 通用寄存器阵列具体接口信息
状态寄存器:存储处理CPU运行过程产生的‘C’‘Z’状态,通过状态标志位的状态影响程序的运行。
图2-7状态寄存器结构
窗口指针:用于存放通用寄存器的窗口偏移值,达到扩大通用寄存器数量的目的。
因为本课程设计所设计的精简指令集CPU中,通用寄存器地址(编号)只有两比特,也就是直接表示只能表示4个寄存器;而加上窗口指针偏移值后,就可以表示更多的通用寄存器。
本CPU共设有8个通用寄存器。
图2-8窗口指针寄存器结构
控制器
控制器:控制器是7状态的状态机,不同状态下向数据通路发送不同的控制信号。
控制器采用Huffman风格的编码,通过把下一状态值赋给reg类型的变量Nstate,来实现状态转化。
下面分别介绍控制器的各个部分。
控制器端口:控制器的输入包括指令寄存器输出,ALU的标志位和外部控制信号。
控制器出37个信号给数据通路。
控制器的输出是reg类型,通过组合的always语句对它们赋值。
控制状态:参数声明里定义了7个状态。
Reset状态和halt状态分别对应状态机的初始和中断状态。
在fetch状态下,状态机读取16比特的指令。
在memread状态下,控制器等存储器准备好数据后发送memDataReady信号。
memread的下一状态是execl,在execl状态
下执行指令。
指令lda在状态execl下开始执行,但是它需要额外的状态(execllda)来完成对存储器的读操作。
在执行状态,大部分指令会让程序计数器的值加1,但是使用地址总线的某些指令不进行这个操作,它们在incpc状态下让程序计数器加1。
操作码:根据表2-1,指令的操作码被定义为4比特的参数。
状态声明:当前状态和下一状态分别声明为4比特的寄存器Nstate和Pstate。
组合逻辑块:这个组合逻辑块是一个always语句,其中case语句根据状态机的当前状态选择状态转化和控制信号的输出。
always语句开始时设置所用控制信号为无效状态,避免输出产生锁存器。
always @ (Instruction or Pstate or ExternalReset or Cflag or Zflag or memDataReady) begin
ResetPC = 1'b0;
PCplusI = 1'b0;
PCplus1 = 1'b0;
RplusI = 1'b0;
Rplus0 = 1'b0;
EnablePC = 1'b0;
B15to0 = 1'b0;
AandB = 1'b0;
AorB = 1'b0;
notB = 1'b0;
shrB = 1'b0;
shlB = 1'b0;
AaddB = 1'b0;
AsubB = 1'b0;
AmulB = 1'b0;
AcmpB = 1'b0;
RFLwrite = 1'b0;
RFHwrite = 1'b0;
WPreset = 1'b0;
WPadd = 1'b0;
IRload = 1'b0;
SRload = 1'b0;
Address_on_Databus = 1'b0;
ALU_on_Databus = 1'b0;
IR_on_LOpndBus = 1'b0;
IR_on_HOpndBus = 1'b0;
RFright_on_OpndBus = 1'b0;
ReadMem = 1'b0;
WriteMem = 1'b0;
ReadIO = 1'b0;
WriteIO = 1'b0;
Shadow = 1'b0;
Creset = 1'b0;
Zset = 1'b0;
Zreset = 1'b0;
Rs_on_AddressUnitRSide = 1'b0;
Rd_on_AddressUnitRSide = 1'b0;
时序逻辑块:控制器程序的最后一部分是时序的always语句,在时钟有效沿把Pstate 的值赋给Nstate。
控制状态寄存器和所有数据寄存器是下降沿触发,它们的值一直保持到下一个时钟下降沿到来时。
always @ (negedge clk)
Pstate = Nstate;
指令的执行:exec1状态下mvr指令的执行,该指令从寄存器阵列的右路地址读取一个字,写入左路地址。
在数据通路里,指令寄存器的输出连接到寄存器阵列的输入,得到左路和右路地址(目的和源)。
控制信号RFright_on_OpndBus的值为1时,RegisterFile的内容送到OpndBus上。
因为这条总线连接到ALU的输入端,ALU的另一输入端(B)的数据必须经过它送给输出。
因此将输入控制信号设为B15to0,直接输出B端的数据。
ALU的输出连接到寄存器阵列的输入端,控制信号RFLwrite和RFHwrite控制数据写入RegisterFile的目的位置。
mvr : begin
RFright_on_OpndBus = 1'b1;
B15to0 = 1'b1;
ALU_on_Databus = 1'b1;
RFLwrite = 1'b1;
RFHwrite = 1'b1;
SRload = 1'b1;
if (ShadowEn==1'b1)
Nstate = exec2;
else begin
PCplus1 = 1'b1;
EnablePC=1'b1;
Nstate = fetch;
end
end
CPU的大部分指令采用了上面讨论的执行过程。
然而,进行存储器访问的指令(如lda)需要另一个时钟周期来读取存储器。
指令lda执行的前半部分从寄存器阵列里读取地址送给地址总线,同时发出控制信号ReadMem,对存储器读操作进行初始化。
指令lda执行的后半部分在状态execllda里完成,在execllda状态下,ReadMem的值保持为1,当memDataReady 的值变为1时,状态机进入下一状态。
在这种情况下,Databus上的存储器数据由RFLwrite 和RFHwrite控制,在时钟节拍下存入RegisterFile。
其他指令的执行和我们讨论的例子类似。
lda : begin
Rplus0 = 1'b1;
Rs_on_AddressUnitRSide = 1'b1;
Nstate = exec1lda;
end
CPU的数据结构和控制器受系统主时钟的下降沿触发。
控制信号在时钟下降沿被赋值,并保持到下一个时钟下降沿。
这段时间里信号经过总线和逻辑单元在数据通路里传输。
用Verilog描述CPU时,采用图2-2所示的层次结构。
数据结构是单独的部分,把它里面的各个单元连起来构成数据通路。
控制器也是单独的Verilog模块。
在整个Verilog设计中,用连线把数据通路和控制器连接起来。
3、外围模块设计
要想让CPU工作,还需要一些外围模块配合CPU工作。
如分频器模块、存储器模块和I/.O模块。
图2-6给出了系统最高层结构图,具体接口信息如表2-7所示。
图2-6 系统最高层结构图
表2-7 系统最高层具体接口信息
分频器:为CPU正常工作提供稳定的时钟信号。
它的输入端口接外部时钟输入,输出为10MHz稳定时钟,作为CPU和存储器的时钟输入。
存储器:作为CPU的外部存储器,存储CPU执行的程序指令。
图2-7给出了存储器结构图,具体接口信息如表2-8所示。
注:存储器的设计务必注意对数据总线的访问与隔离控制。
图2-7 存储器结构图
表2-8 存储器具体接口信息
I/O模块:作为本课程设计唯一的输出模块,从数据总线上得到CPU处理好的数据,驱动LED灯闪烁。
4、仿真与测试
完成CPU各模块的设计,外围模块的设计和软件程序的编写编译及初始化到存储器中的工作,对系统最高层进行仿真通过后,就可以下载到FPGA实验板上,进行实际测试,运行观察分析实验现象。
精简指令集CPU的执行过程为CPU从RAM 中取指令并执行,完成运算处理后再将相应的结果输出给LED。
三、提交的设计和文档要求
1.介绍所设计的16位精简指令集CPU指令系统;
2.上交精简指令集CPU所有模块的代码及仿真结果图,并分析仿真结果;
3.分析所编写的汇编程序,并翻译成二进制机器码;
4.自己编写的CPU外围模块如分频器,存储器和IO接口,上交各模块代码,利用库例化完成的介绍步骤,并分析CPU执行程序的仿真结果图。