数字IC入门学习:Verilog参数化树形极值统计模块(二分比较树架构+有效数据自适应)

📅 2026/7/5 13:16:50
数字IC入门学习:Verilog参数化树形极值统计模块(二分比较树架构+有效数据自适应)
数字IC入门学习Verilog参数化树形极值统计模块二分比较树架构有效数据自适应前言本文完整实现一款基于二分比较树Tree Comparator架构的硬件极值统计模块可在极低延迟下同时输出一组数据的最大值与最小值。模块采用全参数化设计支持可变有效数据长度输入输出全寄存器打拍时序收敛友好打平总线输入接口与同系列排序、RLE编解码模块完全统一可无缝嵌入FPGA数据处理流水线喵~相比串行逐一遍历的方案树形并行架构将N个数据的比较延迟从O(N)压缩到O(log₂N)16个数据仅需4级比较即可得到结果非常适合高速流水线中的实时极值统计场景。全文包含完整可综合源码、树形比较原理拆解、逐段代码逻辑详解、方案优缺点分析与全套仿真验证结果。若代码存在逻辑漏洞、写法优化空间欢迎评论区指正交流喵喵喵一、树形比较器硬件实现原理1.1 核心算法思想传统串行极值统计需要逐个遍历数据数据量越大耗时越长延迟随规模线性增长。而二分比较树采用分治思想的全并行分层架构将N个数据两两分组每一层并行比较得出每组的极值逐层向上汇聚最终得到全局最大/最小值。对于16个输入数据仅需4级比较16→8→4→2→1即可输出最终结果延迟固定为log₂(N)级组合逻辑延迟数据量越大并行优势越明显o( ∩ω∩ )m1.2 本模块核心设计亮点全并行树形架构延迟极低4级比较器并行运算16个数据仅需一级组合逻辑输出打拍高速场景下性能远超串行方案双路并行输出两套独立比较树同时工作一次输入同步得到最大值与最小值资源复用率高有效数据自适应掩码通过valid_cnt指定有效数据长度无效位自动填充边界极值最大值路径填0、最小值路径填全1不干扰最终统计结果全参数化通用设计数据位宽DATA_WIDTH、数据深度DATA_DEPTH顶层可配修改参数即可快速适配不同规模场景时序友好的双拍架构输入、输出均做寄存器打拍切断长组合路径极易时序收敛可直接插入高速流水线标准总线接口沿用打平总线输入格式与同系列排序、编解码模块接口完全一致级联零成本喵~1.3 模块整体架构模块整体分为四级逻辑链路输入寄存级对输入数据总线与有效计数打一拍隔离外部组合逻辑改善时序数据拆分与掩码级将打平总线拆分为数组无效数据填充边界极值树形比较级三级generate生成的比较器树逐层汇聚得到全局极值输出寄存级最终极值结果打一拍输出提升输出端时序性能二、完整Verilog代码实现2.1 环境与适配说明开发语言标准Verilog HDL兼容Vivado、Quartus全系列FPGA工具复位方式同步高电平复位与系统时钟同源延迟特性输入到输出共2拍延迟完美适配流水线级联无第三方IP依赖纯逻辑实现可直接综合布线2.2 完整带注释源码话不多说源码奉上喵~/** * module max_min * brief 参数化树形极值统计模块二分比较树架构同时输出最大值与最小值 * author FPGA开发 * description 全并行3级比较树支持可变有效数据长度无效位自动填充边界值 * 输入输出全寄存器打拍时序友好打平总线接口 */ module max_min#( parameter DATA_WIDTH 8, // 单数据位宽默认8bit parameter DATA_DEPTH 16 // 最大数据深度默认16个数据建议为2的整数次幂 ) ( // 全局时钟、同步高电平复位 input clk, input rst, // 输入数据总线打平拼接的多组数据 input [DATA_DEPTH*DATA_WIDTH - 1:0] din_flat, input [4:0] valid_cnt, // 有效数据个数仅前valid_cnt个参与统计 // 极值输出 output reg [DATA_WIDTH - 1:0] max_val, // 统计得到的最大值 output reg [DATA_WIDTH - 1:0] min_val // 统计得到的最小值 ); // 内部寄存器与连线定义 // 输入拆分后的单数据数组 reg [DATA_WIDTH - 1:0] din [0:DATA_DEPTH - 1]; // 输入打拍寄存器时序隔离 reg [DATA_DEPTH*DATA_WIDTH - 1:0] din_flat_r; reg [4:0] valid_cnt_r; // 三级比较树中间连线L1→L2→L3→最终输出 wire [DATA_WIDTH -1:0] L1_min[7:0]; // 第1级比较输出8组最小值 wire [DATA_WIDTH -1:0] L1_max[7:0]; // 第1级比较输出8组最大值 wire [DATA_WIDTH -1:0] L2_min[3:0]; // 第2级比较输出4组最小值 wire [DATA_WIDTH -1:0] L2_max[3:0]; // 第2级比较输出4组最大值 wire [DATA_WIDTH -1:0] L3_min[1:0]; // 第3级比较输出2组最小值 wire [DATA_WIDTH -1:0] L3_max[1:0]; // 第3级比较输出2组最大值 // 有效掩码后的极值数组无效位填充边界值 reg [DATA_WIDTH - 1:0] din_max [0:DATA_DEPTH - 1]; reg [DATA_WIDTH - 1:0] din_min [0:DATA_DEPTH - 1]; // 最终极值组合逻辑输出 wire [DATA_WIDTH - 1:0] max_val_w; wire [DATA_WIDTH - 1:0] min_val_w; integer i; // 组合逻辑循环变量 genvar g; // generate生成块循环变量 // 输入打拍一级寄存器改善输入时序 always (posedge clk) begin if(rst)begin din_flat_r 0; valid_cnt_r 0; end else begin din_flat_r din_flat; valid_cnt_r valid_cnt; end end // 打平总线拆分将一维总线拆分为二维数组 always (*)begin for(i0;iDATA_DEPTH;i i1)begin din[i] din_flat_r[i*DATA_WIDTH:DATA_WIDTH]; end end // 有效数据掩码无效位填充边界极值 // 最大值统计无效位填0最小值永远不会被选为最大值不干扰结果 // 最小值统计无效位填全1最大值永远不会被选为最小值不干扰结果 always (*)begin for(i0;iDATA_DEPTH;i i1)begin if(ivalid_cnt_r)begin din_max[i] din[i]; din_min[i] din[i]; end else begin din_max[i] 8h00; // 无效位填最小数值不影响max统计 din_min[i] 8hFF; // 无效位填最大数值不影响min统计 end end end // 生成块三级二分比较树 generate // 第1级16个数据 → 8组极值 for(g0;g8;gg1)begin:L1 assign L1_max[g] din_max[2*g1] din_max[2*g] ? din_max[2*g1] : din_max[2*g]; assign L1_min[g] din_min[2*g1] din_min[2*g] ? din_min[2*g1] : din_min[2*g]; end // 第2级8组 → 4组极值 for(g0;g4;gg1)begin:L2 assign L2_max[g] L1_max[2*g1] L1_max[2*g] ? L1_max[2*g1] : L1_max[2*g]; assign L2_min[g] L1_min[2*g1] L1_min[2*g] ? L1_min[2*g1] : L1_min[2*g]; end // 第3级4组 → 2组极值 for(g0;g2;gg1)begin:L3 assign L3_max[g] L2_max[2*g1] L2_max[2*g] ? L2_max[2*g1] : L2_max[2*g]; assign L3_min[g] L2_min[2*g1] L2_min[2*g] ? L2_min[2*g1] : L2_min[2*g]; end endgenerate // 最终级2组 → 全局极值 assign max_val_w L3_max[1] L3_max[0] ? L3_max[1] : L3_max[0]; assign min_val_w L3_min[1] L3_min[0] ? L3_min[1] : L3_min[0]; // 输出打拍一级寄存器改善输出时序 always (posedge clk)begin if(rst)begin max_val 0; min_val 0; end else begin max_val max_val_w; min_val min_val_w; end end endmoduleCSDN富文本编辑器代码块语言选择CLike即可实现关键字高亮切换Markdown编辑器使用verilog标签高亮效果更完整喵~三、代码分模块详细解析3.1 参数与端口定义module max_min#( parameter DATA_WIDTH 8, parameter DATA_DEPTH 16 ) ( input clk, input rst, input [DATA_DEPTH*DATA_WIDTH - 1:0] din_flat, input [4:0] valid_cnt, output reg [DATA_WIDTH - 1:0] max_val, output reg [DATA_WIDTH - 1:0] min_val );顶层参数DATA_WIDTH单个数据的位宽默认8bit可按需配置为16、32等位宽DATA_DEPTH单次统计的最大数据个数默认16建议设置为2的整数次幂完美适配二分树架构输入端口clk / rst全局时钟、同步高电平复位din_flat打平拼接的输入数据总线多个数据拼接为单根总线简化顶层连线valid_cnt有效数据个数仅前valid_cnt个数据参与极值统计输出端口max_val统计得到的最大值min_val统计得到的最小值3.2 输入打拍与数据拆分always (posedge clk) begin if(rst)begin din_flat_r 0; valid_cnt_r 0; end else begin din_flat_r din_flat; valid_cnt_r valid_cnt; end end always (*)begin for(i0;iDATA_DEPTH;i i1)begin din[i] din_flat_r[i*DATA_WIDTH:DATA_WIDTH]; end end输入一级寄存器打拍切断外部输入的组合逻辑路径改善模块整体时序性能避免外部毛刺直接进入比较树组合逻辑for循环将一维打平总线拆分为二维数组方便后续按索引访问比较语法简洁清晰3.3 有效数据掩码逻辑always (*)begin for(i0;iDATA_DEPTH;i i1)begin if(ivalid_cnt_r)begin din_max[i] din[i]; din_min[i] din[i]; end else begin din_max[i] 8h00; din_min[i] 8hFF; end end end这是模块支持可变有效数据的核心巧妙设计最大值统计路径无效位置填充全0数值最小在后续比较中永远不会被选为最大值完全不影响最终结果最小值统计路径无效位置填充全1数值最大在后续比较中永远不会被选为最小值完全不影响最终结果两套独立的掩码数组分别服务于max和min两条比较树互不干扰无需额外的边界判断与控制逻辑纯组合逻辑实现简洁高效喵喵喵3.4 Generate生成块构建比较树generate for(g0;g8;gg1)begin:L1 assign L1_max[g] din_max[2*g1] din_max[2*g] ? din_max[2*g1] : din_max[2*g]; assign L1_min[g] din_min[2*g1] din_min[2*g] ? din_min[2*g1] : din_min[2*g]; end // L2、L3层级同理... endgenerate使用Verilog的generate语法通过循环批量生成比较器避免重复手写代码结构规整易维护共三级生成块实现16→8→4→2的逐层汇聚每一层都是完全并行的比较运算max和min两套比较树完全并行同时输出结果不会额外增加延迟如果需要修改数据深度只需调整参数和generate循环次数扩展性极强3.5 最终比较与输出打拍assign max_val_w L3_max[1] L3_max[0] ? L3_max[1] : L3_max[0]; assign min_val_w L3_min[1] L3_min[0] ? L3_min[1] : L3_min[0]; always (posedge clk)begin if(rst)begin max_val 0; min_val 0; end else begin max_val max_val_w; min_val min_val_w; end end最后一级二选一比较得到全局最大、最小值输出一级寄存器打拍避免组合逻辑直接输出提升模块时序性能方便下游模块直接采样整体输入到输出共2个时钟周期延迟非常适合插入流水线中四、模块优缺点分析4.1 核心优点延迟极低性能强劲全并行树形架构组合逻辑延迟仅为log₂(N)级16个数据仅4级比较器延迟远优于串行遍历方案适合高速数据流场景同时输出双极值一套模块同时得到最大值和最小值两套比较树并行工作资源利用率高可变有效数据支持通过边界值填充的巧妙设计原生支持任意长度有效数据无需额外控制逻辑参数化扩展性强位宽、深度均可配置修改参数即可适配不同场景代码复用率高时序收敛友好输入输出双寄存器打拍切断长组合路径极易满足时序约束接口标准易级联打平总线输入格式与同系列模块完全统一可与排序、RLE编解码模块无缝级联o( ∩ω∩ )m4.2 局限与待优化点资源随深度线性增长数据深度越大需要的比较器越多资源占用线性增加超大规模数据场景资源开销较高深度建议为2的幂次二分树架构在数据深度为2的整数次幂时效率最高非2次幂深度需要额外补位处理组合逻辑层级较长大深度下组合逻辑路径较长若需极高工作频率可在L2层级插入一级流水线寄存器拆分级联逻辑五、适用场景与使用注意事项5.1 推荐使用场景高速数据流水线实时统计数据流中的最大最小值如ADC采样数据极值检测排序算法辅助作为排序模块的配套子单元快速获取边界值图像信号处理统计图像行/窗口的像素极值用于自动曝光、对比度调整、形态学滤波传感器数据预处理批量采样数据的极值快速统计数字信号处理窗口极值检测、峰值检测、门限自适应等算法5.2 开发使用注意事项复位为同步高电平复位需与系统时钟同源DATA_DEPTH建议设置为2的整数次幂以完美适配二分比较树架构valid_cnt取值范围为0~DATA_DEPTH超出范围可能导致统计结果异常模块为2拍延迟下游模块采样时需注意时序对齐若需更高工作频率可在L2层级插入一级流水线寄存器拆分级联逻辑当前为无符号数比较设计若需支持有符号数需修改比较逻辑为有符号比较六、仿真验证与测试用例6.1 仿真环境说明仿真工具Vivado 2023.2 行为级功能仿真系统时钟50MHz20ns周期测试架构全自动自校验Testbench内置数据设置任务、自动比对逻辑输出PASS/FAIL统计核心观测信号clk、rst、din_flat、valid_cnt、max_val、min_val覆盖场景基础极值、全同值、单元素、边界位置、满深度、零有效数据、动态有效长度共7组测试用例6.2 详细测试用例与仿真现象Test 15个数据基础极值统计测试目的验证基本的最大值最小值统计功能正确性输入条件5个数据[10,50,30,80,20]valid_cnt5仿真现象输入数据经2拍延迟后max_val和min_val稳定输出验证结果max80min10与预期完全一致基础功能正确Test 2全部相同数据测试目的验证全同值场景下的极值统计正确性输入条件5个数据均为42valid_cnt5仿真现象所有比较层级输出均为42无交换无错误验证结果max42min42全同值场景工作正常Test 3单元素边界测试测试目的验证最小数据量下的统计功能输入条件仅第0位为77valid_cnt1仿真现象无效位填充边界值后比较树逐层汇聚最终输出唯一有效数据验证结果max77min77单元素场景无异常Test 4极值在边界位置测试目的验证最大值在首位、最小值在末位的极端分布场景输入条件数据[99,50,60,70,10]valid_cnt5仿真现象最大值沿比较树逐层向上传递最小值同理最终正确汇聚验证结果max99min10边界位置极值统计准确喵~Test 5满16个数据完整统计测试目的验证最大深度下的完整统计功能覆盖所有比较单元输入条件16个随机数据包含0和127两个边界值valid_cnt16仿真现象16个数据全部参与比较三级比较树全工作最终输出全局极值验证结果max127min0满深度场景功能正确Test 6valid_cnt0 空输入边界测试目的验证有效数据为0的极端边界场景稳定性输入条件valid_cnt0所有数据均为无效仿真现象max路径全填充0min路径全填充255比较树正常工作验证结果max0min255空输入场景模块稳定无异常Test 7动态改变有效数据长度测试目的验证valid_cnt动态变化时的统计正确性输入条件同一组数据先设valid_cnt6再缩小为2仿真现象valid_cnt变化后掩码逻辑自动更新有效范围输出对应极值验证结果6个数据时max100、min502个数据时max100、min90动态调整功能正常6.3 仿真代码话不多说仿真源码奉上喵~timescale 1ns / 1ps ////////////////////////////////////////////////////////////////////////////////// // Testbench: max_min ////////////////////////////////////////////////////////////////////////////////// module tb_max_min(); // Parameters localparam DATA_WIDTH 8; localparam DATA_DEPTH 16; // Signals reg clk; reg rst; reg [DATA_DEPTH*DATA_WIDTH-1:0] din_flat; reg [4:0] valid_cnt; wire [DATA_WIDTH-1:0] max_val; wire [DATA_WIDTH-1:0] min_val; // DUT max_min #( .DATA_WIDTH(DATA_WIDTH), .DATA_DEPTH(DATA_DEPTH) ) dut ( .clk(clk), .rst(rst), .din_flat(din_flat), .valid_cnt(valid_cnt), .max_val(max_val), .min_val(min_val) ); // Clock always #10 clk ~clk; // 50MHz // Shared test data reg [7:0] tv [0:DATA_DEPTH-1]; // single shared array for all tests integer k; // Helper task task set_input; input [4:0] cnt; begin valid_cnt cnt; din_flat 0; for (k 0; k DATA_DEPTH; k k 1) din_flat[k*8 : 8] tv[k]; end endtask // Test sequence initial begin clk 0; rst 1; valid_cnt 0; din_flat 0; // release reset #25 rst 0; #20; // ---- Test 1: 5 values, normal range ---- begin : test1 tv[0]10; tv[1]50; tv[2]30; tv[3]80; tv[4]20; tv[5]0; tv[6]0; tv[7]0; tv[8]0; tv[9]0; tv[10]0; tv[11]0; tv[12]0; tv[13]0; tv[14]0; tv[15]0; set_input(5); #40; if (max_val 80 min_val 10) $display([PASS] Test1: 5 values max%0d min%0d, max_val, min_val); else $display([FAIL] Test1: expected max80 min10, got max%0d min%0d, max_val, min_val); end // ---- Test 2: all identical ---- begin : test2 tv[0]42; tv[1]42; tv[2]42; tv[3]42; tv[4]42; tv[5]0; tv[6]0; tv[7]0; tv[8]0; tv[9]0; tv[10]0; tv[11]0; tv[12]0; tv[13]0; tv[14]0; tv[15]0; set_input(5); #40; if (max_val 42 min_val 42) $display([PASS] Test2: all identical max%0d min%0d, max_val, min_val); else $display([FAIL] Test2: expected max42 min42, got max%0d min%0d, max_val, min_val); end // ---- Test 3: single value ---- begin : test3 tv[0]77; tv[1]0; tv[2]0; tv[3]0; tv[4]0; tv[5]0; tv[6]0; tv[7]0; tv[8]0; tv[9]0; tv[10]0; tv[11]0; tv[12]0; tv[13]0; tv[14]0; tv[15]0; set_input(1); #40; if (max_val 77 min_val 77) $display([PASS] Test3: single value max%0d min%0d, max_val, min_val); else $display([FAIL] Test3: expected max77 min77, got max%0d min%0d, max_val, min_val); end // ---- Test 4: max at head, min at tail ---- begin : test4 tv[0]99; tv[1]50; tv[2]60; tv[3]70; tv[4]10; tv[5]0; tv[6]0; tv[7]0; tv[8]0; tv[9]0; tv[10]0; tv[11]0; tv[12]0; tv[13]0; tv[14]0; tv[15]0; set_input(5); #40; if (max_val 99 min_val 10) $display([PASS] Test4: max-head/min-tail max%0d min%0d, max_val, min_val); else $display([FAIL] Test4: expected max99 min10, got max%0d min%0d, max_val, min_val); end // ---- Test 5: full 16 values ---- begin : test5 tv[0]5; tv[1]15; tv[2]127; tv[3]0; tv[4]100; tv[5]66; tv[6]33; tv[7]99; tv[8]1; tv[9]88; tv[10]44; tv[11]77; tv[12]22; tv[13]55; tv[14]11; tv[15]120; set_input(16); #40; if (max_val 127 min_val 0) $display([PASS] Test5: full 16 values max%0d min%0d, max_val, min_val); else $display([FAIL] Test5: expected max127 min0, got max%0d min%0d, max_val, min_val); end // ---- Test 6: valid_cnt0 boundary ---- begin : test6 tv[0]55; tv[1]66; tv[2]77; tv[3]88; tv[4]99; tv[5]0; tv[6]0; tv[7]0; tv[8]0; tv[9]0; tv[10]0; tv[11]0; tv[12]0; tv[13]0; tv[14]0; tv[15]0; set_input(0); #40; // valid_cnt0: all slots padded to 0 (max) and 0xFF (min) if (max_val 0 min_val 255) $display([PASS] Test6: valid_cnt0 max%0d min%0d, max_val, min_val); else $display([FAIL] Test6: expected max0 min255, got max%0d min%0d, max_val, min_val); end // ---- Test 7: dynamically change valid_cnt ---- begin : test7 tv[0]100; tv[1]90; tv[2]80; tv[3]70; tv[4]60; tv[5]50; tv[6]0; tv[7]0; tv[8]0; tv[9]0; tv[10]0; tv[11]0; tv[12]0; tv[13]0; tv[14]0; tv[15]0; // first: valid_cnt6 set_input(6); #40; if (max_val 100 min_val 50) $display([PASS] Test7a: valid_cnt6 max%0d min%0d, max_val, min_val); else $display([FAIL] Test7a: expected max100 min50, got max%0d min%0d, max_val, min_val); // reduce: valid_cnt2 set_input(2); #40; if (max_val 100 min_val 90) $display([PASS] Test7b: valid_cnt reduced to 2 max%0d min%0d, max_val, min_val); else $display([FAIL] Test7b: expected max100 min90, got max%0d min%0d, max_val, min_val); end $display( Test Complete ); $finish; end endmodule6.4 仿真结论7组测试用例完整覆盖了基础功能、边界场景、动态调整三大类情况模块所有逻辑分支均得到验证。树形比较器工作稳定有效数据掩码逻辑正确动态调整响应及时全场景下统计结果均准确无误。模块功能完整、时序稳定可直接用于工程集成喵喵喵总结本文基于二分比较树架构实现了一款参数化的硬件极值统计模块以极低的延迟同时输出最大值与最小值通过巧妙的边界值填充设计原生支持可变有效数据长度输入输出双寄存器打拍保证了优秀的时序性能。作为FPGA数据处理链路中的常用子模块树形极值统计器在性能和资源之间取得了很好的平衡是高速数据流处理的经典基础单元非常适合作为数字IC入门学习并行硬件设计的案例。如果本文对你有帮助欢迎点赞、收藏、关注代码存在逻辑缺陷、优化思路或疑问欢迎在评论区提出指正我会及时回复与修正喵~