* QUESTION 1 ============ * Question 1.1 [15 points] On utilise une bascule D : - Le fil CK de la bascule est connecté sur l'horloge H. - La sortie Q de la bascule est routée à travers un inverseur et rebouclée sur l'entrée D de la bascule. * Question 1.2 [15 points] H 0000111100001111000011110000111100001111... Q 0000111111110000000011111111000000001111... La fréquence du signal Q est égale à la moitié de celle de H. Ce circuit est un diviseur de fréquence. * Question 1.3 [15 points] Q1 0000000011111111000000001111111100000000... Q0 0000111100001111000011110000111100001111... Pour calculer les tables de vérité de D0 et D1, il faut imaginer que les nouvelles valeurs des bascules seront calculées au moyen des anciennes. Au top d'horloge, on charge les deux bascules avec les valeurs présentes sur les entrées D0 et D1 à partir des valeurs de Q0 et Q1. Table de D0 en fonction de [Q1, Q0] : 00 1 01 0 10 1 11 0 Donc : D0 = \Q0 Table de D1 en fonction de [Q1, Q0] : 00 0 01 1 10 1 11 0 Donc : D1 = Q0 XOR Q1 Le circuit se câble avec deux bascules D : - L'entrée CK de chacune des deux bascules est connectée à l'horloge H. - La sortie Q0 est routée à travers un inverseur et rebouclée sur l'entrée D0 (c'est la question Q1.1). - Les deux sorties Q0 et Q1 sont câblées sur une porte XOR, dont la sortie est câblée sur l'entrée D1. * Question 1.4 [15 points] Pour réaliser ce chenillard, on utilise trois bascules D, que l'on branche en série, rebouclées de la façon suivante : - L'entrée CK de chacune des trois bascules est connectée à l'horloge H. - La sortie Q0 est connectée à l'entrée D1. - La sortie Q1 est connectée à l'entrée D2. - La sortie Q2 est routée à travers un inverseur et connectée à l'entrée D0 de la première bascule. * QUESTION 2 ============ * Question 2.1 [20 points] rrmovl rA,rB Fetch : icode:ifun = M1[PC] rA:rB = M1[PC+1] valP = PC + 2 Decode : valA = R[rA] Execute : ValE = 0 + valA Memory : Write back : R[rB] = valE PC update : PC = valP pushl rA Fetch : icode:ifun = M1[PC] rA:rB = M1[PC+1] valP = PC + 2 Decode : valA = R[rA] valB = R[%esp] Execute : valE = valB + (-4) Memory : M4[valE] = valA Write back : R[%esp] = valE PC update : PC = valP ret Fetch : icode:ifun = M1[PC] valP = PC + 1 Decode : valA = R[%esp] valB = R[%esp] Execute : valE = valB + 4 Memory : ValM = M4[valA] Write back : R[%esp] = valE PC update : PC = valM * Question 2.2 [20 points] Voir en fin de corrigé le code HCL modifié. * Question 2.3 [10 points] Il faut deux accès en lecture, pour lire les anciennes valeurs de rA et rB, et deux accès en écriture, pour stocker les nouvelles valeurs de rA et rB, résultant de l'échange de leurs valeurs. * Question 2.4 [10 points] Dans le schéma actuel, l'une des deux valeurs pouvant être stockées dans la banque de registres est valM, valeur issue d'une lecture mémoire. Or, XCHGL n'utilise pas la mémoire. Il faut donc un nouveau bus, allant de l'une des deux valeurs (par exemple valB) à la banque de registres, multiplexée avec valM. * Question 2.5 [10 points] Voir en fin de corrigé le code HCL modifié. On met la valeur de rA dans rB de la même façon que pour RRMOVL. On suppose que, quand la mémoire n'est pas utilisée, valM est égale à valB, grâce à un nouveau multiplexeur. * QUESTION 3 ============ * Question 3.1 [20 points] Le passage des paramètres par la pile permet de gérer les fonctions réentrantes. On empile les paramètres dans l'ordre inverse de afin de pouvoir gérer les fonctions à nombre d'arguments variables : ainsi, le premier paramètre a toujours la même place par rapport au sommet de pile, ce qui permet à la fonction appelée de toujours savoir où il se trouve. Les conventions "caller/callee save" fixent qui, de la fonction appelante ou de la fonction appelée, est responsable de la sauvegarde de certains registres. Fonction appelante : EAX, ECX et EDX. Fonction appelée : EBX, ESI, EDI et EBP. * Question 3.2 [20 points] .pos 0 irmovl 200,%esp # Initialisation de la pile irmovl 5,%eax pushl %eax # Empilage de la constante 5 call fact iaddl 4,%esp # Fonctionnellement inutile ici, mais plus propre rmmovl %eax,a halt .align 4 # Alignement sur un mot de la zone de données a: .long 0 # "a" est l'étiquette de la zone mémoire * Question 3.3 [30 points] fact2: mrmovl 4(%esp),%edx # %edx = n (paramètre d'appel) irmovl 1,%eax # %eax = 1 (valeur de retour) rrmovl %edx,%ecx # %ecx est un registre de travail subl %eax,%ecx # %ecx = (n - 1) jle fin2 # Si %ecx <= 0, donc si n <= 1 pushl %edx # Empilage 1er paramètre de mul : n pushl %ecx # Empilage paramètre de fact2(n-1) call fact2 # Récursion iaddl 4,%esp # Dépilage paramètre de fact2(n-1) pushl %eax # Empilage 2ème paramètre de mul : fact2(n-1) call mul iaddl 8,%esp # Dépilage des 2 paramètres de mul fin2: ret # Valeur de retour dans %eax fact: mrmovl 4(%esp),%edx # %edx = n (paramètre d'appel) irmovl -1,%eax # %eax = -1 (valeur de retour) andl %edx,%edx # Test de n jl fin # Si n >= 0 pushl %edx # Empilage paramètre de fact2(n) call fact2 iaddl 4,%esp # Dépilage paramètre de fact2(n) fin: ret # Valeur de retour de la récursion Note : les 4 instructions "jl fin / pushl %edx / call fact2 / iaddl 4,%esp" peuvent être remplacées par l'unique instruction "jge fact2", du fait que les contextes de pile de fact2 et fact sont identiques. * CORRIGE HCL (Questions 2.2 et 2.5) ==================================== Chercher les mentions de RRMOVL, PUSHL, RET et XCHGL ################ Fetch Stage ################################### # Does fetched instruction require a regid byte? bool need_regids = icode in { OPL, IOPL, POPL, IRMOVL, RMMOVL, MRMOVL, RRMOVL, PUSHL, XCHGL }; # Does fetched instruction require a constant word? bool need_valC = icode in { IRMOVL, RMMOVL, MRMOVL, JXX, CALL, IOPL }; ################ Decode Stage ################################### ## What register should be used as the A source? int srcA = [ icode in { RMMOVL, OPL, RRMOVL, PUSHL, XCHGL } : rA; icode in { POPL, RET } : RESP; 1 : RNONE; # Don't need register ]; ## What register should be used as the B source? int srcB = [ icode in { OPL, IOPL, RMMOVL, MRMOVL, XCHGL } : rB; icode in { POPL, CALL, PUSHL, RET } : RESP; 1 : RNONE; # Don't need register ]; ## What register should be used as the E destination? int dstE = [ icode in { IRMOVL, OPL, IOPL, RRMOVL, XCHGL } : rB; icode in { POPL, CALL, PUSHL, RET } : RESP; 1 : RNONE; # Don't need register ]; ## What register should be used as the M destination? int dstM = [ icode in { MRMOVL, POPL, XCHGL } : rA; # Pour XCHGL : avec valM contenant valB si la mémoire n'est pas utilisée 1 : RNONE; # Don't need register ]; ################ Execute Stage ################################### ## Select input A to ALU int aluA = [ icode in { OPL, RRMOVL, XCHGL } : valA; icode in { IRMOVL, RMMOVL, MRMOVL, IOPL } : valC; icode in { CALL, PUSHL } : -4; icode in { POPL, RET } : 4; # Other instructions don't need ALU ]; ## Select input B to ALU int aluB = [ icode in { RMMOVL, MRMOVL, OPL, IOPL, CALL, POPL, PUSHL, RET } : valB; icode in { IRMOVL, RRMOVL, XCHGL } : 0; # Other instructions don't need ALU ]; ## Set the ALU function int alufun = [ icode in { OPL, IOPL } : ifun; 1 : ALUADD; ]; ## Should the condition codes be updated? bool set_cc = icode in { OPL, IOPL }; ################ Memory Stage ################################### ## Set read control signal bool mem_read = icode in { MRMOVL, POPL, RET }; ## Set write control signal bool mem_write = icode in { RMMOVL, CALL, PUSHL }; ## Select memory address int mem_addr = [ icode in { RMMOVL, CALL, MRMOVL, PUSHL } : valE; icode in { POPL, RET } : valA; # Other instructions don't need address ]; ## Select memory input data int mem_data = [ icode in { RMMOVL, PUSHL } : valA; icode == CALL : valP; # Default: Don't write anything ]; ################ Program Counter Update ############################ ## What address should instruction be fetched at int new_pc = [ icode == CALL : valC; icode == JXX && Bch : valC; icode == RET : valM; 1 : valP; ];