Question

I'm implementing a quadrature decoder in VHDL and came up with two solutions.

In method 1, all of the logic is placed within one process that is sensitive to clock and reset. On a Spartan-3A, this uses four slices, seven FFs and four input LUTs.

Code 1

architecture Behavioral of quadr_decoder is
    signal chan_a_curr : std_logic;
    signal chan_a_prev : std_logic;
    signal chan_b_curr : std_logic;
    signal chan_b_prev : std_logic;
begin
    process (n_reset, clk_in) begin
        if (n_reset = '0') then
            -- initialize internal signals
            chan_a_curr <= '0';
            chan_a_prev <= '0';
            chan_b_curr <= '0';
            chan_b_prev <= '0';
            -- initialize outputs
            count_evt <= '0';
            count_dir <= '0';
            error_evt <= '0';
        elsif (clk_in'event and clk_in = '1') then
            -- keep delayed inputs
            chan_a_prev <= chan_a_curr;
            chan_b_prev <= chan_b_curr;
            -- read current inputs
            chan_a_curr <= chan_a;
            chan_b_curr <= chan_b;
            -- detect a count event
            count_evt <= ((chan_a_prev xor chan_a_curr) xor
                          (chan_b_prev xor chan_b_curr));
            -- determine count direction
            count_dir <= (chan_a_curr xor chan_b_prev xor 
                          count_mode);
            -- detect error conditions
            error_evt <= ((chan_a_prev xor chan_a_curr) and 
                          (chan_b_prev xor chan_b_curr));
        end if;
    end process;
end Behavioral;

Method 2 splits the logic into separate sequential and combinatorial processes. It uses two slices, four FFs and four input LUTs.

Code 2

architecture Behavioral of quadr_decoder is
    signal chan_a_curr : std_logic;
    signal chan_a_prev : std_logic;
    signal chan_b_curr : std_logic;
    signal chan_b_prev : std_logic;
begin
    process (n_reset, clk_in) begin
        if (n_reset = '0') then
            -- initialize internal signals
            chan_a_curr <= '0';
            chan_a_prev <= '0';
            chan_b_curr <= '0';
            chan_b_prev <= '0';
        elsif (clk_in'event and clk_in = '1') then
            -- keep delayed inputs
            chan_a_prev <= chan_a_curr;
            chan_b_prev <= chan_b_curr;
            -- read current inputs
            chan_a_curr <= chan_a;
            chan_b_curr <= chan_b;
        end if;
    end process;

    process (chan_a_prev, chan_a_curr, chan_b_prev, chan_b_curr) begin
            -- detect a count event
            count_evt <= ((chan_a_prev xor chan_a_curr) xor 
                          (chan_b_prev xor chan_b_curr));
            -- determine count direction
            count_dir <= (chan_a_curr xor chan_b_prev xor count_mode);
            -- detect error conditions
            error_evt <= ((chan_a_prev xor chan_a_curr) and 
                         (chan_b_prev xor chan_b_curr));
    end process;
end Behavioral;

When I simulate the code (behavioral), both results look fine. But I can't believe that both methods are equally valid. Could somebody shed some light into which method should be favored over the other?

Was it helpful?

Solution

Your code version 2 drives the outputs combinatorially whereas code version 1 registers the outputs:

  • count_evt
  • count_dir
  • error_evt

This accounts for the 3 additional flip-flops (and since a Spartan 3 has 2xregisters per slice means you need 2 additional slices).

While the logical function performed by the code is identical they will not behave the same. If/when you connect the outputs to another blocks inputs the result will be available 1 cycle earlier for version 2. Assuming the downstream block takes these inputs and applies some more logic you will see that version 2 results in longer paths and therefore the achievable frequency is lower.

Some guidelines state that you should generally register the outputs to blocks to improve timing. Sometimes you want to be able to chain multiple blocks together combinatorially so as with all guidelines there are always some exceptions. It is good practice to comment in the declaration if any outputs are driven combinatorially. If you're feeling particularly keen you could make the output register optional using a generic.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top