/* * Copyright(C) 2024 Matthias P. Braendli * Copyright(C) 2018 Gerald Coe, Devantech Ltd * * Permission to use, copy, modify, and/or distribute this software for any purpose with or * without fee is hereby granted, provided that the above copyright notice and * this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ // This is following the excellent explanation on // https://tomverbeure.github.io/2020/09/30/Moving-Average-and-CIC-Filters.html module mictoled #( parameter integer INTEGRATOR_WIDTH=16, parameter integer COMB_WIDTH=12) ( input clk, // 12MHz input btn, output mic_clk, input mic_dat, output led1, output led2, output led3, output led4, output led5, output led6, output led7, output led8, output lcol1, output lcol2, output lcol3, output lcol4 ); /* PDM mic clock gen */ reg [31:0] clkdiv = 8'b0; always @ (posedge clk) begin clkdiv <= (clkdiv + 1) % 24; end wire mic_strobe = clkdiv == 12; assign mic_clk = clkdiv >= 12; /* CIC decimator by 64 => 2MHz -> 31.25 kHz */ reg signed [INTEGRATOR_WIDTH-1:0] integrator = 0; reg [5:0] decim_strobe_counter = 8'b0; always @ (posedge clk) begin if (mic_strobe) begin if (mic_dat == 0) integrator <= integrator + 1; else integrator <= integrator - 1; decim_strobe_counter <= (decim_strobe_counter + 1) % 64; end end /* Comb stage after decimation */ wire decim_strobe = decim_strobe_counter == 0 && mic_strobe; reg signed [INTEGRATOR_WIDTH-1:0] previous_integrator = 0; reg signed [COMB_WIDTH-1:0] comb_out = 0; // can be reduced_width always @ (posedge clk) begin if (decim_strobe) begin comb_out = integrator - previous_integrator; previous_integrator <= integrator; end end /* Goertzel Algorithm to detect presence of frequencies */ // coef = 2 * cos(2 * PI * freq / sample_rate); // 440 Hz -> 1.992178649 // 1200 Hz -> 1.942068556 // Represent in fixed point in Q.8, i.e. a scaling factor of 256 reg signed [31:0] coef_440 = 510; // 439.75 Hz reg signed [31:0] q1_440 = 0; reg signed [31:0] q2_440 = 0; real coef_440_f = 1.992178649; real q1_440_f = 0; real q2_440_f = 0; reg [7:0] goertzel_strobe_counter = 0; real m_squared_440_f = 0; reg signed [63:0] m_squared_440 = 0; wire signed [31:0] comb_wide = comb_out << 8; always @ (posedge clk) begin if (decim_strobe) begin goertzel_strobe_counter <= goertzel_strobe_counter + 1; // overflow at 256 if (goertzel_strobe_counter == 0) begin m_squared_440_f <= q1_440_f * q1_440_f + q2_440_f * q2_440_f - coef_440_f * q1_440_f * q2_440_f; q1_440_f <= 0; q2_440_f <= 0; m_squared_440 <= ((q1_440 * q1_440) >>> 8) + ((q2_440 * q2_440) >>> 8) - ((coef_440 * ((q1_440 * q2_440) >>> 8)) >>> 8); q1_440 <= 0; q2_440 <= 0; end else begin q1_440_f <= coef_440_f * q1_440_f - q2_440_f + $itor(comb_out); q2_440_f <= q1_440_f; q1_440 <= ((coef_440 * q1_440) >>> 8) - q2_440 + comb_wide; q2_440 <= q1_440; end end end /* LED drivers - counter is inverted for display because leds are active low */ //assign {led8, led7, led6, led5, led4, led3, led2, led1} = m_squared_440[14:6] ^ 8'hff; assign {lcol4, lcol3, lcol2, lcol1} = 4'b1110; endmodule