Escrita por: Alix Angarita

Revisado por: Holguer A. Becerra

Objetivos

  • Dar un repaso general a la sintaxis de la descripción de hardware en Verilog
  • Describir algunos dispositivos útiles durante el desarrollo del curso como multiplexores, contadores, sincronizadores y divisores de frecuencia
 
Diapositivas Base:

Procedimiento

Para empezar es necesario crear un proyecto, esto se hará a través del System Builder.

Al ejecutarlo se abrirá una interfaz como la de la siguiente figura, es importante notar las partes de la tarjeta seleccionadas, que son las que usaremos en nuestro proyecto.  Llamaremos a este ejemplo test_clase1

Después de hacer clic en Generate se nos abrirá una ventana preguntando dónde deseamos guardar, preferiblemente dentro de una carpeta destinada para todos los proyectos del curso, y seleccionamos guardar.

Ya podemos salir del System builder con el botón Exit.  Para empezar a trabajar en nuestro proyecto tenemos que buscar en la ubicación que elegimos (por default estará en la misma carpeta del System builder en una subcarpeta llamada CodeGenerated) y abrirlo con el software quartus II.

 

Nos abrirá algo como esto:

 

Al hacer doble clic en test_clase1 en el Project Navigator se nos abrirá el archivo Verilog que se puede visualizar en el área central.

En el código podemos encontrar las declaraciones de las entradas y salidas que dejamos seleccionadas en la ventana del System Builder.

Este archivo representa el chip principal, dentro del cual describiremos los elementos que deseamos para esta práctica, comenzando por un multiplexor.

 

Descripción de un multiplexor

Estando en la misma ventana que acabamos de ver, seleccionamos Nuevo > Verilog HDL File > OK


 

Se nos abrirá nuestro nuevo archivo Verilog, dentro del cual empezaremos nuestra descripción del multiplexor, en este caso deseamos que sea de 4 a 1, con cada entrada de 8 bits.

Al describir cualquier elemento primero declaramos el módulo, que será llamado igual que el archivo sobre el que estamos trabajando, en este caso lo declararemos como ejem_mux

 

Seguimos indicando cuáles serán las entradas y salidas de nuestro multiplexor, usaremos las cuatro entradas como constantes, y los selectores como “variables”, se declaran así:


Cabe aclarar que el selector tiene dos bits, ya que son necesarios para abarcar las 4 posibles entradas a multiplexar, y la salida tiene 8 bits tal como se indicó en la figura.

El siguiente paso es asignar la salida, para esto usaremos condiciones, que nos permitirán según un valor fijado en el selector, poner la salida deseada.

Esta es la descripción completa del multiplexor, ahora veremos cómo instanciarlo dentro del chip principal, es importante prestar mucha atención a este proceso, ya que lo usaremos para los siguientes elementos que describiremos.

En primer lugar crearemos la instanciación, esto en File > Create/Update > Create Verilog Instantiation Template Files for Current File


Esto habrá creado un archivo llamado ejem_mux_inst.v en la carpeta del proyecto, podemos abrirlo con cualquier editor de texto


El texto será así:


Se copiará esto dentro del chip principal, poniendo un nombre de nuestra elección y borrando lo que hay dentro de los paréntesis, es allí donde indicaremos que entradas y salidas del chip principal corresponden.


Para este caso se usaron dos interruptores para los selectores, y ocho leds para visualizar la salida.

Ya tenemos listo nuestro multiplexor dentro del chip principal con todas las conexiones hechas, si queremos ver y comprobar gráficamente que todo esté en su lugar vamos a RTL viewer desde la sección de tareas y le damos clic derecho > open:


Desde la ventana del RTL Viewer revisamos el diagrama esquemático, notando que todo está tal cual lo describimos. 


Continuamos con Analysis & Synthesis, Fitter y Assembler.  Si no aparece ningún error y el proceso finaliza exitosamente sabremos que todo ha sido descrito correctamente y que nuestro diseño se ajusta a las características de la FPGA que estamos utilizando.

Para revisar el módulo terminado e implementarlo en los siguientes proyectos, se puede descargar de aquí.

Descripción de un contador

Un contador se puede construir con un Flip-Flop tipo D y un sumador, los cuales se conectarían de la siguiente manera:

Se declara el módulo con el nombre contador y se guarda el archivo con el mismo nombre, luego se declaran las entradas y las salidas, resultando algo como esto:


Las entradas como se puede ver a la izquierda del diagrama corresponden a una señal de reloj y una de reset para reiniciar el conteo.  A la salida tenemos 8 bits que irán cambiando a medida que se vaya contando.

Aquí se introduce un nuevo concepto, y es la descripción del flip-flop tipo D, que simplemente se hace a través de un registro de 8 bits que llamaremos conteíto.  Los registros siempre deben ser inicializados.

reg [7:0]conteito=8'd0;

 

Pero aparece una situación particular cuando se usan registros, es necesario usar un proceso llamado always para asignarle su comportamiento, también se trabaja con lógica secuencial en vez de combinacional, por lo que las asignaciones se harán con el símbolo <=.

Al usar always, se requiere una señal como argumento para realizar el proceso a cada cambio de esta, para este caso realizaremos el proceso contar siempre que se tenga un flanco de reloj en el flip-flop, por lo que se describe así:


Lo que vamos a hacer para contar es ir aumentando en uno el registro conteíto, esto sólo interrumpido cuando se pone un valor bajo en la entrada reset (activo bajo).

La lógica descrita fue:


Ya listo el contador, genera la instanciación igual que con el multiplexor.

