Problem writing quadrature decoder

Do you have a question? Post it now! No Registration Necessary

Threaded View
Hi there - I am continuing to attempt to learn VHDL this weekend!
Currently I'm trying to interface to the quadrature encoder on my
Spartan 3E Starter Kit. It outputs normal quadrature signals. So, I
tried to write a very simple bit of code for this purpose, which just
checks which edge on which signal occurred and then checks the state
of the other signal and infers if the count should be incremented or
decremented from that. My code is at the bottom of my post.

In theory this method of quadrature decoding should work perfectly,
unless I'm forgetting something. But for some reason which I'm afraid
I don't understand this is not synthesizable. I liked the idea of
using this method for quadrature decoding as it didn't require me to
deal with storing the previous state - the use of the falling_edge()
and rising_edge() functions did that for me. Xilinx ISE help brought
me to this page: http://www.xilinx.com/support/answers/14047.htm .
However, I don't have any embedded 'event statements, or any 'event
statements at all, for that matter (unless again I'm missing
something).

What exactly am I doing wrong, and is there a way to fix it? Thanks so
much!

-Michael

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.numeric_std.all;

entity hello_world is
port (
    clk, enc_a, enc_b : in std_logic;
    switches : in std_logic_vector (3 downto 0);
    led : out std_logic_vector (7 downto 0)
);
end hello_world;

architecture rtl of hello_world is
    signal cnt : unsigned (30 downto 0);
    signal encval : unsigned (7 downto 0);
    signal enccnt : unsigned (7 downto 0);
begin
    process(clk)
    begin
        if rising_edge(clk) then
            cnt <= cnt + 1;
            encval <= "000000" & enc_b & enc_a;
        end if;
    end process;
    process (enc_a, enc_b)
    begin
        if (rising_edge(enc_a) and enc_b = '1') then
            enccnt <= enccnt - 1;
        elsif (rising_edge(enc_a) and enc_b = '0') then
            enccnt <= enccnt + 1;
        elsif (falling_edge(enc_a) and enc_b = '1') then
            enccnt <= enccnt + 1;
        elsif (falling_edge(enc_a) and enc_b = '0') then
            enccnt <= enccnt - 1;
        elsif (rising_edge(enc_b) and enc_a = '1') then
            enccnt <= enccnt + 1;
        elsif (rising_edge(enc_b) and enc_a = '0') then
            enccnt <= enccnt - 1;
        elsif (falling_edge(enc_b) and enc_a = '1') then
            enccnt <= enccnt - 1;
        elsif (falling_edge(enc_b) and enc_a = '0') then
            enccnt <= enccnt + 1;
        end if;
    end process;

    led <= std_logic_vector(cnt(30 downto 23)) when switches(0)='0' else
std_logic_vector(encval);

end rtl;

Re: Problem writing quadrature decoder

Quoted text here. Click to load it

1. Using inputs as clocks.
2. Using two clocks in a process.

and is there a way to fix it?

Declare as many registers as you need,
but put everything in your first process.
Have a look at my examples.

     -- Mike Treseler



Re: Problem writing quadrature decoder
Quoted text here. Click to load it

So rising_edge() and falling_edge() can only be used with clocks?

Quoted text here. Click to load it

Why does everything have to be in one process? Is there a reason it's
objectionable to have one process that is sensitive to only my clock
and one process that's only sensitive to a couple inputs? And so
you're suggesting I go with a state based approach? Or something else?
What are these extra registers for?

Quoted text here. Click to load it

What examples are you referring to?

Thanks!

-Michael

Re: Problem writing quadrature decoder

Quoted text here. Click to load it

For synchronous designs, yes.

Quoted text here. Click to load it

It doesn't. Just a suggestion.

Peter has the problem solved for you.
Post a question to comp.lang.vhdl if you need more help.

    -- Mike Treseler

Re: Problem writing quadrature decoder

Quoted text here. Click to load it

The quadrature encoder on the Spartan 3E Starter Kit is mechanical, you
should implement some debouncing. Maybe some simple holdoff is sufficient,
but if there are fast crosstalk glitches, a simple low pass filter (in
VHDL) would be a good idea, too.

