咖啡图片
正在将巧克力泡入咖啡
ntainer" style="display: none">
文章

Verilog建模设计

<摘要>

Verilog建模设计

内功心法

写 Verilog 强调建模而不是编程,思想是用 Verilog 去建立清晰可理解的硬件结构,模块是始终运行的硬件,顺序仅存在于观察结果而不是结构本身。建立了正确的建模直觉,后续的接口封装、系统组合、高级抽象都会变得顺理成章。你是否真的知道自己在描述什么硬件资源,你的设计是否能被画成结构图,你的模块是否在并行地、各司其职地运行。先拆、再分、再并行、再封装、最后组合成系统。

在有一定熟练度之后,写之前最好是先设计好电路大概长什么样(到加法器、ram、fifo 这个层级就可以),然后再用 verilog 实现。一个 always 块可以看作一个电路或者实物上的一个芯片。另外,Verilog 开发遵循自顶向下的模块化设计,模块划分要功能单一,且要留出使能、复位等接口以便系统搭建。有可能的话,把握编译器的理解方式也有助于开发,写出编译器能清晰理解的代码可以大大降低错误率。

功能划分

在实现上,一个模块只允许承担一个功能。功能模块只干具体的事,控制模块只负责调度,组合模块只负责拼装。功能模块 = 自己知道“怎么做”,但不知道“什么时候做”;控制模块 = 自己知道“什么时候做”,但不知道“具体怎么做”。

  • 功能模块是一种高度自洽的、近似“硬件器件”的存在。它的最大特点是:只要输入条件满足,它就自然地、持续地工作,而不需要外部有人一步一步指挥。功能模块通常具备自己的时序、自己的计数器、自己的寄存器状态,但这些状态都是为了完成“它这一件事”,而不是为了配合系统流程。它们的行为可以脱离“系统上下文”而存在。从设计风格上看,功能模块往往是“条件驱动”的,而不是“步骤驱动”的。也就是说,它更像是在回答:“当某个条件成立时,我该输出什么”,而不是:“现在轮到我做第几步了”。

  • 控制模块更像一个时序协调者、流程调度者,最大的特征是:它本身不干具体活,它只决定谁在什么时候该干活。控制模块关心的不是“这个模块内部怎么算”,而是“它什么时候开始”“它什么时候结束”“下一个该谁上”。因此,控制模块往往非常依赖外部模块的反馈信号,比如 done、busy、valid、ready 一类信号。从结构上看,控制模块通常是“强顺序感”的,是可以合理使用状态机思维的地方。控制模块只负责状态切换和调度决策,而不会把具体的功能逻辑塞进去。

    所谓的控制模块,就是用来产生 enable 信号的模块,其它功能模块中加入使能信号的设计,这样就可以由控制模块来调度功能模块。同时,功能模块起到沟通协调的作用,可以灵活调整,以接收各种功能模块的输入,产生各种功能的使能信号,保证了可扩展性。

串行和并行的理解

串行操作有步骤的概念,前后行为具有依赖关系;并行操作则是独立执行互不影响,各自在固定的时间执行操作。顺序操作的语言很多都是高级语言,一些调用的指令都是被隐性处理,如函数的调用指令和返回指令等。而在 Verilog 建模中,主要用状态机这种仿顺序的方式实现这种看起来是顺序的操作。

基本所有外设驱动的编写逻辑,都是晶振脉冲计时输出标志位,然后状态机根据计时标志位进行状态转换和相应动作。

封装和 FIFO

模块的封装,定义为某个基础的最后建模工作,以及封装过后的模块具有独立性。FIFO 可用于消除上下游模块的依赖关系,以缓冲的方式摆脱反馈的束缚。下层模块完成内部工作后,可以直接从 FIFO 中读取操作信息而不用等待上层模块等到下层反馈后下达新的指令。事实上,FIFO 是用来缓冲两个时钟域不同的访问,可以利用 FIFO 这个仓库作为接口的输入,使得接口具备独立性。上层模块调用某个接口时,只需要把信息写入仓库即可,上层模块不用与接口互动而被束缚,如果发现仓库有信息则处理即可。

image-20260131215406381

完整来说应该是,对于接收上游模块指令的模块,可以在最初环节之前引入 FIFO,而对于向下游模块发出指令的模块,可以在最后环节之后引入 FIFO,使得封装之后的接口具有独立性。不过理论上说,基本上所有模块都需要接收上游指令并向下游模块发出指令,所以在最开头还是最末尾引入 FIFO 也是相对的,还是根据具体需求来设计。这样,上层模块只管往 FIFO 中写入操作信息,不用等待下层模块的反馈信息就可以执行其它操作。控制模块接收到反馈模块后再从 FIFO 读取信息。

Verilog 综合

  1. 非阻塞赋值在触发时是同时赋值的,很符合触发器在上升沿到来后将 D 输出到 Q 的实际情况。时序 always 块中左侧变量被综合成触发器,右侧的表达式用逻辑电路实现后连接到触发器的 D 端。与 if 复位配对的 else if 中的变量会被综合成使能信号。

    image-20260130150256115

  2. 相较于 case,if else if 是有优先级的,会综合出多个 MUX2 来实现。

  3. 组合逻辑如果有分支没有输出则会综合出锁存器,因为没有时钟但是又需要存储的功能。锁存器会占用大量资源且时序上不稳定。

    image-20260130151051946

  4. 在Xilinx FPGA开发中,加减乘的运算可以直接写,但不要直接写除法或取余的操作。因为加法和乘法在Xilinx的FPGA芯片中都有对应的硬件资源(比如乘法有DSP Slice),而除法和取余这些操作则没有。这些操作符被综合后,结构和时序往往不易控制。应该使用相关优化后的 ip 模块或工艺库中的集成模块。但是 parameter 类型的常量就可以使用此类操作符,因为在编译之初编译器就会计算出常量运算的结果,不会消耗多余的硬件资源。

  5. 多个数的加法或乘法可以进行展开,因为硬件资源只支持两个操作数,将多个操作数的加法或乘法展开成两个操作数有助于时序收敛并提高频率,即用面积换速度。

参考资料

内功心法部分来自黑金动力社区的《Verilog那些事儿——建模篇》

本文由作者按照 CC BY 4.0 进行授权
/body>