TUTORIAL VERILOG v.1.1
El presente trabajo tiene por objetivo abordar de una manera rápida y concisa el diseño de elementos de electrónica digital mediante el lenguaje de descripción de hardware VERILOG.
La simulación de los códigos presentados se la efectúa en el programa ISE de Xilinx.
Contador de 4 bits
Contador síncrono de 4 bits a través de la seña “countout”, habilitado por la señal “ena” cuando esta posee un estado lógico ALTO, y llevado a cero mediante el estado lógico ALTO de la señal “rst”.
Diagrama de bloques
![]() |
Figura 1. Esquemático de contador síncrono de 4 bits. |
Diseño
El primer paso al diseño es la creación del módulo. En el contexto de VERILOG un módulo describe a un componente básico dentro de un diseño, puede ser un elemento o un conjunto de elementos que conforman un diseño superior (high-level). Un módulo provee su funcionalidad a un nivel más superior mediante sus puertos de interfaz (inputs, outputs), pero esconde su estructura interna, permitiendo al diseñador cambiar internamente al módulo sin afectar el resto del diseño.
Para describir mediante VERILOG a un módulo, se hace uso de la palabra reservada module seguido del nombre o identificador del módulo, luego entre paréntesis y separados por comas, se nombran los puertos de E/S (lista de terminales) que interactúan con el mundo exterior.
// Especificación de módulo (por medio de // se puede escribir comentarios de una línea que ayuden a la documentación del diseño) module Contad ( clk, ena, rst, countout ); // ß Poner atención con el punto y coma endmodule |
Dentro del módulo, el siguiente paso es declarar la dirección de los puertos, los puertos de entrada se especifican mediante input <tamaño> <nombre*>, y los puertos de salida mediante output <tamaño> <nombre*>, quedando el código de la siguiente manera:
*Son los mismos nombres declarados en el módulo
module Contad ( clk, ena, rst, countout ); input clk; input rst; input ena; // Puertos de Salida output [3:0] countout; endmodule |
Tipos de Datos
En este paso se deben definir los tipos de datos, en VERILOG se utiliza dos tipos de datos principalmente: Nets y Registers.
-
Nets: Utilizados para representar conexiones estructurales entre componentes. No permiten almacenar datos. De los tipos de nets el más utilizado es el tipo wire, el resto de tipos y sus aplicaciones se detallan en [1]
-
Registers: Representan variables que tienen la capacidad de almacenar datos. Retienen el valor de un dato hasta que un nuevo valor es colocado sobre estos. Los tipos de registers más comunes son: reg e integer, estos últimos se utilizan en la creación de test benches.
Para los puertos declarados como entrada (input), el tipo de dato no suelen definir, por defecto son tomados como tipo wire. En los puertos declarados como salida (output) pueden ser de tipo wire o registers.
-
Vectores: Los tipos de datos registers o nets pueden ser declarados como vectores (arreglos de bits). Si el tamaño de bits no es especificado, es escalar por defecto (1-bit).Los vectores pueden ser declarados como [#mayor: #menor] o [#menor: #mayor], pero siempre el número de la izquierda dentro de los paréntesis es tomado como el más significativo.
El resto de tipos de datos como Integer, Real, Time register, Arrays, Memories, Strings, Parameters, se describen a detalle en [1]
Retomando el diseño se tiene:
module Contad ( clk, ena, rst, countout ); // Puertos de Entrada input clk; input rst; input ena; // Puertos de Salida output [3:0] countout; // vector de 4 bits el countout(3) es el MSB y countout(0) es el LSB // Declaración de tipo de datos: // Entradas (Opcional pero útil): wire clk; wire rst; wire enable; // Salidas: reg [3:0] countout // Este elemento debe retener su valor. endmodule |
Procesos
El concepto de procesos que se ejecutan en paralelo es una de las características básicas de VERILOG, siendo ese uno de los aspectos diferenciales con respecto a lenguajes de programación comunes como C.
Toda descripción de comportamiento en lenguaje VERILOG debe declararse dentro de un proceso, aunque existe una excepción que se la trata a detalle en [2].Existen en VERILOG dos tipos de procesos, también denominados bloques concurrentes.
- Initial. proceso se ejecuta una sola vez al tiempo cero. Este proceso no es sintetizable, es decir no se puede utilizar en una descripción RTL. Su uso está definido en la realización del testbenches.
-
Always. Este tipo de proceso se ejecuta continuamente a modo de lazo. Tal y como su nombre indica, se ejecuta siempre. Este proceso es totalmente sintetizable. La ejecución de este proceso está controlada por una temporización (síncrona) o por eventos. En este último caso, si el bloque se ejecuta por más de un evento, al conjunto de eventos se lo denomina “lista sensitiva” [2].
Para el diseño de ejemplo, el contador debe actualizar su valor con cada flanco de subida de la señal de reloj denominada “clk”. Por tanto la señal clk se incluye en la lista sensitiva, mediante el carácter @ seguido del evento, se controla el instante en que se produce un proceso always. De la siguiente forma:
always @ (posedge clk)
Se distinguen dos tipos de eventos:
Eventos de nivel
Este evento se produce por el cambio de valor de una variable simple o de una lista sensible. Ejemplos:
| Evento | Descripción |
always @(x) z<= x + y; |
Cada vez que el valor de la señal “x” cambia, el proceso se evalúa. |
always @(x or y or z) t <= x + y + z |
Cada vez que el valor de la señal “x” ó “y” ó “z” cambia, el proceso se evalúa. En este caso x, y, y z conforman la lista sensible. |
Eventos de flanco:
Se producen por la combinación de flancos de subida y/o bajada de una señal simple o de una lista sensitiva. Ejemplos:
| Evento | Descripción |
always @(posedge clk or posedge irt) z<= z + y; |
Cada vez que se produce un flanco de subida de clk o de irt, el proceso se evalúa. |
always @(posedge clk or negedge irt) z<= z+ y; |
Cada vez que se produce un flanco de subida de clk o un flanco de bajada de irt, el proceso se evalúa. |
Complementando, el diseño queda de la siguiente forma:
module Contad ( clk, ena, rst, countout ); // Puertos de Entrada input clk; input rst; input ena; // Puertos de Salida output [3:0] countout; // vector de 4 bits el countout(3) es el MSB y countout(0) es el LSB // Declaración de tipo de datos: // Entradas (Opcional, pero útil): wire clk; wire rst; wire enable; // Salidas: reg [3:0] countout // Este elemento debe retener su valor. always @(posedge clk) begin: COUNTER // Inicio del contador. if (rst ==1’b1)begin countout<= 4’b0000; end else if (ena == 1’b1)begin
countout<= countout + 1; end end // Fin del bloque COUNTER endmodule |
Simulación
La simulación de este diseño se la realiza en el software ISE de Xilinx. Mediante la creación de un Testbench, los resultados obtenidos son:
![]() |
Figura 2. Simulación de contador de 4 bits. |
Bibliografía
[1]. Samir Palnitkar, Verilog HDL A guide to Digital Design and Synthesis,Ed. Sunsof t Press,1996 .
[2]. Deepak Kumar Tala, “Verilog Tutorial”.
APÉNDICE
Formato de números
Existen dos tipos de especificación del formato de números en VERILOG: sized y unsized:
Formato sized
Son representados de la siguiente manera: <tamaño>’<base> <número>.
<tamaño> es escrito sólo en decimal y especifica el número de bits para la representación del número.
<base> formatos válidos para especificar la base son: decimal (d ó D), hexadecimal (h ó H), binario (b ó B) y octal (o ó O).
<número> especificado de forma consecutiva mediante los dígitos : 0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f. Tomando en cuenta el rango válido para cada base.
Ejemplos:
4’b1111 |
Este es un número binario de 4 bits |
12’habc |
Este es un número hexadecimal de 12 bits |
16’d255 |
Este es un número decimal de 16 bits |
Formato Unsized
Números que son especificados sin un formato para la base <base>, se consideran números decimales por automáticamente. Números en los que no se especifica el tamaño por defecto es de al menos 32 bits según la especificación del simulador.
Ejemplos :
1234 |
Este es un número decima de 32 bits , por defecto |
’hc3 |
Este es un número hexadecimal de 32 bits |
‘021 |
Este es un número octal de 32 bits |
Tipos de Operaciones
Otras
Bit a bit (Bitwise)
Sean:
X= 4b’0001;
Y= 4b’1101;
Z= 4b’10x1;
~X // Operando NOT. Resultado: 4’b1110
X & Y // Operando AND. Resultado: 4’b0001
X | Y // Operando OR. Resultado: 4’b1101
X ^ Y // Operando XOR. Resultado: 4’b 1100
X | Z // Resultado: 4’b10x1
Cabe diferenciar el resultado de estos operandos de tipo “bitwise” con los operandos lógicos (!, &&, ||), estos últimos son utilizados en sentencias condicionales cuyo resultado es 1L ,0L ó x.
Ejemplo
if ((CEz==0)&&(OEz==0)) begin
regD <= #(AT) memo[A]; access=1;
end
Operador Condicional (Ejemplo)
“?” El formato de este operador es: (condición) ? <true_expr>:<false _expr>
Ejemplo
out <= (enable) ? a:b; // out toma el valor a cuando enable esta en 1L , caso contario out toma el valor de b
Operador de Igualdad (Ejemplo)
Es importante denotar la diferencia entre los operadores (== y!=) con (=== y !==). Las operaciones lógicas con (== y !=) arrojan resultados “x(indefinido)” o “z(alta impedancia)” , si alguno de los dos operandos a comparar contienen “x” o “z” entre sus bits. En cambio las operaciones case de igualdad (=== y !==) comparan los dos operandos bit a bit incluyendo “x” y “z”, resultando 1L si concuerdan, o 0L si son diferentes, las operaciones case de igualdad nunca arroja resultados del tipo “x” o “z”
Ejemplos:
// A=4, B=3
//X = 4’b1010, Y = 4’b1101
//Z= 4’1xxz, M = 4’b1xxz, N= 4’b1xxx
A==B // Resulta un 0L
X !=Y // Resulta un 1L
X ==Z // Resulta in “x”
Z===M // Resulta un 1L (todos los bits coinciden, incluyendo “x” y “z”)
Z===N // Resulta un 0L (LSB no coinciden)
M !== N // Resulta un 1L




