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.

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.
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:
Operación que involucra solo a registros:
OPCODE2 |
OPCODE1 |
OPCODE0 |
Rn1 |
Rn0 |
Rm1 |
Rm0 |
0 |
Rd1 |
Rd0 |
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 Rn − Rm 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.
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.

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:
T0: Acumulador(A) ← Y
T1: G ← X OP Acumulador(A)
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

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

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¶
-- Ver en el campus Virtual de la Asignatura
-- Ver en el campus Virtual de la Asignatura
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;
-- Ver en el campus Virtual de la Asignatura
--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;