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:

𝑻𝒎𝒊𝒏 = 𝑻𝒄𝒒 + 𝑻𝒑𝒅 + 𝑻𝒔𝒖

𝑇𝑚𝑖𝑛 = 15.106𝑛𝑠 + 2.612𝑛𝑠 − 0.049𝑛𝑠 = 17.669𝑛𝑠

Luego
1
𝑓𝑚𝑎𝑥 = = 56.59𝑀𝐻𝑧
𝑇𝑚𝑖𝑛
Los peores casos de tsu, tco , tpd y th se dan:

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


Tipo Peor Desde Hacia
caso
tsu -0.049 altera_internal_jtag~TMSUTAP sld_hub:sld_hub_inst|node_ena[2]~reg0
tco 15.106ns iFetch ALU_result_out[0]
tpd 2.612ns altera_internal_jtag~TDO altera_reserved_tdo
th 1.984ns altera_internal_jtag~TMSUTAP sld_hub:sld_hub_inst|sld_shadow_jsm:shadow_jsm|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