Bitácora · FEC · Concepto

El decodificador BCH en hardware

Unidad 6 · Los códigos BCH · las tres métricas, la aritmética de GF(2ᵐ) hecha compuertas, el síndrome con constantes, el iterativo sin inversiones, Chien en paralelo, la cascada segmentada y el (255,239) dimensionado

El decodificador de un código BCH binario hace tres cosas en fila: calcula los síndromes, encuentra el polinomio localizador y busca sus raíces. Los algoritmos dicen qué se calcula. Acá va cómo se lo arma en un circuito, cuánto cuesta cada pieza y qué decide cuántos bits por segundo pasan. Casi todo el trabajo se empuja hacia las operaciones baratas de GF(2ᵐ), y el régimen lo pone la etapa más lenta de una cascada, no la suma de las tres.

01 · Tres métricas

Para comparar dos circuitos hay que decir en qué. Alcanzan tres magnitudes.

El camino crítico es el mayor retardo combinacional entre dos registros. Fija el período mínimo del reloj: el circuito no anda más rápido que su camino más lento.

La latencia es la cantidad de ciclos entre que entra un dato y está disponible el resultado que le corresponde.

El régimen binario, o throughput, es la cantidad de bits procesados por unidad de tiempo. Si el circuito acepta J bits por ciclo y el reloj tiene frecuencia f, el régimen es

régimen=Jf

Las tres son independientes. Un circuito puede tener latencia alta y régimen alto a la vez, si tiene muchos bloques adentro, cada uno en una etapa distinta. Y cortar el camino crítico con un registro en el medio sube la latencia un ciclo sin cambiar el régimen, salvo que se lo aproveche para subir f.

Hay una restricción que vuelve en cada etapa: un lazo de realimentación no se segmenta. En un registro de desplazamiento realimentado el estado nuevo es función del viejo, s′=F(s,u). Un registro metido adentro del lazo hace que el estado dependa del de dos ciclos atrás: el circuito pasa a calcular otra recurrencia, no la misma con un ciclo de demora. Para que un lazo procese más, la única salida es replicar la lógica y avanzar J pasos por ciclo.

02 · La aritmética de GF(2ᵐ), hecha compuertas

Las cuatro operaciones del cuerpo cuestan muy distinto en un circuito, y toda la ingeniería del decodificador consiste en correr el trabajo hacia las baratas. Un elemento es un vector de m bits (a0,…,am−1), con ai el coeficiente de αi. Los ejemplos van en GF(2⁴) con p(X)=1+X+X4, donde α4=1+α.

El sumador es un banco de m compuertas XOR independientes, una por bit. Retardo de una compuerta y nada que propagar: en característica dos no hay acarreo.

El multiplicador por constante. Multiplicar por un αj fijo en el diseño es lineal en los bits de entrada: es una matriz binaria de m×m, y se realiza con XOR cableadas, sin una sola AND ni un registro. Por α, en GF(2⁴),

aα=a3+(a0+a3)α+a1α2+a2α3

es un desplazamiento con una sola realimentación: una XOR. Por α3 se escribe aα3=a0α3+a1α4+a2α5+a3α6, se reemplaza α4=1+α, α5=α+α2, α6=α2+α3, y se junta por potencia:

(b0b1b2b3)=(0100011000111001)(a0a1a2a3),b=aα3

Tres filas con dos unos: tres XOR. El costo depende del peso de la matriz, no de m. En GF(2⁸), recorridas las 255 constantes no nulas, va de 0 XOR, la del uno, a 39, y el promedio es 24,1; la de α cuesta 3.

El elevador al cuadrado. Paso 1: en característica dos, (a+b)2=a2+b2, porque el término cruzado aparece dos veces y se cancela. Paso 2: entonces a2=∑aiα2i, con ai2=ai porque cada ai es un bit. Paso 3: se reducen las potencias que pasan de m−1. En GF(2⁴), a2=a0+a1α2+a2α4+a3α6, y con α4=1+α y α6=α2+α3:

