Dual Edged Counter

C

Cory Shol

Hi All,

I am researching ways onto create a dual edged Counter.

Problem details:

Original Clock = 25 MHz Put the Original clock through a PLL and multiply it by 5 making a new: Clk_125MHz = 125 MHz clock.

Using the 125 MHz clock, I want to use a 9 bit register [Called Duty_cycle] to state the Duty cycle of a PWM.
For example if the Duty_cycle = "011111111" it will have a 50% duty cycle. Duty_cycle = "1111111110" it will have a ~0.2% duty cycle etc...

The period of the PWM is 4096 ns (125 Mhz/ 2^9) = 125000000/512 = 244KHz =1/244KHz= 4096 ns.
Code looks something like:

process(clk_125Mhz, reset)

begin
if (reset = '1') then
dc_count_i <= X"00" &'0'; -- you can use others statement as well.
elsif(rising_edge(clk_125Mhz)) then
if(dc_count_i < duty_cycle) then
duty_out <='0';
else
duty_out <= '1';
end if;
dc_count_i <= dc_count_i + 1;
end if;
end process;

Alright this is all fine and works decent.

Now the extension I want to make my PWM have a period of 2048 ns. There are three ways I can think of to do this:
1) PLL the clock to 250 MHz ( Do not want to run at Max frequency in the device)
2) Change the 9 bit Duty Cycle register to 8 bits (Lower the resolution)
3) Every time there is a rising or falling edge count up the counter

This is coming back to topic title: to create a dual Edged Counter that can generate a duty cycle of a PWM.

I tried something like:
------------------------------------------------------
process(clk_125Mhz, reset)

begin
if (reset = '1') then
rise_counter <= X"00" &'0';
elsif(rising_edge(clk_125Mhz)) then

rise_counter <= rise_counter + 2;
end if;
end process;

process(clk_125Mhz, reset)

begin
if (reset = '1') then
fall_counter <= X"00" &'1';
elsif(falling_edge(clk_125Mhz)) then
fall_counter <= fall_counter + 2;
end if;
end process;


process(xor_counter, final_counter)
begin
final_counter <= final_counter + 1;
end process;


xor_counter <= rise_counter XOR fall_counter;

dc_count <= final_counter;

duty_out <= '0' when (final_counter < duty_cycle) else '1';

But this creates a Combinatorial loop.

I tried:
---------------------------------------------------

process(clk_125Mhz, reset)

begin
if (reset = '1') then
rise_counter <= X"00" &'0';
elsif(rising_edge(clk_125Mhz)) then

rise_counter <= rise_counter + 2;
end if;
end process;

final_counter <= rise_counter when clk_125Mhz ='1' else (rise_counter or "000000001");

duty_out <= '0' when (final_counter < duty_cycle) else '1';

But this produced a glitch in simulation.

Does anyone else have any other ideas, on how to implement a dual edged counter? I feel like this should be an easy solution, I just keep thinking too complex.
 
G

GaborSzakacs

Cory said:
Hi All,

I am researching ways onto create a dual edged Counter.

Problem details:

Original Clock = 25 MHz Put the Original clock through a PLL and multiply it by 5 making a new: Clk_125MHz = 125 MHz clock.

Using the 125 MHz clock, I want to use a 9 bit register [Called Duty_cycle] to state the Duty cycle of a PWM.
For example if the Duty_cycle = "011111111" it will have a 50% duty cycle. Duty_cycle = "1111111110" it will have a ~0.2% duty cycle etc...

The period of the PWM is 4096 ns (125 Mhz/ 2^9) = 125000000/512 = 244KHz =1/244KHz= 4096 ns.
Code looks something like:

process(clk_125Mhz, reset)

begin
if (reset = '1') then
dc_count_i <= X"00" &'0'; -- you can use others statement as well.
elsif(rising_edge(clk_125Mhz)) then
if(dc_count_i < duty_cycle) then
duty_out <='0';
else
duty_out <= '1';
end if;
dc_count_i <= dc_count_i + 1;
end if;
end process;

Alright this is all fine and works decent.

Now the extension I want to make my PWM have a period of 2048 ns. There are three ways I can think of to do this:
1) PLL the clock to 250 MHz ( Do not want to run at Max frequency in the device)
2) Change the 9 bit Duty Cycle register to 8 bits (Lower the resolution)
3) Every time there is a rising or falling edge count up the counter

This is coming back to topic title: to create a dual Edged Counter that can generate a duty cycle of a PWM.

I tried something like:
------------------------------------------------------
process(clk_125Mhz, reset)

begin
if (reset = '1') then
rise_counter <= X"00" &'0';
elsif(rising_edge(clk_125Mhz)) then

rise_counter <= rise_counter + 2;
end if;
end process;

process(clk_125Mhz, reset)

