Verilog Práctico

Capítulo 14. Generando Tonos

12 de noviembre de 2021

En este capítulo veremos cómo generar tonos de distintas frecuencias con la Alhambra FPGA y además aprenderemos algo muy útil e importante en Verilog, interconectar módulos.

Esquema


El esquema de conexión es muy sencillo. Tan solo necesitamos un transistor, un altavoz y un par de resistencias. 

 Monta el circuito como en la imagen y conéctalo a la salida D0 de la Alhambra FPGA. La resistencia de 47 Ohm es opcional, para bajar un poco el volumen. También se puede alimentar el altavoz con 3,3V.

Ejemplo 1. Generador de tonos simples.

Código Verilog

El código Verilog consiste en un generador de frecuencias de 1 Hz. La salida del generador de frecuencias la aplicamos a una entrada de una puerta AND cuya salida activamos con el switch 1 de la Alhambra. Cuando pulsamos SW1 dejamos pasar la señal de 1 Hz hacia el altavoz, así no está siempre sonando.

module simple_tone (clk, sw1, out);


 input clk;

 input sw1;

 reg tone;

 output out;

 reg [19:0] cnt;

 parameter freq = 12000;


always @(posedge clk)

 begin

  cnt <= cnt + 1;

  if (cnt == freq)

   begin

    tone <= ~tone;

    cnt <= 0;

   end

 end


and (out, sw1, tone);


endmodule


Archivo .pcf


set_io sw1 10

set_io out 119

set_io clk 21

¿Qué es lo que hemos sintetizado en la Alhambra FPGA?


Dentro del bloque always tenemos un contador que se incrementa una unidad con cada flanco positivo del reloj. Este contador lo comparamos con un valor constante, en este caso 12.000.

Cuando el contador llega a 12.000 invertimos el valor de la señal binaria tone haciéndola pasar de 0 a 1 constantemente:  tone <= ~tone y reseteamos el contador a cero:  cnt <= 0


La cifra de 12.000 no es aleatoria. Dado que la Alhambra tiene un reloj de 12 MHz, en contar hasta 12.000 tarda 1 ms luego f = 1 / 0,001 = 1 KHz. Cambiando el valor del parámetro freq obtenemos tonos de distintas frecuencias.


 

Como decíamos más arriba, la señal  tone la aplicamos a la entrada de una puerta AND cuya salida activamos con el switch 1 de la Alhambra, para que el altavoz sólo suene cuando pulsamos SW1.

Ejemplo 2. Generador de dos tonos + multiplexor


En este ejemplo vamos a generar dos tonos de distinta frecuencia. Conectaremos las salidas del generador de tonos, a las entradas de un multiplexor 2 - 1 para obtener, en la salida, una señal de alarma de dos tonos.

Crearemos dos módulos con una declaración module / endmodule y guardaremos cada uno en un archivo .v diferente. Después uniremos las entradas y salidas; aprenderemos a interconectar diferentes módulos mediante cables.

Código Verilog

Este código es similar al del ejemplo 1, pero con dos contadores independientes cnt1 y cnt2 y dos parámetros de distinto valor 60000 y 6000 para generar dos tonos de distinta frecuencia: 200 Hz y 2 KHz.


Generador de 2 tonos

module two_tones_generator (input wire reloj, output reg Hz_200, KHz_2);


reg [19:0] cnt1, cnt2;


always @(posedge reloj)

 begin

  cnt1 <= cnt1 + 1;

  cnt2 <= cnt2 + 1;


  if (cnt1 == 60000) // 200 Hz

   begin

    Hz_200 <= ~Hz_200;

    cnt1 <= 0;

   end


  if (cnt2 == 6000) // 2 KHz

   begin

    KHz_2 <= ~ KHz_2;

    cnt2 <= 0;

   end

 end

endmodule

Multiplexor 2-1


Utilizamos un prescaler para obtener un reloj clk2 personalizado, que se encargará de conmutar el valor de la entrada sel del multiplexor.


Dependiendo del bit que pongamos en la declaración  assign clk2 = prescaler[21]; obtenemos el tiempo de conmutación entre los dos tonos. Esta técnica ya la hemos empleado anteriormente para hacer contadores automáticos en los capítulos 3 y 4. 

