Friday, April 2, 2021

Gated Clock Divider for integral N

Here is a practice question in Verilog where we implement a gated clock divider with the given specifications.

Gated Clock Divider in Verilog

Clock Gating: Clock gating is a low power design concept which helps to turn off the clock whenever not required. This helps to save power when the clock is not being used.

Requirement: Design a gated clock divider in Verilog (50% duty cycle) that can divide the clock frequency by any integral value. An enable signal indicates whether the clock divider is turned on or off.

Modules to be used: 
clkdiv - top module for clock divider
down_counter  - module to perform counting operations (down counter must be used)

Specifications:

Input signals for clkdiv:
inclk     (input clock)
rst_n     (active low reset)
enable   (enable clock divider)
value     (4 bits, clock divider value)

Output signals for clkdiv:
outclk    (output clock after clock division)

Hints:

Let us now see how we can go about solving the problem.

For clock divider by an integer N, we have seen in an earlier post here:
Clock divider by even number

Only difference being instead of using an up counter to count from 0 to N/2, we now use a down counter to count from N/2 - 1 to 0.

That was quite easy to implement. Now for clock division by an odd integer N, we need a little more intuitive thinking and some mathematical skills!
This website explains it in detail.
Clock divider by odd number
Source: referencedesigner.com

So that was tricky! Our job now is to combine the above two modules and ensure that this clock divider works for any integer value of N.
Also to add is the enable signal. Only when enable is high, the clock divider must work.

Solution:

As mentioned earlier, the down counter module will perform our counting and required signals can be passed on to it. We will have a poscnt and a negcnt, since we need to count on the positive as well as negative clock edges (for clock divide by odd number). 

The top module clkdiv will instantiate the counter. Since we have a different logic for odd and even values of clkdiv_val, we will need to select the correct logic based on the value. 

  • Signal outclk checks if we are out of reset and enable is high. If so, check whether value is an odd or even number and select outclk_odd or outclk_even respectively.
  • outclk_even is computed in the sequential block.
  • outclk_odd is computed using assign statement based on the logic to implement clock division by an odd number. If we have value=1, then the input clk is directly passed.
  • Also see that we are not decrementing counter (poscnt and negcnt) when enable is turned off and counter value is zero. This helps save power by eliminating unnecessary toggling of bits.

Verilog Code:

module clkdiv (
	inclk,
	rst_n,
	enable,
	value,
	outclk
    );
	 
input inclk;
input rst_n;
input enable;
input [3:0] value;
output outclk;

reg outclk_even;

wire [3:0] poscnt;
wire [3:0] negcnt;

wire outclk_odd;
wire outclk;

assign outclk_odd = (enable & value==1) ? inclk : (poscnt > (value>>1)) || (negcnt > (value>>1));
assign outclk = (!rst_n || !enable) ? 1'b0 : (!value[0]) ? outclk_even : outclk_odd;

always @ (posedge inclk or negedge rst_n)
begin
  if(!rst_n)
  begin
    outclk_even <= 1'b0;
  end
  else
  begin
    outclk_even <= (enable && (poscnt == 0)) ? ~outclk_even : outclk_even;
  end
end

down_counter u_down_counter (
	.clk     (inclk),
	.rst_n   (rst_n),
	.en      (enable),
	.div_val (value),
	.poscnt  (poscnt),
	.negcnt  (negcnt)
	);

endmodule

//4-bit down counter module
module down_counter(
	clk,
	rst_n,
	en,
	div_val,
	poscnt,
	negcnt
	);
	
input clk, rst_n;
input en;
input [3:0] div_val;

output [3:0]poscnt;
output [3:0]negcnt;

reg [3:0]poscnt, next_poscnt;  
reg [3:0]negcnt, next_negcnt; 

always @ (posedge clk or negedge rst_n)   
begin
 if (!rst_n)
 begin
 poscnt <= 3'b0;
 end
 else
 begin
 poscnt <= next_poscnt;
 end
end

always @ (negedge clk or negedge rst_n)   
begin
 if (!rst_n)
 begin
 negcnt <= 3'b0;
 end
 else
 begin
 negcnt <= next_negcnt;
 end
end

always @ (*)
begin
  if (en && poscnt==0)
  begin
    next_poscnt = !div_val[0] ? (div_val>>1) - 1 : div_val-1;
    next_negcnt = !div_val[0] ? 4'd0 : div_val-1;
  end
  else
  begin
    next_poscnt = (|poscnt) ? poscnt - 1'b1 : poscnt;
    next_negcnt = (|negcnt) ? negcnt - 1'b1 : negcnt;
  end
end

endmodule

Testbench:

module clkdiv_tb;

// Inputs
reg inclk;
reg rst_n;
reg enable;
reg [3:0] value;

// Outputs
wire outclk;

// Instantiate the Unit Under Test (UUT)
clkdiv uut (
	.inclk(inclk), 
	.rst_n(rst_n), 
	.enable(enable), 
	.value(value), 
	.outclk(outclk)
);

  always #5 inclk = ~inclk;

initial begin
	// Initialize Inputs
	inclk = 1;
	rst_n = 0;
	enable = 1'b0;
	value = 0;
	#20 rst_n = 1'b1;
	enable = 0;
	value = 0;
	#30 value = 1;
	#40 enable = 1'b1;
	#150
	enable = 1'b0;
	value = 3;
	#10 enable = 1'b1;
	#150 
	enable = 1'b0;
	value = 2;
	#30 enable = 1'b1;
	#100 enable = 1'b0;
	#50 value = 5;
	#10 enable = 1'b1;
	#150
	enable = 1'b0;
	value = 4;
	#60 enable = 1'b1;
	#150 enable = 1'b0;
	#50
	$finish;
end
     
endmodule

Simulation Result:



As can be seen from simulation, different values were tried and we have obtained the clock divided output based on the input value.
Also output is present only when enable is high.


No comments:

Post a Comment