2020/11/23

Forzando Brutalmente MD5 - parte 6 - FPGA

¿Venís de leer la intro, la parte 1, la parte 2, la parte 3 la parte 4, la parte 5 y la implementación FPGA en nexys4ddr?

El tema

Habiendo agotado en lo que a mi respecta las maneras de calcular MD5 usando una o varias CPU, ya sean sus instrucciones básicas o extendidas, habiendo aplicado una optimización llamada "loop unroll", que consiste en tomar un loop, quitarle el contador y escribir su cuerpo tantas veces como hubiese sido ejecutado, adaptando cada iteración a los valores que hubiese recibido del contador, llegó el momento de no usar más CPU.


Fijate que más arriba le dediqué mucho al loop unroll. Es que desde el primer día que decidí encarar este proyecto, sabía que era lo que en última instancia iba a utilizar.

Todo el caminito que recorrí por las CPUs, aunque muy divertido e instructivo, realmente aprendí mucho, asenté, fundamenté y completé mucho conocimiento, no era para mi, era para vos.

Lo que yo quería hacer desde el comienzo era esto, implementar el forzador de MD5 en una FPGA, pero si vos no sabés nada de técnicas digitales, ni cómo funcionan la computadoras, ni assembly, ni C, ni programar quizás, te muestro esto primero y te mato.

En el trabajo fui mostrando cada etapa en una charlas a gente de diverso conocimiento técnico y apuesto que de no haber seguido este camino, a la mayoría ahora le costaría asimilar algo de todo esto, no habría ninguna conexión con su conocimiento.

 

FPGA

Ni instrucciones básicas, ni extendidas, ni multithread/multiprocess, ni optimizaciones del compilador, nada.

Había comentado antes que la velocidad del clock se había convertido en un limitante y eso había impulsado el paralelismo del multicore.

Tambien que en el caso de supercomputadoras como Epiphany y GPGPU, que esos cores fueran lo más sencillos posibles para ahorra silicio y poner más cores.

Aondando en esa tendencia, pensá que un core es una CPU de propósito más o menos general y aunque sea de propósito muy específico, cada problema que resuelvas no va a utilizar buena parte de los recursos que ofrezca.

Si además, es muy especifica y el problema cambia, no la vas a poder utilizar.

Si pudieras reconfigurar el hardware para que el silicio existente esté íntegramente dedicado a la resolución del problema que estás encarando, alcanzarías el máximo rendimiento.


El hardware reconfigurable se llama FPGA y se programa principalmente en VHDL como en este caso y Verilog.


Pipeline
Pipeline

La idea es que con un contador vas generando las claves candidatas y le vas a aplicando algo así como el loop unroll que habíamos visto antes, pero con la diferencia que cada etapa, cada ronda es un circuito que está ejecutando las operaciones en simultáneo con las demas. 

Durante los primeros 63 clicks del reloj el comparador recibe basura, de ahí en más lo que había en el contador hace 64 ticks.

De esta manera tenemos un paralelismo de 64, así como con AVX2 8, con GPU varios centenares y con multicore x 32.

La ventaja abismal, es que en cada tick se obtiene un hash entero, a diferencia de CPU/GPU que lleva muchísimimos ticks, al punto de que el clock de la FPGA que tengo es de sólo 100Mhz y aún así, como tengo espacio suficiente en la FPGA para instanciar hasta 10 pipelines, alcanza a la GPGPU, consumiendo mucho menos.

Si además este diseño lo ajustar para poder pasarlo a un ASIC, esto es un chip no reconfigurable, carísimo para un sólo ejemplar pero baratísimo por millón, podría aumentar la velocidad de clock y agregar instancias del pipeline, pues FPGA ocupa mucho espacio por ser reconfigurable.

Según he leido, ese cambio lo haría 10 veces más rápido por el clock y supongo que otras 10 veces más rápido por poner más instancias. De hecho, para minar bitcoins existen ASICs.

El camino


Tuve que tomar diversos caminos, divergente, simultáneos e incluso superpuestos debido en parte a mi desorden mental, en parte a mis conocimientos limitados, al tiempo disponible, al hardware disponible y a las dificultades de los entornos de desarrollo.

Una vez que tuve una versión que malamente funcionaba, le quité todo lo superfluo y lo instancié varias veces en mi hardware para ver cual era el rendimiento máximo.

