diff options
-rw-r--r-- | bindings/galearn_pdm.cpp | 9 | ||||
-rw-r--r-- | bindings/sim_cic3_pdm.cc | 5 | ||||
-rw-r--r-- | bindings/test_galearn_pdm.py | 12 | ||||
-rw-r--r-- | cocotb_try/cic3_pdm.v | 185 |
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 |