# LED PWM controller

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.

• Your using a Spartan 6? Nov 8 '14 at 20:30
• Where do you set the port as output? Nov 8 '14 at 20:31
• Yes it is Spartan 6. Where do I set the port as output ? What do you mean ? Nov 8 '14 at 21:15
• If you use "View RTL Schematic" you'll see that synthesis has dropped whatever is not recognized/supported.
– mng
Nov 10 '14 at 20:35

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'.

• I understand your idea it is really good. Referring you your first sentence I am not allowed to perform division and a multiplication operation on a signal explicitly ?? Nov 11 '14 at 22:32
• Yes you can write these operations in vhdl, but I think it was not your intention to use masses of FPGA resources :) if the synthesis tool is smart it will recognize, that your division by 2**N can be replaced by a shift operation, but at least the multiplication needs a hardware multiplier or if the input value is small it can be translated into a ROM / LookupTable. So I think using some synthesis time constant 'calculation magic', 2 counters and a comparator is one of the cheapest and most flexible solutions. Nov 12 '14 at 0:35