Utilicé la placa Nexys4-DDR que entre que la pedí y antes de llegar ya me quedó obsoleta, mala suerte. Es una FPGA de las más caras de las baratas o más baratas de las caras, tiene VGA, switches, botones, 7 segmentos, audio, usb, de todos.

Más arriba mencioné el loop unroll como gran protagonista. Es que de no utilizarlo, tendría que diseñar una circuitería de control que comenzaría a parecerse a una CPU, perdiendo las ventajas ganadas.

De todos modos, esa proto CPU tendría sólo las instrucciones necesarias para este problema.






cpucpu
unroll
avx2
+ unroll
threads
x 4
+ avx2
threads
x 32
gpu
fpga
x 1
fpga
x 8
fpga
x 10
velocidad5.79.647.9153.41791090.11008001000
rendimiento watt0.861.447.1923.013.1106.6445360450
rendimiento dolar2.38419.9663.9213.16454.2178.13625781.25






cpucpu
unroll
avx2
+ unroll
threads
x 4
+ avx2
threads
x 32
gpu
fpga
x 1
fpga
x 8
fpga
x 10


5.79.647.9153.41791090.11008001000
cpu5.71.001.688.4026.9131.40191.2517.54140.35175.44
cpu
unroll
9.6
1.004.9915.9818.65113.5510.4283.33104.17
avx2
+ unroll
47.9

1.003.203.7422.762.0916.7020.88
threads
x 4
+ avx2
153.4


1.001.177.110.655.226.52
threads
x 32
179



1.006.090.564.475.59
gpu
1090.1




1.000.090.730.92
fpga
x 1
100





1.008.0010.00
fpga
x 8
800






1.001.25

 

Con 8 pipelines, como corre a 100Mhz, tenemos 800 de velocidad, no muy lejos de los 1090 de la GPU. Aunque no lo hice funcionar pues se me complica la lógica, podría tener 10 pipelines y estoy seguro que de tener un poco más de praćtica y aprovechando los DSPs podria llega a meter 11 o incluso 12.



nexys4ddrparallellaPYNQ-Z2


artix-7zynq 7010zynq 7020

luts15k25k85k

dsp24080220
pipelines
implementados
o implementables
81345
máximo
estimado
111447


Utilizando sólo los luts y dsp disponibles, proyecto los números para las otras dos placas que tengo. No estoy considerando que quizás podría subir la velocidad del clock por encima de 100Mhz. Para ello tendría que usar los PLLs disponibles y evaluar los timings a ver si llegan a hacerse las operaciones a tiempo.

Con respecto a los DSP, supongo que no haría un pipeline de DSPs si no que una etapa de cada pipeline estaría implementada así, futuro improbable.

Si juntara todo, tendría entonces entre 6600M y 7200M hashes por segundo y 1090 con la GPU. Luego, tendría que pedirle a alguien que tenga una GPU decente que ejecute mi programa, pues mi GPU es GeForce 940MX, al momento de hacer este análisis, 2019, un GeForce GTX 1080 Ti era casi 9 veces más veloz según algún benchmark que no registré.


Al momento de escribir esto, GeForce RTX 2080 Ti sale u$s 1000, el doble que las tres FPGA que tengo y consume 250 Watts vs los menos de 15 Watts que consumen las tres juntas, pero, si se puede proyectar la comparación de mi GPU, tendría una velocidad de 15000M, así que el rendimiento dolar sería apenas mejor para GPU y el energético 15 veces mejor para las FPGA, pero tomalo con pinzas.

No sé cuántos LUTs se consiguen por u$s 1000.

De todos modos, todo esto es para MD5, habría que ver que pasa con algoritmos modernos tipo SHAx.

 

Código

  • https://github.com/cpantel/Forzando-Brutalmente-MD5/tree/master/v8_fpga

Seguí en la parte 7

2020/11/14

H4kc3d 2020: the missing pieces

 

Otro año de H4ck3D


Aunque no es la primera vez que doy una charla virtual, pues ya he dado varias en el trabajo, es la primera ante tanta gente tan desconocida. Por más que Javier y Fabián (los organizadores) confían en que aunque yo pase vergüenza por todos los errores que cometo ellos no por que quizás de algún modo logro darle una cierta cohesión y claridad, a veces sospecho que les gusta más que lo técnico lo "artístico", no sé, pero suficiente de estas intangibilidades.