--
Frank Buss, fb@frank-buss.de
http://www.frank-buss.de , http://www.it4-systems.de

Re: Problem writing quadrature decoder
Quoted text here. Click to load it

Hi Frank - I thought about debouncing and - unless I'm being dumb - I
think as long as only one input is changing at a time, bounce won't
affect this approach in the steady state. I mean that if I turn it 4
counts, it might count something like 0 1 2 3 2 3 4 3 4. But the final
value will be correct.

-Michael

Re: Problem writing quadrature decoder

Quoted text here. Click to load it

There are different classes of Quad encoder.
The Simplest feed one phase into a CLK and the
other into DIRN, but that counts only once per whole cycle.

The best designs can count on every edge, and can tolerate
a chattering edge. You might also want to catch
illegal state jumps (missed states), as that indicates
something is amiss in your design.
One easy to understand way to code this, is to create
a internal 2 bit phase engine, and lock it to the
external sampled edges. That design makes illegal
state jumps easy to catch.
You have a simple state engine, with 2 IP bits,
2 Present bits, [16 combinations] and output  CE, DIRN, and ERR,
as well as 2 bits for Next state.

-jg

Re: Problem writing quadrature decoder
Quoted text here. Click to load it

I'm looking to count every single edge. I had another idea about how
to do it:

process(enc_a, enc_b)
begin
    if enc_a'event then
        case prevstate is
            when "00"3D%>
                enc_cnt <3D% enc_cnt + 1;
            when "01"3D%>
                enc_cnt <3D% enc_cnt - 1;
            when "10"3D%>
                enc_cnt <3D% enc_cnt - 1;
            when "11"3D%>
                enc_cnt <3D% enc_cnt + 1;
            when others 3D%>
                enc_cnt <3D% enc_cnt;
        end case;
    else
        case prevstate is
            when "00"3D%>
                enc_cnt <3D% enc_cnt - 1;
            when "01"3D%>
                enc_cnt <3D% enc_cnt + 1;
            when "10"3D%>
                enc_cnt <3D% enc_cnt + 1;
            when "11"3D%>
                enc_cnt <3D% enc_cnt - 1;
            when others 3D%>
                enc_cnt <3D% enc_cnt;
        end case;
    end if;

    prevstate <3D% enc_a & enc_b;
end process;

Unfortunately - apparently the "if enc_a'event then" line is not
synthesizable - you can't trigger on both edges. So once again I'm
stuck.

Any suggestions?

Thanks!

-Michael

Re: Problem writing quadrature decoder
Quoted text here. Click to load it

Michael, when you say: count every single edge", do you mean every
edge of A, or every edge of either A or B ?
One is 2 edges per 360 deegrees, the other one is 4. Mine is 1.
Incidentally, why is this important? For manual control, you have too
many edges to start with...
Peter

Re: Problem writing quadrature decoder
Quoted text here. Click to load it

I'm looking for 4 counts/360 degrees. My final use for quadrature
decoding will be with motor control - so increasing the resolution of
my encoder counting is very beneficial.

-Michael

Re: Problem writing quadrature decoder
Quoted text here. Click to load it

A-ha! I realized I could get rid of the edge checking and just compare
with my saved previous state. It synthesizes! Unfortunately, it gives
me some warnings during synthesization, and then (I believe due to the
warnings) it won't give me a programming file. The warnings are:

the following signal(s) form a combinatorial loop: led<7>, enc_cnt<7>,
Maddsub_enc_cnt14.

(for led<7> - led<0>)

Then it gives me a bunch of errors during mapping, like this one:

ERROR:MapLib:661 - LUT4 symbol "Maddsub_enc_cnt41" (output
   signal3D%Maddsub_enc_cnt4) has input signal "Maddsub_enc_cnt" which
will be
   trimmed. See the t


I'm so close now (I think!) Can anybody tell me what is wrong with my
code? I've never had a problem with mapping before - so this is very
odd for me. I've posted the full code below. Thanks so much!

-Michael



library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.numeric_std.all;

entity hello_world is
port (
    clk, enc_a, enc_b : in std_logic;
    switches : in std_logic_vector (3 downto 0);
    led : out std_logic_vector (7 downto 0)
);
end hello_world;

