SC91-A, instruction de chargement direct
SC91-A, implementation banc de registres

Etape 10 : Création d'un première instruction complète

Rien de tel, pour tester globalement l'architecture que nous venons de décrire que de réaliser une première instruction. LOADd est une instruction simple à réaliser mais aussi très utile pour tester le fonctionnement des registres, c'est donc celle-ci que j'ai choisi pour débuter.

Création de l'ALU

LOADd sert à charger une valeur directe dans un registre suivant différent modes qui permettent de ne modifier que les bits de poids fort ou faibles voir tous les bits lors de chargement de type SIGNED ou UNSIGNED. Cette instruction va passer à l'ALU la valeur direct en se connectant sur le bus de première opérande DNN. Il y aura donc un signal permettant de réaliser cet aiguillage comme le montre le code suivant :

         -- ==========================================================================================
         -- DNN Specific routing
         i_DNN(15 downto 0)  <= IVV(IVV_LOADd_VALUE_TOP downto IVV_LOADd_VALUE_TOP-15) when I_LOADd = '1' else
                                DNN;
         i_DNN(31 downto 16) <= "0000000000000000" when I_LOADd = '1' else
                                DNN;
      

La valeur du registre de destination servira de seconde opérande, nécessaire au calcul des bits inchangé dans le résultat final. Faute de place dans le codage de l'instruction, l'aiguillage classique de RMM ne sera pas possible. Le séquenseur devra donc aiguiller spécifiquement RDD vers RMM pour que DMM soit positionné avec la valeur adéquat. Enfin le calcul de la nouvelle valeur de RDD pourra se faire comme suit, en fonction du mode :

            -- ============================================================================
            -- LOADd Instruction block
            LOADd_MODE(1 downto 0) <= IVV(IVV_LOADd_MODE_TOP downto IVV_LOADd_MODE_TOP-1);
            LOADd_c <= '0';
            for i in 31 downto 16 generate
               LOADd_out(i) <= i_DNN(i-16) when LOADd_mode = LOADd_MODE_HIGH   else
                               i_DNN(15)   when LOADd_mode = LOADd_MODE_SIGNED else
                               DMM(i)      when LOADd_mode = LOADd_MODE_LOW    else
                               '0';
            end generate;
            for i in 15 downto 0 generate
               LOADd_out(i) <= DMM(i) when LOADd_mode = LOADd_MODE_HIGH else
                               i_DNN(i);
            end generate;
      

Bref, bien que d'approche assez simple, cette instruction demande quelques spécificités, autant dans le séquenceur (avec un aiguillage des registres particulier) que dans l'ALU (avec un aiguillage du bus DNN particulier).

Création du Séquenceur

Le bloc séquenceur doit tout d'abord charger l'instruction à exécuter, il devra s'assurer qu'une instruction soit prête sans quoi il devra insérer une bulle (a savoir un NOP) sans pour autant incrémenter le compteur ordinal. Cette opération est exécutée par le premier bloc suivant :

         -- =============================================================================
         -- CURRENT INSTRUCTION REGISTER
         PC_LOAD <= IN_RDY;
         i_INSTR <= INSTR when ( IN_RDY = '1' ) else INSTRUCTION_NOP;
         i_rinstr : cl_reg generic map ( WIDTH => 32 )
                           port map ( D => i_INSTR, Q => instruction, CLK => CLK,
                                      LOAD => '1', RESET => RST);
         IVV <= instruction;
      

Le signal IN_RDY pour INstruction ReaDY est émis par le bloc de gestion de la mémoire et indique par un état haut que l'instruction est prette a être récupérée.
Le signal PC_LOAD commande le chargement du registre R00 au prochain cycle. Cette ligne permet de ne pas incrémenter le compteur ordinal lorsque l'on insère une bulle (un NOP).
Le registre d'instruction sera chargé avec l'INSTRuction ou avec une commande NOP dans le cas où la mémoire ne serait pas prette.

