Compare commits
2 Commits
771fa58769
...
49b8a77480
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
49b8a77480 | ||
|
|
165faefa59 |
@@ -22,8 +22,10 @@ files_verilog = rtl/toplevel/top_generic.v
|
|||||||
rtl/core/nco_q15.v
|
rtl/core/nco_q15.v
|
||||||
rtl/core/sigmadelta_sampler.v
|
rtl/core/sigmadelta_sampler.v
|
||||||
rtl/core/sigmadelta_rcmodel_q15.v
|
rtl/core/sigmadelta_rcmodel_q15.v
|
||||||
|
rtl/core/sigmadelta_input_q15.v
|
||||||
rtl/core/mul_const.v
|
rtl/core/mul_const.v
|
||||||
rtl/core/lpf_iir_q15_k.v
|
rtl/core/lpf_iir_q15_k.v
|
||||||
|
rtl/core/decimate_by_r_q15.v
|
||||||
rtl/arch/spartan-6/lvds_comparator.v
|
rtl/arch/spartan-6/lvds_comparator.v
|
||||||
rtl/arch/spartan-6/clk_gen.v
|
rtl/arch/spartan-6/clk_gen.v
|
||||||
files_con = boards/mimas_v1/constraints.ucf
|
files_con = boards/mimas_v1/constraints.ucf
|
||||||
@@ -50,8 +52,10 @@ files_verilog = sim/tb/tb_nco_q15.v
|
|||||||
rtl/core/nco_q15.v
|
rtl/core/nco_q15.v
|
||||||
rtl/core/lvds_comparator.v
|
rtl/core/lvds_comparator.v
|
||||||
rtl/core/sigmadelta_rcmodel_q15.v
|
rtl/core/sigmadelta_rcmodel_q15.v
|
||||||
|
rtl/core/sigmadelta_input_q15.v
|
||||||
rtl/core/mul_const.v
|
rtl/core/mul_const.v
|
||||||
rtl/core/lpf_iir_q15_k.v
|
rtl/core/lpf_iir_q15_k.v
|
||||||
|
rtl/core/decimate_by_r_q15.v
|
||||||
sim/overrides/sigmadelta_sampler.v
|
sim/overrides/sigmadelta_sampler.v
|
||||||
sim/overrides/clk_gen.v
|
sim/overrides/clk_gen.v
|
||||||
files_other = rtl/util/conv.vh
|
files_other = rtl/util/conv.vh
|
||||||
|
|||||||
74
rtl/core/decimate_by_r_q15.v
Normal file
74
rtl/core/decimate_by_r_q15.v
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
`timescale 1ns/1ps
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Decimator by R
|
||||||
|
// Reduces the effective sample rate by an integer factor R by selecting every
|
||||||
|
// R-th input sample. Generates a one-cycle 'out_valid' pulse each time a new
|
||||||
|
// decimated sample is produced.
|
||||||
|
//
|
||||||
|
// Implements:
|
||||||
|
// For each valid input sample:
|
||||||
|
// if (count == R-1):
|
||||||
|
// count <= 0
|
||||||
|
// out_q15 <= in_q15
|
||||||
|
// out_valid <= 1
|
||||||
|
// else:
|
||||||
|
// count <= count + 1
|
||||||
|
//
|
||||||
|
// parameters:
|
||||||
|
// -- R : integer decimation factor (e.g., 400)
|
||||||
|
// output sample rate = input rate / R
|
||||||
|
// -- CNT_W : counter bit width, must satisfy 2^CNT_W > R
|
||||||
|
//
|
||||||
|
// inout:
|
||||||
|
// -- clk : input clock (same rate as 'in_valid')
|
||||||
|
// -- rst_n : active-low synchronous reset
|
||||||
|
// -- in_valid : input data strobe; assert 1'b1 if input is always valid
|
||||||
|
// -- in_q15 : signed 16-bit Q1.15 input sample (full-rate)
|
||||||
|
// -- out_valid : single-cycle pulse every R samples (decimated rate strobe)
|
||||||
|
// -- out_q15 : signed 16-bit Q1.15 output sample (decimated stream)
|
||||||
|
//
|
||||||
|
// Notes:
|
||||||
|
// - This module performs *pure downsampling* (sample selection only).
|
||||||
|
// It does not include any anti-alias filtering; high-frequency content
|
||||||
|
// above the new Nyquist limit (Fs_out / 2) will alias into the baseband.
|
||||||
|
// - For most applications, an anti-alias low-pass filter such as
|
||||||
|
// lpf_iir_q15 or a FIR stage should precede this decimator.
|
||||||
|
// - The output sample rate is given by:
|
||||||
|
// Fs_out = Fs_in / R
|
||||||
|
// - Typical usage: interface between high-rate sigma-delta or oversampled
|
||||||
|
// data streams and lower-rate processing stages.
|
||||||
|
// =============================================================================
|
||||||
|
module decimate_by_r_q15 #(
|
||||||
|
parameter integer R = 400, // decimation factor
|
||||||
|
parameter integer CNT_W = 10 // width so that 2^CNT_W > R (e.g., 10 for 750)
|
||||||
|
)(
|
||||||
|
input wire clk,
|
||||||
|
input wire rst_n,
|
||||||
|
input wire in_valid, // assert 1'b1 if always valid
|
||||||
|
input wire signed [15:0] in_q15, // Q1.15 sample at full rate
|
||||||
|
output reg out_valid, // 1-cycle pulse every R samples
|
||||||
|
output reg signed [15:0] out_q15 // Q1.15 sample at decimated rate
|
||||||
|
);
|
||||||
|
reg [CNT_W-1:0] cnt;
|
||||||
|
|
||||||
|
always @(posedge clk or negedge rst_n) begin
|
||||||
|
if (!rst_n) begin
|
||||||
|
cnt <= {CNT_W{1'b0}};
|
||||||
|
out_valid <= 1'b0;
|
||||||
|
out_q15 <= 16'sd0;
|
||||||
|
end else begin
|
||||||
|
out_valid <= 1'b0;
|
||||||
|
|
||||||
|
if (in_valid) begin
|
||||||
|
if (cnt == R-1) begin
|
||||||
|
cnt <= {CNT_W{1'b0}};
|
||||||
|
out_q15 <= in_q15;
|
||||||
|
out_valid <= 1'b1;
|
||||||
|
end else begin
|
||||||
|
cnt <= cnt + 1'b1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
58
rtl/core/sigmadelta_input_q15.v
Normal file
58
rtl/core/sigmadelta_input_q15.v
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
`timescale 1ns/1ps
|
||||||
|
|
||||||
|
module sigmadelta_input #(
|
||||||
|
parameter integer R_OHM = 3300,
|
||||||
|
parameter integer C_PF = 220
|
||||||
|
)(
|
||||||
|
input wire clk_15,
|
||||||
|
input wire resetn,
|
||||||
|
|
||||||
|
input wire adc_a,
|
||||||
|
input wire adc_b,
|
||||||
|
output wire adc_o,
|
||||||
|
|
||||||
|
output wire signed [15:0] signal_q15,
|
||||||
|
output wire signal_valid
|
||||||
|
);
|
||||||
|
`include "rc_alpha_q15.vh"
|
||||||
|
|
||||||
|
wire sd_signal;
|
||||||
|
wire signed [15:0] raw_sample_q15;
|
||||||
|
wire signed [15:0] lpf_sample_q15;
|
||||||
|
|
||||||
|
sigmadelta_sampler sd_sampler(
|
||||||
|
.clk(clk_15),
|
||||||
|
.a(adc_a), .b(adc_b),
|
||||||
|
.o(sd_signal)
|
||||||
|
);
|
||||||
|
assign adc_o = sd_signal;
|
||||||
|
|
||||||
|
localparam integer alpha_q15_int = alpha_q15_from_rc(R_OHM, C_PF, 15000000);
|
||||||
|
localparam signed [15:0] alpha_q15 = alpha_q15_int[15:0];
|
||||||
|
localparam signed [15:0] alpha_q15_top = alpha_q15 & 16'hff00;
|
||||||
|
sigmadelta_rcmodel_q15 #(
|
||||||
|
.alpha_q15(alpha_q15_top)
|
||||||
|
) rc_model (
|
||||||
|
.clk(clk_15), .resetn(resetn),
|
||||||
|
.sd_sample(sd_signal),
|
||||||
|
.sample_q15(raw_sample_q15)
|
||||||
|
);
|
||||||
|
|
||||||
|
lpf_iir_q15_k #(
|
||||||
|
.K(10)
|
||||||
|
) lpf (
|
||||||
|
.clk(clk_15), .rst_n(resetn),
|
||||||
|
.x_q15(raw_sample_q15),
|
||||||
|
.y_q15(lpf_sample_q15)
|
||||||
|
);
|
||||||
|
|
||||||
|
decimate_by_r_q15 #(
|
||||||
|
.R(375), // 15MHz/375 = 40KHz
|
||||||
|
.CNT_W(10)
|
||||||
|
) decimate (
|
||||||
|
.clk(clk_15), .rst_n(resetn),
|
||||||
|
.in_valid(1'b1), .in_q15(lpf_sample_q15),
|
||||||
|
.out_valid(signal_valid), .out_q15(signal_q15)
|
||||||
|
);
|
||||||
|
|
||||||
|
endmodule
|
||||||
@@ -1,3 +1,6 @@
|
|||||||
|
`ifndef CONV_VH
|
||||||
|
`define CONV_VH
|
||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
// Convert Q1.15 to a biased UQ0.16 signal
|
// Convert Q1.15 to a biased UQ0.16 signal
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
@@ -9,3 +12,5 @@ begin
|
|||||||
q15_to_uq16 = biased[15:0];
|
q15_to_uq16 = biased[15:0];
|
||||||
end
|
end
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
`endif
|
||||||
@@ -7,58 +7,79 @@
|
|||||||
`define RC_ALPHA_Q15_VH
|
`define RC_ALPHA_Q15_VH
|
||||||
|
|
||||||
function integer alpha_q15_from_rc;
|
function integer alpha_q15_from_rc;
|
||||||
input integer R_OHM; // resistance in ohms
|
input integer R_OHM; // ohms
|
||||||
input integer C_PF; // capacitance in picofarads
|
input integer C_PF; // picofarads
|
||||||
input integer FS_HZ; // sampling frequency in Hz
|
input integer FS_HZ; // Hz
|
||||||
|
|
||||||
integer N; // fractional bits for x (QN)
|
|
||||||
reg [127:0] num_1e12_sllN;
|
|
||||||
reg [127:0] denom_u;
|
|
||||||
reg [127:0] x_qN; // x in QN
|
|
||||||
reg [255:0] x2; // x^2 in Q(2N)
|
|
||||||
reg [383:0] x3; // x^3 in Q(3N)
|
|
||||||
|
|
||||||
integer term1_q15; // x -> Q1.15
|
|
||||||
integer term2_q15; // x^2/2 -> Q1.15
|
|
||||||
integer term3_q15; // x^3/6 -> Q1.15
|
|
||||||
integer acc; // accumulator for result
|
|
||||||
begin
|
|
||||||
// Choose QN for x. N=24 is a good balance for accuracy/width.
|
// Choose QN for x. N=24 is a good balance for accuracy/width.
|
||||||
N = 24;
|
integer N;
|
||||||
|
|
||||||
// x = 1 / (Fs * R * C) with C in pF ==> x = 1e12 / (Fs * R * C_PF)
|
// We'll keep everything as unsigned vectors; inputs copied into vectors first.
|
||||||
|
reg [63:0] R_u, C_u, FS_u;
|
||||||
|
|
||||||
|
// x = 1 / (Fs * R * C) with C in pF -> x = 1e12 / (Fs*R*C_pf)
|
||||||
// x_qN = round( x * 2^N ) = round( (1e12 << N) / denom )
|
// x_qN = round( x * 2^N ) = round( (1e12 << N) / denom )
|
||||||
num_1e12_sllN = 128'd1000000000000 << N;
|
reg [127:0] NUM_1E12_SLLN; // big enough for 1e12 << N
|
||||||
|
reg [127:0] DENOM; // Fs*R*C
|
||||||
// denom = Fs * R * C_PF (fits in 64..96 bits for typical values)
|
reg [127:0] X_qN; // x in QN
|
||||||
denom_u = 0;
|
|
||||||
denom_u = denom_u + FS_HZ[127:0];
|
|
||||||
denom_u = denom_u * R_OHM[127:0];
|
|
||||||
denom_u = denom_u * C_PF[127:0];
|
|
||||||
|
|
||||||
// rounded divide for x_qN
|
|
||||||
x_qN = (num_1e12_sllN + (denom_u >> 1)) / denom_u;
|
|
||||||
|
|
||||||
// Powers
|
// Powers
|
||||||
x2 = x_qN * x_qN; // 128x128 -> 256
|
reg [255:0] X2; // x^2 in Q(2N)
|
||||||
x3 = x2 * x_qN; // 256x128 -> 384
|
reg [383:0] X3; // x^3 in Q(3N)
|
||||||
|
|
||||||
|
integer term1_q15;
|
||||||
|
integer term2_q15;
|
||||||
|
integer term3_q15;
|
||||||
|
integer acc;
|
||||||
|
|
||||||
|
begin
|
||||||
|
N = 24;
|
||||||
|
|
||||||
|
// Copy integer inputs into 64-bit vectors (no bit-slicing of integers)
|
||||||
|
R_u = R_OHM[31:0];
|
||||||
|
C_u = C_PF[31:0];
|
||||||
|
FS_u = FS_HZ[31:0];
|
||||||
|
|
||||||
|
// Denominator = Fs * R * C_pf (fits in < 2^64 for typical values)
|
||||||
|
DENOM = 128'd0;
|
||||||
|
DENOM = FS_u;
|
||||||
|
DENOM = DENOM * R_u;
|
||||||
|
DENOM = DENOM * C_u;
|
||||||
|
|
||||||
|
// // Guard: avoid divide by zero
|
||||||
|
// if (DENOM == 0) begin
|
||||||
|
// alpha_q15_from_rc = 0;
|
||||||
|
// disable alpha_q15_from_rc;
|
||||||
|
// end
|
||||||
|
|
||||||
|
// Numerator = (1e12 << N). 1e12 * 2^24 ≈ 1.6777e19 (fits in 2^64..2^65),
|
||||||
|
// so use 128 bits to be safe.
|
||||||
|
NUM_1E12_SLLN = 128'd1000000000000 << N;
|
||||||
|
|
||||||
|
// x_qN = rounded division
|
||||||
|
X_qN = (NUM_1E12_SLLN + (DENOM >> 1)) / DENOM;
|
||||||
|
|
||||||
|
// Powers
|
||||||
|
X2 = X_qN * X_qN;
|
||||||
|
X3 = X2 * X_qN;
|
||||||
|
|
||||||
|
// Convert terms to Q1.15:
|
||||||
// term1 = x -> shift from QN to Q15
|
// term1 = x -> shift from QN to Q15
|
||||||
term1_q15 = (x_qN >> (N - 15)) & 16'hFFFF;
|
term1_q15 = (X_qN >> (N - 15)) & 16'hFFFF;
|
||||||
|
|
||||||
// term2 = x^2 / 2 -> shift from Q(2N) to Q15 and divide by 2
|
// term2 = x^2 / 2 -> Q(2N) to Q15 and /2
|
||||||
term2_q15 = (x2 >> (2*N - 15 + 1)) & 16'hFFFF;
|
term2_q15 = (X2 >> (2*N - 15 + 1)) & 16'hFFFF;
|
||||||
|
|
||||||
// term3 = x^3 / 6 -> shift from Q(3N) to Q15, then divide by 6 (rounded)
|
// term3 = x^3 / 6 -> Q(3N) to Q15, then /6 with rounding
|
||||||
begin : gen_term3
|
begin : gen_t3
|
||||||
reg [383:0] tmp_q15_wide;
|
reg [383:0] tmp_q15_wide;
|
||||||
reg [383:0] tmp_div6;
|
reg [383:0] tmp_div6;
|
||||||
tmp_q15_wide = (x3 >> (3*N - 15));
|
tmp_q15_wide = (X3 >> (3*N - 15));
|
||||||
tmp_div6 = (tmp_q15_wide + 6'd3) / 6; // +3 for rounding
|
tmp_div6 = (tmp_q15_wide + 6'd3) / 6;
|
||||||
term3_q15 = tmp_div6[15:0];
|
term3_q15 = tmp_div6[15:0];
|
||||||
end
|
end
|
||||||
|
|
||||||
// Combine: alpha_q15 = x - x^2/2 + x^3/6 ; clamp to [0, 0x7FFF]
|
// Combine and clamp
|
||||||
acc = term1_q15 - term2_q15 + term3_q15;
|
acc = term1_q15 - term2_q15 + term3_q15;
|
||||||
if (acc < 0) acc = 0;
|
if (acc < 0) acc = 0;
|
||||||
else if (acc > 16'h7FFF) acc = 16'h7FFF;
|
else if (acc > 16'h7FFF) acc = 16'h7FFF;
|
||||||
|
|||||||
@@ -13,32 +13,24 @@ module tb_sigmadelta();
|
|||||||
initial begin
|
initial begin
|
||||||
$dumpfile("out.vcd");
|
$dumpfile("out.vcd");
|
||||||
$dumpvars;
|
$dumpvars;
|
||||||
#1_000_000
|
#2_000_000
|
||||||
$finish;
|
$finish;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
wire sd_a;
|
wire sd_a;
|
||||||
wire sd_b;
|
wire sd_b;
|
||||||
wire sd_o;
|
wire sd_o;
|
||||||
// 3K3R 220PC 15MHZT
|
wire signed [15:0] decimated_q15;
|
||||||
sigmadelta_sampler sd_sampler(
|
wire decimated_valid;
|
||||||
.clk(clk),
|
|
||||||
.a(sd_a), .b(sd_b),
|
|
||||||
.o(sd_o)
|
|
||||||
);
|
|
||||||
|
|
||||||
wire signed [15:0] sample_q15;
|
sigmadelta_input #(
|
||||||
sigmadelta_rcmodel_q15 rc_model(
|
.R_OHM(3300),
|
||||||
.clk(clk), .resetn(resetn),
|
.C_PF(220)
|
||||||
.sd_sample(sd_o),
|
) dut(
|
||||||
.sample_q15(sample_q15)
|
.clk_15(clk), .resetn(resetn),
|
||||||
);
|
.adc_a(sd_a), .adc_b(sd_b), .adc_o(sd_o),
|
||||||
|
.signal_q15(decimated_q15),
|
||||||
wire signed [15:0] y_q15;
|
.signal_valid(decimated_valid)
|
||||||
lpf_iir_q15_k #(8) lpf(
|
|
||||||
.clk(clk), .rst_n(resetn),
|
|
||||||
.x_q15(sample_q15),
|
|
||||||
.y_q15(y_q15)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
endmodule
|
endmodule
|
||||||
|
|||||||
Reference in New Issue
Block a user