In 2024 I spent some time to find a good solution to convert digital audio to analog signals. I ended up using different types of sigma-delta-modulators with different orders and tested multiple configurations. The following implementations contain the most promising solutions for the Class-D-Amplifier we built up at the University of Kassel in 2024 for the IFEC competition. As the implementations are done by different people, please take note of the individual copyrights and licenses.
For a better understanding, here the three signals displayed over the time: the input (sinewave between 0 and 255), the internal accumulator and the output (digital signal):

In plain C the first-order-modulator would look like this:
int16_t input;
int16_t feedback = 0;
int16_t accumulator = 0;
bool output = false;
accumulator = accumulator + input - feedback;
if (accumulator > 255) {
output = true;
feedback = 255;
}else{
output = false;
feedback = 0;
}
And now the implementation in VHDL:
First Order Sigma-Delta-Modulator
-- This file contains a first-order sigma-delta-converter
-- Source by Mike Field (https://github.com/hamsternz/second_order_sigma_delta_DAC)
--
-- MIT License
--
-- Copyright (c) 2020 Mike Field
--
-- Permission is hereby granted, free of charge, to any person obtaining a copy
-- of this software and associated documentation files (the "Software"), to deal
-- in the Software without restriction, including without limitation the rights
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-- copies of the Software, and to permit persons to whom the Software is
-- furnished to do so, subject to the following conditions:
--
-- The above copyright notice and this permission notice shall be included in all
-- copies or substantial portions of the Software.
--
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-- SOFTWARE.
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
entity sigma_delta_first is
generic(
bit_width : natural range 16 to 32 := 24
);
Port (
clk : in STD_LOGIC;
sample_in : in STD_LOGIC_VECTOR (23 downto 0);
output : out STD_LOGIC
);
end sigma_delta_first;
architecture Behavioral of sigma_delta_first is
signal dac_out : std_logic := '0';
signal dac_accum : signed(bit_width + 8 - 1 downto 0) := (others => '0');
signal dac_feedback : signed(bit_width + 8 - 1 downto 0) := (others => '0');
begin
output <= dac_out;
with dac_out select dac_feedback <= to_signed(-2**(bit_width - 1), bit_width + 8) when '1',
to_signed( 2**(bit_width - 1), bit_width + 8) when others;
dac_proc: process(clk)
variable new_val : signed(bit_width + 8 - 1 downto 0);
begin
if rising_edge(clk) then
-- calculate output
new_val := dac_accum + signed(sample_in) + dac_feedback;
-- quantizing to 1-bit data and calculate feedback-value
if new_val < 0 then
dac_out <= '0';
else
dac_out <= '1';
end if;
-- store values
dac_accum <= new_val;
end if;
end process;
end Behavioral;Second-Order Sigma-Delta-Modulator
-- This file contains a second-order sigma-delta-converter
-- Source by Mike Field (https://github.com/hamsternz/second_order_sigma_delta_DAC)
--
-- MIT License
--
-- Copyright (c) 2020 Mike Field
--
-- Permission is hereby granted, free of charge, to any person obtaining a copy
-- of this software and associated documentation files (the "Software"), to deal
-- in the Software without restriction, including without limitation the rights
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-- copies of the Software, and to permit persons to whom the Software is
-- furnished to do so, subject to the following conditions:
--
-- The above copyright notice and this permission notice shall be included in all
-- copies or substantial portions of the Software.
--
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-- SOFTWARE.
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
entity sigma_delta_second is
generic(
bit_width : natural range 16 to 32 := 24
);
Port (
clk : in STD_LOGIC;
sample_in : in STD_LOGIC_VECTOR (23 downto 0);
output : out STD_LOGIC
);
end sigma_delta_second;
architecture Behavioral of sigma_delta_second is
signal dac_out : std_logic := '0';
signal dac_accum1 : signed(bit_width + 8 - 1 downto 0) := (others => '0');
signal dac_accum2 : signed(bit_width + 8 - 1 downto 0) := (others => '0');
signal dac_feedback : signed(bit_width + 8 - 1 downto 0) := (others => '0');
begin
output <= dac_out;
with dac_out select dac_feedback <= to_signed(-2**(bit_width - 1), bit_width + 8) when '1',
to_signed( 2**(bit_width - 1), bit_width + 8) when others;
dac_proc: process(clk)
variable new_val1 : signed(bit_width + 8 - 1 downto 0);
variable new_val2 : signed(bit_width + 8 - 1 downto 0);
begin
if rising_edge(clk) then
-- calculate outputs of both stages
new_val1 := dac_accum1 + signed(sample_in) + dac_feedback;
new_val2 := dac_accum2 + new_val1 + dac_feedback;
-- quantizing to 1-bit data and calculate feedback-value
if new_val2 < 0 then
dac_out <= '0';
else
dac_out <= '1';
end if;
-- store values
dac_accum1 <= new_val1;
dac_accum2 <= new_val2;
end if;
end process;
end Behavioral;Two-Stage Sigma-Delta-Converter with Noise-Shaping
-- Two-Stage Sigma-Delta-Converter with Noise-Shaping
-- Christian Noeding, christian@noeding-online.de
-- https://chrisdevblog.com | https://github.com/xn--nding-jua
--
-- Released under GNU General Public License v3
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
entity sigma_delta_twostage is
generic(
bit_width : natural range 16 to 32 := 24
);
port(
clk : in std_logic;
sample_in : in signed(bit_width - 1 downto 0);
output : out std_logic
);
end sigma_delta_twostage;
architecture Behavioral of sigma_delta_twostage is
signal dac_out : std_logic := '0';
signal feedback_z : signed(bit_width + 30 - 1 downto 0) := (others => '0');
signal feedback_zz : signed(bit_width + 30 - 1 downto 0) := (others => '0');
begin
output <= dac_out;
dac_proc: process(clk)
variable new_value : signed(bit_width + 30 - 1 downto 0); -- variables will be updated immediately
begin
if rising_edge(clk) then
-- calculate output
new_value := resize(sample_in, new_value'length) + feedback_z + feedback_z - feedback_zz;
-- quantizing to 1-bit data and calculate feedback-value
if (new_value < 0) then
dac_out <= '0'; -- digital output
feedback_z <= new_value - to_signed(-2**(bit_width - 1), feedback_z'length); -- feedback_z = new_value - dac_feedback
else
dac_out <= '1'; -- digital output
feedback_z <= new_value - to_signed(2**(bit_width - 1), feedback_z'length); -- feedback_z = new_value - dac_feedback
end if;
-- store second delay
feedback_zz <= feedback_z;
end if;
end process;
end Behavioral;