7.5. Trabajo de prácticas: Microprocesador simple

7.5.1. Objetivo

En este trabajo se desarrollará procesador sencillo. Un microprocesador ejecuta operaciones especificadas en forma de instrucciones que busca (fetch) y lee desde el registro de instrucciones (IR). El microprocesador tendrá 4 registros (R0, R1, R2 y R3) y realizará operaciones básicas de carga (load), copiar (copy), suamr (add) y restar (sub).

El trabajo se realizará en un grupo formado por dos estudiantes.

7.5.2. Introducción

La Figura 7.25 representa el diagrama de bloques del microprocesador. Está compuesto por un registro de instrucciones (IR), encargado de almacenar la instrucción que se está realizando en este momento; una unidad de control que lee, decodifica las instrucciones y activa todas las señales de control; una unidad de datapath que realiza las operaciones y unos registros (R0 … R3) que almacenan información intermedia.

../../_images/diagrama_bloques.png

Figura 7.25 Diagrama de bloques del microprocesador simple

7.5.3. Juego de instrucciones

La Tabla 7.1 enumera las instrucciones que admite este procesador. Cada operación estará definida por un código de operación (OPCODE). La columna izquierda muestra el nombre de una instrucción y sus operandos. La columna central define el código de operación y la columna de la derecha el funcionamiento de cada instrucción.

Tabla 7.1 Instrucciones del microprocesador.

Instrucciones

OPCODE

Funcionamiento

nop

000

No hace nada (no operación)

nop

001

No hace nada (no operación)

load Rn, #dato

010

Carga el #dato en el registro número n

copy Rn, Rm

011

Copia el registro m en el registro n

add Rn, Rm, Rd

100

Suma el registro n al registro m y lo guarda en el registro destino Rd

addi Rn, #dato

101

Suma el #dato al registro n y lo guarda en R3

sub Rn, Rm, Rd

110

Resta el registro m al registro n y lo guarda en el registro destino Rd

subi Rn, #dato

111

Resta el #dato al registro n y lo guarda en R3

Las instrucciones se cargan desde la entrada externa Din y se almacenan en el registro IR (Insruction Register), utilizando la conexión indicada en la Figura 7.25. Cada instrucción se codifica utilizando un formato de 10 bits. Existen dos posibilidades para la instrucción dependiendo si la operación solo se realiza entre registros o si se requieren datos que vienen codificados en el registro de instrucción. Este tipo de datos se denomina dato inmediato y se señala incluyendo el símbolo “#” delante del dato. Los formatos de la instrucción serían los siguientes:

  1. Operación que involucra solo a registros:

OPCODE2

OPCODE1

OPCODE0

Rn1

Rn0

Rm1

Rm0

0

Rd1

