<!-- title: 5G HARQ-ACK码本与DAI机制拆解 -->
<!-- domain: ran -->

# 5G HARQ-ACK 码本与 DAI 机制全链拆解：从漏检恢复到动态码本构造

## 一、问题起点：为什么需要"码本"这个东西

在 LTE 时代，HARQ 反馈相对直接：基站每发一个下行传输块（TB），终端就在固定的时序位置回一个 ACK/NACK。但 5G NR 把这套规则打碎了——因为 NR 同时引入了**灵活 K1 反馈时序**、**载波聚合（CA）多服务小区**、**一个 PUCCH 反馈多个 PDSCH**、**多时隙调度**等特性。

这带来了一个致命问题：当终端**漏检了某个 DCI**（即没有收到某个 PDSCH 的调度授权），它就不知道这一轮本该反馈多少个 ACK/NACK，也不知道基站期望的反馈在 PUCCH 上的**比特位置**。位置一错，整个反馈窗口全乱：基站把 NACK 误读成 ACK，重传不再触发，数据静默丢失。

为了解决"**终端与基站对反馈位置的认知必须严格对齐**"这一核心矛盾，3GPP 在 TS 38.213 第 9 章定义了 **HARQ-ACK codebook（码本）** 机制，并在 TS 38.212 的 DCI 格式中嵌入了 **DAI（Downlink Assignment Index，下行分配索引）** 字段。这两者是一对孪生设计：**码本规定反馈结构，DAI 保证在动态结构下终端能"补齐漏检"**。

本文聚焦 TS 38.213 §9.1 系列，把这条链路从 K1 时序、三种码本类型、DAI 计数原理、Type-2 构造算法、到 PUCCH 承载与 UCI 复用，逐层拆解。

## 二、前置：HARQ 反馈时序 K1

一切码本讨论都建立在 **K1（PDSCH-to-HARQ-ACK timing）** 之上。K1 定义的是：PDSCH 所在的时隙 `n`，其 HARQ-ACK 反馈在 `n + K1` 个上行时隙发送。

- **RRC 配置**：参数 `dl-DataToUL-ACK`（或 R16 的 `dl-DataToUL-ACK-r16`）给出一组候选 K1 值，例如 `{1, 2, 3, 4, 6, 8}`。
- **DCI 动态指示**：DCI format 1_1 中有 **"PDSCH-to-HARQ_feedback timing indicator"** 字段（通常 3 bit），从候选集合中动态挑选一个 K1。
- **DCI 1_0**：无该指示字段，直接使用候选集合中的第一个值。

这个设计的关键在于：**不同 PDSCH 可以指向同一个 PUCCH 反馈时隙**。比如时隙 n 的两个 PDSCH 分别用 K1=4 和 K1=3 指向同一个上行时隙 n+4。于是"一个 PUCCH 携带多个 PDSCH 的反馈"成为常态——这正是码本要解决的问题。

```mermaid
sequenceDiagram
    autonumber
    participant gNB
    participant UE
    Note over gNB,UE: 一个 PUCCH 反馈时隙承载多个 PDSCH 的反馈
    gNB->>UE: DCI 1_1 (PDSCH#A, K1=3)
    gNB->>UE: PDSCH#A @ slot n
    gNB->>UE: DCI 1_1 (PDSCH#B, K1=4)
    gNB->>UE: PDSCH#B @ slot n+1
    gNB->>UE: DCI 1_1 (PDSCH#C, K1=2)
    gNB->>UE: PDSCH#C @ slot n+2
    Note over UE: 三者 K1 指向同一反馈时隙 n+4
    UE->>gNB: PUCCH (slot n+4) 携带 [ACK_A, NACK_B, ACK_C] 码本
```

理解了 K1 是"汇聚器"，码本就是"被汇聚的比特集合的排列规则"。

## 三、三种码本类型：半静态、动态、一次性

3GPP 定义了三种 HARQ-ACK codebook，对应三种不同的"反馈排列哲学"：

### 1. Type 1 — 半静态码本（Semi-Static，§9.1.2）

由 RRC 参数 `pdsch-HARQ-ACK-Codebook = semiStatic` 触发。码本大小**与实际调度无关，预先固定**。

构造逻辑：终端根据 RRC 配置的 K1 集合、PDCCH 监听时机（MO）集合、服务小区集合，预先枚举出一个"候选 PDSCH 接收时机"的二维/三维网格 `M`。无论基站实际是否在这些位置调度了 PDSCH，码本里**每一个位置都占一格**：

- 收到了 PDSCH → 填实际的 ACK/NACK；
- 没收到 → 填一个 **NACK 占位**。

