stringtranslate.com

SistemaVerilog

SystemVerilog , estandarizado como IEEE 1800 , es un lenguaje de descripción y verificación de hardware utilizado para modelar, diseñar , simular , probar e implementar sistemas electrónicos. SystemVerilog se basa en Verilog y algunas extensiones, y desde 2008, Verilog ahora forma parte del mismo estándar IEEE . Se utiliza comúnmente en la industria del diseño electrónico y de semiconductores como una evolución de Verilog.

Historia

SystemVerilog comenzó con la donación del lenguaje Superlog a Accellera en 2002 por parte de la empresa emergente Co-Design Automation. [1] La mayor parte de la funcionalidad de verificación se basa en el lenguaje OpenVera donado por Synopsys . En 2005, SystemVerilog fue adoptado como estándar IEEE 1800-2005. [2] En 2009, el estándar se fusionó con el estándar básico Verilog (IEEE 1364-2005), creando el estándar IEEE 1800-2009.

El estándar SystemVerilog se actualizó posteriormente en 2012, [3] 2017, [4] y más recientemente en diciembre de 2023. [5]

Caracteristicas de diseño

El conjunto de funciones de SystemVerilog se puede dividir en dos funciones distintas:

  1. SystemVerilog para diseño de nivel de transferencia de registro (RTL) es una extensión de Verilog-2005 ; todas las funciones de ese idioma están disponibles en SystemVerilog. Por tanto, Verilog es un subconjunto de SystemVerilog.
  2. SystemVerilog para verificación utiliza extensas técnicas de programación orientada a objetos y está más estrechamente relacionado con Java que Verilog. Estas construcciones generalmente no son sintetizables.

El resto de este artículo analiza las características de SystemVerilog que no están presentes en Verilog-2005 .

Vida útil de los datos

Hay dos tipos de vida útil de los datos especificados en SystemVerilog: estático y automático . Las variables automáticas se crean en el momento en que la ejecución del programa llega al alcance de la variable. Las variables estáticas se crean al inicio de la ejecución del programa y mantienen el mismo valor durante toda la vida útil del programa, a menos que se les asigne un nuevo valor durante la ejecución.

Cualquier variable que se declare dentro de una tarea o función sin especificar tipo se considerará automática. Para especificar que una variable es estática, coloque la palabra clavestatic " " en la declaración antes del tipo, por ejemplo, " ". La palabra clave " " se utiliza de la misma manera.static int x;automatic

Nuevos tipos de datos

Los tipos de variables mejorados añaden nuevas capacidades al tipo "reg" de Verilog:

lógica [ 31 : 0 ] my_var ;  

Verilog-1995 y -2001 limitan las variables de registro a declaraciones de comportamiento como el código RTL . SystemVerilog amplía el tipo de registro para que pueda ser controlado por un único controlador, como una puerta o un módulo. SystemVerilog denomina a este tipo "lógica" para recordar a los usuarios que tiene esta capacidad adicional y no es un registro de hardware. Los nombres "lógica" y "reg" son intercambiables. Una señal con más de un controlador (como un búfer de tres estados para entrada/salida de uso general ) debe declararse como un tipo de red como "cable" para que SystemVerilog pueda resolver el valor final.

Los arreglos empaquetados multidimensionales unifican y amplían la noción de "registros" y "memorias" de Verilog:

lógica [ 1 : 0 ][ 2 : 0 ] mi_paquete [ 32 ];  

El Verilog clásico permitía declarar solo una dimensión a la izquierda del nombre de la variable. SystemVerilog permite cualquier número de dimensiones "empaquetadas". Una variable de tipo matriz empaquetada se asigna 1:1 a una cantidad aritmética entera. En el ejemplo anterior, cada elemento de my_packpuede usarse en expresiones como un entero de seis bits. Las dimensiones a la derecha del nombre (32 en este caso) se denominan dimensiones "sin empaquetar". Como en Verilog-2001 , se permite cualquier número de dimensiones desempaquetadas.

Los tipos de datos enumerados (enums) permiten asignar nombres significativos a cantidades numéricas. Las variables declaradas de tipo enumerado no se pueden asignar a variables de un tipo enumerado diferente sin conversión . Esto no es cierto para los parámetros, que eran la técnica de implementación preferida para cantidades enumeradas en Verilog-2005:

typedef enumeración lógica [ 2 : 0 ] { ROJO , VERDE , AZUL , CIAN , MAGENTA , AMARILLO } color_t ;           color_t mi_color = VERDE ; visualización inicial $ ( "El color es %s" , my_color . nombre ());     

Como se muestra arriba, el diseñador puede especificar un tipo aritmético subyacente ( logic [2:0]en este caso) que se utiliza para representar el valor de enumeración. Los metavalores X y Z se pueden utilizar aquí, posiblemente para representar estados ilegales. La función incorporada name()devuelve una cadena ASCII para el valor enumerado actual, lo cual es útil en validación y pruebas.

Nuevos tipos de enteros : SystemVerilog define byte, shortinty intcomo longinttipos integrales con signo de dos estados que tienen 8, 16, 32 y 64 bits respectivamente. Un bittipo es un tipo de dos estados de ancho variable que funciona de manera muy similar a logic. Los tipos de dos estados carecen de los metavalores X y Z del Verilog clásico; trabajar con estos tipos puede resultar en una simulación más rápida.

Las estructuras y uniones funcionan de manera muy similar a como lo hacen en elde programación C. Las mejoras de SystemVerilog incluyen el atributo empaquetado y el atributo etiquetado . Eltaggedatributo permite realizar un seguimiento en tiempo de ejecución de qué miembros de una unión están actualmente en uso. Elpackedatributo hace que la estructura o unión se asigne 1:1 a una matriz empaquetada de bits. El contenido destructlos tipos de datos ocupa un bloque continuo de memoria sin espacios, similar a los campos de bits en C y C++:

estructura typedef empaquetada { bit [ 10 : 0 ] expo ; signo de bit ; bit [ 51 : 0 ] mant ; } FP ;            FP cero = 64'b0 ;   

Como se muestra en este ejemplo, SystemVerilog también admite typedefs , como en C y C++.

Bloqueos procesales

SystemVerilog presenta tres nuevos bloques de procedimientos destinados a modelar hardware : always_comb(para modelar lógica combinacional ), always_ff(para flip-flops ) y always_latch(para pestillos ). Mientras que Verilog utilizó un único bloque de propósito general alwayspara modelar diferentes tipos de estructuras de hardware, cada uno de los nuevos bloques de SystemVerilog está destinado a modelar un tipo específico de hardware, imponiendo restricciones semánticas para garantizar que el hardware descrito por los bloques coincida con el uso previsto de el modelo. Un compilador HDL o un programa de verificación pueden tomar medidas adicionales para garantizar que solo se produzca el tipo de comportamiento previsto.

Un always_combbloque modela la lógica combinacional . El simulador infiere que la lista de sensibilidad son todas las variables de las declaraciones contenidas:

siempre_comb comenzar tmp = b * b - 4 * a * c ; no_root = ( tmp < 0 ); fin                 

Un always_latchbloque modela pestillos sensibles al nivel . Nuevamente, la lista de sensibilidad se infiere del código:

siempre_latch si ( es ) q <= d ;     

Un always_ffbloque modela la lógica síncrona (especialmente la lógica secuencial sensible a los bordes ):

siempre_ff @( posedge clk ) recuento <= recuento + 1 ;       

Las herramientas de automatización de diseño electrónico (EDA) pueden verificar la intención del diseño verificando que el modelo de hardware no viole ninguna semántica de uso de bloques. Por ejemplo, los nuevos bloques restringen la asignación a una variable al permitir solo una fuente, mientras que alwaysel bloque de Verilog permitía la asignación de múltiples fuentes procesales.

Interfaces

Para diseños pequeños, el puerto Verilog describe de forma compacta la conectividad de un módulo con el entorno circundante. Pero los bloques principales dentro de una gran jerarquía de diseño normalmente poseen miles de puertos. SystemVerilog introduce el concepto de interfaces para reducir la redundancia de las declaraciones de nombres de puertos entre módulos conectados, así como señales agrupadas y abstractas relacionadas en un paquete declarado por el usuario. Un concepto adicional es modport, que muestra la dirección de las conexiones lógicas.

Ejemplo:

interfaz intf ; lógica a ; lógica b ; modport en ( entrada a , salida b ); salida modport ( entrada b , salida a ); interfaz final                  parte superior del módulo ; intf i (); u_a m1 (. i1 ( yo . en )); u_b m2 (. i2 ( i . fuera )); módulo final          módulo u_a ( intf . en i1 ); módulo final   módulo u_b ( intf . out i2 ); módulo final   

