如何做FPGA的模块划分?

做过复杂FPGA项目的朋友应该都有这种体会:一个工程里几十个模块,相互之间信号错综复杂。改一个小功能,牵一发而动全身;加一个新需求,不知道从哪里下手。到最后调试的时候,一个信号不知道是哪个模块拉低的,仿真波形看得眼花缭乱。

这些问题的根源,很多时候就是模块划分没做好

今天咱们就来聊聊:FPGA的模块划分到底应该怎么做?有哪些原则和方法,能让你的工程更清晰、更容易维护、也更容易复用?

一、先想清楚:为什么要划分模块?

很多新手写FPGA,习惯在一个顶层文件里把所有的always块、assign语句全部堆在一起。小工程没问题,一旦代码超过2000行,这种写法就会变成灾难:

  • 修改一个功能,不知道会影响哪些地方

  • 仿真调试时,信号铺天盖地,找不到关键节点

  • 换个人接手代码,根本看不懂

  • 想复用某部分功能,发现耦合太紧,剪不出来

所以模块划分的目的很简单:分而治之,降低复杂度。好的模块划分,能让每个模块功能单一、接口清晰、内部独立,就像积木一样,可以单独开发、单独仿真、单独测试,最后拼到一起就能跑。

二、模块划分的五个核心原则

原则一:功能单一,高内聚

一个模块只做一件事。比如"数据采集模块"只管从ADC读数据,"滤波模块"只管做FIR滤波,"存储模块"只管写DDR。不要做一个"全能模块",既管采集又管滤波还管存储,那样的模块内部逻辑纠缠不清,改一个功能可能影响其他功能。

判断标准:你能不能用一句话说清楚这个模块是做什么的?如果能,说明功能够单一;如果需要说三句话,大概率太复杂了,需要拆分。

原则二:接口清晰,低耦合

模块之间的连接信号越少越好。理想情况下,几个模块之间只有数据总线、握手信号(valid/ready)、和控制信号。

避免 :全局信号满天飞。很多工程师喜欢用wire把内部信号到处拉,顶层文件里几百根线,看着就头疼。更好的做法是用总线接口(比如AXI-Stream、AXI4-Lite)把多个信号打包,顶层只看到几个接口例化。

原则三:时钟和复位域独立

不同时钟域的模块一定要分开。比如一个模块跑100MHz,另一个模块跑200MHz,它们之间不能混在一起,中间必须加异步FIFO或握手桥接。

同理,不同复位信号的模块也建议分开。不要在顶层用一个全局复位把所有模块都挂上,那样会导致整个芯片在复位瞬间产生巨大的电流尖峰,而且不利于模块独立复位调试。

原则四:考虑复用性

如果一个模块将来可能用到别的项目里,设计时就尽量让它不依赖项目特定的参数。比如把数据位宽、FIFO深度、算法系数等做成可配置的参数(parameter),而不是写死。

另外,模块的输入输出接口尽量用标准的、通用的协议(AXI、AVALON、简单valid/ready),而不是自定义的、只有你自己看得懂的握手方式。

原则五:分层清晰

顶层不要放任何逻辑,只做例化和连线。第二层是"功能大块",比如数据通路、控制通路、接口桥接。第三层才是具体的功能模块。

这样做的好处是:你拿到一个新需求时,先看应该放在哪一层、哪个大块下面,而不是随便往顶层加信号。

三、一个实际的划分案例

假设我们要做一个数据采集系统:ADC进来数据,经过数字滤波,存入DDR,同时通过USB上传到电脑。

不好的划分:一个顶层文件里,ADC接口、滤波、DDR控制、USB控制全混在一起。

好的划分

  • adc_interface:负责从ADC读取数据,做位对齐、格式转换,输出valid+data

  • filter_module:接收adc_interface的数据,做滤波处理(可配置参数),输出滤波后数据

  • ddr_writer:接收滤波后数据,通过AXI接口写入DDR,带FIFO缓存

  • usb_uploader:从DDR读取数据,打包成USB格式发送

  • top:只负责例化上面四个模块,连接信号,以及一个简单的控制状态机