**优点**：结构完全可预测，终端和基站永远对齐，**完全不需要 DAI**（DAI 在 Type 1 中仅作冗余校验）。
**缺点**：开销大。当基站实际只在网格里调度了少量 PDSCH 时，码本里塞满了无意义的 NACK 占位比特，浪费 PUCCH 容量。

### 2. Type 2 — 动态码本（Dynamic，§9.1.3）

由 `pdsch-HARQ-ACK-Codebook = dynamic` 触发。码本大小**根据实际收到的 DCI 动态确定**，DAI 是其核心。

终端不再枚举固定网格，而是只对**实际检测到下行分配的 DCI** 进行 ACK/NACK 排列。由于结构是动态的，必须有 DAI 来告诉终端"你这一轮总共该收到几个、当前是第几个"，否则漏检一处就会错位。

**优点**：开销最小，只反馈实际调度。
**缺点**：逻辑复杂，依赖 DAI 的正确计数与漏检恢复。

### 3. Type 3 — 一次性码本（One-shot，R16，§9.1.4）

Rel-16 引入。一次性反馈**一个或多个 HARQ 进程的当前 ACK/NACK 状态**，与是否被本次调度无关。

由 DCI 中的 "HARQ-ACK request (one-shot)"（或 DCI 1_2 的对应字段）触发。用于 eMIMO、multi-TRP、URLLC 等场景下，**长时间未反馈导致 gNB 与 UE 的 HARQ 进程状态失步后，强制同步一次**。

三种码本的对比如下表：

| 维度 | Type 1 半静态 | Type 2 动态 | Type 3 一次性 |
|------|--------------|-------------|---------------|
| 引入版本 | R15 | R15 | R16 |
| TS 38.213 章节 | §9.1.2 | §9.1.3 | §9.1.4 |
| RRC 触发参数 | `pdsch-HARQ-ACK-Codebook=semiStatic` | `=dynamic` | DCI one-shot 字段触发 |
| 码本大小 | 固定（RRC 预计算） | 动态（随调度变化） | 固定（=HARQ 进程数） |
| 是否依赖 DAI | 否（仅冗余校验） | 是（核心） | 否 |
| 漏检 DCI 处理 | 天然占位 NACK | DAI 补齐 NACK | 反馈进程当前状态 |
| 开销 | 高 | 低 | 中（一次性） |
| 典型场景 | 调度密集、稳定 | CA、稀疏调度 | multi-TRP、URLLC、状态同步 |

## 四、DAI 深度解析：Counter DAI 与 Total DAI

DAI 不是单一字段，而是**两个子字段的组合**，这是初学者最容易混淆的点。

### Counter DAI（C-DAI）

**累计计数器**：到当前 PDCCH 监听时机为止，基站已经下发（含当前这条）的下行分配的累计序号。计数顺序为：**先按服务小区（carrier）递增，再按 PDCCH 监听时机（MO）递增**。

例如：PCell 上调度 → C-DAI=1，SCell#1 调度 → C-DAI=2，SCell#2 调度 → C-DAI=3，下一个 MO 的 PCell → C-DAI=4……以此类推，**模 4 循环**（0→1→2→3→0）。

### Total DAI（T-DAI）

**总数指示**：到当前 PDCCH 监听时机为止，跨所有服务小区，本轮（将复用到同一个 PUCCH）下行分配的**总数**。同样模 4。

T-DAI 的价值在于"封口"：它告诉终端"这一捆里一共有多少个分配"，使终端能在最后一个监听时机就确定码本的总大小。

### DAI 字段在 DCI 中的承载（TS 38.212）

| DCI 格式 | Counter DAI | Total DAI | 比特数 | 说明 |
|----------|-------------|-----------|--------|------|
| DCI 1_0 | 有（2 bit） | 无 | 2 | 仅 counter，常用于回退调度 |
| DCI 1_1 | 有（2 bit） | 有（2 bit） | 2 + 2 | counter + total，Type-2 主力 |
| DCI 1_2 (R17) | 有 | 有 | 可变 | 更紧凑，适配 NR-Light |

两个 DAI 都是 **2 bit、模 4 计数**。模 4 的设计是一个精妙折衷：4 个值（0/1/2/3）能在 2 bit 内表达，覆盖一轮 4 个分配的循环；当一轮分配数超过 4 时靠多轮循环 + T-DAI 配合还原。

### DAI 漏检恢复原理（核心机制）

这是整个机制的灵魂。设想一个 CA 场景，本轮 5 个下行分配：

