XADC Fan PWM

From Hamsterworks Wiki!

Jump to: navigation, search

This |FPGA Project was completed in December 2015.

The fan on the Genesys 2 FPGA board is configured to spin at full speed unless the designer does something about it.

So here is how you can do something about it, without the full complexity of a PID system.

The XADC is used to read the system temperature, and this is then used to control the speed of the fan.

Source

xadc_temp.vhd

A lot of these settings are defaults copied out of the XADC User Guide. The ADC is configured to scan a few channels, and the fabric interface is set up to just read the temperature (register 0).

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

Library UNISIM;
use UNISIM.vcomponents.all;

entity xadc_temp is
    Port ( clk100   : in  STD_LOGIC;
           temp_k   : out STD_LOGIC_VECTOR (8 downto 0));
end xadc_temp;

architecture Behavioral of xadc_temp is
    signal reading : std_logic_vector(11 downto 0) := (others => '0');
    signal reading_x2048 : unsigned(22 downto 0) := (others => '0');
    signal reading_x32   : unsigned(16 downto 0) := (others => '0');
    signal reading_x1    : unsigned(11 downto 0) := (others => '0');
    signal temp_scaled : unsigned(22 downto 0) := (others => '0');

    signal muxaddr : std_logic_vector( 4 downto 0) := (others => '0');
    signal channel : std_logic_vector( 4 downto 0) := (others => '0');
begin
    -- Temp in kelvin = (reading/16) * 2015/16384
    reading_x2048 <= unsigned(reading) & to_unsigned(0,11);
    reading_x32   <= unsigned(reading) & to_unsigned(0,5);
    reading_x1    <= unsigned(reading);
    temp_scaled   <=  reading_x2048 - reading_x32 - reading_x1;

