零基础学FPGA(十四)第一片IC——精简指令集RISC_CPU设计精讲
时间:02-19
来源:互联网
点击:
不得不说,SDRAM的设计是我接触FPGA以来调试最困难的一次设计,早在一个多月以前,我就开始着手想做一个SDRAM方面的教程,受特权同学影响,开始学习《高手进阶,终极内存技术指南》这篇论文,大家都知道这篇文章是学习内存入门的必读文章,小墨同学花了一些时间在这上面,说实话看懂这篇文章是没什么问题的,文件讲的比较直白,通俗易懂,很容易入手。当了解了SDRAM工作方式之后,我便开始写代码,从特权同学的那篇经典教程里面,我认真研读代码的来龙去脉,终于搞懂了特权同学的设计思想,并花了一些时间将代码自己敲一遍,并加上自己的注释
然而设计并没有像我想象的那么简单,代码设计好之后还要经过仿真,时序约束,仿真总体来说还好,但是时序约束我接触的很少,于是又去学时序约束方面的知识,由于控制SDRAM时钟跑到了100MHZ,时序约束对这个设计来说可以算是关键部分了,之前的设计由于对时序要求的不高,所以不用约束就可以实现。学习时序约束可以算是一个漫长的过程,经过这段时间的学习,小墨同学也开始反思,是不是自己的跨度有点大,设计SDRAM的过程确实有些吃力,所以,小墨同学决定暂时放弃SDRAM的教学,等以后发Nios II的文章的时候再来谈SDRAM。
这次我们先来学习一个CPU的设计,这个设计看似简单,但是真要一步步做完还是需要点耐心和精力的。本篇文章我们主要介绍Risc_CPU的设计过程以及代码分析,下一篇文章小墨同学主要和大家分享test bench的书写与仿真测试,帮助大家一步一步学会这个cpu的设计,那么,我们开始今天的教学吧~
一、 设计前的准备
设计开始之前,我们得先知道什么是CPU,cpu即中央处理器,是计算机的核心部件。cpu的工作过程,小墨同学简单说一下。首先我们计算机的程序和数据是存在我们计算机的内存中的,上电后cpu就需要从第一条指令的地址开始取指令,即取指令。取出的指令需要经过指令译码来告诉cpu这条指令是用来干什么的,即分析指令。当得知指令内容以后,cpu需要产生操作指令来完成相应的操作,即执行指令。所以,任何一种CPU内部,至少应该包含下列这些部件:
1.算数运算器
2.累加器
3.指令计数器
4.指令寄存器和译码器
5.时序控制器
二. 工作原理
在本次设计中,我们假设ROM中装的是我们的程序指令,等会我们会往里面装数据,程序指令是16位的,cpu每次读取8位,分两次读完,读出来的数据存到指令寄存器中。
这16位数据的高三位代表指令码
HLT = 3'b000,
SKZ = 3'b001,
ADD = 3'b010,
AND = 3'b011,
XOR = 3'b100,
LDA = 3'b101,
STO = 3'b110,
JMP = 3'b111;
后13位代表地址码,指令寄存器将读回来的数据分成指令码opcode[2:0]和地址码ir_addr[12:0],状态控制器读回指令码进行译码,看是什么指令,然后根据指令的内容操作其他部件,若为写数据则打开RAM进行写数据,若为计算则将数据送到算数运算器进行算数运算,若为跳转指令则地址指向下一地址,下次读取指令的时候就跳过一个地址执行等等
三、各模块精讲
本次设计的cpu共包含8个模块,下面我来一一讲解
1.时钟发生器
本模块的用来产生分频信号,外部50M的时钟,做8分频输出,此8分频信号用来控制地址多路器的输出,由于之后我们要用到8状态的状态机,因此这里做成8分频,至于具体原因,我们后面再讲,然后是一个算数运算器的使能信号,每一个8分频信号到来之前有一个算术运算器使能信号的高脉冲,意思就是,每一此状态机循环开始之前做一次运算
才有状态机的方式来设计分频信号避免了使用计数器计数的方法来延时,提高了程序的可读性和速度
2.指令寄存器
本模块的作用是将来自ROM的16位数据寄存并分为高三位的指令码和低13位的地址码
指令寄存器分两次读取ROM中的数据
3.累加器
算数运算器的初值位0,来自ROM的数据经算数运算器处理之后输出到累加器,累加器使能信号到来时,再将这份数据送到算数运算器作为初值,与下次从ROM中读回的数据进行算数运算
4.算数运算器
算术运算器先判断来自ROM的高三位指令码是何种指令,然后将来自ROM中的数据和来自累加器中的数据进行算数运算,并将结果输出。如果算数运算器中的数据是0的话,那么有一个高脉冲zero输出
5.数据控制器
如果想将算数运算器的结果保存起来,输出到RAM,那么可以选通数据控制器,将数据输出到RAM,配合指令STO
6.地址多路器
8分频时钟的前半周期用来读取ROM中指定地址的数据,后半周期用来进行指令操作,所以前半个周期需要从指定ROM中读数据,所以输出地址应为pc_addr,后半个周期用来处理指令,那么要操作的肯定是从ROM中读回的13位地址,即ir_addr,并将其输出
7.程序计数器
刚开始从ROM中读取数据的地址是pc_addr,是0地址,若为跳转指令,则需要将从ROM中读回的13位地址码作为新地址给pc_addr,等下一个8分频时钟到来时就读取该地址的指令,实现跳转指令的目的。如果不是跳转指令,那么pc_addr加1指向下一地址,继续执行
8.状态控制器
状态控制器,即我们的状态机,使我们本次设计的核心部件,状态机共用了8个状态
前两个状态用来读取ROM中的16位数据,需要两个时钟,也就是需要两个状态。
第三个状态等待一个时钟周期,目的是要凑够8个状态,因为8分频时钟的后半个周期需要4个状态来完成,故前半周期也需要4个状态,由于前半周期只需要读取数据和地址指向下一地址即可,故需凑一个状态
第四个状态地址加1 ,指向下一地址
第五到八个状态用来分析指令和执行指令,若为跳转指令,则控制程序计数器改变目标地址,若为跳过下一条指令,则控制程序计数器pc_addr地址加4,若为运算指令,则将控算数运算器进行相应的逻辑运算,若为LDA写入指令,则将读回的数据放入累加器中等等,具体操作指令的含义见下图
各个模块设计好之后将其组装便完成了我们的cpu设计,组装后的顶层模块见下图
这样,我们的cpu就设计完成了,当然设计完成之后还要进行仿真,要仿真就还需要外围电路,包括存期指令程序ROM,存取数据的RAM,地址译码器等,由于ROM和RAM是不可综合的,但是我们可以在仿真的时候模拟。
由于每篇文章最多只能上传20张图片,所以今天的教程就到此为止吧,具体的仿真过程和ROM、RAM的设计小墨同学将在下一篇文章中介绍,下一篇文章中将会提到modolsim SE的使用,test bench的书写,前面的博客中虽然也略有提及,但并不系统,下一篇文章将会以本次设计为例,给大家介绍仿真的全过程,希望大家大力支持,下面附上部分仿真的图片和测试结果供大家参考
波形仿真
谢谢各位大神的支持,这篇文章写了几个小时,纯手打~
然而设计并没有像我想象的那么简单,代码设计好之后还要经过仿真,时序约束,仿真总体来说还好,但是时序约束我接触的很少,于是又去学时序约束方面的知识,由于控制SDRAM时钟跑到了100MHZ,时序约束对这个设计来说可以算是关键部分了,之前的设计由于对时序要求的不高,所以不用约束就可以实现。学习时序约束可以算是一个漫长的过程,经过这段时间的学习,小墨同学也开始反思,是不是自己的跨度有点大,设计SDRAM的过程确实有些吃力,所以,小墨同学决定暂时放弃SDRAM的教学,等以后发Nios II的文章的时候再来谈SDRAM。
这次我们先来学习一个CPU的设计,这个设计看似简单,但是真要一步步做完还是需要点耐心和精力的。本篇文章我们主要介绍Risc_CPU的设计过程以及代码分析,下一篇文章小墨同学主要和大家分享test bench的书写与仿真测试,帮助大家一步一步学会这个cpu的设计,那么,我们开始今天的教学吧~
一、 设计前的准备
设计开始之前,我们得先知道什么是CPU,cpu即中央处理器,是计算机的核心部件。cpu的工作过程,小墨同学简单说一下。首先我们计算机的程序和数据是存在我们计算机的内存中的,上电后cpu就需要从第一条指令的地址开始取指令,即取指令。取出的指令需要经过指令译码来告诉cpu这条指令是用来干什么的,即分析指令。当得知指令内容以后,cpu需要产生操作指令来完成相应的操作,即执行指令。所以,任何一种CPU内部,至少应该包含下列这些部件:
1.算数运算器
2.累加器
3.指令计数器
4.指令寄存器和译码器
5.时序控制器
二. 工作原理
在本次设计中,我们假设ROM中装的是我们的程序指令,等会我们会往里面装数据,程序指令是16位的,cpu每次读取8位,分两次读完,读出来的数据存到指令寄存器中。
这16位数据的高三位代表指令码
HLT = 3'b000,
SKZ = 3'b001,
ADD = 3'b010,
AND = 3'b011,
XOR = 3'b100,
LDA = 3'b101,
STO = 3'b110,
JMP = 3'b111;
后13位代表地址码,指令寄存器将读回来的数据分成指令码opcode[2:0]和地址码ir_addr[12:0],状态控制器读回指令码进行译码,看是什么指令,然后根据指令的内容操作其他部件,若为写数据则打开RAM进行写数据,若为计算则将数据送到算数运算器进行算数运算,若为跳转指令则地址指向下一地址,下次读取指令的时候就跳过一个地址执行等等
三、各模块精讲
本次设计的cpu共包含8个模块,下面我来一一讲解
1.时钟发生器
本模块的用来产生分频信号,外部50M的时钟,做8分频输出,此8分频信号用来控制地址多路器的输出,由于之后我们要用到8状态的状态机,因此这里做成8分频,至于具体原因,我们后面再讲,然后是一个算数运算器的使能信号,每一个8分频信号到来之前有一个算术运算器使能信号的高脉冲,意思就是,每一此状态机循环开始之前做一次运算
才有状态机的方式来设计分频信号避免了使用计数器计数的方法来延时,提高了程序的可读性和速度
2.指令寄存器
本模块的作用是将来自ROM的16位数据寄存并分为高三位的指令码和低13位的地址码
指令寄存器分两次读取ROM中的数据
3.累加器
算数运算器的初值位0,来自ROM的数据经算数运算器处理之后输出到累加器,累加器使能信号到来时,再将这份数据送到算数运算器作为初值,与下次从ROM中读回的数据进行算数运算
4.算数运算器
算术运算器先判断来自ROM的高三位指令码是何种指令,然后将来自ROM中的数据和来自累加器中的数据进行算数运算,并将结果输出。如果算数运算器中的数据是0的话,那么有一个高脉冲zero输出
5.数据控制器
如果想将算数运算器的结果保存起来,输出到RAM,那么可以选通数据控制器,将数据输出到RAM,配合指令STO
6.地址多路器
8分频时钟的前半周期用来读取ROM中指定地址的数据,后半周期用来进行指令操作,所以前半个周期需要从指定ROM中读数据,所以输出地址应为pc_addr,后半个周期用来处理指令,那么要操作的肯定是从ROM中读回的13位地址,即ir_addr,并将其输出
7.程序计数器
刚开始从ROM中读取数据的地址是pc_addr,是0地址,若为跳转指令,则需要将从ROM中读回的13位地址码作为新地址给pc_addr,等下一个8分频时钟到来时就读取该地址的指令,实现跳转指令的目的。如果不是跳转指令,那么pc_addr加1指向下一地址,继续执行
8.状态控制器
状态控制器,即我们的状态机,使我们本次设计的核心部件,状态机共用了8个状态
前两个状态用来读取ROM中的16位数据,需要两个时钟,也就是需要两个状态。
第三个状态等待一个时钟周期,目的是要凑够8个状态,因为8分频时钟的后半个周期需要4个状态来完成,故前半周期也需要4个状态,由于前半周期只需要读取数据和地址指向下一地址即可,故需凑一个状态
第四个状态地址加1 ,指向下一地址
第五到八个状态用来分析指令和执行指令,若为跳转指令,则控制程序计数器改变目标地址,若为跳过下一条指令,则控制程序计数器pc_addr地址加4,若为运算指令,则将控算数运算器进行相应的逻辑运算,若为LDA写入指令,则将读回的数据放入累加器中等等,具体操作指令的含义见下图
各个模块设计好之后将其组装便完成了我们的cpu设计,组装后的顶层模块见下图
这样,我们的cpu就设计完成了,当然设计完成之后还要进行仿真,要仿真就还需要外围电路,包括存期指令程序ROM,存取数据的RAM,地址译码器等,由于ROM和RAM是不可综合的,但是我们可以在仿真的时候模拟。
由于每篇文章最多只能上传20张图片,所以今天的教程就到此为止吧,具体的仿真过程和ROM、RAM的设计小墨同学将在下一篇文章中介绍,下一篇文章中将会提到modolsim SE的使用,test bench的书写,前面的博客中虽然也略有提及,但并不系统,下一篇文章将会以本次设计为例,给大家介绍仿真的全过程,希望大家大力支持,下面附上部分仿真的图片和测试结果供大家参考
波形仿真
谢谢各位大神的支持,这篇文章写了几个小时,纯手打~
- 基于FPGA的片上系统的无线保密通信终端(02-16)
- 基于Virtex-5 FPGA设计Gbps无线通信基站(05-12)
- 基于FPGA的DVI/HDMI接口实现(05-13)
- 基于ARM的嵌入式系统中从串配置FPGA的实现(06-09)
- 采用EEPROM对大容量FPGA芯片数据实现串行加载(03-18)
- 赛灵思:可编程逻辑不仅已是大势所趋,而且势不可挡(07-24)