More fun with VHDL

C

Chuck McManis

Guest
State machines and synchronization.

The background is that I'm working on this PWM unit that uses a serial shift
register to hold the pulse "width" data. The original code goes something
like:

-- process to manage clocking in the data
sr: process (sdata_in, sclock, reset) is
begin
if (reset = '1') then
serial_data <= "00000000";
elsif rising_edge(sclock) then
serial_data <= sdata_in & serial_data(serial_data'length-1 downto
0);
end if;
end process;

-- concurrent assignment of serial data out.
sdata_out <= serial_data(0);

-- process to manage pwm output
pwm: process is (pwm_count) is
begin
if (pwm_count < serial_data) then
pwm_out <= '1';
else
pwm_out <= '0';
end if;
end process;

Things that aren't here is that the architecture for this thing calls for a
current PWM count as input (same width as serial_data), pwm_out as output,
and our shift register, serial_data, defined with a signal define in the
behavioral section.

This works as I would expect, and I can clock in different bit streams and
get different wave forms. However, a couple of problems persist:

1) I don't get "full" PWM. This is because the pwm process is forced to use
either < or > If I want to support both 0% on (output stays low) and 100%
on, (output stays high), I need an additional statement to cover that.

I can modify pwm as follows:
pwm: process is (pwm_count, serial_data) is
begin
if (serial_data = "11111111") then
pwm_out <= '1';
elsif (pwm_count < serial_data) then
pwm_out <= '1';
else
pwm_out <= '0';
end if;
end process;

Assuming the original code inferred an 8 bit comparator, I'd guess the above
code would then infer an 8 bit and gate whose output is OR'd into the output
of the comparator (at least that is what I'd do). But it seems like I should
be able to save the two macrocells this is going to eat by changing the
logic of the inferred comparator to include an output of '1' when all inputs
are 1.

2) The second problem is that I would really like the pwm output to change
exactly on the period boundary. Now I started this with a simple state
machine where a new input set a latch to tell me to change, and then added
that into the sensitivity list of the pwm function like so:

ld: process (serial_load) is
begin
if rising_edge(serial_load) then
new_data <= '1';
end if;
end process;

pwm: process is (pwm_count, pwm_data, new_data) is
begin
if (new_data = '1') and (pwm_count = "11111111") then
pwm_data <= serial_data;
new_data <= '0';
elsif (pwm_data = "11111111") then
pwm_out <= '1';
elsif (pwm_count < pwm_data) then
pwm_out <= '1';
else
pwm_out <= '0';
end if;
end process;