a2=(a0+a2)+a2α+(a1+a3)α2+a3α3

Dos XOR. El cuadrado es una matriz fija, igual que el producto por constante, y no un multiplicador: en GF(2⁸) cuesta 12 XOR. Esa asimetría es la que vuelve casi gratis la relación S2i=Si2, y es la misma operación que recorre las clases conjugadas.

El multiplicador general. Con los dos factores variables, la cosa cambia. La realización serie sigue el esquema de Horner sobre los bits ci de γ:

βγ=(⋯((cm−1β)α+cm−2β)α+⋯)α+c0β

Son m ciclos, un acumulador de m bits, m compuertas AND que forman ciβ y un multiplicador por α. La realización paralela desdobla lo mismo en lógica combinacional: m2 AND para los productos parciales, XOR para sumarlos y reducir, y un camino crítico del orden de logm compuertas. Contadas sin compartir sumas: en GF(2⁴), 16 AND y 15 XOR, 9 del producto de polinomios y 6 de la reducción; en GF(2⁸), 64 AND y 77 XOR, 49 más 28.

El inversor es la operación cara. Por tabla, es una memoria de 2m palabras de m bits: 2048 bits en GF(2⁸), 10 240 en GF(2¹⁰). Por el pequeño teorema de Fermat, a−1=a2m−2, y como 2m−2=2+4+⋯+2m−1, se arma con m−1 cuadrados y m−2 productos generales. En GF(2⁸) son siete elevadores y seis multiplicadores: 546 XOR y 384 AND, con una latencia que crece con m. De las dos formas es el bloque más caro del decodificador, y de ahí el interés de las formulaciones que no lo usan.

03 · El síndrome, con multiplicadores por constante

El síndrome Si es r(αi), y se evalúa por Horner a medida que entra la palabra, empezando por rn−1:

r(αi)=(⋯(rn−1αi+rn−2)αi+⋯)αi+r0

Una celda por síndrome: un registro de m bits, un multiplicador por αi y un sumador. En un BCH binario la celda se abarata dos veces. El multiplicador es por constante, porque αi lo fija el diseño: es cableado. Y el sumando rj es un bit, no un elemento del cuerpo: la suma es una sola XOR, sobre el bit de α0.

Además alcanza con las t celdas de índice impar. Los pares salen de S2i=Si2 con elevadores al cuadrado, que son lógica fija. El circuito entero son t celdas y t elevadores.

Para tragar J bits por ciclo se desarrolla Horner J pasos de una vez. Si en un ciclo entran las posiciones c+J−1 a c,

Si←SiαJi+∑j=0J−1rc+jαij

Los productos rc+jαij no cuestan nada: son el bit rc+j llevado con cables a las posiciones donde αij tiene un uno. Lo que cuesta es sumar esos J vectores y el único multiplicador por αJi. Contado en el (255,239) con J=32: la celda de S1 son 82 XOR para la suma de entrada, 22 para el producto por α32 y 8 para sumar al registro, 112 en total; la de S3, 114, 17 y 8, 139. Juntas, 251. Y dan lo mismo que Horner bit a bit, comprobado sobre una palabra de 255 bits.

Como 255 no es múltiplo de 32, el bloque ocupa ⌈255/32⌉=8 ciclos y sobra un lugar. Se rellena con un cero en la posición 255, arriba de todo, y Horner no se entera: un cero adelante no cambia la evaluación.

04 · El iterativo, sin inversiones

El algoritmo de Berlekamp-Massey binario actualiza el localizador con

σ(µ+1)(X)=σ(µ)(X)+dµdρ−1X2(µ−ρ)σ(ρ)(X)

y esa división está adentro del lazo de la iteración. El camino crítico incluye un inversor entero, que es el bloque más lento, y como el lazo no se segmenta, no hay registro que lo corte: el reloj tiene que esperarlo.

