5G/NR - Channel Coding

 

 

 

Channel Coding

I don't have any experties to explain the channel coding algorithm in detail not to mention of explanining it in plain / intuitive way. So this page will describe only on high level descriptions of NR channel coding as I learn more on this.

Channel coding is a fundamental part of wireless communication systems. It introduces redundancy to transmitted data to enable error detection and correction, which is crucial for reliable communication over noisy channels.

  • In LTE:
    • Turbo Coding and Convolutional Coding are predominantly used.
    • These techniques offer strong error correction but have limitations in terms of scalability and complexity for larger block sizes.
  • In NR
    • Polar Coding and LDPC (Low-Density Parity-Check) Coding replace Turbo and Convolutional Coding due to their better performance and efficiency for 5G use cases.
    • NR’s choice of Polar and LDPC coding ensures better performance for 5G applications, where large data blocks and high reliability are critical.
    • The modularity in UCI coding (Repetition, Simplex, Reed-Muller) reflects a tailored approach to different payload sizes.
    • While encoding in LDPC is complex, its advantages in decoding make it the preferred choice for high-throughput data channels.

Following is a list of details that we are going to go through in this note

Why NR needs two different codes

The obvious question when you first see the table below is why 3GPP did not simply pick the best code and use it everywhere. The answer is that "best" depends entirely on how many bits you are protecting, and NR has to protect two wildly different kinds of message.

  Control : BCH, DCI, UCI Data : DL-SCH, UL-SCH

Typical payload

tens of bits
(a DCI is roughly 40 to 60 bits)

thousands of bits
(up to 8448 per code block)

If it fails

The UE misses the whole scheduling opportunity, or worse, acts on a message meant for someone else

HARQ retransmits it. Annoying, not fatal

Retransmission available ?

No - one shot

Yes - HARQ with soft combining

Throughput demand

Trivial

Up to 20 Gbps

So the code must be

Excellent at short blocks, with a very low false alarm rate

Excellent at long blocks, and cheap to decode in parallel

Answer

Polar

LDPC

Those are genuinely opposite requirements. LDPC works by passing messages around a large sparse graph ; give it only 40 bits and there is no room for the graph to do anything useful. Polar works by recursively splitting a channel into reliable and unreliable sub-channels ; that mechanism is superb at short lengths but the decoder becomes expensive as the block grows. Neither code is "better". They are specialists, and NR uses each where it wins.

Channel Coding Algorithms in NR

Below is a breakdown of the channel coding algorithms used in NR for various data types, along with their characteristics:

Channel   Coding Algorithm Reference

BCH

Polar Coding

38.212 - 7.1.4

DCI

Polar Coding

38.212 - 7.3.3

UCI

12 or more bits

Polar Coding

38.212 - 6.3.1.3, 6.3.2.3

1 bit

Repetition Code

2 bit

Simplex Code

3 to 11 bits

Reed-Muller Code

DL-SCH

LDPC

38.212 - 7.2.4

UL-SCH

LDPC

38.212 - 6.2.4

Read the UCI rows downwards and you can see the whole design philosophy in one column : 1 bit, 2 bits, 3 to 11 bits, 12 and above, each with a different code. Nothing else in NR is subdivided this finely, and the reason is that UCI is sent constantly, on every ACK, so even a few wasted resource elements matter. There is a section on those four sizes further down.

Basically Polar Coding is to replace TBCC (Tail Bit Convolution Code) and LDPC is to replace Turbo Coding.

Turbo Coding is relatively simple at Encoding phase but complex in decoding phase. This complexity gets higher as the size of code block gets larger.

LDPC is not as simple as turbo coding at Encoding phase, but much simpler in decoding phase. So good for large block size.  

The encode / decode asymmetry, and why it decided everything

Those three sentences above are the heart of the matter, and they are worth unpacking, because on the face of it the trade looks like a bad one. Why would anyone accept a harder encoder to get an easier decoder?

The answer is that encoding and decoding are not equally hard jobs, and they do not happen equally often in the places that matter.

Turbo decoding is fundamentally sequential. A turbo decoder runs two soft-input soft-output decoders that pass information back and forth, iteration after iteration, and each iteration walks the trellis one step at a time. You cannot start step 5 before step 4 is finished. Doubling the block size roughly doubles the work, and the work is in a long chain that hardware cannot spread out.

