Procesador MIPS segmentado
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
signal ALUOp: std_logic_vector(2 downto 0);
--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';
when "100" => -- LUI
AluControl <= "100";
when "101" => -- ADDI
AluControl <= "010";
when "110" => -- ANDI
AluControl <= "000";
when "111" => -- ORI
AluControl <= "001";
when “100” => aux <= b(15 downto 0) & x”0000”;
if(a<b) then
aux <= x"00000001";
else aux <= x"00000000";
end if;
end process;
### Agregado de Jump
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
```vhdl
-- 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);
o
ID_Jump <= '1';
ID_Jump <= '0';
EX_Jump <= ID_Jump;
-- 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;
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:
-- 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');