From 62af726f0c73784ef254f6fb06527ab86c9f9d1e Mon Sep 17 00:00:00 2001 From: Yves Chevallier Date: Sun, 29 Sep 2024 23:28:14 +0200 Subject: [PATCH] Work on CPU architecture --- TODO.md | 1 + docs/assets/images/barrel-shifter.drawio | 449 ++++ docs/assets/images/cache-hierarchy.drawio | 61 + docs/assets/images/chipset-cpu.drawio | 115 + docs/assets/images/chipset.drawio | 115 + docs/assets/images/cpu-core-diagram.drawio | 409 ++++ docs/assets/images/cpu-diagram.drawio | 124 ++ docs/assets/images/cpu-registers.drawio | 1841 +++++++++++++++++ docs/assets/images/cpu-zero.drawio | 465 +++++ docs/assets/images/von-neumann-harvard.drawio | 67 + .../25-architecture-and-systems/computer.md | 365 ++++ 11 files changed, 4012 insertions(+) create mode 100644 docs/assets/images/barrel-shifter.drawio create mode 100644 docs/assets/images/cache-hierarchy.drawio create mode 100644 docs/assets/images/chipset-cpu.drawio create mode 100644 docs/assets/images/chipset.drawio create mode 100644 docs/assets/images/cpu-core-diagram.drawio create mode 100644 docs/assets/images/cpu-diagram.drawio create mode 100644 docs/assets/images/cpu-registers.drawio create mode 100644 docs/assets/images/cpu-zero.drawio create mode 100644 docs/assets/images/von-neumann-harvard.drawio diff --git a/TODO.md b/TODO.md index 45f1a016..bd8785c3 100644 --- a/TODO.md +++ b/TODO.md @@ -11,6 +11,7 @@ ## Improvements +- [ ] Find a way to add crop cut on PDF as we don't use A4 format - [ ] Better separation terms and definitions, acronyms, glossary - [ ] Integrate bibliography (extra pages in backmatter) - [ ] Chapter "Crédits des illustrations" in backmatter (ai generated?) diff --git a/docs/assets/images/barrel-shifter.drawio b/docs/assets/images/barrel-shifter.drawio new file mode 100644 index 00000000..47705bea --- /dev/null +++ b/docs/assets/images/barrel-shifter.drawio @@ -0,0 +1,449 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/assets/images/cache-hierarchy.drawio b/docs/assets/images/cache-hierarchy.drawio new file mode 100644 index 00000000..fede5246 --- /dev/null +++ b/docs/assets/images/cache-hierarchy.drawio @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/assets/images/chipset-cpu.drawio b/docs/assets/images/chipset-cpu.drawio new file mode 100644 index 00000000..f8ff8965 --- /dev/null +++ b/docs/assets/images/chipset-cpu.drawio @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/assets/images/chipset.drawio b/docs/assets/images/chipset.drawio new file mode 100644 index 00000000..f8ff8965 --- /dev/null +++ b/docs/assets/images/chipset.drawio @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/assets/images/cpu-core-diagram.drawio b/docs/assets/images/cpu-core-diagram.drawio new file mode 100644 index 00000000..afc13c87 --- /dev/null +++ b/docs/assets/images/cpu-core-diagram.drawio @@ -0,0 +1,409 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/assets/images/cpu-diagram.drawio b/docs/assets/images/cpu-diagram.drawio new file mode 100644 index 00000000..660343df --- /dev/null +++ b/docs/assets/images/cpu-diagram.drawio @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/assets/images/cpu-registers.drawio b/docs/assets/images/cpu-registers.drawio new file mode 100644 index 00000000..3474f8c8 --- /dev/null +++ b/docs/assets/images/cpu-registers.drawio @@ -0,0 +1,1841 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/assets/images/cpu-zero.drawio b/docs/assets/images/cpu-zero.drawio new file mode 100644 index 00000000..1580a51f --- /dev/null +++ b/docs/assets/images/cpu-zero.drawio @@ -0,0 +1,465 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/assets/images/von-neumann-harvard.drawio b/docs/assets/images/von-neumann-harvard.drawio new file mode 100644 index 00000000..a12f8e2f --- /dev/null +++ b/docs/assets/images/von-neumann-harvard.drawio @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/course-c/25-architecture-and-systems/computer.md b/docs/course-c/25-architecture-and-systems/computer.md index 227867b7..89fc00d8 100644 --- a/docs/course-c/25-architecture-and-systems/computer.md +++ b/docs/course-c/25-architecture-and-systems/computer.md @@ -194,3 +194,368 @@ L'Ivy Bridge a introduit une finesse de gravure de 22 nm, permettant une meilleu La quatrième génération, basée sur l’architecture Haswell, a continué d'améliorer les performances et l'efficacité énergétique, tout en proposant des améliorations comme l’intégration de la gestion de l’alimentation et des performances graphiques améliorées pour répondre aux besoins des utilisateurs modernes. Ce résumé souligne les progrès constants en termes de puissance de traitement, de gestion mémoire, de parallélisme, et d’efficacité énergétique des processeurs Intel au fil des générations. + +### Architecture + +Les architectures Von Neumann et Harvard sont deux modèles fondamentaux d'organisation d'un système informatique, qui se distinguent principalement par la façon dont elles gèrent la mémoire et le bus de données. Chacune a ses avantages et inconvénients, et elles sont utilisées dans différents contextes selon les besoins spécifiques du système. + +![Comparaison Havard et Von Neumann](/assets/images/von-neumann-harvard.drawio) + +Dans le modèle de John von Neumann, les instructions et les données sont stockées dans une mémoire unifiée, et le processeur utilise le même bus pour accéder à la mémoire, qu'il s'agisse d'instructions ou de données. Cela rend l'architecture plus simple et plus flexible, mais peut entraîner des goulets d'étranglement si le processeur doit accéder à la mémoire pour les deux types d'opérations simultanément. + +Avec Harvard, les instructions et les données sont stockées dans des mémoires séparées, chacune ayant son propre bus de données. Cela permet au processeur d'accéder simultanément à une instruction et à des données, améliorant les performances globales du système. + +De nos jours, les processeurs modernes (x86-64, ARM, etc.) utilisent une architecture hybride qui combine des éléments des deux modèles. Par exemple, les caches de niveau 1 et 2 stockent à la fois les instructions et les données, tandis que la mémoire principale est organisée de manière Von Neumann. Cela permet de combiner les avantages des deux approches pour obtenir des performances optimales. + +### Chipset + +![Chipset](/assets/images/chipset-cpu.drawio) + +### Mémoire cache + +![Hiérarchie mémoire](/assets/images/cache-hierarchy.drawio) + +### Structure interne + +![Diagramme d'un processeur](/assets/images/cpu-diagram.drawio) + +### Coeur + +![Diagramme d'un coeur de processeur](/assets/images/cpu-core-diagram.drawio) + +#### Registres + +Dans une architecture super-scalaire hors-ordre (*out-of-order*) comme le x86-64, lorsqu'un programme est écrit en assembleur ou en langage machine, il fait appel à des registres architecturaux comme RAX, RBX etc. Ces registres font partie du **modèle de programmation** x86-64 et chaque programmeur ou compilateur les voit comme des éléments physiques qu'ils peuvent manipuler. + +Cependant, dans un processeur moderne hors-ordre (comme les Intel Core i7/i9, AMD Ryzen, etc.), la réalité matérielle est plus complexe. Les registres que le programmeur n'existent pas directement sous cette forme dans le matériel. Les processeurs modernes sont capables d'exécuter plusieurs instructions en parallèle grâce à une technique appelée exécution **hors-ordre**. Dans ce contexte, il est nécessaire de gérer plusieurs versions d'un même registre (comme RAX) à différents moments, car plusieurs instructions peuvent essayer de modifier ou d'utiliser ce registre de manière simultanée ou presque. + +C'est là qu'intervient le concept de renommage des registres. Le processeur dispose d'un ensemble de registres physiques (ou registres de données) qui stockent les valeurs des registres architecturaux. Ces registres physiques sont opaques pour le programmeur. Lorsque le processeur exécute un programme, il utilise une technique appelée renommage des registres pour associer temporairement chaque registre architectural (comme RAX) à un registre physique spécifique. Cela permet au processeur d'éviter les conflits entre instructions qui voudraient utiliser le même registre en même temps. + +Voici comment cela fonctionne : + +1. Lorsqu'une instruction est décodée, le processeur associe chaque registre architectural à un registre physique. Le processeur gère une table de renommage qui fait correspondre chaque registre architectural à un registre physique. +2. Exécution hors-ordre : Les instructions sont exécutées dans l'ordre dans lequel elles sont décodées, mais les résultats peuvent être renvoyés dans un ordre différent. Cela signifie que les instructions peuvent être exécutées dans un ordre différent de celui dans lequel elles apparaissent dans le code source. +3. Retirement (retrait) : une fois que les instructions sont exécutées et validées, les résultats des registres physiques sont reflétés dans les registres architecturaux lors de l'étape de retrait (retirement). À ce stade, le programmeur ou le système voit les registres comme étant mis à jour dans l'ordre correct. + +Le *Reorder Buffer (ROB)* est une structure clé dans ce processus. Il permet au processeur de garder une trace des instructions en cours d'exécution hors-ordre et il s'assure que les instructions sont retirées (c'est-à-dire, finalisées et appliquées aux registres architecturaux) dans le bon ordre, tel que prévu par le programmeur. Au moment du retrait, via le ROB, les résultats des registres physiques sont copiés vers les registres architecturaux visibles par le programmeur. C'est ce moment-là où, du point de vue du programmeur, le registre RAX ou RBX est mis à jour. + +### L'assembleur + +Le saviez-vous, il n'est pas indispensable de connaître le C pour développer des programmes sur un ordinateur. 85% des processeurs équipant les ordinateurs personnels sont sur une base x86-64. C'est à dire que si l'on connaît l'architecture, on peut écrire des programmes directement en assembleur. C'est un langage de bas niveau qui permet de contrôler directement le processeur. + +Admettons que l'on souhaite réécrire notre programme `hello.c` mais en assembleur. Ici, il ne faut pas imaginer accéder directement à l'écran pour écrire du texte. Il faut passer par le système d'exploitation. Pour cela, on utilise les appels systèmes. C'est exactement ce que fait `printf`. + +```nasm +section .data + message db "Hello, World!", 0xA + len equ $-message ; Taille du message + +section .text +global _start + +_start: + ; Appel système pour écrire (sys_write) + mov rax, 1 ; Numéro d'appel système pour sys_write + mov rdi, 1 ; Descripteur de fichier 1 (stdout) + mov rsi, message ; Adresse du message + mov rdx, len ; Longueur du message + syscall ; Appel système + + ; Appel système pour terminer le programme (sys_exit) + mov rax, 60 ; Numéro d'appel système pour sys_exit + xor rdi, rdi ; Code de sortie 0 + syscall ; Appel système +``` + +Pour le compiler, on utilisera `nasm` et `ld`: + +```bash +nasm -f elf64 hello.s -o hello.o +ld hello.o -o hello +``` + +Ce programme ne comporte que deux appel systèmes `write` et `exit`. + +Admettons que l'on souhaite multiplier 12345 avec 67890 et afficher le résultat sur la sortie standard. Ce programme est déjà plus compliqué. Les deux valeurs peuvent être stockées dans un registre 64-bit comme `rax` et `rbx`. Pour afficher le résultat, il faut convertir le nombre en chaîne de caractères, c'est ce que fait normalement `%d` dans `printf` mais ici nous sommes en assembleur, nous n'avons pas accès à cette fonction facilement. La stratégie est donc de diviser le nombre par 10 et de stocker le reste dans un tampon. On répète l'opération jusqu'à ce que le quotient soit nul. Ensuite, on inverse le tampon pour obtenir le nombre correct. + +```nasm +section .data + message db "12345 * 67890 = " ; Message à afficher + len equ $-message ; Taille du message + +section .bss + buffer resb 20 ; Tampon pour stocker le résultat converti + +section .text +global _start + +; Fonction int_to_string : Convertit un entier en chaîne de caractères +; Entrée : rdi = entier à convertir, rsi = tampon pour stocker la chaîne (ptr) +; Sortie : rbx = longueur de la chaîne (entier) +int_to_string: + mov rbx, 0 ; Longueur initiale de la chaîne + mov rcx, 10 ; Diviseur (10 pour conversion décimale) + +.convert_loop: + xor rdx, rdx ; Réinitialiser rdx + div rcx ; Diviser rax par 10, quotient dans rax, reste dans rdx + add dl, '0' ; Convertir le chiffre en caractère ASCII + dec rsi ; Déplacer le pointeur dans le tampon + mov [rsi], dl ; Stocker le caractère dans le tampon + inc rbx ; Augmenter la longueur de la chaîne + test rax, rax ; Vérifier si le quotient est zéro + jnz .convert_loop ; Si ce n'est pas zéro, continuer la conversion + + ; Retourner le début de la chaîne + mov rax, rsi ; Placer le pointeur vers la chaîne convertie dans rax + ret + +_start: + ; Afficher le message + ; rax = 1 (sys_write), rdi = 1 (stdout), rsi = message, rdx = len + mov rax, 1 + mov rdi, 1 + mov rsi, message + mov rdx, len + syscall + + ; Multiplication non signée de 12345 par 67890 + mov rax, 12345 + mov rbx, 67890 + imul rax, rbx + + ; Appel de la sous-routine pour convertir l'entier en chaîne + ; rdi = rax (résultat calcul), rsi = buffer + 20 (fin du tampon) + mov rdi, rax + mov rsi, buffer + 20 + call int_to_string + + ; Afficher le résultat (chaîne convertie) + ; rax = 1 (sys_write), rdi = 1 (stdout), rdx = longueur de la chaîne + mov rax, 1 + mov rdi, 1 + mov rdx, rbx + syscall + + ; Fin du programme + ; rax = 60 (sys_exit), rdi = 0 (code de sortie) + mov rax, 60 + xor rdi, rdi + syscall +``` + +### SSE + +Les instructions SSE (*Streaming SIMD Extensions*) sont une extension de l'architecture x86 introduite par Intel en 1999 avec les processeurs Pentium III. Elles permettent d'effectuer des opérations vectorielles sur des registres de 128 bits, ce qui améliore les performances pour les calculs intensifs en parallèle. + +Le SIMD (*Single Instruction, Multiple Data*) est une technique de parallélisme de données qui permet d'effectuer la même opération sur plusieurs données simultanément. + +Les applications possibles sont : filtre d'image, mélange de couleurs, compression d'image, traitement audio, multiplication de matrices, etc. + +On peut par exemple additionner $[a_1, a_2, a_3, a_4]$ avec $[b_1, b_2, b_3, b_4]$ en une seule instruction: + +```nasm +section .data + ; Tableaux de 4 floats (single precision, 32 bits) + a: dd 1.1, 2.2, 3.3, 4.4 + b: dd 5.5, 6.6, 7.7, 8.8 + +add_floats: + ; Charger les 4 floats dans xmm0 et xmm1 + movaps xmm0, [a] ; Charger les 4 floats (32 bits chacun) + movaps xmm1, [b] ; Charger les 4 floats à partir de [b] + + addps xmm0, xmm1 ; Additionner les 4 floats dans xmm0 + + mulps xmm0, xmm0 ; Carré des 4 floats dans xmm0 +``` + +Dans gcc, on peut utiliser les options `-msse` ou `-msse2` pour activer les instructions SSE, alternativement on peut utiliser `-march=native` pour activer les instructions SIMD spécifiques à l'architecture du processeur. + +Hélas, ni gcc ni clang ne semblent pas être très performants. Même avec ces options, il est incapable de vectoriser convenablement les opérations. L'alternative est d'utiliser la bibliothèque intrinsèque de gcc pour les instructions SIMD ``: + +```c +#include + +void compute(const float *a, const float *b, float *c) { +#if 0 + __m128 va = _mm_loadu_ps(a); + __m128 vb = _mm_loadu_ps(b); + __m128 vc = _mm_add_ps(va, vb); + _mm_storeu_ps(c, vc); +#else + c[0] = a[0] + b[0]; + c[1] = a[1] + b[1]; + c[2] = a[2] + b[2]; + c[3] = a[3] + b[3]; +#endif +} +``` + +L'objectif est d'obtenir le code suivant : + +```nasm +compute: + vmovups xmm0, xmmword ptr [rdi] + vaddps xmm0 , xmm0, xmmword ptr [rsi] + vmovups xmmword ptr [rdx], xmm0 + ret +``` + +### AVX + +Les instructions AVX (*Advanced Vector Extensions*) sont une extension de l'architecture x86 introduite par Intel en 2011 avec les processeurs Sandy Bridge. Elles permettent d'effectuer des opérations vectorielles sur des registres de 256 bits, ce qui améliore les performances pour les calculs intensifs en parallèle. + +Voici l'exemple d'une addition de 8-float en utilisant les instructions AVX: + +```nasm +section .data + ; Tableau de 8 floats (single precision, 32 bits) + numbers: dd 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8 + +add_floats: + ; Initialiser les registres + lea rdi, [numbers] ; Charger l'adresse de départ du tableau dans rdi + vpxor ymm0, ymm0, ymm0 ; Initialiser ymm0 à zéro pour la somme + + ; Charger les 8 floats dans ymm1 + vmovaps ymm1, [rdi] ; Charger les 8 floats (32 bits chacun) + ; à partir de [numbers] + + ; Additionner les 8 floats + vaddps ymm0, ymm0, ymm1 ; Additionner les 8 floats dans ymm0 + + ; Réduction des 8 valeurs dans ymm0 pour obtenir la somme finale + vhaddps ymm0, ymm0, ymm0 ; Addition horizontale des paires d'éléments + vhaddps ymm0, ymm0, ymm0 ; Répéter pour réduire encore les valeurs + vhaddps ymm0, ymm0, ymm0 ; Une dernière fois pour obtenir la somme finale + + ; À ce point, la somme finale des 8 floats est dans le registre ymm0[0] +``` + +Sans système de vectorisation, il faut plus d'instruction : + +```nasm +section .data + numbers: dd 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8 + +add_floats: + lea rdi, [numbers] ; Charger l'adresse de départ du tableau dans rdi + + ; Charger et ajouter les flottants avec la FPU + fld dword [rdi] ; Charger le premier float sur la pile FPU + fld dword [rdi + 4] ; Charger le deuxième float + fadd ; Ajouter le deuxième au premier + fld dword [rdi + 8] ; Charger le troisième float + fadd ; Ajouter le troisième au résultat + fld dword [rdi + 12] ; Charger le quatrième float + fadd ; Ajouter le quatrième au résultat + fld dword [rdi + 16] ; Charger le cinquième float + fadd ; Ajouter le cinquième au résultat + fld dword [rdi + 20] ; Charger le sixième float + fadd ; Ajouter le sixième au résultat + fld dword [rdi + 24] ; Charger le septième float + fadd ; Ajouter le septième au résultat + fld dword [rdi + 28] ; Charger le huitième float + fadd ; Ajouter le huitième au résultat + + ; À ce point, la somme est au sommet de la pile FPU (ST(0)) +``` + +## Processeur minimal + +Pour mieux comprendre le fonctionnement d'un processeur, il est intéressant de concevoir un processeur minimal. +Imaginons donc un processeur le plus simple sachant qu'un compromis doit être trouvé entre simplicité et fonctionnalité. + +En effet, un processeur dispose des caractéristiques fondamentales suivantes : + +- Largeur du bus de données +- Nombre d'instructions + +Avec un jeu d'instruction très réduit comme comme sur l'OISC (*One Instruction Set Computer*) ou le CPU Zero, on peut concevoir un processeur minimaliste avec l'instruction *Subtract and Branch if Negative* (SBN). C'est une instruction qui soustrait deux valeurs et saute à une adresse si le résultat est négatif. Avec cette seule instruction, il est possible de simuler toutes les opérations qui seraient normaelemnt effectuées par un ensemble d'instructions plus riche comme l'addition, la soustraction, les comparaisons et les sauts conditionnels. + +La quantité d'instructions dans un processeur classifie sa nature, on distingue les processeurs RISC (*Reduced Instruction Set Computer*) et CISC (*Complex Instruction Set Computer*). Les processeurs RISC ont un jeu d'instructions réduit, ce qui les rend plus simples et plus rapides. Les processeurs CISC ont un jeu d'instructions plus riche, ce qui les rend plus complexes mais plus polyvalents. Il y eut longtemps le débat entre les deux architectures, mais de nos jours, les processeurs modernes sont hybrides, ils combinent les avantages des deux architectures. + +Pour notre processeur, on retiendra une dizaine d'instructions regroupant les opérations de base comme l'addition, la soustraction, les opérations logiques, les sauts conditionnels et les opérations de transfert de données. + +Sur la figure suivante, on peut voir l'architecture minimale d'un processeur avec un jeu d'instructions réduit: + +![Architecture minimale](/assets/images/cpu-zero.drawio) + + +- `ADD` : additionne `A` et `B` et stocke le résultat dans `C` +- `SUB` : soustrait `A` et `B` et stocke le résultat dans `C` +- `AND` : fait un `ET` logique entre `A` et `B` et stocke le résultat dans `C` +- `OR` : fait un `OU` logique entre `A` et `B` et stocke le résultat dans `C` +- `XOR` : fait un `OU EXCLUSIF` logique entre `A` et `B` et stocke le résultat dans `C` +- `JNZ` : si `A` est non nul, saute à l'instruction `B` +- `MOV A, Rx` : copie le registe `Rx` dans `A` +- `MOV B, Rx` : copie le registe `Rx` dans `B` +- `MOV Rx, C` : copie `C` dans le registe `Rx` +- `MOV Rx, Ry` : copie le registe `Ry` dans le registe `Rx` +- `MOV Rx, @Ry,Ry+1` : copie la valeur pointée par le registe `Ry` dans le registe `Rx` + +- L'architecture est 8-bit, et la mémoire est de 256 bytes. +- L'adresse `0x0000` est le point d'entrée du programme. +- L'adresse `0xFFFE` est la queue d'entrée, une valeur lue +- L'adresse `0xFFFF` est la queue de sortie, l'affichage de la sortie, chaque valeur écrite est imprimée en ASCII sur une imprimante. + +OP: `Opcode`, c'est le code de l'instruction à exécuter. Il est chargé dans l'ALU pour sa configuration. Si l'OP est `ADD` l'ALU exécute combinatoirement l'addition de `A` et `B` et le résultat est disponible sur `C`. + +ST: C'est le statut de l'ALU. On y trouve les flags `Z` (zero), `N` (negative), `C` (carry) et `O` (overflow). + +PC: C'est le pointeur d'instruction. Il pointe sur l'adresse de la prochaine instruction à exécuter. + +ADDH et ADDL c'est l'adresse de la mémoire à laquelle on veut accéder. + +`DATA` c'est la valeur lue, ou que l'on souhaite écrire dans la mémoire. + +Ce CPU à un pipeline simple. A chaque coup d'horloge : + +1. PC est copié dans l'adresse de la mémoire à laquelle on veut accéder. +2. RD est activé pour lire la mémoire. +3. La valeur lue est chargée dans OP. +4. L'instruction est exécutée. +5. PC est incrémenté de 1. + +Exemple d'exécution de `MOV A, R0` : + +1. L'OE est activé sur R0. +2. Latch est activé sur A. +3. Latch est désactivé sur A. +4. L'OE est désactivé. + +- 16 instructions donc 4 bits pour l'OP +- 8 registres donc 3 bits pour les registres + + +| Opcode | Instruction | Exemple | Description | +| ---------- | ----------- | ----------- | ------------------------------------- | +| 0b0000xxxx | ADD | | | +| 0b0001xxxx | SUB | | | +| 0b0010xxxx | AND | | | +| 0b0011xxxx | OR | | | +| 0b0100xxxx | XOR | | | +| 0b0101xxxx | JNZ | 0b0101'xxxx | Saut relatif de B (PC += B) si A != 0 | +| 0b01100rrr | MOV A, Rx | 0b0110'0001 | MOV A, R1 | +| 0b01101rrr | MOV B, Rx | 0b0111'0010 | MOV B, R2 | +| 0b0111xrrr | MOV Rx, C | 0b0111'1000 | MOV R0, C | +| 0b10rrrsss | MOV Rx, Ry | 0b1000'0010 | MOV R0, R2 | +| 0b11rrrsss | MOV Rx, @Ry | 0b1100'0010 | MOV R0, @(R2, R3) | + + +## Barrel Shifter + +Le *barrel shifter* est un circuit logique qui permet de décaler un nombre binaire vers la gauche ou vers la droite. Il est utilisé dans les processeurs pour effectuer des opérations de décalage et de rotation sur les données. Sur un processeur 32 bits, le *barrel shifter* peut effectuer des décalages de 1 à 31 bits en une seule opération, ce qui le rend très efficace pour les opérations de manipulation de bits. + +![Barrel Shifter](/assets/images/barrel-shifter.drawio) + +Rappelons qu'un décalage des bits vers la gauche correspond à une multiplication par 2, tandis qu'un décalage vers la droite correspond à une division par 2. Les puissance de 2 sont donc très efficaces car elles correspondent à des décalages de bits pouvant être effectués très rapidement par le *barrel shifter*. + +Un *barrel shifter* peut également gérer la rotation des bits, c'est-à-dire déplacer les bits d'un mot binaire d'un certain nombre de positions vers la gauche ou vers la droite, en déplaçant les bits qui sortent d'un côté à l'autre du mot. Cela permet de réaliser des opérations de permutation et de cryptographie sur les données. + +Dans un processeur ARM, une instruction de décalage peut être incluse directement dans une instruction arithmétique, ce qui permet d'effectuer des opérations de décalage en même temps que des opérations arithmétiques, sans avoir besoin d'une instruction séparée pour le décalage : + +```assembly +ADD r0, r1, r2, LSR #5 ; r0 = (r1 + r2) >> 5 +``` \ No newline at end of file