La división de señales de reloj es una operación frecuente en el diseño digital. A continuación se describen varios enfoques para lograr una relación de trabajo del 50% en señales de reloj dividido, comúnmente emplaedos en implementaciones con FPGAs.
Uso de recursos de reloj dedicados
Los FPGAs modernos suelen incluir bloques de gestión de reloj (como PLLs o MMCMs) que permiten generar múltiples frecuencias derivadas de una referencia. Aunque es la solución más limpia y precisa, estos recursos son limitados en cantidad dentro del dispostiivo, por lo que se reservan para las frecuencias más críticas del diseño.
División basada en contadores duales
Este método utiliza dos contadores separados que operan en los flancos de subida y bajada del reloj original. Al comparar sus valores se genera una señal dividida con relación de trabajo ajustable. El siguiente código ilustra esta técnica:
module divisor_reloj_contador (
input wire clk_fuente,
input wire rst_n,
output reg clk_salida
);
parameter [15:0] FACTOR_DIVISION = 16'd4;
localparam [15:0] UMBRAL_MEDIO = FACTOR_DIVISION / 2;
reg [15:0] contador_flanco_subida;
reg [15:0] contador_flanco_bajada;
always @(posedge clk_fuente or negedge rst_n) begin
if (!rst_n)
contador_flanco_subida <= 16'b0;
else if (contador_flanco_subida == FACTOR_DIVISION - 1)
contador_flanco_subida <= 16'b0;
else
contador_flanco_subida <= contador_flanco_subida + 16'b1;
end
always @(negedge clk_fuente or negedge rst_n) begin
if (!rst_n)
contador_flanco_bajada <= 16'b0;
else if (contador_flanco_bajada == FACTOR_DIVISION - 1)
contador_flanco_bajada <= 16'b0;
else
contador_flanco_bajada <= contador_flanco_bajada + 16'b1;
end
wire pulso_subida = (contador_flanco_subida < UMBRAL_MEDIO) ? 1'b0 : 1'b1;
wire pulso_bajada = (contador_flanco_bajada < UMBRAL_MEDIO) ? 1'b0 : 1'b1;
always @(*) begin
if (FACTOR_DIVISION[0])
clk_salida = pulso_subida & pulso_bajada;
else
clk_salida = pulso_subida;
end
endmodule
Extracción de bits de contador
Una técnica simple para divisores de potencia de dos consiste en utilizar un contador continuo y tomar diferentes bits de su registro como salidas de reloj. Cada bit k proporciona una división por 2^(k+1). El siguiente ejemplo muestra un contador de 4 bits:
module divisor_potencia_dos (
input wire clk_sistema,
input wire rst_n,
output wire [3:0] relojes_divididos
);
reg [3:0] acumulador;
always @(posedge clk_sistema or negedge rst_n) begin
if (!rst_n)
acumulador <= 4'b0;
else
acumulador <= acumulador + 4'b1;
end
assign relojes_divididos = acumulador;
endmodule
Síntesis de frecuencia mediante acumulador de fase
Este método, basado en el principio DDS (Direct Digital Synthesis), permite generar frecuencias arbitrarias mediante un acumulador y un paso de incremento programable. Es especialmente útil en módulos IP donde se reuqiere flexibilidad en la configuración de la frecuencia mediante interfaces de bus. La resolución de frecuencia viene determinada por el tamaño del acumulador.
module generador_dds (
input wire clk_ref,
input wire rst_n,
input wire [31:0] paso_frecuencia,
output wire reloj_sintetizado
);
reg [31:0] acumulador_fase;
always @(posedge clk_ref or negedge rst_n) begin
if (!rst_n)
acumulador_fase <= 32'b0;
else
acumulador_fase <= acumulador_fase + paso_frecuencia;
end
assign reloj_sintetizado = acumulador_fase[31];
endmodule
La frecuencia generada se calcula como f_salida = (paso_frecuencia * f_reloj_ref) / 2^32. La relación de trabajo no es exactamente del 50% y puede existir un pequeño error en la frecuencia, dependiendo de la resolución del sistema.