LDPC decoding is fundamentally parallel. An LDPC decoder passes messages between check nodes and variable nodes on a sparse graph. Almost all of those messages are independent of each other within an iteration, so you can compute thousands of them at once. If you want more throughput, you build more compute units and they all work simultaneously.

Now put a number on it. LTE-Advanced aimed at roughly 1 Gbps. NR aims at up to 20 Gbps in the downlink. That is not an incremental step, and it is a decoder-side problem : the handset has to decode at that rate, in a phone, on a battery. A sequential decoder simply cannot be scaled there by making the clock faster - the clock is already as fast as it is going to get. It has to be scaled by doing many things at once, and that is exactly what LDPC allows and Turbo does not.

So the trade is a good one after all :

  Turbo    easy encode  ->  hard, serial decode      base station encodes, handset decodes
  LDPC     harder encode -> easy, parallel decode    base station encodes, handset decodes
                                                              ^
                                       the expensive half was moved off the
                                       device that can least afford it

The extra encoding cost lands on the base station, which is mains powered and rack mounted and does not care. The saving lands on the handset. That is the entire argument, and it is why LDPC won for data even though your sentence above is right that its encoder is the more complex one.

Below is the brief descriptions for each coding techniques

  • Polar Coding:
    • Purpose: Replaces TBCC (Tail-Biting Convolutional Code) used in LTE.
    • Advantages:
      • Highly efficient for small block sizes.
      • Performance improves as block size increases.
      • Excellent for control channels due to its capacity-achieving properties under successive cancellation decoding.
      • Applications: BCH, DCI, and certain UCI payloads.
  • Repetition Code:
    • Purpose: Simplest form of error correction, repeating each bit multiple times to ensure reliability.
    • Applications: UCI with minimal payload (e.g., 1 bit).
  • Simplex Code:
    • Purpose: A lightweight code for small payloads like 2 bits of UCI, providing moderate error correction capability.
  • Reed-Muller Code:
    • Purpose: Used for moderate payload sizes in UCI (3 to 11 bits).
    • Advantages: Balances simplicity and error correction performance.
  • LDPC Coding:
    • Purpose: Replaces Turbo Coding for data channels like DL-SCH and UL-SCH.
    • Advantages:
      • Complexity shifts towards encoding rather than decoding, making it scalable for high throughput and large block sizes.
      • Decoding is simpler and well-suited to hardware implementation.
      • Achieves near-Shannon limit performance with iterative decoding algorithms.

LDPC in detail : base graphs, lifting size, segmentation

The intuition first. An LDPC code is nothing more than a large set of parity checks, each of which involves only a handful of bits - that is what "low density" means. Picture a sudoku where every row, column and box must add up correctly. If you smudge one number you can recover it from the constraints that touch it. If you smudge several, you solve the easy constraints first, and each one you fix makes its neighbours easier. That is exactly what an LDPC decoder does : it passes confidence values around the graph, and certainty spreads outwards from the checks that were easiest to satisfy.

NR does not define one LDPC code. It defines two families. They are called base graphs, and everything else is generated from them :

  Base Graph 1 (BG1) Base Graph 2 (BG2)

Base matrix size

46 x 68

42 x 52

Systematic columns

22

10

Information bits K

22 x Z

10 x Z

Encoded bits N

66 x Z

50 x Z

Mother code rate

22/66 = 1/3

10/50 = 1/5

Max code block Kcb

8448 bits (Z = 384)

3840 bits (Z = 384)

Designed for

Large blocks, higher rates
roughly 1/3 to 8/9

Small blocks, lower rates
roughly 1/5 to 2/3

Which base graph is used is not a choice - it falls out of the transport block size A and the target code rate R by a fixed rule :

  use BG2  if   A <= 292
             or   A <= 3824  and  R <= 0.67
             or   R <= 0.25

  otherwise use BG1

Read it as three ways of saying "this is a small or heavily protected transmission". A tiny payload, a modest payload sent carefully, or anything sent at a very low rate - all of those go to BG2, which starts from a 1/5 mother code and therefore has more raw redundancy to give away. Everything else goes to BG1.