Rd0

  1. Operación que requiere un dato inmediato de 5 bits (#dato):

OPCODE2

OPCODE1

OPCODE0

Rn1

Rn0

#dato4

#dato3

#dato2

#dato1

#dato0

Los OPCODE son los que aparecen en la Tabla 7.1 y los registros se seleccionaran utilizando dos bits cuya codificación binaria sea el número de registro. Por ejemplo, 10 será el registro R2.

Si quisiéramos sumar (add) el contenido del registro 2 al registro 0 y almacenar el resultado en el registro 1, la instrucción correspondiente sería así:

OPCODE2

OPCODE1

OPCODE0

Rn1

Rn0

Rm1

Rm0

0

Rd1

Rd0

1

0

0

0

0

1

0

0

0

1

Por el contrario, si quisiéramos sumar 5 al registro 0 y almacenarlo en el registro 3 (addi, suma con un dato inmediato), la instrucción sería así:

OPCODE2

OPCODE1

OPCODE0

Rn1

Rn0

#dato4

#dato3

#dato2

#dato1

#dato0

1

0

1

0

0

0

0

1

0

1

La instrucción copy (copia) copia el contenido de un registro en otro, usando la sintaxis copy Rn, Rm. También se puede utilizar para inicializar un registro con datos inmediatos (load), como en load Rn, #dato.

Todas estas operaciones se pueden definir usando transferencia de registros de la siguiente manera:

  • copy Rn,Rm: Rn Rm.

  • load Rn,#dato: Rn #dato.

  • add Rn,Rm,Rd: Rd Rn + Rm.

  • addi Rn,#dato: R3 Rn + #dato.

  • sub Rn,Rm,Rd: Rd Rn – Rm.

  • subi Rn,#dato: R3 Rn - #dato.

La instrucción add Rn, Rm, Rd produce la suma Rn + Rm y carga el resultado en Rd. La instrucción addi Rn, #dato produce la suma Rn + #dato, donde D es un número positivo de 5 bits, y guarda el resultado en R3. Del mismo modo, la instrucción sub Rn, Rm, Rd genera RnRm y carga el resultado en Rd o Rn #dato y carga el resultado en R3.

7.5.4. Unidad de datapath y registros de datos

La Figura 7.26 representa la unidad de datapath. El principal elemento es una ALU que realiza las operaciones de suma, resta y suma con cero (es decir, deja pasar el dato de entrada intacto). Su funcionamiento es que realiza siempre las operaciones de una entrada conectada a un registro denominado Acumulador y otra entrada que puede ser seleccionada por el multiplexor. El multiplexor seleccionará su entrada según el valor de la señal select.

La ALU realizará las operaciones que se indican en la siguiente Tabla. Hay que notar que las operaciones se realizan siempre con la información que se incluye en el acumulador.

Tabla 7.2 Operaciones realizadas en la ALU

OP1

OP2

Operación

0

0

X + Y

Suma la entrada X y la Y (alu_suma)

0

1

X - Y

Resta a la entrada X la Y (alu_resta)

1

0

0 + Y

Suma 0 a la entrada Y (equivale a pasar Y a la salida, alu_pasa)

1

1

0

Pone 0 a la salida (alu_cero)

El resultado de la ALU se almacena en otro registro de 6 bits (denominado G en la figura). Esta ALU generará unos flags (indicadores) que son tres bits que se activarán en el caso que el resultado de la operación realizada sea cero (flag Z), negativo (flag N) o haya desbordamiento (flag O).

También se representan los registros de datos y su conexión con la ALU a través de un multiplexor. La escritura en estos registros se habilitará cuando el valor de la entrada enable (en_R) conectada se ponga a 1.

../../_images/datapath.png

Figura 7.26 Unidad de datapath y registros de datos.

Si se opera con un dato inmediato (#dato) este se almacenará en el registro correspondiente mediante la señal en_dato. Dado que los datos #dato se representan dentro de la instrucción codificada usando cinco bits, se le añadirá un bit de mayor peso que será siempre 0 para indicar números positivos. En cualquier caso, los registros Rn serán de 6 bits ya que las operaciones que se realicen con ellos pueden necesitar 6 bits.

Los datos pueden transferirse a través del multiplexor de 7 entradas de 6 bits de ancho de un registro del sistema a otro. La salida del multiplexor se llamará databus en la figura porque el término bus se usa a menudo y permite que los datos se transfieran de una ubicación en un registro a otro.

Una MEF controlará todo el sistema y en concreto las líneas select del multiplexor, lo que permite que cualquiera de sus entradas se transfiera a cualquier registro que esté conectado al databus. La utilización de la ALU siempre consistirá en un paso previo de guardar el operador data2 en el acumulador y luego realizar la operación necesaria usando la entrada op y al mismo tiempo seleccionando en el multiplexor la entrada de data1 para realizar la operación: data1 op data2. Por ejemplo, realizar la instrucción sub Rn, Rm, Rd requiere tres ciclos: 1) almacenar Rm en el acumulador, 2) establecer op = 01 (alu_resta) y seleccionar en el multiplexor la opción Rn. Finalmente, 3) el resultado se almacenará en Rd seleccionando en el multiplexor la entrada correspondiente a G_reg y habilitando el registro Rd.

7.5.5. Máquina de estados

Todas las instrucciones se realizarán de manera uniforme en 5 ciclos de reloj (los tres mencionados en el apartado anterior precedidos de un ciclo para leer el registro de instrucción y otro para decodificar el IR). Una vez que se establezca la instrucción a realizar (se utilizarán los diez switches de entrada según los códigos de instrucción vistos anteriormente) y se active la señal run el procesador leerá la instrucción, la decodificará y realizará los pasos necesarios para su ejecución.

Todas las instrucciones se realizan con tres pasos, que se representan mediante la siguiente transferencia de registros:

  1. T0: Acumulador(A) Y

  2. T1: G X OP Acumulador(A)

  3. T2: Z G

Donde X puede ser el contenido de un registro Rn; Y puede ser 0, #dato o un registro Rm (ver Tabla 7.3). La operación a realizar (OP) puede ser la suma de dos entradas, la resta de dos entradas o la suma de una entrada con 0. El resultado se almacena en Z, que puede ser Rn para las operaciones de mover datos o cualquier registro o R3 si son operaciones aritméticas.

Los estados serán los siguientes:

Fetch: estado en el que se lee el registro de instrucción y se espera la orden de run. El usuario puede incluir una instrucción usando los switches siguiendo las instrucciones anteriores.

Decode: decodifica la instrucción a realizar. Consiste en obtener de la instrucción la información correspondiente a los registros, #dato, etc.