Funciones de verificación

Las siguientes funciones de verificación normalmente no se pueden sintetizar, lo que significa que no se pueden implementar en hardware basado en código HDL. En cambio, ayudan en la creación de bancos de pruebas extensibles y flexibles .

Nuevos tipos de datos

El tipo de datos representa una cadenastring de texto de longitud variable . Por ejemplo:

cadena s1 = "Hola" ; cadena s2 = "mundo" ; cadena p = ".?!" ; cadena s3 = { s1 , "," , s2 , p [ 2 ]}; // concatenación de cadenas $ display ( "[%d] %s" , s3 . len (), s3 ); // la simulación imprimirá: "[13] ¡Hola mundo!"                   

Además de la matriz estática utilizada en el diseño, SystemVerilog ofrece matrices dinámicas , matrices asociativas y colas :

int cmdline_elementos ; // # elementos para la matriz dinámica int da []; // matriz dinámica int ai [ int ]; // matriz asociativa, indexada por int int como [ cadena ]; // matriz asociativa, indexada por cadena int qa [$]; // cola, indexada como una matriz o mediante métodos integrados          inicio inicial cmdline_elements = 16 ; da = nuevo [ cmdline_elements ]; // Asignar fin de matriz con 16 elementos          

Una matriz dinámica funciona de manera muy similar a una matriz desempaquetada, pero ofrece la ventaja de ser asignada dinámicamente en tiempo de ejecución (como se muestra arriba). Mientras que el tamaño de una matriz empaquetada debe conocerse en el momento de la compilación (a partir de una constante o expresión de constantes), la matriz dinámica El tamaño se puede inicializar desde otra variable de tiempo de ejecución, lo que permite dimensionar y cambiar el tamaño de la matriz arbitrariamente según sea necesario.

Una matriz asociativa se puede considerar como un árbol de búsqueda binario con un tipo de clave y un tipo de datos especificados por el usuario . La clave implica un ordenamiento ; Los elementos de una matriz asociativa se pueden leer en orden lexicográfico. Finalmente, una cola proporciona gran parte de la funcionalidad del tipo deque STL de C++ : se pueden agregar y eliminar elementos desde cualquier extremo de manera eficiente. Estas primitivas permiten la creación de estructuras de datos complejas necesarias para marcar un diseño de gran tamaño.

Clases

SystemVerilog proporciona un modelo de programación orientado a objetos .

En SystemVerilog, las clases admiten un modelo de herencia única , pero pueden implementar una funcionalidad similar a la herencia múltiple mediante el uso de las llamadas "clases de interfaz" (idénticas en concepto a la interfacecaracterística de Java). Las clases se pueden parametrizar por tipo , proporcionando la función básica de las plantillas de C++ . Sin embargo, no se admiten plantillas de especialización de plantillas ni de funciones.

Las características de polimorfismo de SystemVerilog son similares a las de C++: el programador puede escribir específicamente una virtualfunción para que una clase derivada obtenga el control de la función. Ver función virtual para más información.

La encapsulación y ocultación de datos se logra utilizando las palabras clave localy protected, que deben aplicarse a cualquier elemento que deba ocultarse. De forma predeterminada, todas las propiedades de clase son públicas .

Las instancias de clase se crean dinámicamente con la newpalabra clave. Se puede definir un constructor denotado por . function newSystemVerilog tiene recolección automática de basura , por lo que no existe ninguna función de lenguaje para destruir explícitamente instancias creadas por el nuevo operador .

Ejemplo:

Memoria de clase virtual ; bit de función virtual [ 31 : 0 ] leído ( bit [ 31 : 0 ] dirección ); función final función virtual escritura nula ( bit [ 31 : 0 ] dirección , bit [ 31 : 0 ] datos ); clase final de función final                    clase SRAM # ( parámetro AWIDTH = 10 ) extiende la memoria ; bit [ 31 : 0 ] mem [ 1 << ANCHO ];          bit de función virtual [ 31 : 0 ] leído ( bit [ 31 : 0 ] dirección ); devolver memoria [ dirección ]; función final          función virtual escritura nula ( bit [ 31 : 0 ] dirección , bit [ 31 : 0 ] datos ); mem [ dirección ] = datos ; clase final de función final            

