2021/03/24

Refactorizaciones de MD5 en FPGA: 4 algunos arreglitos

Venís de poder ingresar hashes arbitrarios.


Ajustes de los probes

Había dicho que el haber partido el probe de 128 bits en dos de 64 no me quitaba el sueño y habrás pensado que había algo más.

Pues si, tengo en mi desván mental la idea de AXI <=> 32 bits, así que escarbé superficialmente y en algún manual encontre que:

 ARM expects that:

    the majority of components use a 32-bit interface

    only components requiring 64-bit atomic accesses use a 64-bit interface.
 


y no veo motivo para contrariar a ARM, así que partir los 128 en cuatro de 32 medio que estaba en mis planes, sólo me quedó el resentimiento de no haber podido comprender si VIO/Logic Analyzer no se llevan bien con 128 bits o fue a raiz de mi error que no pude usar un probe de 128 bits, eso está en la entrada anterior, olvidémoslo.

Sintetizando, voy a partir en cuatro los 128 bits porque parece ser lo más sencillo para el futuro, que no sé si llegará.

De paso, para poder automatizar toda la interacción, pase el botón de reset a VIO, una pena, me gustaba usar el botón del reset de la CPU...

Partido en cuatro:

// virtual inputs
   wire           enable_switch;
   wire           reset_switch;  
   wire [  4 : 0] probe_in0;
   wire [ 31 : 0] probe_in1;
   wire [  1 : 0] probe_out0;
   wire [ 31 : 0] probe_out1;
   wire [ 31 : 0] probe_out2;
   wire [ 31 : 0] probe_out3;
   wire [ 31 : 0] probe_out4;
  
// VIO patching
   assign probe_in0  = { status_paused,
                         status_running,
                         status_warming,
                         status_found,
                         status_done
                       };
   assign probe_out0 = { reset_switch,
                         enable_switch
                       };
   assign probe_in1  = target;
  

vio_0 vio_driver (
  .clk(CLK),                // input wire clk
  .probe_in0(probe_in0),    // input wire [4 : 0] probe_in0
  .probe_in1(probe_in1),    // input wire [31 : 0] probe_in1
  .probe_out0(probe_out0),  // output wire [0 : 0] probe_out0
  .probe_out1(probe_out1),  // output wire [31 : 0] probe_out1
  .probe_out2(probe_out2),  // output wire [31 : 0] probe_out2
  .probe_out3(probe_out3),  // output wire [31 : 0] probe_out3
  .probe_out4(probe_out4)   // output wire [31 : 0] probe_out4 
);


Testing con Tcl

Dejando de lado la simulación, para testear sobre todo al comienzo utilicé métodos muy cavernícolas, directamente sobre la placa, usando la opción de avanzar el contador de a un paso con un botón y me parece que tambien inspeccionando con los 8 dígitos de 7 segmentos estados intermedios.

Ahora, con VIO, puedo tanto con la GUI interactuar de modo más sencillo y el valor resultante copiarlo y pegarlo en lugar de transcribirlo a mano, un cierto progreso.

Pero seguiría siendo un cavernícola... mejor digo, siendo un cavernícola, voy a pasar de la Edad de Piedra a la Edad de Bronce e interactuar con VIO usando la consola Tcl, desde la cual puedo ejecutar scripts tanto con comandos como con lógica.

Lo primero es descifrar cómo se lee y escribe, que es muy sencillo, en Vivado Design Suite User Guide: Programming and Debugging (UG 908) desde la página 205 hay algunas explicaciones y además mirando lo que Vivado va ejecutando en la consola no podés dejar de entender algo, sólo que lleva un tiempito.

Por ahora y quizás por siempre, no me interesan los detalles y el protocolo subyacente, lo usaré tipo caja negra. Mi POC consiste en poder ejecutar un ciclo completo de reset, cargar hash, habilitar, deshabilitar, evaluar si fué hallado y mostrarlo.

El formato de este blog no es el mejor para mostrar las sucesivas refactorizaciones, agradeceré tu mejor esfuerzo.

Si es tu primer contacto con Tcl:

set variable valor  equivale a variable = valor 

set variable1 $variable2  equivale a variable1 = variable2

funcion arg1 arg2 equivale a funcion(arg1, arg2)

set var [ funcion arg1 arg2 ] equivale a var = funcion(arg1, arg2)

En general, la clave de entender Tcl es que son todas funciones, incluido el set, if, while:

while condicion acción : la función while recibe dos argumentos.

 

Con respecto a {}, yo sabía programar bastante bien en Tcl, pero eso fue hace 20 años y para esto no necesito recuperar tanto, así que no estoy en condiciones de explicarlo responsablemente. Podés RTFM y ver la previous y next lesson. Si más o menos sabés programar en dos o tres lenguajes, tu sistema de pattern recognition seguramente te permitirá entender todo lo que sigue.

Versión base

En esta versión, pegué todo el código sin mayor pensamiento en escalabilidad:


# Deshabilitar el contador

set_property OUTPUT_VALUE 0 [get_hw_probes enable_switch]
commit_hw_vio [get_hw_probes {enable_switch}]
 

# Apretar y soltar el botón de reset


startgroup
set_property OUTPUT_VALUE 0 [get_hw_probes reset_switch]
commit_hw_vio [get_hw_probes {reset_switch}]
endgroup

startgroup
set_property OUTPUT_VALUE 1 [get_hw_probes reset_switch]
commit_hw_vio [get_hw_probes {reset_switch}]
endgroup


# Cargar las cuatro partes del hash

set_property OUTPUT_VALUE 2d1bbde2 [get_hw_probes probe_out1]

commit_hw_vio [get_hw_probes {probe_out1}]

set_property OUTPUT_VALUE acac0afd [get_hw_probes probe_out2]
commit_hw_vio [get_hw_probes {probe_out2}]

set_property OUTPUT_VALUE 07646d98 [get_hw_probes probe_out3]
commit_hw_vio [get_hw_probes {probe_out3}]

set_property OUTPUT_VALUE 154f402e [get_hw_probes probe_out4]
commit_hw_vio [get_hw_probes {probe_out4}]

# Habilitar el contador

set_property OUTPUT_VALUE 1 [get_hw_probes enable_switch]
commit_hw_vio [get_hw_probes {enable_switch}]

set status_done 0

while {$status_done == 0} {

      # Leer el valor de status_done

    refresh_hw_vio [get_hw_vios {hw_vio_1}]
    set status_done [ get_property INPUT_VALUE [get_hw_probes status_done] ]
    after 1000
}

      # Leer el valor de status_found

set status_found [ get_property INPUT_VALUE [get_hw_probes status_found] ]


if {$status_found == 1} {

      # Leer el valor hallado

    refresh_hw_vio [get_hw_vios {hw_vio_1}]
    set result [ get_property INPUT_VALUE [get_hw_probes target] ]
    puts $result
} else {
    puts "Not found"
}

# Deshabilitar el contador


set_property OUTPUT_VALUE 0 [get_hw_probes enable_switch]
commit_hw_vio [get_hw_probes {enable_switch}]


Primera refactorización: procedures


Tenemos un nuevo elemento, proc, que recibe tres argumentos: nombre del procedimiento, argumentos y código a ejecutar.

Con respecto a startgroup/endgroup, me desasné en UG 835 Vivado Design Suite TclCommand Reference Guide, del cual no tengo link, pues para variar usé "Documentation Navigator", una simpática aplicación que te permite buscar documentación y en el caso de los pdf te muestra la última versión y opcionalmente anteriores y te los va bajando a ~/Documents/XilinxDocs/Vivado/documentation/sw_manuals/xilinx2019_2, en este caso. Me imagino que depende de lo que bajes en lugar de 2019_2 dirá otra cosa.

Me resulta medio raro no tener bajados los archivos junto al resto de los miles de manuales, pero es bastante cómodo, creo que voy a tirar un symlink para tener lo mejor de los dos mundos.

Volviendo a xxxgroup, viene a ser parece para hacer transacciones reversibles, por ahora las voy a dejar, pero en la próxima iteración probaré quitarlas.

 