The lifting size Z is what turns a fixed base graph into a code of the size you actually need. Each entry of the base matrix is expanded into a Z x Z block - either all zeros, or an identity matrix rotated by some amount. That structure is called quasi-cyclic, and it is the reason the decoder parallelises so well : all Z rows of a block can be processed at the same time.

Z is not free either. It must come from a fixed list, which is every value of the form a x 2j with a in {2, 3, 5, 7, 9, 11, 13, 15}, up to 384 :

    2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  18  20  22  24  26
   28  30  32  36  40  44  48  52  56  60  64  72  80  88  96 104 112 120 128 144
  160 176 192 208 224 240 256 288 320 352 384

  the UE and gNB both pick the smallest Z in this list that is big enough
  to hold the payload, and any leftover room is stuffed with filler bits

Code block segmentation handles the case where the transport block simply will not fit. If B (the transport block plus its CRC) exceeds Kcb, it is chopped into C equal pieces and each piece gets its own 24 bit CRC. That per-block CRC is not decoration : it lets the receiver tell the transmitter which specific code blocks failed, which is what makes code block group based HARQ retransmission possible instead of resending the whole transport block.

Rate matching is the last step and the one that actually sets the code rate you see in a log. The encoder always produces the mother code - 1/3 or 1/5 - and then a circular buffer is read out to produce exactly as many bits as the allocated resources can carry. If fewer bits are needed than the mother code produced, the surplus is punctured away. If more are needed, the buffer wraps and bits repeat.

The starting point in that buffer is the redundancy version, and NR transmits them in the order RV 0, 2, 3, 1. RV 0 starts at the systematic bits, so a first transmission is self-decodable on its own ; the later RVs start further round the buffer and carry mostly fresh parity, which is what makes HARQ soft combining gain real rather than just a repeat.

Polar in detail : polarization, frozen bits, CRC-aided list decoding

The intuition. Polar coding starts from a strange and rather beautiful observation. Take two identical noisy channels and combine them in a particular way, and you no longer have two equally mediocre channels - you have one slightly better and one slightly worse. Do that recursively over N = 2n channels and they separate almost completely : some become nearly perfect, the rest become nearly useless. That separation is called channel polarization.

Once the channels have polarized, the coding scheme writes itself :

   N sub-channels after polarization, sorted by reliability

   nearly perfect  ############################  put the information bits here
                   ####################
                   ############
                   ######
   nearly useless  ##                            set these to a fixed 0 : frozen bits

   both the transmitter and receiver know exactly which are which,
   so the receiver already knows the value of every frozen bit before
   it starts - and that knowledge is what makes the decoding work

The set of frozen positions is not computed on the fly. 38.212 carries a single reliability sequence of 1024 entries, ordered from least to most reliable, and it is nested - the sequence for a shorter code is just the front of the same list. One table serves every block length, which is a large part of why polar coding was practical enough to standardise.

  Downlink : PBCH, PDCCH Uplink : UCI on PUCCH / PUSCH

Maximum mother code length N

512  (n max = 9)

1024  (n max = 10)

CRC attached

CRC24C

CRC6 for 12-19 bits
CRC11 for 20 bits and above

CRC-aided list decoding is the part worth understanding, because it changes what a CRC is for. A plain polar decoder decides each bit in turn, and one early wrong decision poisons everything after it. A list decoder instead keeps L candidate paths alive at once - typically 8 - and carries them all to the end. At that point it has 8 complete candidate messages and must choose one.

It chooses using the CRC. Whichever candidate has a valid CRC is the answer. So in NR the CRC is doing two jobs at once :

  • its traditional job, telling the receiver whether the result is trustworthy
  • and a new one, steering the decoder to the right path out of the list

This is also why the DCI CRC grew from 16 bits in LTE to 24 bits in NR. With 8 candidates all being tested against the CRC, the chance that a wrong one passes by accident is roughly 8 times higher than with a single candidate. A larger CRC buys that risk back. It matters enormously for PDCCH, because the UE blind decodes dozens of candidate locations every slot - and a false CRC pass means acting on a grant that was never meant for it.

NOTE : this is where the RNTI lives too. The DCI CRC is scrambled with the RNTI before transmission, so "the CRC checks out after unscrambling with my RNTI" simultaneously proves that the decode was correct and that the message was addressed to this UE. Two answers from one 24 bit field.

The small UCI codes, and why the sizes split where they do

