-------------------------------------------------------------------------
--	Data of Last Change: 25th/October/2022
--  MIPS_S_withBRAMs Processing System
-- 		A MIPS ISA subset coupled to program/data memories implemented
--		with BRAMs from Xilinx FPGAs
--	Authors: 	Ney Calazans / Fernando Moraes
--  Nov/2007 - First version - with data and instruction memories
--  Jun/2012 - Version where the processor is transformed into a single
--		entity-architeture pair
--  26/Nov/2021 - Starting adaptations to update the system. 
--		- Name changed to MIPS_S_withBRAMs
--		- Package name changed to p_MIPS_S
--  05/Apr/2022 - A major revision started:
--		- Improved  comments on the whole code
--		- A more complete external interface provided to the 
--			MIPS_S_withBRAMs system, with a clearer separation
--			between CPU and USER interfaces to access the data 
--			memory. The system now supports full external control
--			to not only read data memory words, but to read or write
--			words or bytes. The CPU or USER control is handled by
--			the single selUSER signal (when in '1' the USER controls
--			reading/writing from/to memory, otherwise CPU has control
--			over the data memory.).
--	18/10/2022 (Ney) - Continuing the major changes we started to make
--          the code compatible with the latest version of the simulatable 
--			MIPS_S organization
--		- PC/NPC/IR moved to the control unit;
--		- All regs are now rising edge sensitive;
--      - All processor actions are now only executed in the rising edge
--          of the clock;
--		- Multiplication/division modules names were changed (to English)
--		- Several changes in the ordering and naming of signals to make
--			this implementation converge with the simulatable MIPS_S;
--		- The multiplier and divider modules were updated, to become 
-- 			identical to the newer versions from the simulatable MIPS_S;
--		- Some signal names were changed to English version, to keep the
-- 			coherence of the conventions, especially in the mult/div
--			modules interface;
--  20/10/2022 (Ney) - After the many changes above a problem arised in 
--          code that employs real-world synchronous memory driven by the
--          clock edge. Data is only available at the end of the clock edge
--          where memory acess signals are asserted. Thus, registers
--          receiving data from memory started to contain wrong data in some
--          memory read operations. To solve this, we simple substituted 
--          registers IR and MDR by wires. This was validated.
--  25/10/2022 (Ney) - The main change here is to insert the hold_in control
--          input and the hold_out status output to the MIPS_S, to allow
--          better control by external entities of the memory and of the
--          processor. Also, the signal selUSER no longer resets MIPS_S,
--          since it is now possible to suspend the processor actions by
--          using hold_in. Note that hold_in and hold_out only affect the
--          MIPS_S control unit. Care were taken to avoid incrementing the
--          and writing NPC when entering the hold state from the fetch state.
--          Also, a bug was discovered. The whilo signal was undriven.
--          The problem has been solved now.
--
--	To Dos - there are several ways to enhance this system, let us see:
--			1) modify the selUSER functionality, in order NOT to reset 
--				the processor, by transforming it into a "hold" input
--				to MIPS_S. This enables to suspend and continue 
--				processor software execution, instead of starting over
--				software execution every time selUSER goes to '1' and
--				then goes back to '0';
--			...
-------------------------------------------------------------------------

--++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
--++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-- The processor 8 internal modules: 
--      (1) the package -> p_MIPS_S
--      (2) the basic register -> reg32bits
--      (3) the 32-register bank -> reg_bank
--      (4) the 17-operation ALU -> alu
--      (5) a serial shift-add multiplier -> mult_serial
--      (6) a serial shift-subtract divider -> div_serial
--      (7) the MIPS_S datapath -> datapath
--      (8) the MIPS_S control unit -> control_unit
--++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
--++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

--++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-- (1) A package with basic types
--++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
library IEEE;
use IEEE.Std_Logic_1164.all;
package p_MIPS_S is  

        -- inst_type defines the instructions decodeable by the control unit
        type inst_type is  
                ( ADDU, SUBU, AAND, OOR, XXOR, NNOR, SSLL, SLLV, SSRA, SRAV, SSRL, SRLV,
                ADDIU, ANDI, ORI, XORI, LUI, LBU, LW, SB, SW, SLT, SLTU, SLTI,
                SLTIU, BEQ, BGEZ, BLEZ, BNE, J, JAL, JALR, JR, MULTU, DIVU, MFHI, MFLO,
                invalid_instruction);
        type microinstruction is record
                CY1:   std_logic;       -- command of the first stage
                CY2:   std_logic;       --    "    of the second stage
                walu:  std_logic;       --    "    of the third stage
                wpc:   std_logic;       -- PC write enable
                wreg:  std_logic;       -- register bank write enable
				whilo: std_logic;       -- HI and LO registers write enable
                ce:    std_logic;       -- Chip enable and R_W controls
                rw:    std_logic;
                bw:    std_logic;       -- Byte-word control (mem write only)
                i:     inst_type;       -- operation specification
				rst_md:std_logic;       -- multiplier and divider reset signal
        end record;
end p_MIPS_S;

--++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-- (2) A 32-bit register
--++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
library IEEE;
use IEEE.std_logic_1164.all;

entity reg32bits is
           generic( INIT_VALUE : STD_LOGIC_VECTOR(31 downto 0) := (others=>'0'));
           port( ck, rst, ce : in std_logic;
				D : in  STD_LOGIC_VECTOR (31 downto 0);
                Q : out STD_LOGIC_VECTOR (31 downto 0)
               );
