Está en la página 1de 11

4to.

LABORATORIO DE SISTEMAS DIGITALES II EE-636M, PERÍODO


2016-2
ARANGUREN PASCASIO EDDY, PEÑALOZA MEZA BRYAN
Facultad de Ingeniería Eléctrica y Electrónica
Universidad Nacional de Ingeniera
{ed.aranguren.p, bdanielpm}@gmail.com

Informe Final
ABSTRACT

La presente experiencia evalúa el uso de la herramienta EDA de diseño digital Quartus II


de Altera en el diseño e implementación de la especificación de un computador básico
(CPU). Se busca asimismo familiarizarnos con una gama más amplia de lenguaje de
instrucciones del computador, enfocándonos también en los operandos entre registros,
así como en el uso adecuado de un set de instrucciones codificados en memoria, una
exclusiva para datos y otra para instrucciones, con la finalidad de cumplir la función para
la cual será diseñada el computador: Ordenamiento de números.

I. INTRODUCCION

El desarrollo de esta experiencia busca el diseño de un CPU, haciendo uso de un repertorio de


instrucciones, las subrutinas, así como de las diferentes operaciones y transferencias a nivel de
registros, teniéndose en cuenta los registros fuente y destino, lo cual nos dará una idea sobre
el código en VHDL que se va a implementar. Con esta experiencia se busca generar un
algoritmo que realice el ordenamiento de números mediante el método de la burbuja. Esta
experiencia nos permitirá también comprender la comunicación entre el CPU y dos memorias
trabajando en conjunto, una de tipo RAM y la otra de tipo ROM, así como el uso de los PLL en
la generación de relojes de múltiples frecuencias, a las cuales se les puede ajustar la fase de
acuerdo a conveniencia nuestra; aplicando los conocimientos adquiridos en las experiencias
previas, como el diseño jerárquico, bloques fetch, decodificación, ejecución y escritura/lectura
(para este caso, en un solo ciclo), dentro de nuestro CPU.

II. TRABAJOS RELACIONADOS

Para poder realizar esta experiencia fue muy importante tener presente los trabajos
desarrollados en las experiencias previas, especialmente el de la tercera experiencia, la cual
nos dio un soporte base para el diseño de los diferentes bloques de nuestro CPU monociclo.

III. PLANTEAMIENTO DEL PROBLEMA

Examinaremos una implementación que incluye un subconjunto del conjunto de instrucciones


MIPS básico:
- Las instrucciones de referencia de memoria Load Word (lw) and Store Word (sw).
- Las instrucciones aritméticas-lógicas add, sub, and, or y slt.
- Las instrucciones Branch Equal (beq) y Jump (j).
Este subconjunto no incluye todas las instrucciones de enteros (por ejemplo, faltan:
multiplicar, dividir y shift), ni incluye ninguna instrucción de punto flotante. Sin embargo, los
principios clave utilizados en la creación de un camino de datos y el diseño del control se
ilustra en la Fig.1. La implementación de las instrucciones restantes es similar.
Al examinar la implementación, tendremos la oportunidad de ver cómo la arquitectura de
conjunto de instrucciones determina muchos aspectos de la implementación y cómo la
elección de diversas estrategias de implementación afecta la velocidad de reloj y CPI para la
máquina.
En la experiencia anterior, examinamos las instrucciones MIPS básicas, incluyendo las
instrucciones aritméticas-lógicas enteras, las instrucciones de referencia de memoria y las
instrucciones de ramificación. Mucho de lo que se necesita hacer para implementar estas
instrucciones es lo mismo, independiente de la clase exacta de instrucción. Para cada
instrucción, los dos primeros pasos son idénticos:
- Enviar el contador de programa (PC) a la memoria que contiene el código y buscar la
instrucción de esa memoria
- Leer uno o dos registros, utilizando los campos de la instrucción para seleccionar los registros
a leer. Para la instrucción de palabra de carga, necesitamos leer sólo un registro, pero la
mayoría de las otras instrucciones requieren que leamos dos registros.

IV. DESCRIPCION GENERAL DE LA SOLUCION

El procedimiento para el desarrollo de la experiencia se basó en el desarrollo de los siguientes


bloques, los cuales han sido desarrollados teniendo como referencia la estructura física del
diagrama correspondiente a cada una de las partes de nuestro CPU RISC monociclo.

Bloque ifetch:

En este bloque el registro PC se incrementa en 1. Pero además se debe tener consideraciones


con respecto al next_PC, dependiendo del tipo de instrucción que se está realizando si es una
instrucción tipo Branch, tipo Jump u otras instrucciones.
Posterior a ello, se desarrolló un proceso donde actualizamos el valor del PC, de forma
síncrona y con una señal de reset.
Fue necesario en este caso, la creación de señales adicionales a las establecidas inicialmente
en la experiencia. Así, se utilizaron las señales Jump, proveniente de la parte de Control, y el
Jump_target correspondiente al formato de PC establecida para estos tipos de instrucciones.

Bloque control:

En el desarrollo de este este diseño simplemente guiándonos de la Tabla N°1 y de la Fig1, se ha


desarrollado la habilitación de señales de control dependiente de los códigos de operación
asociados a cada instrucción.

Bloque execute:

En este bloque se han definido las señales del ALU_Result, la cual me definirá de acuerdo a la
instrucción, la operación entre registros a realizar.
Asimismo se ha definido una señal señal_aux la cual ha sido definida basándonos en el
multiplexor cuya señal de control es el ALUsrc. Tambien se ha desarrollado la expresión de la
señal Add_Result basándonos también en la Fig.1.

Bloque decode:

En este bloque, se han definido las señales read_data_1 y read_data_2 las cuales serán
obtenidas del arreglo de registros register_array. Asimismo, un multiplexor, para la señal write
register_address, las cual puede provenir de dos diferentes caminos: write_register_rt
(Instruction (9 DOWNTO 8)) y write_register_rd (Instruction (7 DOWNTO 6)).
Luego un proceso, donde se inicializa el register_array con la señal de reset, y de forma
síncrona se va actualizando. Así, se consideró que sobre el registro 0 no se puede escribir, y
que en la instrucción jump and link, el valor de R(3) debe ser actualizado con el valor de PC ya
aumentado en 1.

Bloque write bacK:

En este bloque simplemente se ha trabajado al igual que un multiplexor, donde la señal


write_data puede provenir de dos diferentes caminos, tanto de la señal read_data como del
Alu_result, dependiendo de la señal de control MemtoReg.

Fig. 1 Organización del CPU RISC monociclo


Fig.2 Datapath con instrucción Jump incluida

Tabla1 Señales de control para diferentes instrucciones

V. RESULTADOS EXPERIMENTALES

1. ANÁLISIS DE TIEMPOS

Según el análisis de tiempos secuenciales:

T min =T cq + T pd +T su

T min =15.106 ns+2.612 ns−0.049 ns=17.669 ns

Luego
1
f max = =56.59 MHz
T min
Los peores casos de tsu, tco , tpd y th se dan:

Tabla 2: Peores casos en el Análisis de tiempos


Tip Peor Desde Hacia
o caso
tsu -0.049 altera_internal_jtag~TMSUTA sld_hub:sld_hub_inst|node_ena[2]~reg0
P
tco 15.106n iFetch ALU_result_out[0]
s
tpd 2.612ns altera_internal_jtag~TDO altera_reserved_tdo
th 1.984ns altera_internal_jtag~TMSUTA sld_hub:sld_hub_inst|sld_shadow_jsm:shadow_jsm|
P state[9]

VI. Observaciones y Conclusiones:

 En el transcurso de esta experiencia pudimos observar a diferencia de la experiencia


anterior, en que los diferentes bloques de fetch, decodificación y ejecución,
incluyendo los procesos de escritura/lectura, se realizan en solo un pulso de reloj.
 En esta experiencia se está trabajando con dos memorias, una memoria de datos
(RAM), y una memoria donde se encuentra nuestro programa (ROM). En base al uso
de estas dos memorias, se puede decir que nuestro CPU a implementar es de
arquitectura tipo Hardvard.
 A diferencia de la experiencia anterior, en esta experiencia se trabaja con una amplia
gama de instrucciones de diferentes formatos, y a su vez se desarrolla mucho la
operación entre registros.
 Se pudo asimismo observar el trabajo con dos PLL para generar las señales del reloj
clock y clockram, observando que cuando se trabajaba con un desfase, el valor del tco
alcanzaba un valor muy superior al requerido, motivo por el cual fue necesario trabajar
con dos PLL que trabajen en fase, para poder generar frecuencias de reloj múltiplos a
una frecuencia de referencia.
 Asimismo fue necesario generar la señal jump para este tipo de instrucciones, cuyas