module mux2_1 (input in1, in2, reloj, output reg out);

 reg sel;

 reg [24:0] prescaler = 0;

 wire clk2;


 assign clk2 = prescaler[21];


always @ (posedge reloj)

 begin

  prescaler <= prescaler + 1;

 end


always @ (posedge clk2)

 begin

  sel <= sel + 1;

end


always @ (sel) begin

  case (sel)

   1'b0 : out = in1;

   1'b1 : out = in2;

 endcase

end

endmodule

Cómo unir dos módulos


Para unir los módulos tone generator y multiplexor vamos a crear un tercer módulo tone_mixer donde declaramos los cables w1 y w2 que unen las salidas del generador de tonos con las entradas del multiplexor.


Es lo que se llama diseño estructural; particionar circuitos complejos en otros más sencillos facilitando así el diseño.


La inclusión de módulos de nivel inferior dentro de un módulo de nivel superior se llama jerarquía y se hace instanciando, es decir, invocando a un módulo desde otro. 

La sintaxis es:


nombre_del_modulo <identificador> (.puerto_1(wire_1), .puerto_2(wire_2));


Donde  puerto_n son las E/S de los módulos y wire_n  son las señales a las que están conectados los puertos. El nombre del puerto está precedido de un punto .puerto_1  y el nombre de la señal a la que está conectando va entre paréntesis (wire_1) y cada conexión se separa mediante comas. 

Esta forma de interconectar módulos se llama Mapeo Explícito y el orden en que se listan las conexiones no importa, por eso es menos dado a equivocaciones.


Existe otra técnica para conectar módulos llamada Mapeo Posicional. No lo voy a explicar para no enrollarme en exceso, lo dejo como tarea para casa.


Resumiendo, el módulo que interconecta el generador y el multiplexor queda como sigue:

module tone_mixer (input wire clk, output wire spk);


wire w1, w2;


two_tones_generator  MOD_1 (.reloj(clk), .Hz_200(w1), .KHz_2(w2));

mux2_1               MOD_2 (.reloj(clk), .out(spk), .in1(w1), .in2(w2));


endmodule


El archivo .pcf tiene solamente dos puertos, la señal de reloj clk y la salida spk hacia el altavoz:


set_io spk 119

set_io clk 21


Los tres archivos .v con los tres módulos, se guardan en la misma carpeta junto con el archivo .pcf

Lo subimos a la Alhambra y lo que vemos y oímos en la salida son dos señales de 200 Hz y 2 Khz alternándose, así obtenemos una señal de alarma de dos tonos. 

Normas de interconexión entre módulos


La conexión de dos módulos debe respetar unas reglas.

Internamente, las entradas de un módulo siempre son tipo net. Externamente las conexiones pueden venir de una variable tipo reg o net.

Internamente, las salidas de un módulo pueden ser tipo  reg o net y sólo se pueden conectar a una variable externa tipo net.

Ejemplo 3. Generador de notas musicales


Para finalizar vamos a llevar el ejemplo anterior un poco más lejos. Haremos un generador de 8 notas musicales que sacaremos por una salida mediante un multiplexor 8-1.

La forma de programar el generador de notas es igual que en el ejemplo 2, pero ahora con 8 contadores que conmutaran 8 señales digitales apara conseguir las frecuencias de las notas musicales DO, RE, MI, FA, SOL, LA, SI.

Bueno, en realidad he añadido DO sostenido para que sea más fácil implementar el multiplexor 8-1.

El código Verilog del generador de notas y el multiplexor no lo he pegado porque son muchas líneas, pero lo puedes descargar más abajo. Recuerda que los tres archivos .v deben estar en la misma carpeta, igual que en ejemplo 2.

La forma de instanciar (interconectar) el generador de notas con el multiplexor es la siguiente.

Eso es todo, espero que os haya gustado

¡Hasta pronto!

Links


El generador del notas musicales lo he sacado del Open FPGA Verilog Tutorial del gran Obijuan, capítulos 17, 18 y 19

Descargas


Descarga los ejemplos 1, 2 y 3 aquí abajo.