Very High Speed Integrated Circuit Hardware Description Language/Petit Système monopuce MCPU

Leçons de niveau 15
Une page de Wikiversité, la communauté pédagogique libre.
Début de la boite de navigation du chapitre
fin de la boite de navigation du chapitre
En raison de limitations techniques, la typographie souhaitable du titre, « Very High Speed Integrated Circuit Hardware Description Language : Petit Système monopuce MCPU
Very High Speed Integrated Circuit Hardware Description Language/Petit Système monopuce MCPU
 », n'a pu être restituée correctement ci-dessus.

Vers les systèmes monopuces (SoC)[modifier | modifier le wikicode]

Si l’on désire aller plus loin dans l'intégration de systèmes dans un FPGA, il faut commencer à penser aux microprocesseurs et aux micro contrôleurs. Dans un FPGA vous pouvez insérer un micro contrôleur existant (je veux dire disponible sur le marché en tant que circuit) ou concevoir un processeur complètement dédié.

Nous allons commencer par une architecture dédiée et très simple qui comporte un jeu d'instructions réduit (quatre instructions) mais conçu de manière très intelligente. Il est en effet assez rare de rencontrer un système de 4 instructions capable de réaliser un programme qui réalise autre chose qu'une addition et c’est bien le cas ici, comme nous le montrerons avec un calcul de PGCD.

Étude du miniprocesseur embarqué "MCPU"[modifier | modifier le wikicode]

