自制CPU需要什么
《CPU自制入门》
计算机系统基础
计算机系统的SoC(System-on-a-Chip,片上系统)以CPU为核心,同时实现了负责存储程序和数据的内存、负责和外部进行输入输出的I/O以及它们之间的连接总线。
PC(Personal Computer,个人电脑)
I/O(Input/Output,输入输出装置)进行数据交换
CPU如何运行
CPU①读取(Fetch)内存中的指令,然后对其要处理的操作进行②解码(Decode),最后进行③执行。
①读取
PC(Program Counter,程序计数器)寄存器,其中保存着即将执行的指令的地址。
将PC寄存器的值输出给内存,由内存返回该值对应地址中的指令。
②解码
指令由CPU中被称为指令解码器的模块进行解码。
通用寄存器(General Purpose Register)保存地址和运算结果
③执行
CPU可以从内部存储装置——寄存器或外部的内存读取数据并处理,
然后将结果写回寄存器或内存。
CPU执行的指令,由代表操作种类的操作码和代表操作对象的操作数(由寄存器地址、内存地址或立即数指定;立即数是指嵌入指令中的固定常数)两部分组成。
CPU分为RISC(Reduced Instruction Set Computer,精简指令集计算机)和CISC(Complex Instruction Set Computer,复杂指令集计算机)两种。
RISC架构最大的特点是只使用载入和存储指令访问内存,这种架构称为载入存储架构(Load/Store Architecture)。
好处是可以简化指令集和流水线设计。
在这种架构下,运算指令只能对寄存器中的数据进行操作。
位宽
位宽/字(word):可以访问的地址空间或数据的大小;可以处理的整数型数据的宽度
eg.32位CPU可以处理32位的数据,可以访问的地址空间为4G字节(2的32次方)
内存:用来存放运行时指令(程序)和数据的存储器,有时也称为主存(Main memory)(和计算机中长期保存数据和程序的存储器区别)
DRAM(Dynamic Random Access Memory,动态随机存储器)通过在电容器中积蓄电荷来保存数据的存储元件。电容器中充电状态是1,放电状态是0,并以此表示数值。
电容器中的电荷一段时间后会衰减,故DRAM需要定期进行重新写入数据的刷新(Refresh)操作。
内存使用地址(数据存储的位置)来管理存储的数据。
字节编址:每个数据单元都有一个地址。大多情况下数据单元是一个字节(8位)长度。
存储器层级
“高速小容量”、“中速中等容量”到“低速大容量”等多种存储器组合的混合型架构。
存储层面,速度最快的是CPU中的寄存器。
CPU比内存速度快很多,由CPU直接访问内存效率较低。
为了提高内存访问速度,在CPU和内存间增加了被称为缓存的高速小容量存储器。
字节序
将多字节数据存储在内存中时,各字节的存储顺序。
人类更容易理解大端,计算机更适合处理小端。
首先:数据一定是从内存的低地址依次向高地址读取和写入
大端是最前面的先排队上车,小端是从最后面的先排队上车
对应的是数据的高位从低地址开始存放,然后按顺序数据的低位存放下去
很多处理器考虑到软件的通用性和可移植性,同时支持两种字节序并可依据程序切换,称为双端序。
总线
CPU、内存和I/O之间交换数据的共同通道
总线一般由数据总线(传输交换的数据)、地址总线(指定访问的地址)和控制总线(负责总线访问的控制)构成。
总线协议:各个信号的时序、进行交换的规则。
总线传输:通过总线交换数据的整个过程。
总线的优点:
- 只要遵循总线协议,任何设备都可以简单地进行连接。
- 由于使用的是共享通道,硬件的成本也比较低。
缺点:数据传输的吞吐量较低。
数字电路基础
数字电路是利用两种不连续/离散的电位(电源电压H(High,高)电平、接地电压L(Low,低)电平分别代表1和0)来表示信息的电子电路。
基础:通过组合MOSFET(Metal-Oxide-Semiconductor Field-Effect Transistor,金属氧化物半导体场效应管)实现各种各样的逻辑电路。
MOSFET
MOSFET:一种在施加电压后可以像开关一样工作的半导体器件。
持有相反特性的N型MOSFET和P型MOSFET互补使用形成的门电路称为CMOS(Complementary Metal Oxide Semiconductor,互补金属氧化物半导体)。
CMOS可以用来制作各种各样的逻辑电路。
【进制】10(Dec)、2(Bin)、8(Oct)、16(Hex)
十进制(decimal number):0到9的数值在一位中表示
二进制(binary number):从0到1的数值在一位数字中表示,遇2则向上进位;第n个数字位,数值上是2的n-1次方位
八进制(octal number):通常以0开头(区分于十进制),从0开始的八个数表达数值
十六进制(hexadecimal number):通常以0x开头(或末尾加H),以0到9加上A到F(10到15)表示十六个数值
有符号二进制
补码表示法:N位的二进制数的最高位代表数值$-2^{N-1}$
无符号二进制数变成补码时,将所有比特反转(又称取反码)后加1。数字1表示为0001,-1表示为1111。(最高位为0时是正数,最高位为1时是负数)
比特(bit, binary digit):二进制中的一个数字位,表示数据量的常用单位
字节(byte):一个字节代表8比特(2的8次方表达的范围0~255比较适合表达文字,如英文字母、符号、控制符等),大多数CPU处理数据的单位
字节编址方式:为每字节赋予一个内存地址
1K=1024比特:衡量计算机内存和网络数据包大小
1K=1000:在硬盘等存储器的标签上记述的尺寸或物理学中使用
CMOS基本逻辑门电路
存储元件
锁存器(Latch):保存(像闩锁一样锁住并维持)数据的存储元件
1.简单锁存器
2.D锁存器(Data Latch,D-Latch,数据锁存器)/通过型锁存器(E为1时输入的D直接通过Q输出)
D(Data)和E(Enable)
3.依据时钟信号同步并保存数据的D触发器
D(Data)和C(Clock)
C为0时,前端D锁存器输出信号D的值,后端D锁存器保持之前的数据。
C为1时,前端D锁存器保持之前的数据,后端D锁存器将前端D锁存器保持的数据直接通过Q输出。
建立时间与保持时间
建立时间(setup time):时钟变化前必须稳定输入信号的时间。
保持时间(hold time):时钟变化后必须稳定输入信号的时间。
组合电路和时序电路
组合逻辑电路:输出值仅由输入信号的状态决定,不依赖于过去输入、不需要记忆维持(不含有存储元件)过去的输入信号的电路。
时序电路:输出值同时依赖于现在和过去输入信号(含有用于保持输入的存储元件)的逻辑电路。
同步时钟如何区别现在和过去
时钟同步设计中,有一种周期性地在H和L间变化的时钟信号,时钟变化边沿(上升沿或下降沿)之前被称为过去,之后被称为现在。
Verilog HDL语言
Verilog HDL是一种HDL语言(Hardware Description Language,硬件描述性语言)
可以进行抽象度较高的RTL(Register Transfer Level,寄存器传输级)电路设计
面积、时钟周期等约束参数
使用Verilog HDL进行硬件设计
逻辑综合:将RTL级别记述的抽象电路转换到门电路级别的电路网表的过程
(对ASIC(Application Specific Integrated Circuit)、FPGA(Field Programmable Gate Array)等不同电路实现技术,需要使用技术厂商提供的相应目标元件库)
模块(最基本构成单位):设计一个功能单位的逻辑
模块声明的语法
(1)在module关键字后记述该模块名
(2)<输入输出信号的定义>
①信号声明的关键字
输入信号的声明使用input关键字
输出信号的声明使用output关键字
双向信号的声明使用inout关键字
②描述
数据类型、信号线的位宽(方括号中记述最高位和最低位的位置,中间用冒号隔开)和信号名
比特数据的最高位被称为MSB(Most Significant Bit)
最低位称为LSB(Least Significant Bit)
输入输出信号的声明之后使用右圆括号加分号结束,如);
(3)<电路描述>模块内的电路逻辑
assign语句将in_0和in_1相加的结果输出到out
(4)endmodule关键字结束模块的定义
eg.32位的加法器(in_0和in_1两个32位的输入信号,相加的结果从32位的out信号输出)
加法器框图
加法器程序
【注】
1.自由格式语言:可在任意地方加入换行、空格以及Tab等空白符号
2.区分大小写:大小写的英文字符分别表示不同的含义
3.有效标识符(用来命名变量和模块):英文字母(a~z, A~Z)、数字(0~9)、下划线(_)和美元符号($)
4.自定义的标识符必须以英文字母或下划线开头
5.注释: /*和 */之间,或从//开始到一行末尾
6.块:begin和end之间的部分
模块实例化
其他模块调用设计好的模块
模块的实例化
逻辑值与常数表达
变量的声明与数据类型
数据类型
(1)寄存器型:可以保存上次写入数据的数据类型,根据程序不同可以生成锁存器、触发器等存储元件,也可能生成组合电路
寄存器型变量可以在always和initial语句中实现过程赋值(Procedural Assignment)
(一个过程块只能二选一)
①阻塞式赋值:按照代码顺序进行赋值(先赋值的代码赋值完成之前阻塞后续代码的赋值);用=运算符
②非阻塞式赋值:所有代码不会互相阻塞,同时进行赋值;用<=运算符
(2)网络型:用来描述模块和寄存器间连接的数据类型,只描述信号的传输不持有数据
网络型变量可在assign语句或声明语句中实现连续赋值(Continuous Assignment)
assign语句中连续赋值
声明语句中连续赋值
signed和unsigned关键字指定变量符号
无符号数转换为有符号数时使用$signed(),有符号数转换为无符号数时使用$unsigned()
【注】定义默认网络类型,可不用声明直接使用;可在大量使用网络型变量的网表程序中减少代码量。但由于失误而未声明类型的信号会被自动处理为默认网络类型,编译器无法检测错误,故推荐将默认网络类型设置为无效。
运算符
缩减运算符:对信号的所有位进行位运算,最终输出1位的运算结果
使用拼接运算符
①组合比特序列
②重复比特序列
条件分支语句if与case
可以在initial或always语句声明的过程块中使用
循环语句for与while
可以在initial或always语句声明的过程块中使用
always过程块
①指定事件表达式
所指定的事件触发时执行其中的语句序列
事件:特定信号的变化、信号的上升沿(posedge)、信号的下降沿(negedge)等
②指定常数表达式(仿真时常用)
在每经过该常数时间便执行一次always中的语句序列
用always语句描述组合电路
定义了一个adder模块,它有两个32位wire型输入in_0和in_1、一个32位reg型输出。always中将两个输入相加后赋值给输出。阻塞式赋值。
【注】
1.一定要将case语句的条件写全,或者使用default来确定默认值
2.使用if语句时一定要写else条件,或者在if语句前为变量值赋予默认值
否则虽然设计的是组合电路,但会产生本不应有的存储元件,从而变成时序电路。
Don't care指示方法:在default中为输出赋予不定值,输出为逻辑综合时优化的数值
用always描述时序电路
定义了一个叫做ff的模块,它有clk、reset_、d_in三个一位wire型输入信号,和一位reg型输出信号d_out。
d_out在clk的上升沿同步动作,将d_in的值储存。并且d_out在reset_的下降沿被异步地复位,初始化为0。
基本上都是按照时钟同步执行,事件表达式中要指定时钟的信号边沿(确定在时钟信号上升时触发电路动作,或者在时钟信号下降时触发电路动作)和时钟信号名
上升时动作记述为posedge,下降时动作记述为negedge
事件表达式:可以使用or列举多个条件
预处理
代码编译前对其进行预先处理
例子程序
32组位宽为32的寄存器堆。
读取操作:将地址信号(addr)指定的寄存器的内容,通过多路选择器选择,输出到输出信号(d_out)。
写入操作:当写入使能信号(we_)有效时,向地址信号(addr)指定的寄存器写入输入数据(d_in)。
寄存器堆模块在regfile.v文件中实现;regfile.v引用了regfile.h头文件。
regfile.h头文件
`ifndef __REGFILE_HEADER__ // 包含文件防范 `define __REGFILE_HEADER__ /********** 信号电平 *********/ `define HIGH 1'b1 // 高电平 `define LOW 1'b0 // 低电平 /********** 逻辑值 *********/ `define ENABLE_ 1'b0 // 有效(负逻辑) `define DISABLE_ 1'b1 // 无效(负逻辑) /********** 数据 *********/ `define DATA_W 32 // 数据宽度 `define DataBus 31:0 // 数据总线 `define DATA_D 32 // 数据深度 /********** 地址 *********/ `define ADDR_W 5 // 地址宽度 `define AddrBus 4:0 // 地址总线 `endif
regfile.v
正逻辑、负逻辑与信号状态
正逻辑:高电平有效、低电平无效的分配方式。
负逻辑:高电平无效、低电平有效的分配。
assert(断言):控制信号转为有效状态的动作
negate(无效):转为无效状态的动作
enable(使能):信号有效
disable(非使能):信号无效
Testbench:记述仿真程序的文件
Testbench没有输入输出信号,调用被测模块,传递输入信号并观测输出。
系统蓝图
字编址与字节位移
CPU一次处理宽度大于一个字节的数据。
字:CPU能处理的数据宽度
字编址:为每一个字宽的数据赋予一个地址的方式
总线的设计与实现
总线协议:使用信号线的通信规则。
通过总线访问时,需要预先确定总线主控与总线从属之间的通信规则。
总线访问过程
①读取访问
[Ⅰ]请求总线使用权
[Ⅱ]取得总线使用权
[Ⅲ]总线访问开始
[Ⅳ]来自总线从属的应答
[Ⅴ]总线访问结束并释放总线使用权
②写入访问
[Ⅰ]请求总线使用权
[Ⅱ]取得总线使用权
[Ⅲ]总线访问开始
[Ⅳ]来自总线从属的应答
[Ⅴ]总线访问结束并释放总线使用权
总线的实现
1.总线仲裁器的实现
接受总线主控发来的总线使用请求,并将使用权赋予合适的总线主控。
总线仲裁器的状态转移图
2.总线主控多路复用器的实现
基于总线仲裁器输出的总线赋予信号,选择总线使用权所有者的信号,并将其输出到总线。
3.地址解码器的实现
基于总线主控输出的地址信号,判断将要访问哪个总线从属,并生成片选信号。
地址映射(address map):访问的地址与总线从属的对应关系
4.线从属多路复用器的实现
基于地址解码器输出的片选信号,将被选择的总线从属的输出信号发送到总线。
5.总线的顶层模块
将总线仲裁器、总线主控多路复用器、地址解码器以及总线从属多路复用器4个模块进行连接的模块。
存储器的设计与实现
制作存储器用到了FPGA的RAM区域。可以作为子模块,以实例化的方式使用!
Dual Port RAM的访问时序图
[Ⅰ]存储器的实例化
块RAM的实例化。
[Ⅱ]异步复位
复位信号有效时,将就绪信号初始化。
[Ⅲ]生成就绪信号
片选信号与地址选通信号同时有效时,因为即将访问总线,就绪信号变为有效。其他情况时,就绪信号设置为无效。
AZ Processor的设计与实现
CPU
流水线处理:一种提高CPU的处理性能的技术;将处理操作分为多个阶段,然后像流水线作业一样执行。
CPU中的各种硬件资源,只在处理的相应阶段使用,其他时间大多处于空闲状态。
读取某条指令之后,在该指令解码的同时读取下一条指令。
通过使各个阶段的动作重叠,让硬件资源有效使用,同时提高处理速度。
各个流水线级的处理时间应该尽量相等。
【典型的5级流水线】体系结构 | 五段流水线 | 流水线技术_stone_fall的博客-CSDN博客
- IF(Instruction Fetch)阶段:将PC的值发送到内存,读取指令。
- ID(Instruction Decode)阶段:将读取的指令解码并决定将要进行的操作,从寄存器堆读取数据。
- EX(Execution)阶段:使用运算器执行操作。可以执行算术运算和逻辑运算的运算器称为ALU(Arithmetic and Logic Unit)。
- MEM(Memory Access)阶段:进行内存访问。
- WB(Write Back)阶段:将结果写回寄存器堆。
实现了流水线化的CPU,将5个流水线级的操作重叠使用!!!
冒险
流水线故障(无法执行操作)的原因
CPU模式
- 内核模式(Kernel Mode)或管理者模式(Supervisor Mode):全部指令可以无限制执行的模式;操作系统等系统软件需要的
- 用户模式(User Mode):可执行的指令被限制的模式;应用软件通常工作的。(被限制的操作包括CPU控制寄存器的访问、改变CPU状态的指令等)
更改CPU模式
- 从高权限的内核模式转换到低权限的用户模式时,可通过操作控制寄存器实现。
- 从低权限的用户模式转换到高权限的内核模式,需使用专用的指令。
中断
让CPU暂停正在执行的操作,跳转到中断处理程序执行其他操作的功能,会变更到内核模式。中断处理完成后返回到中断处继续执行。
常用在通知来自I/O的事件、处理程序执行中的异步事件等。
外部因素引起的与正在执行的操作的异步情况。
异常
CPU的执行产生了预期之外(无法解码的指令、运算结果溢出以及操作违反权限等)的结果。
暂时中断当前程序,跳转到异常处理程序,变更到内核模式。异常处理完成后,原则上将返回异常中断处,但如果发生致命错误会强制中止执行的程序。
在正在执行的操作的内部发生的。
中断和异常的处理本质上是一致的。
CPI(Clock cycle Per Instruction):平均一条指令所需的时钟周期。(知道了程序行数和CPI,即可计算出程序执行所需要的时钟周期数。)
SPM(Scratch Pad Memory):CPU可不经过总线直接访问的专用内存。
I/O的设计与实现
最基本的I/O
- 测量时间用的定时器
- 串口通信规范UART(Universal Asynchronous Receiver Transmitter)
- 控制LED、开关用的GPIO(General Purpose Input Output)
定时器
用来测量时间的装置。计算机利用定时器实现时间测量、周期性处理、超时判断等许多用途。
两种动作模式
- 单次定时模式:经过设定时间后向CPU请求一次中断即完成操作,只进行一次时间测量时使用。
- 循环定时模式:每经过设定时间就向CPU请求一次中断,在需要执行周期性操作时使用。
UART
起止式同步接收、发送串口通信装置。
使用收、发两根信号线进行串口通信(数据一位接一位地传输)的标准。
起止式同步通信:在发送的数据前添加表示通信开始的起始位(L)、在数据末尾添加表示通信结束的停止位(H)。空闲时总是输出停止位。
从LSB一端开始按顺序输出,并可以选择添加奇偶校验位,最后输出停止位。数据传输单位为7位或8位。
通信速率:用波特率(baud rate)表示。
波特率指的是信号被调制以后的变化率,即单位时间内载波(数据位、起始位、停止位、奇偶校验位)变化的次数。
常用的波特率:9600 baud、19200 baud、38400baud等。
RS-232(Recommended Standard 232)(DE-9接头)或串口:UART标准的实现;连接调制解调器等计算机周边设备;作为控制台接口。
GPIO
以位为单位进行数字输入输出的I/O接口。
输入时从外部读取输入信号、输出时将写入的值输入到外部。
控制寄存器
输入输出方向(INOUT_DIR)该寄存器用来设置输入输出端口的信号方向。当寄存器值为0时端口为输入、值为1时端口为输出。该寄存器各个比特对应控制相应的输入输出端口。
GPIO框图
输入输出端口使用三态门实现。
三态门(也称为三态缓冲器)是一种可以输出H、L以及高阻(电气上绝缘/断路)状态的电路结构。
AZPR SoC整体连接
各模块的连接
CPU、ROM、定时器、UART、GPIO,以及连接这些模块的总线的连接
时钟模块的实现
提供3种信号:时钟信号(clk)、反相时钟信号(clk_)、与异步复位信号(reset)。
对主时钟信号分频、倍频、移相,从而提供用户电路所需要的时钟信号。
DCM的输入为时钟信号(CLKIN_IN)和异步复位信号(RST_IN),输出为生成的时钟信号(CLK0_OUT, CLK180_OUT)和锁频信号(LOCKED_OUT)。
复位无效时,DCM将输入时钟信号进行处理,生成用户需要的时钟信号。生成的时钟稳定后将锁频信号变为有效。
DCM锁存和复位
顶层模块的实现
芯片与时钟模块连接
AZPR SoC的仿真
对整个系统进行仿真时,需要准备FPGA中使用的DCM和内存模型。
DCM模型
配置的DCM将依据输入的时钟,输出频率相同但相位为0度和180度的两种时钟。
Single Port ROM是只有一个读取端口的专用存储器。
Dual Port RAM是具有两个可以同时读写端口的存储器。
Testbench的编写
向被测电路输入时钟和复位信号,推进仿真周期。
使用Icarus Verilog软件和做好的Testbench对系统进行仿真。