operaciones fueron definidas en el bloque execute, aunque se pudo tambien visulaizar
que el PC era afectado tambien por ciertas instrucciones lo cual nos sugeria de definir
un camino adicional para nuestra señal PC, lo cual implicaba modificaciones en nuestro
bloque FETCH.
 Se desarrolló el proyecto basándonos en el esquema presentado en la guía, para lo
cual en el tope de jerarquía, fue necesario realizar a conexión correcta entre las
señales, asi como definir señales auxiliares a utilizarse.

VII. RECONOCIMIENTOS

Para el posible desarrollo de la experiencia, se valora el aporte de nuestro profesor de curso,


quien, con sus aportes y asesorías continuas, nos brindó un panorama mayor sobre cómo
implementar el diseño solicitado.

VIII. REFERENCIAS
[1] Computer Orgamization and Design: The Hardware and Software Interface. CAPITULOS: “El
lenguaje de instrucciones en un computador”, “Estructura interna del procesador” - - David A.
Patterson, Jhon l. Hennessy

ANEXOS

Bloque Control
-- control module (implements TinyMIPS RISC processor control unit)
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.NUMERIC_STD.ALL;

ENTITY control IS
PORT( Opcode : IN STD_LOGIC_VECTOR(3 DOWNTO 0);
RegDst : OUT STD_LOGIC;
ALUSrc : OUT STD_LOGIC;
MemtoReg : OUT STD_LOGIC;
RegWrite : OUT STD_LOGIC;
MemRead : OUT STD_LOGIC;
MemWrite : OUT STD_LOGIC;
Branch : OUT STD_LOGIC;
Jump : OUT STD_LOGIC --auxiliar
);
END control;

-- TODO: Implement the behavioral description of control unit


ARCHITECTURE behavior OF control IS
-- you may need to create some signals to generate the output signals
BEGIN

-- TODO: Code to generate control signals using opcode bits


RegDst <= '1' when opcode = "0000" or opcode ="0001"or opcode ="0010"or
opcode ="0011"or opcode ="0100"or opcode ="0101"or opcode ="0110" else '0';
ALUSrc <= '1' when opcode = "1101"or opcode ="1110" else '0';
MemtoReg <= '1' when opcode = "1101" else '0';
RegWrite <= '1'when opcode = "0000" or opcode ="0001"or opcode ="0010"or
opcode ="0011"or opcode ="0100"or opcode ="0101"or opcode ="0110"or opcode="1101" else
'0';
MemRead <= '1'when opcode = "1101" else '0';
MemWrite <='1' when opcode = "1110" else '0';
Branch <= '1' when opcode = "1111" else '0';
Jump <= '1' when opcode = "0111" or opcode = "1000" else '0'; -- auxiliar
END behavior;

Bloque Execute
-- EXEcute stage (implements the data ALU and
-- Branch Address Adder for the TinyMIPS RISC processor)
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.NUMERIC_STD.ALL;

ENTITY EXEcute IS
PORT( Read_data_1 : IN STD_LOGIC_VECTOR(15 DOWNTO 0);
Read_data_2 : IN STD_LOGIC_VECTOR(15 DOWNTO 0);
Sign_extend : IN STD_LOGIC_VECTOR(15 DOWNTO 0);
Opcode : IN STD_LOGIC_VECTOR( 3 DOWNTO 0);
ALUSrc : IN STD_LOGIC;
Zero : OUT STD_LOGIC;
ALU_Result : OUT STD_LOGIC_VECTOR(15 DOWNTO 0);
Add_Result : OUT STD_LOGIC_VECTOR(15 DOWNTO 0);
PC_plus_1 : IN STD_LOGIC_VECTOR(15 DOWNTO 0)
);
END EXEcute;

-- TODO: Implement the behavioral description of EXE stage


ARCHITECTURE behavior OF EXEcute IS
-- TODO: you may need to include signals for easier implementation
SIGNAL señal_aux : STD_LOGIC_VECTOR(15 DOWNTO 0);

BEGIN
señal_aux <= Sign_extend when (ALUSrc='1') else
Read_data_2;

Add_Result <= std_logic_vector(unsigned(PC_plus_1)+unsigned(Sign_extend));

process(Read_data_1,señal_aux,Opcode)

