0

I'm trying to create a PWM generator using a 100khz clock and PWM ranging from .6ms to 2.4 ms but I'm stuck implementing this into vhdl I've been trying to use a state machine to do this but it has become more complicated than it should.

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
--use IEEE.NUMERIC_STD.ALL;

-- Uncomment the following library declaration if instantiating
-- any Xilinx primitives in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

entity PWM is
    Port ( clk : in  STD_LOGIC;
           reset : in  STD_LOGIC;
           pwm_out : out  STD_LOGIC;
              PWM_CONST : STD_LOGIC_VECTOR(2 downto 0));
end PWM;

architecture Behavioral of PWM is
type State_type is (LOW,HIGH,Counting); --States
signal Sreg, Snext : State_type; --Curent state and next state
signal t20ms,t100ms,t_6ms,t_825ms,t1_05ms,t1_275ms,t1_5ms,t1_725ms,t1_95ms,t2_175ms,t2_4ms: STD_LOGIC; 
signal twenty : STD_LOGIC;
signal count_flag : STD_LOGIC; 
begin
-------------------------Clock Dividers for timing---------------------------------------
process(clk,reset)
variable toggle_20ms,toggle_1666hz,toggle_1212hz,toggle_952hz,toggle_785hz,toggle_666hz,toggle_579hz,toggle_512hz,toggle_460hz,toggle_416hz  : STD_LOGIC :='0'; 
variable counter_20ms : integer range 0 to 999; 
variable counter_1666hz : integer range 0 to 29;
variable counter_1212hz : integer range 0 to 40;
variable counter_952hz : integer range 0 to 51;
variable counter_785hz : integer range 0 to 63;
variable counter_666hz : integer range 0 to 74;
variable counter_579hz : integer range 0 to 85;
variable counter_512hz : integer range 0 to 96;
variable counter_460hz : integer range 0 to 107;
variable counter_416hz : integer range 0 to 119;
 begin
 if reset = '1' then
      t_6ms<='0';
        t_825ms<='0';
        t1_05ms<='0';
        t1_275ms<='0';
        t1_5ms<='0';
        t1_725ms<='0';
        t1_95ms<='0';
        t2_175ms<='0';
        t2_4ms<='0';
        twenty<='0';
 else
    if(clk'event and clk = '1') then 
        counter_20ms := counter_20ms+1;
        counter_1666hz := counter_1666hz+1;
        counter_1212hz := counter_1212hz+1;
        counter_952hz := counter_952hz+1;
        counter_785hz := counter_785hz+1;
        counter_666hz := counter_666hz+1;
        counter_579hz := counter_579hz+1;
        counter_512hz := counter_512hz+1;
        counter_460hz := counter_460hz+1;
        counter_416hz := counter_416hz+1; 
        counter_20ms := counter_20ms+1; 
        if (counter_1666hz = 29) then 
            toggle_1666hz := not toggle_1666hz; 
            counter_1666hz := 0; 
        end if;
        if (counter_1212hz = 40) then 
            toggle_1212hz := not toggle_1212hz; 
            counter_1212hz := 0; 
        end if;
        if (counter_952hz  = 51) then 
            toggle_952hz  := not toggle_952hz ; 
            counter_952hz  := 0; 
        end if;
        if (counter_785hz = 63) then 
            toggle_785hz := not toggle_785hz; 
            counter_785hz := 0; 
        end if;
        if (counter_666hz = 74) then 
            toggle_666hz := not toggle_666hz; 
            counter_666hz := 0; 
        end if;
        if (counter_579hz = 85) then 
            toggle_579hz := not toggle_579hz; 
            counter_579hz := 0; 
        end if;
        if (counter_512hz = 96) then 
            toggle_512hz := not toggle_512hz; 
            counter_512hz := 0; 
        end if;
        if (counter_460hz = 107) then 
            toggle_460hz := not toggle_460hz; 
            counter_460hz := 0; 
        end if;
        if (counter_416hz = 119) then 
            toggle_416hz := not toggle_416hz; 
          counter_416hz := 0; 
       end if;
        if (counter_20ms = 999) then 
            toggle_20ms := not toggle_20ms;
            counter_20ms := 0;
        end if; 
        t_6ms<=toggle_1666hz;
        t_825ms<=toggle_1212hz;
        t1_05ms<=toggle_952hz;
        t1_275ms<=toggle_785hz;
        t1_5ms<=toggle_666hz;
        t1_725ms<=toggle_579hz;
        t1_95ms<=toggle_512hz;
        t2_175ms<=toggle_460hz;
        t2_4ms<=toggle_416hz;
        twenty<=toggle_20ms;
    end if; 
end if;
end process; 

-------------------------Next State Logic----------------------------------
    process(Sreg,reset,PWM_CONST)
        begin 
            case Sreg is 
                when LOW => if (rising_edge(twenty)) then Snext <= HIGH; 
                                else                                 Snext <= LOW; 
                                end if; 
                when HIGH => if reset = '1'        then Snext <= LOW;
                                 else                   Snext <= Counting; 
                                 end if;
                when Counting => if count_flag = '1'     then Snext <= Counting;
                                      else               Snext <= LOW; 
                                      end if; 
            end case; 
    end process; 
-----------------------Update State----------------------------------------
    process(clk)
        begin 
            if (clk'event and clk='1') then 
                Sreg <= Snext;
            end if;
    end process; 
------------------------Count_flag---------------------------------------------
    process(clk,PWM_CONST)
        begin
        case PWM_CONST is
                when "000"=> t_6ms <= count_flag;
                when "001"=> t_825ms <= count_flag;
                when "010"=> t1_05ms <= count_flag;
                when "011"=> t1_275ms <= count_flag;
                when "100"=> t1_5ms <= count_flag;
                when "101"=> t1_725ms <= count_flag;
                when "110"=> t1_95ms <= count_flag;
                when "111"=> t2_175ms <= count_flag;
                when others => t2_4ms <= count_flag;
        end case;       
    end process; 
----------------------Output Logic-----------------------------------------
with Sreg select -- output logic based on state only
        pwm_out <= '1' when HIGH | Counting,
                      '0' when LOW,
                      '0' when others;
---------------------------------------------------------------------------                   
end Behavioral;
<code>

I get a bad synchronous error when I try to synthesize

zach thomas
  • 1
  • 2
  • 2
  • Maybe you should rethink your design because PWM generation just needs a bunch of counters, a LUT for counter start or counter max values and if needed a simple T-flipflop for the state. A PWM signal has a fixed frequency and variable duty cycle. So for example counter1 counts 0.6 ms and counter2 2.4 ms. That's a period of 3ms aka 333 Hz. Depending on your input, you can select different values for counter1/2 to change the duty cycle: 1.2/1.8, 1.8/1.2, 2.4/0.6. – Paebbels Apr 14 '15 at 05:59

1 Answers1

1

There is at least three major problems with your code. The first one is your next state process:

process(Sreg,reset,PWM_CONST)
    begin 
        case Sreg is 
            when LOW => if (rising_edge(twenty)) then Snext <= HIGH; 
                            else                                 Snext <= LOW; 
                            end if; 
            when HIGH => if reset = '1'        then Snext <= LOW;
                             else                   Snext <= Counting; 
                             end if;
            when Counting => if count_flag = '1'     then Snext <= Counting;
                                  else               Snext <= LOW; 
                                  end if; 
        end case; 
end process;

You can't have a rising_edge in a case, I'm not sure what electronic component it would represent. When using rising_edge, all the statements has to be enclosed within it (with exception of asynchronous reset), like you did for your clock divider and update state processes.

If you want to detect that the signal twenty goes from '0' to '1', here how you would do it:

process(reset, clk)
begin
    if reset = '1' then
        twenty_dl <= '0';
    elsif rising_edge(clk) then
        twenty_dl <= twenty; -- Delayed version of twenty

        if twenty_dl = '0' and twenty = '1' then -- twenty is rising
            -- Insert logic here
        end if;
    end if;
end process;

As a side note, a signal driven by a register should never be used as a clock. Clocks are sensible, they come from a clock enabled pin on the fpga and are modified through PLLs and such. Using a register to drive a clock signal is supported (unfortunately), but the tool can't analyze the net as it does for global clock, i.e. your design may fail without the tool telling you about it. If you want to "divide" a clock, use clock enables:

process(reset, clk)
begin
    if reset = '1' then
        cnt <= (others => '0');
    elsif rising_edge(clk) then
        if cnt = 199 then -- Divide the clock by 200
            clk_div_200_en <= '1';
            cnt <= (others => '0');
        else
            clk_div_200_en <= '0';
            cnt <= cnt + 1;
        end if;
    end if;
end process;

process(clk)
begin
    if rising_edge(clk) and clk_div_200_en = '1' then
        -- Insert logic here
    end if;
end process;

The second major problem with your code is you have multiple drivers for signals t_6ms and cie. Both your clock divider process and your count_flag process assign these signals. In VHDL, all assignations to a signal must be done in a single process. Otherwise, you will get multiple drivers which is generally not supported in synthesis and can result in conflicts on the net.

The third major problem is the count_flag process. This process is combinational (no clock). In a combinational process, any signal the process assigns has to be assign in every path through the process. Which means, t_6ms has to be assigned when PWM_CONST is "001", "010", etc. Failure to do so will result in a latch element, which is bad!

Jonathan Drolet
  • 3,318
  • 1
  • 12
  • 23