T0: Acumulador(A) Y

T1: G X OP Acumulador(A)

T2: Z G

../../_images/grafo_estados.png

Figura 7.27 Grafo de estados de la máquina de control.

7.5.5.1. Lógica de decodificación

Según las operaciones a realizar los valores X, Y y Z serán los siguientes:

Tabla 7.3 Selección de operandos para cada una de las instrucciones.

Intrucciones

OPCODE

X

Y

Z

Operación

nop

00X

load Rn, #dato

010

0

#dato

Rn

0+B

copy Rn, Rm,Rd

011

0

Rm

Rn

0+B

add Rn, Rm,Rd

100

Rn

Rm

Rd

A+B

addi Rn, #dato

101

Rn

#dato

R3

A+B

sub Rn, Rm,Rd

110

Rn

Rm

Rd

A-B

subi Rn, #dato

111

Rn

#dato

R3

A-B

7.5.5.2. Señales de control

Las señales de control que debe generar la máquina de estado para cada paso (T0, T1 y T2).

Instrucciones

OPCODE

en_Rx

#dato

en_dato

select

en_A

en_G

op

nop

00X

0

0

0

0

0

0

00

load Rn, #dato

010

T0

#dato

1

select_dato

1

0

T1

0

select_zero

0

1

10

T2

en_Rn

0

select_G

0

0

copy Rn, Rm

011

T0

0

select_Rm

1

0

T1

0

select_zero

0

1

10

T2

en_Rn

0

select_G

0

0

addi Rn, #dato

101

T0

#dato

1

select_dato

1

0

T1

0

select_Rn

0

1

00

T2

en_R3

0

select_G

0

0

add Rn, Rm

100

T0

1

select_Rm

1

0

T1

0

select_Rn

0

1

00

T2

en_Rd

0

select_G

0

0

subi Rn, #dato

111

T0

#dato

1

select_dato

1

0

T1

0

select_Rn

0

1

01

T2

0

select_G

0

0

sub Rn, Rm

110

T0

1

select_Rm

1

0

T1

0

select_Rn

0

1

01

T2

en_Rd

0

select_G

0

0

En el T0:

load Rn,#dato

copy Rn,Rm

addi Rn,#dato

add Rn,Rm,Rd

subi Rn,#dato

sub Rn,Rm,Rd

en_Rx

0

0

0

0

0

0

#dato

#dato

X

#dato

X

#dato

X

en_dato

1

X

1

X

1

X

select

select_dato

select_Rm

select_dato

select_Rm

select_dato

select_Rm

en_A

1

1

1

1

1

1

en_G

0

0

0

0

0

0

op

XX

XX

XX

XX

XX

XX

En el T1:

load Rn,#dato

copy Rn,Rm

addi Rn,#dato

add Rn,Rm,Rd

subi Rn,#dato

sub Rn,Rm,Rd

en_Rx

0

0

0

0

0

0

#dato

0

0

0

0

0

0

en_dato

0

0

0

0

0

0

select

select_cero

select_cero

select_Rn

select_Rn

select_Rn

select_Rn

en_A

0

0

0

0

0

0

en_G

1

1

1

1

1

1

op

10

10

00

00

01

01

En el T2:

load Rn,#dato

copy Rn,Rm

addi Rn,#dato

add Rn,Rm,Rd

subi Rn,#dato

sub Rn,Rm,Rd

en_Rx

en_Rn

en_Rn

en_R3

en_Rd

en_R3

en_Rd

#dato

0

0

0

0

0

0

en_dato

0

0

0

0

0

0

select

select_G

select_G

select_G

select_G

select_G

select_G

en_A

0

0

0

0

0

0

en_G

0

0

0

0

0

0

op

XX

XX

XX

XX

XX

XX

7.5.6. Trabajo previo a realizar

7.5.6.1. Descripción de la ALU y de la unidad de datapath

En primer lugar se deberá describir en VHDL la ALU que realizaa las operaciones descritas en la Table 7.2.

Posteriormente se deberá describir en VHDL el data-path que se representa en la Figura 7.26. Los registros que aparecen en este esquema deben realizarse con el código VHDL del Listado 7.17, (registro de n bits).

7.5.7. Representación de información en la DE10-Lite

Una vez programada la tarjeta DE10-Lite con nuestro diseño microprocesador, se introducen las instrucciones de forma manual, utilizando los switches y botones de la tarjeta. Los switches se utilizan para indicar las instrucciones a ejecutar (es decir, representan la entrada Din de nuestro microprocesador). Mientras que la entrada Run, que da inicio al procesamiento de cada instrucción, estará conectada a uno de los botones de la tarjeta. El otro botón hará las veces de reset del sistema.

