2018/11/11

Parallella: FPGA, entrada del hash

Esto no ha resultado particularmente difícil. Aclaro que yo apenas sé VHDL y tengo que hacerlo en Verilog. Aún así, lo miré fijo y salió al primer intento.

Lo que hice fue buscar patrones, reproducirlos y extenderlos.


Preguntémosle a git diff cuál fué el cambio:



Desplacé el registro de salida 8 bytes para darle lugar a REG_INPUT2 e REG_INPUT3 y los demás cambios, no voy a mentir, de algún modo los entiendo pero soy absolutamente incapaz de explicarlos.

Lo que si puedo entender es el programa de test, que ahora toma los valores de la linea de comando, los parsea, escribe en las cuatro direcciones consecutivas de memoria y lee de la última el resultado de la operación.


#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>


#define REG_INPUT0 0x810f0000
#define REG_INPUT1 0x810f0004
#define REG_INPUT2 0x810f0008
#define REG_INPUT3 0x810f000C
#define REG_OUTPUT 0x810f0010

int main(int argc, char *argv[]) {

    uint32_t data0, data1, data2, data3, rdata;

    data0=strtol(argv[1],0,16);
    data1=strtol(argv[2],0,16);
    data2=strtol(argv[3],0,16);
    data3=strtol(argv[4],0,16);

    e_write(REG_INPUT0,data0);
    e_write(REG_INPUT1,data1);
    e_write(REG_INPUT2,data2);
    e_write(REG_INPUT3,data3);

    e_read(REG_OUTPUT, &rdata);

    printf ("RESULT=%d\n", rdata);        
}




2018/11/06

Parallella: FPGA, plan MD5

Habiendo logrado hacer funcionar un ejemplo básico de FPGA en la parallella, que consiste en escribir en dos posiciones de memoria y leer en otra el resultado de una operación matemática o lógica, rumbo a implementar MD5, corresponde ir modificando el ejemplo.



Partiendo de esta linea en src/accelerator/hdl/accelerator.v

   //(PUT CODE HERE!)
   assign result[31:0] = reg_input0[31:0] + reg_input1[31:0];



tengo que pasarle el hash que espero, de 16 bytes (128 bits). Esos reg_inputX ya son 64 bits, así que necesitaría reg_input2 y reg_input3.

Luego necesito un contador de 0x0000...0000 a 0xFFFF...FFFF, comenzando por 24 bits, 16777216 combinaciones, que es lo que vengo manejando como unidad de cálculo. Si fuera increiblemente veloz, tendría que expandir.

Luego una operación lógica muy sencilla aplicada sobre el contador.

Luego, una comparación de los reg_inputX con la salida de la operación lógica muy sencilla.

Y finalmente, reemplazar la función lógica muy sencilla por el pipeline de MD5



                                          +----------------+ 
                                      (1) |reg_input[127:0]|
                                          +--------+-------+
                                                   |
                                                 /128/
                                                   |    
+-------------+      +----------------+       +----v-----+
|counter[23:0]+-/24/->operación lógica+-/128/->comparador|
+-------------+      |  muy sencilla  |       +----------+
                     +----------------+
(2)                     (3)                       (4)  
                         |
                     evolución
                         |
                         V
                        (5)
            +-----+                        +-----+   
            |stage|                        |stage|
      -/24/->     +-/128/->  .....  -/128/->     |
            |  0  |                        |  63 |
            +-----+                        +-----+

Y eso es todo... no, falta todo el asunto del clocking, parar el contador cuando el comparador de igual y restar al contador la cantidad de etapas en el pipeline para saber cual era el valor originario de la comparación cierta, esto último en la CPU, no es una operación frecuente.
Y además el contador podría implementarse con DSPs, quizás el comparador tambien.
Y además, dependiendo de cuantos recursos queden libres, hacer dos o más calculadores.

Parallella: FPGA en 15 minutos


a.k.a.

Creating an FPGA accelerator in 15 minutes 2018

(feel free to ask me for an english version)


Recapitulando, mi objetivo es configurar la FPGA de la placa parallella para que calcule MD5 en general, en particular con las optimizaciones que apliqué durante el proceso en CPU:

  • loop unroll -> acá se llama pipeline
  • una sola ronda, pues el mensaje mide menos que el bloque
  • valores precalculados, pues hay una sola ronda
  • valores cortocircuitados, pues el mensaje a los sumo medirá 12 bytes, o sea que afecta a tres de las sesenta y cuatro operaciones.
Todo eso más o menos ha sido implementado en las versiones detalladas en destrozando brutalmente MD5