The four-way split in your table looks arbitrary until you notice that each boundary is the point where the previous scheme stops being the cheapest way to do the job.

UCI size Code Why this one

1 bit

Repetition

There are only two possible messages. There is no structure to exploit, so the only thing you can do with extra resources is say it again, louder. Anything cleverer would cost more than it returns

2 bits

Simplex

Four possible messages. The (3,2) simplex code sends the two bits plus their XOR, which spreads the four codewords as far apart as three bits allow. Maximum separation for minimum cost

3 to 11 bits

Reed-Muller

The (32, A) block code : whatever A is in this range, the output is 32 bits built from a fixed set of 11 basis sequences. It has an extremely fast decoder based on the Hadamard transform, and no iteration at all

12 bits and up

Polar

Past about 11 bits the Reed-Muller construction runs out of basis sequences, and the payload is finally long enough for polarization to pay for its more complex decoder

So the boundaries are not arbitrary at all. Each one marks the size at which a simpler scheme stops being good enough or a more complex one starts being worth it. And the reason this is worth optimising so carefully is volume : a HARQ ACK is one or two bits and it is sent constantly, so a scheme that wastes a few resource elements per ACK wastes them millions of times a second across a cell.

The whole chain, end to end

It helps to see where the coding actually sits, because in a log you never see "LDPC" - you see a transport block size and a code rate, and those are the two ends of this pipeline.

  DL-SCH / UL-SCH  (data)          DCI  (control)

   transport block, A bits                 DCI payload
              |                                 |
              v                                 v
  +-----------------------+        +------------------------+
  | TB CRC attach         |        | CRC24C attach, then    |
  | CRC24A if A > 3824    |        | scramble the CRC       |
  | CRC16   otherwise     |        | with the RNTI          |
  +-----------------------+        +------------------------+
              |  B = A + L                      |
              v                                 v
  +-----------------------+        +------------------------+
  | code block            |        | Polar encode           |
  | segmentation          |        | N = 2^n,  n <= 9  DL   |
  | if B > K_cb, split    |        |           n <= 10 UL   |
  | and add CRC24B each   |        +------------------------+
  +-----------------------+                     |
              |                                 v
              v                    +------------------------+
  +-----------------------+        | rate matching          |
  | LDPC encode           |        | puncture / shorten /   |
  | BG1: K=22Z, N=66Z     |        | repeat                 |
  | BG2: K=10Z, N=50Z     |        +------------------------+
  +-----------------------+                     |
              |                                 v
              v                               PDCCH
  +-----------------------+
  | rate matching         |  <-- the code rate you see in a log
  | circular buffer       |      is set HERE, not by the encoder
  | RV 0 -> 2 -> 3 -> 1   |
  +-----------------------+
              |
              v
  code block concatenation
              |
              v
         scrambling
              |
              v
         modulation
              |
              v
            PDSCH

The single most useful thing on that diagram is the arrow. The encoder always produces the mother code rate, 1/3 or 1/5, no matter what. When a log says cr=0.87, that number was produced by the rate matcher throwing away most of the parity the encoder generated. The encoder never knew about it.

A worked example with real numbers

Take a transport block of 8000 bits sent at about half rate, and follow it through :

  A = 8000 bits, target R = 0.5

  1. CRC          A = 8000 > 3824   ->  CRC24A  ->  L = 24
                    B = 8000 + 24 = 8024 bits

  2. base graph   A > 3824, R = 0.5 > 0.25   ->  BG1

  3. segmentation BG1 -> K_cb = 8448
                    B = 8024 <= 8448   ->  no segmentation, C = 1

  4. lifting size BG1 -> K_b = 22
                    need  22 x Z >= 8024   ->  Z >= 364.7
                    smallest allowed Z in the list  ->  Z = 384

  5. encode       K = 22 x 384 = 8448     filler bits = 8448 - 8024 = 424
                    N = 66 x 384 = 25344    mother rate = 8448/25344 = 1/3

  6. rate matching read ~16050 bits out of the circular buffer starting at RV0
                    -> delivered code rate lands near the requested 0.5

Two things in that trace are worth keeping. The 424 filler bits are pure overhead - the payload did not divide neatly into the lifting size, so the difference is padded and thrown away at the receiver. And the encoder produced 25344 bits of which only about 16000 were actually sent ; the rest were punctured. Both are normal, and both are invisible in a log.