process(clk100)
    begin
        if rising_edge(clk100) then
            temp_k        <= std_logic_vector(temp_scaled(temp_scaled'high downto 14));
        end if;
    end process;
XADC_inst : XADC generic map (
      -- INIT_40 - INIT_42: XADC configuration registers
      INIT_40 => X"9000", -- averaging of 16 selected for external channels
      INIT_41 => X"2ef0", -- Continuous Seq Mode, Disable unused ALMs, Enable calibration
      INIT_42 => X"0800", -- ACLK = DCLK/8 = 100MHz / 8 = 12.5 MHz
      -- INIT_48 - INIT_4F: Sequence Registers
      INIT_48 => X"4701", -- CHSEL1 - enable Temp VCCINT, VCCAUX, VCCBRAM, and calibration
      INIT_49 => X"0000", -- CHSEL2 - enable nothing else
      INIT_4A => X"0001", -- SEQAVG1 disabled all channels
      INIT_4B => X"0000", -- SEQAVG2 disabled all channels
      INIT_4C => X"0000", -- SEQINMODE0 - all channels unipolar
      INIT_4D => X"0000", -- SEQINMODE1 - all channels unipolar
      INIT_4E => X"0000", -- SEQACQ0 - No extra settling time all channels
      INIT_4F => X"0000", -- SEQACQ1 - No extra settling time all channels
      -- INIT_50 - INIT_58, INIT5C: Alarm Limit Registers
      INIT_50 => X"b5ed", -- Temp upper alarm trigger 85°C
      INIT_51 => X"5999", -- Vccint upper alarm limit 1.05V
      INIT_52 => X"A147", -- Vccaux upper alarm limit 1.89V
      INIT_53 => X"dddd", -- OT upper alarm limit 125°C - see Thermal Management
      INIT_54 => X"a93a", -- Temp lower alarm reset 60°C
      INIT_55 => X"5111", -- Vccint lower alarm limit 0.95V
      INIT_56 => X"91Eb", -- Vccaux lower alarm limit 1.71V
      INIT_57 => X"ae4e", -- OT lower alarm reset 70°C - see Thermal Management
      INIT_58 => X"5999", -- VCCBRAM upper alarm limit 1.05V
      INIT_5C => X"5111", -- VCCBRAM lower alarm limit 0.95V

      -- Simulation attributes: Set for proper simulation behavior
      SIM_DEVICE       => "7SERIES",    -- Select target device (values)
      SIM_MONITOR_FILE => "design.txt"  -- Analog simulation data file name
   ) port map (
      -- ALARMS: 8-bit (each) output: ALM, OT
      ALM          => open,             -- 8-bit output: Output alarm for temp, Vccint, Vccaux and Vccbram
      OT           => open,             -- 1-bit output: Over-Temperature alarm

      -- STATUS: 1-bit (each) output: XADC status ports
      BUSY         => open,             -- 1-bit output: ADC busy output
      CHANNEL      => channel,          -- 5-bit output: Channel selection outputs
      EOC          => open,             -- 1-bit output: End of Conversion
      EOS          => open,             -- 1-bit output: End of Sequence
      JTAGBUSY     => open,             -- 1-bit output: JTAG DRP transaction in progress output
      JTAGLOCKED   => open,             -- 1-bit output: JTAG requested DRP port lock
      JTAGMODIFIED => open,             -- 1-bit output: JTAG Write to the DRP has occurred
      MUXADDR      => muxaddr,          -- 5-bit output: External MUX channel decode

      -- Auxiliary Analog-Input Pairs: 16-bit (each) input: VAUXP[15:0], VAUXN[15:0]
      VAUXN        => (others => '0'),            -- 16-bit input: N-side auxiliary analog input
      VAUXP        => (others => '0'),            -- 16-bit input: P-side auxiliary analog input

      -- CONTROL and CLOCK: 1-bit (each) input: Reset, conversion start and clock inputs
      CONVST       => '0',              -- 1-bit input: Convert start input
      CONVSTCLK    => '0',              -- 1-bit input: Convert start input
      RESET        => '0',              -- 1-bit input: Active-high reset

      -- Dedicated Analog Input Pair: 1-bit (each) input: VP/VN
      VN           => '0', -- 1-bit input: N-side analog input
      VP           => '0', -- 1-bit input: P-side analog input

      -- Dynamic Reconfiguration Port (DRP) -- hard set to read channel 6 (XADC4/XADC0)
      DO(15 downto 4) => reading,
      DO(3 downto 0)  => open,
      DRDY            => open,
      DADDR           => "0000000",  -- The address for reading tempeatue
      DCLK            => clk100,
      DEN             => '1',
      DI              => (others => '0'),
      DWE             => '0'
   );
end Behavioral;

fan_pwm.vhd

The PWM frequency is a little low, so you can are able to hear the fan pulse, but apart from that it works fine.


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

entity fan_pwm_control is
    Port ( clk100 : in STD_LOGIC;
           temp_k : in STD_LOGIC_VECTOR (8 downto 0);
           fan_pwm : out STD_LOGIC);
end fan_pwm_control;

architecture Behavioral of fan_pwm_control is
    signal cycle_counter : unsigned(19 downto 0) := (others => '0');
    signal ms_counter    : unsigned(4 downto 0)  := (others => '0');
    signal new_ms        : std_logic             := '0';
    signal pwm_level     : unsigned(4 downto 0)  := (others => '0');
begin

process(clk100)
    begin
        if rising_edge(clk100) then
            if ms_counter < pwm_level then
                fan_pwm <= '1';
            else
                fan_pwm <= '0';
            end if;


            if new_ms = '1' then
               if ms_counter = 15 then
                    ms_counter <= (others => '0');

                    if unsigned(temp_k) < 30+273 then
                        pwm_level <= to_unsigned(2,5);
                    elsif unsigned(temp_k) < 35+273 then
                        pwm_level <= to_unsigned(4,5);
                    elsif unsigned(temp_k) < 40+273 then
                        pwm_level <= to_unsigned(6,5);
                    elsif unsigned(temp_k) < 45+273 then
                        pwm_level <= to_unsigned(7,5);
                    elsif unsigned(temp_k) < 50+273 then
                        pwm_level <= to_unsigned(8,5);
                    elsif unsigned(temp_k) < 55+273 then
                        pwm_level <= to_unsigned(10,5);
                    elsif unsigned(temp_k) < 60+273 then
                        pwm_level <= to_unsigned(12,5);
                    elsif unsigned(temp_k) < 65+273 then
                        pwm_level <= to_unsigned(14,5);
                    else
                        pwm_level <= to_unsigned(16,5);
                    end if;

               else
                  ms_counter <= ms_counter + 1;
               end if;
            end if;
            if cycle_counter = 999999 then
                new_ms  <= '1';
                cycle_counter <= (others => '0');
            else
                new_ms  <= '0';
                cycle_counter <= cycle_counter + 1;
            end if;
        end if;
    end process;
end Behavioral

Personal tools