Verilog es un lenguaje de descripción de hardware (HDL, Hardware Description Language) diseñado para modelar sistemas y circuitos electrónicos. Este lenguaje está especificado por el estándar IEEE 1364 y soporta el diseño, prueba e implementación de circuitos analógicos, digitales y de señal mixta con diferentes niveles de abstracción. Los lenguajes de descripción de hardware difieren en los lenguajes de programación de software, debido a que incluyen mecanismos para describir los tiempos de propagación y dependencias entre señales. Con HDL, los diseños de circuitos se hacen en niveles muy abstractos, lo que permite independizarse de la tecnología que será utilizada para la fabricación. Esto es de gran ayuda para verificar el funcionamiento de un diseño y su optimización.

Los lenguajes HDL como Verilog y VHDL son utilizados generalmente para programar FPGA (Field Programmable Gate Array). Estos dispositivos programables contienen bloques de lógica cuya interconexión y funcionalidad puede ser configurada mediante uno de estos lenguajes de descripción especializados (HDL).


Altera Flex EPF10K20RC240-4, un viejo FPGA de 20.000 celdas (imagen de dominio público).

Se trata precisamente de programar hardware.



Icarus Verilog es un compilador de lenguaje Verilog desarrollado para GNU/Linux, pero que puede funcionar correctamente en Windows gracias a la pila MinGW (Minimal GNU for Windows). MinGW es el mismo compilador que utiliza, por ejemplo, el IDE para desarrollo en lenguaje C Code::Blocks. Ya sea en sus versiones para Linux como en sus versiones para Windows, Icarus Verilog es liberado bajo la licencia GNU GPL.

Es un compilador liviano e incluye el simulador GTKWave para verificar el funcionamiento de un diseño.

Este artículo cubre el proceso de instalación y configuración de Icarus Verilog en Windows, así como el uso básico del mismo a través de un programa (diseño) de prueba. En un próximo artículo explicaré cómo instalar y configurar Icarus Verilog en GNU/Linux.

Instalación de Icarus Verilog en Windows

El proceso de instalación de Icarus Verilog es similar al de cualquier otra pieza de software en Windows (siguiente... siguiente... siguiente...)

Descargar la última versión disponible de Icarus Verilog para Windows desde bleyer.org/icarus/. Luego ejecutar el instalador y seguir los pasos.

Para que Icarus Verilog funcione correctamente es necesario especificar un directorio de instalación que no contenga espacios en el nombre. Seleccionar la instalación completa y agregar las rutas a los ejecutables al PATH.

Es común que, a pesar de que la instalación finalice con éxito, las rutas a los binarios no sean agregadas correctamente a la variable de entorno PATH. Para agregarlas manualmente, acceder a la configuración avanzada del sistema (Advanced system settings). En Windows 7 se hace desde Control Panel > System and Security > System:

Luego presionar el botón "Environment Variables..." y editar la variable de sistema (System variables) "Path":

Agregar las rutas a la variable "Path" de la siguiente forma: ;C:\iverilog\gtkwave\bin\;C:\iverilog\bin\ (al final de la cadena) ya que en Windows las rutas en la variable de entorno PATH se separan con punto y coma.

Para comprobar la instalación, simplemente abrir una consola y ejecutar iverilog. Si la salida dice "no source files", significa que ha sido instalado exitosamente.

Primer desarrollo con Verilog

Ahora que Icarus Verilog ha sido instalado correctamente, veamos cómo diseñar un flip-flop JK activado por flanco con Verilog, cómo compilarlo con Icarus Verilog, y cómo verificar su funcionamiento con GTKWave. A modo de ejemplo de trabajo con Verilog.

Tal como mencioné anteriormente, Verilog está definido por el estándar IEEE 1364. Como la mayoría de los estándares IEEE, no es gratuito, es necesario pagar para acceder a la especificación del mismo. Sin embargo en la página de Wikipedia en inglés existe un buen resumen del mismo: Verilog (Wikipedia en inglés).

Los programas en Verilog se separan en módulos, por ende desarrollaremos un módulo que funcione como flip-flop JK activado por flanco descendente:

La tabla de verdad condensada de un flip-flop (o biestable, en español) J-K es la siguiente:

JKQQ'
0x00
1x01
x110
x011

