• Aucun résultat trouvé

Exécution symbolique et simplification

Laurent Arditi et Hélène Collavizza

4. Les techniques de preuve

4.2. Exécution symbolique et simplification

sub( ❆✟✁ ) soustraction

incr(V)équivalent àsum(V,"1") incrémentation and( ❆✟✁

✤✆✤

) opérations logiques bit-à-bit

not( ) négation logique

mux(✡ ❆✟✁

)équivalent à sortie d’un multiplexeur if then

else

BV_nat( )équivalent à☛☞ conversion vers les entiers naturels exts(

) extension à✬ bits

length( ) longueur d’un vecteur de bits lsb( )équivalent à [0:0] bit de poids le plus faible msbs( )équivalent à [1:length( )-1] bits de poids forts BV_null(✬ ) vecteur formé de✬ bits nuls eq( ❆✟✁

)ou ❆✍✌

égalité

Tableau 1. L’algèbre des vecteurs de bits

4. Les techniques de preuve

Avant de présenter les différentes techniques de preuve, nous introduisons l’al-gèbre de vecteurs de bits à la base de toutes ces techniques.

4.1. Algèbre de vecteurs de bits

La vision abstraite d’un processeur opère sur des valeurs entières qui sont codées au niveau concret par des vecteurs de bits. Nous devons donc définir une algèbre sur les vecteurs de bits qui inclut les opérations logiques usuelles : concaténation, décalage, rotation, et logique,✂✂

Puisque notre but est de prouver l’équivalence entre les deux vues du processeur, nous devons aussi fournir des fonctions d’abstraction des données entre les entiers naturels et les vecteurs de bits. Le tableau 1 décrit les principaux opérateurs de cette algèbre.

4.2. Exécution symbolique et simplification

La technique la plus simple que nous utilisons est l’exécution symbolique associée à un système de simplification que nous détaillons ici.

4.2.1. Un évaluateur symbolique de transitions

Le comportement des processeurs est défini par des fonctions de transition d’état. Soit une transition

✂✗❀✁ ✂✡✌

; son exécution se déroule comme suit :

— un appel à la méthodereadrend la valeur de✂✂✌

si elle désigne ou inclut des composants du processeur,

— le système de simplification est appelé pour réduire la valeur rendue sous une

forme canonique,

— la méthodewriteest appelée pour modifier la valeur de ✂❁❀

.

L’évaluateur symbolique tire parti de l’approche orientée-objet : le processus d’éva-luation est générique du fait que les méthodesreadetwritesont associées à chaque classe de composants. C’est par discrimination que la méthode correspondant au type du composant auquel on accède est sélectionnée.

4.2.2. Le système de simplifi cation

Tout système de vérification formelle basé sur les techniques de preuve utilise comme outil de base un moteur de simplification symbolique. En effet, les preuves doivent manipuler les valeurs symboliques en entrée des spécifications. Comme deux niveaux d’un même système sont décrits par des opérations différentes, les valeurs finales symboliques diffèrent par leur syntaxe. Il faut alors vérifier qu’elles sont sé-mantiquement équivalentes. Or cette vérification sémantique peut être réalisée par une mise sous forme canonique, puis par une vérification syntaxique. Pour ce faire, nous avons développé un système de calcul formel dédié à la manipulation des entiers et des vecteurs de bits. Ce système est moins puissant que des démonstrateurs de théorèmes généraux comme HOL, Nqthm, PVS, Larch Prover, OBJ3 ✁✂ mais il est le plus sou-vent suffisant pour la vérification des processeurs. De plus il est bien plus efficace et facile d’utilisation : il est entièrement automatique et plus rapide qu’un système plus général. Les résultats que nous avons obtenus et qui sont donnés en 5.1 confirment cette affirmation. Il est particulièrement bien adapté pour ce problème spécifique où les expressions sont simples mais très nombreuses, et pour lequel l’efficacité et la simplicité doivent supplanter la puissance et la généralité.

D’autres systèmes de preuve utilisent des techniques de réécriture pour simplifier des expressions symboliques (voir par exemple [SEK 89, STA 93]). Nous avons choisi une approche basée sur le calcul formel par souci d’efficacité et pour traiter facilement les opérateurs associatifs et commutatifs.

4.3. *BMD : une représentation symbolique compacte des vecteurs de bits

La seconde technique de preuve que notre système utilise est aussi basée sur l’exé-cution symbolique mais s’appuie sur une représentation plus efficace.

