I am trying to control 13 different servos with my Spartan3 FPGA. For that, I need 13 independent PWM signals generated by my FPGA. The PWM module that I am using works perfectly fine when only one output pin is controlled (instantiated as follows).
SERVO_1 : entity work.Servo(Behavioral)
port map (
clk => clk,
rst => rst,
position => s_pos_1, -- integer from 0 to 255 (256 steps from 0° to 180°)
pwm => pwm_1 -- output pin of FPGA
);
But as soon as I add more servo entities, the output of the PWM pins behave unexpectedly. Some are constantly high, low, or are constant in their pulse width (frozen). Overall, the behaviour is (for me) kind of random (talking about actual hardware behavior). I instantiate the entities as follows:
SERVO_1 : entity work.Servo(Behavioral)
port map (
clk => clk,
rst => rst,
position => s_pos_1,
pwm => pwm_1
);
SERVO_2 : entity work.Servo(Behavioral)
port map (
clk => clk,
rst => rst,
position => s_pos_2,
pwm => pwm_2
);
SERVO_3 : entity work.Servo(Behavioral)
port map (
clk => clk,
rst => rst,
position => s_pos_3,
pwm => pwm_3
);
-- ... and so on
What I checked so far
- The simulation works as expected, every pwm output behaves correctly.
- I can safely say that the problem has nothing to do with the servos/current/power supply/drivers.
- I am checking the outputs on a scope all the time.
- Checked that there are no signals without default value
- Changed multiplication in VHDL to accessing a ROM with the needed values (which were multiplicated before, I thought that maybe the multiplication part caused the problems)
- Checked the logic utilization of my FPGA, which is rather low
- Checked the timing report of ISE Webpack, no errors
Besides that I get no errors during synthesis.
There are two infos regarding timing:
- Timing:3390 - This architecture does not support a default System Jitter value, please add SYSTEM_JITTER constraint to the UCF to modify the Clock Uncertainty calculation.
- Timing:3389 - This architecture does not support 'Discrete Jitter' and 'Phase Error' calculations, these terms will be zero in the Clock Uncertainty calculation. Please make appropriate modification to SYSTEM_JITTER to account for the unsupported Discrete Jitter and Phase Error
Source code of Servo.vhd:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.math_real.round;
entity Servo is
port (
clk : in std_logic;
rst : in std_logic;
position : in integer range 0 to 255;
pwm : out std_logic
);
end Servo;
architecture Behavioral of Servo is
constant clk_hz : real := 50.0e6; -- 50 MHz Spartan3
constant pulse_hz : real := 50.0; -- 50 Hz PWM-Signal
constant min_pulse_us : real := 500.0; -- 0° pulse width
constant max_pulse_us : real := 2500.0; -- 180° pulse width
constant step_bits : positive := 8; -- 0 to 255
constant step_count : positive := 2**step_bits;
-- Number of clock cycles in Microseconds
function cycles_per_us (us_count : real) return integer is
begin
return integer(round(clk_hz / 1.0e6 * us_count));
end function;
constant min_count : integer := cycles_per_us(min_pulse_us); -- 0° in clock cycles
constant max_count : integer := cycles_per_us(max_pulse_us); -- 180° in clock cycles
constant min_max_range_us : real := max_pulse_us - min_pulse_us;
constant step_us : real := min_max_range_us / real(step_count - 1);
constant cycles_per_step : positive := cycles_per_us(step_us); -- clock cycles per step (1/256)
constant counter_max : integer := integer(round(clk_hz / pulse_hz)) - 1; -- clock cycles for 20 ms (50 Hz)
signal counter : integer range 0 to counter_max;
signal duty_cycle : integer range 0 to max_count;
begin
-- position <= to_integer(rom_data);
COUNTER_PROC : process(clk) -- basic counter
begin
if rising_edge(clk) then
if rst = '1' then
counter <= 0;
else
if counter < counter_max then
counter <= counter + 1;
else
counter <= 0;
end if;
end if;
end if;
end process;
PWM_PROC : process(clk) -- Setting the pwm pin according to duty_cycle
begin
if rising_edge(clk) then
if rst = '1' then
pwm <= '0';
else
pwm <= '0';
if counter < duty_cycle then
pwm <= '1';
end if;
end if;
end if;
end process;
DUTY_CYCLE_PROC : process(clk) -- Setting the duty_cycle
begin
if rising_edge(clk) then
if rst = '1' then
duty_cycle <= min_count;
else
duty_cycle <= position * cycles_per_step + min_count;
end if;
end if;
end process;
end Behavioral;
'clk' is connected to the 50 MHz oscillator pin. 'rst' is a signal and comes from a reset module that synchronizes everything with 3 FFs.