2021/12/20

Forzador MD5 en PYNQ, el hardware

Este es el detalle del trabajo práctico de la materia Micro Arquitecturas y Soft Cores, ya comentada previamente.

El objetivo es instanciar el Forzador Brutal de MD5 en un chip ZYNQ de una placa PYNQ-Z2 proveniente de una Nexys4DDR que es más grande, ya habiendo fracasado durante la cursada de Basic Digital Design agregando VIO e ILA como interfaz. En esta oportunidad la interfaz será AXI hacia los cores donde se ejecutará un programita escrito en C.

 

Pasos

Necesito saber previamente algunos datos:

Número de registros para el periférico AXI

  • para determinar el hash (input, 128 bits, 16 bytes, 4 registers )
  • para obtener el valor (output, 32 bits, 4 bytes, 1 register)
  • control para iniciar proceso (input, 1 bit, 1 register)
  • status (output 3 bits, 1 register)
    • running
    • found
    • done

Son 7 registros.

 

Respecto a estos pasos, tengo un fuerte Déjà vu, es que ya lo he hecho antes pero entendiendo menos al hacer los ejercicios de dos libros.

 

Crear el IP

  • Tasks -> manage IP -> New IP Location
    • Part: xc7z020clg400-1
    • Target Language: Verilog
    • Crear y seleccionar una carpeta, en esta aparecerá todo lo que cree Vivado
  •  Tools -> Create and Package New IP
    • Create a new AXI4 peripheral
    • Name: ip_md5
    • Number of registers:7
    • Interfaces: quizás quitar el "00"
    • Next Step: Edit IP
  • Copiar los archivos del proyecto original a  ip_repo/ip_md5_1.0/hdl
    • Agregarlos con Add Design Sources
      • mejor NO copy sources into IP Directory
    • en este momento
      • ya se ha creado el C con los drivers de bajo nivel
      • es buena idea hacer un commit
  • Editar el ip de S_AXI, ip_md5_v1_0_S_AXI_inst
    • Acá va la magia, ver luego
  • Flow -> Run Synthesis
  • Flow -> Package IP 
    • identification
      • add category
    • file groups
      • merge changes from file groups wizard
    • customization parameters
      • merge changes from customization parameters wizard
    • ports and interfaces
      • hay que ver que esté lo que hayas puesto, en este caso status
    •  review and package
      • re-package IP
  • Otro buen momento para un commit 
  • close project 

 

 Crear el proyecto

  • File -> project -> new
    • location lo puse a la par que ip_repo
    • varios next
    • choose board -> pynq-z2
    • varios next
  • Settings 
    • Project Settings
      • IP
        • Repository -> ADD
          • lo del paso Crear IP
  • IP Integrator -> Create block design
  • Add IP -> zynq
  • run block automation
  • re-customize IP ( doble click)
    • ps-pl configuration 
      • general
        • enable clock resets
          • fclk_reset0_n
      • Axi non secure enablement
        • gp master axi interface
          • m axi gp0 interface ON
    • MIO Configuration
      • I/O Peripherals
        • UART 0 -> MIO 14::15
    • clock configuration
      • pl fabric clocks
        • fclk_clk0 -> 100
  • Add  IP ->
  • run connection automation
  • si tenías ports
    • make external
    • agregar el xdc y ajustarlo
  • validate design
  • source ->design_1.bd -> create hdl wrapper
  • source -> design_1.bd -> generate output products (paciencia)
  • Program and debug
    • generate bitstream (más paciencia)
  • file
    • export
      • export hardware -> include bitstream
  • file
    • launch sdk

SDK

  • comprobar en system.hdf que esté la IP y la uart
  • comprobar que en wrapper platform esten los drivers 
  • file
    • new
      • application project
        • empty application template
        • comprobar bsp-> ps7_cortexa9_0-> include -> ip_md5.h
  • el programa en C que quieras, otro día vemos más detalles.
  • botón derecho sobre el proyecto -> run as -> launch on hardware (GDB)
  • un segundo antes abriste la terminal:
    • miniterm.py  /dev/ttyUSB1  115200
    • y para un programa muy sencillo:

printf("MD5 Brute Forcer SelfTest\r\n");
   MD5_ACCELERATOR_Reg_SelfTest(
   XPAR_MD5_ACCELERATOR_0_S_AXI_BASEADDR
);

    • una salida muy sencilla:

