En esta oportunidad expuse un mezcladito de varios open:
Open HW/Core/SW: SoC ICICLE con CPU RISC-V en EDU-CIAA-FPGA
A mi propuesta original que incluía el ataque mostrado en H4CK3D 2021, el Profesor Matías me recomendó podarla para que sea menos técnica. Esa poda fué insuficiente, ya el título es bastante largo y complicado, a la mayor parte de la asistencia no era un tema que le interesara, no importa, para FLISoL 2023 tengo pensado algo más apropiado.
Mi idea fue ir evolucionando distintas implementaciones de la luz ondulante de K.I.T.T. hasta llegar a un softcore en FPGA, aprovechando el trabajo de un montón de gente:
Historia |
Lo rojito a la derecha era mi objetivo, lo expuesto en H4CK3D 2021
La primera versión es con componentes digitales, la hice hace casi 35 años, un registro de desplazamiento con unas puertas OR animadas a continuación, con un 555 para el clock, unos OR, un flip flop y algún capacitor para inyectar el primer bit.
Ese bit entra en la primera posición del registro de desplazamiento y en cada tick del clock se va moviendo, con la salida conectada a la entrada con un OR al flip flop que salvo en el arranque siempre está en cero, lo tenemos para siempre. Yendo hacia los leds, con los OR de la derecha transformamos el movimiento en un aparente ida y vuelta.
K.I.T.T. con compuertas digitales |
Comparando precios, el costo es similar a implementarlo con un microcontrolador. Ponemos un bit en uno y lo vamos desplazando para un lado hasta detectar que llegó al punto deseado, ahí invertimos el sentido del movimiento, para siempre.
K.I.T.T. con microcontrolador y lógica específica |
El programa es corto pero complicado e inadaptable a otros patrones. Una versión mejor aunque te indigne desde el punto de vista de la programación es:
while (true) {
gpio_A.out(1);
delay(DELAY);
gpio_A.out(2);
delay(DELAY);
gpio_A.out(4);
delay(DELAY);
gpio_A.out(8);
delay(DELAY);
gpio_A.out(16);
delay(DELAY);
gpio_A.out(8);
delay(DELAY);
gpio_A.out(4);
delay(DELAY);
gpio_A.out(2);
delay(DELAY);
gpio_A.out(1);
delay(DELAY);
}
¿Por qué me atrevo a incluir esta manera? Pues por que es el precursor para esta version más linda, en lugar de código hardcodeado, la información está en un array:
unsigned int out[]={1,2,4,8,16,8,4,2,1};
int pos = 0;
int limit = sizeof(out)/sizeof(out[0]);
while ( true) {
gpio_A.out(1);
delay(DELAY);
++pos;
if (pos == limit) {
pos = 0;
}
}
y esto se parece mucho a la implementación nuevamente con componentes digitales.
K.I.T.T. con contador y memoria |
Ahora, tanto en hardware como en software, podemos con gran facilidad mostrar otros patrones manipulando la memoria o el array según corresponda:
K.I.T.T. con contador y memoria y patrones arbitrarios |
En la charla acompañando a esta evolución también fui contando como funciona una computadora a un nivel más bajo, que me cuesta mucho convertir a este formato, quizás haga un video en algún momento, lamentablemente no pude grabar la sesión.
Eso llevó a la explicación de FPGA y RISC-V, desembocando en el tema que me interesaba, la implementación primero en Verilog de la versión con lógica, como dispositivo incluido en el SoC icicle:
https://github.com/cpantel/evilCodeSequence/blob/master/kitt.sv
module kitt #( parameter BASETIME) ( input clk, input reset, output [4:0]display_out, /* memory bus */ input [31:0] address_in, input sel_in, //input read_in, //output logic [31:0] read_value_out, input [3:0] write_mask_in, input [31:0] write_value_in, output logic ready_out ); logic [25:0]q; logic direction; logic [4:0]display; assign ready_out = sel_in; assign display_out = display; always_ff @(posedge clk) begin if (reset) begin display <= 1; direction <= 1; end else if ( q > ( BASETIME / 1000 * 300 ) ) begin q <= 0; if (direction ) begin if ( display[4] ) begin direction = ~ direction; display <= display >> 1; end else display <= display << 1; end else begin if ( display[0] ) begin direction = ~ direction; display <= display << 1; end else display <= display >> 1; end end else begin q <= q + 1; display <= display; end end endmodule
Y por software, de modo concurrente y absolutamente independiente, ejecutándose en la CPU:
/* This program implements a SW kitt */ #include <stdint.h> #include "../memmap.h" #include "../uart.h" #include "../delay.h" int main() { uart_init(); uart_puts("KITT starting\r\n"); for (;;) { LEDS = 1; delay(); LEDS = 2; delay(); LEDS = 4; delay(); LEDS = 8; delay(); LEDS = 4; delay(); LEDS = 2; delay(); uart_puts("KITT .\r\n"); } }
Si algo entendés de Verilog, te darás cuenta que ese dispositivo como que está en vano conectado a los buses, ya que no hay nada con lo que el programa en la CPU pueda interactuar. Es que me quedé sin tiempo, querría al menos haberle permitido cambiar la velocidad.
En una próxima entrega espero no muy lejana, mostraré la implementación de un nuevo dispositivo que he de llamar "sequencer", que consistirá en una lógica que lea un trozo de memoria y lo exponga en los leds (vía PMOD como es ahora kitt) y que desde el programa se pueda arrancar, detener, pausar, cambiar la velocidad y modificar esa memoria.