La salida está en una observación: el localizador se puede multiplicar por cualquier constante no nula sin que cambien sus raíces, y entonces sin que cambien las posiciones de error. Multiplicando los dos miembros por dρ:

σ(µ+1)(X)=dρσ(µ)(X)+dµX2(µ−ρ)σ(ρ)(X)

Sólo productos. Lo que sale es el localizador multiplicado por un factor acumulado, que no importa. El precio es un producto más por iteración, mucho menos que la inversión que se va.

Un detalle muerde. Escalado, el localizador ya no empieza en 1, y la discrepancia tiene que llevar su término constante:

dµ=σ0S2µ+1+σ1S2µ+⋯+σlµS2µ+1−lµ

Con σ0 adentro, recorridos los 576 patrones de hasta tres errores del (15,5), la versión sin inversiones da un múltiplo del σ(X) de la versión con inversiones en los 576. Si se copia la discrepancia de siempre, que da por hecho σ0=1, acierta en 226. Con los errores en 3, 5 y 12 del ejemplo del (15,5), sale

α5+α5X+α10X3=α5(1+X+α5X3)

La versión sin inversiones además es regular. Todas las iteraciones hacen las mismas operaciones sobre los coeficientes, así que se implementa como una fila de elementos de proceso idénticos, uno por coeficiente, que en cada ciclo hablan sólo con sus vecinos. Cada elemento tiene dos registros, dos multiplicadores y un sumador; el control es un contador y la comparación que elige la rama. El camino crítico es el de un solo elemento y no depende de t. En el caso binario, saltear los pasos impares deja t iteraciones: la etapa consume t ciclos.

Con t=2 ni hace falta iterar. La fórmula cerrada de Peterson es σ1=S1 y σ2=(S3+S13)/S1. Así como está pide un elevador, dos multiplicadores generales y un inversor. Con el mismo truco, multiplicada por S1,

S1σ(X)=S1+S12X+(S3+S13)X2

pide un elevador, un multiplicador general y una suma, sin inversor. En el (255,239), recorridos los 255 patrones de un error y los 32 385 de dos, la búsqueda de raíces sobre este polinomio encuentra exactamente las posiciones erradas en los 32 640. Y si S1=0 con S3≠0, que con dos errores o menos no puede pasar, el polinomio queda S3X2, sin raíces en el cuerpo: la búsqueda no encuentra ninguna y la falla queda declarada.

05 · Chien, en paralelo

La búsqueda de Chien evalúa

σ(αl)=1+σ1αl+σ2α2l+⋯+σtαtl

en cada posición, y para pasar de αl a αl+1 multiplica el término i por la constante αi. El circuito son t registros de m bits, t multiplicadores por constante, un árbol de sumadores y una NOR de m entradas que detecta el cero. Si la suma se anula, la posición n−l está errada y se invierte el bit que sale de la memoria. Hace una posición por ciclo: n ciclos por bloque.

Las otras dos etapas tardan menos, y por eso n ciclos es el cuello de botella. Para evaluar J posiciones por ciclo se replica la estructura J veces: la réplica j evalúa en αl+j, multiplicando el registro i por la constante αij, y los registros avanzan J lugares por ciclo, multiplicados por αiJ. La etapa termina en ⌈n/J⌉ ciclos y el costo crece lineal en J: Jt multiplicadores por constante, J árboles y J detectores.

En el (255,239) con J=32 y el localizador escalado de la sección anterior, contado constante por constante: los 62 multiplicadores de las réplicas son 1190 XOR, los dos de avance, por α32 y α64, 51 más, y las 32 sumas de tres términos de 8 bits, 512. Son 1753 XOR y 32 compuertas NOR de 8 entradas: el bloque más grande del decodificador.

Para t≤3 hay una alternativa que se saltea la búsqueda. Un localizador de grado dos o tres se lleva, con un cambio de variable, a una forma canónica cuyas raíces se leen de una tabla. Son unos pocos ciclos en vez de n, a costa de una memoria de consulta, y es el camino de los decodificadores de régimen muy alto con t=2 o 3.

