summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJon Nordby <jononor@gmail.com>2025-08-08 21:26:58 +0200
committerJon Nordby <jononor@gmail.com>2025-08-08 21:27:16 +0200
commite440fc873593821254b6b2741a5daec6bccdef47 (patch)
treeb4040f8bcfeb68be6193c71b2c729eb4039eb383
parent3a0cec889f67dbe4820e29931689555ea702726a (diff)
pdm: Try add high pass filter for DC blocking/removal
Does not work right...
-rw-r--r--bindings/Makefile1
-rw-r--r--bindings/sim_cic3_pdm.cc2
-rw-r--r--bindings/test_galearn_pdm.py61
-rw-r--r--cocotb_try/cic3_pdm.v28
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