Le décodage de l'instruction est en suite réalisé sur le schéma suivant :

         -- =====================================================================
         -- INSTRUCTION DECODING
         process ( instruction )
         begin
            -- Default values
            t_LOADd <= '0';
            SPP     <= '0';
            SPM     <= '0';
            LFL     <= '1';
            PC_R00  <= "00";
            LDD     <= '0';

            if     ( instruction(31 downto 30) = "00" ) then
                     -- Instuction JMP
            elsif  ( instruction(31 downto 30) = "01" ) then
                     -- Instruction CALL
            elsif  ( instruction(31 downto 29) = "110" ) then
                     -- Instruction JB
            elsif  ( instruction(31 downto 29) = "101" ) then
                     -- Instruction ADDd
            else
               case instruction(31 downto 24) is
                  when "10000000"  =>
                     -- Instruction ADDr
                  when "11111100"  =>
                     -- Instruction LOADd
                     t_LOADd <= '1';
                     LDD <= '1';

                  when others      =>
               end case;
            end if;
         end process;
      

Le signaux de contrôle de l'ALU sont mis à jour en fonction de l'instruction en cours d'exécution. Ce modèle sera complété au fur et à mesure de l'ajout d'instructions. Le sequenceur contient aussi le routage des signaux RDD, RNN, RMM ... Dans le cas de l'instruction LOADd ici décrite, le signal t_LOADd permet l'aiguillage spécifique de RDD sur RMM pour lire et charger le même registre. LDD déclenche le chargement du registre de destination. LFL, par défaut, actif entraine la mise à jour des flags.

Programme de test

Outre la réalisation des blocs séquenceur et alu, cette instruction est l'occasion de réaliser la partie mémoire pour y paramétrer le premier programme de test. Pour l'instant, la mémoire de programme et celle de données seront séparés (durant la phase de conception). La mémoire des programme est donc consituée par une ROM de 128 mots de 32 bits. Le programme est actuellement inscrit en dur dans le vhdl de ce bloc.
Pour réaliser le test de cette première instruction, l'horloge est controlée manuellement à l'aide de l'un des boutons poussoir de la carte d'essai. L'afficheur 7 segments imprime la valeur du registre R04 utilisé dans le jeu d'essai.
Le programme utilisé pour les tests est décrit ci-dessous :

       i_romCode : cl_rom128x32 generic map ( INIT =>
       ( "11111100001000100001100100001000", -- FC 00100 0100001100100001 00 0 - LOADd R04,0x4321, low            0
         "11111100001001000011101100101010", -- FC 00100 1000011101100101 01 0 - LOADd R04,0x8765, high           1
         "11111100001000100001100100001010", -- FC 00100 0100001100100001 01 0 - LOADd R04,0x4321, high           2
         "11111100001001000011101100101000", -- FC 00100 1000011101100101 00 0 - LOADd R04,0x8765, low            3
         "11111100001000100001100100001100", -- FC 00100 0100001100100001 10 0 - LOADd R04,0x4321, signed         4
         "11111100001001000011101100101100", -- FC 00100 1000011101100101 10 0 - LOADd R04,0x8765, signed         5
         "11111100001000100001100100001110", -- FC 00100 0100001100100001 11 0 - LOADd R04,0x4321, unsigned       6
         "11111100001001000011101100101110", -- FC 00100 1000011101100101 11 0 - LOADd R04,0x8765, unsigned       7
         X"00000000",                        --                                                                   8
         X"00000000",                        --                                                                   9
         X"00000000",                        --                                                                  10
         X"00000000",                        --                                                                  11
         X"00000000",                        --                                                                  12
         X"00000000",                        --                                                                  13
         X"00000000",                        --                                                                  14
         X"00000000",                        --                                                                  15
         [...]
      

Petite remarque - pour voir si vous suivez ;o) -

Lors du demarrage, le registre instruction est initialisé à zéro, et durant le premier cycle c'est bien cette instruction qui sera exécutée avant même que la première vraie instruction soit chargée il est donc important que cette instruction "0" ne pause pas de problème. Comme il s'agit d'après le jeu d'instruction d'un JMP à l'adresse 0 ... Tout va bien.

Quelques instructions simples de plus
Simple-Cpu The Simple CPU project The Simple CPU project
Un projet de disk91
Small CPU project