end reg32bits;

architecture reg32bits of reg32bits is 
begin
  process(ck, rst)
  begin
       if rst = '1' then
              Q <= INIT_VALUE(31 downto 0);
       elsif ck'event and ck = '1' then
           if ce = '1' then
              Q <= D; 
           end if;
       end if;
  end process;      
end reg32bits;

--++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-- (3) The Register Bank (R0 to R31) - 31 general-purpose 32-bit regs and 
--++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
library IEEE;
use IEEE.Std_Logic_1164.all;
use ieee.STD_LOGIC_UNSIGNED.all;   
use work.p_MIPS_S.all;   
      
library UNISIM;
use UNISIM.vcomponents.all; 

entity reg_bank is
       port( ck, rst, ce        :    in std_logic;
             AdRP1, AdRP2, AdWP : in std_logic_vector(4 downto 0);
             DataWP 			: in std_logic_vector(31 downto 0);
             DataRP1, DataRP2	: out std_logic_vector(31 downto 0) 
           );
end reg_bank;

architecture reg_bank of reg_bank is
   type wirebank is array(0 to 31) of std_logic_vector(31 downto 0);
   signal reg : wirebank ;                            
   signal wen : std_logic_vector(31 downto 0) ;
begin            
    g1: for i in 0 to 31 generate        

        -- Lembrar que o registrador $0 ($zero) é a constante 0, não um registrador.
        -- Ele é descrito como um registrador cujo ce nunca é ativado
		-- O sinal wen é o vetor sinais de controle de habilitação de escrita em cada
		-- um dos 32 registradores: wen(0) é a habilitação de escrita no registrador 0
		-- (como dito acima, wen(0)='0', sempre. Os demais dependem do valor de AdWP
		-- o do ce global.
        wen(i) <= '1' when i/=0 and AdWP=i and ce='1' else '0';
         
        -- Lembrar que o registrador $29, por convenção de software é o apontador de
		-- pilha. Ele aponta inicialmente para um lugar diferente do valor usado no MARS.
		-- A pilha aqui é pensada como usando a parte final da memória de dados
        g2: if i=29 generate -- SP ---  x10010000 + x800 -- local do topo da pilha
           r29: entity work.reg32bits generic map(INIT_VALUE=>x"10010800")    
                port map(ck=>ck, rst=>rst, ce=>wen(i), D=>DataWP, Q=>reg(i));
        end generate;  
                
        g3: if i/=29 generate 
           rx: entity work.reg32bits 
					port map(ck=>ck, rst=>rst, ce=>wen(i), D=>DataWP, Q=>reg(i));                    
        end generate;
                   
   end generate g1;   
    

    DataRP1 <= reg(CONV_INTEGER(AdRP1));    -- seleção do fonte 1 

    DataRP2 <= reg(CONV_INTEGER(AdRP2));    -- seleção do fonte 2 

end reg_bank;

--++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-- (4) The ALU - its operation is defined by the current instruction 
--       alone (as decoded by the control unit)
--++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.std_logic_unsigned.all;
use IEEE.std_logic_arith.all;
use work.p_MIPS_S.all;

entity alu is
       port( op1, op2 : in std_logic_vector(31 downto 0);
             outalu :   out std_logic_vector(31 downto 0);   
             op_alu : in inst_type   
           );
end alu;

architecture alu of alu is 
   signal menorU, menorS : std_logic ;
begin
    menorU <=  '1' when op1 < op2 else '0';
    menorS <=  '1' when ieee.Std_Logic_signed."<"(op1,  op2) else '0' ; -- signed
	
    outalu <=  op1 - op2                                                                when  op_alu=SUBU                  else
               op1 and op2                                                              when  op_alu=AAND  or op_alu=ANDI  else 
               op1 or  op2                                                              when  op_alu=OOR   or op_alu=ORI   else 
               op1 xor op2                                                              when  op_alu=XXOR  or op_alu=XORI  else 
               op1 nor op2                                                              when  op_alu=NNOR                  else 
               op2(15 downto 0) & x"0000"                                               when  op_alu=LUI                   else
               (0=>menorU, others=>'0')                                                 when  op_alu=SLTU or op_alu=SLTIU  else   -- signed
               (0=>menorS, others=>'0')                                                 when  op_alu=SLT  or op_alu=SLTI   else   -- unsigned
               op1(31 downto 28) & op2(27 downto 0)                                     when  op_alu=J    or op_alu=JAL    else 
               op1                                                                      when  op_alu=JR   or op_alu=JALR   else 
               to_StdLogicVector(to_bitvector(op1) sll CONV_INTEGER(op2(10 downto 6)))  when  op_alu=SSLL                  else 
               to_StdLogicVector(to_bitvector(op2) sll CONV_INTEGER(op1(5 downto 0)))   when  op_alu=SLLV                  else 
               to_StdLogicVector(to_bitvector(op1) sra CONV_INTEGER(op2(10 downto 6)))  when  op_alu=SSRA                  else 
               to_StdLogicVector(to_bitvector(op2) sra CONV_INTEGER(op1(5 downto 0)))   when  op_alu=SRAV                  else 
               to_StdLogicVector(to_bitvector(op1) srl CONV_INTEGER(op2(10 downto 6)))  when  op_alu=SSRL                  else 
               to_StdLogicVector(to_bitvector(op2) srl CONV_INTEGER(op1(5 downto 0)))   when  op_alu=SRLV                  else 
               op1 + op2;       -- default for ADDU,ADDIU,LBU,LW,SW,SB,BEQ,BGEZ,BLEZ,BNE    
end alu;

--++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-- (5) The Multiplier - This is an implementation of a N-bit serial
--		multiplier as proposed in Hennessy & Patterson textbook
--		- The implementation waits for a 1 clock cycle initialization 
--		input control signal, then it computes the multiplication in
--		the next 65 clock cycles, in the last of these generating a
--		1 clock cycle output signal (end_mul) to indicate
--		the end of the multiplication, which result is then 
--		available at the data outputs.
--++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
library IEEE;
use IEEE.Std_Logic_1164.all;
use IEEE.Std_Logic_unsigned.all;

entity multiplier is
	generic(N: integer := 32);
	port( clock,start:	in  std_logic; 
		Mcand:				in  std_logic_vector((N-1) downto 0);
		Mplier:				in  std_logic_vector((N-1) downto 0);
		endop:				out std_logic;
		product:			out std_logic_vector(2*N-1 downto 0));
end;

architecture multiplier of multiplier is   
	type State_type is (initialize, shift, add, finish, endm);
	signal EA: State_type;
	signal regP :	std_logic_vector( N*2 downto 0); 
	signal regB :	std_logic_vector( N   downto 0);
	signal count:	integer;

begin
	--
	-- Registers: regP, regB, product
	-- endop flip-flop and counter to control execution
	--
	process(start, clock)
	begin
		if (start='0' and EA=initialize) then 
			-- Initialize registers when leaving the state,
			-- 	not on state entry
			regP( N*2 downto N)	<= (others=>'0');
			regP( N-1 downto 0) 	<= Mplier;         
			regB					<= '0' & Mcand;
			count					<= 1;
			endop					<= '0';
			product 				<= (others=>'0');
		elsif clock'event and clock='1' then
			if EA=add and regP(0)='1' then
				regP(N*2 downto N) <= regP(N*2 downto N) + regB;
			elsif EA=shift then
				regP <= '0' & regP(N*2 downto 1);
				count <= count + 1;
			elsif EA=finish then
				product	<= regP( N*2-1 downto 0);
				endop	<= '1';
			elsif EA=endm then
				endop <= '0';
			end if;
		end if;       
	end process;

	-- State machine to control the multiplication process
	process (start, clock)
	begin
		if start='1' then
			EA <= initialize;
		elsif clock'event and clock='1' then  
			case EA is
				when initialize	=>   EA <= add;   
				when add		=>  EA <= shift;
				when shift		=>  if count=N then 
										EA <= finish; 
									else 
										EA <= add;
									end if;             
				-- finish state serves only to generate a single pulse on endop  
				when finish		=>	EA <= endm;
				when endm		=>	EA <= endm;    
			end case;
		end if;
	end process;

end multiplier;

--++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-- (6) The Divider - This is an implementation of a 32-bit serial restoring
--		divider as proposed in Hennessy & Patterson textbook
--		- The implementation waits for a 1 clock cycle initialization 
--		input control signal, then it computes the division in
--		the next 65 clock cycles, in the last of these generating a
--		1 clock cycle output signal (end_div) to indicate
--		the end of the division, which result is then 
--		available at the data outputs. The algorithm uses 
--		restoration to produce the final division remainder.
--++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
library IEEE;
use IEEE.Std_Logic_1164.all;
use IEEE.Std_Logic_unsigned.all;

entity divider is
	generic(N: integer := 32);
	port( clock,start :		in  std_logic;
		dividend:			in  std_logic_vector( (N-1) downto 0);
		divisor:			in  std_logic_vector( (N-1) downto 0); 
		endop :				out std_logic;
		quotient :			out std_logic_vector( N-1 downto 0);
		remainder :			out std_logic_vector( N-1 downto 0));
end;

architecture divider of divider is   
	type State_type is (initialize, shift, addsub, finish, endd);
	signal EA :			State_type;
	signal regP :		std_logic_vector(N*2 downto 0); 
	signal regB :		std_logic_vector(N   downto 0);
	signal difference :	std_logic_vector(N  downto 0);
	signal count:		integer;

begin
	difference <=  regP( N*2 downto N) -  regB( N downto 0);   

	process(start, clock)
	begin    
		if (start='0' and EA=initialize) then
			-- Inicializa-se os registradores na saída do estado
			-- e não na entrada
			regP(N*2 downto N)	<= (others=>'0');
			regP(N-1 downto 0)	<= dividend;
			regB				<= '0' & divisor;
			count				<= 1;
			endop				<= '0';
			remainder			<= (others=>'0');
			quotient			<= (others=>'0');
		elsif clock'event and clock='1' then
			if EA=shift then
				regP <= regP(N*2-1 downto 0) & regP(N*2);
			elsif EA=addsub then  
				if difference(N)='1' then  
					regP(0)<='0';
				else
					regP(0)<='1';
					regP(N*2 downto N) <= difference;
				end if;
				count <= count + 1;
			elsif EA=finish then
				remainder	<= regP(N*2-1 downto N);
				quotient 	<= regP(N-1   downto 0);
				endop		<= '1';
			elsif EA=endd then
					endop <= '0';
			end if;
		end if;
	end process;
	
	-------------------------------------------------
	-- Máquina de Estados para controlar a divisão
	-------------------------------------------------
	process (start, clock)
		begin
			if start='1'then
				EA <= initialize;
			elsif clock'event and clock='1' then  
				case EA is
					when initialize => EA <= shift; 
					when shift		=> EA <= addsub;
					when addsub		=> if count=N then 
											EA <= finish; 
										else 
											EA <= shift;  
										end if;
					-- estado termina só serve para gerar o pulso em endop  
					when finish		=> EA <= endd;
					when endd		=> EA <= endd;
				end case; 
			end if;
	end process;

end divider;

--++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-- (7) The MIPS_S Datapath - Instantiates above modules (1) to (6)
--++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
library IEEE;
use IEEE.Std_Logic_1164.all;
use IEEE.Std_Logic_signed.all; -- needed for comparison instructions SLTxx
use IEEE.Std_Logic_arith.all; -- needed for comparison instructions SLTxU
use work.p_MIPS_S.all;

entity datapath is
      port( ck, rst :     					in std_logic;
			d_address :   					out std_logic_vector(31 downto 0);
	-- Differently from the simulatable MIPS_S data input and output here are two
	-- unidirectional buses - data_in and data_out
            data_in :     					in std_logic_vector(31 downto 0);  
			data_out :    					out std_logic_vector(31 downto 0);
            uins :        					in microinstruction;
			IR_IN :  						in std_logic_vector(31 downto 0);
			NPC_IN : 						in std_logic_vector(31 downto 0);
			end_mul, end_div :	   			out std_logic;
			inst_branch_out, salta_out :	out std_logic;
			RESULT_OUT :  					out std_logic_vector(31 downto 0)
          );
end datapath;

architecture datapath of datapath is
    signal result, R1, R2, R3, R1_in, R2_in, R3_in, RIN, sign_extend, op1, op2,
		   outalu, RALU, MDR, HI, LO,
		   quotient, remainder, D_Hi, D_Lo : std_logic_vector(31 downto 0) := (others=> '0'); 
	-- Remember, initialization of these signals to all 0's is only valid for simulation...
    signal adD, adS : std_logic_vector(4 downto 0);
    signal inst_branch, inst_R_sub, inst_I_sub, rst_muldiv : std_logic;
    signal salta : std_logic:= '0';
    signal product : std_logic_vector(63 downto 0);
begin
	-- auxiliary signals 
	-- inst_branch is a wire that identifies when an instruction under execution is
	-- conditional branch
	inst_branch  <= '1' when uins.i=BEQ or uins.i=BGEZ or uins.i=BLEZ  or uins.i=BNE else 
                    '0';
	inst_branch_out <= inst_branch;
	-- inst_R_sub is a wire that identifies a subset of R-type instructions (logic and arithmetic)
	inst_R_sub  <= '1' when uins.i=ADDU or uins.i=SUBU or uins.i=AAND or uins.i=OOR or uins.i=XXOR or uins.i=NNOR else
                     '0';
	-- inst_I_sub is a subset of I-type instructions (logic and arithmetic)
	inst_I_sub  <= '1' when uins.i=ADDIU or uins.i=ANDI or uins.i=ORI or uins.i=XORI else
                     '0';

   --==============================================================================
   -- second stage
   --==============================================================================
   -- mux to select source register Rt for logic shifts with shamt field Rs otherwise       
	M3: adS <=	IR_IN(20 downto 16) when uins.i=SSLL or uins.i=SSRA or uins.i=SSRL else 
				IR_IN(25 downto 21);

	-- Here goes the instance (REGS) of the register bank and its connections
	REGS: entity work.reg_bank port map
            (AdRP1=>adS, DataRP1=>R1_in, AdRP2=>IR_IN(20 downto 16), 
			DataRP2=>R2_in, ck=>ck, rst=>rst, ce=>uins.wreg, AdWP=>adD, DataWP=>RIN);
			
	-- sign extension hardware
	sign_extend <=  x"FFFF" & IR_IN(15 downto 0) when IR_IN(15)='1' else
             x"0000" & IR_IN(15 downto 0);

	-- Computation of the immediate constant - 0-extension, sign-extension, sign-extension times 4, 
	-- special extension and the M5 mux
	M5: R3_in <= sign_extend(29 downto 0)  & "00"     when inst_branch='1'     else  
			-- branch address adjustment for word frontier
			"0000" & IR_IN(25 downto 0) & "00" when uins.i=J or uins.i=JAL else
			-- J/JAL are word addressed. MSB four bits are defined at the ALU, not here!
             x"0000" & IR_IN(15 downto 0) when uins.i=ANDI or uins.i=ORI  or uins.i=XORI else
			 -- logic instructions with immediate operand are zero extended
             sign_extend;
			 -- The default case is used by addiu, lbu, lw, sbu and sw instructions
			 
	-- second stage registers
	R1reg: entity work.reg32bits  port map(ck => ck, rst => rst, ce => uins.CY2, D => R1_in, Q => R1);
	
	R2reg: entity work.reg32bits  port map(ck => ck, rst => rst, ce => uins.CY2, D => R2_in, Q => R2);
	
	R3reg: entity work.reg32bits  port map(ck => ck, rst => rst, ce => uins.CY2, D => R3_in, Q => R3);
	
	--==============================================================================
	-- third stage
	--==============================================================================
	-- mux to select the first ALU operand                           
	-- op1 <= pc  when (inst_branch='1' or uins.i=J or uins.i=JAL) else R1
		-- use the above code for programs generated with the PCSPIM simulator 
		-- but this needs a change in the datapath/control unit interface...
	-- op1 <= NPC_IN  when (inst_branch='1' or uins.i=J or uins.i=JAL) else R1 
		-- use the above code for programs generated with the MARS simulator   
	M6: op1 <= NPC_IN  when (inst_branch='1' or uins.i=J or uins.i=JAL) else R1;
	
	-- mux to select the second ALU operand
	-- R2 for R-type instructions or SLT/SLTU or JR or shifts with no shamt field
	M7: op2 <= 	R2 when inst_R_sub='1' or uins.i=SLTU or uins.i=SLT or uins.i=JR 
                  or uins.i=SLLV or uins.i=SRAV or uins.i=SRLV else
		R3;
		
	-- ALU instantiation
	DALU: entity work.alu port map (op1 => op1, op2 => op2, outalu => outalu, op_alu => uins.i);
	
	-- ALU register
	ALUreg: entity work.reg32bits   port map(ck => ck, rst => rst, ce => uins.walu,
			D => outalu, Q => RALU);

	-- The comparator, which evaluates the branch iunstructions conditions
	salta <=	'1' when ( (R1=R2  and uins.i=BEQ)  or (R1>=0  and uins.i=BGEZ) or
						(R1<=0  and uins.i=BLEZ) or (R1/=R2 and uins.i=BNE)
						)  else
				'0';
	salta_out <= salta;
	
	-- The multiplier and divider reset; The two are reset by both, 
	-- 	the global reset (rst) and by the rst_md signal, produced by the control unit
	-- finite state machine, during the second cycle of multu/divu
	rst_muldiv <= rst or uins.rst_md; 

	-- instantiation of the multiplier and of the divider
	inst_mult: entity work.multiplier port map (clock=>ck, start=>rst_muldiv,
		Mcand=>R1_in, Mplier=>R2_in,  endop=>end_mul, product=>product);
	  
	inst_div: entity work.divider generic map (32) port map (clock=>ck, start=>rst_muldiv,
	dividend=>R1_in, divisor=>R2_in, endop=>end_div, quotient=>quotient, remainder=>remainder);

	M10: D_Hi <= product(63 downto 32) when uins.i=MULTU else remainder; 
	
	M11: D_Lo <= product(31 downto 0) when uins.i=MULTU else quotient; 

	-- The HI and LO registers
	HIreg: entity work.reg32bits  port map(ck=>ck, rst=>rst, ce=>uins.whilo, 
			D=>D_Hi, Q=>HI);               
	LOreg: entity work.reg32bits  port map(ck=>ck, rst=>rst, ce=>uins.whilo, 
			D=>D_Lo, Q=>LO);               

   --==============================================================================
   -- fourth stage
   --==============================================================================
   d_address <= RALU;
   
   data_out <= R2;  

   -- The M8 mux chooses betwee passing 32 bits from memory to the inside of MIPS_S
   -- or passing just the least significant byte, with a 0-extension of 24 bits. 
   -- Here, as everywhere a little endian processor is assumed 
   M8: MDR <= data_in when uins.i=LW  else
              x"000000" & data_in(7 downto 0);
    --  25/10/2022 - To solve the synchronous memory problem we eliminate
    --      the MDR register changing it by 32 wires     
  
   M9: result <=	MDR when uins.i=LW  or uins.i=LBU else
                    HI when uins.i=MFHI else
                    LO when uins.i=MFLO else
                    RALU;
									   
	--==============================================================================
	-- fifth stage
	--==============================================================================
	-- M2 escolhe o sinal com o dado a ser escrito no banco de registradores
	M2: RIN <= NPC_IN when (uins.i=JALR or uins.i=JAL) else result;
   
	-- M4 seleciona o endereço de escrita no banco de registradores 
	M4: adD <= "11111" when uins.i=JAL else -- JAL escreve sempre no registrador $31
			IR_IN(15 downto 11) when (inst_R_sub='1' 
					or uins.i=SLTU or uins.i=SLT
					or uins.i=JALR
					or uins.i=MFHI or uins.i=MFLO
					or uins.i=SSLL or uins.i=SLLV
					or uins.i=SSRA or uins.i=SRAV
					or uins.i=SSRL or uins.i=SRLV) else
			IR_IN(20 downto 16) 	-- inst_I_sub='1' ou uins.i=SLTIU ou uins.i=SLTI 
			;	-- ou uins.i=LW ou uins.i=LBU ou uins.i=LUI, ou "default"
    
  RESULT_OUT <= result;
	 
end datapath;

--++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-- (8) The MIPS_S Control Unit
--++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
library IEEE;
use IEEE.Std_Logic_1164.all;
use IEEE.Std_Logic_unsigned.all;
use work.p_MIPS_S.all;

entity control_unit is
        port(	ck, rst : 					in std_logic;
                hold_in :                   in std_logic;
                hold_out :                  out std_logic;
				i_address : 				out std_logic_vector(31 downto 0);
				instruction : 				in std_logic_vector(31 downto 0);
				IR_OUT : 					out std_logic_vector(31 downto 0);
				uins : 						out microinstruction;				
				NPC_OUT : 					out std_logic_vector(31 downto 0);
				inst_branch_in, salta_in :	in std_logic;
				end_mul, end_div : 			in std_logic;
				RESULT_IN : 				in std_logic_vector(31 downto 0)
            );
end control_unit;
                   
architecture control_unit of control_unit is
	type type_state is (Sfetch, Shold, Sreg, Salu, Swbk, Sld, Sst, Ssalta); -- 8 states only
	signal PS, NS : type_state;
	signal i : inst_type;
	signal uins_int : microinstruction;
	signal dtpc, NPC, pc, incpc, IR  : std_logic_vector(31 downto 0);  
begin
    ----------------------------------------------------------------------------------------
    -- BLOCK 1 of 4 - The control registers of MIPS_S.
	-- Instruction fetch and PC increment
    ----------------------------------------------------------------------------------------
	-- Mux M1 selects the input to the PC register 
	M1: dtpc <=	RESULT_IN when (inst_branch_in='1' and salta_in='1') or uins_int.i=J
   			or uins_int.i=JAL or uins_int.i=JALR or uins_int.i=JR	else
   		NPC;
   
	NPC_OUT <= NPC;
	
	-- Starting address where program is stored. Beware of this value!
	-- The value below (x"00400000") serves for code generated bu the MARS simulator
	PC_reg: entity work.reg32bits generic map(INIT_VALUE=>x"00400000")   
		port map(ck=>ck, rst=>rst, ce=>uins_int.wpc, D=>dtpc, Q=>pc);

	incpc <= pc + 4;
  
	NPCreg: entity work.reg32bits 
		 port map(ck=>ck, rst=>rst, ce=>uins_int.CY1, D=>incpc, Q=>NPC);     
           
--  This is a test to solve the synchronous memory problem.
--      I will eliminate the IR register changing it by 32 wires     
--	IR_reg:	entity work.reg32bits  
--		port map(ck=>ck, rst=>rst, ce=>uins_int.CY1, D=>instruction, Q=>IR);
    IR <= instruction;

	IR_OUT <= IR ;    	-- IR is the instruction register 
             
	i_address <= pc;	-- connects the PC output to the instruction memory address wires

    ----------------------------------------------------------------------------------------
    -- BLOCK 2 of 4 - INSTRUCTION DECODING and ALU operation definition.
    -- This block generates one of the control unit outputs, i.
    ----------------------------------------------------------------------------------------
    i <=   ADDU   when IR(31 downto 26)="000000" and IR(10 downto 0)="00000100001" else
           SUBU   when IR(31 downto 26)="000000" and IR(10 downto 0)="00000100011" else
           AAND   when IR(31 downto 26)="000000" and IR(10 downto 0)="00000100100" else
           OOR    when IR(31 downto 26)="000000" and IR(10 downto 0)="00000100101" else
           XXOR   when IR(31 downto 26)="000000" and IR(10 downto 0)="00000100110" else
           NNOR   when IR(31 downto 26)="000000" and IR(10 downto 0)="00000100111" else
           SSLL   when IR(31 downto 21)="00000000000" and IR(5 downto 0)="000000" else
           SLLV   when IR(31 downto 26)="000000" and IR(10 downto 0)="00000000100" else
           SSRA   when IR(31 downto 21)="00000000000" and IR(5 downto 0)="000011" else
           SRAV   when IR(31 downto 26)="000000" and IR(10 downto 0)="00000000111" else
           SSRL   when IR(31 downto 21)="00000000000" and IR(5 downto 0)="000010" else
           SRLV   when IR(31 downto 26)="000000" and IR(10 downto 0)="00000000110" else
           ADDIU  when IR(31 downto 26)="001001" else
           ANDI   when IR(31 downto 26)="001100" else
           ORI    when IR(31 downto 26)="001101" else
           XORI   when IR(31 downto 26)="001110" else
           LUI    when IR(31 downto 26)="001111" else
           LW     when IR(31 downto 26)="100011" else
           LBU    when IR(31 downto 26)="100100" else
           SW     when IR(31 downto 26)="101011" else
           SB     when IR(31 downto 26)="101000" else
           SLTU   when IR(31 downto 26)="000000" and IR(5 downto 0)="101011" else
           SLT    when IR(31 downto 26)="000000" and IR(5 downto 0)="101010" else
           SLTIU  when IR(31 downto 26)="001011"                             else
           SLTI   when IR(31 downto 26)="001010"                             else
           BEQ    when IR(31 downto 26)="000100" else
           BGEZ   when IR(31 downto 26)="000001" and IR(20 downto 16)="00001" else
           BLEZ   when IR(31 downto 26)="000110" and IR(20 downto 16)="00000" else
           BNE    when IR(31 downto 26)="000101" else
           J      when IR(31 downto 26)="000010" else
           JAL    when IR(31 downto 26)="000011" else
           JALR   when IR(31 downto 26)="000000"  and IR(20 downto 16)="00000"
                                           and IR(10 downto 0) = "00000001001" else
           JR     when IR(31 downto 26)="000000" and IR(20 downto 0)="000000000000000001000" else
           MULTU  when IR(31 downto 26)="000000" and IR(15 downto 0)="0000000000011001" else
           DIVU   when IR(31 downto 26)="000000" and IR(15 downto 0)="0000000000011011" else
           MFHI   when IR(31 downto 16)=x"0000" and IR(10 downto 0)="00000010000" else
           MFLO   when IR(31 downto 16)=x"0000" and IR(10 downto 0)="00000010010" else
           invalid_instruction ; -- IMPORTANT: the final else condition is for invalid instructions
        
    assert i /= invalid_instruction
          report "******************* INVALID INSTRUCTION *************"
          severity error;
                   
    uins_int.i <= i;    -- This signal instructs the ALU to execute its expected operation, if any applies
						-- Also, it controls the operation of pratically all muxes, which define
						--		the paths to use in the datapath and in the control unit

    --------------------------------------------------------------------------------------------
    -- BLOCK 3 of 4 - Two parts of the control unit finite state machine - two processes 
	--	implementing the FSM state register and its next-state function computation - combinational
	--------------------------------------------------------------------------------------------
    process(rst, ck)
    begin
       if rst='1' then
            PS <= Sfetch;
        -- Sfetch is the state the machine stays while processor is being reset
       elsif ck'event and ck='1' then
			PS <= NS;
       end if;
    end process;
     
    process(PS, i, end_mul, end_div, hold_in)
        -- NEXT state: depends on the PRESENT state, on the current instruction
        -- and on the end_mul signal, that unlocks the control unit attribute the end of a
        -- multiplication operation
        --  25/10/2022 - Important, hold_in need to be added to the process sensitivity list!!! 
    begin
       case PS is         
            -- first stage:  input the current instruction and increment PC to NPC
            when Sfetch => if hold_in='1' then
                                NS <= Shold;
                           else
                                NS <= Sreg;
                           end if;  
            -- hold:  Special state where the processor is kept without doing anything
            when Shold => if hold_in='1' then
                                NS <= Shold;
                          else
                                NS <= Sfetch;
                          end if;                             
            --
            -- second stage: read the register bank and generate the immediate datum
            when Sreg => NS <= Salu;  
            --
            -- third stage: alu operation enter multu or subu, do comparisons for branches
            when Salu  =>	if (i=LBU or i=LW) then 
									NS <= Sld;  
								elsif (i=SB or i=SW) then 
									NS <= Sst;
								elsif (i=J or i=JAL or i=JALR or i=JR or i=BEQ
                               or i=BGEZ or i=BLEZ  or i=BNE) then 
									NS <= Ssalta;
								elsif ((i=MULTU and end_mul='0') or (i=DIVU and end_div='0')) then
									NS <= Salu;
								elsif ((i=MULTU and end_mul='1') or (i=DIVU and end_div='1')) then
									NS <= Sfetch;
								else 
									NS <= Swbk; 
							end if;
            --                  
            -- fourth stage: data memory operation (read or write - not all instructions pass here!)
            when Sld =>   NS <= Swbk; 
            --
            -- fourth or fifth stage: last state for most instructions write reg_bank, update PC &
			-- go back to Sfetch
            when Sst | Ssalta | Swbk => NS <= Sfetch;
       end case;
    end process;

    ----------------------------------------------------------------------------------------
    -- BLOCK 4 of 4 - Registers load control and memory control signals generation
	--		This corresponds to the finite state machine output function computation
	--	This block produces 11 control signals - most of these are write enable
	--		for registers in the datapath and in the control unit
    -- 	The block also generates control signals to access the data memory and the 
	--		signal to initialize the multiplication and divdider blocks
	--	In all there are 11 Boolean functions, one for each control signal
	-- 25/10/2022 - Change to include the hold_out status output
    ----------------------------------------------------------------------------------------
    hold_out        <= '1' when PS=Shold else '0';
    
    uins_int.CY1    <= '1' when (PS=Sfetch and hold_in='0') else '0';
                        -- 25/10/2022 change. Only writes to NPC if not going to the Shold state
    
    uins_int.CY2    <= '1' when PS=Sreg else '0';
    
    uins_int.walu   <= '1' when PS=Salu else '0';
   
    uins_int.wreg   <= '1' when PS=Swbk or (PS=Ssalta and (i=JAL or i=JALR)) else '0';
    
    uins_int.whilo  <= '1' when (PS=Salu and end_mul='1' and i=MULTU) or
                                (PS=Salu and end_div='1' and i=DIVU) else  '0';
    
    uins_int.rw     <= '0' when PS=Sst else '1'; 
    
    uins_int.ce    <= '1' when PS=Sld or PS=Sst else '0';
    
    uins_int.bw    <= '0' when (PS=Sst and i=SB)  else '1';
    
    uins_int.wpc   <= '1' when PS=Swbk or PS=Sst or PS=Ssalta 
                           or (PS=Salu and ((i=DIVU and end_div='1')
                           					or (i=MULTU and end_mul='1'))) else '0';

    uins_int.rst_md   <= '1' when PS=Sreg and (i=MULTU or i=DIVU) else  '0';

    uins <= uins_int;

end control_unit;

--++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
--++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-- (9) The top-level instantiation of the MIPS_S, joining
--      (7) the MIPS_S datapath -> datapath
--      (8) the MIPS_S control unit -> control_unit
--++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
--++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
library IEEE;
use IEEE.Std_Logic_1164.all;
use work.p_MIPS_S.all;
entity MIPS_S is
    port( clock, reset: in std_logic;
        hold_in:        in std_logic;
        hold_out:       out std_logic;
        ce, rw, bw: 	out std_logic;
        i_address: 		out std_logic_vector(31 downto 0);
        instruction: 	in std_logic_vector(31 downto 0);
        d_address: 		out std_logic_vector(31 downto 0);
        data_in: 		in std_logic_vector(31 downto 0);
		data_out: 		out std_logic_vector(31 downto 0)
		);
end MIPS_S;

architecture MIPS_S of MIPS_S is
	signal IR, NPC, RESULT : std_logic_vector(31 downto 0);
	signal uins : microinstruction;  
	signal inst_branch, salta, end_mul, end_div : std_logic;
 begin
	ct: entity work.control_unit port map( ck=>clock, rst=>reset,
	    hold_in=>hold_in, hold_out=>hold_out,
		i_address=>i_address, instruction=>instruction,
		IR_OUT=>IR, uins=>uins, NPC_OUT=>NPC,
		inst_branch_in=>inst_branch, salta_in=>salta,
		end_mul=>end_mul, end_div=>end_div,
		RESULT_IN=>RESULT
		);

	dp: entity work.datapath   
		port map( ck=>clock, rst=>reset, 
		d_address=>d_address, data_in=>data_in, data_out=>data_out,
		uins=>uins, IR_IN=>IR, NPC_IN=>NPC,
		end_mul=>end_mul, end_div=>end_div, 
		inst_branch_out=>inst_branch, salta_out=>salta,
		RESULT_OUT=>RESULT		
		);
				
     ce <= uins.ce;
     rw <= uins.rw; 
     bw <= uins.bw;
	 
end MIPS_S;

--++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
--++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-- (10) Top-level instantiation of the MIPS_S_withBRAMs module, joining
--  module (9), the processor with the program memory and the data memory
--  as described in the memory.vhd file. 
--++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
--++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
library IEEE;
use IEEE.Std_Logic_1164.all;
use IEEE.Std_Logic_unsigned.all;
use work.p_MIPS_S.all;

entity MIPS_S_withBRAMs is
    port(clock, reset, selUSER:     in std_logic;
        hold_in :                   in std_logic;   -- 25/10/2022 - new signal
        hold_out :                  out std_logic;  -- 25/10/2022 - new signal
		resetCPU : out std_logic;
        -- External USER data memory control interface
        addressUSER: in std_logic_vector(31 downto 0);
        ceUSER, rwUSER, bwUSER:     in std_logic;
        dataUSER: in std_logic_vector(31 downto 0);
        -- data_outRAM is shared between the external user (output to USER)
        --  and the processor (it is an input to the CPU)
        data_outRAM: out std_logic_vector(31 downto 0);
        -- Internal CPU data memory control interface (exposed to external USER)
		-- 	and the processor reset status
		ceCPU, rwCPU, bwCPU : out std_logic;
		addressCPU: out std_logic_vector(31 downto 0);
        data_outCPU: out std_logic_vector(31 downto 0)
        );
end MIPS_S_withBRAMs;

architecture MIPS_S_withBRAMs of MIPS_S_withBRAMs is
      signal IR: std_logic_vector(31 downto 0);
      signal uins: microinstruction;
      signal ceCPU_int, ceRAM, rwCPU_int, rwRAM, bwCPU_int, bwRAM, rstCPU: std_logic;
      signal int_address : std_logic_vector(8 downto 0); -- 9-bit addressable : 512 words --
      signal i_address, instruction, addressCPU_int, addressRAM, 
        data_out_CPU, data_out_RAM, data_in_RAM : std_logic_vector(31 downto 0);
 begin
	 addressCPU <= addressCPU_int;
	 ceCPU <= ceCPU_int; 
	 rwCPU <= rwCPU_int;
	 bwCPU <= bwCPU_int;
	 resetCPU <= rstCPU; 
	 
     rstCPU <= reset; -- 25/10/2022 SelUSER no longer resets the MIPS_S
	 
	 -- the MIPS_S processor instantiation
     I_MIPS_S: entity work.MIPS_S
    		port map(clock=>clock, reset=>rstCPU,
    		hold_in=>hold_in, hold_out=>hold_out,
          	ce=>ceCPU_int, rw=>rwCPU_int, bw=>bwCPU_int, 
			i_address=>i_address, instruction=>instruction,
			d_address=>addressCPU_int, 
			data_out=>data_out_CPU, data_in=>data_out_RAM);
			
     -- Instantiation of the program memory
        -- use the line below when running programs generated with PCSpim
            -- int_address <=  i_address(10 downto 2)-8;  -- subtract 0x20 displacement       
        -- use the line below when running programs generated with Mars
            -- int_address <=  i_address(10 downto 2); 
     int_address <=  i_address(10 downto 2);
	 
     P_mem: entity work.program_memory
        port map(clock=>clock, address=>int_address, instruction=>instruction);
		
     -- A set of 5 2-to-1 muxes to select who controls the data memory                      
     addressRAM  <= addressCPU_int when selUSER='0' else  addressUSER; -- data address
     data_in_RAM <= data_out_CPU   when selUSER='0' else  dataUSER;    -- data contents
     ceRAM <= ceCPU_int when selUSER='0' else ceUSER; -- chip enable
     rwRAM <= rwCPU_int when selUSER='0' else rwUSER; -- read/write
     bwRAM <= bwCPU_int when selUSER='0' else bwUSER; -- byte write

     -- Instantiation of the data memory 
     D_mem: entity work.data_memory
		port map (clock=>clock, ce=>ceRAM, we=>rwRAM, bw=>bwRAM, 
		address=>addressRAM(12 downto 2), data_in=>data_in_RAM, data_out=>data_out_RAM);
 
     -- Other externally viewed signals
     data_outRAM <= data_out_RAM; -- what comes from memory 
     data_outCPU <= data_out_CPU; -- what comes from the processor
	 
end MIPS_S_withBRAMs;