architecture rtl of hello_world is
    signal cnt : unsigned (30 downto 0);
    signal encval : unsigned (7 downto 0);
    signal enc_cnt : unsigned (7 downto 0);
    signal prevstate : std_logic_vector (1 downto 0);
begin
    process(clk)
    begin
        if rising_edge(clk) then
            cnt <3D% cnt + 1;
            encval <3D% "000000" & enc_b & enc_a;
        end if;
    end process;

process(enc_a, enc_b)
begin
    if prevstate(0) /3D% enc_a then
        case prevstate is
            when "00"3D%>
                enc_cnt <3D% enc_cnt + 1;
            when "01"3D%>
                enc_cnt <3D% enc_cnt - 1;
            when "10"3D%>
                enc_cnt <3D% enc_cnt - 1;
            when "11"3D%>
                enc_cnt <3D% enc_cnt + 1;
            when others 3D%>
                enc_cnt <3D% enc_cnt;
        end case;
    else
        case prevstate is
            when "00"3D%>
                enc_cnt <3D% enc_cnt - 1;
            when "01"3D%>
                enc_cnt <3D% enc_cnt + 1;
            when "10"3D%>
                enc_cnt <3D% enc_cnt + 1;
            when "11"3D%>
                enc_cnt <3D% enc_cnt - 1;
            when others 3D%>
                enc_cnt <3D% enc_cnt;
        end case;
    end if;

    prevstate(0) <3D% enc_a;
    prevstate(1) <3D% enc_b;
end process;

    led <3D% std_logic_vector(cnt(30 downto 23)) when switches(0)3D%'0' else
std_logic_vector(enc_cnt);

end rtl;

Re: Problem writing quadrature decoder
<snip>
Quoted text here. Click to load it

You almost have what you want but you will still have troubles because
mechanical chatter can matter in a binary implementation.  One way you
CAN achieve your goals is to use Gray code counters, at least for the
LSbits.

I previously designed a 3-bit Gray code counter for a first stage of a
very fast counter such that I only have 1 LUT of delay for each bit and
was able to count at a clock frequency limited by the delay of a single
LUT.  My design produced the same combinatorial loops warning but for a
stable  combinatorial quadrature counter, those are expected warnings
and should still produce a programming file.  Even here, I used this
first stage as a divide-by-8 prescaler rather than using the count
directly because getting a 3-bit Gray counter to sync up with a
multi-bit Gray or binary counter isn't trivial though it is doable with
a little care.

The key to getting a quadrature counter to work with a Gray code counter
is to realize that when you are at a specific Gray count, only two bits
can change: one for the forward direction, one for reverse.  Effectively
one channel of the quadrature counter can be slaved to the LSbit while
the count determines which upper bit (and at what polarity) the other
quadrature channel controls.  When you transition between two upper bits
being controlled by the one quadrature channel, the bits are both safe
because the quadrature channel that controls those upper bits is in the
middle, stable part of its cycle while the LSbit channel is
transitioning the count, affecting which upper bit is controlled.

When the Gray count is read, the value is (typically) registered in the
clock domain twice such that if one bit is caught mid-transition then
that value will be settled upon by the second register - only one
register - as a defined value.  If instead there are multiple registers
fed by that one first-stage value, it's possible two paths could "see"
different values for this asynchronously changing signal.

If you can figure how to count in Gray and then how to change one bit
(at a defined polarity) at a time for a given count, you will have an
every-transition counter for full resolution and be
asynchronous-friendly in your design.  And you will get plenty of
combinatorial-loop warnings for your design but you will be able to
produce a programming file; they are just warnings, after all.

- John_H

Re: Problem writing quadrature decoder
You have my solution available at :
http://www.alse-fr.com/English/ips.html

There is a test bench and simulation script.

As mentioned : don't forget to register A & B before hooking to the
module.

Bertrand


Re: Problem writing quadrature decoder
Quoted text here. Click to load it

John - unless I'm missing something here... I think just watching
edges is good enough to deal with any sort of debouncing problems, as
long as only A or B (not both) are bouncing at any given time.

