Skip to content
Juan Gonzalez-Gomez edited this page Aug 22, 2016 · 56 revisions

ACC0: Apollo CPU Core 0

Toma de contacto. Empezamos nuestro viaje espacial

Introducción

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

Objetivos

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.

Teoría: Memoria en el AGC

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)

Descripción del ACC0

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

Diagrama de bloques

El diagrama de bloques del ACC0 se muestra en la siguiete figura:

ACC0 en Icestudio

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

ACC0 en Verilog

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

Prescaler de N bits

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

Antirrebotes (debouncer-pulse)

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 2K16

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

ACC0: Módulo principal

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

Toolchain para el ACC0

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

Programa: test.ags

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

Instrucciones de uso

Instalar la toolchain del AGS

La toolchain se puede compilar desde este repositorio

Para Ubuntu 16.04, la toolchain se ha precompilado:

$ tar vzxf toolchain-AGC-linux_x86_64-1.tar.gz 

$ cd toolchain-AGC-linux_x86_64-1/

$ sudo cp * /usr/local/bin/

(opcional) Compilando la toolchain del AGC en linux

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:

$ 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

Descargar el icestudio desde este enlace y descomprimirlo

Descargar o clonar el repo del ACC y entrar en el directorio del ACC0

$ git clone https://github.com/Obijuan/ACC.git
$ cd ACC/hw/roadmap/10-ACC0/

Ensamblar test.ags y generar la ROM

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

Sintetizar y cargar 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!

Trabajando con el ACC0 en Verilog

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!

Probando el ACC0

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

Vídeo

El funcionamiento se puede ver en este vídeo en youtube (Pulsar en la imagen para ver):

Click to see the youtube video

Ejercicios propuestos

  1. Ensamblar el programa test.ags, sintetizar el ACC0 (con apio o icestudio) y cargarlo en la FPGA. Comprobar que funciona correctamente

  2. 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

Sobre esta página

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