LED PWM controller

fpgavhdl

I want to create Led PWM controller and I tought it is easy but one line in my code generates more warnings than all my previous little projects. Here is the code:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity pwmLed is
generic(    N : integer := 4
);
port( pwm : in std_logic_vector(N-1 downto 0);
        clk : in std_logic;
        leds : out std_logic_vector(7 downto 0)
);
end pwmLed;

architecture Behavioral of pwmLed is
constant CLOCK_CYCLES : integer := 1000;
signal counter : integer ;
begin

process(clk)
begin
    if counter > CLOCK_CYCLES then
        counter <= 0;
    elsif (clk'event and clk = '1') then
        counter <= counter + 1;
    end if;
end process;

leds <= "11111111" when counter < (to_integer(unsigned(pwm))/(2**N) * CLOCK_CYCLES) else "00000000";
end Behavioral;

The problematic line is :

leds <= "11111111" when counter < (to_integer(unsigned(pwm))/(2**N) * CLOCK_CYCLES)  else "00000000";

All the warnings:

WARNING:HDLCompiler:92 – "E:\Xilinx\Projects\PWM_LED\pwmLed.vhd" Line 48: counter should be on the sensitivity list of the process

WARNING:Xst:647 – Input is never used. This port will be preserved and left unconnected if it belongs to a top-level block or it belongs to a sub-block and the hierarchy of this sub-block is preserved.

WARNING:Xst:3002 – This design contains one or more registers/latches that are directly
incompatible with the Spartan6 architecture. The two primary causes of this is
either a register or latch described with both an asynchronous set and
asynchronous reset, or a register or latch described with an asynchronous
set or reset which however has an initialization value of the opposite
polarity (i.e. asynchronous reset with an initialization value of 1).

WARNING:Xst:1426 – The value init of the FF/Latch counter_31_LD hinder the constant cleaning in the block pwmLed.
You should achieve better results by setting this init to 0.

WARNING:Par:288 – The signal pwm<0>_IBUF has no load. PAR will not attempt to route this signal.

WARNING:Par:288 – The signal pwm<1>_IBUF has no load. PAR will not attempt to route this signal.

WARNING:Par:288 – The signal pwm<2>_IBUF has no load. PAR will not attempt to route this signal.

WARNING:Par:288 – The signal pwm<3>_IBUF has no load. PAR will not attempt to route this signal.

WARNING:Par:283 – There are 4 loadless signals in this design. This design will cause Bitgen to issue DRC warnings.

WARNING:PhysDesignRules:372 – Gated clock. Clock net
counter[31]_GND_4_o_LessThan_1_o is sourced by a combinatorial pin. This is
not good design practice. Use the CE pin to control the loading of data into
the flip-flop.

I understand them but I got no idea why they occur. I use pwm explicitly so why the warning that this input is never used.

Best Answer

You are performing a division and a multiplication operation on a signal. At least the multiplication is mapped to a hardware multiplication circuit on your FPGA.

Your module provides the generic parameter N, which could also be called PWM_RESOLUTION. Beside this, I miss two additional parameters or constants in your module:

  1. The clock frequency of the system clock
  2. The pwm output frequency [Notice: don't forget that pwm signals have a fixed frequency but variable duty cycle.]

One easy way to build a pwm generator is to use two counters:

  • Counter 1 is a frequency divider, which divides the system clock into \$2^N \cdot f_{pwm}\$ Hz
  • Counter 2 counts \$2^N\$ cycles on every tick from counter 1

Here some calculations for the frequency counter:

constant PWM_FREQ          : freq     := 25 kHz;
constant SYSTEM_CLOCK_FREQ : freq     := 100 MHz;

constant STEPS             : positive := 2**N;
constant STEP_FREQ         : freq     := PWM_FREQ * STEPS;
constant FREQ_COUNTER_MAX  : positive := TimingToCycles(to_period(STEP_FREQ), SYSTEM_CLOCK_FREQ);
constant FREQ_COUNTER_BITS : positive := log2ceilnz(FREQ_COUNTER_MAX);

signal Freq_Counter  : unsigned(FREQ_COUNTER_BITS - 1 downto 0) := (others => '0');
signal Pulse_Counter : unsigned(N - 1 downto 0)                 := (others => '0');

Some hints on the compact code above:

  • freq is a new physical type representing a frequency - you can also use the real type
  • to_period converty a frequency into a period (a time)
  • TimingToCyles is a function which calculates how many cycles must pass until a given timing (period) has elapsed at a given circuit frequency.
  • log2ceilnz (logarithm dualis, round up, not zero) calculates \$max(1, \lceil log_2(x)\rceil)\$

Example 1:
If N=4 the pwm signal can have 16 duty cycle steps. Counter 1 is running at 400 kHz. This equals a period of 2.5 us, which need 250 cycles at 100 MHz to achieve this timing. A 8 bit signal is needed for counter 1.

The output is then a simple comparison:

PWMOut <= to_sl(PulseCounters < unsigned(PWMIn));

This code does not prohibit the input of PWMIn=0 to produce a flat-line output, which would not be a correct pwm signal (100% low, 0% high, no edges -> no frequency)

Example 2:
If PWMIn is 3 ("0011") then PWMOut is '1' for counter values 0,1,2 else '0'.