begin
if (reset = '1') then
fall_counter <= X"00" &'1';
elsif(falling_edge(clk_125Mhz)) then
fall_counter <= fall_counter + 2;
end if;
end process;


process(xor_counter, final_counter)
begin
final_counter <= final_counter + 1;
end process;


xor_counter <= rise_counter XOR fall_counter;

dc_count <= final_counter;

duty_out <= '0' when (final_counter < duty_cycle) else '1';

But this creates a Combinatorial loop.

I tried:
---------------------------------------------------

process(clk_125Mhz, reset)

begin
if (reset = '1') then
rise_counter <= X"00" &'0';
elsif(rising_edge(clk_125Mhz)) then

rise_counter <= rise_counter + 2;
end if;
end process;

final_counter <= rise_counter when clk_125Mhz ='1' else (rise_counter or "000000001");

duty_out <= '0' when (final_counter < duty_cycle) else '1';

But this produced a glitch in simulation.

Does anyone else have any other ideas, on how to implement a dual edged counter? I feel like this should be an easy solution, I just keep thinking too complex.

Is the output of the PWM going to a pin that is supported by a DDR
output flop? If so, then you could simply reduce the counter
by one bit, but compare it with the upper bits of the duty cycle
input. If the LSB of the duty cycle is zero, then the output DDR
flop D inputs go from 11 to 00, but the it is 1, then the D inputs
go from 11 to 10 to 00 as you pass the duty cycle threshold.
No internal dual edges, no glitches.
 
C

Cory Shol

Cory said:
Hi All,
I am researching ways onto create a dual edged Counter.
Problem details:
Original Clock = 25 MHz Put the Original clock through a PLL and multiply it by 5 making a new: Clk_125MHz = 125 MHz clock.
Using the 125 MHz clock, I want to use a 9 bit register [Called Duty_cycle] to state the Duty cycle of a PWM.
For example if the Duty_cycle = "011111111" it will have a 50% duty cycle. Duty_cycle = "1111111110" it will have a ~0.2% duty cycle etc...

The period of the PWM is 4096 ns (125 Mhz/ 2^9) = 125000000/512 = 244KHz =1/244KHz= 4096 ns.
Code looks something like:

process(clk_125Mhz, reset)


if (reset = '1') then
dc_count_i <= X"00" &'0'; -- you can use others statement as well.
elsif(rising_edge(clk_125Mhz)) then
if(dc_count_i < duty_cycle) then
duty_out <='0';

duty_out <= '1';
dc_count_i <= dc_count_i + 1;
end process;

Alright this is all fine and works decent.

Now the extension I want to make my PWM have a period of 2048 ns. There are three ways I can think of to do this:
1) PLL the clock to 250 MHz ( Do not want to run at Max frequency in the device)
2) Change the 9 bit Duty Cycle register to 8 bits (Lower the resolution)
3) Every time there is a rising or falling edge count up the counter

This is coming back to topic title: to create a dual Edged Counter that can generate a duty cycle of a PWM.

I tried something like:

process(clk_125Mhz, reset)


if (reset = '1') then
rise_counter <= X"00" &'0';
elsif(rising_edge(clk_125Mhz)) then

rise_counter <= rise_counter + 2;
end process;

process(clk_125Mhz, reset)


if (reset = '1') then
fall_counter <= X"00" &'1';
elsif(falling_edge(clk_125Mhz)) then
fall_counter <= fall_counter + 2;
end process;


process(xor_counter, final_counter)

final_counter <= final_counter + 1;
end process;


xor_counter <= rise_counter XOR fall_counter;

dc_count <= final_counter;

duty_out <= '0' when (final_counter < duty_cycle) else '1';

But this creates a Combinatorial loop.

I tried:


process(clk_125Mhz, reset)


if (reset = '1') then
rise_counter <= X"00" &'0';
elsif(rising_edge(clk_125Mhz)) then

rise_counter <= rise_counter + 2;
end process;

final_counter <= rise_counter when clk_125Mhz ='1' else (rise_counter or "000000001");

duty_out <= '0' when (final_counter < duty_cycle) else '1';

But this produced a glitch in simulation.

Does anyone else have any other ideas, on how to implement a dual edged counter? I feel like this should be an easy solution, I just keep thinking too complex.



Is the output of the PWM going to a pin that is supported by a DDR

output flop? If so, then you could simply reduce the counter

by one bit, but compare it with the upper bits of the duty cycle

input. If the LSB of the duty cycle is zero, then the output DDR

flop D inputs go from 11 to 00, but the it is 1, then the D inputs

go from 11 to 10 to 00 as you pass the duty cycle threshold.

No internal dual edges, no glitches.

The research is for an Microsemi Actel Igloo AGL1000. I don't think it has a DDR output flop.
 
G

GaborSzakacs