Un módulo de Verilog es un bloque funcional que posee entradas, salidas y una lógica interna. Pueden pensarse como bloques en un diagrama de bloques de un circuito. Existen dos tipos de módulos: de comportamiento y de estructura. Ambos tipos pueden tener el mismo funcionamiento pero difieren en la forma en que se escriben. Para implementar y testear el flip-flop se utilizan tres módulos: el módulo que implementa el flip-flop, el módulo de testeo, y el testbench. Estos últimos dos módulos son necesarios en casi todo diseño, a fin de poder testear un circuito.

Código del módulo ffjk:

// Módulo ffjk - representa un flip-flop JK
module ffjk(j, k, clk, q, qn);
	input j, k, clk;
	output q, qn;
	reg q, qn;

	// Inicializar las salidas del flip-flop
	initial
	begin
		q=0;
		qn=1;
	end

	// Cambiar el estado (q) en el flanco negativo del reloj
	always @(negedge clk) 
	begin
		if(j==1'b0 && k==1'b1)
		begin
			q <= 1'b0;
		end
		else if(j==1'b1 && k==1'b0)
		begin
			q <= 1'b1;
		end
		else if(j==1'b1 && k==1'b1)
		begin
			q <= ~q;
		end
		qn <= ~q;
	end
endmodule

En este código se puede observar la estructura básica de un módulo de Verilog. Comienza con la declaración del módulo (module ffjk(j, k, clk, q, qn);) y finaliza con endmodule. La declaración indica el nombre del módulo junto con sus parámetros. Dentro del módulo se debe indicar cuales variables corresponden con entradas y cuales con salidas, utilizando las directivas input y output respectivamente.

Las variables en Verilog pueden ser registros o cables. Un cable no tiene memoria, con lo cual no pueden retener valores. La palabra clave reg le da a una variable la capacidad de mantener su valor una vez asignado. Debido a que se requiere mantener el estado del flip-flop, se definen las salidas q y qn como registros. De lo contrario ambas salidas nunca serían vistas por otros bloques, ya que su valor se perdería inmediatamente una vez asignado. No es necesario definir el resto de variables como cables (j, k y clk) ya que lo son por defecto.

Los valores numéricos se interpretan como <tamaño> <base> <valor>. Por ejemplo, el valor 1'b0 se descompone de la siguiente forma:

  • 1: tamaño de 1 bit.
  • 'b: base binaria.
  • 0: valor igual a cero (1 bit).

Esto representa un bit valiendo cero. El tamaño es opcional. Si no se especifica es 32 bits por defecto.

El modo en que se define la lógica interna de un módulo depende si es de comportamiento o estructural. El módulo para el flip-flop JK es un ejemplo de tipo de módulo de comportamiento. Para ello se definen los bloques initial y always. El código dentro del bloque initial se ejecuta una sola vez, cuando el flip-flop es creado. En Verilog, por defecto las variables no son inicializadas (tienen el valor "x": ni cero, ni uno, ni alta impedancia), por ello se establecen valores concretos para las salidas q y qn dentro del bloque initial.

El código dentro del bloque always se ejecuta cada vez que se cumple una condición. En este caso cuando el reloj (variable clk) tiene un flanco descendente. Esta condición reasigna las variables q y qn, las cuales representan el estado del flip-flop. Esto describe completamente la lógica del mismo.

Como se observa, es simple, y cuando una condición no se cumple Verilog mantiene el valor de las salidas. Cabe detacar que, como regla general, cuando se escribe un módulo de comportamiento, las salidas se definen como cables (no registros como en este ejemplo).

Verilog posee estructuras de control como while, if-else, for y repeat como cualquier otro lenguaje de programación. Se recomienda leer al menos alguno de los tutoriales que se sugieren más adelante en este artículo.

Código del módulo de testeo:

// Módulo de testeo, envía la señal de reloj al flip-flop
module tester(q, qn, clk, j, k);
	input q, qn;
	output clk, j, k;
	reg clk, j, k;

	// Correr el test sólo una vez
	initial
	begin
		// Inicializar el reloj
		clk = 0;
		// Volcar los resultados de la simulación en el archivo ffjk.vcd
		$dumpfile("ffjk.vcd");
		$dumpvars;
		// Generar las señales de entrada j y k
		j = 0;
		k = 0;
		#3 j = 1; #2 k = 1; #4 j = 0; #5 j = 1;
		#4 $finish;
	end

	// Generar la señal de reloj periódica
	always
	begin
		#2 clk=!clk;
	end
endmodule

Este módulo tiene como objetivo probar el funcionamiento del flip-flop generando los pulsos de reloj y las entradas J y K de acuerdo al diagrama de temporizado anterior. Como salida otorga las señales Q y QN del flip-flop. Las salidas de este módulo hacen de entradas al flip-flop y viceversa.

