Back to Code and Software Demo

VHDL – Water Treatment Controller

Goal

The goal of this project is to design a Water Treatment Controller that monitors real-time water quality using turbidity, pH, chlorine, and flow rate sensors. The system generates multi-level alerts (Normal, Level 1, Level 2, Level 3) based on an internal water quality score computed from the sensor readings. This project demonstrates the ability to design modular, robust VHDL architectures suitable for industrial water monitoring, with configurable thresholds for proactive safety management.

Engineering Approach and Tools

The system is implemented in VHDL, simulated using GHDL. Sensor inputs and control outputs are modeled with std_logic_vector signals, and score calculations are handled using unsigned arithmetic to avoid overflow. The design consists of three main processes:

The testbench applies realistic sensor values representing normal conditions, Level 1, Level 2, Level 3 alerts, and a recovery scenario, ending with a controlled assert false statement to mark the simulation end.

Execution Behavior and Output Interpretation

Simulation confirms correct functionality:

The simulation ends intentionally with END OF SIMULATION, which is standard in GHDL and indicates controlled termination rather than an error.

Code Cells

design.vhd
--Author: Hamza Bendahmane library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; entity WaterTreatmentController is generic ( NUM_PROCESS_UNITS : integer := 8; SENSOR_DATA_WIDTH : integer := 16; CONTROL_PRECISION : integer := 12 ); port ( clk : in std_logic; reset_n : in std_logic; -- Sensor inputs turbidity_sensor : in std_logic_vector(SENSOR_DATA_WIDTH-1 downto 0); ph_sensor : in std_logic_vector(SENSOR_DATA_WIDTH-1 downto 0); chlorine_sensor : in std_logic_vector(SENSOR_DATA_WIDTH-1 downto 0); flow_rate_sensor : in std_logic_vector(SENSOR_DATA_WIDTH-1 downto 0); -- Control outputs coagulant_dose : out std_logic_vector(CONTROL_PRECISION-1 downto 0); chlorine_dose : out std_logic_vector(CONTROL_PRECISION-1 downto 0); filter_backwash : out std_logic; pump_speed : out std_logic_vector(7 downto 0); -- System status - Now with 2 bits for alert levels water_quality_alert : out std_logic_vector(1 downto 0); -- 00: Normal, 01: Level 1, 10: Level 2, 11: Level 3 system_efficiency : out std_logic_vector(7 downto 0); energy_consumption : out std_logic_vector(15 downto 0) ); end entity; architecture AdvancedControl of WaterTreatmentController is -- 10-bit to avoid overflow signal internal_quality_score : unsigned(9 downto 0) := (others => '0'); -- Alert threshold constants (based on industrial standards) constant ALERT_LEVEL1_THRESHOLD : integer := 125; -- Enhanced monitoring constant ALERT_LEVEL2_THRESHOLD : integer := 225; -- Corrective action constant ALERT_LEVEL3_THRESHOLD : integer := 300; -- Emergency shutdown -- Internal signal for alert level to use in case statement signal alert_level_internal : std_logic_vector(1 downto 0); begin -- COMBINATORIAL PROCESS for quality score (IMMEDIATE) quality_calc: process(turbidity_sensor, ph_sensor, chlorine_sensor) begin internal_quality_score <= resize(unsigned(turbidity_sensor(15 downto 8)), 10) + resize(unsigned(ph_sensor(15 downto 8)), 10) + resize(unsigned(chlorine_sensor(15 downto 8)), 10); end process; -- COMBINATORIAL PROCESS for alert detection (IMMEDIATE) alert_detection: process(internal_quality_score) begin if internal_quality_score >= ALERT_LEVEL3_THRESHOLD then alert_level_internal <= "11"; -- Level 3: Critical alert elsif internal_quality_score >= ALERT_LEVEL2_THRESHOLD then alert_level_internal <= "10"; -- Level 2: High alert elsif internal_quality_score >= ALERT_LEVEL1_THRESHOLD then alert_level_internal <= "01"; -- Level 1: Early warning else alert_level_internal <= "00"; -- Normal level end if; end process; -- CLOCKED PROCESS (only for registered outputs) main_process: process(clk, reset_n) begin if reset_n = '0' then coagulant_dose <= (others => '0'); chlorine_dose <= (others => '0'); pump_speed <= (others => '0'); filter_backwash <= '0'; system_efficiency <= (others => '0'); energy_consumption <= (others => '0'); elsif rising_edge(clk) then -- Control outputs scaled coagulant_dose <= std_logic_vector(resize(unsigned(turbidity_sensor(11 downto 0)), CONTROL_PRECISION)); chlorine_dose <= std_logic_vector(resize(unsigned(chlorine_sensor(11 downto 0)), CONTROL_PRECISION)); pump_speed <= std_logic_vector(unsigned(flow_rate_sensor(15 downto 8))); -- Filter backwash based on current alert level (IMMEDIATE) if alert_level_internal >= "10" then -- Level 2 or 3 filter_backwash <= '1'; else filter_backwash <= '0'; end if; -- Connect internal signal to output port water_quality_alert <= alert_level_internal; -- System efficiency and energy (demo scaling) system_efficiency <= std_logic_vector(to_unsigned(255,8) - unsigned(flow_rate_sensor(15 downto 8))); energy_consumption <= std_logic_vector(resize(unsigned(flow_rate_sensor),16)); -- Debug print for simulation with alert levels case alert_level_internal is when "00" => report "Score=" & integer'image(to_integer(internal_quality_score)) & " | NORMAL"; when "01" => report "Score=" & integer'image(to_integer(internal_quality_score)) & " | ALERT LEVEL 1 - Monitoring"; when "10" => report "Score=" & integer'image(to_integer(internal_quality_score)) & " | ALERT LEVEL 2 - Corrective action"; when "11" => report "Score=" & integer'image(to_integer(internal_quality_score)) & " | ALERT LEVEL 3 - CRITICAL"; when others => report "Score=" & integer'image(to_integer(internal_quality_score)) & " | UNKNOWN STATE"; end case; end if; end process; end architecture;
testbench.vhd
-- --Author: Hamza Bendahmane -- Multi-level alerts triggered by water quality library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; entity tb_WaterTreatmentController is end entity; architecture sim of tb_WaterTreatmentController is signal clk : std_logic := '0'; signal reset_n : std_logic := '0'; signal turbidity_sensor, ph_sensor, chlorine_sensor, flow_rate_sensor : std_logic_vector(15 downto 0); signal coagulant_dose, chlorine_dose : std_logic_vector(11 downto 0); signal filter_backwash : std_logic; signal pump_speed : std_logic_vector(7 downto 0); signal water_quality_alert : std_logic_vector(1 downto 0); -- Changed to 2 bits signal system_efficiency : std_logic_vector(7 downto 0); signal energy_consumption : std_logic_vector(15 downto 0); begin clk <= not clk after 10 ns; UUT: entity work.WaterTreatmentController port map( clk => clk, reset_n => reset_n, turbidity_sensor => turbidity_sensor, ph_sensor => ph_sensor, chlorine_sensor => chlorine_sensor, flow_rate_sensor => flow_rate_sensor, coagulant_dose => coagulant_dose, chlorine_dose => chlorine_dose, filter_backwash => filter_backwash, pump_speed => pump_speed, water_quality_alert => water_quality_alert, system_efficiency => system_efficiency, energy_consumption => energy_consumption ); stimulus: process begin reset_n <= '0'; wait for 50 ns; reset_n <= '1'; -- Normal water (Score ~31) turbidity_sensor <= x"1000"; ph_sensor <= x"0700"; chlorine_sensor <= x"0800"; flow_rate_sensor <= x"4000"; wait for 100 ns; -- Level 1 Alert (Score ~65) turbidity_sensor <= x"3000"; ph_sensor <= x"0800"; chlorine_sensor <= x"0900"; flow_rate_sensor <= x"5000"; wait for 100 ns; -- Level 2 Alert (Score ~276) turbidity_sensor <= x"FF00"; ph_sensor <= x"0A00"; chlorine_sensor <= x"0B00"; flow_rate_sensor <= x"6000"; wait for 100 ns; -- Level 3 Alert (Need higher values - Score >300) turbidity_sensor <= x"FF80"; ph_sensor <= x"0F00"; chlorine_sensor <= x"0F00"; flow_rate_sensor <= x"7000"; wait for 100 ns; -- Recovery to normal turbidity_sensor <= x"0800"; ph_sensor <= x"0700"; chlorine_sensor <= x"0600"; flow_rate_sensor <= x"4000"; wait for 100 ns; assert false report "END OF SIMULATION" severity failure; wait; end process; end architecture;
© 2025 – Hamza Bendahmane. All rights reserved.