Mirando el video, la presentación que tenía y lo que hay en mi mente, no puedo dejar de notar que algunas cosas me faltaron decir o recalcar.

 

La charla


La apertura fue mostrar las diferencias entre software y hardware por un lado y entre el error y la mala intención por el otro.

 

La idea perdida

Es extremadamente difícil desde el software detectar o prevenir cualquiera de estos ataques, listo, lo dije. Era lo que venía a continuación de la cadena de confianza.


Las fallas

 

En comparación a otras charlas, podríamos decir esta ha sido de las mejores, pues es de las más complicadas y con un sólo error técnico, que en la demo del bit supervisor no se vió como se cargaba el capacitor en el osciloscopio, calculo que no había seleccionado el canal apropiado, hubiese sido tán fácil apretar un botoncito... pero en "modo demo" he llegado a la conclusión de que es mejor no tocar nada, no vaya a ser que se rompa lo siguiente. Está bueno intentar acordarse al final y ahí experimentar.

Tampoco expliqué nada de ese circuito, como que se me vino encima el poco tiempo que quedaba.

 

Las demos


Quería hacer un montón de demos, pero justo los tres meses previos han sido terribles, sólo pude hacer dos decentes.

 

Supervisor

 

Se trata de poder cambiar el bit de supervisor mediante un circuito auxiliar que detecta una anomalía en la frecuencia de uso de ciertas pistas. En el caso del ejemplo se trata del bit más alto de las instrucciones. Los detalles completos los iré publicando a medida que tenga tiempo. Te puedo adelantar los dos videos, en el primero, vemos el comportamiento normal:

 




En el segundo, con el bichito activo.





Acelerador criptográfico troyanizado

 

Las explicaciones completas están en estos artículos:


No hay video, pues la terminé a la 1:am del día de la charla, fuí sin red de seguridad.

 

Fuga de información por VGA

 

De esto tambien haré un artículo pronto, sólo mostré un video y no lo he implementado en hardware aún. El resumen es que como hay tiempos muertos en la transmisión de la señal VGA de la placa a la pantalla, se puede aprovechar para insertar información ahí. Este es el video de la simulación:

 


El código de las demos:


  • https://github.com/cpantel/HardwareMalwareAccelerator
  • https://github.com/cpantel/HardwareMalwareSupervisor

 

y en algún momento 

  • HardwareMalwareLinuxReset
  • VGALeak



Todos los videos de todas las charlas en:


  • https://www.youtube.com/watch?v=D5btBXAcEec (minuto 15 aprox.)
  • https://www.youtube.com/watch?v=dVXJvWRWl5o
  • https://www.youtube.com/watch?v=CtljkKeirtk

2020/11/13

Lidiando con protocolos legacy

Inspirado por un problema que suele ocurrir en ambientes legacy y en un artefacto que hallé hace muchos años tirado en la vereda, diseñaré una mitigación e intentaré implementar unos componentes, con la excusa de practicar qemu, networking, programar en C, cambiar el firmware de un router y finalmente FPGA.


En FTP las credenciales circulan en texto plano
En FTP las credenciales circulan en texto plano
 


El problema

 

Tenemos dos equipos, quizás en distintos datacenters, que están usando para intercambiar información sensible un protocolo inseguro como http, ftp o telnet. Estos protocolos son muy fáciles de inspeccionar si podés ver el tráfico. No se pueden cambiar por X motivo, esa es la esencia del legacy.

 

Inspiración

 
Cifrador de  X25
Cifrador de X25

El componente de la fotito lo encontré en la calle y se mete en el medio de una conexión serial para cifrar el canal, uno en cada extremo. En cierto modo es como agregar una capa que normalmente es lógica de modo físico. Está en mi lista de deseos en algún momento lograr la máxima posesión del mismo.

No sé los detalles de lo que hace, pero me imagino que cifra todo el tráfico y que lo hace de modo transparente, los nodos que se comunican no se enteran de este proceso. La diferencia con mi proyecto es que se tratará sólo un tipo de tráfico y el resto circulará sin modificaciones.

 

Análisis de impacto

 

Hay varios inconvenientes: un atacante puede ver usuario y credencial si puede inspeccionar el tráfico de red. Luego, como puede ver todo el tráfico la información sensible que haya.