06 · La cascada segmentada

Las tres etapas trabajan sobre bloques sucesivos y no comparten recursos, así que se las encadena: mientras Chien procesa el bloque i, el iterativo trabaja sobre el i+1 y el síndrome recibe el i+2. Con J bits por ciclo en la primera y la tercera, cada bloque consume:

etapaciclos por bloquelo que domina el costo
síndrome⌈n/J⌉J·t multiplicadores por constante
localizadortuna fila de elementos de proceso
Chien y corrección⌈n/J⌉J·t multiplicadores por constante

Llamando P=⌈n/J⌉, un bloque nuevo puede entrar cuando la etapa más lenta se libera. El período, el régimen y la latencia son

I=max(P,t)ciclos régimen=nIbits por ciclo latencia=2P+tciclos

La latencia es la del bloque: del primer bit que entra al último corregido que sale. Mientras t≤P, la etapa del medio no se nota y el régimen es n/P, que es J cuando J divide a n. El régimen lo fija la etapa más lenta, no la suma de las tres.

Cronograma Cinco bloques por la cascada — elegí el código y apostá hasta qué J sigue creciendo el régimen; después subí J. El bloque 0 va en azul, con su latencia arriba; en rojo, los ciclos en que la entrada tiene que esperar. Abajo, los bits guardados en la memoria intermedia, contra la línea de 2n.
Bits por ciclo J
síndrome — localizador — Chien — un bloque cada — régimen — latencia — memoria —

Arranca en el (255,239) con un bit por ciclo, donde la etapa del medio ni se ve: dos ciclos contra 255. Apostá y subí J: con 8, las etapas de punta bajan a 32 ciclos y la latencia de 512 a 66. Después probá el (255,191), que corrige ocho, y el (255,47), que corrige 42.

La memoria intermedia. La palabra recibida tiene que quedar guardada hasta que Chien decide, porque la corrección se aplica recién cuando se conocen las posiciones. Cada bit sale P+t ciclos después de entrar: su lugar en el bloque es el mismo a la entrada y a la salida. La memoria tiene, entonces, lo que entró en los últimos P+t ciclos. Y como P≤I y t≤I, ningún bit espera más de dos períodos: la memoria nunca necesita más de 2n bits.

En el (255,239) con J=32, los últimos diez ciclos son los ocho de un bloque, 255 bits, y dos del siguiente, 64: hacen falta 319 bits, medidos ciclo a ciclo. Si la memoria se organiza en dos bancos de un bloque, uno que se escribe mientras el otro se lee, son 2n=510; alcanza con dos porque la lectura de un banco va P−t ciclos adelante de la escritura que lo reusa. Despreciable en el (255,239). En un código de varios miles de bits, esa memoria puede dominar el área del decodificador.

07 · Cuando la etapa del medio manda

Que la etapa del localizador no limite pide t≤⌈n/J⌉. Con t=2 se cumple hasta J=128. Pero J sube con el régimen que se pide, y t sube en los códigos de tasa baja. Con los cuatro códigos del cronograma, en el último J en que mandan las etapas de punta y en el primero en que manda la del medio:

códigotJ⌈n/J⌉bloque cadabits por ciclo
(255,239)212822127,5
25612127,5
(255,191)8328831,88
644831,88
(255,131)18832327,97
16161814,17
(255,47)42464643,98
832426,07

En rojo, el período que fija el localizador. En el (255,191), pasar de 32 a 64 duplica la lógica del síndrome y la de Chien y no compra un solo bit por ciclo. En el (255,131) la etapa del medio ya manda con 16: entrega 14,17 bits por ciclo, no los 15,94 que darían 16 ciclos. Y en el (255,47), con 8 bits por ciclo, la entrada pasa 10 de cada 42 ciclos esperando.

