caravel/verilog/rtl/clock_div.v

215 lines
6.3 KiB
Verilog

// SPDX-FileCopyrightText: 2020 Efabless Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// SPDX-License-Identifier: Apache-2.0
/* Integer-N clock divider */
`default_nettype none
module clock_div #(
parameter SIZE = 3 // Number of bits for the divider value
) (
in, out, N, resetb
);
input in; // input clock
input [SIZE-1:0] N; // the number to be divided by
input resetb; // asynchronous reset (sense negative)
output out; // divided output clock
wire out_odd; // output of odd divider
wire out_even; // output of even divider
wire not_zero; // signal to find divide by 0 case
wire enable_even; // enable of even divider
wire enable_odd; // enable of odd divider
reg [SIZE-1:0] syncN; // N synchronized to output clock
reg [SIZE-1:0] syncNp; // N synchronized to output clock
assign not_zero = | syncN[SIZE-1:1];
assign out = (out_odd & syncN[0] & not_zero) | (out_even & !syncN[0]);
assign enable_odd = syncN[0] & not_zero;
assign enable_even = !syncN[0];
// Divider value synchronization (double-synchronized to avoid metastability)
always @(posedge out or negedge resetb) begin
if (resetb == 1'b0) begin
syncN <= `CLK_DIV; // Default to divide-by-2 on system reset
syncNp <= `CLK_DIV; // Default to divide-by-2 on system reset
end else begin
syncNp <= N;
syncN <= syncNp;
end
end
// Even divider
even even_0(in, out_even, syncN, resetb, not_zero, enable_even);
// Odd divider
odd odd_0(in, out_odd, syncN, resetb, enable_odd);
endmodule // clock_div
/* Odd divider */
module odd #(
parameter SIZE = 3
) (
clk, out, N, resetb, enable
);
input clk; // slow clock
output out; // fast output clock
input [SIZE-1:0] N; // division factor
input resetb; // synchronous reset
input enable; // odd enable
reg [SIZE-1:0] counter; // these 2 counters are used
reg [SIZE-1:0] counter2; // to non-overlapping signals
reg out_counter; // positive edge triggered counter
reg out_counter2; // negative edge triggered counter
reg rst_pulse; // pulse generated when vector N changes
reg [SIZE-1:0] old_N; // gets set to old N when N is changed
wire not_zero; // if !not_zero, we devide by 1
// xor to generate 50% duty, half-period waves of final output
assign out = out_counter2 ^ out_counter;
// positive edge counter/divider
always @(posedge clk or negedge resetb) begin
if (resetb == 1'b0) begin
counter <= `CLK_DIV;
out_counter <= 1;
end else if (rst_pulse) begin
counter <= N;
out_counter <= 1;
end else if (enable) begin
if (counter == 1) begin
counter <= N;
out_counter <= ~out_counter;
end else begin
counter <= counter - 1'b1;
end
end
end
reg [SIZE-1:0] initial_begin; // this is used to offset the negative edge counter
wire [SIZE:0] interm_3; // from the positive edge counter in order to
assign interm_3 = {1'b0, N} + 2'b11; // guarantee 50% duty cycle.
localparam [SIZE:0] interm_init = {1'b0,`CLK_DIV} + 2'b11;
// Counter driven by negative edge of clock.
always @(negedge clk or negedge resetb) begin
if (resetb == 1'b0) begin
// reset the counter at system reset
counter2 <= `CLK_DIV;
initial_begin <= interm_init[SIZE:1];
out_counter2 <= 1;
end else if (rst_pulse) begin
// reset the counter at change of N.
counter2 <= N;
initial_begin <= interm_3[SIZE:1];
out_counter2 <= 1;
end else if ((initial_begin <= 1) && enable) begin
// Do normal logic after odd calibration.
// This is the same as the even counter.
if (counter2 == 1) begin
counter2 <= N;
out_counter2 <= ~out_counter2;
end else begin
counter2 <= counter2 - 1'b1;
end
end else if (enable) begin
initial_begin <= initial_begin - 1'b1;
end
end
//
// reset pulse generator:
// __ __ __ __ _
// clk: __/ \__/ \__/ \__/ \__/
// _ __________________________
// N: _X__________________________
// _____
// rst_pulse: __/ \___________________
//
// This block generates an internal reset for the odd divider in the
// form of a single pulse signal when the odd divider is enabled.
always @(posedge clk or negedge resetb) begin
if (resetb == 1'b0) begin
rst_pulse <= 0;
end else if (enable) begin
if (N != old_N) begin
// pulse when reset changes
rst_pulse <= 1;
end else begin
rst_pulse <= 0;
end
end
end
always @(posedge clk) begin
// always save the old N value to guarante reset from
// an even-to-odd transition.
old_N <= N;
end
endmodule // odd
/* Even divider */
module even #(
parameter SIZE = 3
) (
clk, out, N, resetb, not_zero, enable
);
input clk; // fast input clock
output out; // slower divided clock
input [SIZE-1:0] N; // divide by factor 'N'
input resetb; // asynchronous reset
input not_zero; // if !not_zero divide by 1
input enable; // enable the even divider
reg [SIZE-1:0] counter;
reg out_counter;
wire [SIZE-1:0] div_2;
// if N=0 just output the clock, otherwise, divide it.
assign out = (clk & !not_zero) | (out_counter & not_zero);
assign div_2 = {1'b0, N[SIZE-1:1]};
// simple flip-flop even divider
always @(posedge clk or negedge resetb) begin
if (resetb == 1'b0) begin
counter <= 1;
out_counter <= 1;
end else if (enable) begin
// only use switching power if enabled
if (counter == 1) begin
// divide after counter has reached bottom
// of interval 'N' which will be value '1'
counter <= div_2;
out_counter <= ~out_counter;
end else begin
// decrement the counter and wait
counter <= counter-1; // to start next transition.
end
end
end
endmodule //even
`default_nettype wire