En términos de CVSS, como vulnerabilidad, si dejamos de lado la integridad el valor pasa de 6.5 a 4.6 

 

Adyacente sin integridad
Adyacente sin integridad


Physical sin integridad
Physical sin integridad


Si consideramos la integridad, de 8.16.1  


Adyacente con integridad
Adyacente con integridad


 

Physical con integridad
Physical con integridad


Esta mejoría se debe a que hace falta ir físicamente al datacenter y pinchar el cable o sacar componentes, no se puede atacar desde otra máquina conectada a la misma red. 

Fijate que no cambie de Adyacente a Físico en la puntuación base, pues ese es el valor de vulnerabilidad en general, lo hice en la puntuación de entorno, es la aplicación concreta de las circunstancias a la base.

 

Mitigaciones

 

El cable cruzado


Una solución sencilla si estuvieran juntos y se pudiese agregar una placa de red en cada equipo es usar un cable cruzado.

 

Networking


Si no se puede tirar un cable o agregar placas, se podría hacer a nivel del equipamiento de red, cosa que no sé hacer, no tengo equipamiento de red. Me imagino algo así como "si viene de tal IP y va a tal IP y es de tal Protocolo, enviar por algún modo tal que no sea muy visible, ya sea una vlan o mejor cifrado.

 

Componentes como el de la fotito


Puedo desarrollar un par de dispositivos que separen el tráfico, algo así:

 

            +-----------------+           +----------------+
+-------+   |                 tráfico común                |  +-------+
|sistema|<--------------------------------------------------->|sistema|
+-------+   |+--------------+ |           |+--------------+|  +-------+
            ||tráfico legacy|<--cifrado-->||tráfico legacy||
            |+--------------+ |           |+--------------+|
            +-----------------+           +----------------+

 

Si el tráfico no corresponde, pasarlo de una interfaz a la otra sin más trámite.

Si el tráfico corresponde, tratarlo de alguna manera  y pasarlo al otro componente, que invierte el tratamiento y se lo dá al destino.

 

Modo de protección


Veo dos maneras de implementar la protección:

 

Tunneling

 

Este es el modo más sensato, usar la magia de iptables/fwbuilder y establecer una VPN para hacer circular el tráfico de interés por ahi.

 

Cifrado por paquete

 

No sé casi nada de criptografía, así que no tomés como referencia mi diseño ni implementación.

En terminos de latencia, lo mejor que podría hacer es un xor del tamaño del buffer, pero tendría que ir cambiando ese xor para cada paquete, pues si se repitiera se abriría la puerta a sencillos ataques.

 

Ponele que cifro con el mismo k varios mensajes:

a       01011011 
k       01010101  
A       00001110

b       00110011
k       01010101
B       01100110



Como atacante, tengo todos los A,B...


Supongamos que tengo un a, debido a que por lo general el primer mensaje suele ser igual debido a encabezados, por ejemplo una página web suele comenzar con


<!DOCTYPE html>
<html

Fijate como obtengo el mensaje b, o cualquier otro, teniendo un solo mensaje conocido:



A       00001110
B       01100110
xor     01101000

a       01011011 
b       ????????
xor     01101000

b       00110011

 

Y este es un ataque increiblemente sencillo, casi el único que estoy en condiciones de explicarte, no sos vos, soy yo. Después hay mil maneras sutiles de fallar. Repito, lo que haga es de juguete.


Cuando llegue el momento, veremos...

 

Implementaciones


Hay varias maneras que se me ocurren y pienso intentar implementar apuntando al máximo aprendizaje.

 

Virtualización


Puedo usar Qemu o VirtualBox

  • Ventajas
    • gratis
    • homogéneo
    • ambos modos
  • Desventajas
    • aburrido
    • poco creible

 

Pese a lo aburrido y poco creible, va a ser lo primero que haga, usando ambos modos, como para comprobar que se puede hacer y estar atento a que no haya algún bloqueante que luego va a ser más difícil de identificar. Además usaré qemu pues tengo poca práctica últimamente

 

Computadoras


Dos viejas netbooks y un adaptador usb-ethernet

  • Ventajas
    • más creible
    • homogéneo
    • ambos modos
  • Desventajas
    • tengo que comprar otro adaptador usb-ethernet o mejor dos para que sean usb 3 y gigabit, unos u$s 40
    • ocupa mucho lugar y consumo
    • sigue siendo aburrido

 

