1,什么是cpu?
CPU即中央处理器的英文缩写,它是计算机的核心部件。
计算机进行信息处理可分为两个步骤:
(1)将数据和程序(即指令序列)输入到计算机的存储器中;
(2)从第一条指令的地址起开始执行该程序,得到所需结果,结束运行。CPU的作用是协调并控制计算机的各个部件执行程序的指令序列,使其有条不紊的进行,因此必须具有以下功能:
a)取指令:当程序已在存储器中时,首先根据程序入口地址取出一条程序,为此要发出指令地址及控制信号。
b)分析指令:即指令译码。是对当前取得的指令进行分析,指出它要求什么操作,并产生相应的操作控制命令。
c)执行指令:根据分析指令时产生的操作命令,形成相应的操作控制信号序列,通过运算器,存储器及输入/输出设备的执行,实现每条指令的功能,其中包括对运算结果的处理以及下条指令地址的形成。
CPU的功能:
1)能对指令进行译码并执行规定的动作;
2)可以进行算术和逻辑运算;
3)能与存储器,外设交换数据;
4)提供整个系统所需的控制;
CPU内部结构至少要包含的部件:
1)算术逻辑运算部件(ALU)
2)累加器
3)程序计数器
4)指令寄存器,译码器
5)时序和控制部件
RISC:精简指令集计算机(Reduced Instruction Set Computer)
RISC与一般的CPU相比不仅简化了指令系统,而且是通过简化指令系统使计算机的结构更加简单合理,从而提高了运算速度。从实现途径看,RISC_CPU与一般的CPU不同之处在于:它的时序控制信号形成部件是用硬件布线逻辑实现的,而不是采用微程序控制的方式。所谓硬布线逻辑也就是用触发器和逻辑门直接连线所构成的状态机和组合逻辑,故产生控制序列的速度比用微程序控制方式快很多,因为这样做省去了读取微指令的时间。
2,RISC_CPU的结构
RISC_CPU是一个复杂的数字逻辑电路,但其基本部件的逻辑并不复杂,一般由下面8个部件组成:
1)时钟发生器
2)指令寄存器
3)累加器
4)RISC_CPU算术逻辑运算单元
5)数据控制器
6)状态控制器
7)程序计数器
8)地址多路器
各个部件的具体结构和逻辑介绍如下:
2.1 时钟发生器
时钟发生器clk_gen利用外来时钟信号clk,来生成一系列时钟信号送往cpu的其他部件。
- FETCH:外来时钟clk的八分频信号。利用fetch的上升沿来触发cpu控制器开始执行一条指令,同时fetch信号还将控制 地址多路器输出指令地址和数据地址;
- CLK1:用作指令寄存器、累加器、状态控制器的时钟信号;
- ALU_CLK:用于触发算术逻辑运算单元;
时钟发生器的实现波形如下:
由于时钟发生器的设计中采用了同步状态机的设计方法,不但使clk_gen模块的源程序可以被综合,也使得其生成的输出在跳变时间同步性能上有明显的提升。
2.2 指令寄存器
指令寄存器用于寄存指令。
指令寄存器的触发时钟是clk1,在正沿下触发,寄存器将数据总线送来的指令存入高8位或低8位寄存器中。但并不是每个clk1的上升沿都寄存数据总线的数据,因为数据总线上有时传输指令,有时传输数据。什么时候寄存,由CPU的状态控制器的load_ir信号控制。Load_ir信号通过ena口输入到指令寄存器。复位后,指令寄存器被清零。
每条指令为2个字节,即16位,高3位是操作码,低13位是地址。本设计的数据总线为8位,每条指令需取两次,先高后低,当前取得是高8位还是低8位由state记录,0表示取得是高8位,1表示取得是低8位。
2.3 累加器
用于存放当前得结果,也是双目运算其中一个数据来源;复位后,累加器得值是零。当累加器通过ena口收到来自CPU状态控制器load_acc信号时,在clk1时钟正跳变沿时就收到来自数据总线得数据。
2.4 算术运算器
根据输入得8种不同操作码分别实现相应得加、与、异或、跳转等8种基本运算操作。利用这几种运算可以实现很多种其它运算以及逻辑判断等操作。
2.5 数据控制器
数据控制去器的作用是控制累加器数据输出,由于数据总线是各种操作时传送数据的公共通道,不同情况下传送不同的内容,有时要传输指令,有时要传送RAM区或接口的数据。累加器的数据只有在需要往RAM区或端口写时才允许输出,否则应呈现高阻态,以允许其他部件使用数据总线。所以任何部件往总线上传输数据时,都需要一控制信号。而此控制信号的的启停,则由CPU状态控制器输出的各信号控制决定,数据控制器何时输出累加器的数据则由状态控制器输出的控制信号datactl_ena决定。
2.6 地址多路器
地址多路选择器用于选择输出的地址是PC(程序计数)地址还是数据/端口地址。每个指令周期的前4个时钟周期用于从ROM中读取指令,输出的应是PC地址。后4个时钟周期用于对RAM或端口的读写,该地址由指令中给出。地址的选择输出信号由时钟信号的8分频信号fetch提供.
2.7 程序计数器
程序计数器用于提供指令地址,以便读取指令,指令按照顺序放在存储器中。形成指令地址的两种情况:一是顺序执行的情况;二是遇到要改变顺序执行程序的情况。
复位后,指令指针为零,即每次CPU重启将从ROM的零地址开始读取指令并执行。每条指令执行完需要2个时钟,这时pc_addr已被增2,指向下一条指令。(每条指令占两个字节)。如果正执行的语句是跳转语句,这时CPU状态控制器将会输出load_pc信号,通过load口进入程序计数器。程序计数器(pc_addr)将装入目标地址(ir_addr),而不是增2.
2.8 状态控制器
状态控制器由两部分组成:
1)状态机(上图中的MACHINE部分)
2)状态控制器(上图中的MACHINECTL部分)
状态控制器接收复位信号RST,当RST有效时通过信号ena使其为0,输入到状态机中停止状态机的工作。
状态机是CPU的控制核心,用于产生一系列的控制信号,启动或停止某些部件。CPU合适进行读指令、读写I/O端口、RAM区等操作,都是由状态机来控制的。状态机的当前状态由变量state记录,state的值就是当前这个指令周期中已经过的时钟数。
指令周期是由8个时钟周期组成,每个时钟周期都要完成固定的操作。
- 第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无变化。
2.9 外围模块
- 地址译码器
用于产生选通信号,选通ROM或RAM
//FFFFH-1800H RAM
//1800H-0000H ROM
2.RAM,ROM模块
ROM用于装载测试程序,可读不可写,RAM用于存放数据,可读可写
2.10 risc_cpu模块总的连接图
工程总的连接图
3,整个工程工作流程
- register(指令寄存器模块):输入数据data是从ROM中读取的指令数据,每次读取8bit;输入数据ena是指令寄存器的控制信号,为1时开始加载16位指令,先加载高8位,后低8位,在两个时钟周期内完成。信号ena由machine模块中的load_ir信号控制。
- accum(累加器):输入数据data来自算术运算单元alu的输出;当控制信号ena为1时,在时钟的上升沿,将数据输出;信号ena由machine模块中的load_acc信号控制。
- alu(算术运算器):输入数据accum是累加器的输出,输入数据data 是从ram中读取的数据;opcode信号是对输入的两个数据要进行的操作的选择;如果累加器输入的树位零,则输出zero为1;alu_out是运算后的结果,有两个方向,一是存到ram中,二是做为累加器的输入。
- Machinectl(状态控制器):在fetch上升沿到来时,将使能信号ena置1,使能machine模块,开始工作;
- Datactl(数据控制器模块):在data_ena使能信号为1 的时候,将运算单元的结果输出,如果此时wr信号使能,则将数据写入ram中;data_ena信号由machine模块中的datactl_ena信号控制。
- counter(程序计数器模块,提供指令地址):当load为1时输出的地址为指令寄存器中输出指令中提供的地址,相当于执行跳转指令;为0时,地址在当前地址的基础上加1,即顺序执行;然后地址输入到地址多路选择其中。其中load信号由machine模块中的load_pc信号控制,clock信号由machine模块中的inc_pc信号控制。
- adr(地址多路选择器):用于选择输出的是指令中要跳转的地址,还是顺序执行时的地址。为1时,输出前者,否则输出后者。
- Machine(状态机):当ena为0时,所有输出复位;为1时正常工作;
- 第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无变化。 指令系统8条指令的作用
- Addr_decode(地址译码器): //FFFFH-1800H RAM //1800H-0000H ROM
- Rom: 读和使能满足,输出给定地址中的指令数据;
- ram: 读和使能满足,输出给定地址的数据;写的上升沿,将数据写入指定的地址
- 各指令语句的作用;
a. HLT:停机操作。该操作将空一个指令周期,即8个时钟周期;
b. SKZ:为零跳过下一条语句。该操作先判断当前alu中的结果是否为零,若是零就跳过下一条语句,否则继续执行。
c. ADD:相加。该操作将累加器中的值与地址所指的存储器或端口的数据相加,结果仍送回累加器中。
d. AND:相与。该操作将累加器的值与地址所指的存储器或端口的数据相与,结果仍送回累加器中。
e. XOR:异或。该操作将累加器的值与指令中给出地址的数据异或,结果仍送回累加器。
f. LDA:读数据。该操作将指令中给出地址的数据放入累加器。
g. STO:写数据。该操作将累加器的数据放入指令中给出的地址。
h. JMP:无条件跳转语句。该操作将跳转至指令给出的目的地址,继续执行。
RISC_CPU是8位微处理器,一律采用直接寻址方式,即数据总是放在存储器中,寻址单元的地址由指令直接给出。
4 RISC_CPU 操作和时序
一个微机系统为了完成自身的功能,需要CPU执行许多操作。以下是RISC_CPU的主要操作:
1.系统的复位和启动操作
2.总线读操作
3.总线写操作
下面详细介绍一下每个操作:
4.1 系统的复位和启动操作
RISC_CPU的复位和启动操作是通过rst引脚的信号触发执行的。当rst信号一进入高电平, RISC_CPU就会结束现行操作,并且只要rst停留在高电平状态,CPU就维持在复位状态。在复位状态,CPU各内部寄存器都被设为初值,全部为零。数据总线为高阻态,地址总线为0000H,所有控制信号均为无效状态。rst回到低电平后,接着到来的第一个fetch上升沿将启动RISC_CPU开始工作,从ROM的000处开始读取指令并执行相应操作。
4.2 总线读操作
每个指令周期的前0–3个时钟周期用于读指令,在状态控制器一节中已详细讲述,这里就不再重复。第3.5个周期处,存储器或端口地址就输出到地址总线上,第4–6个时钟周期,读信号rd有效,数据送到数据总线上,以备累加器锁存,或参与算术、逻辑运算。第7个时钟周期,读信号无效,第7.5个周期,地址总线输出PC地址,为下一个指令做好准备。
4.3 写总线操作
每个指令周期的第3.5个时钟周期处,写的地址就建立了,第4个时钟周期输出数据,第5个时钟周期输出写信号。至第6个时钟结束,数据无效,第7.5时钟地址输出为PC地址,为下一个指令周期做好准备。
5 risc_cpu前仿真结果
注:仿真中使用的xxx.pro文件和xxx.data文件在联合仿真时要放到simulation文件夹下才能成功加载
$readmemb ( “test1.pro”,t_rom_.memory ); 和 $readmemb ( “test1.dat”,t_ram_.ram); 的作用
可以把编译好的汇编机器码装入虚拟ROM,把需要参加运算的数据装入虚拟RAM就可以开始仿真。上面语句中的第一项为打开的文件名,后一项为系统层次管理下的ROM和RAM模块中的存储器memory和ram.下面为测试设计功能所需要的机器码的数据文件。
以下介绍前仿真的步骤, 首先按照表示各模块之间连线的电路图编制测试文件, 即定义Verilog的wire变量作为连线,连接各功能模块之间的引脚,并将输入信号引入,输出信号引出。如若需要,可加入必要的语句显示提示信息。例如,risc_cpu 的测试文件就是cputop.v。其次,使用仿真软件进行仿真, 由于不同的软件使用方法可能有较大的差异, 以下只简单的介绍modelsim的使用。在进入modelsim的环境之后,在file项选择change direction来确定编制的文件所在的目录,然后在design项选择或创建一个library,完成后即可开始编译。在design项选compile…项,进入编译环境,选定要编译的文件进行编译。Modelsim的编译器语法检查并不严格,有时会出现莫名其妙的逻辑错误,书写时应注意笔误。完成编译后,还是在compile…项,选择load new design项,选中编译后提示的top module的名字,然后开始仿真。
6 总结
经过好几周断断续续的学习终于完成了这个简单的设计的实现,由于软件的原因,仅实现到前仿真,后续内容未实现。虽然整个设计中的内容都能看懂,但是里面的实现和逻辑关系还是经过了很长时间的研究才弄明白。经过这个设计的实现,才发现自己对设计的理解真的差很多,还是要继续学习。