Con esto, instanciamos el contador en el chip principal, usando el reloj de la FPGA para la señal de reloj, una Key para el reset y ocho leds para visualizar el conteo.

Revisando el RTL, encontramos que nuestras conexiones estuvieron bien descritas y que la descripción fue exitosa.


No olvidar el proceso de análisis y síntesis, enrutar y ensamblar para verificar que todo esté perfectamente diseñado y descrito.

El módulo de verilog se puede descargar aquí.

Descripción de un sincronizador

A veces, cuando una señal de entrada empieza después del flanco que se analiza y termina antes del siguiente flanco a analizar ésta pasa inadvertida, perdiéndose información importante o dejando sin ejecutar alguna acción que puede resultar en un grave problema para el sistema, para esto fueron diseñados los sincronizadores.

Un sincronizador sirve de puente entre dos dispositivos con diferentes frecuencias, y consta de dos entradas, una de reloj y otra de la señal asíncrona que queremos recuperar, a la salida tenemos una señal sincronizada que nos evitará cualquier problema de este tipo.


Para esta descripción nuestras entradas y salidas serán las que declaramos, una entrada asíncrona, una de reloj y la salida síncrona

Tenemos un diseño de sincronizador, el cual consiste en tres flip-flops y un arreglo de compuertas que permite resetear el primer flip-flop según el dato proporcionado a la salida del primero.  Los otros dos flip-flops, como se puede ver en una imgen anterior, funcionan con el reloj del sistema al cual se va a conectar y generan la señal síncrona como salida del sincronizador.
 
Para describirlo se requiere de un registro por cada flip-flop, tal como se usó para el contador, sin olvidar indicar su estado incial; descripción de lógica combinacional para las compuertas, y cables para conexiones adicionales.
 
Se usan dos procesos always, ya que los registros usan dos clocks diferentes, en el primer always se trabaja con los flancos de subida de la señal asíncrona y del reset cuando se cumplan las condiciones, en el segundo se depende unicamente del reloj al cual los flip-flops en cascada están conectados.
 
 
Para el primer always se revisa siempre el estado del reset para poner a la salida en bajo si llega un alto, y poner la salida en algo si no es así.  Para el segundo sólo se envía el dato que sale del primer flip-flop al segundo, y del segundo flip-flop al tercero.
Luego de tener esto listo se hace la instanciación y se revisan las conexiones con el RTL viewer.
Para la entrada asíncrona se usó un pulsador, y para el reloj el de 50MHz de la FPGA.
 
El sincronizador se encuentra en el siguiente enlace.
 
Divisor de frecuencia
 
Ya que para el diseño de circuitos secuenciales requeriremos de un reloj a una frecuencia determinada, requeriremos de dividir la frecuencia del oscilador interno de la FPGA con la que contamos cuantas veces sea necesario para conseguir la frecuencia deseada.
 
Por esto se describieron dos métodos de división de frecuencia, cada una con sus ventajas.  
 
Para el primer caso podremos dividir la frecuencia inicial en potencias de dos, no son muchas posibilidades, pero el método es muy sencillo.  Si tenemos una señal de por ejemplo 50MHz y queremos una señal de la mitad de la frecuencia como muestra la imagen, simplemente deberemos crear una nueva señal cuyo estados dependan del reloj de entrada, es decir, que cambien siempre que haya un flanco de la señal de entrada.
Para describirlo, hacemos un cable para generar esta nueva señal, y con un always hacemos la asignación de nuestra nueva señal, así:
 
wire clk_25mhz;
 
always@(posedge FPGA_CLK1_50)
begin
    clk_25mhz<=~clk_25mhz;
end
 

Ya que como se mencionó esto sólo permite una pequeña cantidad de posibilidades, fue necesario conocer otro diseño de división de frecuencias.

 
 
Este diseño permite con un valor de entrada constante determinado por la fórmula
 
 
Donde Fin es la frecuencia de entrada, y Fout es la frecuencia de salida.
Así tenemos casi cualquier valor de frecuencia, siempre que la constante en IN sea un entero.
 
El divisor se describe entonces usando los elementos vistos previamente, los registros, contadores y multiplexores.
Los puertos del chip se pueden ver claramente en la imagen.
 
Se declaran entonces dos registros, el primero de 32 posiciones debido a la cantidad de bits de la entrada, y otro que es el que genera la señal de salida, de forma secuancial se hace la comparación de la entrada la cual si se cumple genera un flanco en la salida y si no, mantiene su estado actual.
 
Esta sería la descripción final:
 
Se revisa la instanciación, y se conecta en el chip principal para una frecuencia deseada de 1Hz.
 
 
Si se revisa el chip internamente con el RTL viewer, tiene la siguiente configuración tal como lo describimos.
 
El divisor de frecuencia listo y descrito en verilog como lo hemos hecho se puede descargar aquí.
 
En la sección de Simulación se puede encontrar el procedimiento para simular cualquiera de los circuitos descritos previamente en el Software Modelsim.
También se puede revisar como ejemplo la simulación del divisor de frecuencia.
 
Todos estos códigos se pueden encontrar en el repositorio de Adrizcorp:
 
Multiplexor: https://github.com/Adrizcorp/ece10243upb2016/tree/master/Prof_Examples/1-1-Ejemplo_mux/test_clase1
Contador: https://github.com/Adrizcorp/ece10243upb2016/tree/master/Prof_Examples/1-2-Ejemplo_contador/test_clase1
Sincronizador: https://github.com/Adrizcorp/ece10243upb2016/tree/master/Prof_Examples/1-3-Sincronizador