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.
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 bits por ciclo y el reloj tiene frecuencia , el régimen es
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 .
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, . 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 pasos por ciclo.
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 bits , con el coeficiente de . Los ejemplos van en GF(2⁴) con , donde .
El sumador es un banco de 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 fijo en el diseño es lineal en los bits de entrada: es una matriz binaria de , y se realiza con XOR cableadas, sin una sola AND ni un registro. Por , en GF(2⁴),
es un desplazamiento con una sola realimentación: una XOR. Por se escribe , se reemplaza , , , y se junta por potencia:
Tres filas con dos unos: tres XOR. El costo depende del peso de la matriz, no de . 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, , porque el término cruzado aparece dos veces y se cancela. Paso 2: entonces , con porque cada es un bit. Paso 3: se reducen las potencias que pasan de . En GF(2⁴), , y con y :
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 , 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 de :
Son ciclos, un acumulador de bits, compuertas AND que forman y un multiplicador por . La realización paralela desdobla lo mismo en lógica combinacional: AND para los productos parciales, XOR para sumarlos y reducir, y un camino crítico del orden de 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 palabras de bits: 2048 bits en GF(2⁸), 10 240 en GF(2¹⁰). Por el pequeño teorema de Fermat, , y como , se arma con cuadrados y productos generales. En GF(2⁸) son siete elevadores y seis multiplicadores: 546 XOR y 384 AND, con una latencia que crece con . 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.
El síndrome es , y se evalúa por Horner a medida que entra la palabra, empezando por :
Una celda por síndrome: un registro de bits, un multiplicador por y un sumador. En un BCH binario la celda se abarata dos veces. El multiplicador es por constante, porque lo fija el diseño: es cableado. Y el sumando es un bit, no un elemento del cuerpo: la suma es una sola XOR, sobre el bit de .
Además alcanza con las celdas de índice impar. Los pares salen de con elevadores al cuadrado, que son lógica fija. El circuito entero son celdas y elevadores.
Para tragar bits por ciclo se desarrolla Horner pasos de una vez. Si en un ciclo entran las posiciones a ,
Los productos no cuestan nada: son el bit llevado con cables a las posiciones donde tiene un uno. Lo que cuesta es sumar esos vectores y el único multiplicador por . Contado en el (255,239) con : la celda de son 82 XOR para la suma de entrada, 22 para el producto por y 8 para sumar al registro, 112 en total; la de , 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 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.
El algoritmo de Berlekamp-Massey binario actualiza el localizador con
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 :
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:
Con adentro, recorridos los 576 patrones de hasta tres errores del (15,5), la versión sin inversiones da un múltiplo del de la versión con inversiones en los 576. Si se copia la discrepancia de siempre, que da por hecho , acierta en 226. Con los errores en 3, 5 y 12 del ejemplo del (15,5), sale
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 . En el caso binario, saltear los pasos impares deja iteraciones: la etapa consume ciclos.
Con ni hace falta iterar. La fórmula cerrada de Peterson es y . Así como está pide un elevador, dos multiplicadores generales y un inversor. Con el mismo truco, multiplicada por ,
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 con , que con dos errores o menos no puede pasar, el polinomio queda , sin raíces en el cuerpo: la búsqueda no encuentra ninguna y la falla queda declarada.
La búsqueda de Chien evalúa
en cada posición, y para pasar de a multiplica el término por la constante . El circuito son registros de bits, multiplicadores por constante, un árbol de sumadores y una NOR de entradas que detecta el cero. Si la suma se anula, la posición está errada y se invierte el bit que sale de la memoria. Hace una posición por ciclo: ciclos por bloque.
Las otras dos etapas tardan menos, y por eso ciclos es el cuello de botella. Para evaluar posiciones por ciclo se replica la estructura veces: la réplica evalúa en , multiplicando el registro por la constante , y los registros avanzan lugares por ciclo, multiplicados por . La etapa termina en ciclos y el costo crece lineal en : multiplicadores por constante, árboles y detectores.
En el (255,239) con 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 y , 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 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 , a costa de una memoria de consulta, y es el camino de los decodificadores de régimen muy alto con o .
Las tres etapas trabajan sobre bloques sucesivos y no comparten recursos, así que se las encadena: mientras Chien procesa el bloque , el iterativo trabaja sobre el y el síndrome recibe el . Con bits por ciclo en la primera y la tercera, cada bloque consume:
| etapa | ciclos por bloque | lo que domina el costo |
|---|---|---|
| síndrome | ⌈n/J⌉ | J·t multiplicadores por constante |
| localizador | t | una fila de elementos de proceso |
| Chien y corrección | ⌈n/J⌉ | J·t multiplicadores por constante |
Llamando , un bloque nuevo puede entrar cuando la etapa más lenta se libera. El período, el régimen y la latencia son
La latencia es la del bloque: del primer bit que entra al último corregido que sale. Mientras , la etapa del medio no se nota y el régimen es , que es cuando divide a . El régimen lo fija la etapa más lenta, no la suma de las tres.
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í : 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 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 ciclos. Y como y , ningún bit espera más de dos períodos: la memoria nunca necesita más de bits.
En el (255,239) con , 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 ; alcanza con dos porque la lectura de un banco va 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.
Que la etapa del localizador no limite pide . Con se cumple hasta . Pero sube con el régimen que se pide, y sube en los códigos de tasa baja. Con los cuatro códigos del cronograma, en el último en que mandan las etapas de punta y en el primero en que manda la del medio:
| código | t | J | ⌈n/J⌉ | bloque cada | bits por ciclo |
|---|---|---|---|---|---|
| (255,239) | 2 | 128 | 2 | 2 | 127,5 |
| 256 | 1 | 2 | 127,5 | ||
| (255,191) | 8 | 32 | 8 | 8 | 31,88 |
| 64 | 4 | 8 | 31,88 | ||
| (255,131) | 18 | 8 | 32 | 32 | 7,97 |
| 16 | 16 | 18 | 14,17 | ||
| (255,47) | 42 | 4 | 64 | 64 | 3,98 |
| 8 | 32 | 42 | 6,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 ciclos por bloque, y el período baja a .
El código del ejemplo 4: , , y
con 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 del codificador cíclico: 16 registros y 10 XOR, una por cada coeficiente no nulo de 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:
con los bits del mensaje que entran en el ciclo. Con 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 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 , 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:
| bloque | XOR | además | ciclos por bloque |
|---|---|---|---|
| codificador | 255 | 16 registros | 8 |
| síndrome, S₁ y S₃ | 251 | 2 registros de 8 bits | 8 |
| localizador, S₁σ(X) | 97 | 64 AND | 2 |
| Chien, 32 posiciones | 1753 | 32 NOR de 8 entradas | 8 |
| memoria intermedia | — | 319 bits | — |
El localizador son los 12 XOR del elevador, los 77 del multiplicador que da y los 8 de la suma con . Como la fórmula cerrada sólo usa y , y ni se calculan.
El régimen. La interfaz acepta . 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 ciclos, 45 ns, del primer bit que entra al último que sale. Un bit suelto espera menos: 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 . Cada etapa del decodificador crece con — celdas de síndrome, una fila de elementos de proceso del largo de , 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.
El codificador de bits por ciclo contra la división bit a bit, sus XOR contra , y la cascada. Los polinomios binarios van como enteros, con el bit en el coeficiente de . 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 salen las 10 XOR del divisor serie; la estimación es para grande. Las columnas de la cascada son , , 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.