proc reset {} {
    startgroup
    set_property OUTPUT_VALUE 0 [get_hw_probes reset_switch]
    commit_hw_vio [get_hw_probes {reset_switch}]
    endgroup

    startgroup
    set_property OUTPUT_VALUE 1 [get_hw_probes reset_switch]
    commit_hw_vio [get_hw_probes {reset_switch}]
    endgroup
}

proc disable {} {
  set_property OUTPUT_VALUE 0 [get_hw_probes enable_switch]
  commit_hw_vio [get_hw_probes {enable_switch}]
}

proc enable {} {
  set_property OUTPUT_VALUE 1 [get_hw_probes enable_switch]
  commit_hw_vio [get_hw_probes {enable_switch}]
}

proc writeHash {hash1 hash2 hash3 hash4} {
  set_property OUTPUT_VALUE $hash1 [get_hw_probes probe_out1]
  commit_hw_vio [get_hw_probes {probe_out1}]

  set_property OUTPUT_VALUE $hash2 [get_hw_probes probe_out2]
  commit_hw_vio [get_hw_probes {probe_out2}]

  set_property OUTPUT_VALUE $hash3 [get_hw_probes probe_out3]
  commit_hw_vio [get_hw_probes {probe_out3}]

  set_property OUTPUT_VALUE $hash4 [get_hw_probes probe_out4]
  commit_hw_vio [get_hw_probes {probe_out4}]
}

proc readStatus {} {
  refresh_hw_vio [get_hw_vios {hw_vio_1}]
  return [ get_property INPUT_VALUE [get_hw_probes status_done] ]
}

proc readFound {} {
  refresh_hw_vio [get_hw_vios {hw_vio_1}]
  return [ get_property INPUT_VALUE [get_hw_probes status_found] ]
}

proc readResult {} {
  refresh_hw_vio [get_hw_vios {hw_vio_1}]
  return [ get_property INPUT_VALUE [get_hw_probes target] ]
}

#inicio

reset

disable

writeHash 2d1bbde2 acac0afd 07646d98 154f402e

enable

set status_done 0

while {$status_done == 0} {

    set status_done [ readStatus ]
    after 1000
}

set status_found [ readFound ]


if {$status_found == 1} {
    set result [ readResult ]
    puts [ readResult ]
} else {
    puts "Not found"
}


disable

 

Segunda refactorizacion: más usable

Evité repeticiones y toma el hash en una sola pieza:


proc readProbe {pin} {
  refresh_hw_vio [get_hw_vios {hw_vio_1}]
  return [ get_property INPUT_VALUE [get_hw_probes $pin] ]
}

proc writeProbe {value pin } {
    set_property OUTPUT_VALUE $value [get_hw_probes $pin]
    commit_hw_vio [get_hw_probes ${pin}]
}

proc reset {} {
  writeProbe 0 reset_switch
  writeProbe 1 reset_switch
}

proc disable {} {
  writeProbe 0 enable_switch
}

proc enable {} {
  writeProbe 1 enable_switch
}

proc writeHash {hash} {
  writeProbe [ string range $hash  0  7 ] probe_out1
  writeProbe [ string range $hash  8 15 ] probe_out2
  writeProbe [ string range $hash 16 23 ] probe_out3
  writeProbe [ string range $hash 24 31 ] probe_out4
}

proc readResult {} {
  return [ readProbe target]
}

proc isDone {} {
  return [ readProbe status_done ]
}

proc isFound {} {
  return [ readProbe status_found ]
}

reset

disable

writeHash 2d1bbde2acac0afd07646d98154f402e

enable

while { ! ( [ isDone ] ) } {
    after 1000
}

if { [ isFound ] } {
    puts [ readResult ]
} else {
    puts "Not found"
}

disable

 

Lo próximo será utilizar el código anterior para ejectuar muchas búsquedas para valores interesantes y así ver si hay errores (los hay en los extremos) y ajustar correctamente el valor obtenido:


El código en github

Notas relacionadas


 

No hay comentarios:

Publicar un comentario