1. 从一条调度日志说起:PDSCH时域资源到底在调什么
第一次接触5G NR物理层调度的人,大概率会被一堆缩写砸晕:PDSCH、PDCCH、CORESET、SLIV、K0、TDRA……我当年啃这块的时候,最大的困惑不是“这些词是什么意思”,而是“基站到底在什么时刻、把哪些符号分给了哪个用户”。后来真正上手做链路级仿真和调度算法验证,才慢慢把这条链路串起来。
PDSCH,物理下行共享信道,是5G里真正承载用户数据的“货车”。而“时域资源调度”,说白了就是回答一个问题:这辆货车在哪个时间窗口出发、走多久、中间有没有停靠。它不像频域调度那样直观地体现在RB分配上,时域调度藏在DCI里的一串索引背后,稍不留神就会算错起始符号,导致整个slot的数据解不出来。
这篇内容适合三类人看:一是刚入行做物理层协议栈或调度算法的新人,二是做链路级/系统级仿真需要配置TDRA表的工程师,三是想搞懂DCI里那个“时域资源分配”字段到底怎么映射到实际符号的进阶学习者。我会从设计思路讲到参数计算,再到实操配置和踩坑记录,尽量把这条链路讲透。核心关键词就几个:PDSCH、时域资源分配、SLIV、K0、TDRA表、起始符号与长度指示。
先说结论性的认知:NR的时域调度比LTE灵活得多,但灵活的代价是配置复杂度陡增。LTE里PDSCH基本固定在某个子帧的特定符号开始,NR则允许在一个slot内从任意符号起步、持续任意合法长度,还能跨slot调度。这种灵活性是为URLLC、eMBB等混合业务场景服务的,但也意味着你必须把TDRA表、K0取值、SLIV计算这三件事吃透,否则调出来的资源要么重叠要么浪费。
2. 时域资源分配的整体设计思路拆解
2.1 为什么NR要把时域调度做得这么灵活
LTE时代的PDSCH时域位置相对固定,控制区域占前1到3个符号,数据从第4个符号左右开始,长度基本填满剩余符号。这种设计简单,但应对不了5G的多样化需求。NR要同时支持增强移动宽带、超可靠低时延、海量机器通信三类场景,它们的包大小、时延要求、调度周期完全不同。如果时域位置写死,URLLC的小包就得被迫占用整个slot,浪费严重。
所以NR引入了“起始符号+持续长度”的二维指示方式,用SLIV这一个数值同时编码起点和长度。再配合K0这个“调度偏移”,让DCI可以在slot n调度slot n+K0的PDSCH,给终端留出解码DCI和准备接收的时间。这套机制的本质,是把时域资源的决定权从固定帧结构转移到动态信令上,调度器可以根据业务实时调整。
2.2 TDRA表:时域调度的“菜单”
基站不会在DCI里直接传起始符号和长度,而是维护一张TDRA表,表里每一行是一个合法的时域资源分配方案,DCI里只传行索引。这张表通过RRC信令配置给终端,最多16行。终端收到索引后查表,得到K0、起始符号、长度、映射类型这几个参数。
为什么用表而不是直接传值?两个原因。一是压缩DCI开销,索引4比特就能表示16种方案,比直接传起始符号加长度省比特。二是保证合法性,表里配置的方案都是基站和终端都认可的,避免出现非法组合。这张表的设计直接决定了调度的灵活度和信令开销的平衡,是时域调度里最需要花心思的地方。
2.3 映射类型A与类型B的分工
NR定义了两种PDSCH映射类型。类型A的起始符号固定在slot的前几个符号(通常是0到3),持续长度较长,适合eMBB的大包传输,能充分利用slot资源。类型B的起始符号可以是slot内任意符号,长度也灵活,适合URLLC的小包和低时延场景,可以插空传输。
这两种类型不是随便选的,它们对应不同的解调参考信号配置和前置DMRS位置。类型A的DMRS位置相对固定,类型B则更灵活。实际配置TDRA表时,往往两种类型都配几行,让调度器根据业务类型选择。我见过一些实现只配类型A,结果URLLC业务时延压不下来,后来补了类型B的行才达标。
3. 核心参数解析与SLIV计算实操
3.1 K0:调度偏移的取值逻辑
K0表示DCI所在的slot与PDSCH所在slot之间的偏移,单位是slot。K0等于0表示同一个slot内调度,等于1表示下一个slot。取值集合由RRC配置,范围通常是0到32,但实际常用的是0、1、2这几个值。
K0的选择直接影响时延和调度灵活性。K0等于0时延最低,但要求终端在收到DCI后立刻解码并准备接收,对终端处理能力要求高。K0等于1给终端留了一个slot的处理时间,是eMBB的常见选择。K0大于1一般用于跨slot调度或者给终端更长的准备时间。需要注意的是,K0的取值必须和终端能力匹配,能力低的终端不支持太小的K0。
3.2 SLIV的计算过程:把起点和长度压成一个数
SLIV是时域资源分配的核心编码。假设起始符号为S,持续长度为L,一个slot有14个符号。SLIV的计算分两种情况:
当(L-1)小于等于7时: SLIV = 14 × (L - 1) + S
当(L-1)大于7时: SLIV = 14 × (14 - L + 1) + (14 - 1 - S)
这个公式的设计目的是让SLIV值唯一对应一组(S, L),且取值范围在0到104之间。为什么分两种情况?因为要保证SLIV能唯一解码,同时让常用的小长度方案占据较小的SLIV值,便于索引。
我举个实际例子。假设S等于2,L等于12。因为L-1等于11大于7,用第二个公式:SLIV = 14 × (14 - 12 + 1) + (14 - 1 - 2) = 14 × 3 + 11 = 53。终端收到53后,反解出S和L,就知道从符号2开始持续12个符号。
反解的过程也要掌握。给定SLIV,先判断它落在哪个区间。如果SLIV小于14×7等于98,说明是第一类,L-1等于SLIV除以14取整,S等于SLIV对14取余。否则是第二类,需要反向计算。这个反解逻辑在终端侧实现里是必写的,写错了就会解错资源位置。
3.3 起始符号与长度的合法组合
不是所有(S, L)组合都合法。NR规定了一些约束:S加L不能超过14(对于类型A和类型B在slot内的分配),某些S和L的组合因为和PDCCH控制区域冲突而被排除。比如类型A的S通常限制在0到3,L限制在3到14。类型B的S可以是0到13,L是1到14,但要满足S+L小于等于14。
配置TDRA表时,这些约束必须遵守,否则终端会认为配置非法。我建议在生成表的时候写一个校验函数,把所有非法组合过滤掉,避免手工配置出错。下面是一个简单的Python校验示例:
def is_valid_sliv(S, L, mapping_type='A'): if S + L > 14: return False if mapping_type == 'A': if S > 3 or L < 3: return False elif mapping_type == 'B': if L < 1: return False return True def compute_sliv(S, L): if L - 1 <= 7: return 14 * (L - 1) + S else: return 14 * (14 - L + 1) + (14 - 1 - S)这段代码可以直接用来生成合法的SLIV集合,再从中挑选需要的行填入TDRA表。
4. 实操过程:从TDRA表配置到DCI解析
4.1 配置一张实用的TDRA表
假设我们要为一个支持eMBB和URLLC混合业务的终端配置TDRA表。我的思路是:前几行配类型A的长资源给eMBB,中间配类型B的短资源给URLLC,最后留几行做跨slot调度。
具体配置如下表所示:
| 索引 | K0 | 映射类型 | 起始符号S | 长度L | SLIV |
|---|---|---|---|---|---|
| 0 | 0 | A | 0 | 14 | 0 |
| 1 | 0 | A | 0 | 12 | 14 |
| 2 | 0 | A | 0 | 10 | 28 |
| 3 | 0 | B | 2 | 4 | 30 |
| 4 | 0 | B | 6 | 4 | 62 |
| 5 | 0 | B | 10 | 4 | 90 |
| 6 | 1 | A | 0 | 14 | 0 |
| 7 | 2 | A | 0 | 14 | 0 |
这张表里,索引0到2是类型A的长资源,适合大包。索引3到5是类型B的短资源,起始符号分散在slot不同位置,适合小包插空。索引6和7是K0等于1和2的跨slot调度,给终端更多处理时间。注意索引0和6、7的SLIV相同,但K0不同,这是允许的,因为K0是独立参数。
配置这张表的时候有个细节:类型B的短资源起始符号要避开PDCCH占用的符号。如果PDCCH占了前2个符号,类型B的S就不能是0和1,否则会和控制区域冲突。这个约束在协议里有明确规定,配置时务必检查。
4.2 DCI中的时域资源分配字段解析
DCI格式1_0和1_1里都有“时域资源分配”字段,宽度由TDRA表的行数决定。如果表有8行,字段就是3比特;有16行就是4比特。终端收到DCI后,取出这个字段的值作为索引,去TDRA表里查对应的行,得到K0、映射类型、S和L。
这里有个容易踩的坑:DCI格式1_0的时域资源分配字段是固定4比特,但实际使用的表可能只有8行,多出来的比特怎么处理?协议规定,如果字段值超过了表的行数,终端要按默认表处理或者视为错误。实现时要注意这个边界条件,别直接拿字段值去索引数组导致越界。
解析出S和L后,还要结合K0确定PDSCH所在的slot。假设DCI在slot n收到,K0等于1,S等于2,L等于12,那么PDSCH就在slot n+1的符号2到符号13。终端据此配置接收窗口,开始解调。
4.3 一个完整的调度时序示例
假设当前slot编号为100,基站要在slot 100发送DCI,调度slot 101的PDSCH。选择TDRA表索引6,对应K0等于1,类型A,S等于0,L等于14。
时序是这样的:slot 100的PDCCH区域发送DCI,终端在slot 100内解码DCI,得到索引6,查表得知PDSCH在slot 101的符号0到13。终端在slot 100结束后切换到接收状态,在slot 101的符号0开始接收PDSCH,持续14个符号。
这个过程中,终端从收到DCI到开始接收PDSCH有一个slot的准备时间,这就是K0等于1的意义。如果K0等于0,终端必须在同一个slot内完成DCI解码和PDSCH接收准备,对处理能力要求高得多。实际部署中,K0等于0通常只用于能力强的终端或者对时延极敏感的业务。
5. 常见问题与排查技巧实录
5.1 SLIV计算错误的典型表现
SLIV算错是最常见的问题,表现是终端解出的PDSCH位置和基站预期不一致,导致解码失败。我遇到过几次,总结下来原因有几类。
第一类是公式用反了。有人把两种情况的条件判断写错,L-1小于等于7用了第二个公式,结果SLIV值完全不对。排查方法是拿几组已知的(S, L)手动算一遍,和代码输出对比。
第二类是边界值处理错误。比如L等于8时,L-1等于7,应该用第一个公式,但有人误以为大于7才用第二个,结果L等于8走了错误分支。这个边界要特别注意,条件是小于等于7,不是小于7。
第三类是反解时没考虑SLIV的合法范围。SLIV最大是104,如果收到的值大于104,说明配置或解析有问题,要报错而不是硬算。
5.2 K0与终端能力不匹配导致的问题
K0配置得太小,终端处理不过来,会直接导致PDSCH接收失败。这种情况在测试早期很常见,尤其是用低能力终端测URLLC场景时。表现是终端上报解码失败,但基站侧看资源分配没问题。
排查思路是查终端能力信令,看它支持的K0最小值是多少。如果配置的K0小于终端能力,就要调整TDRA表,把K0改大。另一个办法是给不同能力的终端配不同的TDRA表,能力强的用小K0,能力弱的用大K0。这个在RRC配置里是可以做到的,按终端能力分组配置。
5.3 TDRA表行数超限与索引越界
TDRA表最多16行,超过16行配置会失败。我见过有人想配20行,结果RRC信令被拒。解决办法是精简表,把不常用的方案删掉,或者用多个表切换。
索引越界是另一个问题。DCI字段宽度和表行数不匹配时,可能出现字段值大于表行数的情况。比如表只有8行,但DCI字段是4比特,能表示0到15。如果基站发了索引10,终端查表就会越界。协议规定这种情况终端要按默认表处理,但实现时最好在基站侧就保证不发越界索引,避免依赖终端行为。
5.4 常见问题速查表
| 问题现象 | 可能原因 | 排查方法 | 解决措施 |
|---|---|---|---|
| PDSCH解码失败,资源位置不对 | SLIV计算错误 | 手动验算SLIV | 检查公式条件和边界 |
| 终端上报解码失败但资源正常 | K0小于终端能力 | 查终端能力信令 | 调整K0或按能力分表 |
| RRC配置被拒 | TDRA表超过16行 | 检查表行数 | 精简表或分多表 |
| 索引越界 | DCI字段宽度与表行数不匹配 | 检查字段宽度 | 基站侧限制索引范围 |
| 类型B资源与控制区域冲突 | S取值未避开PDCCH符号 | 检查S取值范围 | 调整S避开控制区域 |
5.5 几个实操中总结的小技巧
第一个技巧是建一个SLIV速查表,把常用的(S, L)组合和对应SLIV列出来,配置时直接查,避免每次手算。我习惯把S从0到13、L从1到14的所有合法组合都算出来,存成CSV,配置时筛选。
第二个技巧是TDRA表配置完后,写一个反向验证脚本,把表里每一行的SLIV反解回(S, L),确认和配置值一致。这个能抓出大部分手工配置错误。
第三个技巧是测试时先用K0等于1的保守配置跑通,再逐步尝试K0等于0,这样能快速定位是K0问题还是其他问题。一上来就用K0等于0,出了问题很难判断是终端能力不足还是SLIV算错。
第四个技巧是关注映射类型和DMRS配置的配合。类型A和类型B的前置DMRS位置不同,如果TDRA表里配了类型B但DMRS配置还是按类型A来的,解调性能会下降。这两者要一起配置,不能分开调。
6. 时域调度与频域调度的配合要点
时域资源确定后,频域资源分配才有着落。DCI里的频域资源分配字段和时域字段是配合使用的,两者共同确定PDSCH的RE位置。时域调度决定了哪些符号可用,频域调度决定这些符号里哪些RB可用。
实际调度时,时域和频域的粒度要匹配。比如时域用了类型B的短资源,只有4个符号,频域就不宜分配太多RB,否则单符号上的RE数不够,解调性能差。反过来,类型A的长资源可以配更多RB,充分利用时域长度。
还有一个配合点是跨slot调度时的频域跳频。如果K0大于0且开启了频域跳频,PDSCH在两个slot的频域位置可以不同,这需要在TDRA表和频域资源配置里同时体现。配置复杂但能提升抗干扰性能,适合信道条件差的场景。
7. 仿真验证中的参数配置经验
做链路级仿真时,TDRA表的配置直接影响仿真结果的真实性。我的经验是不要只用一张表跑所有场景,而是按业务类型分表。eMBB场景用类型A为主,URLLC场景用类型B为主,混合场景两种都配。
K0的配置也要和仿真目标匹配。如果仿真关注吞吐量,K0等于1比较合适,给终端足够处理时间又不至于浪费slot。如果关注时延,K0等于0才能测出最低时延,但要相应调整终端处理能力模型。
SLIV的分布也要注意。如果TDRA表里大部分行的SLIV集中在少数几个值,仿真出来的资源分配会不均衡。建议表里的SLIV覆盖不同的起始符号和长度组合,让仿真更接近真实调度的多样性。
验证时域调度是否正确,最直接的方法是抓DCI和PDSCH的时序图,看PDSCH的实际起始符号和长度是否和TDRA表配置一致。我习惯在仿真里加一个断言,每次调度后检查实际资源位置和预期是否匹配,不匹配就报错。这个断言帮我抓出过好几次SLIV反解的错误。
8. 写在最后的几点个人体会
时域资源调度这块,协议文本读起来枯燥,但真正理解了SLIV的设计逻辑和K0的作用,会发现整套机制其实很精巧。它用一张表和几个参数,就把灵活的时域分配压缩进了DCI的少量比特里,这是NR设计里很典型的一种“用配置换灵活”的思路。
我在实际配置中最深的体会是:不要孤立地看时域调度。它和频域分配、DMRS配置、终端能力、业务类型都绑在一起,任何一个环节没对齐,都会表现为PDSCH解码失败。排查问题时要从整条链路看,而不是只盯着SLIV算得对不对。
另外,TDRA表的设计没有标准答案,要根据实际业务分布来调。我见过一张表打天下的做法,也见过按业务精细分表的做法,后者效果明显更好,但配置和维护成本高。怎么平衡,取决于你的场景和资源。如果拿不准,先从一张覆盖主要场景的表开始,跑起来再根据实测数据优化,比一开始就追求完美配置要务实得多。