La salida es la misma de la sección 01. La iteración del localizador es un lazo, y no se segmenta; lo que sí se puede es replicarlo. Con dos unidades que se alternan los bloques, cada una tiene 2I ciclos por bloque, y el período baja a max(P,⌈t/2⌉).

08 · El (255,239), dimensionado

El código del ejemplo 4: m=8, t=2, y

g(X)=1+X+X5+X6+X8+X9+X10+X11+X13+X14+X16

con J=32 y un reloj de 400 MHz. Las XOR se cuentan sin compartir subexpresiones: un sintetizador comparte y baja los números, pero el orden de magnitud queda.

El codificador. Con un bit por ciclo es el divisor por g(X) del codificador cíclico: 16 registros y 10 XOR, una por cada coeficiente no nulo de g(X) menos uno. Es un lazo, así que para 32 bits por ciclo se compone la transición 32 veces, y la composición sigue siendo una matriz fija:

s′(X)=Rg(X){XJs(X)+Xn−kuJ(X)}

con uJ(X) los J bits del mensaje que entran en el ciclo. Con J=32 los dos sumandos se pisan en los grados 32 a 47: 16 XOR los juntan. Quedan 32 coeficientes, de grado 16 a 47, y cada uno va a los bits de Rg(X){Xd} que valen uno. Esos 32 restos tienen 255 unos entre todos, ocho por resto en promedio, la mitad de 16: reducir cuesta 239 XOR, y el total es 255. Por eso el costo crece como J(n−k)/2, que acá da 256. Con 8 bits por ciclo son 72. Sobre un mensaje al azar, las tres versiones dan la misma paridad que la división bit a bit.

El decodificador, etapa por etapa, con lo medido en las secciones anteriores:

bloqueXORademásciclos por bloque
codificador25516 registros8
síndrome, S₁ y S₃2512 registros de 8 bits8
localizador, S₁σ(X)9764 AND2
Chien, 32 posiciones175332 NOR de 8 entradas8
memoria intermedia—319 bits—

El localizador son los 12 XOR del elevador, los 77 del multiplicador que da S13 y los 8 de la suma con S3. Como la fórmula cerrada sólo usa S1 y S3, S2 y S4 ni se calculan.

El régimen. La interfaz acepta 32×400MHz=12,8Gb/s. Pero un bloque de 255 bits ocupa ocho ciclos y deja un lugar vacío, así que entran 255 bits cada ocho ciclos: 12,75 Gb/s de bits de código, de los que 11,95 Gb/s son información, 239 cada ocho ciclos.

La latencia del bloque es 2·8+2=18 ciclos, 45 ns, del primer bit que entra al último que sale. Un bit suelto espera menos: P+t=10 ciclos, 25 ns.

Lo que conviene retener es la comparación. El codificador son unos cientos de compuertas. El decodificador, con el mismo código y el mismo régimen, suma 2101 XOR, ocho veces más, y guarda 319 bits contra 16: veinte veces más. Y eso con t=2. Cada etapa del decodificador crece con t —t celdas de síndrome, una fila de elementos de proceso del largo de t, Jt multiplicadores en Chien—, y el codificador sigue siendo un divisor sin aritmética de GF(2ᵐ). La asimetría es estructural: el costo de corregir está en el receptor.

09 · La cuenta en Python

El codificador de J bits por ciclo contra la división bit a bit, sus XOR contra J(n−k)/2, y la cascada. Los polinomios binarios van como enteros, con el bit i en el coeficiente de Xi. Lo de los comentarios es la salida de correrlo.

import random

g = sum(1 << e for e in (0, 1, 5, 6, 8, 9, 10, 11, 13, 14, 16))   # el (255,239)
n, k, t = 255, 239, 2
nk = n - k

def resto(a, b):
    while a and a.bit_length() >= b.bit_length():
        a ^= b << (a.bit_length() - b.bit_length())
    return a

