Lab 2 : Description d'un microcontrôleur élémentaire mu0

Lab 2 : Description d'un microcontrôleur élémentaire mu0

Sources à Compléter

mu0.zip

1 - Présentation

Rappel sur le fonctionnement de mu0 [.ppt]

mu0_mem.svg

jeu_instruction.svg


2 - Description des Composants

Multiplexeur

mux.svg

library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;

entity mux_A is 
  port( sel : in std_logic;
        e_0, e_1 : in std_logic_vector(11 downto 0);
        s : out std_logic_vector( 11 downto 0));
end mux_A;

architecture arch_mux_A of mux_A is
  begin
    
--***************************************
--            A COMPLETER              --
--***************************************
    
end arch_mux_A;    
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;

entity mux_B is 
  port( sel : in std_logic;
        e_0, e_1 : in std_logic_vector(15 downto 0);
        s : out std_logic_vector( 15 downto 0));
end mux_B;

architecture arch_mux_B of mux_B is
  begin
    
--***************************************
--            A COMPLETER              --
--***************************************
    
end arch_mux_B;    

Un multiplexeur est un composant combinatoire permettant d’aiguiller une information.
On utilisera pour la description VHDL soit :

  • l’affectation conditionnelle ( s <= a when choix=’0’ else b),
  • un process combinatoire, à condition de mettre dans la liste de sensibilité du process toutes les entrées du composant.

muxA et muxB répondent à la même description, seuls les tailles des vecteurs d’entrée et de sortie diffèrent (12 pour muxA, 16 pour muxB) La notion de généricité peut être utilisée dans ce cas.

Porte 3 états

porte_3e.svg

library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;

entity tristate is
  port( oe : in std_logic;
        data_in  : in std_logic_vector( 15 downto 0);
        data_out : inout std_logic_vector(15 downto 0));
end tristate;

architecture arch_tristate of tristate is
begin

--***************************************
--            A COMPLETER              --
--***************************************

end arch_tristate;  
 

Une porte 3 états est un composant combinatoire permettant de contrôler le forçage des niveaux logiques d’un bus.
Dans notre cas, si l’entrée oe est à ‘1’, alors l’entrée data_in sera vue sur la sortie data_out ; sinon la sortie sera à l’état haute impédance (‘Z’).

Unité Arithmétique et Logique

ual.svg

library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
use work.up_pack.all;

entity alu is 
  port ( A, B : in std_logic_vector(15 downto 0);
          alufs : in ALU_FCTS;
          S : out std_logic_vector( 15 downto 0));
end alu;

architecture arch_alu of alu is

begin

--***************************************
--            A COMPLETER              --
--***************************************

end arch_alu;          

L’UAL est un composant combinatoire effectuant des opérations arithmétiques et logiques entre les opérandes d’entrée A et B.
L’entrée alufs permet de sélectionner le type d’opération.
Alufs appartient au type ALU_FCTS défini dans le paquetage up_pack.

Registre Accumulateur

reg_acc.svg

library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;

entity accumulator is
  port( clk, raz, load : in std_logic;
        data_in : in std_logic_vector(15 downto 0);
        data_out : out std_logic_vector(15 downto 0);
        acc15, accz : out std_logic );
end accumulator;

architecture arch_acc of accumulator is
  signal q_reg : std_logic_vector(15 downto 0);
  begin   
  
--***************************************
--            A COMPLETER              --
--***************************************    

end arch_acc;
      

Le registre accumulateur a pour rôle de mémoriser le résultat de l’UAL présent sur data_in lorsque load=’1’.
Ce résultat est alors visible sur data_out.
accz vaut ‘1’ quand data_out est nulle.
acc15 correspond au bit de poids fort de la donnée mémorisée.

Registre d’Instruction

reg_ir.svg

library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
use work.up_pack.all; 

entity ir_reg is
  port( clk, raz, load : in std_logic;
        data_in : in std_logic_vector(15 downto 0);
        data_out : out std_logic_vector(11 downto 0);
        opcode : out OPCODE);
end ir_reg; 

architecture arch_ir_reg of ir_reg is
 signal interne :  std_logic_vector(3 downto 0);
begin  
 
--***************************************
--            A COMPLETER              --
--***************************************

