Projet Red Spice
Cartouche
| Champ | Valeur |
|---|---|
| Auteur·e | Élise |
| Édition | 2024-11-14 |
| Durée | 3 semaines |
| Taille des équipes | 2~3 personnes |
| Rendu | via git, dépôt $YEAR_spice, droits en lecture à delivery_collector |
Règlement
Prenez connaissance du règlement ici
L’intégralité de la bibliothèque standard est autorisée. Vous êtes encouragés à vous servir des outils qu’elle vous propose s’ils résolvent un de vos problèmes. Votre objectif c’est d’éviter de ré-inventer la roue quand ça n’est pas nécessaire.
Certains crates sont proposés dans le sujet. Vous avez le droit de vous servir de toutes leurs fonctionnalités.
Vous pouvez rechercher des alternatives à ces crates, voir des crates qui vous proposent des fonctionnalités complètement différentes. Si ces crates affectent l’écriture de votre algorithme ou l’architecture de votre programme, vous devez les faire valider en amont.
Barème
| Critères | Points accessibles |
|---|---|
| Simulation | 3 |
| Shell de commandes (simulation, input=value, display, loop, debug) | 3 |
| Composants implémentés | 3 |
| Parsing fichier de configuration | 3 |
| Qualité du code, facilité de lecture | 3 |
| Architecture, découpage | 3 |
| Prévention des boucles infinies | 2 |
| Malus maximum (non respect du trait, ré-interprétation d’une consigne, etc.) | -5 |
| Total | 20 |
Introduction
Jusqu’à maintenant, vous avez programmé dans plusieurs langages compilés comme le C ou le Rust. Ils sont compilés en assembleur qui fini lui-même transformé en langage machine pour être compris par un microprocesseur.
Si on descend encore plus bas que les microprocesseur on trouve les chipset (lit. jeux de puces) et les puces électroniques elles-mêmes (ou circuits intégrés).
<img src=“https://upload.wikimedia.org/wikipedia/commons/8/80/Three_IC_circuit_chips.JPG” width=“300px” >
Les puces électroniques reproduisent elles-mêmes le comportement de circuits formés à partir de composants électroniques de base comme des diodes et des transistors.
En assemblant ces composants on est capable de fabriquer des portes logiques. Vous devez déjà avoir entendu parler de la porte ET ou la porte OU. Elles prennent toutes les deux deux entrées et ont chacune une sortie. Ces entrées et sorties sont des broches physiques de la puce (qu’on appelle pin en anglais).
Les états vrai et faux se distinguent par un potentiel électrique (un voltage) différent. En fonction du circuit sur lequel on travaille, vrai peut correspondre à 3 V, 5 V ou 12 V (en fonction de VCC). Faux est en général défini par VSS qui est en général mis à la masse, à 0 volts. VCC et VSS sont nos tensions vraie et fausses de référence.
Lorsqu’on étudie et que l’on travaille avec des portes logiques et des circuits électroniques, on utilise des tables de vérité pour déterminer la sortie que l’on attend pour chaque combinaison d’entrées.
Voici par exemple la table de vérité d’une porte ET :
| Entrée A | Entrée B | Sortie S |
|---|---|---|
| 0 | 0 | 0 |
| 1 | 0 | 0 |
| 0 | 1 | 0 |
| 1 | 1 | 1 |
On voit que la porte ET a une sortie de 1 seulement lorsque ses deux entrées sont a 1. Exactement comme lorsqu’on écrit une condition avec un && dans les langages qui héritent leur syntaxe du C.
La table de vérité d’une diode toute seule, ou d’un fil, ressemble à ça :
| E (entrée) | S (sortie) |
|---|---|
| 0 | 0 |
| 1 | 1 |
Exercice : porte OU
Écrivez la table de vérité d’une porte OU.
Pensez à un if avec un opérateur || :
// when does this return true? or false?
if input_a || input_b {
return true;
} else {
return faise;
}
Exemple de puce
La puce 4081, dont vous avez la documentation, comprend 4 portes ET.