Como no le veo mucha diferencia a virtualizar y me obliga a comprar el adaptador y principalmente sigue siendo aburridísimo, no lo voy a implementar.

 

Routers


Un router hogareño de más, otro que tiene sólo un ethernet, pero puedo usar el adaptador usb pues tiene usb

  • Ventajas
    • gratis
    • divertido
    • ambos modos
    • por fin le doy uso a esos routers
    • tengo que cambiar el firmware
  • Desventajas
    • heterogéneo
    • son distintos
    • tengo que cambiar el firmware

 

Esta implementación sólo la haré por que me obliga a cambiar el firmware, cosa que nunca he hecho. Probablemente me conforme con el modo tunneling.

 

FPGA


Usar dos FPGA con dos o cuatro pmods ethernet

  • Ventajas
    • máxima diversión
    • máxima dificultad
    • máximo aprendizaje
    • heterogeno, tengo que usar dos placas distintas pues es lo que tengo
  • Desventajas
    • heterogeno, tengo que usar dos placas distintas pues es lo que tengo
    • al menos u$s 80 mas impuestos y gastos de envío
    • sólo cifrado
    • quizás no me dé el cerebro para hacerlo

 

 

Resumiendo

 


TunnelingCifradoLo haré?
Virtualessisiambos
CompussisiNo
Routerssinotunneling
FPGAsnosicifrado


Dame unos meses, pues ahora estoy medio complicado.





2020/11/11

PYNQ acelerador criptográfico con leak

Recapitulemos:


Primero había visto como agregar un puerto serial y usarlo desde la CPU, luego un sencillo acelerador criptográfico por hardware y finalmente, que los mensajes enviados por el puerto serial fueran cifrados por el acelerador.


Ha llegado el momento de hacer el ataque, que consiste en que el acelerador criptrográfico divulgue la clave o el mensaje no cifrado por algún canal alternativo.


Para ello, primero tengo que aprender a usar un IP en mi IP, esto es un componte existente en mi componente. Esto lo voy a hacer en un proyecto aparte, luego regresaré al proyecto que ya está funcionando y aplicaré lo aprendido.

 

IP en IP

 

Otra vez...


  • File -> New Project ->...
  • Create and Package New IP 
  • Create a new AXI4 peripheral
  • Edit IP
  • IP INTEGRATOR
  • Create Block Design

Mmm esto es un terrible problema, la uart no es simplemente una uart sino axi-uart, tendría que descifrar cómo usarla, quizás sea mucho más fácil y acorde al espíritu del ejercicio usar una uart verilog pelada.

Además sólo necesito un transmisor UART y teniendo el código fuente puedo prescindir con sencillez del receptor.


Fail

 

UART Transmitter copy paste


En la excelente página de nandland hay un ejemplo, veamos si puedo hacerlo funcionar, voy a editar el acelerador:


  • IP INTEGRATOR
  • Open Block Design
  • Window -> IP Catalog -> accelerator_xpr_v1.0 ->  Edit in IP Packager
  • Usar otro nombre, por ejemplo "accelerator_xor_bugger_v1_0_project"
  • Source -> + -> Add or Create constraints -> pynq-z2_v1.0.xdc

##Arduino Digital I/O 

set_property -dict {PACKAGE_PIN V17 IOSTANDARD LVCMOS33}
[get_ports { tx_1 }]; #IO_L21P_T3_DQS_34 Sch=ar[8]

  • Sources -> + -> Add or Create Design Sources -> Create -> UART_TX.v


Esta es la interfaz de módulo

module UART_tx
  #(parameter CLKS_PER_BIT = 870)
  (
   input       i_Clock,
   input       i_Tx_DV,
   input [7:0] i_Tx_Byte,
   output      o_Tx_Active,
   output reg  o_Tx_Serial,
   output      o_Tx_Done
   );

 

Mirando fijo el código, apuesto que hace falta un high en i_Tx_DV para que inicie la transmisión, llamémoslo "uart_send":


¿Qué va en cada port al instanciar?


    UART_tx UART_tx(
      .i_Clock(S_AXI_ACLK),
      .i_Tx_DV(uart_send),
      .i_Tx_Byte(slv_reg1[7:0]),
      .o_Tx_Active(uart_active),
      .o_Tx_Serial(tx_1),
      .o_Tx_Done(uart_done)
    );