这样一来,任何一个模块需要修改,都不会影响其他模块。仿真时可以单独仿filter_module,不需要跑整个DDR和USB的模型。

四、模块划分中常见的坑

坑一:模块大小两极分化。有的模块只有几十行,有的模块几千行。太小的模块没必要独立,合并到上级即可;太大的模块一定要拆分。

坑二:模块间互相调用。比如模块A调用了模块B的某个内部信号,模块B又反过来用了模块A的。这种循环依赖会让设计无法综合,而且非常难调试。解决办法:把共用信号放到上一层,或者定义一个中间模块解耦。

坑三:忽略边界条件的模块划分。比如异步时钟域之间的信号直接跨模块传递,没有在接收模块做同步处理。正确的做法是:同步逻辑放在接收模块内部,让接口看起来是同步的。

坑四:过度划分。为了追求模块小而精,把本来逻辑简单、强相关的几部分拆成太多个小块,反而导致顶层连线复杂、例化代码冗长。适度就好。

五、实际工作中的建议

  • 先画框图,再写代码。用Visio或者纸上画好模块、接口方向、数据流向,评审通过后再开写。花1小时画图,能省3小时重构。

  • 保持一致性 。团队的代码风格、模块命名规则、接口信号命名规则要统一。比如所有模块的输出都用xxx_o,输入用xxx_i,时钟用clk,复位用rst_n

  • 文档跟着代码走。每个模块开头写一段注释:功能描述、输入输出说明、参数列表、修改记录。不然半年后自己都不记得当初为什么这么分了。

由你创科技能做什么

我们由你创科技FPGA开发上承接了大量各类规模的项目,从小型接口桥接到大型多芯片协同处理系统。模块划分是我们每一个项目开始时必做的功课------不只是为了代码整洁,更是为了项目能按时交付、后期能维护迭代。

如果你正在规划一个新的FPGA项目,不确定怎么划分模块才合理,或者现有的代码已经乱成一团、改一个bug引出三个新bug,欢迎来找我们聊聊。我们可以帮你做架构梳理、模块重构,甚至把整个开发接过去。

毕竟,好的模块划分,是写出来的代码能一直用下去的基础;乱的模块划分,是每个接手的人都要骂娘的开端。

相关推荐
techdashen1 天前
从网络栈继续往下:micro:bit、2.4GHz、调制方式,以及一个不太靠谱但很有趣的想法
网络·fpga开发
FPGA小徐1 天前
FIR 数字滤波器 --verilog设计实现
fpga开发
zlinear数据采集卡1 天前
从协议解析到波形实时显示:硬核拆解ZLinear采集卡上位机软件的开发架构
arm开发·单片机·嵌入式硬件·fpga开发·架构·开源
pcjiushizhu1 天前
ModelSim 仿真时 Simulate 无反应或只显示 Loading 的解决方法:网卡问题排查
fpga开发
FPGA小迷弟1 天前
vivado中的AXI Interconnect到底应该怎么用,他的底层原理是什么,一篇文档全部理清楚!!!
网络协议·tcp/ip·fpga开发·verilog·fpga
国科安芯2 天前
ASC4T245S分组双向控制架构深度解析:独立DIR/OE控制、QFN16封装与混合方向总线桥接
单片机·嵌入式硬件·物联网·fpga开发·架构·risc-v
尤老师FPGA2 天前
GT系列2:GT基础架构(二)
fpga开发
想你依然心痛2 天前
电源时序控制:多路电源的上电顺序与监控——复位、看门狗
fpga开发
Eloudy2 天前
hsb fpga/ 目录分析
fpga开发·量子计算
Hello-FPGA2 天前
GPU Direct DMA RDMA 与FPGA 通讯在Jetson 平台的测试表现
fpga开发