| 事件 | C-DAI | T-DAI | UE 是否收到 |
|------|-------|-------|-------------|
| 分配1（PCell） | 1 | 5 | ✅ 收到 |
| 分配2（SCell1） | 2 | 5 | ❌ **漏检** |
| 分配3（SCell2） | 3 | 5 | ✅ 收到 |
| 分配4（SCell3） | 4 | 5 | ✅ 收到 |
| 分配5（PCell） | 1（模4） | 5 | ✅ 收到 |

UE 的恢复逻辑（对应 TS 38.213 §9.1.3.2 伪代码）：

1. **C-DAI 跳变检测**：UE 收到的 C-DAI 序列是 `1 → (缺2) → 3 → 4 → 1`。当 UE 发现 C-DAI 从 1 直接跳到 3（增量不是 +1），判定 **C-DAI=2 的位置发生了漏检**，在该位置插入一个 **NACK 占位**。
2. **T-DAI 封口校验**：最后收到的 T-DAI=5，说明本轮总数应为 5。UE 用 T-DAI 锁定码本总长度为 5，确保后续 PDSCH（如多 MO 跨度）的对齐。
3. **码本对齐**：UE 最终构造 `[#1=ACK, #2=NACK(补), #3=NACK, #4=ACK, #5=ACK]`，与基站期望的 5 比特位置完全对齐。

**如果连携带 T-DAI 的最后一个 DCI 也漏检了怎么办？** 此时 UE 无法得到总数，使用**最后一个正确接收的 T-DAI 值**作为码本大小上界——这是一种尽力而为的降级，保证不会比真实值少，至多多补 NACK。

## 五、Type 2 动态码本构造流程

将上述原理形式化为流程：

```mermaid
flowchart TD
    A[UE 在 PDCCH 监听时机检测到下行分配] --> B{是否携带 C-DAI / T-DAI?}
    B -- 是 --> C[记录 C-DAI 当前值]
    B -- 否 --> Z[忽略, 非 Type-2 调度]
    C --> D{C-DAI 增量是否等于 +1?}
    D -- 否 --> E[检测到跳变 → 判定漏检]
    E --> F[在缺失位置插入 NACK 占位比特]
    D -- 是 --> G[按序追加该 PDSCH 的 ACK/NACK]
    F --> G
    G --> H{是否还有后续监听时机?}
    H -- 是 --> A
    H -- 否 --> I[取最后一个有效 T-DAI 作为码本总数 N]
    I --> J{UE 实际收集到的 C-DAI 最大值<br/>是否 < T-DAI?}
    J -- 是 --> K[尾部补 NACK 至 N 比特]
    J -- 否 --> L[码本长度对齐完成]
    K --> L
    L --> M[码本输出: N 个 HARQ-ACK 比特]
    M --> N2[映射到 PUCCH 资源传输]
```

这个流程揭示了 Type-2 的三道对齐防线：**C-DAI 防中间漏检、T-DAI 防尾部漏检、模 4 循环防计数溢出**。

## 六、码本类型选择决策与承载

码本类型本身不是终端自选的，而是 RRC 配置 + DCI 触发的组合决策：

```mermaid
flowchart TD
    S[RRC: pdsch-HARQ-ACK-Codebook 配置] --> T{配置值}
    T -- semiStatic --> T1[Type 1: 固定网格<br/>枚举 K1 × MO × Cell]
    T -- dynamic --> T2[Type 2: 动态结构<br/>依赖 DAI 计数]
    T2 --> DCIcheck{DCI 是否带 one-shot<br/>HARQ-ACK request?}
    DCIcheck -- 是 --> T3[Type 3: 一次性<br/>反馈所有 HARQ 进程状态]
    DCIcheck -- 否 --> T2f[保持 Type 2 动态码本]
    T1 --> Sel[确定码本比特序列]
    T2f --> Sel
    T3 --> Sel
    Sel --> PUCCH[PUCCH Format 选择<br/>1~2bit→F0/F1<br/>多bit码本→F2]
    PUCCH --> Tx[上行传输]
```

注意一个细节：**Type 1 与 Type 3 的码本大小都依赖 RRC 配置**（Type 1 依赖 K1 候选集，Type 3 依赖 HARQ 进程数），唯独 **Type 2 是真正运行时动态的**。这也是为什么 DAI 机制只在 Type 2 中是"必需"的。

### 码本如何映射到 PUCCH

码本生成后，需要选择 PUCCH 资源承载。承载能力由 PUCCH 格式决定：