Además, a diferencia de las versiones de CPU, que exploraban desde ';;;;' hasta 'zzzz', o sea, 24 bits (64 que es la distancia entre ';' y 'z' a la cuarta por que son cuatro posiciones), ahora la exploración será desde 0x00000000 hasta 0xFFFFFFFF o quizas desde 0x000000 a 0xFFFFFF para aprovechar un DSP, veremos.


Pasos


He seguido las instruciones de [1] y tras mucho renegar, estas son los pasos completos y adaptaciones.

Como me llevó varios meses de trabajo discontinuo, hay algunas anotaciones que no son muy claras, pues en ese momento tenían completo sentido y ahora al releer carecen por completo de él.


Registrarse en Xilinx

Al intentar el siguiente paso, el sistema te guía.

Bajar vivado 2015.2


https://www.xilinx.com/support/download/index.html/content/xilinx/en/downloadNav/vivado-design-tools/archive.html



Instalar vivado


  • Vivado Design Suite - 2015.2  Full Product Installation
  • elegí system edition
  • soporte sólo para zynq 7000 (luego agregué artix me parece)
  • en lugar de /opt/Xilinx instalé en ~/Xilinx

Es muy prolijo el instalador, permite múltiples versiones.


Obtener e instalar licencia

Seguir los pasos que indican este programa:

~/Xilinx/Vivado/2015.2/bin/vlm


Bajar el adjunto del mail

Esto no sé si hace falta, pues no lo he vuelto a poner y ha funcionado

export XILINXD_LICENSE_FILE=$PATH/Xilinx.lic


Generar

  • Obtener repositorio y usarlo:

git clone https://github.com/parallella/oh


cd oh

  • Hacer checkout en el último commit que funciona con 2015.2 y no falla con el error de CFG_ASIC:

git checkout afccd4a


  • Ajustar path a vivado y target:


. ../setupPath2015_2.sh
../setTarget7010.sh


  • Empalmar con los pasos de [1]:

. setenv.sh
./run.sh accelerator
cd src/accelerator/fpga/
./build.sh

Desplegar

Tras haber aplicado la quick start guide, usando la imagen
 
ubuntu-14.04-headless-z7010-20150130.1.img




Hay dos maneras, primero la más conservadora:

En la SD copiar src/accelerator/fpga/parallella.bit.bin a la partición boot.

sudo cp src/accelerator/fpga/parallella.bit.bin /media/$USER/boot/


Luego, la más cool:

Copiar de algún modo parallella.bit.bin al root filesystem de la parallella y aplicarla con el comando:

cat parallella.bit.bin > /dev/xdevcfg


que no lo he probado aún funciona ok!!!!.

Por el tiempo que lleva generar los bitstreams, la técnica que estoy usando es tener cada versión en el root filesystem catalogada así puedo cambiarlos bien rápido. Ver [2] para el detalle completo.


Arrancar

Para hacerla funcionar con terminal serial, seguir los pasos de Parallella paradumbs: trabajando mas cómodo  -> Ubuntu 14.04 -> Device Tree, donde se le instruye al kernel que use la consola en el puerto serial.

Supongo que por alguna incompatibilidad entre el kernel/distro y el bitstream, aparentemente se queda catastróficamente sin memoria y muere cuando está levantando los servicios. Basta con arrancar con runlevel 1, ejecutar

stty echo
unalias ls
PS1='$> '


Para runlevel 1 seguir los pasos de Parallella paradumbs: trabajando mas cómodo  -> Ubuntu 14.04 -> Device Tree, con:


        chosen {
          bootargs = "console=ttyPS0,115200 root=/dev/mmcblk0p2 rw earlyprintk rootfstype=ext4 rootwait 1";
          linux,stdout-path = "/amba@0/serial@e0001000";
       };


 (mirá fijo hasta que veas el 1 al final de bootargs):

 

 Probar


Lo que hace el ejemplo provisto es una operación aritmética entre dos posiciones de memoria y devolver en una tercera:

[src/accelerator/hdl/accelerator.v]

   //#############################
   //#ACCELERATOR
   //#############################

   //(PUT CODE HERE!)
   assign result[31:0] = reg_input0[31:0] + reg_input1[31:0];