But either way - right now I really just want to figure out why I
can't get the stupid thing to map properly - and then I will worry
more about dealing with the small things. I just posted the full code,
constraints, and console output on the FPGA4Fun forum:
http://www.fpga4fun.com/forum/viewtopic.php?t3D%1281

Can somebody please look at this? I just am at a complete loss as to
what is wrong - but to you more experienced people the problem will
probably jump right out.

Thanks!

-Michael

Re: Problem writing quadrature decoder
<snip>
Quoted text here. Click to load it
<snip>

Can you guarantee that the edge will transition fully and stay
transitioned for a minimum length of time?

While most debounce problems are a true mechanical bounce issue where
hard contacts make it difficult to engage and hold a contact
instantly, I'm not comfortable enough to say that a bounce will be a
hard transition from one logic level to another with microsecond-scale
valid levels between bounces.  I've always considered the mechanical
transition to be undefined enough to make undebounced (I like this
word) switches extremely poor for things like clocks.

In your case, do you even use those edges as clocks?  I thought you
had non-clocked combinatorial loops where you had binary counts
affected by the edge.  If that edge gets undefined, some bits may go
one way while other bits go another way in that binary word unless you
can guarantee the debounced behavior of those edges.

The Gray code based quadrature counter doesn't have problems with
bounce.  At all.

I'd like to make some time to look at your code but I natively code
Verilog and I don't have a synthesizer at home.  I'll see what I can
do.

- John_H

Re: Problem writing quadrature decoder
Quoted text here. Click to load it

What does your trim report say about the trimmed signals?

From your mapper output you posted on FPGA4Fun, "See the trim report
for details about why the input signal will become undriven."

- John_H

Re: Problem writing quadrature decoder
Quoted text here. Click to load it

Hi John - I couldn't figure out how to look at the trim report. There
wasn't a single file with trim in it in the project directory. I
GREPed the directory for trim and didn't turn up anything except for
places where it says to look at the trim report. I googled and
couldn't find anything, and I checked the Xilinx website for
information about how to look at the trim report and couldn't find
anything. Any suggestion as to where I can find this report?

Thanks!

-Michael

Re: Problem writing quadrature decoder

Quoted text here. Click to load it


It's part of the mapper report file, *.mrp (often mydesign.mrp or map.mrp)

- Brian

Re: Problem writing quadrature decoder

Quoted text here. Click to load it

you only have 2 state bits, once you get 4, you can do it on a faster
clock edge,


So if you consider a quad-state/johnson counter, as a first pass.
[easier to follow than binary], and what you are coding is a phase
lock, or
tracking system counter (TSC) that spins that counter to match the
Quad IP.
It CAN spin at any speed, normally it will do one phase click, and
then
many,many idle states until the next phase click is needed


IP.TSC
00.00
01.01
11.11
10.10   are all HOLD or do nothing states, MOST clocks will be here

00.01
01.11
11.10
10.00 are all DEC(TSC) by one, and output DEC_EN to the long counter

00.10
01.00
11.01
10.11 are all INC(TSC) by one, and output INC_EN to the long counter

which leaves 4 other states
00.11
01.10
11.00
10.01 as different by TWO clocks, so these are illegal
and should never happen in a correct margin design.
Take them to a sticky-led, perhaps ?.

Once you have that working, you can compact the two TSC bits into the
Long counter, two LSBs, by change to binary coding, if you really want
to
save two FFs  :)
In a FPGA tho,  why bother ?
( In a small CPLD, you might take the effort. )

Also, on read/compare of the long counter, if the system WORD size
is less than the counter size, you need to capture ALL bits
in one clock, in case the counter rolls over between byte/word reads.

Jim Granville,

Re: Problem writing quadrature decoder
(snip on quadrature decoders)

Quoted text here. Click to load it

As long as the bounce isn't faster than the counter can count, yes.

I like the clocked design that Peter A. has in another post.
I believe that one works as long as the clock is faster than
the fastest possible real count.  Bounces might be missed, but
the count will be right.

Also, I believe one should reset the counter on the first index
pulse and not on  subsequent ones.

-- glen


Site Timeline