来源:ce123的技术博客(已获得作者授权转载)
//blog.csdn.net/ce123
原标题:基于状态机的简易risc cpu设计
目录
一、什么是cpu?
二、risc cpu结构
1.时钟发生器
2.指令寄存器
3.累加器
4.risc cpu算术逻辑运算单元
5.数据控制器
6.状态控制器
7.程序计数器
8.地址多路器
9.外围模块
10.地址译码器
a.ram
b.rom
三、risc cpu中各部件的相互连接关系
四、risc cpu和它的外围电路
五、risc cpu的寻址方式和指令系统
六、risc cpu的操作和时序
正文
cpu 即中央处理单元的英文缩写,它是计算机的核心部件。计算机进行信息处理可分为两个步骤:
能对指令进行译码并执行规定的动作;
可以进行算术和逻辑运算;
能与存储器,外设交换数据;
提供整个系统所需要的控制;
尽管各种cpu的性能指标和结构细节各不相同,但它们所能完成的基本功能相同。由功能分析,可知任何一种cpu内部结构至少应包含下面这些部件:
时钟发生器
指令寄存器
累加器
risc cpu算术逻辑运算单元
数据控制器
状态控制器
程序计数器
地址多路器
其中时钟发生器利用外来时钟信号进行分频生成一系列时钟信号,送往其他部件用作时钟信号。各部件之间的相互操作关系则由状态控制器来控制。下面逐一介绍各部件的具体结构和逻辑关系。
时钟发生器 clkgen 利用外来时钟信号clk 来生成一系列时钟信号clk1、fetch、alu_clk 送往cpu的其他部件。其中fetch是外来时钟 clk 的八分频信号。利用fetch的上升沿来触发cpu控制器开始执行一条指令,同时fetch信号还将控制地址多路器输出指令地址和数据地址。clk1信号用作指令寄存器、累加器、状态控制器的时钟信号。alu_clk 则用于触发算术逻辑运算单元。时钟发生器clkgen的波形见下图所示:
其veriloghdl 程序见下面的模块:
由于在时钟发生器的设计中采用了同步状态机的设计方法,不但使clk_gen模块的源程序可以被各种综合器综合,也使得由其生成的clk1、clk2、clk4、fetch、alu_clk 在跳变时间同步性能上有明显的提高,为整个系统的性能提高打下了良好的基础。
顾名思义,指令寄存器用于寄存指令。
指令寄存器的触发时钟是clk1,在clk1的正沿触发下,寄存器将数据总线送来的指令存入高8位或低8位寄存器中。但并不是每个clk1的上升沿都寄存数据总线的数据,因为数据总线上有时传输指令,有时传输数据。什么时候寄存,什么时候不寄存由cpu状态控制器的load_ir信号控制。load_ir信号通过ena口输入到指令寄存器。复位后,指令寄存器被清为零。
每条指令为2个字节,即16位。高3位是操作码,低13位是地址。(cpu的地址总线为13位,寻址空间为8k字节。)本设计的数据总线为8位,所以每条指令需取两次。先取高8位,后取低8位。而当前取的是高8位还是低8位,由变量state记录。state为零表示取的高8位,存入高8位寄存器,同时将变量state置为1。下次再寄存时,由于state为1,可知取的是低8位,存入低8位寄存器中。
其veriloghdl 程序见下面的模块:
累加器用于存放当前的结果,它也是双目运算其中一个数据来源。复位后,累加器的值是零。当累加器通过ena口收到来自cpu状态控制器load_acc信号时,在clk1时钟正跳沿时就收到来自于数据总线的数据。
其veriloghdl 程序见下面的模块:
算术逻辑运算单元 根据输入的8种不同操作码分别实现相应的加、与、异或、跳转等8种基本操作运算。利用这几种基本运算可以实现很多种其它运算以及逻辑判断等操作。 其veriloghdl 程序见下面的模块:
数据控制器的作用是控制累加器数据输出,由于数据总线是各种操作时传送数据的公共通道,不同的情况下传送不同的内容。有时要传输指令,有时要传送ram区或接口的数据。累加器的数据只有在需要往ram区或端口写时才允许输出,否则应呈现高阻态,以允许其它部件使用数据总线。 所以任何部件往总线上输出数据时,都需要一控制信号。而此控制信号的启、停,则由cpu状态控制器输出的各信号控制决定。数据控制器何时输出累加器的数据则由状态控制器输出的控制信号datactl_ena决定。 其veriloghdl 程序见下面的模块:
地址多路器用于选择输出的地址是pc(程序计数)地址还是数据/端口地址。每个指令周期的前4个时钟周期用于从rom中读取指令,输出的应是pc地址。后4个时钟周期用于对ram或端口的读写,该地址由指令中给出。地址的选择输出信号由时钟信号的8分频信号fetch提供。 其veriloghdl 程序见下面的模块:
程序计数器用于提供指令地址。以便读取指令,指令按地址顺序存放在存储器中。有两种途径可形成指令地址:其一是顺序执行的情况,其二是遇到要改变顺序执行程序的情况,例如执行jmp指令后,需要形成新的指令地址。复位后,指令指针为零,即每次cpu重新启动将从rom的零地址开始读取指令并执行。每条指令执行完需2个时钟,这时pc_addr已被增2,指向下一条指令。(因为每条指令占两个字节。)如果正执行的指令是跳转语句,这时cpu状态控制器将会输出load_pc信号,通过load口进入程序计数器。程序计数器(pc_addr)将装入目标地址(ir_addr),而不是增2。 其veriloghdl 程序见下面的模块:
状态控制器由两部分组成:
第0个时钟,因为cpu状态控制器的输出:rd和load_ir为高电平,其余均为低电平。指令寄存器寄存由rom送来的高8位指令代码。
第1个时钟,与上一时钟相比只是inc_pc从0变为1故pc增1,rom送来低8位指令代码,指令寄存器寄存该8位代码。
第2个时钟,空操作。
第3个时钟,pc增1,指向下一条指令。若操作符为hlt,则输出信号hlt为高。如果操作符不为hlt,除了pc增一外(指向下一条指令),其它各控制线输出为零。
第4个时钟,若操作符为and、add、xor或lda,读相应地址的数据;若为jmp,将目的地址送给程序计数器;若为sto,输出累加器数据。
第5个时钟,若操作符为andd、add或xorr,算术运算器就进行相应的运算;若为lda,就把数据通过算术运算器送给累加器;若为skz,先判断累加器的值是否为0,如果为0,pc就增1,否则保持原值;若为jmp,锁存目的地址;若为sto,将数据写入地址处。
第6个时钟,空操作。
第7个时钟,若操作符为skz且累加器值为0,则pc值再增1,跳过一条指令,否则pc无变化。
状态机的veriloghdl 程序见下面模块:
为了对risc_cpu进行测试,需要有存储测试程序的rom和装载数据的ram、地址译码器。下面来简单介绍一下:
地址译码器
地址译码器用于产生选通信号,选通rom或ram。
ffffh---1800h ram
1800h---0000h rom
rom用于装载测试程序,可读不可写。ram用于存放数据,可读可写。
risc_cpu的指令格式一律为:
指令系统仅由8条指令组成:
1.系统的复位和启动操作
2.总线读操作
3.总线写操作
下面详细介绍一下每个操作:
risc_cpu的复位和启动操作是通过rst引脚的信号触发执行的。当rst信号一进入高电平,risc_cpu就会结束现行操作,并且只要rst停留在高电平状态,cpu就维持在复位状态。在复位状态,cpu各内部寄存器都被设为初值,全部为零。数据总线为高阻态,地址总线为0000h,所有控制信号均为无效状态。rst回到低电平后,接着到来的第一个fetch上升沿将启动risc_cpu开始工作,从rom的000处开始读取指令并执行相应操作。波形图如下图所示。虚线标志处为risc_cpu启动工作的时刻。
risc_cpu的复位和启动操作波形
每个指令周期的前0--3个时钟周期用于读指令,在状态控制器一节中已详细讲述,这里就不再重复。第3.5个周期处,存储器或端口地址就输出到地址总线上,第4--6个时钟周期,读信号rd有效,数据送到数据总线上,以备累加器锁存,或参与算术、逻辑运算。第7个时钟周期,读信号无效,第7.5个周期,地址总线输出pc地址,为下一个指令做好准备。
cpu从存储器或端口读取数据的时序
每个指令周期的第3.5个时钟周期处,写的地址就建立了,第4个时钟周期输出数据,第5个时钟周期输出写信号。至第6个时钟结束,数据无效,第7.5时钟地址输出为pc地址,为下一个指令周期做好准备。