Repository navigation
ACC2

Almacenando datos en el registro A
Partimos del ACC1 al que le añadimos el registro A de 15 bits y la instrucción CA para cargar datos en él. Los 7 bits menos significativos del registro A se sacan por los leds (en vez de los bits del registro G)
- Aprender el funcionamiento de las instrucciones que acceden a memoria para leer datos
En las siguientes tablas se resumen las instrucciones y directivas del ensamblador que podemos usar con el ACC2
| Nombre | Descripción | Ejemplo |
|---|---|---|
| SETLOC k12 | Establecer la dirección de la siguiente instrucción. k12 es una dirección de 12 bits en octal | SETLOC 4000 |
| OCT k15 | Definir una constante de 15 bits, en octal,para que se almacene en memoria ROM | OCT 67705 |
| DEC k15 | Definir una constante de 15 bits, en decimal, para que se almacene en memoria ROM | DEC 1023 |
| label1 EQUALS label2 | Definir la etiqueta label1 con valor igual a label2 | DATA1 EQUALS DATA2 |
| label1 = label2 | Definir la etiqueta label1 con valor igual a label2 | DATA1 = DATA2 |
Se han añadido 3 directivas nuevas. La directiva DEC que nos permite crear una constante en decimal, en la memoria ROM. Es igual que OCT, pero especificando el dato en decimal
La directiva EQUALS es equivalente al símbolo =. Sirve para crear una etiqueta igual a otra previamente definida
| Nombre | Opcode | Descripción | Ejemplo | Código máquina ej. (octal) |
|---|---|---|---|---|
| TCF k12 | 1 | Salto incondicional a la dirección k12 | TCF loop | 10000 + loop |
| NOOP | 1 | No hacer nada. Sólo se consumen ciclos de reloj | NOOP | 10000 + dir_act + 1 |
| CA k12 | 3 | Cargar el dato de la dirección k12 en el registro A | CA dato | 30000 + dato |
La instrucción CA (Clear and Add) lee un dato almacenado en la dirección indicada y lo carga en el registro A, dejándolo accesible para trabajar con él.
ACC2 = ACC1 + Registro A + Instrucción CA
El diagrama de bloques del ACC2 se muestra en la siguiente figura:

Partimos de la ruta de datos del ACC1 y añadimos los nuevos elementos.
La salida de dato de la ROM se conecta al registro G (como antes) y al nuevo registro A (de 15 bits). Cuando la señal de control WA se activa, el regitro A captura los datos (Necesario para la ejecución de la instrucción CA)
El direccionamiento de la memoria ROM (12 bits) ahora se puede hacer desde dos registros diferentes: S ó G, dependiendo de la configuración de la señal de control SelSG del multiplexor. Cuando SelSG=0, se lee la dirección del registro S. Así es como se hacía siempre en el ACC1. Esto permite la lectura de instrucciones. Cuando SelSG=1, se usa el registro G, lo que permite acceder a la ROM para leer datos
Los 7 bits menos significativos del registro A se sacan por los leds, para poder visualizar los valores cargados en el registro A (y comprobar que la instrucción CA está funcionando correctamente)
El resto de la ruta de datos es exactamente igual que la del ACC1

La unidad de control es la misma que la del ACC0, pero incluyen dos señales nuevas WA y SelGS
- WA: Esta señal se activa para cargar datos en el registro A, y poder implementar con ella la instrucción CA
- SelSG: Permite seleccionar qué registro se usará para direccionar la memoria. Cuando se están leyendo instrucciones, se usa la dirección proveniente de S. Cuando se leen datos para cargarlos en el registro A (instrucción CA) se debe usar la dirección del registro G
| Nombre | Descripción |
|---|---|
| WA | (Write A) Cargar el registro A |
| WS | (Write S) Cargar el registro S |
| WG | (Write G) Cargar el registro G |
| INCS | (Increment S) Incrementar el registro S en una unidad |
| SelSG | (Select S or G) Seleccionar qué registro (S ó G) se usará para direccionar la memoria |

