• Aucun résultat trouvé

Preuves inductives avec l’assistant de preuve Coq

Laurent Arditi et Hélène Collavizza

4. Les techniques de preuve

4.4. Preuves inductives avec l’assistant de preuve Coq

✄✝✙ ✔✖✕✛✗ ✕✁

ex-trait les bits de ✔✖✕✘✗ dont les positions sont entre

et : ✔✍☎✞✟ ✔✖✕✛✗ ✆☛✞ ✄✙ ✔✖✕✘✗ ✂✦✥ ✝✡✠ ✔✍☎✔✟ ✄✝✙ ✔✖✕✛✗ ✕✂ ✆☛✞ ✄✝✙ ✔✖✕✘✗ ☎✞✟ ✄✂ ✄✝✙ ✔✖✕✘✗ ✕✁ ✝✗✝ ☎✄ ☎✞✟ ✔✖✕✘✗ ✕✂ ✆☛✞ ✔✍☎✞✟ ✄✂ ✄✙ ✔✖✕✘✗ ✕✁✏☛

4.4. Preuves inductives avec l’assistant de preuveCoq

Nous présentons dans cette section la dernière technique utilisée. Elle réalise des preuves inductives et demande généralement une intervention humaine. C’est pour-quoi cette technique est utilisée en dernier choix, quand l’exécution symbolique, avec ou sans les *BMDs, échoue.

4.4.1. PourquoiCoq?

Coqest un assistant de preuve [COR 95] pour le -calcul d’ordre supérieur. Ses bases théoriques sont le Calcul des Constructions [COQ 88] étendu avec des défini-tions inductives [COQ 90]. Le développement d’une preuve correspond à la construc-tion d’un -terme ; cette caractéristique est exploitée par la possibilité de synthétiser un programme à partir de la preuve de sa spécification. Le lecteur est invité à se référer à [COR 95] pour une description détaillée de ce système.

Nous avons déjà justifié le besoin de preuves inductives pour vérifier les instruc-tions de boucles dépendantes des données (voir le diagramme de 3.1). Nous donnons ici les raisons qui nous ont fait choisirCoq.

La première raison est que, de notre point de vue, une preuve inductive est la dernière méthode à utiliser car elle demande une intervention humaine. Nous avons donc choisi un assistant de preuve et pas un démonstrateur automatique. L’avantage est que la preuve est réalisée pas à pas par l’utilisateur qui a un contrôle total. À l’opposé, avec un démonstrateur automatique comme Nqthm, il est très difficile de comprendre pourquoi une preuve échoue et de la mener à bien dans ces cas. La seconde raison est que nous avons besoin d’un système basé sur une logique d’ordre supérieur. En effet, dans la modélisation des circuits séquentiels, les composants sont représentés par des fonctions sur le temps. Lorsque l’on décrit un système complet par les connections de ces composants, ces fonctions sont des arguments d’autres fonctions.

Coqrépond à ces exigences et des travaux précédents ont montré qu’il peut être utilisé en milieu industriel pour des vérifications à des niveaux abstraits ( [BOL 95]).

La vérification des circuits semble être une des applications majeures des démons-trateurs de théorèmes et des assistants de preuve. Mais ce n’est pas le cas deCoq

puisque, à notre connaissance, les uniques travaux réalisés dans ce domaine avecCoq

sont dûs à Coupet-Grimal et Jakubiec [COU 96] et Paulin-Mohring [PAU 95] ; ils concernent uniquement des circuits combinatoires ou séquentiels simples décrits au niveau des entiers. Comme nous utilisonsCoqdans un environnement plus général, nous avons tout d’abord dû implémenter tous les types de données utilisés dans notre algèbre : les vecteurs de bits et les mémoires. Les booléens (bool,false,true) et les entiers naturels (nat,0,S,plus,minus,mult) sont modélisés en utilisant les théories standards fournies dans la distribution deCoq.

Nous utilisons la théorie deCoqqui définit les listes polymorphes. Ces listes sont définies par le typelistqui a comme constructeursniletconspour lesquels le premier argument est le type des éléments.

Les principales fonctions prédéfinies sur les listes sont la concaténation (app, concat) et la longueur (length). Nous avons défini un certain nombre de fonc-tions supplémentaires (renversement, sélection d’une sous-liste,✂✁ ). De nombreuses propriétés concernant ces fonctions ont été prouvées. La grande expressivité deCoq

nous a laissé une grande liberté dans la formulation de ces fonctions mais son manque d’automatisme s’est ressenti lors des preuves des propriétés. Cependant, cet inconvé-nient est relatif puisque ce travail n’a pas à être répété : la théorie complète définissant les listes polymorphes, les fonctions associées et leurs propriétés est compilée et peut être réutilisée.