Certaines instructions complexes réalisent des opérations qui ne sont pas implé-mentées par un module du chemin de données et sont donc réalisées par des algo-rithmes qui contiennent des boucles. Si la condition de sortie de boucle porte sur une valeur non symbolique (i.e., dépend d’un composant qui a été affecté avec une constante), l’exécution symbolique peut être utilisée pour tracer toutes les étapes. Ce-pendant une représentation compacte des vecteurs de bits est essentielle pour éviter, ou au moins retarder l’explosion combinatoire provoquée par ces boucles. De plus, cette technique de preuve reste entièrement automatique.

Parmi les nombreuses représentations de formules booléennes, nous avons choisi les Binary Moment Diagrams (*BMDs) pour deux raisons majeures : ils fournissent une représentation canonique des fonctions de

✕✂✁☎✄✝✆

dans [BRY 95]. De plus, les représentations des vecteurs de bits et de l’addition ont une taille qui croit linéairement avec le nombre de bits. C’est exactement ce que nous demandons pour traiter des instructions complexes qui implémentent des opérations arithmétiques sur les vecteurs de bits.

4.3.2. Défi nition des *BMDs

Les *BMDs héritent des concepts élaborés pour les Binary Decision Diagrams (BDDs) : ils représentent des fonctions sur des variables booléennes par des graphes acycliques. Soit

une fonction de ✕✟✁✝✄✝✆

dans . Les BMDs sont basés sur l’expan-sion de Reed-Muller de

par rapport à une variable booléenne . Nous notons la restriction de quand et sa restriction quand . L’expansion de Shannon de

par rapport à donne tout d’abord

✁☞☛ ✝✍✌✡✔ ✠✏✎ ✌✡✔

est ainsi décomposée en deux moments :

✠✑✎ ✌✂✔✓✒ ✔✔✒

est le moment constant. Il représente la valeur de la fonction quand✙✄✂ . L’autre moment,✔✔✒✠

, est le moment linéaire. Il représente la variation de

quand passe de en

. Un BMD est donc un arbre avec un identificateur de variable comme racine et deux BMDs comme fils qui sont les moments constant et linéaire.

Les BMDs ont des valeurs numériques associées aux feuilles mais aussi aux bran-ches. Ces valeurs sont multipliées au cours du parcours d’un arbre ; cette structure est donc appelée Multiplicative Binary Moment Diagram (*BMD). Comme pour les autres représentations symboliques équivalentes, les tailles des *BMDs sont réduites en partageant des sous-arbres communs.

Bryant et Chen ont proposé des fonctions de synthèse en appliquant l’addition, la multiplication ou l’exponentiation à des *BMDs tout en conservant une forme

cano-✦

✤✖✕ ✁✘✗✍✁✚✙

v1 v2 v0 4 2 1 0 v1 v2 v0 4 2 1 0 1 w 0 w 2 w v1 v2 v0 4 2 1 0 0 w 1 w 2 w 4 2 (a) (b) (c)

Figure 3. Les représentations *BMD d’un mot non signé (a), de l’addition (b) et de

la multiplication (c). Les valeurs des branches égales à

ne sont pas indi-quées, les lignes pointillées sont les moments constants, les lignes pleines les moments linéaires.

nique [BRY 94]. La figure 3 montre les représentations de quelques opérations avec des *BMDs. Par exemple, on peut voir en (a) que la valeur non signée du vecteur composé des bits✝✁

(poids faible), et est✂☎✄ ✄✝✆ ✝✞ ✂✟✄ ✝✞

. La figure 3 (c) illustre le produit des deux vecteurs com-posé de ✒✡✕ et composé de ✝☞ ✒✞✕

. La fonction ainsi représentée vaut ✂✌✄ ✂✍✄ ✝✞✡✝✟✎ ✂✍✄ ✝✞✂✝✟✎ ✂✍✄ ✝✞✞✝ ✂✎✄ ✂✎✄ ✝✞✡✝

ce qui est bien la valeur de

. Soit par exemple la fonction

✒✞✕ ✝✞✖✝ ✝✞ . La décomposition de par rapport à

donne les deux moments : ✏✒✑ ✂✓✆ ✝✞ et ✔✑✒✏✒✑ L’évaluation de

se déroule comme suit : si

, alors on rend le résultat de l’évaluation de

✝✞

, sinon on rend la somme de l’évaluation de ✝✞