S_AXI_ACLK: espero que sea de 100Mhz, sino hay que ajustar CLKS_PER_BIT.

uart_send: tengo que detectar que hay un caracter a enviar.

slv_reg1[7:0]: los caracteres vienen de a cuatro, por ahora sólo voy a transmitir el último.

uart_active: puedo ignorarlo y no conectarlo.

tx_1: va conectado al pin tx_1 declarado en xdc, es por donde fugará la información.

uart_done: puedo ignorarlo y no conectarlo.

 

Los que puedo ignorar se debe a que apuesto a que voy a transmitir más rápido que lo que me piden cifrar los caracteres y además como sólo voy a transmitir el menos significativo, con menor frecuencia. Para ser realistas, no puedo transmitir el texto plano a la velocidad que me lo puede llegar a pedir la CPU, tendría que transmitir sólo la clave. En realidad tendría que medir.

Dejando de lado esas medidas y optimizaciones, me conviene no ignorarlos, pues para implementar la lógica me vienen bárbaro.

Voy a tener que adaptar el programa para que tome cada caracter como un bloque de cifrado para no tener que implementar la lógica de ir transmitiendo de a cuatro caracteres, para lo cual sí necesitaría active y done.



Otra vez los mismo...

  • Source -> + -> Add or create constraints -> pynq-z2_v1.0.xdc
  • Tools -> Create and Package New IP
  • Create a new AXI4 peripheral
  • Name, version, etc...
  • Next Steps -> Edit IP
  • Finish
  • Source -> + -> Add or create design sources
    • uart_tx.v
    • ip_repo/leaky_accelerator_1.0/src/
  • Agregué un puerto tx_1 tanto a accelerator_bugged_v1_0 como a accelerator_bugged_v1_0_S00_AXI_inst y en el primero lo conecté al segundo
  • Agregué uart_tx y lo instancié
  • Agregué la lógica y los puertos extra de diagnóstico


  • Package IP - accelerator 
  • Review and Package
    • merge * changes
  • Re-Package IP

Volviendo al proyecto original

 Create Block Design

  • Add IP
  • Zynq
  • run block automation 
  • Add IP
    • accelerator_bugged
  • Add IP 
    • uartlite
  • run connection automation
    • axi 
  • axi_uart_lite
    • expandir UART
      • externalizar tx_0 y rx_0
  • accelerator_bugged
    • externalizar tx_1
  • Save Block Design
  • Tools -> Validate
  • Sources -> Design Sources -> leaky_accelerator -> botón derecho -> Create HDL Wrapper
  • Generate bitstream
  • File -> Export Hardware
    • include bitstream
  • File -> Launch SDK
  • File -> New -> Application Project
  • ajustar constantes
  • program device
  • run as...


Falla...

Cuando arranca tx_1_0 esta high como debe, al enviar el programa pasa a low


  • IP INTEGRATOR -> Open Block Design
  • IP Catalog -> leaky accelerator -> botón derecho -> Edit in IP Packager
  • Package IP
    • Review and Package -> merge changes
    • Re-Package IP
  • Detecta que hubo cambio de IP
  • Report IP Status
  • Re run report
  • Upgrade selected
  • Generate bitstream
  • File -> Export Hardware
    • include bitstream
  • File -> Launch SDK
  • System.mss -> Re-generate BSP Sources


y así muchas veces hasta que te dás cuenta del error, que es en uart_tx, ¡¡¡no es sólo mío!!! No te puedo ofrecer mostrarte a ver si vos te dás cuenta... probemos:


module uart_tx
  #(parameter CLKS_PER_BIT = 870)
  (
   input       i_Clock,
   input       i_Tx_DV,
   input [7:0] i_Tx_Byte,
   output      o_Tx_Active,
   output reg  o_Tx_Serial,
   output      o_Tx_Done
   );

...

  reg [2:0]    r_SM_Main     = 0;
  reg [7:0]    r_Clock_Count = 0;
  reg [2:0]    r_Bit_Index   = 0;
  reg [7:0]    r_Tx_Data     = 0;
  reg          r_Tx_Done     = 0;
  reg          r_Tx_Active   = 0;

....

