---------------------------------------------------------------------------------- -- Company: INSA-Toulouse -- Engineer: Paul Faure -- -- Create Date: 18.04.2021 21:19:41 -- Module Name: Etage1_LectureInstruction - Behavioral -- Project Name: Processeur sécurisé -- Target Devices: Basys 3 ARTIX7 -- Tool Versions: Vivado 2016.4 -- Description: Etage 1 du processeur -- - Gestion des instructions, lecture en mémoire -- - Gestion des aléas sur les registres -- - Gestion des sauts et appels de fonction -- -- Dependencies: -- - MemoireInstruction -- - MemoireAdressesRetour ---------------------------------------------------------------------------------- library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; use IEEE.NUMERIC_STD.ALL; entity Etage1_LectureInstruction is Generic (Instruction_size_in_memory : Natural; -- Taille d'une instruction en mémoire (Taille d'un code instruction + 3*Taille d'un mot binaire) Addr_size_mem_instruction : Natural; -- Nombre de bits pour adresser la mémoire d'instruction Mem_instruction_size : Natural; -- Taille de la mémoire d'instruction (nombre d'instructions stockées) Nb_bits : Natural; -- Taille d'un mot binaire Instruction_bus_size : Natural; -- Nombre de bits du bus d'instruction (Taille d'un code instruction) Nb_registres : Natural; -- Nombre de registres du processeurs Mem_adresse_retour_size : Natural; -- Taille de la mémoire des adresses de retour (nombre d'adresse maximum) (profondeur d'appel maximale) Adresse_size_mem_adresse_retour : Natural; -- Nombre de bits pour adresser la mémoire des adresses de retour Instructions_critiques_lecture_A : STD_LOGIC_VECTOR; -- Vecteur de bit représentant les instruction critiques sur l'opérande A (si le bit i est a un, l'instruction i lit une valeur dans le registre n°opérandeA) Instructions_critiques_lecture_B : STD_LOGIC_VECTOR; -- Vecteur de bit représentant les instruction critiques sur l'opérande B (si le bit i est a un, l'instruction i lit une valeur dans le registre n°opérandeB) Instructions_critiques_lecture_C : STD_LOGIC_VECTOR; -- Vecteur de bit représentant les instruction critiques sur l'opérande C (si le bit i est a un, l'instruction i lit une valeur dans le registre n°opérandeC) Instructions_critiques_ecriture : STD_LOGIC_VECTOR; -- Vecteur de bit représentant les instruction critiques en écriture (toujours sur l'opérande A) (si le bit i est a un, l'instruction i ecrit une valeur dans le registre n°opérandeA) -- Exemple 1 : Soit MUL i j k avec pour numéro d'instruction 7 avec le comportement Ri <- Rj*Rk -- Instructions_critiques_lecture_A(7) = '0' --> MUL ne lit pas dans le registre de l'opérande A -- Instructions_critiques_lecture_B(7) = '1' --> MUL lit dans le registre de l'opérande B -- Instructions_critiques_lecture_C(7) = '1' --> MUL lit dans le registre de l'opérande C -- Instructions_critiques_ecriture(7) = '1' --> MUL ecrit dans le registre de l'opérande A -- Exemple 2 : Soit AFC i val avec pour numéro d'instruction 5 avec le comportement Ri <- val -- Instructions_critiques_lecture_A(5) = '0' --> AFC ne lit pas dans le registre de l'opérande A -- Instructions_critiques_lecture_B(5) = '0' --> AFC ne lit pas dans le registre de l'opérande B (pour AFC, B est directement la valeur, pas un numero de registre, il n'y a donc pas de lecture) -- Instructions_critiques_lecture_C(5) = '0' --> AFC ne lit pas dans le registre de l'opérande C -- Instructions_critiques_ecriture(5) = '1' --> AFC ecrit dans le registre de l'opérande A Code_Instruction_JMP : STD_LOGIC_VECTOR; -- Numéro de l'instruction JMP Code_Instruction_JMZ : STD_LOGIC_VECTOR; -- Numéro de l'instruction JMZ Code_Instruction_PRI : STD_LOGIC_VECTOR; -- Numéro de l'instruction PRI Code_Instruction_PRIC : STD_LOGIC_VECTOR; -- Numéro de l'instruction PRIC Code_Instruction_CALL : STD_LOGIC_VECTOR; -- Numéro de l'instruction CALL Code_Instruction_RET : STD_LOGIC_VECTOR; -- Numéro de l'instruction RET Code_Instruction_STOP : STD_LOGIC_VECTOR); -- Numéro de l'instruction STOP Port ( CLK : in STD_LOGIC; -- Clock RST : in STD_LOGIC; -- Reset Z : in STD_LOGIC; -- Flag Zero de l'ALU (utile pour le JMZ) STD_IN_Request : in STD_LOGIC; A : out STD_LOGIC_VECTOR (Nb_bits - 1 downto 0); -- Sortie de l'opérande A B : out STD_LOGIC_VECTOR (Nb_bits - 1 downto 0); -- Sortie de l'opérande B C : out STD_LOGIC_VECTOR (Nb_bits - 1 downto 0); -- Sortie de l'opérande C Instruction : out STD_LOGIC_VECTOR (Instruction_bus_size - 1 downto 0)); -- Sortie du code de l'instruction end Etage1_LectureInstruction; architecture Behavioral of Etage1_LectureInstruction is component MemoireInstructions is Generic (Nb_bits : Natural; Addr_size : Natural; Mem_size : Natural); Port ( Addr : in STD_LOGIC_VECTOR (Addr_size-1 downto 0); D_OUT : out STD_LOGIC_VECTOR (Nb_bits-1 downto 0) := (others => '0')); end component; component MemoireAdressesRetour is Generic (Nb_bits : Natural; Addr_size : Natural; Mem_size : Natural); Port ( R : in STD_LOGIC; W : in STD_LOGIC; D_IN : in STD_LOGIC_VECTOR (Nb_bits-1 downto 0); RST : in STD_LOGIC; CLK : in STD_LOGIC; D_OUT : out STD_LOGIC_VECTOR (Nb_bits-1 downto 0) := (others => '0'); E : out STD_LOGIC; F : out STD_LOGIC); end component; -- Signaux pour récuperer l'instruction de la mémoire signal Pointeur_instruction : STD_LOGIC_VECTOR (Addr_size_mem_instruction - 1 downto 0) := (others => '0'); signal Pointeur_instruction_next : STD_LOGIC_VECTOR (Addr_size_mem_instruction - 1 downto 0) := (0 => '1', others => '0'); signal Instruction_courante : STD_LOGIC_VECTOR (Instruction_size_in_memory - 1 downto 0) := (others => '0'); -- Tableau pour gérer les aléas des registres (lecture en étage 2 avant écriture en étage 5) subtype Registre is integer range -1 to Nb_registres - 1; type Tab_registres is array (1 to 3) of Registre; signal Tableau : Tab_registres := (others => - 1); -- Signaux de gestion pour la mémoire des adresses de retour signal Adresse_Retour : STD_LOGIC_VECTOR (Addr_size_mem_instruction - 1 downto 0) := (others => '0'); signal E : STD_LOGIC; signal F : STD_LOGIC; signal R_Aux : STD_LOGIC := '0'; signal W_Aux : STD_LOGIC := '0'; -- constantes pour injecter des bulles dans le pipeline constant Instruction_nulle : STD_LOGIC_VECTOR (Instruction_bus_size - 1 downto 0) := (others => '0'); constant Argument_nul : STD_LOGIC_VECTOR (Nb_bits - 1 downto 0) := (others => '0'); -- condition pour detecter si une bulle doit être injectée signal bulles : boolean := false; -- Compteur pour attendre lors d'un JMZ que l'instruction d'avant soit a l'ALU, ou lors d'un STOP k signal compteur : integer := 0; -- Compteur de protection des collisions entre les prints signal Compteur_PRI : integer range 0 to Nb_bits/4 + 1 := 0; -- Signal d'arret (STOP 0) signal locked : boolean := false; begin instance : MemoireInstructions generic map (Nb_bits => Instruction_size_in_memory, Addr_size => Addr_size_mem_instruction, Mem_size => Mem_instruction_size) port map (Addr => Pointeur_Instruction, D_OUT => Instruction_courante); instance_MemoireAdressesRetour : MemoireAdressesRetour generic map (Nb_bits => Addr_size_mem_instruction, Addr_size => Adresse_size_mem_adresse_retour, Mem_size => Mem_adresse_retour_size ) port map ( R => R_Aux, W => W_Aux, D_IN => Pointeur_instruction_next, RST => RST, CLK => CLK, D_OUT => Adresse_Retour, E => E, F => F ); process begin -- Synchronisation wait until CLK'event and CLK = '1'; if (RST = '0') then -- Reset de l'étage Tableau <= (others => -1); Pointeur_Instruction <= (others => '0'); compteur <= 0; Compteur_PRI <= 0; locked <= false; C <= Argument_nul; B <= Argument_nul; A <= Argument_nul; Instruction <= Instruction_nulle; elsif (STD_IN_Request = '0') then -- Avancement des instructions en écritures dans le pipeline Tableau(3) <= Tableau(2); Tableau(2) <= Tableau(1); Tableau(1) <= -1; if (Compteur_PRI > 0) then Compteur_PRI <= Compteur_PRI - 1; end if; if (not bulles) then -- S'il ne faut pas injecter de bulles ont traite l'instruction (Possible code factorisable sur ce if) if ((Instruction_courante (Instruction_bus_size + 3 * Nb_bits - 1 downto 3 * Nb_bits) = Code_Instruction_CALL) or (Instruction_courante (Instruction_bus_size + 3 * Nb_bits - 1 downto 3 * Nb_bits) = Code_Instruction_JMP)) then -- CAS PARTICULIER : CALL ou JMP, on transmet et on saute C <= Instruction_courante (1 * Nb_bits - 1 downto 0 * Nb_bits); B <= Instruction_courante (2 * Nb_bits - 1 downto 1 * Nb_bits); A <= Instruction_courante (3 * Nb_bits - 1 downto 2 * Nb_bits); Instruction <= Instruction_courante (Instruction_bus_size + 3 * Nb_bits - 1 downto 3 * Nb_bits); Pointeur_Instruction <= Instruction_courante (2 * Nb_bits + Addr_size_mem_instruction - 1 downto 2 * Nb_bits); elsif (Instruction_courante (Instruction_bus_size + 3 * Nb_bits - 1 downto 3 * Nb_bits) = Code_Instruction_RET) then -- CAS PARTICULIER : RET, on transmet et on revient C <= Instruction_courante (1 * Nb_bits - 1 downto 0 * Nb_bits); B <= Instruction_courante (2 * Nb_bits - 1 downto 1 * Nb_bits); A <= Instruction_courante (3 * Nb_bits - 1 downto 2 * Nb_bits); Instruction <= Instruction_courante (Instruction_bus_size + 3 * Nb_bits - 1 downto 3 * Nb_bits); Pointeur_Instruction <= Adresse_Retour; elsif (Instruction_courante (Instruction_bus_size + 3 * Nb_bits - 1 downto 3 * Nb_bits) = Code_Instruction_JMZ) then -- CAS PARTICULIER : JMZ, on attends que l'instruction précedente arrive sur l'ALU, si le flag Zero est a un on saute, sinon on continue normalement compteur <= compteur + 1; C <= Instruction_courante (1 * Nb_bits - 1 downto 0 * Nb_bits); B <= Instruction_courante (2 * Nb_bits - 1 downto 1 * Nb_bits); A <= Instruction_courante (3 * Nb_bits - 1 downto 2 * Nb_bits); Instruction <= Instruction_courante (Instruction_bus_size + 3 * Nb_bits - 1 downto 3 * Nb_bits); if (compteur = 2) then if (Z = '1') then Pointeur_Instruction <= Instruction_courante (2 * Nb_bits + Addr_size_mem_instruction - 1 downto 2 * Nb_bits); else Pointeur_Instruction <= Pointeur_Instruction + 1; end if; compteur <= 0; end if; elsif (Instruction_courante (Instruction_bus_size + 3 * Nb_bits - 1 downto 3 * Nb_bits) = Code_Instruction_STOP) then -- CAS PARTICULIER : STOP, si on est bloqué, on ne fait rien, programme arrété -- sinon, on regarde si l'on doit se bloquer -- sinon, on incremente le compteur et on attends if (not locked) then if (Instruction_courante (3 * Nb_bits - 1 downto 2 * Nb_bits) = Argument_nul) then locked <= true; end if; compteur <= compteur + 1; if (compteur + 1 = to_integer(unsigned(Instruction_courante (3 * Nb_bits - 1 downto 2 * Nb_bits))) * 1000) then Pointeur_Instruction <= Pointeur_Instruction + 1; compteur <= 0; end if; end if; C <= Instruction_courante (1 * Nb_bits - 1 downto 0 * Nb_bits); B <= Instruction_courante (2 * Nb_bits - 1 downto 1 * Nb_bits); A <= Instruction_courante (3 * Nb_bits - 1 downto 2 * Nb_bits); Instruction <= Instruction_courante (Instruction_bus_size + 3 * Nb_bits - 1 downto 3 * Nb_bits); elsif (Instruction_courante (Instruction_bus_size + 3 * Nb_bits - 1 downto 3 * Nb_bits) = Code_Instruction_PRI) then -- CAS PARTICULIER : PRI, on transmet l'instruction et fixe le compteur pour proteger des collisions Compteur_PRI <= Nb_bits/4 + 1; C <= Instruction_courante (1 * Nb_bits - 1 downto 0 * Nb_bits); B <= Instruction_courante (2 * Nb_bits - 1 downto 1 * Nb_bits); A <= Instruction_courante (3 * Nb_bits - 1 downto 2 * Nb_bits); Instruction <= Instruction_courante (Instruction_bus_size + 3 * Nb_bits - 1 downto 3 * Nb_bits); Pointeur_Instruction <= Pointeur_Instruction + 1; else -- CAS GENERAL : On transmet l'instruction et les opérandes, si elle est critique en ecriture, on enregistre le registre associé dans le tableau C <= Instruction_courante (1 * Nb_bits - 1 downto 0 * Nb_bits); B <= Instruction_courante (2 * Nb_bits - 1 downto 1 * Nb_bits); A <= Instruction_courante (3 * Nb_bits - 1 downto 2 * Nb_bits); Instruction <= Instruction_courante (Instruction_bus_size + 3 * Nb_bits - 1 downto 3 * Nb_bits); if (Instructions_critiques_ecriture(to_integer(unsigned(Instruction_courante (Instruction_bus_size + 3 * Nb_bits - 1 downto 3 * Nb_bits)))) = '1') then Tableau(1) <= to_integer(unsigned(Instruction_courante (3 * Nb_bits - 1 downto 2 * Nb_bits))); end if; Pointeur_Instruction <= Pointeur_Instruction + 1; end if; else -- Si besoin de bulle, on l'injecte C <= Argument_nul; B <= Argument_nul; A <= Argument_nul; Instruction <= Instruction_nulle; end if; end if; end process; -- Condition horrible -> Instruction critique en lecture sur A qui lit dans A=i et Ri dans tableau ou instruction critique en lecture sur B qui lit dans B=j et Rj dans tableau ou instruction critique en lecture sur C qui lit dans C=k et Rk dans tableau bulles <= ( ( Instructions_critiques_lecture_A(to_integer(unsigned(Instruction_courante (Instruction_bus_size + 3 * Nb_bits - 1 downto 3 * Nb_bits)))) = '1' -- Intruction critique sur A ) and ( (to_integer(unsigned(Instruction_courante (3 * Nb_bits - 1 downto 2 * Nb_bits))) = Tableau(1)) -- A est or (to_integer(unsigned(Instruction_courante (3 * Nb_bits - 1 downto 2 * Nb_bits))) = Tableau(2)) -- dans le or (to_integer(unsigned(Instruction_courante (3 * Nb_bits - 1 downto 2 * Nb_bits))) = Tableau(3)) -- tableau ) ) or ( ( Instructions_critiques_lecture_B(to_integer(unsigned(Instruction_courante (Instruction_bus_size + 3 * Nb_bits - 1 downto 3 * Nb_bits)))) = '1' ) and ( (to_integer(unsigned(Instruction_courante (2 * Nb_bits - 1 downto 1 * Nb_bits))) = Tableau(1)) or (to_integer(unsigned(Instruction_courante (2 * Nb_bits - 1 downto 1 * Nb_bits))) = Tableau(2)) or (to_integer(unsigned(Instruction_courante (2 * Nb_bits - 1 downto 1 * Nb_bits))) = Tableau(3)) ) ) or ( ( Instructions_critiques_lecture_C(to_integer(unsigned(Instruction_courante (Instruction_bus_size + 3 * Nb_bits - 1 downto 3 * Nb_bits)))) = '1' ) and ( (to_integer(unsigned(Instruction_courante (1 * Nb_bits - 1 downto 0 * Nb_bits))) = Tableau(1)) or (to_integer(unsigned(Instruction_courante (1 * Nb_bits - 1 downto 0 * Nb_bits))) = Tableau(2)) or (to_integer(unsigned(Instruction_courante (1 * Nb_bits - 1 downto 0 * Nb_bits))) = Tableau(3)) ) ) or ( ( (Instruction_courante (Instruction_bus_size + 3 * Nb_bits - 1 downto 3 * Nb_bits) = Code_Instruction_PRI) or (Instruction_courante (Instruction_bus_size + 3 * Nb_bits - 1 downto 3 * Nb_bits) = Code_Instruction_PRIC) ) and ( not (Compteur_PRI = 0) ) ); -- Gestion de l'écriture/lecture dans la mémoire des adresses de retour R_Aux <= '1' when Instruction_courante (Instruction_bus_size + 3 * Nb_bits - 1 downto 3 * Nb_bits) = Code_Instruction_RET and STD_IN_Request = '0' else '0'; W_Aux <= '1' when Instruction_courante (Instruction_bus_size + 3 * Nb_bits - 1 downto 3 * Nb_bits) = Code_instruction_CALL and STD_IN_Request = '0' else '0'; Pointeur_instruction_next <= Pointeur_instruction + 1; end Behavioral;