Este módulo también es de comportamiento, pues cuenta con bloques initial y always. Los comandos $dumpfile y $dumpvars le indican al simulador de Verilog que registre las variables del módulo en el archivo especificado ("ffjk.vcd"). Los comandos que comienzan con el caracter numeral son retrasos (delays). Estos delays de Verilog retrasan la instrucción siguiente por una determinada cantidad de tiempo. Por ejemplo la línea #2 clk=!clk; dentro del bloque always cambia "clk" cada cuatro unidades de tiempo, produciendo una onda cuadrada. La unidad de tiempo por defecto es igual a 1 segundo. No hay forma de que este programa funcione sin utilizar delays. Esta es la forma de controlar el temporizado del diseño.

Por último, el comando $finish le indica al simulador que detenga la simulación una vez que las señales j y k hayan sido generadas. Si se omite este comando, la simulación continúa de manera indefinida.

Al cambiar el valor del reloj cada 2 unidades de tiempo, el período resulta ser igual a 4 unidades de tiempo. De acuerdo al diagrama anterior, y partiendo de un tiempo inicial igual a 0: en el tiempo 3 la variable j cambia a 1; en 5 la variable k cambia a 1; en 9 j vuelve a 0; y en 14 j cambia nuevamente a 1. La simulación finaliza en el tiempo 18. De esta forma, los retrasos se acumulan de la siguiente forma:

		#3 j = 1; #2 k = 1; #4 j = 0; #5 j = 1;
		#4 $finish;

Por último es necesario definir el módulo de testbench (banco de pruebas):

// El banco de pruebas conecta el tester con el flip-flop
module testbench;
	wire clk, j, k, q, qn;
	ffjk flipflop1(j,k,clk,q,qn);
	tester test1(q,qn,clk,j,k);
endmodule

Este es el más simple de los módulos pero es diferente, ya que se trata de un módulo estructural. Define la estructura del circuito, como si se tratase de un diagrama. En este caso el circuito final es simplemente el flip-flop conectado al tester. En este caso, dentro del módulo estructural se utilizan cables, ya que los registros están definidos dentro de los módulos de comportamiento.

Compilar y simular el circuito

Copiar el código de los tres módulos y pegarlo en un único archivo llamado ffjk.v. El orden de los módulos no importa. La extensión .v es opcional, pero es la que se utiliza típicamente para identificar a los archivos fuentes de Verilog.

Para compilar el diseño, abrir una consola y cambiar al directorio donde se encuentra el archivo ffjk.v. También es posible utilizar directamente la opción "Open command windows here" del menú contextual del explorador de Windows, al que se accede haciendo clic derecho sobre la carpeta mientras se mantiene presionada la tecla SHIFT.

Descarga: ffjk.v

Compilar mediante el comando:

iverilog -o ffjk ffjk.v

La opción -o indica que la salida sea al archivo "ffjk". Este archivo no es ejecutable, sino que debe ejecutarse mediante el simulador vvp. Este es el simulador de Icarus Verilog, el cual produce los resultados de la simulación en forma de ceros y unos para cada variable modelada, en función del tiempo. Esto es, precisamente, señales.

Ejecutar la simulación mediante el comando:

vvp ffjk

Esto produce la salida en el archivo "ffjk.vcd", el cual contiene los resultados de la simulación. El formato de salida de este archivo no es fácil de comprender. Sin embargo, es posible abrirlo con GTKWave para generar las gráficas de tiempo de cada señal:

gtkwave ffjk.vcd

Ahora es necesario agregar las variables para verlas en el diagrama de tiempo. Seleccionar el "testbench" en el cuadro superior izquierdo. Luego seleccionar todas las variables (o sólo las variables que se deseen graficar) en el cuadro inferior izquierdo, y presionar el botón "Insert":

Si todo salió bien (especialmente el temporizado de las señales) se obtiene un gráfico exactamente igual al planteado en el inicio (comparar el gráfico del comienzo del artículo con el producido por GTKWave).

Cuando se hace el testeo de un circuito, es necesario compilar-simular-graficar con cierta frecuencia. En este caso GTKWave tiene la opción de recargar las gráficas desde la salida de la simulación desde el menú "File > Reload Waveform". Esto permite refrescar los diagramas de temporizado de señales de forma rápida.

Tutoriales sobre Verilog

Referencias


Tal vez pueda interesarte


Compartí este artículo