Pourquoi les programmes et les données peuvent être placés dans la même mémoire

Dans la leçon précédente, nous avons vu le tableau complet de l'architecture de von Neumann—cinq composants principaux et trois bus.

Dans cette leçon, nous nous concentrons sur son idée la plus révolutionnaire :considérer les instructions de programme comme un type particulier de données et les stocker avec les données dans la même mémoire。


Analogie inspirée de la vie courante : partitions et lecteurs de musique

Imaginez deux appareils de musique différents :

Appareil A : Boîte à musique

Une boîte à musique à ressort contient un cylindre métallique couvert de minuscules picots. Chaque picot pince une dent d'un peigne en acier pour produire une note.

Vous voulez changer l'air ? Vous devez démonter la boîte à musique et remplacer le cylindre par un autre dont les picots sont disposés différemment. Le cylindre lui-même est une fusion physique du « programme » et du « lecteur ».

Appareil B : Application de musique sur smartphone

Votre téléphone stocke des centaines de fichiers MP3. Touchez-en un et il se lance.

Le fichier MP3 (programme/données) et l'application de lecture (exécuteur) sont séparés—pour changer de chanson, il vous suffit de changer le fichier, pas le téléphone.

Une boîte à musique est un ordinateur pré-von Neumann : le programme et le matériel sont liés, et changer de tâche signifie changer de machine.

Un smartphone est un ordinateur post-von Neumann : les programmes sont un contenu remplaçable dans le stockage, et le matériel est une plateforme d’exécution polyvalente.

Dimension de comparaisonBoîte à musique (commande par programme externe)Smartphone (programme stocké)
Approche du programme enregistréFigé dans la structure mécaniqueStocké en mémoire sous forme de données
Changer de morceauDémonter et remplacer le tambourIl suffit de toucher l’écran
Le matériel est-il universel ?Une boîte à musique = une seule mélodieUn téléphone = des mélodies illimitées
FlexibilitéPresque zéroextrêmement élevé

Ce que von Neumann a fait était exactement ceci : il a extrait la « partition » du matériel et l'a transformée en un « fichier de données » qui pouvait être librement remplacé. Dès lors, le matériel était le matériel, et le logiciel était le logiciel.


Contexte historique : du « contrôle externe du programme » au « programme enregistré »

2.1 Les ordinateurs avant von Neumann

Au début des années 1940, certains ordinateurs électroniques étaient déjà utilisés, mais ils partageaient tous un problème commun :

ENIAC (1946)—Le premier ordinateur électronique à usage général au monde, d'une rapidité stupéfiante, mais chaque fois que la tâche de calcul changeait, les ingénieurs devaient passer plusieurs joursà brancher et débrancher manuellement des centaines de câbles et à régler des milliers d'interrupteursPour « reprogrammer ».

Harvard Mark I (1944)—Les programmes étaient saisis via une bande de papier perforée, mais le stockage du programme (lecteur de bande) et le stockage des données étaient deux dispositifs physiquement séparés ; le CPU ne pouvait pas accéder aux instructions du programme de la même manière qu'il accédait aux données.

Défauts communs de ces machines :

  • Changer les programmes était extrêmement chronophage — cela exigeait un recâblage manuel du matériel ou le remplacement des supports physiques.
  • Les instructions du programme ne pouvaient pas être modifiées par le programme lui-même — cela limitait l'émergence de techniques de programmation avancées (telles que les compilateurs et les éditeurs de liens).
  • La conception du matériel était complexe — différentes tâches pouvaient nécessiter différentes configurations matérielles.

2.2 La percée de von Neumann

En 1945, von Neumann proposa une idée audacieuse dans le « First Draft of a Report on the EDVAC » :

Encoder les instructions d'un programme sous forme de nombres binaires et les stocker avec les données dans la même mémoire adressable.

Cette idée apporta trois avantages décisifs :

  1. Unification matérielle: le CPU n'a pas besoin de savoir si un emplacement mémoire donné contient une instruction ou une donnée — il lit et écrit simplement par adresse. Cela a grandement simplifié la conception matérielle.
  2. Le logiciel existe indépendamment: les programmes sont devenus des « fichiers » qui pouvaient être stockés, copiés et transférés. Ce fut la naissance du concept de « logiciel ».
  3. Les programmes peuvent se modifier eux-mêmes: un programme peut modifier ses propres instructions ou celles d'autres programmes exactement comme il agit sur des données. Cela a posé les fondations de technologies marquantes telles que les assembleurs, les compilateurs et les systèmes d'exploitation.