| PUCCH 格式 | 承载 UCI 比特 | 时长 | 典型用途 |
|-----------|---------------|------|---------|
| Format 0 | 1–2（HARQ-ACK/SR） | short/long | 单或少比特反馈 |
| Format 1 | ≤2 | short/long | HARQ-ACK + SR（序列扩频） |
| Format 2 | >2 | short/long | **码本较大时的主力** |
| Format 3 | ≤1150 | short/long | 大 UCI（CSI + 码本） |
| Format 4 | ≤1150 + OCC | short only | 大 UCI + 正交复用 |

经验法则：HARQ-ACK 比特少时用 Format 0/1；当 Type-2 码本因 CA 或多时隙调度膨胀到数比特以上，自动切换到 Format 2。

## 七、UCI 复用与空域捆绑

### UCI 复用优先级（§9.2）

当 HARQ-ACK、SR（调度请求）、CSI 需要在同一上行时机反馈时，存在复用与丢弃规则：

- **优先级排序**：HARQ-ACK > SR > CSI（HARQ-ACK 永远最高，因为它直接关系重传）。
- **HARQ-ACK + CSI 同时**：通常优先发 HARQ-ACK，CSI 被丢弃或仅保留 CSI Part 2（高优先级 CSI）。
- **HARQ-ACK + SR**：若 SR 为 positive，复用到一个 PUCCH；若 negative，仅发 HARQ-ACK。
- **UCI on PUSCH**：当有 PUSCH 传输时，HARQ-ACK 与 CSI 复用到 PUSCH 的特定 RE 区域（不打孔数据 DMRS）。

### 空域捆绑（Spatial Bundling）

当一个 PDSCH 携带 2 个码字（TB）时，默认需要反馈 2 个 HARQ-ACK 比特。若开启 `harq-ACK-SpatialBundlingPUCCH`，则对两个码字的反馈做**逻辑 AND**，仅反馈 1 比特——两个码字都正确才回 ACK，否则 NACK。这是一种用可靠性换开销的折衷。

## 八、Rel-16/17/18/19 演进脉络

DAI 与码本机制并非一成不变，每个版本都在打补丁与扩边界：

- **Rel-16**：引入 **Type-3 一次性码本**（§9.1.4），支持 multi-TRP、CBG（码块组）反馈；修正 Type-1 码本确定的伪代码歧义。
- **Rel-17**：multicast（多播）HARQ-ACK 码本可配置化；引入 DCI 1_2（更紧凑格式，适配 RedCap/NR-Light），DAI 字段随之精简；NTN（卫星）HARQ 适配。
- **Rel-18**：**增强 Type-2 码本**，支持 multi-slot PDSCH scheduling（一个 DCI 调度多时隙）下的码本生成；解决 PUSCH with repetitions（重复传输）上 HARQ-ACK 复用的问题。
- **Rel-19**：支持 **32 个 HARQ 进程**（TN32HARQ，面向 FR2-2 高频大带宽低时延），DAI 流程适配翻倍的进程号空间。

这条演进线说明：HARQ-ACK 码本始终是 5G 物理层"**可靠性 ↔ 开销 ↔ 时延**"三角博弈的核心调节器。

## 九、实战连接：抓包识别与故障排查

### 抓包识别要点

在 Wireshark/TShark 中分析 5G L1/L2 抓包（如 gNB 内部信令跟踪、或 O-RAN CU/DU 接口抓取），识别 HARQ-ACK 链路：

1. **定位 DCI**：在 PDCCH 解码中找到 DCI 1_1，检查 **"Downlink assignment index"** 字段值，确认 counter/total DAI 的递进。
2. **关联 PDSCH**：通过 RNTI（C-RNTI）和 HARQ Process ID 把 DCI 与对应 PDSCH 配对。
3. **验证 K1**：确认 PDSCH slot + K1 = PUCCH slot，检查 DCI 的 "PDSCH-to-HARQ_feedback timing indicator" 字段是否落在 RRC 配置的候选集内。
4. **解码码本**：在 PUCCH 解码中提取 HARQ-ACK 比特串，按 Type-1/2 规则反推每个比特对应的 PDSCH。
5. **DAI 一致性检查**：人工核对终端上报的码本长度是否等于最后一个 T-DAI；不等则怀疑漏检或实现 Bug。

### 常见故障点

- **症状：基站频繁触发不必要的重传，吞吐量下降**
  排查：检查是否误用 Type-1 半静态码本导致大量占位 NACK 被解读为解码失败；或 Type-2 中 C-DAI 计数因跨载波调度顺序实现不一致而错位。

- **症状：CA 场景下特定 SCell 持续 NACK**
  排查：C-DAI 跳变检测逻辑 Bug——终端未在漏检位置插入占位，导致该 SCell 反馈错位到相邻比特。