La unidad de control del ACC2 se implementa con un autómata finito de 5 estados. En el estado FETCH, todas las señales de control están a 0, por lo que se usa el registro S para direccionar la memoria ROM. En el siguiente ciclo se pasa al estado READ_INST para leer la instrucción, que se almacena en el registro G, y se incrementa el registro S para apuntar a la dirección de la siguiente instrucción. En el siguiente ciclo se entra en el estado EXEC de ejecución. Si el código de operación, almacenado en G, es igual al de la instrucción TCF, se carga en el **registro S **la nueva dirección y se pasa al estado de WAIT. Si el código de oepración se corresponde con la instrucción CA, se selecciona el registro G para direccionar la memoria ROM (en esa dirección es donde se encuentra el dato a cargar en A). En el siguiente ciclo el dato ya está disponible y se pasa al estado EXEC_CA, donde se guarda en el registro A. En el siguiente ciclo se pasa a WAIT, concluyendo la ejecución de la instrucción CA
El proyecto se encuentra en el fichero ACC2.ice. Se ha utilizado el icestudio 0.2.1-dev. Se ha dividido en sus tres componentes básicos: la ruta de datos, unidad de control y la consola de entrada
- Diagrama principal:

- Ruta de datos:
Como icestudio 0.2.1-dev todavía no soporta buses, el cableado es un poco complicado. Y sobre todo hacer modificaciones:

En los próximos ACCs la ruta de datos se implementará en icestudio con un bloque de código, que incluya todo el código verilog, en vez de dividirlo en subpartes que compliquen el cableado
- Consola de entrada:
Es la misma que en el ACC1, pero se ha metido en un bloque separado para que el proyecto principal quede más limpio:

