-
Notifications
You must be signed in to change notification settings - Fork 195
Capítulo 16: Contador de segundos
Ejemplos de este capítulo en github
Para practicar con los divisores de frecuencia, vamos a implementar un contador que se incremente cada segundo. La cuenta la mostraremos por los leds de la iCEstick
Usaremos el divisor de frecuencia del capítulo pasado, pero con algunas mejoras:
//-- divider.v
//-- Incluir constantes definidas para este componente
`include "divider.vh"
//-- Entrada: clk_in. Señal original
//-- Salida: clk_out. Señal de frecuencia 1/M de la original
module divider(input wire clk_in, output wire clk_out);
//-- Valor por defecto del divisor
//-- Lo ponemos a 1 Hz (Constantes definidas en divider.vh)
parameter M = `F_1Hz;
//-- Numero de bits para almacenar el divisor
//-- Se calculan con la funcion de verilog $clog2, que nos devuelve el
//-- numero de bits necesarios para representar el numero M
//-- Es un parametro local, que no se puede modificar al instanciar
localparam N = $clog2(M);
//-- Registro para implementar el contador modulo M
reg [N-1:0] divcounter = 0;
//-- Contador módulo M
always @(posedge clk_in)
divcounter <= (divcounter == M - 1) ? 0 : divcounter + 1;
//-- Sacar el bit mas significativo por clk_out
assign clk_out = divcounter[N-1];
endmodule
La primera mejora es la inclusión del fichero divisor.vh donde se han definido varias constantes para facilitar la generación de señales de determinadas frecuencias.
`include "divider.vh"
En vez de utilizar un número para los divisores, es más claro asociarlos a sus respectivas frecuencias mediante definiciones. Así por ejemplo, para obtener una señal de 1 Hz, basta con usar la constante F_1Hz en vez del número 12_000_000
parameter M = `F_1Hz;
Otra mejora es utilizar el operador ? :
en vez de if-else
para la descripción del contador módulo M. De esta forma el código es más compacto. También le da un aspecto más parecido a C, y eso mola :-)
//-- Contador módulo M
always @(posedge clk_in)
divcounter <= (divcounter == M - 1) ? 0 : divcounter + 1;
La extensión .vh es la que se usa en verilog para la declaración de constantes y macros. La definición de constantes es similar a cómo se hace en C, usando la palabra clave `define . El archivo tiene definidas las siguientes constantes:
//-- Megaherzios MHz
`define F_4MHz 3
`define F_1MHz 12
//-- Hertzios (Hz)
`define F_2Hz 6_000_000
`define F_1Hz 12_000_000
En los siguientes capítulos se irá ampliando, a medida que usemos más frecuencias e los ejemplos
Implementaremos un contador de segundos de 4 bits, que saca por los leds la cuenta
El diseño está formado por un contador de 4 bits a cuya entrada de reloj se le ha acoplado un divisor de frecuencia para generar una señal de 1Hz
El código Verilog es el siguiente:
//-- countsec.v
//-- Incluir las constantes del modulo del divisor
`include "divider.vh"
//-- Parameteros:
//-- clk: Reloj de entrada de la placa iCEstick
//-- data: Valor del contador de segundos, a sacar por los leds de la iCEstick
module countsec(input wire clk, output wire [3:0] data);
//-- Parametro del divisor. Fijarlo a 1Hz
//-- Se define como parametro para poder modificarlo desde el testbench
//-- para hacer pruebas
parameter M = `F_1Hz;
//-- Señal de reloj de 1Hz. Salida del divisor
wire clk_1HZ;
//-- Contador de segundos
reg [3:0] counter = 0;
//-- Instanciar el divisor
divider #(M)
DIV (
.clk_in(clk),
.clk_out(clk_1HZ)
);
//-- Incrementar el contador en cada flanco de subida de la señal de 1Hz
always @(posedge clk_1HZ)
counter <= counter + 1;
//-- Sacar los datos del contador hacia los leds
assign data = counter;
endmodule
Para sintetizarlo conectamos a la entrada de reloj la señal de 12Mhz de la IceStick y la salida data a los leds
ejecutamos el comando:
$ make sint
Los recursos empleados son:
Recurso | ocupación |
---|---|
PIOs | 4 / 96 |
PLBs | 12 / 160 |
BRAMs | 0 / 16 |
Lo cargamos en la FPGA con el comando:
$ sudo iceprog countsec.bin
Los leds empezarán a encenderse. Al cabo de 16 segundos se volverá al comienzo:
El banco de pruebas es el mismo que en capítulos anteriores. No se simula la obtención de la frecuencia de 1Hz porque consume mucho tiempo y espacio. En vez de ello, se usa un divisor entre 10 para verificar que el contador está funcionando:
//-- countsec_tb.v
module countsec_tb();
//-- Registro para generar la señal de reloj
reg clk = 0;
wire [3:0] data;
//-- Instanciar el componente y establecer el valor del divisor
//-- Se pone un valor bajo para simular (sino tardaria mucho)
countsec #(10)
dut(
.clk(clk),
.data(data)
);
//-- Generador de reloj. Periodo 2 unidades
always
# 1 clk <= ~clk;
//-- Proceso al inicio
initial begin
//-- Fichero donde almacenar los resultados
$dumpfile("countsec_tb.vcd");
$dumpvars(0, countsec_tb);
# 100 $display("FIN de la simulacion");
$finish;
end
endmodule
Para simular ejecutamos:
$ make sim
En el simulador veremos cómo el contador "cuenta":
- Ejercicio 1: Cambiar la frecuencia del contador a 2 Hz
- Ejercicio 2: Cambiar la frecuencia de forma que el contador cuente décimas de segundo, en vez de segundos
TODO
0 You are leaving the privative sector (EN)
1 ¡Hola mundo! (EN) (RU)
2 De un bit a datos (EN)
3 Puerta NOT (EN)
4 Contador de 26 bits (EN)
5 Prescaler de N bits (EN)
6 Múltiples prescalers (EN)
7 Contador de 4 bits con prescaler (EN)
8 Registro de 4 bits (EN)
9 Inicializador (EN)
10 Registro de desplazamiento (EN)
11 Multiplexor de 2 a 1 (EN)
12 Multiplexor de M a 1 (EN)
13 Inicializando registros (EN)
14 Registro de N bits con reset síncrono
15 Divisor de frecuencias
16 Contador de segundos
17 Generando tonos audibles
18 Tocando notas
19 Secuenciando notas
20 Comunicaciones serie asíncronas
21 Baudios y transmisión
22 Reglas de diseño síncrono
23 Controladores y autómatas finitos
24 Unidad de transmisión serie asíncrona
25 Unidad de recepción serie asíncrona
26 Memoria ROM
27 Memoria ROM genérica
28 Memoria RAM
29 Puertas triestado
30 Hacia el microprocesador y más allá