A small payload goes the other way :

  A = 500 bits, target R = 0.4

  CRC        A = 500 <= 3824   ->  CRC16  ->  B = 516 bits
  base graph A <= 3824 and R = 0.4 <= 0.67   ->  BG2
  K_b        BG2 with 192 < B <= 560   ->  K_b = 8   (BG2 varies K_b with size)
  lifting    8 x Z >= 516  ->  Z >= 64.5  ->  Z = 72
  encode     K = 10 x 72 = 720, N = 50 x 72 = 3600, mother rate = 1/5

NOTE : notice the trap in that second example. Kb is 8 here, but K is still 10 x Z. Kb is used only to choose the lifting size ; it never appears in the size of the encoded block. For BG1, Kb is always 22 and the distinction never comes up, which is exactly why it catches people out the first time they meet BG2.

Which CRC is used where

NR uses six different CRCs and it is easy to lose track of them. They are all in 38.212 clause 5.1 :

CRC Used for Note

CRC24A

Transport block CRC for DL-SCH / UL-SCH when A > 3824

The end-to-end check on the whole transport block

CRC16

Transport block CRC when A <= 3824

A short block does not warrant 24 bits of overhead

CRC24B

Per code block, after segmentation

Lets the receiver report which block failed - the basis of code block group HARQ

CRC24C

DCI, and BCH / PBCH

Doubles as the list-decoder selector, and for DCI it is scrambled with the RNTI

CRC11

UCI of 20 bits or more

Smaller, because UCI overhead is charged on every ACK

CRC6

UCI of 12 to 19 bits

Smaller still, for the same reason

The pattern is consistent : the CRC gets smaller as the payload gets smaller, because the CRC is pure overhead and a fixed 24 bits on a 12 bit message would more than double what you are sending. UCI below 12 bits gets no CRC at all - the Reed-Muller, simplex and repetition codes handle detection themselves.

Comparison with LTE Channel Coding

The evolution from LTE to NR (New Radio) brought significant advancements in channel coding techniques to meet the demanding requirements of 5G networks. In LTE, Turbo Coding and Convolutional Coding were predominantly used, offering good performance for error correction but facing limitations in scalability and complexity, particularly with larger block sizes. NR introduces Polar Coding and LDPC (Low-Density Parity-Check) Coding as replacements, ensuring improved efficiency, scalability, and performance. Polar Coding excels in small block sizes, making it ideal for control channels, while LDPC is optimized for large data blocks, providing near-optimal error correction with lower decoding complexity. This shift highlights the enhanced adaptability and robustness of NR coding schemes compared to those in LTE.

Aspect LTE (Turbo/Convolutional) NR (Polar/LDPC)

Encoding Complexity

Low

Moderate (Polar) / High (LDPC)

Decoding Complexity

High (Turbo)

Low (LDPC)

Performance

Good for small block sizes

Excellent for large block sizes

Scalability

Limited

Highly scalable

Max code block size

6144 bits (Turbo)

8448 bits (BG1) / 3840 (BG2)

Control channel code

TBCC, CRC16 on PDCCH

Polar, CRC24C on PDCCH

Decoder parallelism

Serial - a trellis walked step by step

Parallel - a sparse graph solved all at once

If you take one thing from this page, make it the last row. Everything else - the base graphs, the lifting sizes, the frozen bits - is machinery in service of that one property. NR needed a code the receiver could decode in parallel, because 20 Gbps in a handset is not reachable any other way, and LDPC and Polar were the two codes that could deliver it at their respective block sizes.

 

Reference

  • 5G NR - A New Era for Enhanced Mobile Broadband (Mediatek)
  • 3GPP TS 38.212 - Multiplexing and channel coding
  • 3GPP TS 38.212 clause 5.1 - CRC calculation
  • 3GPP TS 38.212 clause 5.2.2 / 5.3.2 - LDPC base graphs, lifting sizes, segmentation
  • 3GPP TS 38.212 clause 5.3.1 - Polar coding and the reliability sequence
  • 3GPP TS 38.212 clause 5.3.3 - Small block lengths (repetition, simplex, Reed-Muller)
  • 3GPP TS 38.214 - Physical layer procedures for data (base graph and rate selection)