《Vivado数字设计》读书笔记
数字逻辑基础
数字电路与晶体管
数字电路中的信号变化是不连续的,信号的电压取值只有VCC和GND两种情况,表示逻辑高/低电平,数字电路状态"0, 1"定义了两个宽范围电压区域,在区域内电压的变化不会改变数字信号的状态。因此相比于模拟电路信号,数字信号具有抗干扰能力强和处理方式灵活的显著优势。
为了对电路进行控制,需要引入数字电路的开关,在现代数字电路中的晶体管开关称为金属氧化物半导体场效应晶体管(Metal Oxide Semiconductor Field Effect Transistor, MOSFET),它是三端口器件,称为源极,漏极和栅极。其制作工艺为在半导体硅的源极和漏极区域中植入带电离子,在二者中间创建一个绝缘层并生长另一个导体形成栅极,根据植入离子的电性不同,分为植入负电粒子的nFET和植入正电粒子的pFET。也就是说,栅极的极性决定了下方绝缘层中电荷的极性,是可以变化的,但是在左右的源极和漏极区域中,电荷的极性是预先植入好的,不能发生变化,因此可以通过改变栅极的极性来形成或断开电荷通路,从而达到开关的效果。
以nFET(植入了负电荷)为例进行说明,在逻辑电路中nFET源极接GND,当栅极接VCC时,源极和栅极间形成了电压差,会吸引硅中的负电荷进入中间的沟道(半导体中正负电荷粒子可以移动),形成了连续的负电荷通路,nFET处于强导通状态;当栅极接GND时会吸引正电荷聚集在沟道,与两侧扩散区中的负电荷形成PN结,阻止任何一个方向的电流流过。
在源极接GND的情况下,可以通过控制栅极的极性与源极形成电压差,使电路导通。反之如果源极接VCC的话,栅极接VCC也不会形成电压差,栅极接GND形成电压差也不能吸引负电荷移动到栅极绝缘层下,无法形成负电荷通路。
因此nFET源极通常接地,当栅极为VCC时导通,pFET源极通常接VCC,当栅极为GND时导通。可以总结出使用FET构建逻辑电路的基本规则:
- pFET源极必须连接到VCC,nFET源极必须连接到GND
- 电路输出必须通过一个pFET连接到VCC,电路输出必须通过一个nFET连接到GND
- 逻辑电路的输出不能同时连接到VCC和GND
- 逻辑电路使用数量最少的FET
由于pFET和nFET的特性,称为互补金属氧化物半导体电路,即CMOS电路,常见的逻辑门需要的晶体管数量为:INV,需要两个晶体管,NOR,NAND,需要四个晶体管。这也可以解释为什么综合出的网表文件中,使用的是NOR和NAND逻辑门,因为这是最基本的逻辑电路门,只能基于此来实现OR和AND逻辑。
CMOS电路的功耗与逻辑门的输出信号频率成正比,与供电电压的平方成正比。
逻辑代数
提到了格雷码,卡诺图,以及表示方法Sum Of Product,Product Of Sum。
逻辑函数可以写为
- SOP(Sum of Product)的形式,选取所有最小项(函数输出为1)的积
- POS(Product of Sum)的形式,选取所有最大项(函数输出为0)的和
利用卡诺图,可以通过划定长宽均为2的幂次的长方形来化简逻辑,需要注意卡诺图的四个方向都是循环相接的,例如四个角应当被视作在一个长方形内。
参考资料: 1. Transistors as Switches
数字逻辑电路
常见元器件
格雷码的特点是\(g(x)\)与\(g(x+1)\)只相差一个位,一个由二进制逐位转为格雷码的方式为:\(g(x)[n]\) = \(x[n + 1]\) ^ \(x[n]\),反之也可以逆向转换。
半加器是指不接受进位输入,全加器则接受进位输入,超前进位加法器即使用更复杂的逻辑函数来避免逐级进位产生的电路延迟,但是这也会导致逻辑函数的复杂度随位数快速增大。
SR锁存器即可通过Set, Reset信号锁存状态,引入clk信号即为同步SR锁存器,进一步为了避免SR信号相同的状态发明了D锁存器。进一步地,通过多状态的锁存和逻辑,可以实现D触发器,在时钟信号的上升沿或者下降沿锁存,也就是说信号的上升沿和下降沿是通过组合逻辑与锁存的方式来实现的。位移寄存器可以通过多个D触发器相连的方式实现,每当时钟信号上升沿来临时,前一个触发器的信号就会作为下一个触发器的输入,从而起到位移寄存器的作用。
存储器
- SRAM成本较高,速度较快,1个比特需要6个FET组成。
- DRAM成本较低,速度相对较慢,1个比特只需要一个晶体管和电容,但是由于电容上的电荷会泄露,DRAM需要周期性的充电。访问DRAM时,地址先被译码取出行地址,转为独热码使能信号激活,再译码列地址,这样给Row Hammer这样的攻击留下了可能。
可编程逻辑器件原理
可编程逻辑器件发展历史:
- 由熔丝,紫外线擦除,电可擦除等工艺实现的PROM(可编程只读储存器),一旦编程后不可修改。
- 可编程逻辑阵列(PAL)和通用逻辑阵列(GAL),可以实现相对复杂的逻辑函数,由与门和或门的阵列组成。
- 复杂可编程逻辑器件(CPLD),是FPGA的前身,由可编程的逻辑门与若干功能单元组成。
- 可编程门阵列(FPGA),集成了更多复杂的功能模块,如时钟管理、专用DSP等,也集成了处理器核心和SoC,高速互联总线等。
采用可编程的与门或门阵列控制逻辑时,根据逻辑函数的不同,输入通过的门数量不同,导致阵列的输出延时不可控,需要对具体的逻辑函数实现进行分析,不利于确定频率。而FPGA为了解决这一问题采取了查找表LUT(Look Up Table)来生成逻辑,LUT采用了SRAM的工艺,存储了输入与输出的直接对应关系,无需通过逻辑门运算,通过直接查表的方式输出,从而确定了延迟。在烧录时,EDA会将这一真值表写入对应的LUT资源中。
Vivado集成开发环境设计流程
- Post-Synthesis Project 可以导入综合后的网表创建工程。
- XDC 约束文件遵循 TCL 命令
- Post-Implementation Timing Simulation 综合时考虑到了毛刺等实际问题。
Verilog HDL规范
发展历史
Verilog硬件描述语言由OVI(Open Verilog Internatiaonal)组织推动,最早提出的标准为Verilog-1995,经过改进形成标准Verilog-2001,又融合了Verilog-AMS形成了标准Verilog-2005,可以对集成的模拟信号和混合信号系统进行建模。其中还存在另一个Verilog-2005标准是现在的System Verilog,它是Verilog HDL的超集,是对硬件描述语言和验证语言的集成。
程序结构
最基本的设计单元是模块,通过module
和endmodule
关键字声明,包括端口声明、数据声明和逻辑功能定义:
*
端口具有三种方向,分别为input
,output
,inout
,inout
是由三态门实现的双向端口,需要辅助使能信号控制方向,只能使用assign
语句进行逻辑定义。
*
端口可以声明其数据类型为wire
或reg
,其中输入端口或双向端口只能为wire
类型,默认也为wire
类型。
* 端口可以声明其数据含义为signed
表示为有符号数据。
Verilog中也支持函数这一概念,函数本身与是否实例化为硬件电路并无绝对关系,可以理解为是对代码语言复用的概念,至于是否实例化则却决于复用的代码本身。通过关键字function
和endfunction
声明,需要对输入进行声明,只能输出一个结果。
描述方式
按照抽象层级划分,Verilog可分为以下四种描述方式:
- 行为级描述:通常用于仿真行为描述,而非硬件实例化,包含的关键字有:
initial
:用于初始化寄存器状态,在综合阶段不起作用,若想实现寄存器初值应当通过reset
信号for, while, fork & join
:用于仿真阶段的循环,并行event, task
:可以通过call
触发,用于仿真一些触发行为time, #
:设置时间参数,或表示若干时间停顿后继续执行
- 数据传输级描述:也就是RTL级别,描述数据的控制流与传输流,是主要的硬件逻辑行为描述层
- 结构级描述:以
module
为单位建立整体的结构,相对RTL更加抽象 - 开关级描述:可以使用硬件原语对门级别进行描述,可以对传输延迟,驱动强度等参数进行声明
Verilog HDL要素
变量名以下划线或者字母开头,大小写敏感,可以由数字、字母、$
、_
组成,特别地,由\
开头的表示转义标识。
Verilog中实现了一些系统函数和任务,用于仿真测试,常见的有:
$display("info")
:在仿真控制台上显示信息,可以接受格式化字符串$dumpfile("wave.vcd"), dumpvars
:将波形输出到文件中load("test_vector.hex")
:从文件中加载向量$finish, $random, $realtime ....
类似于其他语言,在编程过程中可以向编译器提供元信息,通过define
进行声明,以指导编译器。也可以通过声明属性的方式进行指导,在变量或者结构前通过(* [attribute] = [constant expression] *)
的方式声明其属性,当然编译器并不保证完全遵照属性要求编译。比较常用的属性是mark_debug
,可以标记信号,在综合阶段生成调试核后保留该信号以便接入调试,书中还介绍了full_case
属性,用于标记case
结构,告诉编译器条件完备,避免编译器生成锁存结构应对未列出的情况。其他常见的属性还有optimize_power, fsm_state, dont_touch ...
Verilog中的数字常量格式为:[sign][width]['][base][number]
,其中sign表示符号,默认为正,bsae表示进制,默认为十进制,当数字超过位宽时截取低位,当数字不足位宽时根据是否表示符号数补0或1(base中若出现s
则表示符号数),未指定宽度一般默认为32位。数字常量中的_
只起分隔作用,无实际意义,也可以定义值为高阻态Z。
Verilog同时也支持浮点数的常量,转换时遵循四舍五入,负数则取整按照相反数取整结果的相反数计算。Verilog中的字符串常量会将每个字符编码为8bitASCII码进行存储。
Verilog HDL数据类型
高阻态指的是电路的一种输出状态,既不是高电平也不是低电平。如果高阻态再输入下一级电路的话,对下级电路无任何影响,等效于未接通。而三态门可以实现高阻态这种设计,使得信号在某些情况下处于高阻态。这种设计可以用于实现总线的仲裁、内存控制等,切换到高阻态可以释放总线。通过使用高阻态,电路可以在不需要信号传递时自动进入休眠状态,从而节省能源并提高系统的稳定性。
Verilog HDL表达式
Verilog中绝大多数的运算符如果对含有X
或Z
的变量进行运算都会得到X
的结果,但是===
和!==
可以对含有X, Z
的变量进行比较,区分于==, !=
。特别地,在[cond]?A:B
表达式中,如果条件cond
含有X
或Z
,那么结果将为A&B
。Verilog中也提供了相对复杂的逻辑运算操作,如>>>, <<<
算术位移,**
幂,*, /, %
乘除法,但是要注意系统自动生成的硬件复杂度。Verilog也提供连接操作符,通过{}
对定位宽变量进行连接,可以使用{[num]{[var_list]}}
将表达式复制相连。需要特别注意的是,+, -
默认的结果宽度为Max(width(A), width(B))
。
Verilog HDl分配
数字系统设计和实现
介绍了一些常用的模块实现:
- 在
module
关键字前声明属性use_dsp48="yes"
可以使用DSP48进行加速运算 - 可以使用
generate
例化生成,实现类似于 Chisel 的效果,可以声明例化变量genvar
- ROM, RAM 直接声明为 reg
数组即可,可以通过声明属性
rom_style="block"
来选择实现方式 - XDC约束文件可以通过EDA自带的 IO Planning 功能生成,根据手册连接到需要的端口上