Tim Boescke, cpldcpu@opencores.org a publié en 2001-2004 un miniprocesseur (appelé MCPU par la suite) de quatre instructions (MCPU chez opencores) destiné à tenir dans un CPLD. Ses quatre instructions (on parle de jeu d'instructions) sont les suivantes :

Jeu d'instructions 8-bit pour MCPU
Opcode (binaire) Mnemonic Description
00AA AAAA NOR Accu = Accu NOR mem[AA AAAA]
01AA AAAA ADD Accu = Accu + mem[AA AAAA] + MAJ retenue
10AA AAAA STA mem[AA AAAA] = Accumulateur
11DD DDDD JCC Positionne le PC à DD DDDD quand retenue = 0 + RAZ retenue

Évidemment on a utilisé dans ce tableau

Seul l’adressage direct est implanté dans cette architecture et c’est bien suffisant pour un début.

Ce qui fait le grand intérêt de cette architecture c’est qu’il est possible de définir par dessus ces instructions des macros instructions de manière assez subtile :

Jeu de macro instructions pour MCPU
Macro Assembleur Description
CLR NOR allone Accu = 0 (allone doit contenir 0xFF)
LDA mem NOR allone, ADD mem Accu = mem[AA AAAA], allone doit contenir 0xFF
NOT NOR zero inverse le contenu de l'accumulateur, zero doit contenir 0x00
JMP dest JCC dest, JCC dest saut inconditionnel à dest
JCS dest JCC *+2, JCC dest saut si retenue
SUB mem NOR zero, ADD mem, ADD one Accu = mem[AA AAAA] - Accu, one doit contenir 0x01
MOV src,dest NOR allone, ADD src, STA dest déplacement de la mémoire src vers dest

Vous disposez à ce point de 11 instructions (4 instructions d'origine + 7 macros), ce qui est suffisant pour un calcul de PGCD (voir aussi le chapitre précédent pour le PGCD).


Transformer notre FSMD en programme[modifier | modifier le wikicode]

Dans cette section, nous désirons reprendre un FSMD qui calculait un PGCD et le transformer en programme.

Il est toujours possible de transformer un FSMD ou un graphe d'état en un ensemble d'instructions. Il faudra ensuite imaginer une architecture capable de réaliser un tel programme.

Nous ne sommes pas tout à fait dans cette situation puisque notre architecture existe, nous venons tout juste de la présenter. Pour notre exemple de calcul de PGCD, cela peut se faire avec un programme en assembleur du style (sans utiliser les macros) :

;********* programme non testé ******
;USE "cpu3.inc"
start:
	NOR 	allone
	ADD	allone	;LDA	allone
	ADD	b
	JCC	end ; fini si B=0
Bdif0:	
	NOR	allone
	ADD	a
	STA	t	;MOV	a,t	; t <- A
;****** T<B ?
rem:		
	NOR 	allone	;CLR		
	LDA	b
	ADD	one	;Akku = - b
	ADD	t	;Akku = t - b
			;Carry set when akku >= 0
	JCC	Tinfb
	STA	t	;t <- t-b
	JCC	rem
	JCC	rem	;JMP rem
Tinfb:
	NOR	allone
	ADD	b
	STA	a	;MOV	b,a	a <- b
	NOR	allone
	ADD	t
	STA	b	;MOV	t,b	b <- t
	JCC	start
	JCC	start	;JMP start
end:
	JCC	end
	JCC	end	;JMP end
a:
	DCB	(126)
b:
	DCB	(124)
t:	
	DCB	(0)
allone:
	DCB	(255)
zero:
	DCB	(0)
one:
	DCB	(1)

Ce programme est facile à comprendre pour qui a un peu d'expérience car il reprend la technique utilisée dans l' unité de contrôle du chapitre précédent (nous avons modifié le programme original de Tim Boescke pour cela). Les données "allone", "zero" et "one" ainsi que les macros peuvent être définies dans un fichier à inclure comme dans le programme original.

Transformer notre programme en ...[modifier | modifier le wikicode]

Quand on a l'habitude de faire fonctionner des micro contrôleurs on sait qu’il suffit de compiler un programme en fichier hex qu'un programmateur permettra d'envoyer à une EPROM. Mais ici, comment et où va finir notre programme ? En général on dispose d'un programme convertisseur qui permet de transformer notre fichier hex en VHDL. Ce VHDL finira dans des blocs logiques ou dans de la RAM (il y en a dans tout FPGA moderne).

Ainsi mettre notre MCPU dans un FPGA ou dans un CPLD (comme initialement prévu par son concepteur) est un peu différent : dans le CPLD il est très probable que le programme soit externe (EPROM externe par exemple), alors que dans un FPGA on s'arrangera pour laisser le programme en interne dans une RAM/ROM prévue à cet effet.


Liens internes[modifier | modifier le wikicode]

  • RAM et FPGA d'un autre projet aborde le problème des RAMs dans un FPGA.

Exercice 1[modifier | modifier le wikicode]

Le programme VHDL de description original de Tim Boescke pour décrire son architecture est tellement simple que nous le donnons maintenant complètement :

-- Minimal 8 Bit CPU
-- rev 15102001
-- 01−02/2001 Tim Boescke
-- 10 /2001 slight changes for proper simulation.
-- t.boescke@tuhh.de
library ieee; 
use ieee.std_logic_1164.all; 
use ieee.std_logic_unsigned.all; 

entity CPU8BIT2 is 
	port (	data:	inout	std_logic_vector(7 downto 0); 
		adress:	out	std_logic_vector(5 downto 0); 
		oe:	out	std_logic; 
		we:	out	std_logic;	-- Asynchronous memory interface 
		rst:	in	std_logic; 
		clk:	in	std_logic); 
end; 

architecture CPU_ARCH of CPU8BIT2 is 
	signal	akku:	std_logic_vector(8 downto 0);	-- akku(8) is carry ! 
	signal	adreg:	std_logic_vector(5 downto 0); 
	signal 	pc:	std_logic_vector(5 downto 0); 
	signal	states:	std_logic_vector(2 downto 0); 
begin 
	process(clk,rst) 
	begin 
	 if (rst = '0') then 
		adreg	<= (others => '0');-- start execution at memory location 0 
		states	<= "000"; 
		akku <= (others => '0'); 
		pc <= (others => '0'); 
	 elsif rising_edge(clk) then 
		-- PC / Adress path 
		if (states = "000") then 
			pc	<= adreg + 1; 
			adreg	<= data(5 downto 0); 
		else	 
			adreg	<= pc; 
		end if; 
		-- ALU / Data Path 
		case states is 
		 when "010" => akku <= ("0" & akku(7 downto 0)) + ("0" & data); 	-- add 
		 when "011" => akku(7 downto 0) <= akku(7 downto 0) nor data;	-- nor 
		 when "101" => akku(8) <= '0';-- branch not taken, clear carry 
		 when others => null;	-- instr. fetch, jcc taken (000), sta (001) 
		end case;						 
		-- State machine 
		if (states /= "000") then states <= "000"; 	-- fetch next opcode 
		elsif (data(7 downto 6) = "11" and akku(8)='1') then states <= "101";	-- branch not taken 
		else states <= "0" & not data(7 downto 6);-- execute instruction	 
		end if;	 
	 end if; 
	end process; 
	-- output 
	adress	<= adreg; 
	data 	<= "ZZZZZZZZ" when states /= "001" else akku(7 downto 0); 
	oe <= '1' when (clk='1' or states = "001" or rst='0' or states = "101") else '0'; 	-- no memory access during reset and 
	we <= '1' when (clk='1' or states /= "001" or rst='0') else '0'; 			-- state "101" (branch not taken) 	 
end CPU_ARCH;

Cette architecture utilise une seule mémoire pour les données et le programme : ils peuvent donc être mélangés dans une même mémoire. Répondre aux questions suivantes :

  1. Le compteur programme destiné à chercher les instructions en mémoire programme est sur 6 bits. Quelle est la taille de la mémoire programme sachant que, comme le montre le tableau des instructions, les opcodes sont sur 8 bits ?
  2. Dans le tableau ci-dessus, AAAAAA désigne une adresse mémoire en binaire sur 6 bits tandis que DDDDDD désigne une destination sur aussi 6 bits. Quelle est la taille de la mémoire donnée adressable ?
  3. L'astuce de cette architecture est qu’il est possible de définir par dessus ces instructions des macros instructions (ensemble d'une ou plusieurs instructions) de manière assez subtile. Pouvez-vous donner l'opcode de "JMP dst" ?
  4. Le signal "Akku" représente le registre accumulateur, là où se trouve le résultat de toute exécution d'instruction. Pourquoi est-il sur 9 bits au lieu de 8 ?
  5. La machine d'états qui séquence le processeur est décrite après le commentaire "State machine" sur 3 lignes. Son état est décrit par le signal "state". Combien d'états (au maximum) peut comporter cette machine ?
  6. Quel est l'état futur de l'état "001" ?
  7. Chercher les équations de récurrences de cette machine d'états.
  8. Assembler manuellement le programme du PGCD, puis transformer manuellement le binaire en mémoire ROM VHDL. Insérer cette ROM avec le processeur et tester à l'aide d'une simulation.

Nous allons nous intéresser maintenant à la réalisation de ce processeur en architecture Harvard, c'est-à-dire en séparant la mémoire donnée de la mémoire programme. L'intérêt est de réaliser un processeur pouvant exécuter une instruction par période d'horloge, mais sa compréhension nécessite un certain recul sur l'architecture des chemins de données dans les processeurs.

Autre code source de MCPU[modifier | modifier le wikicode]

Un membre du forum stackoverflow a écrit une version un peu plus lisible de MCPU en VHDL :

Réalisation du MCPU en mono cycle (un cycle d'horloge par instruction)[modifier | modifier le wikicode]

Le travail demandé dans cette section est important pour simplement doubler la vitesse de notre processeur puisque toutes les instructions étaient réalisées en deux cycles d'horloge et vont l'être maintenant en un seul cycle. Il est même possible que la première version puisse tourner avec une fréquence un peu plus grande et qu'ainsi le gain ne soit pas total.

Pourtant si vous désirez écrire votre propre cœur embarqué nous pensons que cette deuxième façon est plus moderne : la très grande majorité des architectures modernes sont des architectures Harvard.

Cette section est difficile à comprendre. Même si elle ne fait intervenir que des notions du niveau indiqué, il est conseillé d'avoir du recul sur les notions présentées pour bien assimiler ce qui suit. Cependant, ce contenu n'est pas fondamental et peut être sauté en première lecture.

Dans une réalisation mono cycle un processeur doit accéder aux instructions et aux données indépendamment et chaque instruction se réalise en un cycle d'horloge. C'est ce que l’on appelle l'architecture Harvard qui propose une mémoire pour les données et une mémoire pour le programme comme déjà expliqué.

Si l’on veut rapidement réaliser une architecture programmable mono cycle, il faut d’abord se pencher sur le chemin de données. Concevoir le chemin de données est un processus incrémental. On commence toujours par le séquencement des instructions (recherche des instructions en mémoire), puis à chaque étape on examine une classe d'instructions et on essaye de construire une portion du chemin de donnée qui peut exécuter cette classe d'instructions. Nous allons le détailler maintenant pour notre architecture MCPU.

Séquencement du programme[modifier | modifier le wikicode]

Le déroulement d'un programme se fait par recherche de l'instruction "Instruction Fetch" et incrémentation du PC (compteur programme ou compteur ordinal) pour pouvoir aller chercher l'instruction suivante. Voici le schéma correspondant :

Le compteur ordinal

Instructions NOR et ADD[modifier | modifier le wikicode]

Ces deux instructions sont dans le même groupe car elles vont chercher toutes les deux une donnée en mémoire. Voici le schéma correspondant :

Nos deux premières instructions

Vous voyez apparaître dans cette figure une Unité Arithmétique et Logique ou ALU qui est responsable de la réalisation de deux instructions. Cela signifie qu'elle possède à ce stade un seul fil de sélection non présenté dans le schéma. Un registre supplémentaire, l’accumulateur noté "AC", apparaît maintenant dans notre nouvelle architecture.


Séquencement de NOR et ADD[modifier | modifier le wikicode]

Cela consiste tout simplement à associer les deux schémas précédents :

Séquencement de nos deux premières instructions

Instruction STA ou écriture dans la mémoire données[modifier | modifier le wikicode]

L'instruction STA

Évidemment l'adresse provient de l'instruction.

Séquencement des 3 instructions[modifier | modifier le wikicode]

On assemble les deux derniers schémas :

Séquencement de nos trois premières instructions

Un soin tout particulier devra être pris pour l’ensemble AC + ALU + DMUX si l’on veut garder la valeur dans AC (front d'horloge inévitable).

Instruction de contrôle et son chemin de donnée[modifier | modifier le wikicode]

L'instruction de contrôle unique est JCC qui a la particularité de mettre la retenue à 0 après son exécution.

Notre seule et unique instruction de contrôle : JCC

Jusqu'à présent, le bit de retenue n'a pas été ajouté au schéma pour raisons de simplifications.

Ensemble des instructions[modifier | modifier le wikicode]

L'ensemble des instructions peut être réalisé par le chemin de données suivant:

Architecture complète

La fonction combinatoire "Fct" détecte l'instruction JCC et la retenue à 0.


Réalisation de l'unité de contrôle[modifier | modifier le wikicode]

L'unité de contrôle permet de générer "wr_mem", rd_mem", "ac_src" et "pc_src" à partir des bits d'instructions et de la retenue. C'est donc tout simplement une super-fonction combinatoire remplaçant "Fct".

Exercice 2[modifier | modifier le wikicode]

Implanter complètement l'architecture MCPU monocycle et la tester avec le programme PGCD.

Voir aussi[modifier | modifier le wikicode]

Pour aller plus loin, jetez un coup d'œil sur les processeurs et aussi fonctionnement appelé pipeline.