et de . 4.3.3. Une représentation des vecteurs de bits par les *BMDs

Nous avons étendu les *BMDs dans le but de fournir tous les opérateurs de l’al-gèbre décrite en 4.1 [ARD 96]. Nous utilisons les *BMDs exactement comme s’ils représentaient des vecteurs de bits et non pas des entiers. Pour cela nous devons tenir compte de la taille des vecteurs ; nous complétons donc le concept des *BMDs comme suit. Un vecteur de

bits est représenté par un *BMD✔✖✕✘✗ qui est un couple formé par un *BMD et l’entier

. Nous notons✙✚✔✖✕✛✗ pour signifier que est la représentation en *BMD de la valeur non signée d’un vecteur de

bits. Alors que les *BMDs re-présentent des fonctions de

✕✟✁✝✄✝✆

vers , les *BMD✔✖✕✘✗ représentent les fonctions de

✕✟✁✝✄✟✆

vers les entiers de✼✰✝✣✢✤✆

. Nous notons *BMD✔✖✗ l’union de tous les *BMD✔✖✕✘✗ pour✂✦✥

.

✧✓✤✩★

n’est pas le nombre de variables de✪ mais il est le nombre minimum de bits requis pour représenter toutes les valeurs possibles de✪ .

✡✠❀ ✄✙ ✔✖✕✘✗ ✔✖✕✄✂✗ ☎✝✆✟✞ ✄✝✙ ✔✖✕

Les autres opérateurs sur les vecteurs de bits sont définis en utilisant la structure inductive des vecteurs (assimilables à des listes) : un vecteur est composé d’un bit de tête et d’un vecteur de bits formant sa queue. Ces opérateurs ne peuvent pas être directement définis par une induction sur la structure en graphe des *BMDs.

Ainsi, nous avons tout d’abord défini trois primitives pour reproduire la structure des vecteurs avec les *BMD✔✖✗.

La primitive

construit un vecteur à partir d’un bit et d’un vecteur. Elle prend en argument un *BMD

représentant un bit et un *BMD✔✖✕✘✗ représentant un vecteur de bits. Le résultat est un *BMD✔✖✕

✏✓✒ tel que : ✔✖✕✛✗ ☎✝✆✟✞ ✔✖✕ ✏✓✒ La primitive

rend le bit de poids faible d’un vecteur. Nous ne traitons que les mots positifs pour lesquels le bit le moins significatif est à

quand la valeur du mot est impaire ; ce bit est à quand la valeur est paire. Le résultat de la fonction

dépend donc de la parité du *BMD✔✖✕✘✗, avec

: ✝✡✠ ✄✘☛ ✔✕✛✗ ☎✝✆✟✞ ✿●✿ ✄✛☛ La fonction

✿●✿ sur les *BMDs découle directement des propriétés de la parité vis-à-vis de la multiplication et de l’addition. Voici sa définition :

✿●✿ *BMD terminal de valeur ☎✝✆✟✞ modulo ✿●✿ ✄✛☛ de valeur ☎✝✆☛✞ si modulo ✿●✿ ✄✛☛ ✝✓✎ ✿●✿ ✄✛☛ ✿●✿ ✄✛☛ ✿●✿ ✄✛☛ ✝✗✝ sinon La première équation rend un *BMD terminal dont la valeur représente la parité du *BMD argument quand il est terminal. La seconde équation indique qu’un *BMD dont la valeur de la racine est paire est toujours pair car le produit d’un nombre pair avec une valeur quelconque est toujours pair. Si la valeur de la racine est impaire, alors le résultat est un *BMD non terminal calculé par des appels récursifs à

. Ce résultat est obtenu en constatant que si , alors la parité est celle de

. Si

, la parité est celle de la somme

. Cette somme est impaire quand

et

sont tous les deux impairs. On a ainsi

✿●✿ ✄✛☛ ✠✏✎ . La primitive ✄✂

rend les bits de poids fort d’un vecteur. Elle est construite en utilisant le même schéma récursif que pour

.

Grâce à ces trois primitives, on peut définir tous les opérateurs de l’algèbre donnée en 4.1. Ils sont utilisés au point 8 de l’algorithme de preuve (voir 3.2) pour calculer

les *BMD qui représentent les états du processeur aux deux niveaux. Nous donnons un exemple ci-dessous.

La sélection d’un champ de bits est récursivement définie. ☎✞✟

✄✝✙ ✔✖✕✛✗ ✕✁

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

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