opcode <= OP_LDA when interne="0000" else
          OP_STO when interne="0001" else
          OP_ADD when interne="0010" else
          OP_SUB when interne="0011" else
          OP_JMP when interne="0100" else
          OP_JGE when interne="0101" else
          OP_JNE when interne="0110" else
          OP_STP when interne="0111" else
          OP_AND when interne="1000" else
          OP_OR  when interne="1001" else
          OP_XOR when interne="1010" else
          OP_LDR when interne="1011" else
          OP_LDI when interne="1100" else
          OP_STI when interne="1101" else                
          OP_JSR when interne="1110" else
          OP_RET when interne="1111" else
          OP_UNKNOWN;

end arch_ir_reg; 

Le registre IR a pour rôle de mémoriser le code de l’instruction présent sur le bus de données (entrée data_in) , lorsque ir_ld=’1’.
On tachera d’utiliser un signal interne std_logic_vector de taille 4 dans lequel seront copiés les 4 bits de poids fort du signal d’entrée, tandis que data_out sera affectés avec les 12 bits de poids faibles du signal d’entrée.
opcode (appartenant au type OPCODE défini dans le paquetage up_pack) répondra alors à l’affectation suivante (en parallèle du process synchrone) :

Registre Program Counter

reg_pc.svg

library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;

entity pc_reg is
  port( clk, raz, load : in std_logic;
        data_in : in std_logic_vector(11 downto 0);
        data_out : out std_logic_vector(11 downto 0) );
end pc_reg; 

architecture arch_pc_reg of pc_reg is
  begin  
   
--***************************************
--            A COMPLETER              --
--***************************************

end arch_pc_reg;  

Séquenceur

diagramme_etat.svg

sequenceur.svg

library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
use work.up_pack.all;  

entity sequenceur is
  port( clk, reset : in std_logic;
        accz, acc15 : in std_logic;
        opcode : in OPCODE;
        raz  : out std_logic;
        selA : out std_logic;
        selB : out std_logic;
        acc_ld, pc_ld, ir_ld, acc_oe : out std_logic;
        alufs : out ALU_FCTS;
        memrq, rnw : out std_logic );       
end sequenceur;

architecture arch_seq of sequenceur is
  
  type me_states is (INIT, FETCH, EXECUTE, STOP);
  signal  etat_cr, etat_sv : me_states ;
    
    
begin
  
process( clk , reset)
    begin
      if reset='1' then etat_cr <= INIT;
      elsif rising_edge(clk) then etat_cr <= etat_sv; 
      end if;     
end process;   
     
process(etat_cr, opcode, accz, acc15)
    begin
etat_sv <= etat_cr; raz <= '0'; selA <= '0'; selB <= '0'; 
acc_ld <= '0'; acc_oe <= '0';
pc_ld <= '0'; ir_ld <= '0'; 
alufs <= ALU_B_INC;
memrq <= '1'; rnw <='1';
		
		case etat_cr is
			when INIT =>
				etat_sv <= FETCH;
				raz <= '1';
			when FETCH =>
				etat_sv <= EXECUTE;
				ir_ld <= '1';				-- load IR with data on the data bus
				alufs <= ALU_B_INC;			-- op = B+1
				pc_ld <= '1';				-- load PC with PC+1
			when EXECUTE =>
				case opcode is
					when OP_LDA =>			 
						selA <= '1'; 		-- IR[mem] on addr bus
						selB <= '1';						
 						alufs <= ALU_B;		-- op = B
						acc_ld <= '1';		-- load data in accumulator
						etat_sv <= FETCH;
					when OP_STO =>			
						selA <= '1';		-- IR[mem] on addr bus
						acc_oe <= '1';		-- output accumulator on data bus
						rnw <= '0';			-- select write to memory
						etat_sv <= FETCH;
					when OP_ADD =>			
						selA <= '1';		-- IR[mem] on addr bus
						selB <= '1';		-- data to B alu entry
						alufs <= ALU_ADD;	-- op = A+B
						acc_ld <= '1';		-- load result in accumulator
						etat_sv <= FETCH;
					when OP_SUB =>			
						selA <= '1';		-- IR[mem] on addr bus
						selB <= '1';		-- data to B alu entry
						alufs <= ALU_SUB;	-- op = A-B
						acc_ld <= '1';		-- load result in accumulator
						etat_sv <= FETCH;
					when OP_JMP =>			
						selA <= '1';		-- IR[mem] on addr bus
						alufs <= ALU_B;  	-- op = B
						pc_ld <= '1';		-- load PC with IR[mem]
						etat_sv <= FETCH;
					when OP_JGE =>			
						if acc15='0' then	-- jump, else nothing
							selA <= '1';		-- IR[mem] on addr bus
							alufs <= ALU_B;		-- op = B
							pc_ld <= '1';		-- load PC with IR[mem]
						end if;
						etat_sv <= FETCH;
					when OP_JNE =>			
						if accz='0' then	-- jump, else nothing
							selA <= '1';		-- IR[mem] on addr bus
							alufs <= ALU_B;		-- op = B
							pc_ld <= '1';		-- load PC with IR[mem]
						end if;
						etat_sv <= FETCH;
					when OP_STP =>	
						etat_sv <= STOP;
					when OP_AND =>			
						selA <= '1';		-- IR[mem] on addr bus
						selB <= '1';		-- data to B alu entry
						alufs <= ALU_AND;	-- op = A and B
						acc_ld <= '1';		-- load result in accumulator
						etat_sv <= FETCH;
					when OP_OR =>			
						selA <= '1';		-- IR[mem] on addr bus
						selB <= '1';		-- data to B alu entry
						alufs <= ALU_OR;	-- op = A or B
						acc_ld <= '1';		-- load result in accumulator
						etat_sv <= FETCH;
					when OP_XOR =>			
						selA <= '1';		-- IR[mem] on addr bus
						selB <= '1';		-- data to B alu entry
						alufs <= ALU_XOR;	-- op = A xor B
						acc_ld <= '1';      -- load result in accumulator
						etat_sv <= FETCH;
									
					when others =>
						etat_sv <= STOP;
				end case;
				
			when STOP => etat_sv <= STOP;
		end case;	  
 