Principe détaillé : la coexistence des instructions et des données en mémoire

3.1 Schéma du module de mémoire : qu'est-ce qui est une instruction ? qu'est-ce qui est une donnée ?

La figure ci-dessous montre un instantané d'une région de mémoire. Chaque cellule est une unité de stockage avec un numéro d'adresse unique.

Veuillez observer—Les instructions et les données sont physiquement exactement identiques ; ce ne sont que des 0 et des 1.La seule façon de les distinguer est de savoir qui y accède, et quand.

Instruction color gradient Data color gradient Memory module background Main memory (RAM)header Address Stored content Type Actual meaningData row Address 0: instruction LOAD A 5 0x0000 10110001 00000101 [ Instruction ] LOAD A, 5Address 2: Data 5 (value of A) 0x0001 00000000 00000101 [ Data ] Immediate value 5Address 3: Instruction LOAD B 3 0x0002 10110010 00000011 [ Instruction ] LOAD B, 3Address 5: Data 3 (value of B) 0x0003 00000000 00000011 [ Data ] Immediate value 3Address 4: Instruction ADD A B 0x0004 00100000 00000001 [ Instruction ] ADD A, BAddress 6: Instruction STORE A 0x0005 00110000 01100100 [ Instruction ] STORE 100Address 100: data storage result 0x0064 00000000 00001000 [ Data ] Calculation result: 8Legend area Legend: Blue highlight = instruction unit (opcode + operand) Orange highlight = data unit (immediate values, variable values, calculation results) Note: Instructions and data are exactly the same in physical storage; they are all binary bits. Only the PC pointer when the CPU fetches an instruction determines whether it is an instruction or data.

3.2 Question clé : comment le CPU distingue-t-il les instructions des données ?

D'après le diagramme ci-dessus, un fait clé apparaît : les instructions et les données sont indiscernables au niveau du stockage physique.Complètement indiscernables—Ce sont toutes des chaînes de bits binaires.

Alors, comment le CPU sait-il s'il doit traiter une unité de mémoire comme une instruction ou comme une donnée ?

La réponse est simple :Cela dépend du moment de l'accès et de qui y accède.

  • Étape de recherche d'instruction: lecompteur de programme (PC)du CPU fournit l'adresse, et l'unité de commande envoie un signal de commande « lire instruction ». À ce stade, le contenu lu depuis la mémoire est traité comme une « instruction » et envoyé au registre d'instruction pour décodage.
  • Étape d'exécution: le CPU calcule l'adresse de l'opérande à partir du résultat de décodage (qui peut provenir d'un registre ou d'une valeur immédiate dans l'instruction), et le contrôleur émet un signal de commande « lire données ». À ce stade, le contenu lu depuis la mémoire est envoyé à l'ALU en tant que « données » pour participer au calcul.

En une phrase : pour une même adresse mémoire, lorsque le PC pointe vers elle, c'est une « instruction » ; lorsqu'une adresse d'opérande pointe vers elle, ce sont des « données ». C'est l'essence du « programme enregistré » — les instructions et les données sont physiquement indiscernables et logiquement distinguées par le contexte d'accès.


Démonstration interactive : explorez vous-même les instructions et les données en mémoire

Le code Python ci-dessous crée un système de mémoire qui peut être exploré de manière interactive — vous pouvez écrire des instructions ou des données, puis afficher le contenu de n'importe quelle adresse en mémoire et déterminer s'il s'agit d'une instruction ou d'une donnée.

Exemple

"""
Démonstration du stockage mixte des instructions et des données en mémoire (exemple de démo)
Démonstration du concept de « programme enregistré » : les instructions et les données dans une même mémoire ne peuvent pas être distinguées par leur contenu.
"""