La première porte ET a ses entrées A et B sur les broches 1 et 2. Sa sortie est sur la broche 3.
| Indice de la porte | Borche A | Broche B | Broche en sortie |
|---|---|---|---|
| 1 | 1 | 2 | 3 |
| 2 | 6 | 5 | 4 |
| 3 | 8 | 9 | 10 |
| 4 | 13 | 12 | 11 |
| Sur la broche 7, on trouve le VSS qui se relie à la borne négative de l’alimentation. Sur la broche 14, le VDD qui se relie à la borne positive de l’alimentation. Mais nous n’en aurons pas besoin car nous n’allons pas simuler la physique, ou l’électronique, seulement la logique booléenne. |
Fichier de configuration
.chipsets:
input i0
input i1
4081 and_chip
output out
.links:
i0/1 and_chip/1
i1/1 and_chip/2
and_chip/3 out/1
Dans .chipsets on déclare les chipsets sur lesquels la situation doit travailler et on leur donne un nom.
Dans .links on indique quelles broches de quel composants doivent être reliées par fils. On fait références au composants par le nom qu’on leur a donné dans .chipsets
Trait des composants
https://git.ecole-89.com/eriizu/red_spice_traits
https://crates.io/crates/red_spice_traits
Vos composants doivent tous êtres des structures (avec implémentation) différentes. Ils doivent tous implémenter un même trait, le trait Component.
Le trait Component doit prévoir les méthodes suivantes :
modelrenvoie le nom du composant ;simulaterésout l’état des sorties en fonction des entrées et des composants qui y sont reliés en appelant leur méthodesimulate;get_pin_staterenvoie l’état d’une broche ;set_linklie une broche (en entrée) de ce composant à la broche (en sortie) d’un autre ;
Pour le stockage et les liens entre les composants, vous aurez besoin d’utiliser :
Rcreference counter,RefCell, etBoxpour stocker des éléments de taille différentes qui implémentent un même trait.
Chaque composant doit implémenter std::fmt::Debug en affichant son état interne (chaque entrée ou sortie telle qu’initialisée ou calculée précédemment) et ses liens.
Lorsque vous appelez simulate sur un composant, il doit résoudre l’état de ses sorties en fonction de ses entrées. Si l’entrée d’une porte n’est reliée à rien, sa sortie est non-définie. Si l’entrée d’une porte est reliée à un autre composant, sa méthode simulate doit être appelée.
Sur les composants spéciaux, la méthode simulate n’a aucun effet, sauf pour output qui va se contenter d’appeler le simulate du composant auquel il est relié puis stocker la valeur de la broche à laquelle il est relié.
Composants à implémenter
Pour ce projet vous devez simuler les composants suivants :
- 4001 NOR
- 4011 NAND
- 4013 Bascules
- 4030 XOR
- 4069 NOT
- 4071 OR
- 4081 AND
Leur documentation se trouve ici, et est accessible depuis le réseau de l’école
Vous devez aussi implémenter des composants spéciaux :
input: une unique broche dont l’état peut être défini avant la simulation ;output: une unique broche dont l’état peut être consulté après la simulation ;clock: une unique broche dont l’état change d’une simulation à l’autre (0 puis 1 puis 0 puis 1, etc.) ;true: une unique broche dont l’état est toujours vrai ;false: une seule broche dont l’état est toujours faux.
Shell de commandes (runtime)
Votre simulateur doit pouvoir être démarré avec un fichier de configuration en argument. Il doit prendre en charge les commandes suivantes :
exit: quitte le programme ;display: affiche le numéro d’itération de la simulation et l’état des entrées et sorties du circuit ;input=valeur: affecte à une entrée la valeur donnée (soit 0, 1 ou U) ;simulate: simule une itération du circuit et affecte à la clock son complément ;loop: simule en boucle le circuit jusqu’à ce qu’un signal d’interruption doit reçu ;debug: appelle le dump de tous les composants.
Affichez le prompt > lorsque vous êtes prêts à recevoir une commande.
Une entrée ou sortie non définie s’affiche U.
Une simulation consiste à essayer de définir l’était de toutes les sorties en remontant le graphe.
États logiques
Dans un circuit logique, il est possible que des valeurs indéfinies se glissent. Que ce soit parce qu’un composant à état n’ait pas encore été initialisé lors d’une itération, ou parce qu’une entrée n’a pas été définie par l’utilisateur.
Par exemple : une bascule RS (une mémoire vive d’un seul bit) peut très bien avoir un état vrai ou un état faux au démarrage d’un circuit. On dit que sont état est indéfini tant qu’il n’a pas été initialisé explicitement.
Vous devez représenter les états indéfinis ce qui signifie que vous ne pouvez pas simplement utiliser des booléens.
Servez-vous de l’énumération suivante pour vous représenter l’état des broches des composants.
enum LogicState {
Undefined,
False,
True
}
Si vous essayez de déterminer la sortie d’une porte dont lune des entrées est non-définie, alors sa sortie doit aussi être non-définie.
Explications supplémentaires 2024-12-02
.chipsets:
input i0
input i1
4081 and_chip
output out
.links:
i0/1 and_chip/1
i1/1 and_chip/2
and_chip/3 out/1
Dans ce fichier de configuration on déclare deux input : i0 et i1 dont on doit spécifier les valeurs à l’exécution du simulateur.
On déclare aussi l’existence d’une puce 4081 qu’on appelle and_chip à laquelle on va relier des input et output.
Enfin on déclare un output qui sert au simulateur pour savoir quelle information afficher en sortie mais aussi où commencer la simulation.
Mettons, la broche de i0 est mise à False et la broche de i1 est mise à True.
La simulation se passerai ainsi :
- Le simulateur appelle
simulatesur notre premier et seuloutputpour connaitre son état. - Pour connaitre l’état de sa broche, l’output doit connaitre l’état de la broche 3 du composant auquel il est relié (il s’agit de son seul
link), il appellesimulatesur se composant le4081.- Le
4081pour calculer son état doit connaitre celui des composants auxquels il est relié, il va donc appelersimulatesur chacun d’entre eux (en l’occurrence lesinput)- Les
inputsn’ont rien à faire lors dusimulateleur état étant déjà connu.
- Les
- Le
4081get_pin_statesur les deux composants liés (lesinput) et sauvegarde l’état dei0eti1comme étant celui de ses broches 1 et 2 respectivement. - Le
4081calcule l’état de toutes ces sorties en en utilisant celui des broches en entrées. Pour calculer la broche 3 il va faire un ET logique entre les broches 1 et 2. Si l’une des broches est àUndefinedalors la sortie l’est aussi. En l’occurrence les broches sont àFalseetTrue, donc l’état de la broche 3 prend la valeurFalse.
- Le
- L’
outputget_pin_statela broche 3 du composant auquel il est lié (le4081) et trouve la valeurFalse. - La simulation est terminée. La valeur de
falsepour l’outputest affichée.
Qu’est-ce que l’on apprend de cet exemple ?
- Les liens n’ont besoin de se faire que du côté du composant qui “consomme” l’état. Celui qui est intéressé par l’état. Ainsi sur les
outputon aura nécessairement un lien vers un autre composant mais AUCUN composant n’aura de lien sur unoutput. - Chaque composant doit avoir un tableau qui représente l’état de ses broches sans nécessairement différencier les entrées des sorties.
- Chaque composant doit pouvoir générer un état pour ses sorties en fonction de ses entrées.
- Chaque composant doit, avant de calculer l’état de ses sorties, demander aux composants auxquels il est lié de calculer les siennes. (
simulatepuis on calcule, se qui provoque une cascade d’appel àsimulateavant de calculer quoi que ce soit.)