Generación aleatoria restringida

A las cantidades enteras, definidas en una definición de clase o como variables independientes en algún ámbito léxico, se les pueden asignar valores aleatorios en función de un conjunto de restricciones. Esta característica es útil para crear escenarios aleatorios para la verificación .

Dentro de las definiciones de clase, los modificadores randy randcseñalan variables que se someterán a aleatorización. randcespecifica la aleatorización basada en permutación , donde una variable tomará todos los valores posibles una vez antes de que se repita cualquier valor. Las variables sin modificadores no son aleatorias.

clase eth_frame ; bit rand [ 47 : 0 ] destino ; rand bit [ 47 : 0 ] origen ; bit rand [ 15 : 0 ] f_type ; carga útil de bytes rand []; poco [ 31 : 0 ] fcs ; bit rand [ 31 : 0 ] fcs_corrupción ;                        restricción básica { carga útil . tamaño dentro de {[ 46 : 1500 ]}; }       restricción good_fr { fcs_corrupción == 0 ; } clase final      

En este ejemplo, el fcscampo no es aleatorio; en la práctica, se calculará con un generador CRC y el fcs_corruptcampo se utilizará para corromperlo para inyectar errores FCS. Las dos restricciones mostradas son aplicables a tramas Ethernet conformes . Las restricciones pueden habilitarse selectivamente; esta característica sería necesaria en el ejemplo anterior para generar marcos corruptos. Las restricciones pueden ser arbitrariamente complejas e involucrar interrelaciones entre variables, implicaciones e iteraciones. Se requiere que el solucionador de restricciones SystemVerilog encuentre una solución, si existe, pero no ofrece garantías en cuanto al tiempo que llevará hacerlo, ya que en general se trata de un problema NP-difícil ( satisfacibilidad booleana ).

Métodos de aleatorización

En cada clase SystemVerilog hay 3 métodos predefinidos para la aleatorización: pre_randomize, randomize y post_randomize. El usuario llama al método aleatorio para la aleatorización de las variables de clase. El método aleatorio llama al método pre_randomize antes de la aleatorización y el método aleatorio llama al método post_randomize después de la aleatorización.

clase eth_frame ; bit rand [ 47 : 0 ] destino ; rand bit [ 47 : 0 ] origen ; bit rand [ 15 : 0 ] f_type ; carga útil de bytes rand []; poco [ 31 : 0 ] fcs ; rand bit corrupted_frame ;                       restricción básica { carga útil . tamaño dentro de {[ 46 : 1500 ]}; } función nula post_randomize () esto . calcular_fcs (); // actualiza el campo fcs de acuerdo con el marco aleatorio if ( corromped_frame ) // si este marco debería estar dañado this . corrupt_fcs (); // corrompe la clase final de función final de fcs                  

Controlar las restricciones

Los métodos constraint_mode() y random_mode() se utilizan para controlar la aleatorización. constraint_mode() se usa para activar y desactivar una restricción específica y random_mode se usa para activar o desactivar la aleatorización de una variable específica. El siguiente código describe y prueba de procedimiento una trama Ethernet :