class MemoryInspector:
    Inspecteur de mémoire — montre intuitivement la coexistence des instructions et des données en mémoire

    def __init__(self, size=256):
        # Initialiser la mémoire : chaque cellule stocke une valeur entière (simulant 8 bits)
        self.memory = [0] * size
        # Tableau d'étiquettes : enregistre le type de chaque adresse — 'instruction' ou 'data'
        # Remarque : ce marqueur n'existe pas dans le matériel réel ! Il est ici uniquement pour faciliter la démonstration.
        self.type_tags = ['empty'] * size
        # Enregistrer les explications mnémotechniques pour chaque adresse (également pour faciliter la démonstration)
        self.annotations = [''] * size

    def write_instruction(self, address, opcode, operand, annotation=""):
        """Écrire une instruction en mémoire (occupe deux unités : opcode + operand)"""
        self.memory[address] = opcode
        self.type_tags[address] = 'instruction'
        self.annotations[address] = annotation

        self.memory[address + 1] = operand
        self.type_tags[address + 1] = 'instruction'
        self.annotations[address + 1] = f" └ Opérande: {operand}"

    def write_data(self, address, value, annotation=""):
        """Écrire une unité de données en mémoire"""
        self.memory[address] = value
        self.type_tags[address] = 'data'
        self.annotations[address] = annotation

    def inspect(self, start=0, end=16):
        """Afficher le contenu de la mémoire dans une plage spécifiée"""
        print("=" * 68)
        print(f'{Adresse':>6} | {'Valeur stockée':>8} | {'Binaire':>10} | {'Type':>12} | Signification)
        print("-" * 68)
        for addr in range(start, min(end, len(self.memory))):
            val = self.memory[addr]
            binary = format(val, '08b')  # Représentation binaire sur 8 bits
            tag = self.type_tags[addr]
            anno = self.annotations[addr]

            # Utiliser des marqueurs pour distinguer les couleurs d'affichage (pas de couleur dans le terminal, utiliser des symboles à la place)
            marker = "[I]" if tag == 'instruction' else \
                     "[D]" if tag == 'data' else \
                     "[ ]"

            if val != 0 or tag != 'empty':
                print(f" {addr:4d} | {marker} {val:5d} | {binary} | {tag:>12} | {anno}")

        print("=" * 68)
        print([I] = Instruction [D] = Données [ ] = Libre\n")

    def compare_bytes(self, addr1, addr2):
        """
Comparez les octets bruts de deux adresses — vous constaterez qu'elles sont indiscernables au niveau binaire
        """

        v1 = self.memory[addr1]
        v2 = self.memory[addr2]
        b1 = format(v1, '08b')
        b2 = format(v2, '08b')

        print(f"\n--- Comparaison de l'adresse {addr1} et de l'adresse {addr2} ---")
        print(fAdresse {addr1}: Binaire {b1} (Type: {self.type_tags[addr1]}))
        print(fAdresse {addr2}: Binaire {b2} (Type: {self.type_tags[addr2]}))
        if b1 == b2 and self.type_tags[addr1] != self.type_tags[addr2]:
            print(f>>> Constat clé: Les contenus binaires de ces deux adresses sont exactement les mêmes ({b1})!)
            print(f>>> Mais leurs types sont différents: l'adresse {addr1} est {self.type_tags[addr1]},)
            print(f>>> L'adresse {addr2} est {self.type_tags[addr2]}.)
            print(f>>> Cela montre que — avec seulement le contenu binaire, il est impossible de distinguer les instructions des données!)
        else:
            print(f"Les contenus de ces deux adresses sont différents.")

    def simulate_instruction_data_ambiguity(self, addr):
        """
Démontrer que la même valeur binaire est interprétée comme ayant des significations différentes selon les contextes
        """

        val = self.memory[addr]
        binary = format(val, '08b')
        print(f"\n--- Démonstration de l'ambiguïté de l'adresse {addr} ---")
        print(fValeur binaire stockée : {binary} (décimale : {val}))
        print(f"")
        print(f"Si le PC du CPU pointe ici (étape de récupération d'instruction) :")
        print(f" -> cet octet est interprété comme l'opcode d'instruction")
        print(f" -> Par exemple, {binary} pourrait représenter une instruction 'LOAD'")
        print(f"")
        print(f"Si l'ALU fait référence ici via l'adresse de l'opérande (étape d'exécution) :")
        print(f" -> Cet octet est interprété comme 'donnée'")
        print(f-> Par exemple, la valeur décimale {val} n'est qu'une valeur numérique ordinaire)
        print(f"")
        print(f"La même séquence de bits, lue par différents rôles, a des significations complètement différentes !")


# ================================================================
# Démo 1 : Construction d'un stockage hybride en mémoire
# ================================================================
print(>>> Démo 1 : Mélange d'instructions et de données en mémoire\n")
mem = MemoryInspector()

# Écrire une séquence d'instructions
mem.write_instruction(0, 0xB1, 5, "LOAD A, 5")
mem.write_instruction(2, 0xB2, 3, "LOAD B, 3")
mem.write_instruction(4, 0x20, 1, "ADD A, B")
mem.write_instruction(6, 0x30, 100, "STORE 100")

# Écrire des données
mem.write_data(100, 8, Résultat du calcul : 5 + 3 = 8)

# Afficher un aperçu de la mémoire
mem.inspect(0, 8)
mem.inspect(100, 102)

# ================================================================
# Démo 2 : Ambiguïté instruction/donnée d'une même valeur
# ================================================================
print(>>> Démo 2 : La même valeur binaire peut être une instruction ou une donnée\n")

# Écrire intentionnellement une valeur 5 à l'adresse (zone d'instructions), et aussi écrire 5 à l'adresse 200 (zone de données)
mem.write_instruction(10, 5, 0, "Ce 5 est l'opcode")
mem.write_data(200, 5, "Ce 5 est la valeur de donnée")

mem.compare_bytes(10, 200)

# Démonstration de l'ambiguïté
mem.simulate_instruction_data_ambiguity(10)

# ================================================================
# Démo 3 : Concept de code auto-modifiant (sujet avancé)
# ================================================================
print(">>> Démo 3 : Concept de programmes auto-modifiants\n")
print("Comme les instructions et les données sont dans la même mémoire, un programme peut modifier ses propres instructions !")
print(C'est exactement la capacité apportée par l'architecture à programme enregistré.)
print("")
print(Par exemple, le programme peut modifier l'instruction STORE 100 à l'adresse 6 en STORE 200 :)
print(f"Avant modification, adresse 6 : {mem.memory[6]} (opcode)")
print(f"Avant modification, adresse 7 : {mem.memory[7]} (opérande)")

# Le programme se modifie lui-même : changer l'opérande de STORE de 100 à 200
mem.memory[7] = 200
mem.type_tags[7] = 'data'
mem.annotations[7] = Modifié par le programme lui-même : opérande 100 -> 200

print(fAprès modification, adresse 7 : {mem.memory[7]} (l'opérande a changé))
print("")
mem.inspect(6, 8)
print(>>> Cette capacité est le fondement de technologies telles que les compilateurs, les éditeurs de liens dynamiques et les machines virtuelles.)

Exécutez ce programme et vous verrez clairement : les instructions et les données ne sont que des "rôles" différents dans un même bloc de mémoire. Elles partagent le même mécanisme de stockage et d'adressage — c'est l'élégance la plus subtile au cœur de l'architecture de von Neumann.

3.3 Démonstration interactive : cliquez sur une cellule mémoire pour afficher son contenu (exemple)

Ci-dessous se trouve une simulation d'une région mémoire de 64 octets.Les cellules bleues représentent des instructions,Les cellules orange représentent des données, les zones grises sont libres. Survolez n'importe quelle cellule pour voir sa valeur brute stockée,Cliquez sur la celluleafficher l’explication détaillée — y compris si l’adresse stocke réellement une « instruction » ou une « donnée », ainsi que sa signification réelle.

Instruction Donnée Libre (Vide)
Cellules de la grille générées dynamiquement par JS
Cliquez sur la cellule mémoire ci-dessus pour voir le type et la signification du contenu stocké à cette adresse

Comparaison avec l’architecture Harvard

L’architecture de von Neumann n’est pas la seule architecture informatique. Son principal « concurrent » est l’architecture Harvard.Architecture Harvard。

Dimension de comparaisonArchitecture de von NeumannArchitecture Harvard
MémoireMémoire unique, les instructions et les données partagentDeux mémoires indépendantes, les instructions et les données sont séparées
busUn seul ensemble de bus d’adresses/de données ; les instructions et les données partagent le bus en multiplexage temporelDeux ensembles indépendants de bus ; les instructions et les données sont accessibles simultanément
AvantagesConception matérielle simple, utilisation élevée de la mémoire, logiciel flexibleLa récupération des instructions et des données peut se faire simultanément, plus rapide (adapté au pipeline)
InconvénientsLa récupération des instructions et des données peut entrer en conflit (goulot d'étranglement de von Neumann)Matériel plus complexe ; le stockage des instructions et le stockage des données ne peuvent pas être alloués dynamiquement
Applications typiquesOrdinateurs à usage général tels que les PC, les serveurs, etc.MCU embarqués, DSP processeurs de traitement numérique du signal

Dans les implémentations réelles, les CPU modernes fusionnent souvent les deux architectures : en externe, ils présentent le modèle de mémoire unifié de l'architecture von Neumann (pour la commodité de la programmation), tandis qu'en interne, ils utilisent des caches d'instructions et de données séparés au niveau du cache L1 (empruntant les avantages de vitesse de l'architecture Harvard). C'est ce qu'on appelle l'« architecture Harvard modifiée ».

autres extensions