../../_images/uProcesador_pinout.png

Figura 7.28 Pian out del microprocesador.

7.5.8. Entregables

7.5.8.1. Trabajo previo

Por hacer

ET4.1. Memoria de trabajo incluyendo el diagrama de ejecución del microprocesador.

ET4.2. Diagrama de bloques completo del microprocesador.

7.5.8.2. Trabajo práctico

Por hacer

ET4.3. laboratorio virtual (VPL) con la descripción VHDL funcional de la ALU y del datapath.

ET4.4. laboratorio virtual (VPL) con la descripción VHDL de la unidad de control del microprocesador.

ET4.5. Implementación del microprocesador serie en la tarjeta DE10-Lite.

7.5.9. Listados VHDL

Lista 7.17 Código VHDL de un registro de n bits SIGNED con reset y enable (registro_enable.vhd)
-- Ver en el campus Virtual de la Asignatura
Lista 7.18 Código VHDL de un registro de n bits STD_LOGIC con reset y enable (registro_en-rstn.vhd)
-- Ver en el campus Virtual de la Asignatura
Lista 7.19 ENTITY del uProcesador_top.vhd.
ENTITY uProcesador_top IS
  PORT (
        reset_n     : IN  STD_LOGIC ;
        clock       : IN  STD_LOGIC;
        IR_in       : IN  STD_LOGIC_VECTOR(9 DOWNTO 0);
        run_in      : IN  STD_LOGIC;
        results_out : OUT STD_LOGIC_VECTOR(5 DOWNTO 0);
        flags_out   : OUT STD_LOGIC_VECTOR(2 DOWNTO 0);
        done_out    : OUT STD_LOGIC
       );
 END uProcesador_top;
Lista 7.20 Fichero VHDL TrabajoPR4.vhd.
-- Ver en el campus Virtual de la Asignatura
Lista 7.21 Testbench del Microprocesador sencillo (uProcesador_tb).
--Electronica Digital 2º curso EITE/ULPGC
--Para usar únicamente en los Trabajos de asignatura

library ieee;
use ieee.std_logic_1164.all;

entity tb_uProcesador_top is
end tb_uProcesador_top;

architecture tb of tb_uProcesador_top is

    component uProcesador_top
        port (reset_n     : in std_logic;
              clock       : in std_logic;
              IR_in       : in std_logic_vector (9 downto 0);
              run_in      : in std_logic;
              results_out : out std_logic_vector (5 downto 0);
              flags_out   : out std_logic_vector (2 downto 0);
              done_out    : out std_logic);
    end component;

    signal reset_n     : std_logic;
    signal clock       : std_logic;
    signal IR_in       : std_logic_vector (9 downto 0);
    signal run_in      : std_logic;
    signal results_out : std_logic_vector (5 downto 0);
    signal flags_out   : std_logic_vector (2 downto 0);
    signal done_out    : std_logic;

    constant TbPeriod : time := 20 ns; -- EDIT Put right period here
    signal TbClock : std_logic := '0';
    signal TbSimEnded : std_logic := '0';

begin

    dut : uProcesador_top
    port map (reset_n     => reset_n,
              clock       => clock,
              IR_in       => IR_in,
              run_in      => run_in,
              results_out => results_out,
              flags_out   => flags_out,
              done_out    => done_out);

    -- Clock generation
    TbClock <= not TbClock after TbPeriod/2 when TbSimEnded /= '1' else '0';

    -- EDIT: Check that clock is really your main clock signal
    clock <= TbClock;

    stimuli : process
    begin
        -- EDIT Adapt initialization as needed
        IR_in <= (others => '0');
        run_in <= '1';

        -- Reset generation
        -- EDIT: Check that reset_n is really your reset signal
        reset_n <= '0';
        wait for 20 ns;
        reset_n <= '1';

        -- EDIT Add stimuli here
        wait for 2* TbPeriod;
        run_in <= '0';
        IR_in <= "0100011111"; --Load Din (31) in R0
        wait for 20 ns;
        run_in <= '1';
        wait for 3* TbPeriod;
        run_in <= '0';
        wait for 2* TbPeriod;
        IR_in <= "1010011111"; --add Din (31) a R0
        wait for 20 ns;
        run_in <= '1';
        wait for 2* Tbperiod;
        run_in <= '0';
        wait for 3* TbPeriod;
        IR_in <= "0110111000"; --copy R3 a R1
        wait for 20 ns;
        run_in <= '1';
        wait for 2* TbPeriod;
        run_in <= '0';
        wait for 4* TbPeriod;
        run_in <= '1';
        wait for 10 ns;

        -- Stop the clock and hence terminate the simulation
        TbSimEnded <= '1';
        wait;
    end process;

end tb;