clase eth_frame ; bit rand [ 47 : 0 ] destino ; rand bit [ 47 : 0 ] origen ; bit rand [ 15 : 0 ] f_type ; carga útil de bytes rand []; poco [ 31 : 0 ] fcs ; rand bit corrupted_frame ;                       restricción básica { carga útil . tamaño dentro de {[ 46 : 1500 ]}; } restricción one_src_cst { src == 48'h1f00 }               restricción dist_to_fcs { fcs dist { 0 :/ 30 ,[ 1 : 2500 ] :/ 50 }; // 30 y 50 son los pesos (30/80 o 50/80, en este ejemplo) }       clase final . . . eth_frame mi_frame ; mi_marco . one_src_cst . modo_restricción ( 0 ); // la restricción one_src_cst no se tendrá en cuenta my_frame . tipo_f . modo_aleatorio ( 0 ); // la variable f_type no será aleatoria para esta instancia de marco. mi_marco . aleatorizar ();  

Afirmaciones

Las afirmaciones son útiles para verificar las propiedades de un diseño que se manifiestan después de alcanzar una condición o estado específico. SystemVerilog tiene su propio lenguaje de especificación de aserciones, similar al lenguaje de especificación de propiedades . El subconjunto de construcciones del lenguaje SystemVerilog que sirve para la afirmación se denomina comúnmente SystemVerilog Assertion o SVA. [6]

Las aserciones de SystemVerilog se construyen a partir de secuencias y propiedades . Las propiedades son un superconjunto de secuencias; cualquier secuencia puede usarse como si fuera una propiedad, aunque esto no suele ser útil.

Las secuencias constan de expresiones booleanas aumentadas con operadores temporales . El operador temporal más simple es el ##operador que realiza una concatenación: [ aclaración necesaria ]

secuencia S1 ; @( posedge clk ) req ## 1 gnt ; secuencia final      

Esta secuencia coincide si la gntseñal sube un ciclo de reloj después de reqque sube. Tenga en cuenta que todas las operaciones de secuencia son síncronas con un reloj.

Otros operadores secuenciales incluyen operadores de repetición, así como varias conjunciones. Estos operadores permiten al diseñador expresar relaciones complejas entre los componentes del diseño.

Una afirmación funciona intentando continuamente evaluar una secuencia o propiedad. Una afirmación falla si la propiedad falla. La secuencia anterior fallará siempre que reqsea baja. Para expresar con precisión el requisito que gntsigue requna propiedad se requiere:

propiedad req_gnt ; @( posedge clk ) req |=> gnt ; propiedad final      afirmar_req_gnt: afirmar propiedad ( req_gnt ) más $ error ( "solicitud no seguida de gnt." );     

Este ejemplo muestra un operador de implicación|=> . La cláusula a la izquierda de la implicación se llama antecedente y la cláusula a la derecha se llama consecuente . La evaluación de una implicación comienza mediante intentos repetidos de evaluar el antecedente. Cuando el antecedente tiene éxito , se intenta el consecuente, y el éxito de la afirmación depende del éxito del consecuente. En este ejemplo, el consecuente no se intentará hasta que reqalcance el nivel alto, después de lo cual la propiedad fallará si gntno está alto en el siguiente reloj.

Además de las afirmaciones, SystemVerilog admite suposiciones y cobertura de propiedades. Una suposición establece una condición que una herramienta de demostración lógica formal debe asumir como verdadera . Una afirmación especifica una propiedad que debe demostrarse como verdadera. En la simulación , tanto las afirmaciones como las suposiciones se verifican frente a estímulos de prueba. La cobertura de propiedad permite al ingeniero de verificación verificar que las afirmaciones estén monitoreando con precisión el diseño. [ impreciso ]

Cobertura

La cobertura aplicada a los lenguajes de verificación de hardware se refiere a la recopilación de estadísticas basadas en eventos de muestreo dentro de la simulación. La cobertura se utiliza para determinar cuándo el dispositivo bajo prueba (DUT) ha sido expuesto a una variedad suficiente de estímulos como para que exista una alta confianza de que el DUT esté funcionando correctamente. Tenga en cuenta que esto difiere de la cobertura de código que instrumenta el código de diseño para garantizar que se hayan ejecutado todas las líneas de código del diseño. La cobertura funcional garantiza quese hayan explorado todos los casos de esquina yen el espacio de diseño .

Un grupo de cobertura SystemVerilog crea una base de datos de "bins" que almacena un histograma de valores de una variable asociada. También se puede definir la cobertura cruzada, lo que crea un histograma que representa el producto cartesiano de múltiples variables.

Un evento de muestreo controla cuándo se toma una muestra. El evento de muestreo puede ser un evento Verilog, la entrada o salida de un bloque de código o una llamada al samplemétodo del grupo de cobertura. Es necesario tener cuidado para garantizar que los datos se muestreen sólo cuando sean significativos.

Por ejemplo:

clase eth_frame ; // Definiciones como las anteriores covergroup cov ; destino del punto de cobertura { contenedores bcast [ 1 ] = { 48'hFFFFFFFFFFFF }; contenedores ucast [ 1 ] = predeterminado ; } punto de cobertura f_type { longitud de contenedores [ 16 ] = { [ 0 : 1535 ] }; contenedores escritos [ 16 ] = { [ 1536 : 32767 ] }; contenedores otros [ 1 ] = predeterminado ; } psize: carga útil del punto de cobertura . tamaño { tamaño de contenedores [] = { 46 , [ 47 : 63 ], 64 , [ 65 : 511 ], [ 512 : 1023 ], [ 1024 : 1499 ], 1500 }; }                                                      sz_x_t: cruz f_type , psize ; clase final del grupo final    

En este ejemplo, el ingeniero de verificación está interesado en la distribución de tramas de difusión y unidifusión, el campo tamaño/tipo_f y el tamaño de la carga útil. Los rangos en el punto de cobertura del tamaño de la carga útil reflejan los casos de esquina interesantes, incluidos los marcos de tamaño mínimo y máximo.

Sincronización

Un entorno de prueba complejo consta de componentes de verificación reutilizables que deben comunicarse entre sí. La primitiva ' evento ' de Verilog permitía que diferentes bloques de declaraciones de procedimiento se activaran entre sí, pero hacer cumplir la sincronización de subprocesos dependía del uso (inteligente) del programador. SystemVerilog ofrece dos primitivas específicamente para la sincronización entre subprocesos: buzón y semáforo . El buzón se modela como una cola de mensajes FIFO . Opcionalmente, el FIFO puede tener parámetros de tipo para que solo puedan pasar a través de él objetos del tipo especificado . Normalmente, los objetos son instancias de clase que representan transacciones : operaciones elementales (por ejemplo, enviar una trama) que ejecutan los componentes de verificación. El semáforo se modela como un semáforo de conteo .

Mejoras generales al Verilog clásico.

Además de las nuevas funciones anteriores, SystemVerilog mejora la usabilidad de las funciones de lenguaje existentes de Verilog. Las siguientes son algunas de estas mejoras:

Además de esto, SystemVerilog permite una interfaz conveniente para idiomas extranjeros (como C/C++), mediante SystemVerilog DPI (Interfaz de programación directa).

Software de verificación y síntesis.

En la función de verificación del diseño, SystemVerilog se utiliza ampliamente en la industria del diseño de chips. Los tres mayores proveedores de EDA ( Cadence Design Systems , Mentor Graphics , Synopsys ) han incorporado SystemVerilog en sus simuladores HDL de lenguaje mixto . Aunque ningún simulador puede aún afirmar ser compatible con todo el Manual de referencia del lenguaje SystemVerilog, lo que hace que la interoperabilidad del banco de pruebas sea un desafío, se están realizando esfuerzos para promover la compatibilidad entre proveedores. [ ¿ cuando? ] En 2008, Cadence y Mentor lanzaron la Metodología de Verificación Abierta, una biblioteca de clases de código abierto y un marco de uso para facilitar el desarrollo de bancos de pruebas reutilizables y IP de verificación predefinidas. Synopsys, que había sido el primero en publicar una biblioteca de clases SystemVerilog (VMM), respondió posteriormente abriendo su VMM patentado al público en general. Muchos proveedores externos han anunciado o ya han lanzado la IP de verificación SystemVerilog.

En la función de síntesis de diseño (transformación de una descripción de diseño de hardware en una lista de acceso ) , la adopción de SystemVerilog ha sido lenta. Muchos equipos de diseño utilizan flujos de diseño que involucran múltiples herramientas de diferentes proveedores. La mayoría de los equipos de diseño no pueden migrar al diseño RTL de SystemVerilog hasta que todo su conjunto de herramientas de front-end ( linters , verificación formal y generadores automatizados de estructuras de prueba ) admitan un subconjunto de lenguaje común. [¿ necesita actualización? ]

Ver también

Referencias

  1. ^ Rich, D. “La evolución de SystemVerilog” Diseño y prueba de computadoras IEEE, julio/agosto de 2003
  2. ^ IEEE aprueba SystemVerilog, revisión de Verilog
  3. ^ IEEE 1800-2012, IEEE , 2012
  4. ^ IEEE 1800-2017, IEEE , 2017
  5. ^ IEEE 1800-2023, Borrador de estándar aprobado por IEEE para SystemVerilog: lenguaje de verificación, especificación y diseño de hardware unificado, IEEE , 2023
  6. ^ Afirmación de SystemVerilog: Introducción

enlaces externos

Referencia del estándar IEEE
Tutoriales
Desarrollo de estándares
Extensiones de idioma
Herramientas en línea
Otras herramientas
  1. ^ Programa IEEE GET, IEEE