- **症状：PUCCH Format 选择错误，反馈被截断**
  排查：码本比特数超过 Format 1 的 2 比特上限，但 RRC 未配置 Format 2 资源，导致反馈丢失。检查 `PUCCH-Config` 中 `format1`/`format2` 资源集。

- **症状：T-DAI 漏检后码本大小异常**
  排查：终端降级使用"最后有效 T-DAI"的逻辑是否正确实现；若直接用 C-DAI 最大值会导致码本偏小、尾部丢失。
## 常见问题

**Q1: Type 1 和 Type 2 到底该用哪个？**
A: 看调度密度与 CA 程度。调度稳定、密集、CA 较少 → Type 1（实现简单、鲁棒，且此时占位开销可接受）；CA 多、调度稀疏/突发、PUCCH 资源紧张 → Type 2（开销小）。Type 2 是 5G 的主流选择，但 Type 1 在特殊场景仍有价值。

**Q2: 为什么 DAI 只有 2 bit、还要模 4？**
A: 2 bit 是开销与表达力的折衷。模 4 循环配合 T-DAI 的总数指示，即使一轮分配超过 4 个也能正确还原。理论上若用更多 bit（如 LTE TDD 的 4 bit），开销翻倍而收益有限，因为 T-DAI 已提供总数封口。

**Q3: 如果终端连最后一个带 T-DAI 的 DCI 都漏检了怎么办？**
A: 终端使用**最后一个正确接收到的 T-DAI 值**作为码本大小上界（TS 38.213 §9.1.3 降级规则）。这是尽力而为——码本不会比真实值小（不会丢比特），至多多补几个 NACK 占位，基站据此重传，最终仍能收敛正确。

**Q4: DAI 和 HARQ Process ID 是一回事吗？**
A: 完全不同。HARQ Process ID（DCI 中 3-4 bit，R19 扩展支持 32 进程）标识的是**软缓冲区编号**，用于合并重传；DAI 标识的是**本轮码本内的反馈序号**，用于对齐反馈位置。一个码本里多个比特可对应不同 HARQ 进程，两者正交。

**Q5: Type 3 一次性码本什么时候触发？**
A: 由 DCI 1_1/1_2 的 one-shot HARQ-ACK request 字段显式触发。典型用于 multi-TRP 切换、URLLC 高可靠场景，或长期未反馈导致 gNB/UE 进程状态可能失步时，强制同步所有 HARQ 进程的当前 ACK/NACK 状态。

## 参考来源

1. [3GPP TS 38.213 V19.x.x — NR; Physical layer procedures for data](https://www.3gpp.org/dynareport?38213.htm) — §9.1 HARQ-ACK 传输时序、§9.1.2 Type 1 码本、§9.1.3 Type 2 码本、§9.1.4 Type 3 码本
2. [3GPP TS 38.212 — NR; Multiplexing and channel coding](https://www.3gpp.org/dynareport?38212.htm) — DCI format 1_0/1_1/1_2 中 Counter DAI / Total DAI 字段定义
3. [ShareTechnote — 5G/NR HARQ-ACK Codebook](https://www.sharetechnote.com/html/5G/5G_Harq_Codebook.html) — 码本构造图解与举例
4. [3GPP Explorer — DAI (Downlink Assignment Index) Glossary](https://3gpp-explorer.com/glossary/dai/) — DAI 机制原理与 Rel-15~19 变更记录（CR 列表）
5. [CafeTele — HARQ in 5G NR: Process, Codebook](https://www.cafetele.com/5g-harq/) — 16 进程、NDI、K1/K2、半静态 vs 动态码本总结

---
> 本文由「51学通信」技术团队出品。51学通信由通信行业18年老兵**爱卫生**（微信ID: gprshome201101）维护，聚焦5G/6G核心网、3GPP标准解读、通信前沿技术深度分析。
> 出版作品有《5G核心网原理与实践》《IMS原理与实践》等4本通信专业书籍。知识星球「51学通信」提供深度技术答疑和行业洞察。

> [!tip] 51学通信知识星球
> **星主：爱卫生**（微信：gprshome201101）——公众号「51学通信」创办者，19 年通信行业运维与讲师经验，著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书（各大电商平台有售）。
> **星球定位**：深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域，主打运维岗位与信令分析。
> **当前规模**：5200 篇主题 / 990 个回答，198 元/年。
> **加入福利**：加入星球后请加爱卫生微信，拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题，另有群内分享。
> **AI 加持**：51学通信知识星球已作为 skill 接入 WorkBuddy，使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例，做总结、找文档和 PPT 等场景全覆盖。

![51学通信知识星球海报-扫码加入](https://wiki.500nian.com/images/zhixingqiu_qrcode.png)