def codificar_J(u, J):
    """El registro con J bits por ciclo: s' = R_g{X^J s(X) + X^(n-k) u_J(X)}."""
    s, ciclos = 0, -(-k // J)
    for c in range(ciclos):
        bajo = (ciclos - 1 - c) * J                # entran primero los de mayor orden
        bloque = (u >> bajo) & ((1 << J) - 1)
        s = resto((s << J) ^ (bloque << nk), g)
    return s

def xor_codificador(J):
    """Las XOR del paso de J bits: juntar s con u donde se pisan, y reducir."""
    de_s, de_u = set(range(J, J + nk)), set(range(nk, nk + J))
    unos = [0] * nk
    for d in de_s | de_u:
        r = resto(1 << d, g)
        for i in range(nk):
            unos[i] += (r >> i) & 1
    return len(de_s & de_u) + sum(max(w - 1, 0) for w in unos)

random.seed(1)
u = random.getrandbits(k)
for J in (1, 8, 32):
    igual = codificar_J(u, J) == resto(u << nk, g)
    print(J, igual, xor_codificador(J), J * nk // 2)
# 1 True 10 8
# 8 True 72 64
# 32 True 255 256

def cascada(n, t, J):
    P = -(-n // J)            # ciclos del síndrome y de Chien
    I = max(P, t)             # entra un bloque cada I ciclos
    return P, I, 2 * P + t, n / I

for J in (1, 8, 32):
    P, I, latencia, bits = cascada(n, t, J)
    print(J, P, latencia, bits, bits * 0.4, latencia / 0.4)
# 1 255 512 1.0 0.4 1280.0
# 8 32 66 7.96875 3.1875 165.0
# 32 8 18 31.875 12.75 45.0
print(cascada(255, 8, 32), cascada(255, 8, 64))
# (8, 8, 24, 31.875) (4, 8, 16, 31.875)

Con J=1 salen las 10 XOR del divisor serie; la estimación J(n−k)/2 es para J grande. Las columnas de la cascada son J, P, la latencia en ciclos, los bits por ciclo, los Gb/s a 400 MHz y la latencia en nanosegundos. La última línea es el (255,191): con 32 y con 64 bits por ciclo, el mismo régimen.

Para tener a mano

  1. Camino crítico, latencia y régimen Jf son independientes. Un lazo de realimentación no se segmenta: se replica.
  2. Suma: m XOR. Producto por constante y cuadrado: matrices fijas de XOR (en GF(2⁴), 1 XOR por α, 3 por α3, 2 el cuadrado). Producto general: m2 AND. Inverso: m−1 cuadrados y m−2 productos, el bloque más caro.
  3. Síndrome: t celdas impares de Horner, con multiplicador por constante y una XOR por bit recibido; los pares, al cuadrado. Con J bits por ciclo, Si←SiαJi+∑rc+jαij.
  4. Sin inversiones: σ(µ+1)=dρσ(µ)+dµX2(µ−ρ)σ(ρ), con σ0 dentro de la discrepancia. t ciclos.
  5. Con t=2: S1σ(X)=S1+S12X+(S3+S13)X2: un elevador y un multiplicador, sin inversor.
  6. Chien con J réplicas: ⌈n/J⌉ ciclos, Jt multiplicadores por constante. Es la etapa más grande.
  7. Cascada: un bloque cada I=max(⌈n/J⌉,t) ciclos, régimen n/I bits por ciclo, latencia 2⌈n/J⌉+t. Si t>⌈n/J⌉, subir J no sirve.
  8. Memoria: lo que entró en los últimos P+t ciclos, nunca más de 2n.
  9. (255,239), J=32, 400 MHz: 12,75 Gb/s de código (12,8 en la interfaz), 18 ciclos o 45 ns, 319 bits de memoria. Codificador 255 XOR; decodificador 2101 XOR, 64 AND y 32 NOR.
← Anterior Inicio
Relacionados Los síndromes y el polinomio localizador El algoritmo de Berlekamp-Massey El método de Peterson La búsqueda de Chien Las clases conjugadas y los polinomios mínimos El codificador cíclico El (15,5) de punta a punta