基于FPGA的RISC的设计和仿真
2012-05-12徐正安
汪 涛,徐正安
(重庆大学 物理学院,重庆 401331)
精简指令集计算机RISC (Reduced Instruction Set Computer)是一种执行较少类型计算机指令的微处理器,起源于上世纪80年代的 MIPS主机 (即 RISC机),RISC机中采用的微处理器统称RISC处理器。与一般的CPU相比,其不仅简化了指令系统,而且还通过简化指令系统使计算机的结构更加简单合理,从而提高了计算机的运算速度。从实现的途径看,RISC_CPU与一般的CPU的不同之处在于,它的时序控制信号形成部件是用硬布线逻辑实现的而不是采用微程序的控制方式,因此,与一般CPU相比它省去了读取微指令的时间。
RISC_CPU与生活密切相关,它已经进入消费电子的各个领域,ARM、MIPS、PowerPC和51单片机等处理器都属于RISC家族的一部分,在国际市场上RISC处理器占有巨大的市场份额,RISC的研发也是一个非常热门的领域,只有认真理解RISC的原理才能更好地认识和掌握已有的RISC架构的处理器。
利用现场可编程门阵列FPGA (Field Progamable Gate Array)的灵活性可以很方便地设计出RISC,利用Altera公司的EDA工具QuartusⅡ和Mentor公司的ModelSim可以设计出RISC的RTL模型并对这个模型进行仿真。本文介绍了一种8 bit简单RISC的实现方法,在QuartusⅡ中搭建了模型,通过时序仿真对该模型进行了功能验证。
1 RISC_CPU的实现原理
本文设计的RISC具有8条指令,一律采用直接寻址地方式,采用哈佛结构,指令和数据存储分别放在定制的ROM和RAM中。RISC_CPU的基本结构图1所示。
图1 RISC_CPU的基本结构
该RISC_CPU可以综合到FPGA的具体电路中,因此,在完善外部电路和接口的情况下完全可以当做一台真正的计算机来使用。该RISC_CPU的工作过程如下:首先,将预先编好的汇编指令代码以11 bit二进制的形式存放在定制的ROM中 (在QuartusⅡ的MegaWizard中完成定制),将要处理的数据按地址顺序存放在定制的RAM中;然后,有限状态机(FSM)开始以 8个指令周期的工作模式开始工作,在这8个指令周期中完成指令和数据的加载,FSM产生的 8组标志信号(FLAG)完成对控制单元(CU)和算术逻辑单元(ALU)的控制;最后,由FSM产生的停止信号(HALT)终止程序的运行,并将程序的运行结果存储在RAM的指定地址中。
这款 RISC的每条指令有 11 bit,高3 bit为操作码,低8 bit为ROM和RAM地址,因此它的寻址空间为256 bit,对于简单的汇编指令还是可以应付的。
2 RISC_CPU的逻辑设计
2.1 指令系统的设计
本文设计的RISC主要由8条指令组成,它们的功能如下。
(1)HLT(opcode为 3’b000):停机操作。 该操作将空一个指令周期,即8个时钟周期。
(2)SKZ(opcode 为 3’b001):为零跳过下一条语句。该操作先判断当前alu中的结果是否为零,若是零就跳过下一条语句,否则继续执行。
(3)ADD(opcode为 3’b010):相加。该操作将累加器中的值与地址所指的存储器或端口的数据相加,结果仍送回累加器中。
(4)AND(opcode为 3’b011):相与。该操作将累加器的值与地址所指的存储器或端口的数据相与,结果仍送回累加器中。
(5)XOR(opcode为 3’b100):异或。该操作将累加器的值与指令中给出地址的数据异或,结果仍送回累加器中。
(6)LDA(opcode为 3’b101):读数据。该操作将指令中给出地址的数据放入累加器。
(7)STO(opcode为 3’b110):写数据。 该操作将累加器的数据放入指令中给出的地址。
(8)JMP(opcode为 3’b111):无条件跳转语句。 该操作将跳转至指令给出的目的地址,继续执行。
2.2 指令寄存器的设计
指令寄存器主要完成对指令的解码,由于ROM中存储的是11 bit数据,因此需要在一个指令周期中完成指令的获取,同时,指令寄存器由FSM输出的控制标志符get_ir完成指令的拆分,其中高3 bit为操作码送往ALU中进行操作,低8 bit送往RAM中完成数据的寻址操作。在QuartusⅡ中编写Verilog HDL语言完成了指令寄存器的RTL模型的搭建,其在QuartusⅡ中的封装模块如图2所示。
指令寄存器设计的难点是控制标志符get_ir如何与ROM的读指令标志rd_rom配合完成指令的拆分。好的时序设计能避免产生杂乱无章的信号,使汇编程序能正确的被所设计的指令寄存器识别并被后面的模块使用。
图2 指令寄存器模块
2.3 算术逻辑单元的设计
算术逻辑单元主要完成8种指令的运算和逻辑操作,本设计的指令系统由 HLT、SKZ、ADD、AND、XORR、LDA、STO和JMP这8条指令组成。其中,SKZ是一种判断指令,若累加器的结果为0,则跳过下一条语句继续执行;如果为1,就执行下一条指令。这条指令的设计使得设计的程序变得多样化,因此在ALU的设计中需要输出1个零标志信号zero来与状态机输出的9位标志信号共同配合才能使RISC工作。逻辑单元在QuartusⅡ中封装的模块如图3所示。
图3 算术逻辑单元模块
2.4 程序计数器的设计
程序计数器(PC)的出现是程序设计的必然,因为在设计程序时不可能都是顺序执行的,如果只有顺序执行的情况那么实现一些稍微复杂一点的功能就很困难。计算器不同于计算机就是因为计算机可以按照程序的要求改变程序的执行顺序,例如在执行 JMP(跳转指令)的时候,需要形成新的指令地址。程序计数器在 QuartusⅡ中封装的模块如图4所示。
图4 程序计数器模块
2.5 状态机的设计
状态机是RISC设计的核心,每个指令要成功运行都必须依靠状态机产生的标志信号,因此状态机设计的核心又是标志信号的设计。一个指令周期占8个时钟,即在一个状态机内部有8个状态,这8个状态依次为:0:装载 ROM 中的 11 bit指令;1:程序计数器加 1;2:空操作;3: 程序计数器加1,同时判断指令是否为HLT指令;4:判断指令并读取 RAM中的数据;5:执行相应的指令;6:空操作 ;7: 判 断 指 令 是 否 为SKZ指令,若是则程序计数器加1,若不是则不作任何处理。在QuartusⅡ中封装的状态机模块如图5所示。
图5 状态机模块
2.6 ROM和RAM的设计
在RISC中,ROM用来存储程序代码,在每个指令周期结束后,ROM的地址增1,同时加载一条指令,因此,ROM的地址时钟应该是状态机的输出状态标志rd_rom,在每个rd_rom的上升沿ROM的读地址加1。在调试RISC的时候事先将指令代码用mif文件的格式存储在ROM中,mif文件中存放的指令代码如图6所示。
图6 mif文件中存放的程序
在QuartusII的MegaWizardsTI中定制了一个32×10 bit的ROM,然后加载 mif文件,但是这样仍不够,还需要编写一个模块完成ROM的地址生成,编写的Verilog HDL代码如下:
将这个rom_data_gen封装为一个模块放在主block diagram中,如图7所示。
RAM的设计相对于ROM而言要复杂很多,因为RAM既要读又要写,同时还要在一些时刻形成新的地址。例如要求出一个这样的序列,已经给出了1和2两个数,后面求出的每个数都是前面两个数的和(这样的序列称为费波纳序列),这样求出的序列为:3,5,8,13,21,34,55,89,144…。 用本文设计的指令集来编写汇编程序如下:
LOOP:LDA 1
STO 2
ADD 0
STO 1
LDA 2
STO 0
图7 ROM和rom_data_gen模块
详细的分析结果如图8所示。
RAM设计的难点就是在每个指令周期要完成数据的读操作,在下一个周期要完成数据的写操作,RAM中初始化的数据在一个指令周期之后会发生变化,因此,如何初始化RAM中的数据是一个关键问题。
图8 求解序列的汇编程序分析
在求解一个问题时一定是对内存中的数据进行操作(即RISC中RAM的数据),因此,在每个程序的开始阶段一定要读取RAM中初始化的数据,读取完之后释放RAM中的数据的读控制权,开始由ALU单元进行RAM的数据读写操作。只有这样才能不与RAM中初始化的数据发生冲突,保证程序运行的正确。
因此可以再定制一个ROM来完成RAM中数据的初始化,在任何一个要往RAM中写数据的时刻释放该ROM的读控制权,这个动作由data_choose模块来完成。data_ram_wr_rd即本文所设计的RAM,图9即为这3个模块与ALU的关系示意图。
图9 数据读写操作示意图
data_ram_wr_rd模块的Verilog HDL代码如下:
在 QuartusⅡ中封装的 data_ram_wr_rd模块和RAM_wr_rd模块如图10所示。
3 RISC的测试
编写完RISC的每个模块后,在Block Diagram中连接各个模块。
要求出144以内的费波纳序列,这样的程序运行之后的正确结果为:1,2,3,5,8,13,21,34,55,89,144。
本文所设计的RISC如果能成功运行这个程序,则证明本设计是正确的,至于一些细节的东西(如中断控制、流水线操作、超标量和cache操作等)再做考虑。
图10 data_ram_wr_rd模块和RAM_wr_rd模块
图11 求解序列程序的时序仿真结果
在ROM中装载好已经编写好的汇编语言(为11 bit二进制数),同时在RAM中装载初始化数据 1、2(1和2分别对应RAM中的地址0和1)。然后在QuartusⅡ中进行时序仿真,得到的结果如图11所示。
从图11可以看出,程序运行的结果完全正确,求出了要找的费波纳序列。
本文介绍了基于FPGA的RISC的设计和仿真的方法,通过编写Verilog HDL语言来完成整个RISC模块的搭建。通过分析汇编程序在FPGA中的运行原理,并在QuartusⅡ中进行时序仿真,验证了用FPGA设计RISC的可行性。
[1]夏宇闻.数字系统设计——Verilog实现[M].北京:高等教育出版社,2007.
[2]汤志忠,杨春武.开放式实验 CPU设计[M]北京:清华大学出版社,2007.
[3]姜咏江.基于 QuartusⅡ的计算机核心设计[M]北京:清华大学出版社,2007.
[4]杨天怡.计算机硬件技术基础[M]重庆:重庆大学出版社,2002.
[5]伯杰.计算机硬件及组成原理[M]北京:机械工业出版社,2006.