├── LICENSE ├── README.md ├── dac_1st_order.png ├── dac_2nd_order.png ├── dac_2nd_order_tweaked.png ├── first.png ├── first_order_dac.v ├── make_table.c ├── second.png ├── second_order_dac.v ├── sigma_delta_first.vhd ├── sigma_delta_second.vhd └── top_level.vhd /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Mike Field 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # second_order_sigma_delta 2 | A comparison of 1st and 2nd order sigma delta DAC for implementation in an FPGA. 3 | 4 | This small project implements two 16-bit DACs, one being a first order, the second being a second order. The Verilog and VHDL are functionally identical implmentations. 5 | 6 | A new sample is provided from a 50-entry lookup table every 1000 clock cycles, so with a 100MHz clock it generates a 2.000kHz signal. 7 | 8 | External to the FPGA will need to be a passive low pass filter. 9 | 10 | Spectrum of the 1st order DAC: 11 | 12 | ![First order spectrum](dac_1st_order.png) 13 | 14 | Spectrum of the 2nd order DAC: 15 | 16 | ![second order spectrum](dac_2nd_order.png) 17 | 18 | Code for the DAC modules is in VHDL and Verilog 19 | 20 | The lower two switches control which DAC is active, the next two control the rate they clock at (full, 1/2, 1/4 or 1/8th of the clock rate). 21 | 22 | With tweaking of the code you can achieve lower 2nd harmonics - ask me how! 23 | 24 | ![second order spectrum](dac_2nd_order_tweaked.png) 25 | -------------------------------------------------------------------------------- /dac_1st_order.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hamsternz/second_order_sigma_delta_DAC/ac1b39e76980410d2e400feca9c96e1a790445d6/dac_1st_order.png -------------------------------------------------------------------------------- /dac_2nd_order.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hamsternz/second_order_sigma_delta_DAC/ac1b39e76980410d2e400feca9c96e1a790445d6/dac_2nd_order.png -------------------------------------------------------------------------------- /dac_2nd_order_tweaked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hamsternz/second_order_sigma_delta_DAC/ac1b39e76980410d2e400feca9c96e1a790445d6/dac_2nd_order_tweaked.png -------------------------------------------------------------------------------- /first.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hamsternz/second_order_sigma_delta_DAC/ac1b39e76980410d2e400feca9c96e1a790445d6/first.png -------------------------------------------------------------------------------- /first_order_dac.v: -------------------------------------------------------------------------------- 1 | `timescale 1ns / 1ps 2 | module first_order_dac( 3 | input wire i_clk, 4 | input wire i_res, 5 | input wire i_ce, 6 | input wire [15:0] i_func, 7 | output wire o_DAC 8 | ); 9 | 10 | reg this_bit; 11 | reg [17:0] DAC_acc; 12 | reg [17:0] i_func_extended; 13 | 14 | assign o_DAC = this_bit; 15 | 16 | always @(*) 17 | i_func_extended = {i_func[15],i_func[15],i_func}; 18 | 19 | always @(posedge i_clk or posedge i_res) 20 | begin 21 | if (i_res==0) 22 | begin 23 | DAC_acc <= 16'd0; 24 | this_bit <= 1'b0; 25 | end 26 | else if(i_ce == 1'b1) 27 | begin 28 | if(this_bit == 1'b1) 29 | begin 30 | DAC_acc = DAC_acc + i_func_extended - (2**15); 31 | end 32 | else 33 | begin 34 | DAC_acc = DAC_acc + i_func_extended + (2**15); 35 | end 36 | // When the high bit is set (a negative value) we need to output a 0 and when it is clear we need to output a 1. 37 | this_bit = ~DAC_acc[17]; 38 | end 39 | end 40 | endmodule -------------------------------------------------------------------------------- /make_table.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define TABLE_SIZE 1000 5 | #define SCALE 16000 6 | #define LINE_VALUES 5 7 | 8 | int data[TABLE_SIZE]; 9 | 10 | int main(int argc, char *argv[]) { 11 | int i; 12 | 13 | for(i = 0; i < TABLE_SIZE; i++) { 14 | double angle = (i*2+1.0)/(TABLE_SIZE*2); 15 | double s = sin(angle*M_PI*2.0); 16 | data[i] = s*SCALE; 17 | } 18 | 19 | for(i = 0; i < TABLE_SIZE; i++) { 20 | if(i%LINE_VALUES == 0) 21 | printf(" "); 22 | 23 | printf("to_signed(%6i, 16)",data[i]); 24 | 25 | if(i == TABLE_SIZE-1) { 26 | printf(");\n"); 27 | } else if((i%LINE_VALUES) == LINE_VALUES-1) { 28 | printf(",\n"); 29 | } else { 30 | printf(", "); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /second.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hamsternz/second_order_sigma_delta_DAC/ac1b39e76980410d2e400feca9c96e1a790445d6/second.png -------------------------------------------------------------------------------- /second_order_dac.v: -------------------------------------------------------------------------------- 1 | `timescale 1ns / 1ps 2 | module second_order_dac( 3 | input wire i_clk, 4 | input wire i_res, 5 | input wire i_ce, 6 | input wire [15:0] i_func, 7 | output wire o_DAC 8 | ); 9 | 10 | reg this_bit; 11 | 12 | reg [19:0] DAC_acc_1st; 13 | reg [19:0] DAC_acc_2nd; 14 | reg [19:0] i_func_extended; 15 | 16 | assign o_DAC = this_bit; 17 | 18 | always @(*) 19 | i_func_extended = {i_func[15],i_func[15],i_func[15],i_func[15],i_func}; 20 | 21 | always @(posedge i_clk or negedge i_res) 22 | begin 23 | if (i_res==0) 24 | begin 25 | DAC_acc_1st<=16'd0; 26 | DAC_acc_2nd<=16'd0; 27 | this_bit = 1'b0; 28 | end 29 | else if(i_ce == 1'b1) 30 | begin 31 | if(this_bit == 1'b1) 32 | begin 33 | DAC_acc_1st = DAC_acc_1st + i_func_extended - (2**15); 34 | DAC_acc_2nd = DAC_acc_2nd + DAC_acc_1st - (2**15); 35 | end 36 | else 37 | begin 38 | DAC_acc_1st = DAC_acc_1st + i_func_extended + (2**15); 39 | DAC_acc_2nd = DAC_acc_2nd + DAC_acc_1st + (2**15); 40 | end 41 | // When the high bit is set (a negative value) we need to output a 0 and when it is clear we need to output a 1. 42 | this_bit = ~DAC_acc_2nd[19]; 43 | end 44 | end 45 | endmodule -------------------------------------------------------------------------------- /sigma_delta_first.vhd: -------------------------------------------------------------------------------- 1 | library IEEE; 2 | use IEEE.STD_LOGIC_1164.ALL; 3 | use IEEE.NUMERIC_STD.ALL; 4 | 5 | entity sigma_delta_first is 6 | Port ( clk : in STD_LOGIC; 7 | i : in STD_LOGIC_VECTOR (15 downto 0); 8 | reset : in STD_LOGIC; 9 | ce : in STD_LOGIC; 10 | o : out STD_LOGIC); 11 | end sigma_delta_first; 12 | 13 | architecture Behavioral of sigma_delta_first is 14 | signal dac_out : std_logic := '0'; 15 | signal dac_accum : signed(19 downto 0) := (others => '0'); 16 | signal dac_feedback : signed(19 downto 0) := (others => '0'); 17 | begin 18 | o <= dac_out; 19 | with dac_out select dac_feedback <= to_signed(-32768,20) when '1', 20 | to_signed( 32768,20) when others; 21 | dac2_proc: process(clk) 22 | variable new_val : signed(19 downto 0); 23 | begin 24 | if rising_edge(clk) then 25 | if reset = '0' then 26 | dac_accum <= (others => '0'); 27 | dac_out <= '0'; 28 | elsif ce = '1' then 29 | new_val := dac_accum + signed(i) + dac_feedback; 30 | if new_val < 0 then 31 | dac_out <= '0'; 32 | else 33 | dac_out <= '1'; 34 | end if; 35 | dac_accum <= new_val; 36 | end if; 37 | end if; 38 | end process; 39 | 40 | end Behavioral; -------------------------------------------------------------------------------- /sigma_delta_second.vhd: -------------------------------------------------------------------------------- 1 | library IEEE; 2 | use IEEE.STD_LOGIC_1164.ALL; 3 | use IEEE.NUMERIC_STD.ALL; 4 | 5 | entity sigma_delta_second is 6 | Port ( clk : in STD_LOGIC; 7 | i : in STD_LOGIC_VECTOR (15 downto 0); 8 | reset : in STD_LOGIC; 9 | ce : in STD_LOGIC; 10 | o : out STD_LOGIC); 11 | end sigma_delta_second; 12 | 13 | architecture Behavioral of sigma_delta_second is 14 | signal dac_out : std_logic := '0'; 15 | signal dac_accum1 : signed(19 downto 0) := (others => '0'); 16 | signal dac_accum2 : signed(19 downto 0) := (others => '0'); 17 | signal dac_feedback : signed(19 downto 0) := (others => '0'); 18 | begin 19 | o <= dac_out; 20 | with dac_out select dac_feedback <= to_signed(-32768,20) when '1', 21 | to_signed( 32768,20) when others; 22 | dac2_proc: process(clk) 23 | variable new_val1 : signed(19 downto 0); 24 | variable new_val2 : signed(19 downto 0); 25 | begin 26 | if rising_edge(clk) then 27 | if reset = '0' then 28 | dac_accum1 <= (others => '0'); 29 | dac_accum2 <= (others => '0'); 30 | dac_out <= '0'; 31 | elsif ce = '1' then 32 | new_val1 := dac_accum1 + signed(i) + dac_feedback; 33 | new_val2 := dac_accum2 + new_val1 + dac_feedback; 34 | if new_val2 < 0 then 35 | dac_out <= '0'; 36 | else 37 | dac_out <= '1'; 38 | end if; 39 | dac_accum1 <= new_val1; 40 | dac_accum2 <= new_val2; 41 | end if; 42 | end if; 43 | end process; 44 | 45 | 46 | end Behavioral; 47 | -------------------------------------------------------------------------------- /top_level.vhd: -------------------------------------------------------------------------------- 1 | library IEEE; 2 | use IEEE.STD_LOGIC_1164.ALL; 3 | use IEEE.NUMERIC_STD.ALL; 4 | 5 | entity top_level is 6 | Port ( clk : in STD_LOGIC; 7 | sw : in STD_LOGIC_VECTOR(3 downto 0); 8 | dac : out STD_LOGIC); 9 | end top_level; 10 | 11 | architecture Behavioral of top_level is 12 | component sigma_delta_first is 13 | Port ( clk : in STD_LOGIC; 14 | i : in STD_LOGIC_VECTOR (15 downto 0); 15 | reset : in STD_LOGIC; 16 | ce : in STD_LOGIC; 17 | o : out STD_LOGIC); 18 | end component; 19 | 20 | component sigma_delta_second is 21 | Port ( clk : in STD_LOGIC; 22 | i : in STD_LOGIC_VECTOR (15 downto 0); 23 | reset : in STD_LOGIC; 24 | ce : in STD_LOGIC; 25 | o : out STD_LOGIC); 26 | end component; 27 | 28 | component first_order_dac is 29 | port ( 30 | i_clk : in std_logic; 31 | i_res : in std_logic; 32 | i_ce : in STD_LOGIC; 33 | i_func : in std_logic_vector(15 downto 0); 34 | o_DAC : out std_logic); 35 | end component; 36 | 37 | component second_order_dac is 38 | port ( 39 | i_clk : in std_logic; 40 | i_res : in std_logic; 41 | i_ce : in STD_LOGIC; 42 | i_func : in std_logic_vector(15 downto 0); 43 | o_DAC : out std_logic); 44 | end component; 45 | 46 | signal sample_count : unsigned(5 downto 0) := (others => '0'); 47 | type a_samples is array (0 to 49) of signed(15 downto 0); 48 | signal samples : a_samples := ( 49 | to_signed(1029, 16), 50 | to_signed(3070, 16), 51 | to_signed(5063, 16), 52 | to_signed(6976, 16), 53 | to_signed(8779, 16), 54 | to_signed(10443, 16), 55 | to_signed(11943, 16), 56 | to_signed(13255, 16), 57 | to_signed(14357, 16), 58 | to_signed(15233, 16), 59 | to_signed(15869, 16), 60 | to_signed(16255, 16), 61 | to_signed(16384, 16), 62 | to_signed(16255, 16), 63 | to_signed(15869, 16), 64 | to_signed(15233, 16), 65 | to_signed(14357, 16), 66 | to_signed(13255, 16), 67 | to_signed(11943, 16), 68 | to_signed(10443, 16), 69 | to_signed(8779, 16), 70 | to_signed(6976, 16), 71 | to_signed(5063, 16), 72 | to_signed(3070, 16), 73 | to_signed(1029, 16), 74 | to_signed(-1029, 16), 75 | to_signed(-3070, 16), 76 | to_signed(-5063, 16), 77 | to_signed(-6976, 16), 78 | to_signed(-8779, 16), 79 | to_signed(-10444, 16), 80 | to_signed(-11944, 16), 81 | to_signed(-13255, 16), 82 | to_signed(-14358, 16), 83 | to_signed(-15234, 16), 84 | to_signed(-15869, 16), 85 | to_signed(-16255, 16), 86 | to_signed(-16384, 16), 87 | to_signed(-16255, 16), 88 | to_signed(-15869, 16), 89 | to_signed(-15234, 16), 90 | to_signed(-14358, 16), 91 | to_signed(-13255, 16), 92 | to_signed(-11944, 16), 93 | to_signed(-10444, 16), 94 | to_signed(-8779, 16), 95 | to_signed(-6976, 16), 96 | to_signed(-5063, 16), 97 | to_signed(-3070, 16), 98 | to_signed(-1029, 16)); 99 | 100 | -- to_signed( 1029,16), to_signed( 3070,16), to_signed( 5062,16), to_signed( 6975,16), to_signed( 8778,16), 101 | -- to_signed( 10443,16), to_signed( 11943,16), to_signed( 13254,16), to_signed( 14357,16), to_signed( 15233,16), 102 | -- to_signed( 15869,16), to_signed( 16254,16), to_signed( 16384,16), to_signed( 16254,16), to_signed( 15869,16), 103 | -- to_signed( 15233,16), to_signed( 14357,16), to_signed( 13254,16), to_signed( 11943,16), to_signed( 10443,16), 104 | -- to_signed( 8778,16), to_signed( 6975,16), to_signed( 5062,16), to_signed( 3070,16), to_signed( 1028,16), 105 | -- to_signed( -1029,16), to_signed( -3071,16), to_signed( -5063,16), to_signed( -6976,16), to_signed( -8779,16), 106 | -- to_signed(-10444,16), to_signed(-11944,16), to_signed(-13255,16), to_signed(-14358,16), to_signed(-15234,16), 107 | -- to_signed(-15870,16), to_signed(-16255,16), to_signed(-16384,16), to_signed(-16255,16), to_signed(-15870,16), 108 | -- to_signed(-15234,16), to_signed(-14358,16), to_signed(-13255,16), to_signed(-11944,16), to_signed(-10444,16), 109 | -- to_signed( -8779,16), to_signed( -6976,16), to_signed( -5063,16), to_signed( -3071,16), to_signed( -1029,16)); 110 | signal count : unsigned(9 downto 0) := (others => '0'); 111 | signal sample : signed(15 downto 0) := (others => '0'); 112 | 113 | signal update_enable : std_logic := '0'; 114 | signal mask : unsigned(2 downto 0); 115 | 116 | signal reset : std_logic := '0'; 117 | 118 | signal dac1_vhdl_out : std_logic := '0'; 119 | signal dac2_vhdl_out : std_logic := '0'; 120 | signal dac1_verilog_out : std_logic := '0'; 121 | signal dac2_verilog_out : std_logic := '0'; 122 | begin 123 | 124 | with sw(3 downto 2) select mask <= "000" when "00", 125 | "001" when "01", 126 | "011" when "11", 127 | "111" when others; 128 | process(clk) 129 | begin 130 | if rising_edge(clk) then 131 | case sw(1 downto 0) is 132 | when "00" => dac <= dac1_vhdl_out; 133 | when "01" => dac <= dac2_vhdl_out; 134 | when "10" => dac <= dac1_verilog_out; 135 | when others => dac <= dac2_verilog_out; 136 | end case; 137 | end if; 138 | end process; 139 | 140 | generate_clock_enable: process(clk) 141 | variable new_val : signed(19 downto 0); 142 | begin 143 | if rising_edge(clk) then 144 | if (count(2 downto 0) and mask) = "000" then 145 | update_enable <= '1'; 146 | else 147 | update_enable <= '0'; 148 | end if; 149 | reset <= '1'; 150 | end if; 151 | end process; 152 | 153 | gen_samples: process(clk) 154 | begin 155 | if rising_edge(clk) then 156 | sample <= samples(to_integer(sample_count)); 157 | if count = 999 then 158 | if sample_count = 49 then 159 | sample_count <= (others => '0'); 160 | else 161 | sample_count <= sample_count +1; 162 | end if; 163 | count <= (others => '0'); 164 | else 165 | count <= count + 1; 166 | end if; 167 | end if; 168 | end process; 169 | 170 | vhdl1: sigma_delta_first Port map 171 | ( clk => clk, 172 | reset => reset, 173 | ce => update_enable, 174 | i => std_logic_vector(sample), 175 | o => dac1_vhdl_out); 176 | 177 | vhdl2: sigma_delta_second Port map 178 | ( clk => clk, 179 | reset => reset, 180 | ce => update_enable, 181 | i => std_logic_vector(sample), 182 | o => dac2_vhdl_out); 183 | 184 | verilog1: first_order_dac port map ( 185 | i_clk => clk, 186 | i_res => reset, 187 | i_ce => update_enable, 188 | i_func => std_logic_vector(sample), 189 | o_DAC => dac1_verilog_out); 190 | 191 | verilog2: second_order_dac port map ( 192 | i_clk => clk, 193 | i_res => reset, 194 | i_ce => update_enable, 195 | i_func => std_logic_vector(sample), 196 | o_DAC => dac2_verilog_out); 197 | 198 | end Behavioral; 199 | --------------------------------------------------------------------------------