# Dual Edged Counter

Discussion in 'VHDL' started by Cory Shol, Apr 17, 2013.

1. ### Cory SholGuest

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.

Cory Shol, Apr 17, 2013

2. ### GaborSzakacsGuest

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.

--
Gabor

GaborSzakacs, Apr 17, 2013

3. ### Cory SholGuest

On Wednesday, April 17, 2013 10:21:19 AM UTC-5, Gabor Sz wrote:
> 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.
>
>
>
> --
>
> Gabor

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

Cory Shol, Apr 17, 2013
4. ### GaborSzakacsGuest

Cory Shol wrote:
> On Wednesday, April 17, 2013 10:21:19 AM UTC-5, Gabor Sz wrote:
>> 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.
>>
>>
>>
>> --
>>
>> Gabor

>
> 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.

--
Gabor

GaborSzakacs, Apr 17, 2013
5. ### Cory SholGuest

On Wednesday, April 17, 2013 10:54:46 AM UTC-5, Gabor Sz wrote:
> Cory Shol wrote:
>
> > On Wednesday, April 17, 2013 10:21:19 AM UTC-5, Gabor Sz wrote:

>
> >> 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.

>
> >>

>
> >>

>
> >>

>
> >> --

>
> >>

>
> >> Gabor

>
> >

>
> > 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.
>
>
>
> --
>
> Gabor

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.

Cory Shol, Apr 17, 2013
6. ### rickmanGuest

On 4/17/2013 1:45 PM, Cory Shol wrote:
>
> 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.

--

Rick

rickman, Apr 17, 2013
7. ### rickmanGuest

On 4/17/2013 3:18 PM, rickman wrote:
>
> 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.

--

Rick

rickman, Apr 17, 2013