end process;

end arch_seq;

3 - Instanciation de mu0

Relier les composants décrits précédemment afin de constituer le système Processeur mu0

mu0_composant.svg

mu0_instance.svg

library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
use work.up_pack.all; 
use work.all;

entity mu0 is
  port( 
    reset, clk : in std_logic;
    data_bus : inout std_logic_vector(15 downto 0);
    addr_bus : inout   std_logic_vector(11 downto 0);
    mem_rq  :  out   std_logic;
    rnw     :  out   std_logic);
  end mu0;
  
architecture arch_mu0 of mu0 is
  signal opcode		: OPCODE;									-- opcode operation
	signal raz		: std_logic;			                    -- raz for registers
	signal ir_out	: std_logic_vector(11 downto 0);	 	    -- output of IR
	signal pc_out	: std_logic_vector(11 downto 0);		   	-- output of PC
	signal alu_out	: std_logic_vector(15 downto 0);	 		-- output of ALU
	signal acc_out	: std_logic_vector(15 downto 0);	 		-- output of ACC	
	signal muxb_out	: std_logic_vector(15 downto 0);	 		-- output of MUXb
	signal concat	: std_logic_vector(15 downto 0);	
	signal alufs	: ALU_FCTS;		-- function code for alu
	signal ir_ld	: std_logic;	-- load IR
	signal pc_ld	: std_logic;	-- load PC
	signal acc_ld	: std_logic;	-- load ACC
	signal acc_oe	: std_logic;	-- enable out buffer
	signal selA		: std_logic;	-- multiplexer A select
	signal selB		: std_logic;	-- multiplexer B select
	signal accZ		: std_logic;	-- accumulator all zero's
	signal acc15	: std_logic;	-- accumualtor sign bit
begin

--***************************************
--            A COMPLETER              --
--***************************************

end arch_mu0 ; 

REMARQUE : Le test de mu0 seul est inutile, il est nécessaire d’associer la mémoire à mu0.


4 - Instanciation de mu0_mem

mu0_mem_composant.svg

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.all;

entity mu0_mem is
  port (clk : in std_logic;
  reset : in std_logic;
  data_bus : inout std_logic_vector(15 downto 0);
  addr_bus : inout std_logic_vector(11 downto 0)
);
end mu0_mem;

architecture arch_mu0_mem of mu0_mem is
signal MEMRQ	: std_logic; -- memory request
signal RNW		: std_logic; -- read/write op
begin

--***************************************
--            A COMPLETER              --
--***************************************

end arch_mu0_mem;

Instancier le processeur mu0 avec la mémoire RAM (dans laquelle est écrit le programme à exécuter) dans un composant nommé mu0_mem puis tester le fonctionnement de l’ensemble.


5 - Modification du programme en Mémoire

Modifier le programme de la RAM pour tester l’opération de soustraction ainsi que JMP et JGE