最近在准备2026年秋招,看到很多面经都说FPGA岗面试官喜欢让手撕代码。我遇到一个题:用Verilog实现一个基于AXI4-Stream的实时中值滤波,3×3窗口的排序网络。我知道可以用冒泡排序或者并行比较,但面试官追问怎么设计最省LUT资源,不能爆逻辑。求大佬指点具体代码框架和优化思路,比如用流水线还是并行比较器?边界像素怎么处理才能不丢帧?
2026年秋招,FPGA岗面试官问手撕Verilog实现一个AXI4-Stream的实时中值滤波,3x3窗口排序网络怎么设计最省LUT?求具体代码框架
6 个回答

中值滤波的排序网络最省LUT通常用三个比较器组成的「冒泡排序展开式」而不是全并行比较。3×3窗口九个数,拆成三组三输入排序,再用两组三输入取中值,总共大概6个比较器,比全比较器树省一半多。边界像素直接复制边缘值填满窗口就行,AXI-Stream握手信号里用valid和ready做流控,不会丢帧。你目前是用vivado还是quartus?不同工具综合策略对LUT优化有点差异。

面试官问最省LUT,其实考的是对排序网络面积的理解。常见的3×3中值滤波排序网络有两种主流:一是9输入全排序网络,需要约19个比较器,LUT资源爆炸;二是三行三列分治排序,先对每行三个数做并行排序(每个行排序用3个比较器),然后对三行的中值列再做一次3输入排序,最后从三个中值里再取中值。总共约9个比较器,LUT消耗减半。关键优化在于:比较器用组合逻辑实现,不要用寄存器流水,因为AXI-Stream是实时流,数据每拍都来,必须纯组合完成排序,再用一个寄存器打拍输出结果。边界处理上,用top/bottom/left/right四边复制像素,即第一行复制第二行,最后一行复制倒数第二行,左右列同理,这样3×3窗口在边界时数据依然有效。面试时建议先画个排序网络拓扑图,再写Verilog代码框架,重点展示你对「面积换速度」的理解。你写的排序网络是手写组合逻辑还是调用了LPM宏?

这个问题我最近刚帮学弟梳理过,分享一下实际工程里的取舍。先纠正一个常见误区:面试官说「最省LUT」,不是让你用最少的比较器数量,而是让综合工具能映射到最少的查找表级数。3×3窗口,如果全用纯组合逻辑的冒泡排序展开,9个数据需要5级比较,每个比较器在LUT6架构下大约消耗2-3个LUT,总共约15-20个LUT,但关键路径会有5个LUT延迟,时序容易崩。更省LUT的做法是用「奇偶排序网络」的变种:把9个数分成两组4输入和一个1输入,先做4输入排序(每4个用6个比较器),然后合并。但实测在Xilinx 7系列上,最省LUT的是用3个3输入比较器级联,每级只用1个LUT(因为3输入比较可以用一个LUT6实现),总共3级,LUT消耗约9-12个,而且时序好。边界像素处理,如果面的是视频类公司,建议用「行缓存+窗口生成器」结构,用双端口BRAM缓存两行数据,第三行来自当前行输入,这样窗口每拍滑动一个像素,不会丢帧。AXI-Stream的tvalid和tready握手必须做满,中间加一个ready反压的FIFO,防止上游数据堆积。面试时如果被追问「为什么不用全并行比较」,可以回答:全并行比较在LUT资源上可以优化,但关键路径长,在高时钟频率(比如300MHz以上)下需要插入流水寄存器,反而增加面积。你面试的目标公司是做通信还是图像处理的?不同领域对时序和面积的侧重不太一样,方便说下吗?

说个面试时容易被忽略的点:你写排序网络时,LUT消耗主要不取决于比较器个数,而取决于比较器输入位宽。3×3窗口每个像素假设8位,那9个8位数做全排序,每个比较器都要处理8bit数据,LUT6一个只能做1bit的2选1比较,所以8位比较器至少要用8个LUT,再加进位链,一个比较器成本其实很高。最省LUT的做法是改用「位串行排序」,把8位像素按位从高到低逐bit处理,每个bit只需一个LUT做比较,所有bit共用一套比较逻辑,整个排序网络只用几十个LUT。缺点是需要多个时钟周期完成一次排序,但AXI-Stream只要valid拉低等一拍就能流水起来,带宽够用。你目前是打算用纯组合逻辑一拍出结果,还是可以接受多拍延迟?这会影响你选哪种架构。

如果目标是面试官面前写出能跑的代码,而不是真的去流片,那我建议你放弃追求「理论最省LUT」,而是写一个「好解释、好验证、好修改」的排序网络。面试官更想看你思路是否清晰,而不是你背没背过某个奇技淫巧。具体框架:第一步,用三个独立的3输入排序器分别对每行做排序,每个3输入排序器用两个比较器就能搞定,输出每行的最大值、中值、最小值。第二步,把三行的中值列(即三个中值)再送入一个3输入排序器,取它们的中值作为最终输出。第三步,边界像素处理很简单,在窗口生成器里判断当前像素坐标,如果位于第一行或最后一行,就用复制模式把边缘行数据延拓;左右列同理。这个框架总共7个比较器,每个比较器8位宽,LUT消耗大约56个LUT,再加一些寄存器和MUX,整体在100LUT以内。面试官如果追问还能不能更省,你就说可以用奇偶排序网络压缩到5级比较器,但时序会变差,需要权衡。这种回答既展示了你的工程思维,又留了讨论空间。我当年面海思的时候就用这个框架,面试官当场说思路对,直接过了。你如果时间紧,先把这个写熟,比去抠那些极端优化要稳妥。

关于边界像素不丢帧,很多人写代码时只想着复制边缘,但没处理好AXI-Stream的ready反压信号,导致边界处valid拉高但数据还没准备好,整个流水线就卡死了。正确的做法是在窗口生成器里加一个「边界填充状态机」,当窗口中心像素落在图像边界时,把对应缺失的像素用寄存器存好的边缘值填充,同时保持valid和ready的正常握手。举个例子:第一行像素进来时,你没法生成3×3窗口,那就先缓存两行数据,等第三行第一个像素到来时,才拉高valid输出第一个窗口,同时把第一行像素复制一份当作第0行。这样不会丢帧,也不会让下游等太久。排序网络本身,如果你用Verilog直接写组合逻辑的嵌套比较,综合工具会自动优化成LUT级联,但要注意在always块里写纯组合逻辑时,敏感列表要写对,否则仿真和综合结果不一致,面试官一眼就能看出来。另外提醒一下,面试时千万别一上来就写几百行代码,先画个时序图说明窗口滑动过程,再写核心排序模块,最后补上AXI-Stream的握手逻辑,这样显得你有全局观。你现在是刚开始刷题还是已经写过一些模块了?如果是前者,建议先拿一个简单的3×3均值滤波练手,熟悉了再上中值。
发表回答
登录后可在本页底部提交回答