文档说明 :本文档是 Step 2------CNN 流式卷积加速器的子模块级详细设计报告。Step 1 建立了"分块 + 迭代"的数学框架并论证了算子层可行性;Step 2 在此基础上将理论落地为硬件微架构,聚焦于单个子模块
Conv_Kernel.V的完整 RTL 设计。全文覆盖 Step1 核心回顾、模块接口与数据格式、五级流水线详细设计、BRAM 与 FIFO 资源计算以及多层次验证策略。所有设计同时覆盖 C S I Z E = 3 C_{SIZE}=3 CSIZE=3(9 子模块)和 C S I Z E = 5 C_{SIZE}=5 CSIZE=5(25 子模块)两种配置,推荐优先实现 C S I Z E = 3 C_{SIZE}=3 CSIZE=3。
目录
1. 设计背景与 Step1 核心回顾
1.1 从算法到硬件:为什么需要"分块"
二维卷积是 CNN 推理的算力瓶颈。一个 C S I Z E × C S I Z E C_{SIZE} \times C_{SIZE} CSIZE×CSIZE 的卷积核对一帧 F R A M E _ S I Z E × F R A M E _ S I Z E FRAME\_SIZE \times FRAME\_SIZE FRAME_SIZE×FRAME_SIZE 的图像执行 same 卷积,需要完成 F R A M E _ S I Z E 2 × C S I Z E 2 FRAME\SIZE^2 \times C{SIZE}^2 FRAME_SIZE2×CSIZE2 次乘法累加。在 FPGA 上实现高性能流式卷积,面临三项挑战:
- 吞吐量:目标达到每时钟周期处理一个(一批)输入像素,即流水线不阻塞、不丢拍
- 存储带宽:每个输出位置的部分和需要反复读写,BRAM 端口数受限,直接映射会导致频繁的读写冲突
- 并行度 : C S I Z E 2 C_{SIZE}^2 CSIZE2 次乘累加无法在单周期内串行完成,必须并行展开
Step1 报告建立的分块+迭代同时解决了这三项挑战:
- 分块 :将输出空间按行列坐标对 C S I Z E C_{SIZE} CSIZE 取模的余数划分为 C S I Z E 2 C_{SIZE}^2 CSIZE2 个互不重叠的"责任田",每块交由一个专属子模块 K ( i , j ) K(i, j) K(i,j) 独立管辖。 3 × 3 3\times 3 3×3 卷积分 9 块, 5 × 5 5\times 5 5×5 卷积分 25 块,块间零依赖、零通信。
- 迭代:每个子模块面对逐个到达的像素,在每个时钟周期内就地完成多项判定------激活条件、权重选择、输出归属、更新判定。部分和随像素流动逐次累积,最后一个依赖像素到达的同一周期即产出结果,零缓冲、零等待。
Step2 聚焦 C S I Z E 2 C_{SIZE}^2 CSIZE2 个子模块中任意一个 的微观硬件实现------即单个 Conv_Kernel.V 的内部结构。下文所有流水级、BRAM 规格、验证方案均以"一个子模块"为讨论单位。
1.2 配置参数
| 参数 | 符号 | 可选值 | 默认值 | 说明 |
|---|---|---|---|---|
| 卷积核尺寸 | C S I Z E C_{SIZE} CSIZE | 3 或 5 | 3 | 卷积核行列数,决定分块数与感受野大小 |
| 行偏移 | i i i | [ 0 , C S I Z E − 1 ] [0, C_{SIZE}-1] [0,CSIZE−1] | 例化决定 | 子模块在分块网格中的行索引 |
| 列偏移 | j j j | [ 0 , C S I Z E − 1 ] [0, C_{SIZE}-1] [0,CSIZE−1] | 例化决定 | 子模块在分块网格中的列索引 |
| 帧尺寸 | F R A M E _ S I Z E FRAME\_SIZE FRAME_SIZE | 任意正整数 | 128 | 输入/输出图像的行列像素数 |
| Padding 宽度 | P P P | 1 或 2 | ( C S I Z E − 1 ) / 2 (C_{SIZE}-1)/2 (CSIZE−1)/2 | same 模式零填充半宽 |
1.3 七项核心公式
以下七项公式是 Step1 推导的闭式判定公式,构成流水线各级组合逻辑的数学基础。为便于交叉引用,每条公式标注了其在流水线中的对应级。
公式一:激活条件与输出坐标
给定输入像素坐标 ( x i n , y i n ) (x_{in}, y_{in}) (xin,yin) 和子模块索引 ( i , j ) (i, j) (i,j),该像素在该子模块中所贡献的输出坐标 ( x o u t , y o u t ) (x_{out}, y_{out}) (xout,yout) 为:
x o u t = x i n − P + ( ( i − x i n + P ) m o d C S I Z E ) y o u t = y i n − P + ( ( j − y i n + P ) m o d C S I Z E ) \boxed{ \begin{aligned} x_{out} &= x_{in} - P + \big((i - x_{in} + P) \bmod C_{SIZE}\big) \\[4pt] y_{out} &= y_{in} - P + \big((j - y_{in} + P) \bmod C_{SIZE}\big) \end{aligned} } xoutyout=xin−P+((i−xin+P)modCSIZE)=yin−P+((j−yin+P)modCSIZE)
当且仅当该坐标落在图像有效范围内时,当前像素对子模块 K ( i , j ) K(i,j) K(i,j) 有效:
activated = ( 0 ≤ x o u t < F R A M E _ S I Z E ) ∧ ( 0 ≤ y o u t < F R A M E _ S I Z E ) \boxed{ \text{activated} \;=\; \big(0 \leq x_{out} < FRAME\SIZE\big) \;\land\; \big(0 \leq y{out} < FRAME\_SIZE\big) } activated=(0≤xout<FRAME_SIZE)∧(0≤yout<FRAME_SIZE)
直觉 :以 ( x i n , y i n ) (x_{in}, y_{in}) (xin,yin) 为中心的 C S I Z E × C S I Z E C_{SIZE} \times C_{SIZE} CSIZE×CSIZE 感受野中,恰有一个坐标满足"行模 C S I Z E C_{SIZE} CSIZE 余 i i i、列模 C S I Z E C_{SIZE} CSIZE 余 j j j"------这就是 ( x o u t , y o u t ) (x_{out}, y_{out}) (xout,yout)。→ Stage 1
公式二:起始像素判定(is_first)
输出位置 O ( x o u t , y o u t ) O(x_{out}, y_{out}) O(xout,yout) 在光栅扫描顺序下遇到的第一个依赖像素位于其感受野的左上角:
first_x = max ( 0 , x o u t − P ) first_y = max ( 0 , y o u t − P ) is_first = ( x i n = first_x ) ∧ ( y i n = first_y ) \boxed{ \begin{aligned} \text{first\x} &= \max(0,\; x{out} - P) \\ \text{first\y} &= \max(0,\; y{out} - P) \\[4pt] \text{is\first} &= (x{in} = \text{first\x}) \;\land\; (y{in} = \text{first\_y}) \end{aligned} } first_xfirst_yis_first=max(0,xout−P)=max(0,yout−P)=(xin=first_x)∧(yin=first_y)
is_first = 1 时,该像素是此输出位置的首个贡献者 ,BRAM 中的历史值(无论来自上一帧残留还是上电随机值)应当被忽略,部分和从零开始。→ Stage 2
公式三:权重索引
感受野中 ( x i n , y i n ) (x_{in}, y_{in}) (xin,yin) 相对于 ( x o u t , y o u t ) (x_{out}, y_{out}) (xout,yout) 的偏移,直接映射为参数矩阵的行列索引:
k = x i n − x o u t + P , m = y i n − y o u t + P \boxed{ k = x_{in} - x_{out} + P,\qquad m = y_{in} - y_{out} + P } k=xin−xout+P,m=yin−yout+P
等价形式(直接由取模推导,节省减法器):
k = C S I Z E − 1 − ( ( i − x i n + P ) m o d C S I Z E ) , m = C S I Z E − 1 − ( ( j − y i n + P ) m o d C S I Z E ) k = C_{SIZE} - 1 - \big((i - x_{in} + P) \bmod C_{SIZE}\big),\qquad m = C_{SIZE} - 1 - \big((j - y_{in} + P) \bmod C_{SIZE}\big) k=CSIZE−1−((i−xin+P)modCSIZE),m=CSIZE−1−((j−yin+P)modCSIZE)
其中 k , m ∈ [ 0 , C S I Z E − 1 ] k, m \in [0, C_{SIZE}-1] k,m∈[0,CSIZE−1] 始终唯一合法。→ Stage 2
公式四:输出归属
( x o u t , y o u t ) (x_{out}, y_{out}) (xout,yout) 天然满足同余约束,全局有且仅有一个专属子模块:
x o u t ≡ i ( m o d C S I Z E ) , y o u t ≡ j ( m o d C S I Z E ) \boxed{ x_{out} \equiv i \pmod{C_{SIZE}},\qquad y_{out} \equiv j \pmod{C_{SIZE}} } xout≡i(modCSIZE),yout≡j(modCSIZE)
→ Stage 1 / Stage 5
公式五:BRAM 地址映射
子模块 K ( i , j ) K(i,j) K(i,j) 管辖的输出坐标为 { x ∣ x ≡ i ( m o d C S I Z E ) , x < F R A M E _ S I Z E } \{x \mid x \equiv i \pmod{C_{SIZE}},\; x < FRAME\SIZE\} {x∣x≡i(modCSIZE),x<FRAME_SIZE} 与 { y ∣ y ≡ j ( m o d C S I Z E ) , y < F R A M E _ S I Z E } \{y \mid y \equiv j \pmod{C{SIZE}},\; y < FRAME\_SIZE\} {y∣y≡j(modCSIZE),y<FRAME_SIZE} 的笛卡尔积,共 N x × N y N_x \times N_y Nx×Ny 个,其中:
N x = ⌈ F R A M E _ S I Z E − i C S I Z E ⌉ , N y = ⌈ F R A M E _ S I Z E − j C S I Z E ⌉ N_x = \left\lceil \frac{FRAME\SIZE - i}{C{SIZE}} \right\rceil,\qquad N_y = \left\lceil \frac{FRAME\SIZE - j}{C{SIZE}} \right\rceil Nx=⌈CSIZEFRAME_SIZE−i⌉,Ny=⌈CSIZEFRAME_SIZE−j⌉
一维 BRAM 线性地址由二维输出坐标按行优先映射:
bram_addr = x o u t − i C S I Z E × N y + y o u t − j C S I Z E \boxed{ \text{bram\addr} = \frac{x{out} - i}{C_{SIZE}} \times N_y + \frac{y_{out} - j}{C_{SIZE}} } bram_addr=CSIZExout−i×Ny+CSIZEyout−j
由公式四可证 x o u t − i x_{out} - i xout−i 和 y o u t − j y_{out} - j yout−j 均为 C S I Z E C_{SIZE} CSIZE 的整数倍,因此除法为精确整数除法,可以考虑用LUT来代替除法。→ Stage 2
公式六:末位像素判定(is_last)
x l a s t = min ( x o u t + P , F R A M E _ S I Z E − 1 ) , y l a s t = min ( y o u t + P , F R A M E _ S I Z E − 1 ) \boxed{ x_{last} = \min(x_{out} + P,\; FRAME\SIZE - 1),\qquad y{last} = \min(y_{out} + P,\; FRAME\_SIZE - 1) } xlast=min(xout+P,FRAME_SIZE−1),ylast=min(yout+P,FRAME_SIZE−1)
is_last = ( x i n = x l a s t ) ∧ ( y i n = y l a s t ) \boxed{ \text{is\last} \;=\; (x{in} = x_{last}) \;\land\; (y_{in} = y_{last}) } is_last=(xin=xlast)∧(yin=ylast)
is_last = 1 时,输出位置的全部 C S I Z E 2 C_{SIZE}^2 CSIZE2 次乘累加完成,应产出结果并清零 BRAM 对应地址。→ Stage 5
公式七:卷积乘累加
P s u m ( i , j ) [ x o u t , y o u t ] + = P a r a ( k , m ) ⋅ I ( x i n , y i n ) \boxed{ P_{sum}^{(i,j)}[x_{out}, y_{out}] \;\mathrel{+}=\; Para(k, m) \cdot I(x_{in}, y_{in}) } Psum(i,j)[xout,yout]+=Para(k,m)⋅I(xin,yin)
当 is_first = 1 时,右侧初始部分和取 0。→ Stage 4
位宽注意:Step1 的 Python 仿真使用 64 位浮点无精度问题。FPGA 定点实现中乘法会扩展位宽、累加会逐次增长,必须在设计初期精确计算------详见第 2 章的数据格式分析和第 3 章的 DSP48E2 位宽规划。
2. 模块接口与数据格式
2.1 接口分类总览
Conv_Kernel.V 的对外接口分为五大类:
| 类别 | 说明 |
|---|---|
| 全局信号 | 时钟与复位,所有时序逻辑的节拍来源 |
| AXIS Slave 输入接口 | 标准 AXI4-Stream 从接口,接收像素数据、坐标与有效标志,并反压上游 |
| AXIS Master 输出接口 | 标准 AXI4-Stream 主接口,发送卷积结果、输出坐标与有效标志,响应下游反压 |
| 控制与标志接口 | 模块使能、同步复位,FIFO 状态衍生反压 |
| 卷积参数输入 | 静态参数矩阵 P a r a Para Para,外部并行接入,内部按索引 MUX 选取 |
2.2 数据格式
2.2.1 定点化 Q 格式
Q 格式将 N N N 位有符号二进制数解释为 [ − 1 , + 1 ) [-1, +1) [−1,+1) 之间的小数------1 位符号位、 N − 1 N-1 N−1 位小数位。在硬件中它就是普通的 signed wire/reg,区别仅在于数值解释方式。
| 格式 | 总位宽 | 符号位 | 小数位 | 数值范围 | LSB |
|---|---|---|---|---|---|
| Q8 | 8 | 1 | 7 | [ − 1.0 , 0.9921875 ] [-1.0,\; 0.9921875] [−1.0,0.9921875] | 2 − 7 2^{-7} 2−7 |
| Q10 | 10 | 1 | 9 | [ − 1.0 , 0.998046875 ] [-1.0,\; 0.998046875] [−1.0,0.998046875] | 2 − 9 2^{-9} 2−9 |
| Q12 | 12 | 1 | 11 | [ − 1.0 , 0.99951171875 ] [-1.0,\; 0.99951171875] [−1.0,0.99951171875] | 2 − 11 2^{-11} 2−11 |
| Q16 | 16 | 1 | 15 | [ − 1.0 , 0.999969482421875 ] [-1.0,\; 0.999969482421875] [−1.0,0.999969482421875] | 2 − 15 2^{-15} 2−15 |
Q 格式是非对称的:能表示 − 1.0 -1.0 −1.0,但最大正值为 1 − 2 − ( N − 1 ) 1 - 2^{-(N-1)} 1−2−(N−1)。
2.2.2 整型格式 UINT / INT
| 格式 | 总位宽 | 符号 | 数值范围 | LSB |
|---|---|---|---|---|
| UINT8 | 8 | 无符号 | [ 0 , 255 ] [0, 255] [0,255] | 1 |
| UINT12 | 12 | 无符号 | [ 0 , 4095 ] [0, 4095] [0,4095] | 1 |
| INT8 | 8 | 有符号 | [ − 128 , 127 ] [-128, 127] [−128,127] | 1 |
| INT12 | 12 | 有符号 | [ − 2048 , 2047 ] [-2048, 2047] [−2048,2047] | 1 |
| INT16 | 16 | 有符号 | [ − 32768 , 32767 ] [-32768, 32767] [−32768,32767] | 1 |
整型的 LSB 为 1 1 1。
2.2.3 混合运算的 LSB 传递与位宽推导
当输入格式与参数格式不同时,乘法积的 LSB 为两者 LSB 之积,位宽为两者位宽之和:
LSB product = LSB in × LSB para W product = W in + W para \boxed{\text{LSB}{\text{product}} = \text{LSB}{\text{in}} \times \text{LSB}{\text{para}}} \qquad \boxed{W{\text{product}} = W_{\text{in}} + W_{\text{para}}} LSBproduct=LSBin×LSBparaWproduct=Win+Wpara
全精度部分和经 C S I Z E 2 C_{SIZE}^2 CSIZE2 次累加后位宽再增长 ⌈ log 2 ( C S I Z E 2 ) ⌉ \lceil \log_2(C_{SIZE}^2) \rceil ⌈log2(CSIZE2)⌉ 位:
F U L L _ W I D T H = W in + W para + ⌈ log 2 ( C S I Z E 2 ) ⌉ \boxed{FULL\WIDTH = W{\text{in}} + W_{\text{para}} + \lceil\log_2(C_{SIZE}^2)\rceil} FULL_WIDTH=Win+Wpara+⌈log2(CSIZE2)⌉
典型组合的 LSB 传递:
| 输入格式 | 参数格式 | LSB in \text{LSB}_{\text{in}} LSBin | LSB para \text{LSB}_{\text{para}} LSBpara | LSB product \text{LSB}_{\text{product}} LSBproduct | W product W_{\text{product}} Wproduct |
|---|---|---|---|---|---|
| UINT8 | Q8 | 1 1 1 | 2 − 7 2^{-7} 2−7 | 2 − 7 2^{-7} 2−7 | 16 |
| UINT8 | INT8 | 1 1 1 | 1 1 1 | 1 1 1 | 16 |
| Q8 | Q8 | 2 − 7 2^{-7} 2−7 | 2 − 7 2^{-7} 2−7 | 2 − 14 2^{-14} 2−14 | 16 |
| UINT12 | Q12 | 1 1 1 | 2 − 11 2^{-11} 2−11 | 2 − 11 2^{-11} 2−11 | 24 |
| INT12 | INT16 | 1 1 1 | 1 1 1 | 1 1 1 | 28 |
| UINT12 | Q16 | 1 1 1 | 2 − 15 2^{-15} 2−15 | 2 − 15 2^{-15} 2−15 | 28 |
2.2.4 溢出容限
以最坏情况 C S I Z E = 5 C_{SIZE}=5 CSIZE=5(25 次累加)、UINT12 输入(最大值 4095)、Q16 参数(绝对值最大 ~1.0)为例,全精度累加峰值约为 25 × 4095 × 32768 ≈ 2 31.6 25 \times 4095 \times 32768 \approx 2^{31.6} 25×4095×32768≈231.6。DSP48E2 累加器宽 48 位(上限 2 47 − 1 2^{47}-1 247−1),裕量超过 15 位,在 F U L L _ W I D T H ≤ 48 FULL\_WIDTH \leq 48 FULL_WIDTH≤48 的条件下无需任何中间饱和处理。
2.3 接口信号总表
2.3.1 全局信号
| 信号名 | 方向 | 位宽 | 说明 |
|---|---|---|---|
clk |
input | 1 | 系统时钟 |
rst_n |
input | 1 | 异步硬复位,低有效 |
2.3.2 AXIS Slave 输入接口
| 信号名 | 方向 | 位宽 | 说明 |
|---|---|---|---|
s_axis_tdata |
input | IN_DATA_WIDTH |
输入像素数据 |
s_axis_tuser_x |
input | ⌈ log 2 ( F R A M E _ S I Z E ) ⌉ \lceil\log_2(FRAME\_SIZE)\rceil ⌈log2(FRAME_SIZE)⌉ | 输入行坐标 x i n x_{in} xin |
s_axis_tuser_y |
input | ⌈ log 2 ( F R A M E _ S I Z E ) ⌉ \lceil\log_2(FRAME\_SIZE)\rceil ⌈log2(FRAME_SIZE)⌉ | 输入列坐标 y i n y_{in} yin |
s_axis_tvalid |
input | 1 | 输入有效标志 |
s_axis_tready |
output | 1 | 模块就绪,由 !fifo_almost_full && enable 组合驱动。拉低时阻断上游新数据,零数据丢失 |
s_axis_tlast |
input | 1 | 帧结束标志(可选) |
2.3.3 AXIS Master 输出接口
| 信号名 | 方向 | 位宽 | 说明 |
|---|---|---|---|
m_axis_tdata |
output | OUT_DATA_WIDTH |
输出卷积结果 |
m_axis_tuser_x |
output | ⌈ log 2 ( F R A M E _ S I Z E ) ⌉ \lceil\log_2(FRAME\_SIZE)\rceil ⌈log2(FRAME_SIZE)⌉ | 输出行坐标 x o u t x_{out} xout |
m_axis_tuser_y |
output | ⌈ log 2 ( F R A M E _ S I Z E ) ⌉ \lceil\log_2(FRAME\_SIZE)\rceil ⌈log2(FRAME_SIZE)⌉ | 输出列坐标 y o u t y_{out} yout |
m_axis_tvalid |
output | 1 | 输出有效标志 |
m_axis_tready |
input | 1 | 后级就绪,直接驱动输出 FIFO 读使能 |
m_axis_tlast |
output | 1 | 帧末标志 |
2.3.4 控制与标志接口
| 信号名 | 方向 | 位宽 | 说明 |
|---|---|---|---|
reset |
input | 1 | 同步模块复位,高有效。清空流水线寄存器和输出 FIFO |
enable |
input | 1 | 模块使能,拉低时暂停流水线推进 |
反压设计 :模块不对外暴露粘滞错误标志。溢出保护完全由 AXI-Stream 反压协议保证------
s_axis_tready由 FIFO 剩余深度组合逻辑驱动(剩余深度 ≤ 10 时拉低),上级停止发送。五级流水线在途最多 5 个结果,10 个安全阈值是 2× 裕量,确保 FIFO 永不溢出。
2.3.5 卷积参数输入
| 信号名 | 方向 | 位宽 | 说明 |
|---|---|---|---|
para[k][m] |
input | PARA_WIDTH |
参数矩阵 P a r a [ C S I Z E ] [ C S I Z E ] Para[C_{SIZE}][C_{SIZE}] Para[CSIZE][CSIZE]。帧内稳定不变,帧间可动态切换 |
2.4 模块例化配置参数
| 参数名 | 范围 | 默认值 | 说明 |
|---|---|---|---|
C_SIZE |
3 或 5 | 3 | 卷积核尺寸 |
X_INDEX_OFFSET |
[ 0 , C S I Z E − 1 ] [0, C_{SIZE}-1] [0,CSIZE−1] | 例化决定 | 子模块行索引 i i i |
Y_INDEX_OFFSET |
[ 0 , C S I Z E − 1 ] [0, C_{SIZE}-1] [0,CSIZE−1] | 例化决定 | 子模块列索引 j j j |
FRAME_SIZE |
正整数 | 128 | 帧尺寸 |
IN_DATA_WIDTH |
8/10/12 | 8 | 输入数据位宽 |
IN_FORMAT |
"Q"/"UINT"/"INT" |
"UINT" |
输入格式 |
PARA_WIDTH |
8/12/16 | 8 | 参数位宽 |
PARA_FORMAT |
"Q"/"INT" |
"INT" |
参数格式 |
OUT_DATA_WIDTH |
8/10/12 | 8 | 输出位宽 |
OUT_FORMAT |
"Q"/"UINT"/"INT" |
"UINT" |
输出格式 |
FIFO_DEPTH |
正整数 | 32 | 输出 FIFO 深度 |
P |
由 C S I Z E C_{SIZE} CSIZE 推导 | ( C S I Z E − 1 ) / 2 (C_{SIZE}-1)/2 (CSIZE−1)/2 | Padding 半宽 |
这些参数共同决定流水线寄存器位宽、BRAM 地址线宽度、DSP48E2 输入映射及格式转换逻辑。
3. 五级流水线详细设计
3.1 流水线结构总览
需求仅要求"至少三级",实际设计采用五级,每级承载明确且时序可收敛的逻辑任务。流水级之间由显式寄存器隔开,级内运算为纯组合逻辑,单周期完成。
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ Stage 1 │ │ Stage 2 │ │ Stage 3 │ │ Stage 4 │ │ Stage 5 │
│ 判定阶段 │───▶│索引生成阶段│───▶│数据获取阶段│───▶│ 乘累加阶段 │───▶│回写与输出 │
│ │ │ │ │ │ │ │ │ │
│•公式一 │ │•公式二/三 │ │•BRAM A口 │ │•公式七 │ │•公式六 │
│ x_out, │ │ is_first │ │ 组合读 │ │ DSP乘累加│ │ is_last │
│ y_out │ │•公式三 │ │•参数MUX │ │•计算缓存 │ │•BRAM B口 │
│•activated│ │ k,m索引 │ │ 取Para │ │ 旁路 │ │ 回写 │
│ │ │•公式五 │ │ │ │ │ │•格式转换 │
│ │ │ BRAM地址 │ │ │ │ │ │•FIFO写入 │
└──────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘
1 clk 1 clk 1 clk 1 clk 1 clk
BRAM 端口分配 :A 口专用于 Stage 3 组合读(DOA_REG=0,地址组合驱动,读数据同周期组合返回并打拍至 S3→S4 寄存器);B 口专用于 Stage 5 时序写(地址/数据/使能在时钟上升沿锁存并落盘)。读写在流水线上天然错开 Stage 3 与 Stage 5,消除了同周期 A/B 口地址冲突。
拆分动机:将索引生成(地址 + 参数索引 + is_first)与数据获取(BRAM 读 + MUX)分离为独立的 Stage 2 和 Stage 3,使各级时序路径更均衡。BRAM 读数据在 Stage 3 组合读出并打拍,Stage 4 仅接收干净寄存器数据,关键路径不含 BRAM 延迟。
3.2 Stage 1:判定阶段
功能描述
Stage 1 是流水线入口级,消费 AXIS Slave 总线上的像素数据,完成两项核心任务:
- 输出坐标计算 :按公式一,为每个输入像素 ( x i n , y i n ) (x_{in}, y_{in}) (xin,yin) 计算其在子模块 K ( i , j ) K(i,j) K(i,j) 中所贡献的输出坐标 ( x o u t , y o u t ) (x_{out}, y_{out}) (xout,yout)
- 激活判定 :判断 ( x o u t , y o u t ) (x_{out}, y_{out}) (xout,yout) 是否在有效图像范围内
非激活像素(如 Padding 区域产生越界坐标)在 Stage 1 即被丢弃,不消耗后续流水级资源。
公式一实现
取模辅助量(供公式三等价形式复用):
R x = ( i − x i n + P ) m o d C S I Z E R y = ( j − y i n + P ) m o d C S I Z E \begin{aligned} R_x &= (i - x_{in} + P) \bmod C_{SIZE} \\ R_y &= (j - y_{in} + P) \bmod C_{SIZE} \end{aligned} RxRy=(i−xin+P)modCSIZE=(j−yin+P)modCSIZE
实现过程中,取模操作可以简化为LUT, R x = L U T ( x i n ) R_x = LUT(x_{in}) Rx=LUT(xin), R y = L U T ( y i n ) R_y = LUT(y_{in}) Ry=LUT(yin)。也就是说每一个例化的卷积模块因为 i , j i,j i,j不相同所以LUT里的数据也会不相同。
输出坐标:
x o u t = x i n − P + R x y o u t = y i n − P + R y \begin{aligned} x_{out} &= x_{in} - P + R_x \\ y_{out} &= y_{in} - P + R_y \end{aligned} xoutyout=xin−P+Rx=yin−P+Ry
实现过程中,这处的加法操作没有必要用DSP,综合器会将其转化为异步逻辑。
激活判定:
a c t i v a t e d = ( 0 ≤ x o u t < F R A M E _ S I Z E ) ∧ ( 0 ≤ y o u t < F R A M E _ S I Z E ) activated = (0 \leq x_{out} < FRAME\SIZE) \;\land\; (0 \leq y{out} < FRAME\_SIZE) activated=(0≤xout<FRAME_SIZE)∧(0≤yout<FRAME_SIZE)
s_axis_tready 工作方式
assign s_axis_tready = !fifo_almost_full && enable;
纯组合逻辑,两个拉低条件:
enable = 0:顶层使能关闭fifo_almost_full = 1:输出 FIFO 剩余深度 ≤ 10
反压传导链为全组合逻辑(0 拍延迟),确保 FIFO 在真正溢出前上级已停止发送。
有效标志 s1_valid
s1_valid 合并四重语义------只有四者同时满足,像素才进入流水线:
s1_valid = tvalid && tready && enable && activated
Stage 1 输出接口
| 输出 | 位宽 | 说明 |
|---|---|---|
s1_valid |
1 | 有效标志(合并四重语义) |
s1_x_out |
⌈ log 2 ( F R A M E _ S I Z E ) ⌉ \lceil\log_2(FRAME\_SIZE)\rceil ⌈log2(FRAME_SIZE)⌉ | 输出行坐标 |
s1_y_out |
⌈ log 2 ( F R A M E _ S I Z E ) ⌉ \lceil\log_2(FRAME\_SIZE)\rceil ⌈log2(FRAME_SIZE)⌉ | 输出列坐标 |
s1_x_in |
⌈ log 2 ( F R A M E _ S I Z E ) ⌉ \lceil\log_2(FRAME\_SIZE)\rceil ⌈log2(FRAME_SIZE)⌉ | 输入行坐标(透传) |
s1_y_in |
⌈ log 2 ( F R A M E _ S I Z E ) ⌉ \lceil\log_2(FRAME\_SIZE)\rceil ⌈log2(FRAME_SIZE)⌉ | 输入列坐标(透传) |
s1_pixel_data |
IN_DATA_WIDTH |
输入像素值 |
s1_Rx |
⌈ log 2 ( C S I Z E ) ⌉ \lceil\log_2(C_{SIZE})\rceil ⌈log2(CSIZE)⌉ | 取模值 R x R_x Rx(供 Stage 2 复用) |
s1_Ry |
⌈ log 2 ( C S I Z E ) ⌉ \lceil\log_2(C_{SIZE})\rceil ⌈log2(CSIZE)⌉ | 取模值 R y R_y Ry(供 Stage 2 复用) |
R x R_x Rx 与 R y R_y Ry 在 Stage 1 计算一次,Stage 2 直接用于推导参数索引 ( k , m ) (k, m) (k,m) 的等价形式(公式三第二条),避免了重复取模运算。
处理流程(伪代码)
// ========== Stage 1: 输出坐标 + 激活判定 ==========
Rx = (i - x_in + P) mod C_SIZE
Ry = (j - y_in + P) mod C_SIZE
x_out = x_in - P + Rx
y_out = y_in - P + Ry
activated = (0 <= x_out && x_out < FRAME_SIZE) &&
(0 <= y_out && y_out < FRAME_SIZE)
s1_valid_comb = tvalid && tready && enable && activated
if (s1_valid_comb) begin
s1_valid <= 1
s1_x_out <= x_out
s1_y_out <= y_out
s1_x_in <= x_in
s1_y_in <= y_in
s1_pixel_data <= pixel_data
s1_Rx <= Rx
s1_Ry <= Ry
end else begin
s1_valid <= 0
// 其余寄存器全部显式初始化为 0,避免 latch
end
3.3 Stage 2:索引生成阶段
功能描述
Stage 2 是纯组合逻辑索引计算级,不访问 BRAM。它消费 Stage 1 传来的坐标和取模值,并行计算三项索引:
- BRAM 线性地址(公式五)
- 参数索引 ( k , m ) (k, m) (k,m) (公式三等价形式,复用 Stage 1 的 R x R_x Rx、 R y R_y Ry)
- is_first 起始标志(公式二)
公式五实现------BRAM 地址
x _ i d x = x o u t − i C S I Z E , y _ i d x = y o u t − j C S I Z E , b r a m _ a d d r = x _ i d x × N y + y _ i d x x\idx = \frac{x{out} - i}{C_{SIZE}}, \quad y\idx = \frac{y{out} - j}{C_{SIZE}}, \quad bram\_addr = x\_idx \times N_y + y\_idx x_idx=CSIZExout−i,y_idx=CSIZEyout−j,bram_addr=x_idx×Ny+y_idx
由公式四可证 x o u t − i x_{out} - i xout−i 为 C S I Z E C_{SIZE} CSIZE 的整数倍,因此这里也可以用查找表来完成,查找表的形式如下: b r a m a d d r = L U T ( x o u t , y o u t ) bram_{addr} = LUT(x_{out},y_{out}) bramaddr=LUT(xout,yout)。值得注意的是,由于每一个例化的卷积模块中 i , j i,j i,j不相同,所以LUT里的数据也会不相同;下面类似的实现方案不在赘述。
公式三实现------参数索引
复用 Stage 1 的取模结果:
k = C S I Z E − 1 − R x , m = C S I Z E − 1 − R y k = C_{SIZE} - 1 - R_x,\qquad m = C_{SIZE} - 1 - R_y k=CSIZE−1−Rx,m=CSIZE−1−Ry
公式二实现------is_first
i s _ f i r s t = ( x i n = max ( 0 , x o u t − P ) ) ∧ ( y i n = max ( 0 , y o u t − P ) ) is\first = (x{in} = \max(0, x_{out} - P)) \;\land\; (y_{in} = \max(0, y_{out} - P)) is_first=(xin=max(0,xout−P))∧(yin=max(0,yout−P))
Stage 2 输出接口
| 输出 | 位宽 | 说明 |
|---|---|---|
s2_valid |
1 | 有效标志 |
s2_bram_addr |
⌈ log 2 ( N x × N y ) ⌉ \lceil\log_2(N_x \times N_y)\rceil ⌈log2(Nx×Ny)⌉ | BRAM 线性地址 |
s2_para_k |
⌈ log 2 ( C S I Z E ) ⌉ \lceil\log_2(C_{SIZE})\rceil ⌈log2(CSIZE)⌉ | 参数行索引 k k k |
s2_para_m |
⌈ log 2 ( C S I Z E ) ⌉ \lceil\log_2(C_{SIZE})\rceil ⌈log2(CSIZE)⌉ | 参数列索引 m m m |
s2_is_first |
1 | 起始像素标志 |
s2_x_out、s2_y_out |
--- | 输出坐标(透传) |
s2_x_in、s2_y_in |
--- | 输入坐标(透传至 Stage 5) |
s2_pixel_data |
IN_DATA_WIDTH |
输入像素值(透传) |
处理流程(伪代码)
// ========== Stage 2: BRAM地址 + Para索引 + is_first ==========
if (s1_valid) begin
// 公式五:BRAM 地址
x_idx = (s1_x_out - i) / C_SIZE // 整数除法
y_idx = (s1_y_out - j) / C_SIZE
bram_addr = x_idx * N_y + y_idx
// 公式三等价形式:参数索引(复用 Stage 1 取模结果)
k = C_SIZE - 1 - s1_Rx
m = C_SIZE - 1 - s1_Ry
// 公式二:is_first
first_x = max(0, s1_x_out - P)
first_y = max(0, s1_y_out - P)
is_first = (s1_x_in == first_x) && (s1_y_in == first_y)
s2_valid <= 1
s2_bram_addr <= bram_addr
s2_para_k <= k
s2_para_m <= m
s2_is_first <= is_first
s2_x_out <= s1_x_out
s2_y_out <= s1_y_out
s2_x_in <= s1_x_in
s2_y_in <= s1_y_in
s2_pixel_data <= s1_pixel_data
end else begin
s2_valid <= 0
// 其余寄存器显式初始化为 0
end
3.4 Stage 3:数据获取阶段(BRAM 读 + 参数 MUX)
功能描述
Stage 3 消费 Stage 2 传来的索引,完成两项数据获取操作:
- BRAM A 口组合读 :用
s2_bram_addr组合驱动 BRAM A 地址线(DOA_REG=0),读数据在同周期组合返回并打入 S3→S4 流水寄存器 - 参数 MUX 选取 :用
s2_para_k、s2_para_m从 C S I Z E 2 C_{SIZE}^2 CSIZE2 个并行参数端口中通过组合 MUX 选出 P a r a ( k , m ) Para(k,m) Para(k,m),结果打入 S3→S4 寄存器
其余信号(像素数据、坐标、is_first)从 Stage 2 透传,不做处理。
BRAM A 口接口
BRAM 配置 DOA_REG=0(旁路输出寄存器),地址由 Stage 3 组合逻辑驱动,读数据同周期组合返回:
| BRAM A 口信号 | 方向 | 驱动来源 | 说明 |
|---|---|---|---|
bram_a_addr |
output | s2_bram_addr(组合直连) |
A 口读地址 |
bram_a_en |
output | s2_valid(组合使能) |
A 口使能 |
bram_a_rdata |
input | BRAM A 原始读端口 | 组合读数据(含 BRAM 门延迟 + 布线延迟) |
架构收益 :
bram_a_rdata在 Stage 3 被显式寄存器s3_bram_rdata锁存(Tco ≈ 0.5 ns),Stage 4 关键路径不含 BRAM 延迟。
参数 MUX
C S I Z E 2 C_{SIZE}^2 CSIZE2-to-1 组合 MUX 。参数值 para_val 打入 s3_para_val 寄存器,Stage 4 由寄存器直驱 DSP A 口(零 LUT 延迟)。
Stage 3 输出接口
| 输出 | 位宽 | 说明 |
|---|---|---|
s3_valid |
1 | 有效标志 |
s3_bram_rdata |
FULL_WIDTH |
BRAM 读出的历史部分和(寄存器输出,Tco ≈ 0.5 ns) |
s3_para_val |
PARA_WIDTH |
参数 MUX 输出 P a r a ( k , m ) Para(k,m) Para(k,m)(寄存器直驱 DSP A 口) |
s3_bram_addr |
⌈ log 2 ( N x × N y ) ⌉ \lceil\log_2(N_x \times N_y)\rceil ⌈log2(Nx×Ny)⌉ | BRAM 地址(透传,用于缓存标签) |
s3_is_first |
1 | is_first 标志(透传至 Stage 4 门控) |
s3_x_out、s3_y_out |
--- | 输出坐标(透传) |
s3_x_in、s3_y_in |
--- | 输入坐标(透传至 Stage 5) |
s3_pixel_data |
IN_DATA_WIDTH |
像素值(透传至 Stage 4) |
处理流程(伪代码)
// ========== Stage 3: BRAM A口读 + 参数MUX ==========
// 组合逻辑
bram_a_addr = s2_bram_addr
bram_a_en = s2_valid
para_val = Para[s2_para_k][s2_para_m]
// 时序逻辑
if (s2_valid) begin
s3_valid <= 1
s3_bram_rdata <= bram_a_rdata // BRAM 组合读数据打拍
s3_para_val <= para_val // 参数值寄存器输出
s3_bram_addr <= s2_bram_addr
s3_is_first <= s2_is_first
s3_x_out <= s2_x_out
s3_y_out <= s2_y_out
s3_x_in <= s2_x_in
s3_y_in <= s2_y_in
s3_pixel_data <= s2_pixel_data
end else begin
s3_valid <= 0
// 其余寄存器显式初始化为 0
end
3.5 Stage 4:乘累加阶段(计算缓存核心)
功能描述
Stage 4 是算术核心,消费 Stage 3 传来的全部数据,单周期完成:
- 历史数据三路选择 (按优先级):
is_first = 1→ 历史数据 = 0(无条件门控)- 缓存命中 → 历史数据 = 上一拍新部分和(旁路 BRAM 过时数据)
- 否则 → 历史数据 =
s3_bram_rdata(BRAM 读出的值)
- DSP48E2 乘累加 :
s3_para_val × s3_pixel_data + historical_data - 更新计算缓存 :新部分和打入
s4_partial_sum,下一拍自动成为缓存命中数据源
计算缓存旁路机制
这是流水线设计中最关键的微架构决策,源于 BRAM 真双口的 RAW(Read-After-Write)数据冒险。
RAW 冒险 :Stage 3 的 BRAM A 口组合读与 Stage 5 的 BRAM B 口时序写之间仅隔 1 个流水级。同一输出位置连续两次访问时,后一次读发生在前一次写落盘之前,BRAM A 口必然返回旧值。
缓存旁路方案 :直接复用 Stage 4 流水寄存器中上一拍的 s4_partial_sum 和 s4_bram_addr 作为"计算缓存"。当下一次 Stage 4 需要历史数据时,先比对该地址是否命中:
- 命中 :直接用
s4_partial_sum(上一拍的新部分和),无视 BRAM 旧值 - 未命中 :用
s3_bram_rdata
效果等同于 CPU 的 Store-to-Load Forwarding------尚未落盘的数据从流水寄存器直接旁路给下一次运算。
不做旁路的后果 : C S I Z E = 3 C_{SIZE}=3 CSIZE=3 时,同一输出位置连续被访问 9 次。除第一次外,第 2~9 次全部使用 BRAM 过时旧值,最终结果仅剩最后一次乘累加的贡献------在任何场景下均不可接受。
is_first 门控
is_first 以最高优先级生效:输出位置首次被访问时,无论 BRAM 中残留什么值(上电随机值或上一帧数据),历史值强制为 0。这带来了三项关键硬件收益:
- 上电后无需等待 BRAM 初始化
- 帧间无需 BRAM 清零操作
- 支持背靠背连续帧处理,零帧间等待
历史数据三路选择的完整优先级:
if (is_first) → historical_data = 0
else if (cache_hit) → historical_data = s4_partial_sum
else → historical_data = s3_bram_rdata
Stage 4 输出接口
| 输出 | 位宽 | 说明 |
|---|---|---|
s4_valid |
1 | 有效标志 |
s4_partial_sum |
FULL_WIDTH |
更新后的全精度部分和(同时作为下一拍缓存数据源) |
s4_bram_addr |
⌈ log 2 ( N x × N y ) ⌉ \lceil\log_2(N_x \times N_y)\rceil ⌈log2(Nx×Ny)⌉ | BRAM 地址(同时作为下一拍缓存标签和 Stage 5 回写地址) |
s4_x_out、s4_y_out |
--- | 输出坐标(透传) |
s4_x_in、s4_y_in |
--- | 输入坐标(透传至 Stage 5) |
处理流程(伪代码)
// ========== Stage 4: 三路选择 + 乘累加 + 缓存更新 ==========
// 历史数据三路选择
if (s3_is_first) begin
historical_data = 0
end else if (s4_valid && (s4_bram_addr == s3_bram_addr)) begin
historical_data = s4_partial_sum // 缓存命中------旁路
end else begin
historical_data = s3_bram_rdata // BRAM 读取
end
// DSP48E2 乘累加
partial_sum = s3_para_val * s3_pixel_data + historical_data
// 时序逻辑
if (s3_valid) begin
s4_valid <= 1
s4_partial_sum <= partial_sum // 输出 + 缓存数据
s4_bram_addr <= s3_bram_addr // 输出 + 缓存标签
s4_x_out <= s3_x_out
s4_y_out <= s3_y_out
s4_x_in <= s3_x_in
s4_y_in <= s3_y_in
end else begin
s4_valid <= 0
// 其余寄存器显式初始化为 0
end
DSP48E2 端口映射与位宽规划
┌──────────────────────────────────────┐
│ DSP48E2 │
│ A (30-bit) ← s3_para_val(寄存器直驱) │
│ B (18-bit) ← s3_pixel_data │
│ C (48-bit) ← historical_data │
│ OPMODE ← A×B + C │
│ P (48-bit) → partial_sum │
└──────────────────────────────────────┘
DSP 配置为组合乘累加模式。参数值由 s3_para_val 寄存器直驱 A 口,参数 MUX 的 LUT 延迟已在 Stage 3 消化。
全精度位宽(最坏情况):
| C S I Z E C_{SIZE} CSIZE | I N _ W IN\_W IN_W (max) | P A R A _ W PARA\_W PARA_W (max) | 累加扩展 | F U L L _ W I D T H FULL\_WIDTH FULL_WIDTH | 累加器宽度 | 裕量 |
|---|---|---|---|---|---|---|
| 3 | 12 | 16 | 4 位 | 32 | 48 | 16 位 |
| 5 | 12 | 16 | 5 位 | 33 | 48 | 15 位 |
时序估算(7 系列 FPGA,speed grade -1):
- S3→S4 寄存器 Tco:~0.5 ns
- 历史数据 MUX(3 选 1):~0.5 ns
- DSP48E2 组合乘加:~3.5 ns
- 建立时间:~0.5 ns
- 总路径 :~5.0 ns → f m a x ≈ 200 f_{max} \approx 200 fmax≈200 MHz
3.6 Stage 5:回写与输出阶段
功能描述
Stage 5 是流水线收尾级,消费 Stage 4 的全精度部分和,完成最终判定与分发:
- is_last 判定(公式六):判断当前像素是否为输出位置的最后一个依赖像素
- BRAM B 口回写 :
is_last=1:写入 0(清零),将最终结果推入 FIFOis_last=0:写入更新后的全精度部分和,继续等待后续像素
- 格式转换 (仅
is_last=1):全精度部分和缩放/钳位至输出格式 - 输出 FIFO 写入:格式转换后的结果连同输出坐标写入 FIFO
BRAM B 口接口
| BRAM B 口信号 | 方向 | 驱动来源 | 说明 |
|---|---|---|---|
s5_bram_b_wr_en |
output | Stage 5 组合逻辑 | 每个有效拍均拉高 |
s5_bram_b_addr |
output | s4_bram_addr |
B 口写地址 |
s5_bram_b_wr_data |
output | s4_partial_sum 或 0 |
B 口写数据 |
处理流程(伪代码)
// ========== Stage 5: is_last + BRAM回写 + 格式转换 + FIFO写入 ==========
x_last = min(s4_x_out + P, FRAME_SIZE - 1)
y_last = min(s4_y_out + P, FRAME_SIZE - 1)
is_last = (s4_x_in == x_last) && (s4_y_in == y_last)
s5_bram_b_addr = s4_bram_addr
s5_bram_b_wr_en = s4_valid
if (is_last) begin
s5_bram_b_wr_data = 0 // 清零 BRAM
out_data = format_convert(s4_partial_sum, ...) // 格式转换
s5_fifo_wr_en = 1
s5_fifo_wr_data = {out_data, s4_x_out, s4_y_out}
end else begin
s5_bram_b_wr_data = s4_partial_sum // 写回部分和
s5_fifo_wr_en = 0
end
格式转换
全精度部分和 P f u l l P_{full} Pfull 的隐含 LSB 为 LSB product = LSB in × LSB para \text{LSB}{\text{product}} = \text{LSB}{\text{in}} \times \text{LSB}_{\text{para}} LSBproduct=LSBin×LSBpara。转换为输出格式分两步:
- 缩放 :乘以 LSB product / LSB out \text{LSB}{\text{product}} / \text{LSB}{\text{out}} LSBproduct/LSBout(退化为算术移位)
- 钳位 :钳位到 [ O U T _ M I N , O U T _ M A X ] [OUT\_MIN, OUT\_MAX] [OUT_MIN,OUT_MAX]
示例 :UINT8 输入(LSB=1)、Q8 参数(LSB= 2 − 7 2^{-7} 2−7)、UINT8 输出(LSB=1)→ 右移 7 位,钳位 [ 0 , 255 ] [0, 255] [0,255]。
FIFO 写入接口
Stage 5 消费流水线最终结果,通过专用写入端口推入输出缓冲 FIFO。写入端口信号定义如下:
| 信号名 | 方向 | 位宽 | 说明 |
|---|---|---|---|
s5_fifo_wr_en |
→ FIFO | 1 | FIFO 写使能。仅当 s4_valid && is_last 时为高,将格式转换后的结果 + 坐标压入 FIFO |
s5_fifo_wr_data |
→ FIFO | OUT_PACK_WIDTH |
FIFO 写数据。拼接格式:{out_data, s4_x_out, s4_y_out},其中 out_data 为格式转换后的输出像素值 |
写入时机 :
s5_fifo_wr_en仅在is_last=1时拉高------即输出位置的最后一个依赖像素到达、部分和已完整累计、格式转换已完成的同一周期。一个输出位置在其全生命周期中恰好写入 FIFO 一次。
fifo_almost_full 信号定义
fifo_almost_full 是输出缓冲 FIFO 内部衍生的组合信号,用于驱动 AXIS Slave 反压链:
| 信号名 | 方向 | 宽度 | 定义 |
|---|---|---|---|
fifo_almost_full |
FIFO → 顶层 | 1 | (fifo_depth - fifo_data_count) ≤ 10 时拉高 |
- 阈值选取 ( C S I Z E = 3 C_{SIZE}=3 CSIZE=3,FIFO 深 32):剩余 ≤ 10 个空位。五级流水线在途最多 5 个待写结果,10 是 2× 裕量,确保组合逻辑反压有足够响应时间。
- 传导路径 :
fifo_almost_full0 拍组合逻辑 → 与enable相与 → 驱动s_axis_tready→ 上游立即停止发送新像素。 - 效果:全程零溢出、零数据丢失,无需粘滞错误标志。
FIFO 读出侧:AXIS Master 接口映射
输出 FIFO 同时是 Stage 5 处理结果与模块顶层 AXIS Master 之间的隔离缓冲层。FIFO 读出侧直接映射为 AXIS Master 的 TData / TUser / TValid / TReady:
| FIFO 内部读出信号 | 方向 | AXIS Master 端口 | 说明 |
|---|---|---|---|
fifo_rdata.out_data |
→ | m_axis_tdata |
输出像素数据 |
fifo_rdata.x_out |
→ | m_axis_tuser_x |
输出行坐标 |
fifo_rdata.y_out |
→ | m_axis_tuser_y |
输出列坐标 |
!fifo_empty |
→ | m_axis_tvalid |
非空即有效 |
m_axis_tready |
← | fifo_rd_en |
后级就绪直接驱动 FIFO 读使能 |
// FIFO 读出侧 → AXIS Master 组合逻辑直连
assign m_axis_tvalid = !fifo_empty;
assign fifo_rd_en = m_axis_tready && m_axis_tvalid;
assign m_axis_tdata = fifo_rdata.out_data;
assign m_axis_tuser_x = fifo_rdata.x_out;
assign m_axis_tuser_y = fifo_rdata.y_out;
读写隔离 :FIFO 写入侧由 Stage 5 is_last 门控触发,读出侧由下游 AXIS Master tready 节拍驱动,两端完全解耦。FIFO 吸收流水线产出速率与下游消费速率之间的瞬时差异。
4. BRAM 与 FIFO 资源计算
4.1 BRAM 深度与宽度
4.1.1 存储需求
每个子模块 K ( i , j ) K(i,j) K(i,j) 独立维护其辖内全部输出位置的全精度部分和。子模块管辖的输出位置总数为 N x × N y N_x \times N_y Nx×Ny(见公式五)。BRAM 深度由所有子模块中 N x × N y N_x \times N_y Nx×Ny 的最大值向上取整到 2 的整数次幂,统一所有模块深度以简化时序约束。
depth = 2 ⌈ log 2 ( max i , j N x × N y ) ⌉ \boxed{\text{depth} = 2^{\lceil\log_2(\max_{i,j} N_x \times N_y)\rceil}} depth=2⌈log2(maxi,jNx×Ny)⌉
4.1.2 C S I Z E = 3 C_{SIZE}=3 CSIZE=3, F R A M E _ S I Z E = 128 FRAME\_SIZE=128 FRAME_SIZE=128
| 子模块 ( i , j ) (i,j) (i,j) | N x N_x Nx | N y N_y Ny | N x × N y N_x \times N_y Nx×Ny |
|---|---|---|---|
| ( 0 , 0 ) (0,0) (0,0) | ⌈ 128 / 3 ⌉ = 43 \lceil 128/3\rceil = 43 ⌈128/3⌉=43 | 43 | 1849 |
| ( 0 , 1 ) (0,1) (0,1) | 43 | ⌈ 127 / 3 ⌉ = 43 \lceil 127/3\rceil = 43 ⌈127/3⌉=43 | 1849 |
| ( 0 , 2 ) (0,2) (0,2) | 43 | ⌈ 126 / 3 ⌉ = 42 \lceil 126/3\rceil = 42 ⌈126/3⌉=42 | 1806 |
| ( 1 , 0 ) (1,0) (1,0) | ⌈ 127 / 3 ⌉ = 43 \lceil 127/3\rceil = 43 ⌈127/3⌉=43 | 43 | 1849 |
| ( 1 , 1 ) (1,1) (1,1) | 43 | 43 | 1849 |
| ( 1 , 2 ) (1,2) (1,2) | 43 | 42 | 1806 |
| ( 2 , 0 ) (2,0) (2,0) | ⌈ 126 / 3 ⌉ = 42 \lceil 126/3\rceil = 42 ⌈126/3⌉=42 | 43 | 1806 |
| ( 2 , 1 ) (2,1) (2,1) | 42 | 43 | 1806 |
| ( 2 , 2 ) (2,2) (2,2) | 42 | 42 | 1764 |
max ( N x × N y ) = 1849 , depth = 2 11 = 2048 \max(N_x \times N_y) = 1849, \quad \text{depth} = 2^{11} = \boxed{2048} max(Nx×Ny)=1849,depth=211=2048
4.1.3 C S I Z E = 5 C_{SIZE}=5 CSIZE=5, F R A M E _ S I Z E = 128 FRAME\_SIZE=128 FRAME_SIZE=128
| 子模块 ( i , j ) (i,j) (i,j) | N x N_x Nx | N y N_y Ny | N x × N y N_x \times N_y Nx×Ny |
|---|---|---|---|
| ( 0 , 0 ) (0,0) (0,0) | ⌈ 128 / 5 ⌉ = 26 \lceil 128/5\rceil = 26 ⌈128/5⌉=26 | 26 | 676 |
| ( 0 , 1 ) ∼ ( 2 , 2 ) (0,1) \sim (2,2) (0,1)∼(2,2) | 26 | 26 | 676 |
| 边界子模块 ( i ≥ 3 (i\geq 3 (i≥3 或 j ≥ 3 ) j\geq 3) j≥3) | 26 | 25 | 650 |
max ( N x × N y ) = 676 , depth = 2 10 = 1024 \max(N_x \times N_y) = 676, \quad \text{depth} = 2^{10} = \boxed{1024} max(Nx×Ny)=676,depth=210=1024
4.1.4 位宽与 BRAM 原语折算
| C S I Z E C_{SIZE} CSIZE | 全精度位宽 | BRAM 位宽(取整) | 深度 |
|---|---|---|---|
| 3 | 32 位 | 36 位 | 2048 |
| 5 | 33 位 | 36 位 | 1024 |
Xilinx 7 系列 BRAM36 原生配置表:
| 配置 | 深度 | 宽度 | 总量 |
|---|---|---|---|
| BRAM36 TDP | 1024 | 36 | 36 Kb |
| BRAM36 TDP | 2048 | 18 | 36 Kb |
C S I Z E = 3 C_{SIZE}=3 CSIZE=3(9 模块,深 2048,宽 36) :BRAM36 在 2048 深度下原生仅 18 位宽,需 2 个 BRAM36 拼接为 2048 × 36 2048 \times 36 2048×36 → 每模块 2 BRAM36,9 模块共 18 BRAM36。
C S I Z E = 5 C_{SIZE}=5 CSIZE=5(25 模块,深 1024,宽 36) :BRAM36 在 1024 深度下原生 36 位宽,全精度 33 位恰好装入 → 每模块 1 BRAM36,25 模块共 25 BRAM36。
4.2 FIFO 资源
4.2.1 输出缓冲 FIFO 规格
输出 FIFO 在 is_last=1 时写入一次,数据包位宽为:
OUT_PACK_WIDTH = OUT_DATA_WIDTH + 2 × ⌈ log 2 ( F R A M E _ S I Z E ) ⌉ \text{OUT\_PACK\_WIDTH} = \text{OUT\_DATA\_WIDTH} + 2 \times \lceil\log_2(FRAME\_SIZE)\rceil OUT_PACK_WIDTH=OUT_DATA_WIDTH+2×⌈log2(FRAME_SIZE)⌉
以 F R A M E _ S I Z E = 128 FRAME\_SIZE=128 FRAME_SIZE=128、OUT_DATA_WIDTH=8 为例: 8 + 2 × 7 = 22 8 + 2\times7 = 22 8+2×7=22 位。
| C S I Z E C_{SIZE} CSIZE | 推荐深度 | 依据 |
|---|---|---|
| 3(9 模块) | 32 | 5 级流水在途 + 9 项同时完成 + 反压裕量 |
| 5(25 模块) | 64 | 5 级流水在途 + 25 项可同时完成 + 反压裕量 |
4.2.2 FIFO 实现方式
| 方式 | 资源 | 适用深度 | 说明 |
|---|---|---|---|
| LUT-RAM | ~60 LUT | ≤ 32 | 面积效率高,时序好 |
| BRAM18 FIFO | 1 BRAM18 | ≥ 64 | 控制逻辑约 50 LUT |