Les types
2023-2024
En C++ le type bool
permet de représenter une valeur
logique
true
, false
).SystemC définit, en plus, le type sc_logic
pouvant
prendre 4 valeurs
0
, 1
, Z
,
X
).; sc_logic u
~
,
&
, |
, ^
) et les opérateurs
d’égalité.
sc_logic
et un booléen, un
entier ou un caractère.La description complète de ce type se trouve dans la section 7.9.2 de la norme.
Les quatre valeurs correspondent aux états logiques suivant :
0
: faux1
: vraiZ
: haute impédanceX
: Non défini, inconnu, conflitEn interne, un sc_logic
contient un élément du type
énuméré sc_logic_value_t
défini comme :
enum sc_logic_value_t {
=0;
Log_0,
Log_1,
Log_Z
Log_X};
Notez que ces valeurs ne sont pas directement utilisable. Le standard définit les constantes suivantes que l’on peut utiliser:
SC_LOGIC_0
SC_LOGIC_1
SC_LOGIC_Z
SC_LOGIC_X
Donc, ces 4 valeurs possibles sont des valeurs entières.
En plus du constructeur par défaut et du constructeur de copie, un
sc_logic
peut être construit à partir :
false
-> 0
et true
->
1
'0'
, '1'
, 'x'
,
'z'
, 'X'
, 'Z'
peuvent alors être
utilisésx
Ces constructeurs sont définis comme explicit
et ne
peuvent donc pas être utilisés pour les conversions implicites.
Par contre, les opérateurs binaires ainsi que les opérateurs
d’affectation sont définis pour pouvoir utiliser les
sc_logic
avec des booléens, des caractères ou des
entiers.
Il existe aussi des méthodes pour convertir ou tester ces valeurs
(to_bool
, to_char
, is_01
) qui
peuvent être appelées si besoin.
Pour imprimer la valeur d’une variable de type sc_logic
l’opérateur de flux <<
a été surchargé. En général,
l’opérateur de flux est surchargé pour tous les types définis dans
SystemC.
Travail à faire : mise en pratique des
sc_logic
sc_main
:sc_logic
,sc_logic
,sc_logic
et des bool
,Remarque Il existe aussi un type
sc_bit
ne pouvant prendre que les 2 valeurs
(0
, 1
) mais il est déprécié depuis la version
2.2 ce SystemC. Dans ce cas, on lui préférera le type bool
de C++.
sc_bv<N>
Vecteur d’éléments binaires (bool
).
<8> x; sc_bv
Le paramètre de template est un entier qui permet de définir la taille du vecteur.
sc_lv<N>
Vecteur de variables logiques (sc_logic
).
<8> x; sc_lv
Le paramètre de template est un entier qui permet de définir la taille du vecteur.
Les types sc_lv
et sc_bv
sont des vecteurs.
Ils ont été conçus pour permettre l’accès efficace à leurs différents
éléments. Par contre, ils ne permettent de faire des opérations
arithmétiques.
Pour l’arithmétique, nous verrons par la suite des types plus adaptés.
Les vecteurs peuvent être comparés et initialisés à partir d’une chaîne de caractère. Par exemple :
// vecteur logic de 4 bits de large
<4> x = "01xz";
sc_lv
if (x == "0000")
...
Ici chaque caractère représente un des éléments logiques.
On peut aussi initialiser un vecteur à partir d’un bool
ou d’un char
. Dans ce cas tous les éléments auront la même
valeur. Par exemple :
// initialise tous les éléments d'un vecteur de 8bits à false ?
<8> x = false;
sc_bv<8> y = sc_bv<8>(false);
sc_bv<8> z(false);
sc_bv...
Les vecteurs peuvent aussi être initialisés à partir d’un entier. Par exemple :
//
<8> X;
sc_bv
= 44;
X // Imprimera 44
<< "x---> " << X << "(" << X.to_uint() << ")" << endl;
cout
// Imprimera 44 aussi car X est sur 8 bits
= 300;
X << "x---> " << X << "(" << X.to_uint() << ")" << endl;
cout ...
Les opérateurs binaires (~
, &
,
|
, ^
) ainsi que les opérateurs de décalage
(>>
, <<
) sont supportés.
De plus, certains opérateurs ont été surchargés pour permettre la sélection d’un ou plusieurs éléments. Ils peuvent être utilisés des deux cotés d’une affectation.
<8> X;
sc_bv
[0] = false;
X= X[1];
sc_bit b
(7,4) = "1010";
X(3,0) = X(7,4); X
La méthode range
peut aussi être utilisée pour la
sélection de plusieurs éléments (i.e. X(n1,n2)
est
équivalent à X.range(n1,n2)
).
La concaténation de deux vecteurs se fait en les mettant entre
parenthèses. C’est l’opérateur ','
qui est surchargé.
<8> X = "11110000";
sc_bv<4> a;
sc_bv<4> b;
sc_bv
(a,b) = X;
= (b,a); X
La fonction concat
peut aussi être utilisée pour la
concaténation de deux éléments (i.e. (n1,n2)
est équivalent
à concat(n1,n2)
).
D’autres méthodes sont implémentées, par exemple :
and_reduce()
or_reduce()
xor_reduce()
nand_reduce()
nor_reduce()
xnor_reduce()
to_int()
to_uint()
to_long()
to_ulong()
to_int64()
to_uint64()
Travail à faire : mise en pratique des sc_lv
et sc_bv
sc_main
:Pour des raisons d’efficacité, les types sc_lv
et
sc_bv
ne permettent pas de faire d’opérations
arithmétiques.
Pour cela, la bibliothèque SystemC fournit des types permettant de manipuler des entiers de taille arbitraire.
sc_int<N>
et
sc_uint<N>
SystemC définit ces deux classes pour les entiers de taille inférieure ou égale à 64 bits.
<10> x; // entier signé sur 10 bits
sc_int<7> y; // entier non signé sur 7 bits sc_uint
En interne, ils utilisent les entiers 64 bits natifs de C++ pour avoir une implémentation efficace des opérateurs arithmétiques. Les opérations de troncature et d’extension sont gérées automatiquement.
Ces types supportent tous les opérateurs arithmétiques et logiques supportés par les entiers en C++. En plus, comme pour les types vecteurs, ils supportent les opérateurs de sélection de bits ou de plages de bits, de concaténation, ainsi que les opérateurs de réduction.
Ils peuvent être initialisés par :
sc_lv
ou un sc_bv
ce qui est utile pour
pouvoir faire des opérations arithmétiques sur ces types.En pratique, il faut privilégier les types C++
natifs.
Si l’on a besoin d’une taille arbitraire qui ne correspond pas à un type
natif, on utilise alors les sc_int
et sc_uint
.
Ce n’est que si l’on a besoin de modéliser un bus 3 états
(Z
) qu’on utilise les sc_lv
.
Travail à faire : mise en pratique des
sc_int
et sc_uint
sc_main
:sc_lv
,sc_bigint<N>
et
sc_biguint<N>
Si l’on a besoin de représenter des entiers de taille arbitraire supérieure à 64 bits, il faut utiliser ces types.
Ils sont prévus pour permettre des calculs sur une taille illimitée
mais pour des raisons pratiques, l’implémentation de référence limite
leur taille à SC_MAX_NBITS
. Cette valeur est définie dans
le fichier kernel/sc_constants.h
et peut être modifiée si
nécessaire.
Pour représenter un nombre en virgule fixe on doit préciser sa taille et la position de la virgule.
SystemC propose deux familles de types pour représenter ces nombres :
sc_fixed
et sc_ufixed
pour lesquels
les paramètres sont statiques (définis par des templates)sc_fix
et sc_ufix
pour lesquels les
paramètres peuvent être définis dynamiquementEn plus, des versions _fast
sont définies. Ces types
utilisent en interne les doubles
natifs de C++ pour être
plus efficaces mais sont limités à la taille de ceux-ci.
sc_fixed
et
sc_ufixed
<wl, iwl, q_mode, o_mode, n_bits> X;
sc_fixed <wl, iwl, q_mode, o_mode, n_bits> Y; sc_ufixed
avec :
wl
la taille totaleiwl
la taille de la partie entièreq_mode
le mode de quantificationo_mode
le mode de saturationn_bits
le nombre de bits qui saturentLes paramètres wl
etiwl
doivent être
définis à l’instanciaton. Les autres paramètres ont par défaut les
valeurs suivantes :
q_mode
: SC_TRN
// troncature vers le
baso_mode
: SC_WRAP
// modulon_bits
: 0
// aucun bit ne satureLa définition exacte de ces paramètres et des comportements associés se trouvent dans la section 7.10 du standard.
Remarque
Par défaut, les types permettant de représenter des nombres en
virgule fixe, ne sont pas disponibles. Pour les utiliser il faut définir
la macro SC_INCLUDE_FX
avant d’inclure
systemc.h
.
Ces types peuvent être initialisés à partir de float
ou
de double
.
Toutes les opérations arithmétiques et logique sont faites en fonction des paramètres sur lesquels ils ont été définis.
Ces types peuvent, par exemple, être utilisés pour tester simplement une implémentation en virgule fixe et comparer la précision à une implémentation flottante.
Par exemple :
#define SC_INCLUDE_FX
#include <systemc.h>
// 1 bit pour la partie entière et 2 bits après la virgule
#define WL 3
#define IWL 1
int sc_main (int argc, char * argv[])
{
double d1 = 0.1;
double d2 = 0.9;
double ds = d1 + d2;
<WL,IWL> f1(d1);
sc_fixed<WL,IWL> f2 = d2;
sc_fixed<WL,IWL> fs = f1 + f2;
sc_fixed
double e = ds - double(fs);
cout << "Double " << ds << endl
<< "Fixed " << fs << endl
<< "Error " << e << endl;
return 0;
}
sc_fix
et
sc_ufix
Ces deux types diffèrent des précédents par le fait que les paramètres sont modifiables dynamiquement en fonction d’un contexte. Comme les paramètres sont dynamiques, ils sont plus lents à l’exécution.
Pour définir un contexte :
// ici seule la taille est définie, mais tout est modifiable
(10,3);
sc_fxtype_params param(param);
sc_fxtype_context ctxt
; // paramètre définis dans le contexte sc_fix a
Exemple d’utilisation :
#define SC_INCLUDE_FX
#include <systemc.h>
double compare (const double d1, const double d2)
{
double ds = d1 + d2;
(d1);
sc_fix f1(d2);
sc_fix f2= f1 + f2;
sc_fix fs
return ds - fs;
}
int sc_main (int argc, char * argv[])
{
double d1 = 0.1;
double d2 = 0.9;
// 1 bit pour la partie entière et 2 bits après la virgule
(3,1);
sc_fxtype_params param1// 1 bit pour la partie entière et 4 bits après la virgule
(5,1);
sc_fxtype_params param2// 1 bit pour la partie entière et 6 bits après la virgule
(7,1);
sc_fxtype_params param3
(param1);
sc_fxtype_context ctxt1
cout<< "The error with " << ctxt1.default_value()
<< " format is " << compare(d1,d2) << endl;
(param2);
sc_fxtype_context ctxt2
cout<< "The error with " << ctxt1.default_value()
<< " format is " << compare(d1,d2) << endl;
(param3);
sc_fxtype_context ctxt3
cout<< "The error with " << ctxt1.default_value()
<< " format is " << compare(d1,d2) << endl;
return 0;
}
De façon analogue aux sc_fixed
, le type
sc_fxtype_params
prend, entre autres, comme paramètres de
construction la taille totale de la représentation ainsi que la taille
de la partie entière.
Travail à faire : mise en pratique
Il existe une autre bibliothèque C++ libre, indépendante de SystemC, pour modéliser des calculs matériels. Il s’agit de la Algorithmic C Datatype (AC Datatypes) library. Elle est compatible avec SystemC mais peut aussi être utilisée indépendamment en pure C++.
Elle est distribuée sous les termes de la licence libre Apache 2.0 et est accessible sur https://hlslibs.org/
sc_time
SystemC définit un type pour représenter le temps de simulation ou des intervalles de temps.
Un sc_time
est construit à partir d’un
double
et d’une unité. L’unité est une des valeurs du type
énuméré sc_time_unit
(17.22, SC_NS); sc_time periode
Le type sc_time_unit
est défini comme suit :
enum sc_time_unit {
= 0, // femtosecondes
SC_FS , // picosecondes
SC_PS, // nanosecondes
SC_NS, // microsecondes
SC_US, // millisecondes
SC_MS// secondes
SC_SEC };
En interne, le temps est stocké sous la forme d’un entier 64 bits qui
est un multiple de la résolution temporelle. Cette valeur interne peut
être récupérée en utilisant la méthode value
.
La résolution temporelle est globale à une simulation. Par défaut
elle est égale à 1 picoseconde mais peut être modifiée
en utilisant la fonction sc_set_time_resolution
.
Quelle que soit la résolution, la constante SC_ZERO_TIME
représente toujours un temps nul.
Le type sc_time
supporte des opérateurs de comparaison
ainsi que certains opérateurs arithmétiques. On peut ainsi additionner
deux temps ou les multiplier par un double.
Exemple d’utilisation :
#include <systemc.h>
int sc_main (int argc, char * argv[])
{
(3.51, SC_NS);
sc_time t1<< "---> " << t1.value() << ", " << t1 << endl;
cout
= 2*t1;
sc_time t2 << "---> " << t2.value() << ", " << t2 << endl;
cout
<< "La durée t" ;
cout if (t1>t2)
<< "t1";
cout else
<< "t2";
cout << " est plus grande" << endl;
cout
<< "---> " << SC_ZERO_TIME.value() << ", " << SC_ZERO_TIME << endl;
cout
return 0;
}
© Copyright 2016-2024, Tarik Graba, Télécom Paris. | |
Le contenu de cette page est mis à disposition selon les termes de la Licence Creative Commons Attribution - Partage dans les Mêmes Conditions 4.0 International . | |
Ce document reprend des parties du cours en ligne sur SystemC d'Alexis Polti disponible ici. |