2021/09/09

H4CK3D 2021: leyendo los botones de la placa

Para entender el contexto, te recomiendo leer antes los primeros pasos.

Habíamos quedado en que necesitaba leer los botones y ya había spoileado que icicle no tiene implementado GPIO.

set_io clk       94
set_io leds[0]   1
set_io leds[1]   2
set_io leds[2]   3
set_io leds[3]   4
set_io leds[4]   7
set_io leds[5]   8
set_io leds[6]   9
set_io leds[7]   10
set_io uart_rx   55
set_io uart_tx   56

 

Lo primero que me llamó la atención es que tuviera en el archivo .pcf declarados 8 leds, considerando que la placa tiene 4. Tras consultar el pinout, no sé si soy corto o no está muy fácil de encontrar, y con el tester comprobé que de 4 a 7 están en un PMOD, ok.

Para ir entendiendo el panorama seguí a rx y tx de la uart, suponiendo que por ser de lectura y escritura me iban a contar algo, pero no. 

Miré el código en el programa, ok, esto me cuenta que los leds están mapeados en la dirección 0x00010000.

 

#define LEDS        *((volatile uint32_t *) 0x00010000)
#define UART_BAUD   *((volatile uint32_t *) 0x00020000)
#define UART_STATUS *((volatile uint32_t *) 0x00020004)
#define UART_DATA   *((volatile  int32_t *) 0x00020008)


Este caminito que te conté no es exactamente el que seguí, lo mío tuvo una trayectoria más cuántica y por más que intenté tomar nota, no deja de ser un relato un poco inventado.

Le cambié los nombres en el .pcf:

set_io leds[0]   1
set_io leds[1]   2
set_io leds[2]   3
set_io leds[3]   4
set_io pmod[4]   7
set_io pmod[5]   8
set_io pmod[6]   9
set_io pmod[3]   10

 

y si sabés más que yo anticiparás que me dió error, pues en algún lugar del HDL dice [7:0] leds y no [3:0] leds.

 

Warning: unmatched constraint 'pmod[4]' (on line 6)
Warning: unmatched constraint 'pmod[5]' (on line 7)
Warning: unmatched constraint 'pmod[6]' (on line 8)
Warning: unmatched constraint 'pmod[7]' (on line 9)
ERROR: IO 'leds[7]' is unconstrained in PCF (override this error with --pcf-allow-unconstrained)
ERROR: Loading PCF failed.
4 warnings, 2 errors
make: *** [arch/ice40.mk:24: top_syn.asc] Error 255

 

Decidí ir al último recurso que es ver la documentación y en el README.md dice que tiene:

 

Current Features
  • Memory-mapped UART and LEDs.

y que no tiene:

Planned features

  • Memory-mapped GPIOs.

 

Esto me cuenta que los leds no están implementados como GPIOs, que no hay manera de leer los botones con lo implementado.

Momento de pánico, casi sale mail al autor solicitando ayuda y a embebidos para proponer un grupo de trabajo, pero eran las 4:30 am, me dije que tenía que escarbar un poco más.

 

Me puse a hacer git greps y encontre'en icicle.sv este código:

/* LEDs */
output logic [7:0] leds,
...

casez (mem_address)
    32'b00000000_00000000_????????_????????: ram_sel = 1;
    32'b00000000_00000001_00000000_000000??: leds_sel = 1;
    32'b00000000_00000010_00000000_0000????: uart_sel = 1;
    32'b00000000_00000011_00000000_0000????: timer_sel = 1;
    32'b00000001_????????_????????_????????: flash_sel = 1;
    default:                                 mem_fault = 1;

endcase

 

Lo que hace es determinar según la dirección de memoria apuntada quien se va a encargar, ahora nos interesa leds_sel por que debe ser lo más parecido a los botones.

Luego tenemos el código de leds, si te fijás esto contrasta con el resto de los componentes, que no están tirados por ahí si no que tienen sus propios módulos.

logic [31:0] leds_read_value;
logic leds_ready;

assign leds_read_value = {24'b0, leds_sel ? leds : 8'b0};
assign leds_ready = leds_sel;

always_ff @(posedge clk) begin
    if (leds_sel && mem_write_mask[0])
        leds <= mem_write_value[7:0];
end