Salida seft test
Salida seft test


Detalles

 

En el comienzo me equivoqué con el conteo de registros, pensé que eran 6, sin embargo fue intutitiva la corrección del hdl generado. Como por otros motivos tuve que arrancar de cero, al comparar la nueva generación con lo corregido comprobé que la corrección fué correcta.


Respecto a la paciencia en "Generate Output Products", nunca avisa que termina y arriba a la derecha el iconito de trabajando queda activo y dice "Synthesis out-of-date_". La manera de saber cuando ha terminado es mirando en la ventanita de Design Runs.


output products corriendo
output products corriendo

output products corriendo
output products finalizado


La magia


Primer nivel del wrapper


Si tu IP no usa puertos, no hay nada que tocar. En mi caso, para diagnosticar puse unos puertitos y se los conecté.

va en la interfaz:

      output wire [5:0] status,

en la instanciación de S_AXI:

      .status(status),

 

Segundo nivel del wrapper


Si tu IP no usa puertos, no hay nada que tocar en la interfaz, en este caso si, entonces:

        output wire [5:0] status,

Luego van los registros de salida:

    // Add extra user logic register here
    
    wire [C_S_AXI_DATA_WIDTH-1:0]    target_o;
    wire [C_S_AXI_DATA_WIDTH-1:0]    status_o;

    // I/O Connections assignments

En donde se leen, hay que reemplzar los registros slaves por estos nuevos:

// Address decoding for reading registers
case ( axi_araddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] )
    3'h0   : reg_data_out <= slv_reg0;
    3'h1   : reg_data_out <= slv_reg1;
    3'h2   : reg_data_out <= slv_reg2;
    3'h3   : reg_data_out <= slv_reg3;
    3'h4   : reg_data_out <= slv_reg4;
    3'h5   : reg_data_out <= target_o;
    3'h6   : reg_data_out <= status_o;
    default : reg_data_out <= 0;
endcase


Y por último, la conexión de cada bit de status al registro de status y la instanciación de nuestro IP:

// Add user logic here
    assign status = {status_o[0],status_o[1],
    status_o[2],status_o[3],
    status_o[4],status_o[5]};
       
    driver driver (
       .CLK(S_AXI_ACLK),
       .CPU_RESETN(S_AXI_ARESETN),
       .target_selected({slv_reg3,slv_reg2,slv_reg1,slv_reg0}),
       .enable_switch(slv_reg4[0]),
       .target(target_o),
       .status_paused(status_o[0]),
       .status_running(status_o[1]),
       .status_warming(status_o[2]),
       .status_found(status_o[3]),
       .status_done(status_o[4]),
       .enabled(status_o[5])
    );
    // User logic ends

 

Cuando metés la pata en el IP


La recomendación que he recibido y que sufrí no respetar, es si algo está mal, empezar otra vez de cero. Me parece que no hace falta tanto como de cero, estoy seguro que cuando incorporé los puertos de diagnóstico no lo hice, pero si tirar la sdk.

 

Edición del IP


  • Tasks -> manage IP -> Open IP Location
  • elegir el ip -> edit in IP Packager
  • Corregir el HDL
    • Acá va la magia, corregida
  • Flow -> Run Synthesis
  • Flow -> Package IP 
    • quizás File Groups
    •  review and package
      • re-package IP

 

Editar el proyecto


  • File -> project -> recent
  • IP Integrator -> open block design
    • aparece un mensaje diciendo que hay que hacer upgrade
    • report ip status
      • upgrade selected
    • te ofrece generate output products (paciencia)
    •  
  • Program and debug
    • generate bitstream (más paciencia)
  • [opcional] borrar el .sdk para evitar conflictos
  • file
    • export
      • export hardware -> include bitstream
  • file
    • launch sdk


Respecto a borrar el sdk antes de reexportar, no te olvides de tener copia o todo versionado para recuperar lo que ya hayas hecho. Debido a que mi IP usa Xil_In  y Xil_out, provistos por xgpio.h, cada vez tuve que restaurarlos, obtenidos originalmente de lo generador por uno de los labs.

 

Simulación


Agregué un test bench conectado al componente y a ese testbench lo puse como top. Si ejecutas la simulación con el wrapper como top, estás muerto, tendrías que generar los estímulos para AXI, olvidate...



Notas relacionadas por el lado de EAMTA 2021:

 

No hay comentarios:

Publicar un comentario