- Trabajo Práctico
- Arquitectura de Computadoras I
- Universidad Nacional de Tres de Febrero
- Chavez, Matias. Legajo N° 41902
- Lottero, Bruno. Legajo N° 18434
El objetivo de esta práctica es ampliar a la implementación el microprocesador MIPS (visto en clase de teoría) en VHDL. En concreto, se va a realizar la versión segmentada del microprocesador, cuyos detalles se pueden encontrar en: "Computer Organization and Design: The Hardware/Software Interface ", por David A.Patterson y John L. Hennessy. El modelo de las memorias de datos y programas proporcionado (archivo memory.vhd) no introduce ciclos de espera y responde en el mismo ciclo. Además, utiliza dos archivos separados para el contenido inicial de cada memoria, archivo llamado “program1” para memoria de instrucciones y “data” para memoria de datos. Se proporcionan un archivo de ejemplo (program1.s) para utilizar junto con el testbench de la práctica, si bien se pueden generar otros archivos correspondientes a otros códigos para hacer más pruebas.
Se dispone de la implementación de un procesador completo que admite las siguientes instrucciones: add, sub, and, or, lw, sw, slt y beq. En cualquier caso, la instrucción beq, que implica riesgos de control por ser un salto, funcionará “anómalamente” en la versión básica del ejercicio obligatorio.
Se pide que al diseño completo del procesador MIPS segmentado realizado por la cátedra, incorporar las siguientes instrucciones y verificar el diseño utilizando el archivo “program1” proporcionado por la cátedra.
Para el agregado de las instrucciones LUI, ADDI, AND y ORI, se realizaron cambios sobre
- processor.vhd
- Se aumento la dimension de la señal de control de la ALU de 2 a 3 bits, para soportar los inmediatos nuevos.
signal ALUOp: std_logic_vector(2 downto 0);
- Se definieron en la etapa ID, las señales de control para las operaciones según fue necesario
--LUI--
when "001111" =>
ID_RegWrite<= '1';
ID_MemToReg<= '0';
ID_MemRead<= '0';
ID_MemWrite<= '0';
ID_Branch<= '0';
ID_RegDst<= '0';
ID_AluOp<= "100";
ID_ALUSrc<= '1';
--ADDI--
when "001000" =>
ID_RegWrite<= '1';
ID_MemToReg<= '0';
ID_MemRead<= '0';
ID_MemWrite<= '0';
ID_Branch<= '0';
ID_RegDst<= '0';
ID_AluOp<= "101";
ID_ALUSrc<= '1';
--ANDI--
when "001100" =>
ID_RegWrite<= '1';
ID_MemToReg<= '0';
ID_MemRead<= '0';
ID_MemWrite<= '0';
ID_Branch<= '0';
ID_RegDst<= '0';
ID_AluOp<= "110";
ID_ALUSrc<= '1';
--ORI--
when "001101" =>
ID_RegWrite<= '1';
ID_MemToReg<= '0';
ID_MemRead<= '0';
ID_MemWrite<= '0';
ID_Branch<= '0';
ID_RegDst<= '0';
ID_AluOp<= "111";
ID_ALUSrc<= '1';
--J--
when "000010" =>
ID_RegWrite<= '1';
ID_MemToReg<= '0';
ID_MemRead<= '0';
ID_MemWrite<= '0';
ID_Branch<= '0';
ID_Jump <= '1';
ID_RegDst<= '0';
ID_AluOp<= "011"; --alu va por others
ID_ALUSrc<= '1';
- Se definieron en la etapa EX los casos anteriores en la unidad de control para la ALU
when "100" => -- LUI
AluControl <= "100";
when "101" => -- ADDI
AluControl <= "010";
when "110" => -- ANDI
AluControl <= "000";
when "111" => -- ORI
AluControl <= "001";
- alu.vhd
- En la ALU se adecuó la operación para LUI y luego se redefinieron los valores del case en 3 bits
process(a, b, control)
begin
case control is
when "000" => aux <= a and b;
when "001" => aux <= a or b;
when "010" => aux <= a + b;
when "110" => aux <= a - b;
when "111" =>
if(a<b) then
aux <= x"00000001";
else aux <= x"00000000";
end if;
when "100" => aux <= b(15 downto 0) & x"0000";
when others => aux <= x"00000000";
end case;
end process;
Para el agregado de la J, se realizaron cambios sobre processor.vhd
- En primer lugar se creo la señal de Jump y las señales que resguardan el cálculo del salto
-- Control de mux jump
signal Jump: std_logic;
--ETAPA ID--
signal ID_Jump: std_logic;
--ETAPA EX--
signal EX_Jump: std_logic;
-- Para direccion de salto en jump
signal EX_PC_Jump: std_logic_vector (31 downto 0);
- En la etapa ID se agrega el seteo de la señal de control del jump en CONTROL_UNIT según corresponda
ID_Jump <= '1';
o
ID_Jump <= '0';
- Además se propaga la señal de control
EX_Jump <= ID_Jump;
- A continuación en la etapa EX, se realiza el cálculo del salto para el J y se propaga la señal
-- Calculo dirección de salto (Jump)
EX_PC_Jump <= EX_PC_4(31 downto 28)&EX_immediate(25 downto 0)&"00";
-- Control de Jump
Jump <= EX_Jump;
- Por ultimo, en la etapa IF, se aplica nueva lógica para decidir como cargar el PC según como se hayan cargado las señales antes mencionadas
next_PC <= MEM_PC_Branch when (PcSrc = '1') else
EX_PC_jump when ((PcSrc = '0') and (Jump = '1')) else
PC_4;
Mediante esta lógica podemos tener una instrucción BEQ y a continuación una J en memoria de instrucciones y el funcionamiento va a ser el esperado (no se necesitan NOP) se escribió el programa probando_beq_j.s para comprobar dicho funcionamiento.
Para complementar lo visto en clase y por una cuestión de consistencia con la lógica anterior, optamos por agregar el flush de las etapas que corresponden. El diseño de este procesador MIPS trabaja sin predicción de saltos, o tambien podriamos decir que realiza predicción no efectiva (se asume que el salto no es efectivo). De esta manera, cuando realmente se efectua un salto, es necesario limpiar las etapas que contienen instrucciones mal cargadas. Para realizar esto, aplicamos una logica de reset en los cambios de etapa, a saber:
-
La existencia de un BEQ se conoce en la etapa ID sobre la señal ID_Branch y la misma se propaga hasta la etapa MEM en la señal Branch, ya que recién en la etapa EX se confirma la efectividad del salto. Luego, en caso positivo, se limpian los registros IF/ID-ID/EX-EX/MEM para poder cargar el PC correcto a donde apunta el salto.
-
Luego para el caso del J, se conoce tambien en la etapa ID la señal ID_Jump, la misma se propaga hasta EX en donde se carga finalmente la señal Jump. Luego al avanzar un ciclo, estando el J en etapa MEM, se deben limpiar los registros IF/ID-ID/EX-
-
Para probar el funcionamiento, se escribieron los programas adicionales de prueba:
- probando_flush_beq.s
- probando_flush_j.s
- También es válido para probar esto el programa Program1.s provisto originalmente
-- REGISTRO DE SEGMENTACION IF/ID
if (PcSrc = '1' or Jump = '1') then
ID_PC_4 <= (others => '0');
ID_Instruction <= (others => '0');
-- REGISTRO DE SEGMENTACION ID/EX
if ( PcSrc = '1' or Jump = '1') then
EX_data1_rd <= (others =>'0');
EX_data2_rd <= (others =>'0');
EX_RegWrite <= '0' ;
EX_MemToReg <= '0' ;
EX_MemRead <= '0' ;
EX_MemWrite <= '0' ;
EX_Branch <= '0' ;
EX_Jump <= '0' ;
RegDst <= '0' ;
ALUOp <= (others =>'0');
ALUSrc <= '0' ;
EX_immediate <= (others => '0');
EX_rt <= (others => '0');
EX_rd <= (others => '0');
-- REGISTRO DE SEGMENTACION EX/MEM
if ( PcSrc = '1') then
MEM_RegWrite <= '0';
MEM_MemToReg <= '0';
MEM_MemRead <= '0';
MEM_MemWrite <= '0';
Branch <= '0';
MEM_Zero <= '0';
MEM_AluResult <= (others => '0');
MEM_data2_rd <= (others => '0');
MEM_Instruction_RegDst <= (others => '0');
MEM_PC_Branch <= (others => '0');