diff options
author | Jon Nordby <jononor@gmail.com> | 2025-08-08 21:26:58 +0200 |
---|---|---|
committer | Jon Nordby <jononor@gmail.com> | 2025-08-08 21:27:16 +0200 |
commit | e440fc873593821254b6b2741a5daec6bccdef47 (patch) | |
tree | b4040f8bcfeb68be6193c71b2c729eb4039eb383 | |
parent | 3a0cec889f67dbe4820e29931689555ea702726a (diff) |
pdm: Try add high pass filter for DC blocking/removal
Does not work right...
-rw-r--r-- | bindings/Makefile | 1 | ||||
-rw-r--r-- | bindings/sim_cic3_pdm.cc | 2 | ||||
-rw-r--r-- | bindings/test_galearn_pdm.py | 61 | ||||
-rw-r--r-- | cocotb_try/cic3_pdm.v | 28 |
4 files changed, 87 insertions, 5 deletions
diff --git a/bindings/Makefile b/bindings/Makefile index 5a80cac..40cfdeb 100644 --- a/bindings/Makefile +++ b/bindings/Makefile @@ -18,5 +18,6 @@ check: ${PY_MODULE} PYTHONPATH=../tools python -m pytest -v test_galearn_pdm.py -s ${PY_MODULE}: ${SHARED_LIB} + rm -rf build/ # force rebuild, setup.py does not discover changes to .h files python setup.py build_ext --inplace diff --git a/bindings/sim_cic3_pdm.cc b/bindings/sim_cic3_pdm.cc index 0368217..cb612f8 100644 --- a/bindings/sim_cic3_pdm.cc +++ b/bindings/sim_cic3_pdm.cc @@ -15,6 +15,8 @@ pdm2pcm_cic3(const uint8_t *pdm, int pdm_length, int16_t *pcm, int pcm_length) Vcic3_pdm *top = new Vcic3_pdm{cp}; + top->hpf_alpha = 240; + // Start clock off top->clk = 0; diff --git a/bindings/test_galearn_pdm.py b/bindings/test_galearn_pdm.py index 68ed1c3..5256a46 100644 --- a/bindings/test_galearn_pdm.py +++ b/bindings/test_galearn_pdm.py @@ -121,3 +121,64 @@ def test_sine_simple(frequency): average = numpy.mean(out) assert abs(average) < 0.06 + +def test_dc(): + function = sys._getframe().f_code.co_name # looks up function name + test_name = f'{function}' + sr = SAMPLERATE_DEFAULT + test_duration = 0.10 + + # Generate test data + frequency = 1000 + pcm_input = generate_test_tone(duration_sec=test_duration, + freqs=[frequency], noise_level=0.0, sample_rate=sr, amplitude=0.01, + ) + 0.20 # DC + pdm_input = convert(pcm_input) + out = numpy.zeros(shape=len(pdm_input)//DECIMATION, dtype=numpy.int16) + + # Process using filter + n_samples = galearn_pdm.process(pdm_input, out) + out = out / 1024 # XXX: where does this magical come from? + + # Compensate for delay through filter + delay = find_forward_shift(pcm_input, out) + out_shifted = out[delay:-1] + input_trimmed = pcm_input[:len(out_shifted)] + #out_shifted = out_shifted[5:-5] + #input_trimmed = input_trimmed[5:-5] + error = out_shifted - input_trimmed + + # Plot diagnostics, if enabled + if enable_plotting: + plot_path = os.path.join(out_dir, test_name, 'reconstructed.png') + ensure_dir_for_file(plot_path) + fig = plot_reconstruct(pcm_input, pdm_input, out, sr=sr, + aspect=6.0, pcm_marker='o') + fig.savefig(plot_path) + print('Wrote', plot_path) + + plot_path = os.path.join(out_dir, test_name, 'shifted.png') + ensure_dir_for_file(plot_path) + fig = plot_reconstruct(input_trimmed, pdm_input, error, sr=sr, + aspect=6.0, pcm_marker='o') + fig.savefig(plot_path) + print('Wrote', plot_path) + + # Do checks + n_stages = 3 + expect_delay = n_stages + 1 # XXX: might not be fully correct + assert delay == expect_delay + delay = expect_delay + + # Check that waveform is quite similar + # NOTE: at high frequencies there is an expected reduction in gain/amplitude + # if one would compensate for that, could probably tighten these limits + mse = numpy.mean(error**2) + mae = numpy.mean(numpy.abs(error)) + assert mse < 0.10 + assert mae < 0.06 + + # Check there is no DC + average = numpy.mean(out) + assert abs(average) < 0.06 + diff --git a/cocotb_try/cic3_pdm.v b/cocotb_try/cic3_pdm.v index 9c5f181..e641ba1 100644 --- a/cocotb_try/cic3_pdm.v +++ b/cocotb_try/cic3_pdm.v @@ -1,10 +1,10 @@ -// XXX: A chatbot wrote this, might be complete crack module cic3_pdm ( input wire clk, // PDM clock input wire rst, // active-high synchronous reset input wire pdm_in, // 1-bit PDM data input + input wire [7:0] hpf_alpha, // HPF coefficient (0-255, 255=bypass) output wire signed [15:0] pcm_out, // Decimated PCM output output wire pcm_valid @@ -23,6 +23,12 @@ module cic3_pdm ( /* verilator lint_off UNUSEDSIGNAL */ reg signed [31:0] comb_2 = 0; + // HPF registers + reg signed [15:0] hpf_prev = 0; // Previous input sample + + // CIC output + wire signed [15:0] cic_out = comb_2[OUTPUT_SHIFT + 15 : OUTPUT_SHIFT]; + reg signed [31:0] delay_0 = 0, delay_1 = 0, delay_2 = 0; reg signed [15:0] pcm_out_r = 0; @@ -49,10 +55,15 @@ module cic3_pdm ( decim_counter <= decim_counter + 1; end - // Comb stage (runs every DECIMATION clocks) + // Comb + HPF stage (runs every DECIMATION clocks) always @(posedge clk) begin pcm_valid_r <= 0; - if (decim_counter == 63) begin + + if (rst) begin + hpf_prev <= 0; + pcm_out_r <= 0; + end else if (decim_counter == 63) begin + comb_0 <= integrator_2 - delay_0; delay_0 <= integrator_2; @@ -62,8 +73,15 @@ module cic3_pdm ( comb_2 <= comb_1 - delay_2; delay_2 <= comb_1; - // Bit-shift down to get 16-bit output (tune shift based on DECIMATION and stage count) - pcm_out_r <= comb_2[OUTPUT_SHIFT + 15 : OUTPUT_SHIFT]; + // Simple DC blocking HPF: y[n] = x[n] - x[n-1] + alpha*y[n-1] + if (hpf_alpha == 8'd255) begin + pcm_out_r <= cic_out; + end else begin + // HPF equation rearranged for better stability + pcm_out_r <= cic_out - hpf_prev + ((pcm_out_r * hpf_alpha) >>> 8); + end + hpf_prev <= cic_out; + pcm_valid_r <= 1; end end |