Entendiendo la sincronización de reloj en USB: simulación de codificación NRZI y relleno de bits con Python

La comunicación entre dispositivos USB, como conectar una memoria al ordenador, parece inmediata. Sin embargo, detrás de esta interacción existe un mecanismo sofisticado que permite mantener la sincronización entre emisor y receptor sin necesidad de una línea de reloj dedicada. Este artículo explora cómo funciona este proceso mediante una simulación práctica en Python de la codificación NRZI (Non-Return-to-Zero Inverted) junto con la técnica de bit stuffing.

Codificación NRZI: la base de la señalización en USB

En sistemas digitales, transmitir datos de forma confiable requiere que el transmisor y el receptor estén sincronizados temporalmente. Dado que USB no incluye una línea separada para el reloj, utiliza un esquema de codificación auto-sincronizado. La solución elegida es NRZI, donde el estado del voltaje no representa directamente el valor del bit, sino los cambios entre bits.

Las reglas de codificación son simples:

  • Un bit 0 provoca un cambio en el nivel de señal (alta → baja o baja → alta).
  • Un bit 1 mantiene el nivel de señal anterior.

Este comportamiento asegura que solo ciertos bits generen transiciones, lo cual reduce el consumo energético y mejora la eficiencia del canal.

Implementación en Python

A continuación se presenta una función que aplica la codificación NRZI a una secuencia binaria dada:

def codificar_nrzi(entrada_binaria):
    """
    Codifica una cadena binaria usando NRZI.
    :param entrada_binaria: str, secuencia de '0' y '1'
    :return: lista de niveles lógicos (0 y 1)
    """
    nivel_actual = 1  # Empezamos en alto
    resultado = []

    for bit in entrada_binaria:
        if bit == '0':
            nivel_actual = 1 - nivel_actual  # Invertimos
        resultado.append(nivel_actual)

    return resultado

Relleno de bits (bit stuffing)

Un problema potencial con NRZI es que una larga secuencia de unos produce sin transiciones, lo cual dificulta la recuperación del reloj. Para solucionarlo, USB introduce un mecanismo llamado bit stuffing: cada vez que aparecen seis bits consecutivos en '1', se inserta artificialmente un '0' en el flujo de datos.

Esto garantiza al menos una transición cada seis bits, permitiendo al receptor manetnerse sincronizado. El receptor elimina automáticamente estos bits adicionales tras la recepción.

def aplicar_relleno_bits(secuencia):
    """
    Inserta un '0' después de cada seis '1' consecutivos.
    :param secuencia: str, datos originales
    :return: str, datos con relleno aplicado
    """
    salida = ""
    contador_unos = 0

    for bit in secuencia:
        salida += bit
        if bit == '1':
            contador_unos += 1
        else:
            contador_unos = 0  # Reinicia si hay un '0'

        if contador_unos == 6:
            salida += '0'
            contador_unos = 0  # Reinicia tras el relleno

    return salida

Visualización del comportamiento de la señal

Para observar el efecto de NRZI y el impacto del relleno de bits, podemos usar matplotlib para graficar la señal resultante:

import matplotlib.pyplot as plt
import numpy as np

def graficar_senal(tiempo, valores, titulo="Señal NRZI"):
    plt.figure(figsize=(12, 3))
    plt.step(tiempo, valores, where='post', linewidth=2)
    plt.title(titulo)
    plt.xlabel("Tiempo (unidades)")
    plt.ylabel("Nivel")
    plt.ylim(-0.5, 1.5)
    plt.grid(True, axis='y', linestyle='--', alpha=0.7)
    plt.xticks(np.arange(len(valores)), [str(i) for i in range(len(valores))])
    plt.show()

Supongamos que queremos transmitir la secuencia '1111111'. Sin relleno, esto produciría cinco '1' seguidos y luego otro '1', lo cual sería problemático. Aplicando el relleno:

# Datos originales
datos = "1111111"

# Aplicar relleno
datos_con_relleno = aplicar_relleno_bits(datos)
print("Datos con relleno:", datos_con_relleno)  # Salida: 11111101

# Codificar NRZI
senal_nrzi = codificar_nrzi(datos_con_relleno)

# Generar eje temporal
t = np.arange(len(senal_nrzi))

# Mostrar gráfica
graficar_senal(t, senal_nrzi, "Señal NRZI con bit stuffing")

La inserción del '0' forzado genera una transición crítica que permite al receptor detectar correctamente los límites de cada bit, incluso en flujos con muchos unos.

Conclusión práctica

La combinación de NRZI y bit stuffing en USB resuelve elegantemente el desafío de la sincronización sin requerir hardware adicional. Mediante simulaciones sencillas en Python, podemos visualizar cómo pequeñas decisiones de diseño afectan directamente la robustez del protocolo. Esta aproximación no solo ayuda a comprender mejor USB, sino también principios fundamentales de comunicaciones seriales utilizados en CAN, Etehrnet y otros estándares modernos.

Etiquetas: USB NRZI bit stuffing sincronización reloj

Publicado el 8-5 04:49