一、什么是可调延迟线?
在FPGA/ASIC设计中,我们经常遇到这样一个需求:把一路数据延迟N个时钟周期后再输出,且N可以动态调整。这就是“可调延迟线”(Variable Delay Line)模块。
本文的 user_delay 模块,是一个经典设计:输入一路数据 i_data,通过控制信号 i_delay_cnt,实现 0 到 DELAY-1 个时钟周期任意可调 的延迟输出。
二、代码原理
架构:移位寄存器 + 输出选择器
模块的精髓在于两部分逻辑的配合:
① 数据通道——移位链(时序逻辑)
ram[0] <= i_data;
for (i = 1; i < DELAY; i = i+1)
ram[i] <= ram[i-1];
数据每个时钟周期向前“流动”一级,形成一条深度为4的移位寄存器链。可以想象成一条流水线,数据从 ram[0] 一路传递到 ram[3]。
② 输出通道——多路选择器(组合逻辑)
if (i_delay_cnt == 0)
o_delay_data = i_data; // 0延迟直通
else
o_delay_data = ram[i_delay_cnt - 1];
i_delay_cnt 决定从流水线的哪一级“抽头”取数:
i_delay_cnt = 0:直接取输入,零延迟;
i_delay_cnt = 1:取 ram[0],延迟1拍;
i_delay_cnt = 4:取 ram[3],延迟3拍(最大延迟)。
一个值得注意的细节:位宽用 $clog2(DELAY+1) 计算,因为要表示0~4共5个状态,需要3位而不是2位。
三、仿真验证
测试平台 tb_user_delay 设计得相当系统,共6组测试:
| 测试项 | i_delay_cnt | 预期延迟 |
|---|---|---|
| 测试1 | 0 | 0拍(直通) |
| 测试2 | 1 | 1拍 |
| 测试3 | 2 | 2拍 |
| 测试4 | 3 | 3拍 |
| 测试5 | 4 | 3拍(超出范围,取最大) |
| 测试6 | 动态切换 | 验证运行时调整 |
每组测试前都插入复位,保证从干净状态开始;测试数据以 +0x10 递增(10、20、30、40...),便于肉眼追踪数据流向。仿真中还通过层次化引用 uut.ram[i] 实时打印内部移位链状态,配合 $dumpvars 生成VCD波形,调试一目了然。
测试6尤其关键:数据A0、B0在延迟2配置下流动,随后切换到延迟4,观察C0~F0在切换瞬间的输出行为——动态切换是否引入毛刺、输出是否有过渡态,正是实际工程中最需要关注的点。
四、应用场景
视频处理流水线:行缓存、帧同步场景中,不同分辨率/制式需要的延迟级数不同,运行时可配。
数字滤波器:FIR滤波器的抽头延迟线本质就是移位寄存器,本模块是其参数化版本。
通信协议握手:AXI-Stream等流水线协议中,用于跨模块的时序对齐和反压路径匹配。
测量与标定:时间数字转换(TDC)、信号相关性分析中需要精确可调的延迟基准。
四、如何获取?
关注公众号 我是小灰灰的FPGA,关注我,带你从入门到精通数字逻辑设计。
后台回复(建议复制粘贴):user_delay
198