Compilar driver.c y test.c previamente compiado en el root filesystem desde src/accelerator/sw/*  y ejecutarlo:




Un poquito más


Considero haber llegado acá tanto un éxito como el punto de partida para poder implementar en la FPGA el algoritmo MD5, pero sin red se va a complicar ir probando los distintos bitstreams, asi que...



Me niego a resetear la parallella por no poder enviar un Control-C.

Iba a ser una lista de la infamia con cutecom, gtkterm, miniterm.py, pero pude comprobar que el problema no está en las terminales:





Ok, tuve que tirar del cable.

No pude hacer que arranque el servidor de ssh, pero funciona netcat.

De todos modos, con runlevel 1 dhcp está activo.




Próximos pasos



Lo que sigue es modificar el bloque ACCELERATOR hasta que haga MD5, varios meses más...

Aunque pensándolo bien...


¿...para qué necesito un sistema operativo, si lo único que quiero hacer es mostrar en algún lado el valor tal que genera un hash?

Calculo que con un baremetal en una de las cpu arm del zynq, acceso a la terminal serial y la mínima comunicación con el calculador de MD5 en la FPGA ya debería alcanzar.

Menos que eso voy a hacer si consigo otra FGPA, estoy en ello.

Pero ya he invertido y aprendido tanto en este camino que lo voy a continuar:

Sólo para no decir que las uvas estaban verdes.



Crónica de diagnósticos


Por supuesto que a la primera no funcionó. Como había dicho, me llevó mucho mas que 15 minutos.

Aparentemente lo que ha ocurrido es que el proyecto tuvo una deriva con respecto a generar, por correcciones naturales, quizás por merges, por pasar de 2015.2 a 2015.4, no sé.

CFG_ASIC


El primer error bloqueante fue:


ERROR: [Synth 8-2841] use of undefined macro CFG_ASIC [~/oh/src/accelerator/fpga/system.srcs/sources_1/ipshared/www.parallella.org/axi_accelerator_v1_0/e04ae5f4/src/oh_memory_dp.v:36]

Me han dicho o he hallado que pase a 2015.4, que use el branch stable, miré como se construyen otras cosas, nada, es demasiado complejo para un hello world para mi.
Finalmente cerré los ojos y elegí un commit más o menos arbitrario(), que estuviera cerca de master (258cda9). De ahí me fuí acercando tipo bisecting (2f91330, fbfe559, afccd4a, ee2e234) hasta el último que funciona con las instrucciones simples (afccd4a)./


FAIL CFG_ASIC 2017-04-24 21:57:56 70a6f14 Merge pull request #99 from olajep/zcu102
FAIL CFG_ASIC 2017-02-10 17:00:55 2f91330 common/fpga/create_ip.tcl: Fix error when sub-IP is locked
?             2017-02-09 23:03:51 37a38ab zcu102: zcu102: Use Petalinux 2016.4 design as base
?             2017-02-07 23:02:10 02955c0 zcu102: zcu102: Define oh_verilog_define F
FAIL CFG_ASIC 2017-02-07 23:01:50 fbfe559 fpga/system_build.tcl: Support oh_verilog_define flag
FAIL CFG_ASIC 2017-02-07 19:11:05 ee2e234 Revert "common/hdl: Fix syntax error when CFG_ASIC is undefined"
OK            2017-02-07 18:47:42 afccd4a zcu102: zcu102: Fix Makefile deps and clean target
?             2017-02-07 18:45:52 8706590 zcu102: zcu102: Remove cclk1 port
OK            2017-02-07 18:44:04 258cda9 fpga/system_build.tcl: Create files for SDK


Durante el diagnóstico, varias conversaciones me resultaron útiles[3]. Considerando que es un proyecto muerto, me han contestado bastante.


Quizás más adelante cuando haya aprendido mucho más, pueda probar de acercarme un poquito más master viendo que pasa con 2015.4 o a stable. Difícil, tengo que comprender como construir todo.



Más detalles de CFG_ASIC


Este error ocurre en el paso de ./build.sh

#create bit stream
vivado -mode batch -source run.tcl


Primero hice git grep CFG_ASIC para localizar donde se estaba seteando:

scripts/build.sh: -DCFG_ASIC=0\

src/parallella/fpga/headless_e16_z7010/system_params.tcl:set oh_synthesis_options "-verilog_define CFG_ASIC=0"

src/parallella/fpga/headless_e16_z7020/system_params.tcl:set oh_synthesis_options "-verilog_define CFG_ASIC=0"

src/zcu102/fpga/zcu102/system_params.tcl:set oh_verilog_define "CFG_ASIC=0"



Luego ejecuté  

strace -eopen -f -o trace.txt ./build.sh 

para ver si alguno de esos archivos aparte del primero estaba siendo abierto, no, no son abiertos...


Luego ejecuté  

strace -ff -o trace.txt ./build.sh

para ver si alguno tenía en sus argumentos  CFG_ASIC y nada.



Upgrade a 2015.4


El upgrade aparentemente fue exitoso, pero no corrigió el error de CFG_ASIC

Dejo acá lo aprendido para futura referencia, te debo los snapshots:


Correr con 2015.2 primero para que genere el proyecto.

Ejecutar setVersion2015_4.sh

Abrir con vivado src/accelerator/fpga/axi_accelerator.xpr

Aceptar autocorrección.

Luego "Tools => Report => Report IP Status...", upgrade selected



En la terminal de TCL ejecutar:

write_bd_tcl -force system_bd.tcl

que es más o menos lo mismo que dice el mensaje de warning cuando se usa el proyecto directamente con 2015.4

This script was generated using Vivado <2015.2> and is being run in <2015.4> of Vivado. Please run the script in Vivado <2015.2> then open the design in Vivado <2015.4>. Upgrade the design by running "Tools => Report => Report IP Status...", then run write_bd_tcl to create an updated script.

 

Missing /dev/epiphany


El segundo problema importante con el que me topé es que driver.c contiene este código:

  //Open /dev/mem file if not already
  if(mem_fd < 1) {
    mem_fd = open ("/dev/epiphany", O_RDWR);
    if (mem_fd < 1) {
      perror("f_map");
      return -1;
    }
  }


y obviamente /dev/epiphany no existía, pese a:

root@parallella:/lib/modules/4.6.0+# find . -iname epi*
./kernel/drivers/misc/epiphany.ko

root@parallella:~# zgrep --color=never -i epiphany /proc/config.gz
CONFIG_EPIPHANY=m
# CONFIG_DEBUG_TIMEKEEPING is not set

root@parallella:~# modmod epiphany
root@parallella:~# lsmod
Module Size Used by
epiphany 19123 0

.

Ahí estoy a un mknod de terminar!!!!

Luego me dí cuenta que estaba muy cansado o había un comportamiento errático, usando una distro "limpia", si existía /dev/epiphany, pero era una carpeta:

$> ls /dev/epiphany/ -al
total 0
drwxr-xr-x 2 root root 80 Aug 18 00:04 .
drwxr-xr-x 13 root root 3280 Aug 18 00:05 ..
crw-rw-rw- 1 root root 243, 0 Aug 18 00:04 elink0
crw-rw-rw- 1 root root 243, 1 Aug 18 00:04 mesh0


Primero quise ver en cada imagen disponible como era, para lo cual elaboré un bonito script Mount partitions inside disk image y ahí me dí cuenta que no lo iba a hallar, pues es un módulo, se debe crear en tiempo de ejecución.

Así que tuve que ir quemando cada distro... y la que funcionó fue
ubuntu-14.04-headless-z7010-20150130.1.img

Para hacerla funcionar con terminal serial, seguir los pasos de Parallella paradumbs: trabajando mas cómodo  -> Ubuntu 14.04 -> Device Tree


Freeze en arranque


Tras haber lidiado con todo lo anterior, hacia el final del booteo se me frena, primero con

 * Starting regular background program processing daemon␛[74G[ OK ]
 * Starting deferred execution scheduler␛[74G[ OK ]
 * Starting OpenSSH server␛[74G[ OK ]

Tras haber desactivado OpenSSH (/etc/ssh/...), sigue igual:


 * Starting save kernel messages␛[74G[ OK ]
 * Starting regular background program processing daemon␛[74G[ OK ]
 * Starting deferred execution scheduler␛[74G[ OK ]

Me imaginé que tiene que ver con la memoria y me encaminé  a runlevel 1 como para poder seguir, sabiendo que igual hay que corregir pues sin red y SSH se torna muy difícil estar actualizando el bitstream. Me queda como plan B arrancar cada vez que lo cambie, pero puede ser terrible...


Luego, pensé en comparar el device tree contra las imágenes menos viejas, pero si ya había arrancado ok, puede haber un problema en el bitstream, ya sea por el proyecto accelerator o por el target zynq 7010 vs 7020.

Entonces, zynq 7010 vs 7020, hasta donde yo puedo llegar que es cambiar el target a todos los archivos, cambiando los "cercanos", no es.

Con runlevel 1 arranca y aunque cuesta usarlo por que no hay echo, FUNCIONA EL EJEMPLO!!!!, desde aquí sólo resta mejorarlo.



Scripts

Mientras diagnosticaba las fallas, elaboré algunos scripts útiles que puse en... scripts:

setupPath2015_2.sh
setupPath2015_4.sh

setVersion2015_2.sh
setVersion2015_4.sh

setTarget7010.sh
setTarget7020.sh




De este no estoy tan seguro, relevé los archivos borrando .gitignore tras construir y ejecutando git status.


clean.sh

Todos en [4]

Tambien elaboré esta linda linea de git:


git log --graph --decorate \
--pretty=format:"%Cgreen %ci %Cred %d %Cblue %h %Creset %s"



Notas


[1] Creating an FPGA accelerator in 15 minutes 

[2]  

[3]

https://parallella.org/forums/viewtopic.php?f=51&t=4297&p=20096#p20096

https://parallella.org/forums/viewtopic.php?f=49&t=3411&start=10

[4] https://github.com/cpantel/oh/tree/2018