Ainsi, pour définir le type des vecteurs de bits,BV, il suffit d’instancier le type list. En effet, les vecteurs de bits sont des listes dont les éléments sont des bits, un bit étant représenté par un booléen. De même, une mémoire est une liste, de lon-gueur fixe, de vecteurs de bits ayant tous la même taille. On réutilise donc le type des listes polymorphes en instanciant le type des éléments parBV. Les opérateurs de l’algèbre présentée en 4.1 sont des instanciations des fonctions définies pour les listes polymorphes.

L’abstraction de données, réalisée par une conversion des vecteurs de bits vers les entiers naturels, est définie par la fonctionBV_nat.

D’autre part, nous avons axiomatisé en Coq certains opérateurs souvent utili-sés comme modules primitifs. Par exemple, l’addition est définie par une induction structurelle sur les vecteurs arguments et la correction des opérateurssumetcarry s’énonce par exemple comme suit :

V,W:BV BV_nat(concat(sum(V,W),carry(V,W))) = BV_nat(V)+BV_nat(W)

Les définitions du typeBV, de ces fonctions et théorèmes relatifs sont encapsulées dans un module compilé . Ce module est en fait très proche de la bibliothèque

✂✓✤

La fonction inverse – la conversion des naturels vers les vecteurs de bits – n’est pas utile car, dans notre méthodologie de vérifi cation, nous réalisons uniquement une abstraction d’un niveau concret (décrit par des vecteurs de bits) vers un niveau abstrait (décrit par des vecteurs de bits ou des naturels).

wordécrite pour HOL [WON 93]. Il est important de noter que les théorèmes que nous avons prouvés sur les vecteurs de bits correspondent aux règles de simplification de notre système de calcul formel. Le développement de la théorieBVest donc une validation partielle du noyau de simplification de notre système.

4.4.3. Modélisation des microprocesseurs

Il a été montré que la logique d’ordre supérieur est un bon formalisme pour spéci-fier et vérispéci-fier les circuits digitaux [GOR 85]. La vérification formelle avecCoqpeut donc bénéficier de ces idées.

Nous modélisons l’état d’un processeur par un ensemble de fonctions donnant la valeur des composants. Les variables modifiées à l’intérieur de boucles sont repré-sentées par des fonctions récursives sur le nombre de passages dans la boucle . Cette valeur est nulle à l’entrée de la boucle ; elle est incrémentée à chaque passage. Cette approche est assez voisine de celle adoptée avec Nqthm dans [Hun 89]. Coq

permet les définitions de fonctions mutuellement récursives, contrairement aux autres systèmes. On peut ainsi définir une fonction par composant alors que les autres tra-vaux définissent une seule fonction qui manipule l’état global sous forme d’un n-uplet. Notre approche est plus facile à spécifier, plus lisible et les preuves sont souvent plus aisées.

Soient ✒✞✕

✂✁

les registres et mémoires d’un processeur. Chaque

est une fonction définie comme suit :

✄✝ ☎✔✄✆☎❀ ✡✠❀ ✄❂ ❀☞✝ ✂✁ ❀☞✝ ☎✞✄✆☎❀

est la valeur initiale du composant, généralement la valeur d’une entrée ou une valeur don’t care (une valeur symbolique). À l’instant❀☎✎

, la valeur du composant est calculée par

✡✭❀

en utilisant les valeurs de tous les composants à l’instant précédent. Nous voyons ici l’importance des fonctions mutuellement récursives.

Comme nous voulons limiter l’intervention humaine, nous avons défini des al-gorithmes pour générer automatiquement les formes récursives présentées ci-dessus. Cela revient à construire le graphe de contrôle de l’implémentation et à réaliser deux parcours de ce graphe pour détecter les cycles et construire les fonctions.

Une preuve d’une instruction de boucle consiste à montrer qu’un prédicat, ✌✞☛✞☛

, sur les valeurs des composants et/ou des sorties, est vrai quand une condition,

, est vérifiée : ✄❂ ❀☞✝ ✁✂ ❀☞✝✗✝ ✌✞☛✞☛ ✘☎✄ ❀☞✝ ✁✂ ❀☞✝✗✝

Intuitivement, ce théorème indique qu’à l’instant où la condition de terminaison du programme est vraie, le résultat est correct.

La preuve demande souvent une généralisation, particulièrement quand ✌✞☛✞☛

est une fonction primitive comme la multiplication. L’utilisateur doit trouver un invariant de boucle et le prouver. Cet invariant est un prédicat,☎✔✄✆✝

Tamarack-3 8 16 3 16 1

Proc. Anceau 8 14 4 16 4

DP-32 20 10 258 32 2

AVM-1 30 51 35 32 1

MTI 22 149 38 16 5

Tableau 2. Tailles des processeurs que nous avons vérifi és

qui est inconditionnellement vérifié :

☎✔✄✆✝ ✄❂ ❀☞✝ ✂✁ ❀☞✝✗✝

Ce théorème indique qu’à tout instant un résultat partiel est correct. Le théorème final n’est alors qu’une spécialisation de cet invariant.