Writing a counter to approximate a fraction with minimal error

82 Views Asked by At

I'm writing a VGA controller in Verilog. I have a 100 MHz clock and I want to enable a signal, VPIXEL, 480 times in a span of 16670 ms.

Obviously, I can't enable VPIXEL every 16670ms/480 ~= 34729.166ms or 34729.166... counts of the 100MHz clock.

What is the best way to approach the problem, so that the error is minimal in the long run?

I don't want to use a new clock.

1

There are 1 best solutions below

2
Bob On BEST ANSWER

This is a very old problem, the first instance of this problem that I a m aware of is the design of the Gregorian calendar.

If you are asking to reduce the error in the long run, that is probably because you tried a counter using the integer division threshold, i.e. one pulse every 34729 clocks, and then you notice that the pulse will start to come too early.

Since the denominator is constant you can write a counter you can keep track of the value without any division circuit updating only the numerator of the fraction.

// Code just typed here to illustrate the idea, not tested
// may have even syntax errors
module fractional_counter #(
  parameter N=16670*1000*100,
  parameter D=480
) (
  input logic clk,
  input logic rst,
  output logic pls
);

localparam NB=$clog2(N+D);
logic [NB-1:0] current_num;

always @(posedge clk) begin
  if(rst) begin
    current_num = 0;
  end
  else begin
    
    if(current_num >= N - D) begin
      // (current_num+D)/D >= N/D
      // a cycle is complete
      current_num <= (current_num + D - N);
      pls <= 1;
    end
    else begin
      // Increase the counter
      // current_num / D + 1 = (current_num + D) / D
      current_num <= current_num + D;
      pls <= 0;
    end
  end
end
endmodule

You can reduce a few bits there, by simplifying the factor, e.g. dividing both N and D by 160, also, you 16670 is very suspicious of being just another approximation of 50000/3 in that case maybe you have to put that as a fraction as well.