summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJon Nordby <jononor@gmail.com>2025-08-08 22:13:15 +0200
committerJon Nordby <jononor@gmail.com>2025-08-08 22:22:39 +0200
commit23ef09d5dd337049458b48eaf9ed7ece3f887592 (patch)
tree3df98fb87f969ceab2786c2bb31bbe3982db6abf
parent03feeb8419b46c858f56ac70142780f039881b45 (diff)
pdm: Try switch to 2 stage CIC
Still seeing odd spikes on output
-rw-r--r--bindings/galearn_pdm.cpp9
-rw-r--r--bindings/sim_cic3_pdm.cc5
-rw-r--r--bindings/test_galearn_pdm.py12
-rw-r--r--cocotb_try/cic3_pdm.v185
4 files changed, 120 insertions, 91 deletions
diff --git a/bindings/galearn_pdm.cpp b/bindings/galearn_pdm.cpp
index 6952a7c..13c3d4c 100644
--- a/bindings/galearn_pdm.cpp
+++ b/bindings/galearn_pdm.cpp
@@ -1,13 +1,14 @@
#include <pybind11/numpy.h>
#include <pybind11/pybind11.h>
-int pdm2pcm_cic3(const uint8_t *pdm, int pdm_length, int16_t *pcm,
- int pcm_length);
+int pdm2pcm_cic3(const uint8_t *pdm, int64_t pdm_length,
+ int16_t *pcm, int32_t pcm_length,
+ uint8_t hpf_alpha, uint8_t scale_shift);
namespace py = pybind11;
int
-process(py::array_t<uint8_t> arr1, py::array_t<int16_t> arr2)
+process(py::array_t<uint8_t> arr1, py::array_t<int16_t> arr2, int hpf_alpha, int scale_shift)
{
// Check shapes or sizes if needed
auto buf1 = arr1.request();
@@ -25,7 +26,7 @@ process(py::array_t<uint8_t> arr1, py::array_t<int16_t> arr2)
uint8_t *in = static_cast<uint8_t *>(buf1.ptr);
int16_t *out = static_cast<int16_t *>(buf2.ptr);
- int samples = pdm2pcm_cic3(in, arr1.size(), out, arr2.size());
+ int samples = pdm2pcm_cic3(in, arr1.size(), out, arr2.size(), hpf_alpha, scale_shift);
return samples;
}
diff --git a/bindings/sim_cic3_pdm.cc b/bindings/sim_cic3_pdm.cc
index cb612f8..982581f 100644
--- a/bindings/sim_cic3_pdm.cc
+++ b/bindings/sim_cic3_pdm.cc
@@ -6,7 +6,7 @@
#include "verilated.h"
int
-pdm2pcm_cic3(const uint8_t *pdm, int pdm_length, int16_t *pcm, int pcm_length)
+pdm2pcm_cic3(const uint8_t *pdm, int64_t pdm_length, int16_t *pcm, int32_t pcm_length, uint8_t hpf_alpha, uint8_t scale_shift)
{
// FIXME: verify that output buffer is large enough
@@ -15,7 +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;
+ top->hpf_alpha = hpf_alpha;
+ top->scale_shift = scale_shift;
// Start clock off
top->clk = 0;
diff --git a/bindings/test_galearn_pdm.py b/bindings/test_galearn_pdm.py
index 5256a46..3a19762 100644
--- a/bindings/test_galearn_pdm.py
+++ b/bindings/test_galearn_pdm.py
@@ -76,8 +76,8 @@ def test_sine_simple(frequency):
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?
+ n_samples = galearn_pdm.process(pdm_input, out, 255, 1)
+ out = out / (2**15)
# Compensate for delay through filter
delay = find_forward_shift(pcm_input, out)
@@ -126,19 +126,19 @@ 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
+ test_duration = 0.0013
# 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
+ ) + 0.10 # 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?
+ n_samples = galearn_pdm.process(pdm_input, out, 200, 1)
+ out = out / (2**15) # XXX: where does this magical come from?
# Compensate for delay through filter
delay = find_forward_shift(pcm_input, out)
diff --git a/cocotb_try/cic3_pdm.v b/cocotb_try/cic3_pdm.v
index fc5de46..4e8d664 100644
--- a/cocotb_try/cic3_pdm.v
+++ b/cocotb_try/cic3_pdm.v
@@ -1,100 +1,127 @@
-
-
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
-
+ 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)
+ input wire [2:0] scale_shift, // Right shift amount (0-7 bits)
+ output reg signed [15:0] pcm_out, // Decimated PCM output
+ output reg pcm_valid // Output valid pulse
);
- //parameter DECIMATION = 64; // Decimation factor
- parameter OUTPUT_SHIFT = 10; // Can tune this
-
- // Internal registers
- reg signed [38:0] integrator_0 = 0;
- reg signed [38:0] integrator_1 = 0;
- reg signed [38:0] integrator_2 = 0;
-
- reg [5:0] decim_counter = 0;
- reg signed [38:0] comb_0 = 0, comb_1 = 0;
-
- /* verilator lint_off UNUSEDSIGNAL */
- reg signed [38:0] comb_2 = 0;
+ // CIC filter parameters
+ localparam DECIMATION = 64;
+ localparam CIC_WIDTH = 17; // 17 bits for safe margin against overflow
+
+ // CIC integrator and comb stages
+ reg signed [CIC_WIDTH-1:0] integrator1, integrator2;
+ reg signed [CIC_WIDTH-1:0] comb1, comb2;
+ reg signed [CIC_WIDTH-1:0] comb1_d1, comb2_d1;
+
+ // Decimation counter
+ reg [6:0] decim_count; // 7 bits to match DECIMATION width
+
+ // CIC output and scaling
+ reg signed [CIC_WIDTH-1:0] cic_out;
+ reg signed [15:0] scaled_out;
+ reg cic_valid;
+
// HPF registers
- reg signed [15:0] hpf_prev = 0; // Previous input sample
- reg signed [15:0] cic_reg = 0; // Registered CIC output
- reg cic_valid = 0;
-
- // CIC output
- wire signed [15:0] cic_out = comb_2[OUTPUT_SHIFT + 15 : OUTPUT_SHIFT];
-
- reg signed [38:0] delay_0 = 0, delay_1 = 0, delay_2 = 0;
-
- reg signed [15:0] pcm_out_r = 0;
- reg pcm_valid_r = 0;
-
- // Integrator stage (runs every clk)
+ reg signed [15:0] hpf_x_prev;
+ reg signed [15:0] hpf_y_prev;
+
+ // HPF calculation wires
+ /* verilator lint_off UNUSEDSIGNAL */
+ wire signed [23:0] hpf_temp;
+ /* verilator lint_on UNUSEDSIGNAL */
+ wire signed [15:0] hpf_calc;
+
+ // Convert 1-bit PDM to signed
+ wire signed [CIC_WIDTH-1:0] pdm_signed = pdm_in ? 17'sd1 : -17'sd1;
+
+ // CIC Integrator stages (run at PDM rate)
always @(posedge clk) begin
if (rst) begin
- integrator_0 <= 0;
- integrator_1 <= 0;
- integrator_2 <= 0;
+ integrator1 <= 0;
+ integrator2 <= 0;
end else begin
- integrator_0 <= integrator_0 + (pdm_in ? 1 : -1);
- integrator_1 <= integrator_1 + integrator_0;
- integrator_2 <= integrator_2 + integrator_1;
+ integrator1 <= integrator1 + pdm_signed;
+ integrator2 <= integrator2 + integrator1;
end
end
-
- // Decimation counter
- always @(posedge clk) begin
- if (rst)
- decim_counter <= 0;
- else
- decim_counter <= decim_counter + 1;
- end
-
- // Comb stage
+
+ // Decimation counter and CIC comb stages
always @(posedge clk) begin
- cic_valid <= 0;
if (rst) begin
- cic_reg <= 0;
- end else if (decim_counter == 63) begin
- comb_0 <= integrator_2 - delay_0;
- delay_0 <= integrator_2;
- comb_1 <= comb_0 - delay_1;
- delay_1 <= comb_0;
- comb_2 <= comb_1 - delay_2;
- delay_2 <= comb_1;
- end else if (decim_counter == 0) begin
- // CIC output is now stable, register it
- cic_reg <= cic_out;
- cic_valid <= 1;
+ decim_count <= 0;
+ comb1 <= 0;
+ comb2 <= 0;
+ comb1_d1 <= 0;
+ comb2_d1 <= 0;
+ cic_out <= 0;
+ cic_valid <= 0;
+ end else begin
+ cic_valid <= 0;
+
+ if (decim_count == DECIMATION - 1) begin
+ decim_count <= 0;
+
+ // Comb stage 1
+ comb1 <= integrator2 - comb1_d1;
+ comb1_d1 <= integrator2;
+
+ // Comb stage 2
+ comb2 <= comb1 - comb2_d1;
+ comb2_d1 <= comb1;
+
+ cic_out <= comb2;
+ cic_valid <= 1;
+ end else begin
+ decim_count <= decim_count + 1;
+ end
end
end
- // HPF stage (runs after CIC settles)
+ // Output scaling
+ always @(*) begin
+ case (scale_shift)
+ 3'd0: scaled_out = cic_out[15:0];
+ 3'd1: scaled_out = cic_out[16:1];
+ 3'd2: scaled_out = {{1{cic_out[16]}}, cic_out[16:2]}; // Sign extend from bit 16
+ 3'd3: scaled_out = {{2{cic_out[16]}}, cic_out[16:3]}; // Sign extend from bit 16
+ 3'd4: scaled_out = {{3{cic_out[16]}}, cic_out[16:4]}; // Sign extend from bit 16
+ 3'd5: scaled_out = {{4{cic_out[16]}}, cic_out[16:5]}; // Sign extend from bit 16
+ 3'd6: scaled_out = {{5{cic_out[16]}}, cic_out[16:6]}; // Sign extend from bit 16
+ 3'd7: scaled_out = {{6{cic_out[16]}}, cic_out[16:7]}; // Sign extend from bit 16
+ endcase
+ end
+
+ // HPF calculation
+ assign hpf_temp = ((hpf_alpha * hpf_y_prev) + (({8'd0, scaled_out} - {8'd0, hpf_x_prev}) << 8)) >>> 8;
+ assign hpf_calc = hpf_temp[15:0];
+
+ // High-pass filter (1st order IIR: y[n] = alpha/256 * y[n-1] + (x[n] - x[n-1]))
always @(posedge clk) begin
- pcm_valid_r <= 0;
if (rst) begin
- hpf_prev <= 0;
- pcm_out_r <= 0;
- end else if (cic_valid) begin
- if (hpf_alpha == 8'd255) begin
- pcm_out_r <= cic_reg;
- end else begin
- // HPF: y[n] = x[n] - x[n-1] + alpha*y[n-1]
- pcm_out_r <= cic_reg - hpf_prev + ((pcm_out_r * hpf_alpha) >>> 8);
+ hpf_x_prev <= 0;
+ hpf_y_prev <= 0;
+ pcm_out <= 0;
+ pcm_valid <= 0;
+ end else begin
+ pcm_valid <= 0;
+
+ if (cic_valid) begin
+ if (hpf_alpha == 8'd255) begin
+ // Bypass HPF completely
+ pcm_out <= scaled_out;
+ end else begin
+ // HPF computation: y[n] = alpha/256 * y[n-1] + (x[n] - x[n-1])
+ pcm_out <= hpf_calc;
+ hpf_x_prev <= scaled_out;
+ hpf_y_prev <= hpf_calc;
+ end
+ pcm_valid <= 1;
end
- hpf_prev <= cic_reg;
- pcm_valid_r <= 1;
end
end
- assign pcm_out = pcm_out_r;
- assign pcm_valid = pcm_valid_r;
-
endmodule