This is designed to create a simple state machine between the ld and pwm
processes such that if there is new data, then that data is loaded when the
count is full. However I'm getting errors trying to synthesize this.
Basically XST doesn't like the way I'm assigning new_data in different
processes (although as an engineer it makes sense to me, I'd like to infer
and R/S flip flop and have pwm_count & new_data drive its "R" input and
"serial load" drive its "S" input. Trying to use this:
ld: process (serial_load) is
begin
if serial_load = '1' then
new_data <= '1';
end if;
end process;

Leads to the case where holding serial load high through a full PWM cyle
would cause both R/S to be asserted. So I tried to infer something like a
modified T flip flop to set it on the clock edge. Thoughts anyone?

--
--Chuck McManis
Email to the devnull address is discarded
http://www.mcmanis.com/chuck/robotics/
 
Chuck McManis wrote:

State machines and synchronization.

The background is that I'm working on this PWM unit that uses a serial
shift register to hold the pulse "width" data. The original code goes
something like:

-- process to manage clocking in the data
sr: process (sdata_in, sclock, reset) is
begin
if (reset = '1') then
serial_data <= "00000000";
elsif rising_edge(sclock) then
serial_data <= sdata_in &
serial_data(serial_data'length-1 downto 0);
end if;
end process;
OK. We are receiving and shifting in the pwm bitstream
into parallel bytes.

Take sdata_in out of the sensitivity list and
you have a synchronous process that will
sim and synth the same way.

Hmmm. Why are we making bytes in the first place?
-_______--______----____-------_

Don't we just want to preset a counter
while the data is high and count down
while it is low?

-- Mike Treseler
 
I see that you are using a < comparator to generate pwm. Although this
works OK it takes quite a lot of logic to implement ( see the RTL
schematics and it will all become clear to you ). Some time ago i
needed a six chanel 8 bit pwm generator. I used the 95108, and with <
comparators it wouldn't fit. So I used annother method - compare
match. The basic idea is that you don't need to know if the pwm data
is less that set data, just if they are equal ( == comparator ), which
takes a lot less logic. I created a counter entitiy with clk input and
8 bit counter output. This counter is common to all six pwm
generators. Although you need a serial load generator, I am stil
including my code for a parallel load. But is should be easy for you
to modify it into a serial.

str is strobe signal for the latch
Din is 8 bit parallel data input
duty_cycle is the latched input data
Cin is the 8 bit counter input 8
Clk is the main clock, also used by the counter
pwm is the PWM output

-- data latch
p00: process(rst,str)
begin
if rst = '1' then
duty_cycle <= "00000000";
elsif str'event and str = '1' then
duty_cycle <= Din;
end if;
end process;


-- pwm comparator
p01: process(clk,rst)
begin
if rst = '1' then
pwm <= '0';
elsif clk'event and clk = '1' then
if Cin = "11111111" then
pwm <= '1';
elsif Cin = duty_cycle then
pwm <= '0';
end if;
end if;
end process;


Best regards to you all,
George Mercury
 
On Sat, 22 May 2004 11:59:07 -0700, Chuck McManis wrote:


1) I don't get "full" PWM. This is because the pwm process is forced to
use either < or > If I want to support both 0% on (output stays low) and
100% on, (output stays high), I need an additional statement to cover
that.

I can modify pwm as follows:
pwm: process is (pwm_count, serial_data) is begin
if (serial_data = "11111111") then
pwm_out <= '1';
elsif (pwm_count < serial_data) then
pwm_out <= '1';
else
pwm_out <= '0';
end if;
end process;

Assuming the original code inferred an 8 bit comparator, I'd guess the
above code would then infer an 8 bit and gate whose output is OR'd into
the output of the comparator (at least that is what I'd do). But it
seems like I should be able to save the two macrocells this is going to
eat by changing the logic of the inferred comparator to include an
output of '1' when all inputs are 1.

It looks like that would cause a discontinuity from 254/256 duty to
256/256. I guess that would bother me more that not getting to 100% on.



2) The second problem is that I would really like the pwm output to
change exactly on the period boundary. Now I started this with a simple
state machine where a new input set a latch to tell me to change, and
then added that into the sensitivity list of the pwm function like so:

ld: process (serial_load) is
begin
if rising_edge(serial_load) then
new_data <= '1';
end if;
end process;

pwm: process is (pwm_count, pwm_data, new_data) is begin
if (new_data = '1') and (pwm_count = "11111111") then
pwm_data <= serial_data;
new_data <= '0';
elsif (pwm_data = "11111111") then
pwm_out <= '1';
elsif (pwm_count < pwm_data) then
pwm_out <= '1';
else
pwm_out <= '0';
end if;
end process;


This is designed to create a simple state machine between the ld and pwm
processes such that if there is new data, then that data is loaded when
the count is full. However I'm getting errors trying to synthesize this.
Basically XST doesn't like the way I'm assigning new_data in different
processes (although as an engineer it makes sense to me, I'd like to
infer and R/S flip flop and have pwm_count & new_data drive its "R"
input and "serial load" drive its "S" input. Trying to use this:
ld: process (serial_load) is
begin
if serial_load = '1' then
new_data <= '1';
end if;
end process;

Leads to the case where holding serial load high through a full PWM cyle
would cause both R/S to be asserted. So I tried to infer something like
a modified T flip flop to set it on the clock edge. Thoughts anyone?
Since you probably have a high speed clock for the PWM, I would probably
just use that for everything, as long as your serial clock was enough
lower, then you would have a fully synchonous system which would avoid
a number or problems.

As others have pointed out there are some simpler ways to generate PWM:

one is the = comparator and SR flipflop

There is also a way for N bit PWM that uses a N+1 bit accumulator where the N+1
bit is used as the PWM output.

This design has the advantage that it does not require a reference
counter and that the input can be changed asyncronously relative to the
PWM period and the output will be correct (its kind of an intergrating
PWM source)

The comparator method has the advantage that if you want to filter the
PWM (say to generate a audio signal) you can easily generate interleaved
PWM by bit reversing any desired number of reference count MSBs


Peter Wallace
 
"George" <george_mercury@hotmail.com> wrote in message
news:6d167a0a.0405222134.5947a13c@posting.google.com...
... So I used annother method - compare
match.
Wow! The light bulb goes on! That's a great idea, a simple set of XOR gates
feeding an AND gate make for a match compare. I'll no doubt run into a bit
of trouble later when I'm trying to run the sequences "out" of phase, but I
can do that brute force with a simple adder. Thanks George!

--Chuck
 

Welcome to EDABoard.com

Sponsor

Back
Top