Search code examples
countervhdlfpga

Why is my VHDL counter not outputting pulses as desired?


I am working on creating a variable-frequency pulse train to control a motor, and am using the following code to generate pulses using a counter that counts up by some input increment value inc_i. I then pipe the MSB of the output of my counter, dout_o[N-1], to an output pin on my FPGA. This should give me a 50% duty cycle square wave of the desired frequency, but I am just seeing a signal that starts low, goes high, and then never turns off again. Is there something obviously wrong with my code?

--****************************************************************************
-- Load required libraries
--****************************************************************************
library ieee;
    use ieee.std_logic_1164.all;
    use ieee.numeric_std.all;

--****************************************************************************
-- Define the inputs, outputs, and parameters
--****************************************************************************
entity var_count is

    generic(N: integer :=32);               -- for generic counter size
    port(
            inc_i       : in    std_logic_vector(N-1 downto 0);
            load_i      : in    std_logic;
            clk_i       : in    std_logic;
            clear_i     : in    std_logic;
            clk_en_i    : in    std_logic;
            count_en_i  : in    std_logic;
            msb_o       : out   std_logic
        );

end var_count;

--****************************************************************************
-- Define the behavior of the counter
--****************************************************************************
architecture behavior of var_count is

    -- Define our count variable. No need to initialize in VHDL.
    signal count : unsigned(N-1 downto 0) := to_unsigned(0, N);
    signal incr  : unsigned(N-1 downto 0) := to_unsigned(0, N);

begin   
    -- Define our clock process
    clk_proc : process(clk_i, clear_i, load_i)
    begin
        -- Asynchronous clear
        if clear_i = '1' then
            count <= to_unsigned(0, N);
        end if;

        -- Asynchronous load
        if load_i = '1' then
            incr <= unsigned(inc_i);
        end if;

        -- Define processes synch'd with clock.
        if rising_edge(clk_i) and clk_en_i = '1' then
            if count_en_i = '1' then            -- increment the counter
                count <= count + incr;
            end if;
        end if;     
    end process clk_proc;

    -- Output the MSB for the sake of generating a nice easy square wave.
    msb_o <= count(count'left);

end behavior;

The square wave frequency should be given by the equation pulse_freq = clock_freq * inc_i / 2^N for an N-bit counter.

I am also trying to generate a single clock-cycle pulse by passing the MSB of my counter output (msb_o(k)) through a single-bit D-Q flip-flop to get msb_o(k-1) and then executing:

pulse = ~msb_o(k) * msb_o(k-1)

where ~ represents logical NOT, and * represents logical AND. This should give me a single clock-cycle pulse only when the counter rolls over. Neither this nor the MSB itself (50% duty cycle square wave) are showing when I read the output pin using an oscilloscope. Please let me know if you see any errors in my code.

Thank you for your help!

Reference that I used to put this together can be found here, here, and here.

EDIT 1: I have updated the code in the question with suggestions that users have made. Now when I output the MSB of the counter, it goes high and never turns off again. I need to make the counter reset on overflow (which I have tried to do in the above). Any suggestions for how I can do this?

EDIT 2: I realized that since my increment value inc_i is not necessarily one, I may not be reaching a value of 2**N - 1 for rollover calculations. I therefore changed the rollover condition to:

if count > (2**N - unsigned(inc_i) - 1) then
    count <= to_unsigned(0, N);

I now seem to be getting pulses, but they are not exactly 50% duty cycle (which I think makes some sense) and they do not seem to change as expected when I change my increment value inc_i. Any idea why the rate is not changing?

EDIT 3: I realized that all I care about for my application is the MSB of the counter since that is what I will be using to either generate a 50% duty cycle square wave, or a pulse when it rolls over. In light of this, I replaced dout_o in the entity declaration with:

`msb_o : out std_logic`

and I replaced the concurrent assignment statement at the end with:

msb_o <= '1' when count > (2**(N-1) - 1) else '0';

I am still getting very odd pulses that are not at all 50% duty cycle or necessarily the correct frequency. Any further suggestions would be greatly appreciated.

EDIT 4: Updated my code with further changes. Also adding this free book to the list of references.

EDIT 5: Updated my code to the (final) working edition.


Solution

  • The process does not adhere to general rules for a process to be recognized as flip-flops by the synthesis tools, since the outer level for conditions does not cover the signals in the process sensitivity list. The synthesis tool has probably given one or more warnings about this.

    One way to rewrite the process to adhere to general rules for synthesizable flip-flops is:

    clk_proc : process (CLK)
    begin
      if (CLK'event and CLK = '1') then
        if CLEAR = '1' then           -- clear the counter
          COUNT <= COUNT - COUNT;
        elsif CLK_EN = '1' then       -- increment the counter
          COUNT <= COUNT + INC;
        end if;
      end if;
    end process clk_proc;
    

    This places the check for clock edge at the outer level, and assumes that the CLEAR is synchronous.

    More nformation about VHDL coding style for registers is in for example Xilinx XST User Guide.

    Your description of the single clock-cycle pulse looks OK, so the reason that it does not work is probably derived from the above.

    A larger rewrite of the module may be considered, in order to apply these useful VHDL coding rules:

    • Input and output ports are named with _i and _o, since this ease reading of code where the module is instantiated
    • Signals without default value, since this may not work over all FPGA technologies
    • Only upper case for constant identifiers, since this makes code reading easier.
    • Don't use std_logic_unsigned, since this is Synopsys library and not VHDL standard
    • Use rising_edge() for edge detection, for readability
    • Use (others => '0') to clear, instead of COUNT - COUNT, since this works in simulation even if COUNT is all X's

    Example in code below:

    library ieee;
    use ieee.std_logic_1164.all;
    use ieee.numeric_std.all;
    
    entity var_count is
      port(
        inc_i    : in  std_logic_vector(31 downto 0);
        clk_i    : in  std_logic;
        clear_i  : in  std_logic;
        clk_en_i : in  std_logic;
        dout_o   : out std_logic_vector(31 downto 0));
    end var_count;
    
    architecture behavior of var_count is
    
      -- Define our count variable
      signal count : std_logic_vector(31 downto 0);
    
    begin
    
      -- Define our clock process
      clk_proc : process (clk_i)
      begin
        if rising_edge(clk_i) then
          if clear_i = '1' then           -- clear the counter
            count <= (others => '0');
          elsif clk_en_i = '1' then       -- increment the counter
            count <= std_logic_vector(unsigned(count) + unsigned(inc_i));
          end if;
        end if;
      end process clk_proc;
    
      -- Concurrent assignment statement
      dout_o <= count;
    
    end behavior;
    

    If you are not already using a simulator, then ModelSim maybe a great improvement to your toolbox. Alera has ModelSim-Altera Starter Edition that is free, and can be used for smal designs.