begin
case Opcode is
when "0000"|"1001"|"1101"|"1110" => --suma
ALU_Result <= std_logic_vector(unsigned(Read_data_1)+unsigned(señal_aux));
Zero <= '0';

when "0001"|"1010" => --resta


ALU_Result <= std_logic_vector(unsigned(Read_data_1)-unsigned(señal_aux));
Zero <= '0';

when "0010" => --AND


ALU_Result <= Read_data_1 AND señal_aux;
Zero <= '0';

when "0011" => --OR


ALU_Result <= Read_data_1 OR señal_aux;
Zero <= '0';

when "0100" => --NOR


ALU_Result <= Read_data_1 NOR señal_aux;
Zero <= '0';

when "1011" => --ANDI


ALU_Result <= Read_data_1 AND (señal_aux AND "0000000011111111");
Zero <= '0';

when "1100" => --XORI


ALU_Result <= Read_data_1 XOR (señal_aux AND "0000000011111111");
Zero <= '0';

when "0101" => -- slt


if(Read_data_1 < señal_aux) then
ALU_Result <="0000000000000001";
Zero <= '0';
else
ALU_Result <="0000000000000000";
Zero <= '0';
end if;

when "1111" => --Igual


if(Read_data_1 = señal_aux) then
ALU_Result <="0000000000000000";
Zero <= '1';
else
ALU_Result <="0000000000000000";
Zero <= '0';
end if;

when others =>


ALU_Result <="0000000000000000";
Zero <= '0';
end case;
end process;

END behavior;

Bloque IDecode
-- IDecode stage (implements the register file for the TinyMIPS RISC processor)
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.NUMERIC_STD.ALL;

ENTITY IDecode IS
PORT( read_data_1 : OUT STD_LOGIC_VECTOR(15 DOWNTO 0);
read_data_2 : OUT STD_LOGIC_VECTOR(15 DOWNTO 0);
Instruction : IN STD_LOGIC_VECTOR(15 DOWNTO 0);
write_data : IN STD_LOGIC_VECTOR(15 DOWNTO 0);
RegWrite : IN STD_LOGIC;
RegDst : IN STD_LOGIC;
Sign_extend : OUT STD_LOGIC_VECTOR(15 DOWNTO 0);
clock : IN STD_LOGIC;
reset : IN STD_LOGIC;
PC_plus_1 : IN STD_LOGIC_VECTOR(15 DOWNTO 0));

END IDecode;

ARCHITECTURE behavior OF IDecode IS

TYPE register_file IS ARRAY (0 TO 3) OF STD_LOGIC_VECTOR(15 DOWNTO 0);


SIGNAL register_array : register_file;
SIGNAL write_register_address : STD_LOGIC_VECTOR( 1 DOWNTO 0);
SIGNAL read_register_rs : STD_LOGIC_VECTOR( 1 DOWNTO 0);
SIGNAL read_register_rt : STD_LOGIC_VECTOR( 1 DOWNTO 0);
SIGNAL write_register_rt : STD_LOGIC_VECTOR( 1 DOWNTO 0);
SIGNAL write_register_rd : STD_LOGIC_VECTOR( 1 DOWNTO 0);
SIGNAL immediate_value : STD_LOGIC_VECTOR( 7 DOWNTO 0);

BEGIN
read_register_rs <= Instruction(11 DOWNTO 10);
read_register_rt <= Instruction( 9 DOWNTO 8);
write_register_rt <= Instruction( 9 DOWNTO 8);
write_register_rd <= Instruction( 7 DOWNTO 6);
immediate_value <= Instruction( 7 DOWNTO 0);

-- TODO: Read Register 1 Operation


read_data_1 <= register_array(to_integer(unsigned(read_register_rs)));

-- TODO: Read Register 2 Operation


read_data_2 <=register_array(to_integer(unsigned(read_register_rt))) ;

-- TODO: Mux for Register Write Address


write_register_address <= write_register_rt when (RegDst='1') else write_register_rd;

-- TODO: Generate Sign_extend signal


Sign_extend <= "00000000"&immediate_value;

-- TODO: Specify the process to update the register file

process(clock, reset, Instruction)


