第一章:数字电路基础回顾——从晶体管到FPGA的底层世界
各位同学,大家好。我是你们的FPGA讲师。
说实话,每次开课讲第一章,我都挺感慨的。很多同学一上来就急着写Verilog代码,恨不得马上点亮LED。但我个人习惯,一定要先带大家把“地基”打牢。你想想看,如果连数字电路最基本的单元是什么都不清楚,写出来的代码很容易出问题,而且出了问题你都不知道怎么查。
所以今天,咱们就从最底层的晶体管开始,一路聊到FPGA内部到底长什么样。
1.1 从晶体管到逻辑门:开关的世界
数字电路的核心是什么?说白了,就是“开关”。
晶体管,你可以把它想象成一个水龙头。栅极(Gate)就是阀门,源极(Source)和漏极(Drain)就是水管的两头。给栅极加个电压,水龙头就开了,电流就能从源极流到漏极。不加电压,就关断。
嗯,就是这么简单。
用这个开关,我们就能搭出最基本的逻辑门。比如一个CMOS反相器(NOT门),上面一个PMOS管,下面一个NMOS管。输入为高电平时,上面的PMOS关断,下面的NMOS导通,输出被拉到地,就是低电平。反过来也一样。
核心概念: 数字电路里只有两种状态——0和1。0对应低电平(通常是0V),1对应高电平(比如3.3V或1.8V)。
有了反相器,我们就可以组合出与门、或门、与非门、或非门等等。我在项目中遇到过最经典的例子,就是用四个与非门搭一个SR锁存器。那时候刚毕业,觉得直接用现成的门电路不就行了?后来发现,在FPGA里,很多时候你需要理解这些基本结构,才能写出高效的代码。
1.2 组合逻辑 vs 时序逻辑:一个“记忆”的差别
这两个概念,是数字电路的分水岭。很多初学者搞混,我建议你记住一句话:组合逻辑没有记忆,时序逻辑有记忆。
组合逻辑: 输出只取决于当前的输入。比如一个加法器,你输入1+1,它立刻输出2。输入变了,输出马上跟着变。没有“过去”的概念。
时序逻辑: 输出不仅取决于当前输入,还取决于电路之前的状态。比如一个计数器,它记得自己数到几了。这个“记忆”靠什么实现?靠触发器(Flip-Flop,FF)。
触发器是什么?说白了,就是一个能在时钟边沿“拍照”的单元。时钟上升沿来的时候,它把输入D的值“拍”下来,存到输出Q上。其他时间,Q保持不变。
我的经验: 写Verilog时,always @(*) 里一般写组合逻辑,always @(posedge clk) 里写时序逻辑。这个区分非常重要。我曾经见过一个同事,把所有逻辑都塞进一个always块里,结果仿真怎么都不对,查了两天才发现是组合和时序混在一起了。
为什么要区分它们?因为时序逻辑引入了“时间”的概念。时钟决定了什么时候更新状态,这让电路变得可控、可预测。你想想看,如果没有时钟,所有信号都在乱跳,那整个系统就乱套了。
1.3 FPGA的底层结构:LUT、FF、BRAM、DSP
好了,现在我们知道了晶体管可以组成逻辑门,逻辑门可以组成组合逻辑和时序逻辑。那FPGA内部到底是怎么把这些东西组织起来的?
FPGA,全称是现场可编程门阵列。它不像CPU那样有固定的指令集,而是你可以“现场”配置它的硬件电路。它的基本单元,我称之为“乐高积木”。
1.3.1 查找表(LUT)—— 组合逻辑的万能实现器
LUT是FPGA里实现组合逻辑的核心。它本质上是一个小型的RAM,里面存了一张“真值表”。
举个例子,一个4输入LUT,有4个输入引脚,1个输出引脚。它有2^4=16种输入组合。你可以在LUT里预先存好这16种组合对应的输出值。当输入信号进来时,LUT直接查表,输出对应的结果。
这意味着什么?意味着一个LUT可以实现任意4输入的逻辑函数。不管是与门、或门、异或门,还是更复杂的逻辑,一个LUT全搞定。
避坑指南: 我曾经在项目中为了省资源,试图用几个LUT拼出一个复杂的逻辑功能。后来发现,其实直接用现成的IP核或者更高层次的描述,综合器会自动优化LUT的使用。手动优化往往费力不讨好,还容易出错。
1.3.2 触发器(FF)—— 时序逻辑的基石
每个FPGA的“逻辑单元”(Logic Cell)里,除了一个LUT,通常还会配一个或两个触发器(FF)。LUT负责组合逻辑,FF负责寄存(存储)结果。
你写的 reg q; 和 always @(posedge clk) q <= d;,综合后就会映射到一个FF上。
1.3.3 块RAM(BRAM)—— 真正的“大容量”存储
LUT和FF虽然也能做存储,但容量太小,而且浪费资源。当我们需要存储大量数据时(比如一个FIFO、一个数据缓冲区),就要用到BRAM。
BRAM是FPGA内部专用的存储模块。常见的容量是36Kb或18Kb。你可以把它配置成不同位宽和深度的RAM或ROM。
| 存储类型 | 容量 | 典型用途 |
|---|---|---|
| LUT作为分布式RAM | 很小(几十bit) | 小规模寄存器堆 |
| FF作为寄存器 | 1 bit | 流水线、状态机状态 |
| BRAM | 18Kb / 36Kb | FIFO、数据缓存、查找表 |
注意: BRAM的读写有固定的时序要求。比如读操作,地址给出后,需要等一个时钟周期才能读到数据。这个“读延迟”在写代码时一定要心里有数。我见过有人把BRAM当组合逻辑用,结果仿真和实际行为完全对不上。
1.3.4 DSP单元—— 专门干“苦力活”
FPGA里还有一个宝贝,叫DSP单元(数字信号处理单元)。它专门用来做乘法、加法、乘累加这些运算。
为什么不用LUT搭乘法器?因为太浪费了。一个18x18的乘法器,如果用LUT搭,可能需要几百个LUT,而且速度还慢。而DSP单元,一个时钟周期就能算完,又快又省资源。
我在做图像处理项目时,经常要用到卷积运算。一个3x3的卷积核,需要9个乘法器和8个加法器。如果用LUT实现,整个芯片的资源可能都不够。但用DSP单元,几个就搞定了。
小结
好了,这一章的内容就到这里。我们回顾了从晶体管到逻辑门,再到组合逻辑和时序逻辑的区别,最后深入了解了FPGA的四大底层结构:LUT、FF、BRAM、DSP。
这些东西,是FPGA设计的“内功”。你理解得越深,写代码时就越有底气。下一章,我们就要开始真正接触Verilog语法了。到时候你会发现,很多语法规则,其实都和今天讲的硬件结构一一对应。
嗯,今天就先聊到这儿。有问题随时问我。
课后思考: 为什么FPGA里要用LUT来实现组合逻辑,而不是直接用与门、或门?想想看,这样做的好处是什么?