En uno de mis artículos expliqué cómo dividir la frequencia de una señal de reloj para crear señales de reloj con frecuencias menores. El concepto es muy simple, esperamos que cierto número de ciclos del reloj pasen y cambiamos nuestra señal de salida de 0 a 1, o de 1 a 0. Por ejemplo, la figura 1 describe el diagrama de tiempo de un divisor de frecuencia por 12. Por cada 6 ciclos del reloj de entrada, el valor de la señal de salida cambia.
Fig 1. Diagrama de tiempo de divisor de frecuencia por 12
Si observamos el diagrama de tiempo, notamos que el contador de ciclos usa el borde de subida del reloj como referencia (instante en que el reloj cambia de 0 a 1). Debido a ésto, el circuito es fácil de diseñar si la frecuencia es dividida por un número par. Sin embargo, el circuito se vuelve más complicado si queremos dividir la frecuencia por un número impar. Si observamos la figura 2, un divisor de reloj impar necesita usar el borde de subida y el de bajada del reloj. Por lo tanto, un simple contador de bordes de subida no sirve en estos casos.
Fig 2. Diagrama de tiempo de divisor de frecuencia por 5
2. DESCRIPCION DEL CIRCUITO
Para dividir la frecuencia de un reloj digital por un número impar, necesitamos crear otras señales e implementar lógica secuencial y combinacional.
La forma más sencilla de entender cómo funciona el circuito es observando el diagrama de tiempo en la figura 3 y siguiendo las instrucciones que escribí a continuación:
- Nuestro circuito divide la frecuencia de un reloj por el número impar N. En este ejemplo, N=5.
- La señal clk_in es nuestro reloj de entrada con frecuencia 500MHz y con ciclos de 2ns.
- El bus de señales Count es un contador de ciclos de entrada clk_in y reinicia a 0 cuando el contador llega a N-1. Para nuestro ejemplo, N-1 = 4, o sea 0100 en binario.
- La señal A es igual a 1 sólo cuando el valor en el contador Count es 0001. Sino, es 0.
- La señal B es igual a 1 sólo cuando el valor en el contador Count es (N+1)/2. Para nuestro ejemplo, (5+1)/2 = 3, o 0011 en binario.
- La señal Tff_A es un biestable T (T flip flop) activado por el borde de caída de la señal A.
- La señal Tff_B es un biestable T (T flip flop) activado por el borde de caída del reloj y sólamente cuando la señal B es 1.
- La señal clk_out es nuestro reloj de salida. Para producir esta señal, usamos una puerta XOR entre las señales Tff_A y Tff_B. La señal clk_out es alta cuando Tff_A y Tff_B son diferentes (10,01) y baja cuando ambas señas son iguales (11, 00). En nuestro ejemplo creamos una señal con frecuencia de 100MHz y ciclos de 10ns.
Fig 3. Diagrama de tiempo mostrando las señales usadas en divisor frecuencia por 5
Para una explicación más técnica de lo que hice en este circuito, les recomiendo que lean la hoja de aplicación AND8001-D publicada por ON Semiconductors.
3. Implementación en Verilog
Para crear el circuito descrito en la sección anterior, debemos utilizar lógica secuencial (biestables, registros, etc) y lógica combinacional (puertas lógicas).
A continuación les dejo el código para el divisor de frecuencia por 5, el cual también se encuentra disponible en mi repositorio de Github https://github.com/sphanlung/FPGA/blob/master/clk_div_odd.v
module clk_div_odd(
input clk_in,
output clk_out
);
reg [3:0] count = 4'b0; //4-bit counter
reg A1 = 0;
reg B1 = 0;
reg Tff_A = 0;
reg Tff_B = 0;
wire clock_out;
wire wTff_A;
wire wTff_B;
//Connects registers to wires for combinational logic
assign wTff_A = Tff_A;
assign wTff_B = Tff_B;
assign clk_out = wTff_B ^ wTff_A; //XOR gate
//Counter for division by N
always@(posedge clk_in)
begin
if(count == 4'b0100) //Count to N-1 (4)
begin // Example: Use 4 to divide by 5
count <= 4'b0000;
end
else
begin
count <= count + 1;
end
end
//Set A to high for one clock cycle when counter is 0
always@(posedge clk_in)
begin
if(count == 4'b0000)
A1 <= 1;
else
A1 <= 0;
end
//Sets B to high for one clock cycle when counter is (N+1)/2
always@(posedge clk_in)
begin
if(count == 4'b0011) //Use (N+1)/2
B1 <= 1; //Ex: (5+1)/2 = 3
else
B1 <= 0;
end
//T flip flop toggles
always@(negedge A1) // Toggle signal Tff_A
begin //whenever A1 goes from 1 to 0
Tff_A <= ~Tff_A;
end
always@(negedge clk_in)
begin
if(B1) // Toggle signal Tff_B whenever
begin //B1 is 1
Tff_B <= ~Tff_B;
end
end
endmodule
Fig 4. Diagrama de RTL generado por Xilinx ISE