begin
if (reset = '1') then
for k in 0 to 3 loop
register_array(k) <= "0000000000000000";
end loop;
elsif(clock'event and clock = '1') then
if (write_register_address /= "00") then
register_array(to_integer(unsigned(write_register_address)))<= write_data;
case Instruction(15 downto 12) is
when "1000" =>
register_array(3)<= PC_plus_1;
when others => null;
end case;
end if;
end if;
end process;
END behavior;

Bloque IFetch
-- IFetch stage (provides the PC and instruction memory for the TinyMIPS RISC processor)
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.NUMERIC_STD.ALL;
LIBRARY altera_mf;
USE altera_mf.altera_mf_components.all;

ENTITY IFetch IS
PORT( Instruction_out : OUT STD_LOGIC_VECTOR(15 DOWNTO 0);
PC_plus_1_out : OUT STD_LOGIC_VECTOR(15 DOWNTO 0);
Add_result : IN STD_LOGIC_VECTOR(15 DOWNTO 0);
Branch : IN STD_LOGIC;
Zero : IN STD_LOGIC;
PC_out : OUT STD_LOGIC_VECTOR(15 DOWNTO 0);
clock : IN STD_LOGIC;
clki : IN STD_LOGIC;
reset : IN STD_LOGIC;
Jump : IN STD_LOGIC;
Read_data_1 : IN STD_LOGIC_VECTOR(15 DOWNTO 0));
END IFetch;

ARCHITECTURE behavior OF Ifetch IS


SIGNAL PC, PC_plus_1: STD_LOGIC_VECTOR(15 DOWNTO 0);
SIGNAL Read_Address : STD_LOGIC_VECTOR( 9 DOWNTO 0);
SIGNAL clock_int : STD_LOGIC;
SIGNAL Instruction : STD_LOGIC_VECTOR(15 downto 0);
SIGNAL next_PC: STD_LOGIC_VECTOR(15 DOWNTO 0);
SIGNAL Jump_target: STD_LOGIC_VECTOR(15 DOWNTO 0);
BEGIN
--ROM for Instruction Memory
inst: altsyncram
GENERIC MAP (
operation_mode => "ROM",
width_a => 16,
widthad_a => 10,
numwords_a => 1024,
outdata_aclr_a => "NONE",
width_byteena_a => 1,
clock_enable_input_a => "BYPASS",
clock_enable_output_a => "BYPASS",
lpm_type => "altsyncram",
outdata_reg_a => "UNREGISTERED",
init_file => "rom.mif",
intended_device_family => "Cyclone II",
LPM_HINT =>
"ENABLE_RUNTIME_MOD=YES,INSTANCE_NAME=ROM")
PORT MAP (
clock0 => clock_int,
address_a => Read_Address,
q_a => Instruction);

clock_int <= clki;


Read_Address <= PC(9 downto 0);

-- TODO: Specify how to obtain next PC


PC_plus_1 <= std_logic_vector(unsigned(PC)+1);
Jump_target <= PC_plus_1(15 DOWNTO 12) & Instruction(11 downto 0);
--process(Read_data_1,Instruction,Jump_target,Add_result,Branch,Zero,Jump,PC_plus_1 )
-- begin
-- case Instruction (15 DOWNTO 12) is
-- when "0110" =>
-- next_PC <= Read_data_1;
-- when others =>
next_PC <= Read_data_1 when (Instruction (15 DOWNTO 12)= "0110") else
PC_plus_1 when ((Branch = '0' OR Zero = '0') and Jump
= '0') else
Add_result when(Branch = '1' and Zero = '1' and Jump =
'0') else
Jump_target;
-- end case;
-- end process;
-- TODO: Specify how the PC is updated
process(clock, reset)
begin
if (reset = '1') then
PC <= x"0000";
elsif(clock'event and clock = '1') then

PC <= next_PC;

end if;
end process;
PC_plus_1_out <= PC_plus_1;
Instruction_out <= Instruction;
PC_out <= PC;
END behavior;

Bloque WriteBack
-- WriteBack stage of TinyMIPS RISC processor
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.NUMERIC_STD.ALL;

ENTITY WriteBack IS
PORT( MemtoReg : IN STD_LOGIC;
read_data : IN STD_LOGIC_VECTOR(15 DOWNTO 0);
ALU_result : IN STD_LOGIC_VECTOR(15 DOWNTO 0);
write_data : OUT STD_LOGIC_VECTOR(15 DOWNTO 0));
END WriteBack;

ARCHITECTURE behavior OF WriteBack IS

BEGIN

write_data <= read_data when (MemtoReg = '1') else ALU_result;

END behavior;

También podría gustarte