-
Notifications
You must be signed in to change notification settings - Fork 8
ACC0
Toma de contacto. Empezamos nuestro viaje espacial
Versión Hola mundo de toma de contacto. Es una memoria ROM de 2K con el registro S para su direccionamiento y el registro G (de 15 bits) para la carga de las instrucciones. Los 7 bits más significativos de cada palabra se muestran por los leds de la tarjeta Icezum. El botón SW1 se usa para incrementar la dirección y el SW2 para decrementarla
Poner a punto las herramientas software para programar el ACC. Tener un primer contacto con el mapa de memoria. Tocar el hardware real desde el primer momento sintentizándolo en FPGAs libres y jugando con él.
El bus de direcciones del AGC es de 12 bits, lo que permite direccionar hasta 4K. El mapa de memoria se divide en 4 bloques de 1K. De ellos, el bloque 0 es de memoria RAM y los 3 siguientes de memoria ROM.
El tamaño de las palabras es de 16 bits, aunque el bit más significativo contiene un bit de paridad para detectar errores, por lo que las instrucciones tienen un tamaño efectivo de 15 bits
Al arrancar, el AGC empieza a ejecutar el código que se encuentra en la dirección 0x800 (ó 4000 en octal)
Empezamos a diseñar nuestro micro con una memoria ROM de 2K y 16 bits de datos, mapeada a partir de la dirección 0x800 (dirección de arranque). Esta memoria se direcciona mediante el registro S, de 12 bits, que tiene dos entradas de control: inc y dec, que permiten incrementar la dirección o decrementarla en 1 unidad, según la señal que está activada:
inc | dec | Acción en registro S |
---|---|---|
0 | 0 | Sin modificación |
1 | 0 | Incremento en una unidad |
0 | 1 | Decremento en una unidad |
1 | 1 | Incrento en una unidad (inc tiene prioridad sobre dec) |
Las señales inc y dec están conectadas a los botones SW1 y SW2 de la tarjeta Icezum, a través de un circuito antirrebotes. Este circuito genera un pulso de 1 ciclo de reloj de anchura cada vez que se aprieta el botón.
La palabra leída de memoria se almacena en el registro G, de 15 bits (el bit más significativo es el de paridad, que se descarta). Los 7 bits más significativos del registro G se envían a los leds para su visualización
El diagrama de bloques del ACC0 se muestra en la siguiete figura:
El ACC0 está disponible para Icestudio 0.2, lo que permite ver todos sus componentes fácilmente y también sintetizarlo para las FPGAs libres. Se encuentra en el directorio ACC0/hw/roadmap/10-ACC0
El módulo printicpal (top entity) es el ACC0. Además se han creado módulos con prescaler, antirrebotes y memoria ROM. El código completo se encuentra en el fichero ACC0.v en github
Este módulo se usa dentro del antirrebotes (debouncer-pulse) para generar una pausa de 5.5ms (N=16)
//-- Prescaler N bits
module prescaler(input wire clk_in,
input wire ena,
output wire clk_out);
//-- Bits of the prescaler
parameter N = 22;
//-- N bits counter
reg [N-1:0] count = 0;
//-- The most significant bit is used as output
assign clk_out = count[N-1];
always @(posedge(clk_in)) begin
if (!ena)
count <= 0;
else
count <= count + 1;
end
endmodule /// prescaler
El antirrebotes se usa para las dos entradas del los pulsadores (next, prev) que permiten incrementar o decrementar la dirección de la memoria a visualizar. Este antirrebotes genera un pulso de 1 ciclo de reloj del sistema (12MHZ) cada vez que se suelta el bóton
module debounce_pulse(input wire clk,
input wire sw_in,
output wire sw_out);
//------------------------------
//-- CONTROLLER
//------------------------------
//-- fsm states
localparam IDLE = 0; //-- Idle state. Button not pressed
localparam WAIT_1 = 1; //-- Waiting for the stabilization of 1. Butt pressed
localparam PULSE = 2; //-- 1-clk pulse is generated
localparam WAIT_0 = 3; //-- Button released. Waiting for stabilization of 0
//-- Registers for storing the states
reg [1:0] state = IDLE;
reg [1:0] next_state;
//-- Control signals
reg out = 0;
reg timer_ena = 0;
assign sw_out = out;
//-- Transition between states
always @(posedge clk)
state <= next_state;
//-- Control signal generation and next states
always @(*) begin
//-- Default values
next_state = state; //-- Stay in the same state by default
timer_ena = 0;
out = 0;
case (state)
//-- Button not pressed
//-- Remain in this state until the botton is pressed
IDLE: begin
timer_ena = 0;
out = 0;
if (sw_in)
next_state = WAIT_1;
end
//-- Wait until x ms has elapsed
WAIT_1: begin
timer_ena = 1;
out = 0;
if (timer_trig)
next_state = PULSE;
end
PULSE: begin
timer_ena = 0;
out = 1;
next_state = WAIT_0;
end
WAIT_0: begin
timer_ena = 1;
out = 0;
if (timer_trig && sw_in==0)
next_state = IDLE;
end
default: begin
end
endcase
end
assign sw_out = out;
//-- Timer
wire timer_trig;
prescaler #(
.N(16)
) pres0 (
.clk_in(clk),
.ena(timer_ena),
.clk_out(timer_trig)
);
endmodule // debouncer_pulse
Memoria rom genérica, síncrona. Se usa para implementar la memoria ROM de 2K y palabras de 16 bits del ACC0. El contenido de la rom se encuentra en el fichero rom.list
// -- Generic ROM
module genrom #(
parameter AW = 11, //-- Adress width
parameter DW = 16, //-- Data witdh
parameter ROMFILE = "rom.list") //-- Romfile
(
input wire clk, //-- Clock
input cs, //-- Chip select
input wire [AW-1: 0] addr, //-- Address bus
output reg [DW-1: 0] data_out); //-- Data bus
//-- Total position of the address
localparam NPOS = 2 ** AW;
//-- Memory
reg [DW-1: 0] rom [0: NPOS-1];
always @(negedge clk) begin
if (cs)
data_out <= rom[addr];
end
//-- ROM2: Secuencia
initial begin
$readmemh(ROMFILE, rom);
end
endmodule
Bloque principal del ACC0, que instancia los antirrebotes, la memoria Rom. Se implementan los registros S y G, y se hacen todas las conexiones
module ACC0 (
input wire clk,
input wire next,
input wire prev,
output wire d0,
output wire d1,
output wire d2,
output wire d3,
output wire d4,
output wire d5,
output wire d6,
output wire d7
);
//-- Rom file
parameter ROMFILE = "rom.list";
//-- Parameters for the memory
localparam AW = 12; //-- Address bus
localparam DW = 16; //-- Data bus
//-- Initial address
localparam BOOT_ADDR = 12'h800;
wire [DW-1: 0] rom_dout;
//-- Instantiate the ROM memory (2K)
genrom #(
.ROMFILE(ROMFILE),
.AW(AW-1),
.DW(DW))
ROM (
.clk(clk),
.cs(S[AW-1]), //-- Bit A11 for the chip select
.addr(S[AW-2:0]), //-- Bits A10 - A0 for addressing the Rom (2K)
.data_out(rom_dout)
);
//-- Configure the pull-up resistors for clk and rst inputs
wire next_p; //-- Next input with pull-up activated
wire clk_in;
wire prev_p; //-- Prev input with pull-up activated
wire sw2;
wire sw1;
SB_IO #(
.PIN_TYPE(6'b 1010_01),
.PULLUP(1'b 1)
) io_pin (
.PACKAGE_PIN(next),
.D_IN_0(next_p)
);
SB_IO #(
.PIN_TYPE(6'b 1010_01),
.PULLUP(1'b 1)
) io_pin2 (
.PACKAGE_PIN(prev),
.D_IN_0(prev_p)
);
//-- rst_in and clk_in are the signals from the switches, with
//-- standar logic (1 pressed, 0 not presssed)
assign sw2 = ~prev_p;
assign sw1 = ~next_p;
//-- switch button debounced
wire sw1_deb;
wire sw2_deb;
debounce_pulse deb1 (
.clk(clk),
.sw_in(sw1),
.sw_out(sw1_deb)
);
debounce_pulse deb2 (
.clk(clk),
.sw_in(sw2),
.sw_out(sw2_deb)
);
assign clk_in = sw1_deb;
//-- Register S: Accessing memory
reg [AW-1: 0] S = BOOT_ADDR;
always @(posedge clk) begin
if (sw1_deb)
S <= S + 1;
else
if (sw2_deb)
S <= S - 1;
end
//-- Instruction register
reg [14:0] G = 15'b0;
//-- Control signal for the RI register
//-- Every 15-bit instruction is sotred here before executing
reg WG = 1;
always @(posedge clk)
if (WG)
G <= rom_dout[14:0];
//-- In ACC0, the 7 more significant bits are shown in leds
//assign {d6,d5,d4,d3,d2,d1,d0} = G[14:8];
assign {d6,d5,d4,d3,d2,d1,d0} = rom_dout[14:8];
//-- The LED7 is always set to 0
assign d7 = 1'b0;
endmodule
El ACC0 usa la misma toolchain que la del AGC. Todas las instrucciones del ensamblador del AGC se pueden consultar en el manual del ensamblador del AGC (inglés)
El ensamblador del AGC es el YaYUL. Con esta herramienta se genera el código máquina de los programas Colossus, que controla el módulo de Mando y Luminary, para el módulo lunar. Ambos programas se encuentran disponibles en el repositorio del AGC en github
En este repositorio VirtualAGC, además del código, se encuentran las fuentes de todas las herramientas, así como un emulador del AGC
Las herramientas que usaremos para el ACC0 son:
Herramienta | Nombre | Descripción |
---|---|---|
Ensamblador | yaYUL | Toma los archivos fuentes, en ensamblador del AGC, con extensión .ags y genera el código máquina en el archivo binario .bin |
Simulador | yaAGC | Carga el código máquina del archivo .bin y lo simula (Esta herramienta es opcional. Se usa para aprender a programar en ensamblador del AGC) |
Generador de ROM | ACC-rom | Script en python desarrollado como parte del proyecto ACC, para leer el archivo .bin y generar el fichero rom.list en formato Verilog, para usarse en la síntesis del ACC0 |
El primer programa que cargaremos en la ROM será el test.ags. Este programa en realidad no hace nada, salvo almacenar datos de 16bits en la memoria ROM, a partir de la dirección 0x800 (4000 en octal)
El ACC0 todavía no ejecuta instrucciones. Lo que hará será mostrar los 7 bits mas significativos de estos datos en los leds. Y mediante los pulsadores SW1 y SW2 de la tarjeta Iezum, incrementaremos o decrementaremos la dirección del dato a visualizar
#-- Direccion de arranque
SETLOC 4000
#-- Datos almacenados (en octal)
OCT 40000 #-- 1000000 Shown in leds
OCT 20000 #-- 0100000
OCT 10000 #-- 0010000
OCT 04000 #-- 0001000
OCT 02000 #-- 0000100
OCT 01000 #-- 0000010
OCT 00400 #-- 0000001
OCT 77777 #-- 1111111
La primera instrucción, SETLOC 4000, es una directiva del ensamblador para indicar que lo que viene a continuación se debe situar a partir de la dirección 4000 en octal. SETLOC sólo admite como argumento direcciones en octal
Los comentarios se ponen colocando el carácter # delante del texto
Para definir datos se usan las directivas **DEC y OCT. La primera es para almacenar un número en decimal y la segunda en octal. En el ejemplo test.ags se definen todos los datos en octal. Y en los comentarios está puesto en binario lo que se vería por los leds
La toolchain se puede compilar desde este repositorio
Para Ubuntu 16.04, la toolchain se ha precompilado:
-
Descargar el paquete toolchain-AGC-linux_x86_64-1.tar.gz
-
Descomprimirlo, entrar en el directorio toolchain-AGC-linux_x86_64-1 y copiar los ficheros a /usr/local/bin
$ tar vzxf toolchain-AGC-linux_x86_64-1.tar.gz
$ cd toolchain-AGC-linux_x86_64-1/
$ sudo cp * /usr/local/bin/
El ensamblador yaYUL y el simulador yaAGC están ya compilados para ubuntu 16.04. Para usarlo en otros sistemas, puede ser necesario compilarlo. Las instrucciones son:
- Bajar o clonar el proyecto virtualagc de github
$ git clone https://github.com/avtobiff/virtualagc.git
- Compilar yaYUL:
$ cd virtualagc/yaYUL/
$ make
Esto nos generará el ejecutable yaYUL
- Compilar yaAGC:
$ cd ../yaAGC/
$ sudo apt install libreadline6-dev
$ make
Se generará en ese mismo directorio el ejecutable yaAGC
Descargar el icestudio desde este enlace y descomprimirlo
$ git clone https://github.com/Obijuan/ACC.git
$ cd ACC/hw/roadmap/10-ACC0/
Editar el fichero test.ags y poner los datos que se quieran (o dejar los que vienen por defecto). Una vez grabado el fichero, lo ensamblamos así:
$ yaYUL test.ags
Se crea el fichero test.ags.bin con el código máquina. Ahora generamos el fichero rom.list en formato verilog:
$ acc-rom.py test.ags.bin
Ya lo tenemos listo para sintetizar el ACC0 y cargarlo en la FPGA
Arrancar el icestudio y cargar el proyecto ACC0.ice, pinchando en File/Open Project. Conectar la tarjeta Icezum al USB y dar a la opción Tools/upload. Se realizará la síntesis del ACC0 y se cargará en la FPGA. ¡Listo!
El ACC0 también está disponible en Verilog, además del proyecto en icestudio. La síntesis y descarga la realizaremos utilizando Apio. Las fuentes se encuentran en el archivo ACC0.v
- Instalar apio
- Para sintetizar y descargar ejecutar los siguientes comandos desde el directorio del ACC0:
$ apio clean
$ apio upload
¡Listo!
Una vez configurada la FPGA de la Icezum, observaremos que en los leds de la icezum aparecerán los 7 bits más significativos del dato que se encuentra en la dirección 0x800 (4000 oct) de memoria. Si hemos usado el fichero test.ags original sin ninguna modificación, estará encendido sólo el led 6
Si apretamos el pulsador SW1, accedemos al contenido de la siguiente posición de memoria, por lo que sólo se encenderá el led 5:
Si apretamos sucesivamente SW1, avanzaremos por la memoria. Si apretamos SW2, volveremos a la dirección anterior. De esta forma, con los botones SW1 y SW2 podremos "navegar" por la memoria ROM, comprobando los bits más significativos de los datos cargados
El funcionamiento se puede ver en este vídeo en youtube (Pulsar en la imagen para ver):
-
Ensamblar el programa test.ags, sintetizar el ACC0 (con apio o icestudio) y cargarlo en la FPGA. Comprobar que funciona correctamente
-
Crear un programa en ensamblador que tenga almacenados los datos necesarios para que por los leds aparezca una cuenta binaria desde 0 a 15. Sintetizar el ACC0, cargarlo en la FPGA y comprobar que funciona correctamente
Todas las figuras están hechas con la herramienta libre Inkscape, en formato SVG
Las figuras 3D están hechas con la herramienta libre FreeCAD
Las fuentes de todas las figuras están disponibles en este repositorio