Cory said:
Cory said:
Hi All,
I am researching ways onto create a dual edged Counter.
Problem details:
Original Clock = 25 MHz Put the Original clock through a PLL and multiply it by 5 making a new: Clk_125MHz = 125 MHz clock.
Using the 125 MHz clock, I want to use a 9 bit register [Called Duty_cycle] to state the Duty cycle of a PWM.
For example if the Duty_cycle = "011111111" it will have a 50% duty cycle. Duty_cycle = "1111111110" it will have a ~0.2% duty cycle etc...
The period of the PWM is 4096 ns (125 Mhz/ 2^9) = 125000000/512 = 244KHz =1/244KHz= 4096 ns.
Code looks something like:
process(clk_125Mhz, reset)

begin
if (reset = '1') then
dc_count_i <= X"00" &'0'; -- you can use others statement as well.
elsif(rising_edge(clk_125Mhz)) then
if(dc_count_i < duty_cycle) then
duty_out <='0';
else
duty_out <= '1';
end if;
dc_count_i <= dc_count_i + 1;
end if;
end process;
Alright this is all fine and works decent.
Now the extension I want to make my PWM have a period of 2048 ns. There are three ways I can think of to do this:
1) PLL the clock to 250 MHz ( Do not want to run at Max frequency in the device)
2) Change the 9 bit Duty Cycle register to 8 bits (Lower the resolution)
3) Every time there is a rising or falling edge count up the counter
This is coming back to topic title: to create a dual Edged Counter that can generate a duty cycle of a PWM.
I tried something like:
------------------------------------------------------
process(clk_125Mhz, reset)

begin
if (reset = '1') then
rise_counter <= X"00" &'0';
elsif(rising_edge(clk_125Mhz)) then
rise_counter <= rise_counter + 2;
end if;
end process;
process(clk_125Mhz, reset)

begin
if (reset = '1') then
fall_counter <= X"00" &'1';
elsif(falling_edge(clk_125Mhz)) then
fall_counter <= fall_counter + 2;
end if;
end process;
process(xor_counter, final_counter)
begin
final_counter <= final_counter + 1;
end process;
xor_counter <= rise_counter XOR fall_counter;
dc_count <= final_counter;

duty_out <= '0' when (final_counter < duty_cycle) else '1';
But this creates a Combinatorial loop.
I tried:
---------------------------------------------------
process(clk_125Mhz, reset)

begin
if (reset = '1') then
rise_counter <= X"00" &'0';
elsif(rising_edge(clk_125Mhz)) then
rise_counter <= rise_counter + 2;
end if;
end process;
final_counter <= rise_counter when clk_125Mhz ='1' else (rise_counter or "000000001");
duty_out <= '0' when (final_counter < duty_cycle) else '1';
But this produced a glitch in simulation.
Does anyone else have any other ideas, on how to implement a dual edged counter? I feel like this should be an easy solution, I just keep thinking too complex.


Is the output of the PWM going to a pin that is supported by a DDR

output flop? If so, then you could simply reduce the counter

by one bit, but compare it with the upper bits of the duty cycle

input. If the LSB of the duty cycle is zero, then the output DDR

flop D inputs go from 11 to 00, but the it is 1, then the D inputs

go from 11 to 10 to 00 as you pass the duty cycle threshold.

No internal dual edges, no glitches.

The research is for an Microsemi Actel Igloo AGL1000. I don't think it has a DDR output flop.

Realize that without an output DDR flop, there will be some influence
of routing delays between even and odd values of duty cycle, but the
same approach of using a shorter counter to make the PWM with half the
resolution could work. Follow that with a single flop on the falling
clock edge. Then you have two PWM signals offset by half a clock cycle.
At the output, you would either select the first, or the OR of the two
signals based on the LSB of duty cycle. Again no glitches, but the
monotonicity could suffer slightly.
 
C

Cory Shol

Cory said:
Cory Shol wrote:

Hi All,
I am researching ways onto create a dual edged Counter.
Problem details:
Original Clock = 25 MHz Put the Original clock through a PLL and multiply it by 5 making a new: Clk_125MHz = 125 MHz clock.
Using the 125 MHz clock, I want to use a 9 bit register [Called Duty_cycle] to state the Duty cycle of a PWM.
For example if the Duty_cycle = "011111111" it will have a 50% duty cycle. Duty_cycle = "1111111110" it will have a ~0.2% duty cycle etc...
The period of the PWM is 4096 ns (125 Mhz/ 2^9) = 125000000/512 = 244KHz =1/244KHz= 4096 ns.
Code looks something like:
process(clk_125Mhz, reset)