El módulo principal es el ACC2. El resto son los mismos que los usados en el ACC1
module ACC2 (
input wire clk, //-- System clock
input wire next, //-- Process next instruction/data
input wire selmode, //-- Toggle Mode: Manual / automatic
output wire d0, //-- Output leds (register A)
output wire d1,
output wire d2,
output wire d3,
output wire d4,
output wire d5,
output wire d6,
output wire d7 //-- Model leds
);
//--------------------------------------------
//-- CONSTANTS
//--------------------------------------------
//-- Constants for the modes: automatic/manual
localparam MANUAL_MODE = 1'b1;
localparam AUTOMATIC_MODE = 1'b0;
//-- Constant for the speed of automatic mode (Number of bits for the timmer)
localparam SLOW = 24; //-- 1.4 secs
localparam MEDIUM = 22; //-- 350 ms
localparam FAST = 20; //-- 90 ms
//-- AGC Opcodes
localparam TCF = 3'b001; //-- Transfer Control Fixed. Unconditional jump
localparam CA = 3'b011; //-- Clear and Add. Load the A register from memory
//-------------------------------------------------
//-- PARAMETERS & CONFIGURATION
//-------------------------------------------------
//-- 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;
//-- Initial G-reg value
localparam G_INIT = 15'b0;
//-- Initial A-reg value (shown in leds initially)
localparam A_INIT = 15'h0055;
//-- Default mode configuration (Uncomment one of the options)
//localparam DEFAULT_MODE = AUTOMATIC_MODE;
localparam DEFAULT_MODE = MANUAL_MODE;
//-- Configuration for the timer of the automatic mode
//-- Uncomment one of the options
//localparam AUTOMATIC_MODE_SPEED = SLOW;
localparam AUTOMATIC_MODE_SPEED = MEDIUM;
//localparam AUTOMATIC_MODE_SPEED = FAST;
//----------------------------------------------------------
//-- ROM MEMORY
//----------------------------------------------------------
//-- ROM output data
wire [DW-1: 0] rom_dout;
//-- ROM address wire
wire [AW-1:0] addr;
//-- Instantiate the ROM memory (2K)
genrom #(
.ROMFILE(ROMFILE),
.AW(AW-1),
.DW(DW))
ROM (
.clk(clk),
.cs(addr[AW-1]), //-- Bit A11 for the chip select
.addr(addr[AW-2:0]), //-- Bits A10 - A0 for addressing the Rom (2K)
.data_out(rom_dout)
);
//-- Addr multiplexer. Select where the addres come from
//-- SelSG == 0 --> From the S register
//-- SelSG == 1 --> From the G register
assign addr = (SelSG == 1) ? G[11:0] : S;
//-----------------------------------------------------------
//-- INPUT BUTTONS CONFIGURATION
//-----------------------------------------------------------
//-- Configure the pull-up resistors for clk and rst inputs
wire next_p; //-- Next input with pull-up activated
wire selmode_p; //-- Selmode button with pull-up activated
wire sw1;
wire sw2;
//-- Button 1 pull-up
SB_IO #(
.PIN_TYPE(6'b 1010_01),
.PULLUP(1'b 1)
) io_pin (
.PACKAGE_PIN(next),
.D_IN_0(next_p)
);
//-- Button 2 pull-up
SB_IO #(
.PIN_TYPE(6'b 1010_01),
.PULLUP(1'b 1)
) io_pin2 (
.PACKAGE_PIN(selmode),
.D_IN_0(selmode_p)
);
//-- Buttons with positive logic: (1 pressed, 0 not presssed)
assign sw1 = ~next_p;
assign sw2 = ~selmode_p;
//-- switch button debounced
wire sw1_deb;
wire sw2_deb;
//-- Debouncer for button 1
debounce_pulse deb1 (
.clk(clk),
.sw_in(sw1),
.sw_out(sw1_deb)
);
//-- Debouncer for button 2
debounce_pulse deb2 (
.clk(clk),
.sw_in(sw2),
.sw_out(sw2_deb)
);
//-- This debouncer is used with the timer for generating a 1-cycle width pulse
//-- Not for debouncing
debounce_pulse deb3 (
.clk(clk),
.sw_in(timer_trig),
.sw_out(timer_trig_pulse)
);
//------------------------------------
//-- S REGISTER: Addressing memory
//------------------------------------
//-- Define de S-reg
reg [AW-1: 0] S = BOOT_ADDR;
//-- Register with paralell load (WS) and increment (INCS)
always @(posedge clk) begin
if (WS)
S <= dir12;
else
if (INCS)
S <= S + 1;
end
//---------------------------------------------------------
//-- G REGISTER: Store Instruction/data read from memory
//---------------------------------------------------------
//-- Define de G-reg
reg [14:0] G = G_INIT;
//-- G has different fields
wire [2:0] opcode = G[14:12]; //-- Opcode: 3 bits
wire [11:0] dir12 = G[11:0]; //-- Dir12: 12 bits
//-- Register with parallel load (WG)
always @(posedge clk)
if (WG)
G <= rom_dout[14:0];
//------------------------------------------
//-- A REGISTER
//------------------------------------------
reg [14:0] A = A_INIT;
always @(posedge clk) begin
if (WA)
A <= rom_dout[14:0];
end
//-----------------------------------------
//-- LEDS
//-----------------------------------------
//-- The 7 more significant bits of G regs are shown in leds (DEBUG)
//assign {d6,d5,d4,d3,d2,d1,d0} = G[14:8];
//-- Show the 7 less significant bits of the A reg in the leds
assign {d6, d5, d4, d3, d2, d1, d0} = A[6:0];
//-- The LED7 is for displaying the mode (automatic/manual)
assign d7 = mode;
//assign d7 = A[7]; //-- Debug
//------------------------------------------
//-- Timer for the automatic mode
//------------------------------------------
wire timer_trig;
wire timer_trig_pulse;
prescaler #(
.N(AUTOMATIC_MODE_SPEED)
) timer_automatic (
.clk_in(clk),
.ena(1'b1),
.clk_out(timer_trig)
);
//-------------------------------------------------
//-- Flip-flip T for toggling the mode
//-------------------------------------------------
reg mode = DEFAULT_MODE;
//-- Flip-flip T. The input is the button 2
always @(posedge clk) begin
//-- Change the mode when the SW2 is pressed
if (sw2_deb)
mode = ~mode;
end
//-- Mux for choosing manual/automatic event signal
wire event_trig = (mode == MANUAL_MODE) ? sw1_deb : timer_trig_pulse;
//---------------------------------------------------
//-- CONTROL UNIT
//---------------------------------------------------
//-- fsm states
localparam FETCH = 0;
localparam READ_INST = 1;
localparam EXEC0 = 2;
localparam EXEC1 = 3;
localparam WAIT = 4;
//-- Registers for storing the states
reg [2:0] state = WAIT;
reg [2:0] next_state;
//---------------- Control signals
reg WG = 0; //-- Load the G register
reg INCS = 0; //-- Increment the S register
reg WS = 0; //-- Load the S register
reg WA = 0; //-- Load the A register
reg SelSG = 0; //-- Address multiplexer selection
//-- 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
WG = 0;
INCS = 0;
WS = 0;
WA = 0;
SelSG = 0;
case (state)
FETCH: begin
next_state = READ_INST;
end
READ_INST: begin
WG = 1; //-- Read the opcode into the G register
INCS = 1; //-- Increment the S reg
next_state = EXEC0;
end
EXEC0: begin
//-- If opcode is TCF, load the register S
case (opcode)
TCF: begin
WS = 1;
next_state = WAIT;
end
CA: begin
SelSG = 1;
next_state = EXEC1;
end
default: begin
next_state = WAIT;
end
endcase
end //-- EXEC0
EXEC1: begin
if (opcode == CA) begin
WA = 1;
SelSG = 1;
next_state = WAIT;
end
end
WAIT: begin
//-- Wait until an event is trigger (timer or sw1 pressed)
if (event_trig)
next_state = FETCH;
end
default: begin
end
endcase
end
endmoduleAdemás de las mismas configuraciones que el ACC1, se puede cambiar el valor inicial del registro A:
//-- Initial A-reg value (shown in leds initially)
localparam A_INIT = 15'h0055;Este programa almacena consecutivamente en el registro A diferentes valores cargados de memoria.
En el ACC2, los 7 bits menos significativos del registro A están conectados a los leds, por lo que este programa muestra una secuencia de valores por los leds
SETLOC 4000
loop1 CA val1 #-- Load value store in valx address
CA val2
CA val3
CA val4
CA val5
CA val6
CA val7
TCF loop1
#-- Mostrado por los leds
val1 OCT 00100 #-- 1 000 000
val2 OCT 00040 #-- 0 100 000
val3 OCT 00020 #-- 0 010 000
val4 OCT 00010 #-- 0 001 000
val5 OCT 00004 #-- 0 000 100
val6 OCT 00002 #-- 0 000 010
val7 OCT 00001 #-- 0 000 001
Una vez cargado el val7, el programa salta al comienzo, repitiendo la secuencia en un bucle infinito
Ejemplo del uso de la directiva DEC para almacenar un dato definido en decimal. Se cargan 2 valores decimales en el registro A, consecutivamente. Se repite en un bucle infinito. En los leds se muestra una secuencia de 2 estados
SETLOC 4000
loop CA val1
CA val2
TCF loop
#-- Datos definidos en decimal
val1 DEC 15 # -- 0x000F
val2 DEC 255 # -- 0x00FFEjemplo de uso de las directivas EQUALs y =
Es el mismo ejemplo que test5.ags, pero se definen las direcciones DATA1 y DATA2 que son iguales a val1 y val2 respectivamente. Las dos directivas son equivalentes
#-- Declarar DATA1 y DATA2 como sinónimos de las direcciones de val1 y val2
#-- respectivamente
DATA1 EQUALS val1
DATA2 = val2
SETLOC 4000
loop CA DATA1 #-- Cargar val1 en A
CA DATA2 #-- Cargar val2 en A
TCF loop
#-- Data define in decimal
val1 DEC 255
val2 DEC 63
Los programas se ensamblan igual que hicimos con el ACC1. Por ejemplo, para ensamblar test4.ags y generar el fichero rom.list con el código máquina ejecutamos:
$ yaYUL test4.ags
[...]
$ acc-rom.py test4.ags.bin
[...]Ahora ya podemos sintetizar y cargar el ACC2 (bien usando apio en la línea de comandos o bien icestudio gráficamente)
Los tres ejemplos se pueden simular con yaAGS
Para simular test4.ags seguimos los siguentes pasos:
$ yaYUL test4.ags
[...]
$ yaAGC --exec=test4.ags.bin
[...]
(agc)
Mostramos las primeras líneas del programa, para comprobar que es el que queremos simular, usando el comando list 1:
(agc) list 1
1 #-- ACC2 test
2 #-- Testing the CA insruction
3 #-- ACC2 will load the A register with different values
4 #-- in ACC2 The 7 less significant bytes of the A reg
5 #-- are always shown in the leds
6
7 SETLOC 4000
8
9 loop1 CA val1 #-- Load value store in valx address
10 CA val2
(agc)
Ahora mostrarmos el contenido de todos los registros, con info registers:
(agc) info registers
A 0x0000 0
L 0x0000 0
Q 0x0000 0
EB 0x0000 0
FB 0x0000 0
Z 0x0800 2048
BB 0x0000 0
ARUPT 0x0000 0
LRUPT 0x0000 0
QRUPT 0x0000 0
ZRUPT 0x0000 0
BBRUPT 0x0000 0
BRUPT 0x0000 0
CHAN07 0x0000 0
CYR 0x0000 0
SR 0x0000 0
CYL 0x0000 0
EDOP 0x0000 0
INDEX 0x0000 0
EXTEND 0x0000 0
IRQMSK 0x0000 0
ISR 0x0000 0
(agc)
El primero de todos es el registro A, que tiene inicialmente el valor 0
Simulamos la primera instrucción con step y vemos los registros:
(agc) step
loop1 () at test4.ags:10
10 CA val2
(agc) info registers
A 0x0040 64
Ahora A contiene el valor 0x40 hexadecimal (que se corresponde con el 100 en octal o 64 en decimal)
Damos un paso más de simulación y visualizamos otra vez:
(agc) step
11 CA val3
(agc) info registers
A 0x0020 32
Ahora el registro A ha vuelto a cambiar, y contiene el nuevo valor cargado (40 en octal)
Podemos seguir ejecutando la simulación paso a paso, podemos ejecutar por ejemplo 5 pasos de golpe, con step 5:
(agc) step 5
16 TCF loop1
(agc) info registers
A 0x0001 1
Vemos que ha ejecutado hasta la ultima instrucción, de salto, y que el valor del registro A es 1. Si hacemos un paso más de simulación se ejecuta el salto y volvemos al comienzo:
(agc) step
9 loop1 CA val1
(agc) info registers
A 0x0001 1
El registro A no ha cambiado, porque al ejecutarse la instrucción TCF no se modifica el registro A
Nada más configurar la FPGA (con el programa test4.agc cargado), aparecerá el valor inicial del registro A en los leds:

Igual que en el ACC1, el led7 está activado indicando que estamos en el modo manual. Al apretar una vez el pulsador SW1 se ejecuta la primera instrucción, que carga el valor 00100 (octal) en el registro A y los muestra por los leds:

Si apretamos nuevamente el pulsador SW1, se ejecuta la siguiente instrucción, que carga el valor 00040 (octal) en el registro A (y lo muestra por los leds)

Cada vez que apretamos el pulsador SW ejecutamos la siguiente instrucción. Al llegar a la séptima pulsación, se carga el valor 00001 en el registro A

En la siguiente pulsación, se ejecuta la instrucción TCF, que no modifica el registro A y por tanto los leds no cambian. En la siguiente pulsación se carga en A el valor inicial, volviendo a comenzar la secuencia.
Igual que en el ACC1, si apretamos el SW2 pasamos al modo automático, donde se van ejecutando las instrucciones una detrás de otra
La demostración del funcionamiento del ACC2 en la Icezum Alhambra se puede ver en este vídeo de youtube:
Primero, en el modo manual, se aprieta sucesivamente el pulsador SW1 para ejecutar instrucciones y ver los valores del registro A. Cuando se ejecuta la instrucción TCF vuelve al comienzo. Luego se aprieta SW2 para pasar a modo automico y ver la secuencia sin tener que apretar ningún botón.
-
Modificar el programa test4.ags para que se muestre la misma secuencia la primera vez, pero que a partir de las siguientes no lo haga desde el principio, sino a partir de que el led4 está encendido
-
Hacer un programa nuevo que genere saque los numeros 5, 17, 63 y 120 (definidos en decimal) por los leds, repitiéndose la secuencia
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