// Wait CLKS_PER_BIT-1 clock cycles for start bit to finish
  if (r_Clock_Count < CLKS_PER_BIT-1)
    begin
      r_Clock_Count <= r_Clock_Count + 1;
      r_SM_Main     <= s_TX_START_BIT;
      end
    else

 

Medio que al seleccionar trozos de código te lo tiré en la cara, ¿no? No, ¿y ahora?


  #(parameter CLKS_PER_BIT = 870)
...
   input [7:0] i_Tx_Byte,
...
     if (r_Clock_Count < CLKS_PER_BIT-1)

 

 

¿Ya lo viste?


  #(parameter CLKS_PER_BIT = 870)
...
   input [7:0] i_Tx_Byte,
...
     if (r_Clock_Count < CLKS_PER_BIT-1)

 

Se necesitan más de 8 bits (256 elementos) para contener 870. Supongo que la persona que hizo el código original de nandland, que había puesto 87 pues contaba con un clock de 10Mhz no previó que la gilada iba a tener 100Mhz o más.


La solución es tan sencilla como


  reg [12:0]   r_Clock_Count = 0;


No tengo ahora tiempo para documentarlo bien, pero para el diagnóstico de este problema hice un proyecto que usa uart_tx y lo activa al apretar un botón:


module top(
  input sysclk,
  input btn_send,
  output led_trans_up,
  output led_state,
  output [2:0] ar
);

localparam char = 8'b10100011;
wire trans_up;
wire state;
wire [2:0] bus;

assign bus = ar;
assign led_state = state;
assign led_trans_up = trans_up;

uart_tx uart_tx(
   .i_Clock(sysclk),
   .i_Tx_DV(trans_up),
   .i_Tx_Byte(char),
   .o_Tx_Active(bus[0]),
   .o_Tx_Serial(bus[1]),
   .o_Tx_Done(bus[2])
);

debouncer debouncer_enable(.CLK (sysclk),
  .switch_input(btn_send),
  .trans_up (trans_up),
  .state(state)
);

endmodule



UART-Tx en acción en aislación
UART-Tx en acción en aislación



En realidad hice una simulación, cuando le bajé a 8 para no tener que esperar 870 ciclos para cada bit y ví que funcionaba, comprendí el error:



`timescale 1ns/1ps

module uart_testbench;
  reg simul_Clock;
  reg send;
  wire active;
  wire done;
  wire tx;

localparam char = 8'b10100011;

  initial begin
    simul_Clock = 1'b0;
    forever simul_Clock = #2.5 ~simul_Clock;
  end

  initial begin
    send = 1'b0;
    #100 send = 1'b1;
    #10 send = 1'b0;
   
  end

  initial begin
    repeat(64) @(negedge simul_Clock);
    $finish;
  end

uart_tx uart_tx(
   .i_Clock(simul_Clock),
   .i_Tx_DV(send),
   .i_Tx_Byte(char),
   .o_Tx_Active(active),
   .o_Tx_Serial(tx),
   .o_Tx_Done(done)
);

endmodule



Simulación de UART_Tx
Simulación de UART_Tx

Se vé bien clarito como el send pone en active, manda el start bit y al finalizar el stop bit y pasa a done.

 

Cuando comparta el código en github, va a estar en la carpeta uart_tx.


Finalmente, a la 1:am, a sólo 16 horas de la demo, por que todo esto es para H4CK3D 2020, tras lidiar y renegar con unas señales, logré que:

 

Entra "aaaa", sale "dddd"
Entra "aaaa", sale "dddd"



Tanto en un pin como en un led, la última letra del cleartext
Tanto en un pin como en un led, la última letra del cleartext


 

Para ahorrarme trabajo sólo estoy enviando la última letra de cada bloque, quizás en algún momento lo mejore. Tampoco se está mostrando las señales de done y send, igual no eran parte del objetivo, sólo diagnóstico, si el cyan que es active.

Sólo me faltaría el circuito para tomar esa señal desde el led en lugar del pin.


Este es el diseño final:


Diseño final
Diseño final



Quedan mucho ajustes por hacer, como el rango de la memoria ocupada, comprobar que los tiempos y consumos estén ok, comprender y mejorar la organización de los proyectos,  y además muchos componentes y recursos por usar, tengo la sensación de haber llegado a un 1% o menos de comprensión/conocimiento/experiencia con este tema, me siento como hace mucho tiempo cuando hice

10 print "hola"
20 goto 10
run