Wed. Aug 17th, 2022

Este artículo presenta las técnicas para describir circuitos combinacionales en Verilog al examinar cómo usar el operador condicional para describir tablas de verdad combinatorias. También muestra cómo utilizar el bloque "siempre" Verilog para describir los circuitos combinacionales; un bloque "siempre" puede proporcionarnos una solución aún más fácil para describir un circuito digital.

En un artículo anterior, discutimos el uso de la palabra clave “asignar” de Verilog para realizar una asignación continua. Dichas asignaciones están siempre activas y pueden usarse para adquirir una descripción de circuitos digitales a nivel de puerta. Por ejemplo, en el siguiente código, que describe una puerta AND, el lado derecho se evalúa continuamente y el resultado se coloca en la red out1:

                    asignar out1 = a & b;   
                  

Verilog tiene un operador condicional (? 🙂 que nos permite verificar una condición antes de realizar tales asignaciones. La sintaxis se da a continuación:

                    asignar [signal_name] = [conditional_expression] ? [value_if_true] : [value_if_false];

                  

Se evalúa la "expresión condicional". Si es verdad, se asigna "value_if_true" a "signal_name". Si no es cierto, "signal_name" obtiene "value_if_false". Como ejemplo, considere el siguiente código:

                    asignar out1 = (sel)? (a y b): (a | b);
                  

Si "sel" es verdadero, a y b se asignarán a "out1". Si no es cierto, "out1" obtendrá un | b. Por lo tanto, el código anterior implementa la funcionalidad de un multiplexor de 2 a 1. La implementación conceptual de este código puede ser como se muestra en la Figura 1 a continuación.

La asignación condicional nos permite tener una descripción más abstracta de ciertos circuitos porque tiene la funcionalidad de una declaración "if" que se encuentra en los lenguajes de programación de computadoras tradicionales. El operador condicional se puede utilizar de forma anidada para implementar circuitos más complejos. El ejemplo 1 discute estos detalles.

Ejemplo 1: Operadores condicionales anidados

Utilice el operador condicional (? 🙂 para describir un codificador de prioridad de 4 a 2 con la siguiente tabla de verdad:

El código de Verilog para este codificador de prioridad se proporciona a continuación:

                    módulo Prio_4_to_2
(
cable de entrada [3:0] X,
cable de salida [1:0] y
cable de salida v
);

asigna y = x[3] ? 2'b11:
X[2] ? 2'b10:
X[1] ? 2'b01:
2'b00;

asigna v = (x[3] | X[2] | X[1] | X[0] )? 1'b1: 1'b0;

endmódulo

                  

Aparte de las líneas 7 a 10, el código contiene los elementos básicos del lenguaje discutidos en nuestro artículo anterior. Así que echemos un vistazo a estas líneas.

Los términos 2’b11, 2’b10, 2’b01 se refieren a las notaciones Verilog que representan números binarios de dos bits. En general, el primer número (antes de ‘b) especifica el número de bits. La letra b especifica que los números son binarios. Los dígitos después de ‘b dan el valor del número. Por lo tanto, 2'b01 es la notación Verilog para representar un número binario de dos bits con valor 01 y 3'b100 denota un número binario de tres bits con valor 100.

La línea 7 verifica el MSB de la entrada, x[3], en un operador condicional. Si x[3]= 1, la condición se evalúa como verdadera y 2’b11 se asigna a y (el valor asignado se toma de la tabla de verdad). Si x[3]= 0, la condición se evalúa como falsa y la expresión después de los dos puntos (:) se asignará a y. La expresión después de los dos puntos es el código en la Línea 8, que es en sí mismo otro operador condicional.

El segundo operador condicional en la línea 8 examina el segundo bit más significativo de la entrada, x[2], para determinar si 2’b10 debe asignarse a y o la expresión después de los dos puntos que es nuevamente otro operador condicional (Línea 9) debe ser evaluada. Puede verificar que los valores asignados a y coincidan con la tabla de verdad dada.

La salida válida (v) de la tabla de verdad será alta lógica si al menos un bit de la entrada es alta lógica. La línea 11 muestra esta descripción aplicando el operador OR a nivel de bits (|) a los bits de la entrada. En la Figura 2 se muestra una simulación de Xilinx ISE del código anterior.

Simulación ISE de Xilinx

Figura 2. Una simulación ISE de Xilinx del código anterior.

Es importante tener en cuenta que las expresiones condicionales se evalúan sucesivamente hasta que se encuentra una expresión verdadera. Se realizará la asignación correspondiente a esta expresión verdadera. Como resultado, las expresiones evaluadas anteriormente tienen una prioridad más alta en comparación con las siguientes. Esto significa que, teóricamente, un operador condicional es más adecuado para implementar una red de prioridad (Figura 3) en lugar de una estructura equilibrada como un multiplexor (Figura 4).

red prioritaria

Figura 3. Una red prioritaria.

  Un multiplexor n-1 en el que no hay prioridad entre las entradas.

Figura 4. Un multiplexor n-1 en el que no hay prioridad entre las entradas.

Un artículo anterior revela una discusión similar sobre las asignaciones concurrentes de VHDL.

Declaraciones procesales de Verilog

Podemos separar cualquier circuito combinacional en unas pocas puertas lógicas básicas (AND, OR, NOT, etc.) y usar la declaración "asignar" para describir estas puertas (una descripción a nivel de puerta). También podemos usar el operador condicional descrito en la sección anterior para tener una forma más abstracta de describir algunos circuitos combinacionales (similar a la declaración "if" de los lenguajes de programación de computadoras). Sin embargo, todavía hay una solución más poderosa: usar el bloque Verilog "siempre".

Dentro de un bloque "siempre", podemos tener sentencias de procedimiento que se ejecutan en secuencia. Además, el bloque "siempre" admite construcciones abstractas de lenguaje como las declaraciones "if" y "case".

La función de ejecución secuencial junto con las construcciones de lenguaje abstracto disponibles dentro de un bloque "siempre" nos permite describir más fácilmente la funcionalidad de un circuito, debido al hecho de que el razonamiento humano tiene una naturaleza secuencial y se basa en descripciones abstractas. Por lo general, pensamos de manera algorítmica de alto nivel en lugar de en términos de puertas lógicas de bajo nivel. Un bloque "siempre" puede proporcionarnos una solución más fácil para describir un circuito digital. Para obtener más detalles sobre por qué las HDL admiten descripciones basadas en declaraciones secuenciales, consulte mi artículo Introducción a las declaraciones VHDL secuenciales.

Ejemplo 2: Declaraciones de bloque "Siempre"

La sintaxis simplificada de un bloque "siempre" se da a continuación:

                    siempre @ (lista de sensibilidad)
empezar
secuential_statements;
fin


                  

La lista de sensibilidades especifica cuándo deben ejecutarse las sentencias secuenciales dentro del bloque "siempre". Por ejemplo, considere usar el bloque "siempre" para describir el circuito en la Figura 5.

Figura 5. Circuit_1

Cuando a o b cambia, la salida puede cambiar, lo que significa que a y b deberían estar en la lista de sensibilidad del bloque "siempre". En general, para un circuito combinacional, todas las señales de entrada deben incluirse en la lista de sensibilidad.

Ahora, podemos usar el operador AND a nivel de bits para describir la funcionalidad del circuito (a y b) y asignar el resultado a la salida. Dentro de un bloque "siempre", hay dos tipos diferentes de asignaciones: la asignación de bloqueo (=) y la asignación de no bloqueo (<=). Usando la asignación de bloqueo, obtenemos el siguiente código:

                    siempre @ (a, b)
empezar
     out1 = a & b;
fin
                  

¿Cuál es la diferencia entre una asignación de bloqueo y una asignación de no bloqueo?

Con una asignación de bloqueo, el lado derecho se evalúa y se asigna inmediatamente a out1. Por lo tanto, cuando se ejecuta la Línea 3, out1 se actualiza inmediatamente antes de pasar a la siguiente línea del código. El nombre "asignación de bloqueo" enfatiza que las líneas siguientes se bloquean hasta que se actualiza el lado izquierdo.

Con una asignación sin bloqueo, la expresión de la mano derecha se evalúa pero no se aplica a la variable de la mano izquierda hasta que llegamos al final del bloque "siempre". La elección de una asignación de bloqueo o no de bloqueo puede ser confusa para un principiante y su uso indebido puede llevar a una funcionalidad no deseada. Por ejemplo, el uso de asignaciones de bloqueo para inferir flip-flops puede introducir una condición de carrera.

Para este artículo introductorio, no profundizaremos en los detalles y solo nos ceñiremos a una simple guía para evitar posibles trampas: utilice las asignaciones de bloqueo al escribir el código para un circuito combinado. Por lo tanto, el bloque "siempre" en el Listado 1 se utilizará para describir una puerta AND.

En un artículo anterior, nos familiarizamos con el tipo de datos Verilog "wire". Este tipo de datos representa un cable físico en nuestro diseño FPGA. Dentro de un bloque "siempre", el estándar Verilog no nos permite asignar un valor a un "cable". En su lugar, utilizamos el tipo de datos "reg". El nombre "reg" es algo confuso, pero tenga en cuenta que un "reg" puede o no conducir a un elemento de almacenamiento físico en su diseño. El siguiente código es la descripción de Verilog de la Figura 5 usando un bloque "siempre". Tenga en cuenta que el tipo de datos de salida debe ser "reg" porque obtiene su valor de una asignación de procedimiento.

                    módulo Circuit_1
(
cable de entrada a,
cable de entrada b,
salida reg out1
);

siempre @ (a, b)
empezar
out1 = a & b;
fin

endmódulo
                  

En este artículo, nos familiarizamos con el operador condicional Verilog. Utilizamos la forma anidada de este operador para describir un codificador de prioridad. Luego, abordamos un constructo de lenguaje más poderoso, el bloque "siempre", para describir los circuitos combinacionales. En futuros artículos, examinaremos el uso del bloque "siempre" para implementar circuitos secuenciales.

By Maria Montero

Me apasiona la fotografía y la tecnología que nos permite hacer todo lo que siempre soñamos. Soñadora y luchadora. Actualmente residiendo en Madrid.