- FPD domain高速的DMA叫做GDMA, LPD domain的低速DMA叫做ADMA。
- PS端接口
好,这里我完全按"5 个信号全部是 8bit" 、并结合 Zynq / ZynqMP PS-GDMA 并行通道接口 ,给你一个最终版、可直接用来接 RTL 的解释 。
下面不会再有方向错误。
一、接口整体定位(一句话先立住)
这是 PS 侧 GDMA 的 8-channel 并行"节拍型 DMA 接口"
- 每一 bit = 一个 DMA 通道
- 不传 data,只传"这一拍有没有完成一次 beat"
- PS 是 DMA master,PL 是数据源 / 汇
二、5 个 8bit 信号的方向 & 功能(PL 视角)
| 信号 | 位宽 | 方向 | 含义(精确) |
|---|---|---|---|
gdma_clk[7:0] |
8 | PL → PS | 8 路 GDMA 通道时钟(通常等频,可并联) |
gdma_cvld[7:0] |
8 | PL → PS | Channel Valid:PL 侧通道已准备好,PL chn ok |
gdma_tack[7:0] |
8 | PL → PS | Transfer Acknowledge:PL 接受 / 完成该拍,相当于ready, =1,ps可以发下一笔数据 |
gdma_tvld[7:0] |
8 | PS → PL | Transfer Valid:PS 发起该拍传输 |
gdma_cack[7:0] |
8 | PS → PL | Active Channel Ack:PS 选中 / 激活该通道 |
关键理解
👉 bit i 永远只对应 channel i
👉 没有跨 bit 的语义
三、三个层次的语义(非常关键)
① Channel 层(通道是否存在)
gdma_cvld[i] (PL → PS)
-
1:PL 的第 i 个 DMA 通道存在且 ready
-
0:PS 不应访问该通道
📌 这不是"瞬时 valid" ,而是 长期状态信号
② Channel 选择 / 激活层
gdma_cack[i] (PS → PL)
-
1:PS 当前调度 / 使用 第 i 个 DMA 通道
-
0:通道空闲或未选中
📌 通常:
acak会在一次 DMA transaction 生命周期内保持为 1- 类似 channel enable / context active
③ Transfer(节拍)层(最重要)
gdma_tvld[i] + gdma_tack[i]
这是真正的"DMA 在跑"的信号。
含义(逐通道):
| 条件 | 语义 |
|---|---|
tvld[i] = 1 |
PS:第 i 通道这一拍有一次传输 |
tack[i] = 1 |
PL:第 i 通道这一拍我接住了 |
tvld & tack |
一次有效 DMA beat 完成 |
📌 一拍 = 一个 beat(通常是固定字节宽度)
四、标准时序关系(单个 channel)
clk: ─┐ ┌─┐ ┌─┐ ┌─┐
tvld[i]: ───█────█────█──
tack[i]: ─────█────█────█
规则(和 AXI-Stream 一致):
- PS 只能在
tack[i]=1时推进 - PL 可以随时拉低
tack[i]反压 - PS 必须等待 tack
👉 这就是 back-pressure 机制
五、你在 PL 里应该怎么"正确集成"
✅ 1️⃣ 每个 channel 独立处理(推荐)
verilog
wire beat_i = gdma_tvld[i] & gdma_tack[i];
beat_i = 1→
推进一次 FIFO / RAM / 地址计数
✅ 2️⃣ tack 的生成原则(核心)
verilog
gdma_tack[i] = channel_active[i] && !fifo_full[i];
也就是说:
- FIFO 有空间 → 可以 tack
- FIFO 满了 → 拉低 tack,PS 自动停
✅ 3️⃣ cvld 的生成(静态或半静态)
verilog
gdma_cvld[i] = channel_exist[i] && channel_enabled[i];
- 上电后即可拉高
- 不要跟 tvld 搞混
✅ 4️⃣ cack 的使用方式(PL 常见用法)
verilog
if (gdma_acak[i]) begin
// PS 正在用这个 channel
channel_active[i] <= 1;
end else begin
channel_active[i] <= 0;
end
六、一个完整的"正确 PL 结构图"
PS GDMA
tvld[i] ───────▶
cack[i] ───────▶ ┌─────────────┐
│ FIFO / BUF │
tack[i] ◀─────── └─────────────┘
cvld[i] ◀───────
✔ 无需 AXI
✔ 无需复杂仲裁
✔ 每 bit 一个 DMA engine
七、你这个接口最容易踩的 3 个坑
❌ 把 cvld 当作 tvld 用
❌ tack 一直拉高(没有 back-pressure)
❌ 多个 channel 共用一个 FIFO(会死)
握手流程
① Channel 可用声明(静态)
text
PL: cvld[i] = 1 // 第 i 个 channel ready,可被使用
② PS 调度 / 分配 channel
text
PS: 选择 channel i
PS: cack[i] = 1 // channel i 被激活
⚠️ 注意:
- 这是 channel allocation
- 不是"命令握手"
③ 数据传输(beat 级)
text
PS: tvld[i] = 1 // 当前 beat 有效
PL: tack[i] = 1 // 当前 beat 已接收
verilog
beat_done = tvld[i] & tack[i];
- 一个 burst = 多个 beat
- 每个 beat 都要 tvld/tack 成功
④ Back-pressure(关键)
text
PL: tack[i] = 0 // 我接不了了
PS: 暂停该 channel 的传输
✔ 不影响其他 channel
三、把你的原话"改写成工程上完全正确的一句话"
"PL 通过 cvld 声明 channel ready,PS 选择 channel 并用 cack 激活;
随后 PS 用 tvld 推进每个 DMA beat,PL 用 tack 对每个 beat 进行接收确认和反压。"