begin
if (reset = '1') then
dc_count_i <= X"00" &'0'; -- you can use others statement as well.
elsif(rising_edge(clk_125Mhz)) then
if(dc_count_i < duty_cycle) then
duty_out <='0';
else
duty_out <= '1';
end if;
dc_count_i <= dc_count_i + 1;
end if;
end process;
Alright this is all fine and works decent.
Now the extension I want to make my PWM have a period of 2048 ns. There are three ways I can think of to do this:
1) PLL the clock to 250 MHz ( Do not want to run at Max frequency in the device)
2) Change the 9 bit Duty Cycle register to 8 bits (Lower the resolution)
3) Every time there is a rising or falling edge count up the counter
This is coming back to topic title: to create a dual Edged Counter that can generate a duty cycle of a PWM.
I tried something like:
------------------------------------------------------
process(clk_125Mhz, reset)

begin
if (reset = '1') then
rise_counter <= X"00" &'0';
elsif(rising_edge(clk_125Mhz)) then
rise_counter <= rise_counter + 2;
end if;
end process;
process(clk_125Mhz, reset)

begin
if (reset = '1') then
fall_counter <= X"00" &'1';
elsif(falling_edge(clk_125Mhz)) then
fall_counter <= fall_counter + 2;
end if;
end process;
process(xor_counter, final_counter)
begin
final_counter <= final_counter + 1;
end process;
xor_counter <= rise_counter XOR fall_counter;
dc_count <= final_counter;

duty_out <= '0' when (final_counter < duty_cycle) else '1';
But this creates a Combinatorial loop.
I tried:
---------------------------------------------------
process(clk_125Mhz, reset)

begin
if (reset = '1') then
rise_counter <= X"00" &'0';
elsif(rising_edge(clk_125Mhz)) then
rise_counter <= rise_counter + 2;
end if;
end process;
final_counter <= rise_counter when clk_125Mhz ='1' else (rise_counter or "000000001");
duty_out <= '0' when (final_counter < duty_cycle) else '1';
But this produced a glitch in simulation.
Does anyone else have any other ideas, on how to implement a dual edged counter? I feel like this should be an easy solution, I just keep thinking too complex.


Is the output of the PWM going to a pin that is supported by a DDR

output flop? If so, then you could simply reduce the counter

by one bit, but compare it with the upper bits of the duty cycle

input. If the LSB of the duty cycle is zero, then the output DDR

flop D inputs go from 11 to 00, but the it is 1, then the D inputs

go from 11 to 10 to 00 as you pass the duty cycle threshold.

No internal dual edges, no glitches.

The research is for an Microsemi Actel Igloo AGL1000. I don't think it has a DDR output flop.



Realize that without an output DDR flop, there will be some influence

of routing delays between even and odd values of duty cycle, but the

same approach of using a shorter counter to make the PWM with half the

resolution could work. Follow that with a single flop on the falling

clock edge. Then you have two PWM signals offset by half a clock cycle.

At the output, you would either select the first, or the OR of the two

signals based on the LSB of duty cycle. Again no glitches, but the

monotonicity could suffer slightly.

THE DDR flip flop got me thinking. So I went back and looked at Spartan 3A user guide to see the DDR flip flop.

I then looked for a similar flip flop in the Actel AGL1000 and it indeed does have a DDR register input or output type.

Even after working 2 years in Logic design I still feel like I know nothing.
 
R

rickman

Even after working 2 years in Logic design I still feel like I know nothing.

I know the feeling. There is just so much to learn.

Are you looking for an *average* PWM on the pin or does it need to be
exact on every cycle? If the average value is what is important you can
use a fractional divider which should be easier and can get you even
more precision.

A fractional divider just counts the number of PWM cycles and
periodically adds or subtracts one from the duty_cycle value. This can
be done in a manner that is not actually periodic so that the side tones
it introduces are spread out and at a low level if that is important.

You might also consider using a DCO. I tried to do that for a few
minutes and couldn't think of how that would work. But I'm pretty sure
there is a way. I'm just drawing a blank at the moment. A DCO will
give you a much more precise average value with one clock cycle of
jitter on the edge, similar to the fractional divider, but easier to tune.
 
R

rickman

You might also consider using a DCO. I tried to do that for a few
minutes and couldn't think of how that would work. But I'm pretty sure
there is a way. I'm just drawing a blank at the moment. A DCO will give
you a much more precise average value with one clock cycle of jitter on
the edge, similar to the fractional divider, but easier to tune.

Ok, brain cramp over...

Use a DCO to generate a ramp signal in as many bits as you want. The
step size will set the rate at which it rolls over and so the PWM
frequency. Since the DCO can be lots of bits, the duty_cycle can be
more bits than with a simple counter. So the point in the cycle where
the counter is above the duty_cycle will jitter around a clock edge, but
the average can be very precise, as long as your step_size is not an
integer ratio to the modulus. You need the steps to walk around the
number space.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,901
Latest member
Noble71S45

Latest Threads

Top