Tras algunas exploraciones un tanto indescriptibles, me puse a modificar.

En boards/edufpga.pcf agregué los pines, una mitad asignada a los botones, la otra a un PMOD, tal como esta con los leds.

set_io buttons[0] 31
set_io buttons[1] 32
set_io buttons[2] 33
set_io buttons[3] 34
set_io buttons[4] 11
set_io buttons[5] 12
set_io buttons[6] 15
set_io buttons[7] 16


En programs/hello/main.c asigné los botones a los leds:

#define LEDS        *((volatile uint32_t *) 0x00010000)
#define BUTTONS     *((volatile uint32_t *) 0x00010004)

while (1) {

  LEDS = ~BUTTONS;

}

En top.sv conecté los botones al exterior por el hecho de declararlos y luego los conecté a icicle:

module top(
    ...
    /* LEDs */
    output logic [7:0] leds,

    /* BUTTONS */
    input [7:0] buttons,
    ...
    );

    icicle icicle (
       .clk(pll_clk),
       ...
       /* LEDs */
       .leds(leds),

       /* BUTTONs */
       .buttons(buttons),
       ...
     );

 

En icicle.sv declaré los botones en la interfaz:


module icicle (
    input clk,
    input reset,
    ...    
    /* LEDs */
    output logic [7:0] leds,

    /* BUTTONS */
    input [7:0] buttons,
    ...
);


Conecté los botones a la salida y a la señal de que es leible:

    ...
    assign mem_read_value = ram_read_value | leds_read_value | buttons_read_value | uart_read_value | timer_read_value | flash_read_value;


    assign mem_ready = ram_ready | leds_ready | buttons_ready | uart_ready | timer_ready | flash_ready |
    ...

    
Agregué al selector de dispositivos por decirle de algún modo, la dirección de los botones:

...
logic leds_sel;
logic buttons_sel;
...
    
always_comb begin
  ...
  leds_sel = 0;
  buttons_sel = 0;    
  ...
  casez (mem_address)
      32'b00000000_00000000_????????_????????: ram_sel = 1;
      32'b00000000_00000001_00000000_000000??: leds_sel = 1;
      32'b00000000_00000001_00000000_000001??: buttons_sel = 1;
      32'b00000000_00000010_00000000_0000????: uart_sel = 1;
      32'b00000000_00000011_00000000_0000????: timer_sel = 1;
      32'b00000001_????????_????????_????????: flash_sel = 1;
      default:                                 mem_fault = 1;

  endcase


y finalmente la lógica, a continuación de la de los leds:

assign leds_read_value = {24'b0, leds_sel ? leds : 8'b0};
assign leds_ready = leds_sel;

always_ff @(posedge clk) begin
    if (leds_sel && mem_write_mask[0])
        leds <= mem_write_value[7:0];
end

logic [31:0] buttons_read_value;
logic buttons_ready;

assign buttons_read_value = {24'b0, buttons_sel ? buttons : 8'b0};
assign buttons_ready = buttons_sel;


    
Asombrosamente, si dejamos de lado algún que otro errorcito de sintaxis, me salió a la primera, nada mal considerando que nunca había visto System Verilog, igual no es muy distinto a Verilog.

Tiene un delay bastante apreciable y necesita un pulso de al menos XXX ms para que se active, no sé si soy yo o es inherente.


Ese pulso lo podría calcular de la siguiente manera, con un microcontrolador generar pulsos cuya longitud esté determinada por la lectura de un potenciómetro y que muestre en el puerto serial la longitud actual, mientras, conecto en el programa los 4 bits de los botones al los leds 5-8 que están mapeados al pmod donde miro con el osciloscopio si se activa o no y si hay ruido. A fines prácticos, estamos listos para continuar, no hace falta el experimento. 

Hay un montón de potencial, como por ejemplo implementar GPIO para poder desde el programa determinar qué es de entrada y qué de salida, tal como está ahora hay que tocar el HDL.

Cuesta verlo, pero tengo apretados los botones de los extremos, prenden los leds de los extremos.

 

Botones en acción
Botones en acción


 

De lo externo a la POC, esto era lo más difícil.

Paciencia, uno de estos días versiono y hago el pull request a ciaa/icicle.

 

No hay comentarios:

Publicar un comentario