`timescale 1ns / 1ps ////////////////////////////////////////////////////////////////////////////////// // Company: // Engineer: // _ _ ____ // | | ___ ___| |__ / ___| __ _ ___ // _ | |/ _ \/ __| '_ \ | | _ / _` |/ _ \ // | |_| | (_) \__ \ | | | | |_| | (_| | (_) | // \___/ \___/|___/_| |_| \____|\__,_|\___/ // // Create Date: 04/25/2023 05:53:44 PM // Design Name: // Module Name: rx_dmf // Project Name: // Target Devices: // Tool Versions: // Description: // // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // ////////////////////////////////////////////////////////////////////////////////// module rx_dmf #( // length of pseudorandom bit sequence, need to be a power of 2 parameter PRBS_LEN = 1024, // interpolation of input signal, need to be a power of 2 parameter INTERP_RATE = 32, // signal processing interpolation, need to be a power of 2 parameter PROCESS_INTERP = 2, // possible position of pn code parameter PRBS_POS = PRBS_LEN*PROCESS_INTERP, parameter MAX_DMF_NUM = 64, parameter DATA_I_WIDTH = 12, parameter DATA_O_WIDTH = 18 ) ( input clk , input rst , input dmf_start , output dmf_busy , input din_valid , input [DATA_I_WIDTH-1 : 0] din_real , input [DATA_I_WIDTH-1 : 0] din_imag , input [$clog2(MAX_DMF_NUM+1)-1 : 0] dmf_num , input dmf_continuous , output peak_valid , output last_peak_valid , output [DATA_O_WIDTH-1 : 0] peak_value_abs , output [DATA_O_WIDTH-1 : 0] peak_value_i , output [DATA_O_WIDTH-1 : 0] peak_value_q , output [$clog2(PRBS_POS)-1 : 0] peak_index ); localparam CMPLX_DIM = 2; localparam APPROX_ABS_WIDTH = 29; localparam ABS_WIDTH = 18; localparam DMF_I_WIDTH = 12; localparam DMF_O_WIDTH = DMF_I_WIDTH+$clog2(PRBS_POS); localparam AXIS_DMF_I_WIDTH = 8*((DMF_I_WIDTH-1)/8+1); localparam AXIS_DMF_O_WIDTH = 8*((DMF_O_WIDTH-1)/8+1); genvar m; integer i; //////////////////////////////////////////////////////////////////////////////// ///////////////////////////// Center State Control ///////////////////////////// //////////////////////////////////////////////////////////////////////////////// reg dmf_busy_buf = 'b0; reg dmf_start_d1 = 'b0; reg dmf_start_d2 = 'b0; reg [$clog2(MAX_DMF_NUM)-1 : 0] cnt_dmf_num = 'b0; reg moduli_max_valid = 'b0; assign dmf_busy = dmf_busy_buf; always @(posedge clk) begin dmf_start_d1 <= dmf_start; dmf_start_d2 <= dmf_start_d1; end always @(posedge clk) begin if (rst) begin cnt_dmf_num <= 'b0; end else if (dmf_busy) begin cnt_dmf_num <= (moduli_max_valid) ? cnt_dmf_num + 1'b1 : cnt_dmf_num; end else begin cnt_dmf_num <= 'b0; end end always @(posedge clk) begin if (rst) begin dmf_busy_buf <= 'b0; end else if (cnt_dmf_num == dmf_num+1 && ~dmf_continuous) begin // clear when output is valid dmf_busy_buf <= 'b0; end else if (din_valid && (dmf_start_d2 != dmf_start_d1) && (dmf_num != 'b0)) begin // set when either edge is detected dmf_busy_buf <= 1'b1; end else begin // otherwise keep dmf_busy_buf <= dmf_busy_buf; end end //////////////////////////////////////////////////////////////////////////////// ////////////////////////////// Decimation Counter ////////////////////////////// //////////////////////////////////////////////////////////////////////////////// reg [$clog2(INTERP_RATE/PROCESS_INTERP)-1 : 0] cnt_deci = 'b0; reg din_deci_valid = 'b0; always @(posedge clk) begin if (rst) begin cnt_deci <= 'b0; end else if (dmf_busy && din_valid) begin cnt_deci <= cnt_deci + 1'b1; end else begin cnt_deci <= 'b0; end end always @(posedge clk) begin if (rst) begin din_deci_valid <= 'b0; end else begin din_deci_valid <= (cnt_deci == INTERP_RATE/PROCESS_INTERP-1) ? 1'b1 : 'b0; end end //////////////////////////////////////////////////////////////////////////////// //////////////////////////// Digital Matched Filter //////////////////////////// //////////////////////////////////////////////////////////////////////////////// // input & output signals wire [AXIS_DMF_I_WIDTH-1 : 0] dmf_axis_i_real; wire [AXIS_DMF_I_WIDTH-1 : 0] dmf_axis_i_imag; wire signed [AXIS_DMF_O_WIDTH-1 : 0] dmf_axis_o_real; wire signed [AXIS_DMF_O_WIDTH-1 : 0] dmf_axis_o_imag; wire signed [DMF_O_WIDTH-1 : 0] dmf_o_real; wire signed [DMF_O_WIDTH-1 : 0] dmf_o_imag; wire dmf_o_valid; // dmf input data formatting if (AXIS_DMF_I_WIDTH > DATA_I_WIDTH) begin // sign extension to fit 8-bit axis data format assign dmf_axis_i_real = {{(AXIS_DMF_I_WIDTH-DATA_I_WIDTH){din_real[DATA_I_WIDTH-1]}},din_real}; assign dmf_axis_i_imag = {{(AXIS_DMF_I_WIDTH-DATA_I_WIDTH){din_imag[DATA_I_WIDTH-1]}},din_imag}; end else begin // truncate msb assign dmf_axis_i_real = din_real[DATA_I_WIDTH-1 -: AXIS_DMF_I_WIDTH]; assign dmf_axis_i_imag = din_imag[DATA_I_WIDTH-1 -: AXIS_DMF_I_WIDTH]; end // dmf output data formatting, truncate valid bits assign dmf_o_real = dmf_axis_o_real[DMF_O_WIDTH-1 -: DMF_O_WIDTH]; assign dmf_o_imag = dmf_axis_o_imag[DMF_O_WIDTH-1 -: DMF_O_WIDTH]; // fir port definition wire s_axis_data_tvalid_real; wire s_axis_data_tready_real; wire [AXIS_DMF_I_WIDTH-1 : 0] s_axis_data_ttdata_real; wire m_axis_data_tvalid_real; wire [AXIS_DMF_O_WIDTH-1 : 0] m_axis_data_ttdata_real; wire s_axis_data_tvalid_imag; wire s_axis_data_tready_imag; wire [AXIS_DMF_I_WIDTH-1 : 0] s_axis_data_ttdata_imag; wire m_axis_data_tvalid_imag; wire [AXIS_DMF_O_WIDTH-1 : 0] m_axis_data_ttdata_imag; // fir port connection assign s_axis_data_tvalid_real = din_deci_valid; assign s_axis_data_ttdata_real = dmf_axis_i_real; assign dmf_axis_o_real = m_axis_data_ttdata_real; assign s_axis_data_tvalid_imag = din_deci_valid; assign s_axis_data_ttdata_imag = dmf_axis_i_imag; assign dmf_axis_o_imag = m_axis_data_ttdata_imag; assign dmf_o_valid = m_axis_data_tvalid_real; // fir ip core instantiation fir_prbs10_invert_rep2 u_dmf_real ( .aclk (clk ), // input wire aclk .aresetn (dmf_busy ), // input aresetn .s_axis_data_tvalid (s_axis_data_tvalid_real), // input wire s_axis_data_tvalid .s_axis_data_tready (s_axis_data_tready_real), // output wire s_axis_data_tready .s_axis_data_tdata (s_axis_data_ttdata_real), // input wire [15 : 0] s_axis_data_tdata .m_axis_data_tvalid (m_axis_data_tvalid_real), // output wire m_axis_data_tvalid .m_axis_data_tdata (m_axis_data_ttdata_real) // output wire [23 : 0] m_axis_data_tdata ); fir_prbs10_invert_rep2 u_dmf_imag ( .aclk (clk ), // input wire aclk .aresetn (dmf_busy ), // input aresetn .s_axis_data_tvalid (s_axis_data_tvalid_imag), // input wire s_axis_data_tvalid .s_axis_data_tready (s_axis_data_tready_imag), // output wire s_axis_data_tready .s_axis_data_tdata (s_axis_data_ttdata_imag), // input wire [15 : 0] s_axis_data_tdata .m_axis_data_tvalid (m_axis_data_tvalid_imag), // output wire m_axis_data_tvalid .m_axis_data_tdata (m_axis_data_ttdata_imag) // output wire [23 : 0] m_axis_data_tdata ); //////////////////////////////////////////////////////////////////////////////// ///////////////////////////////// Chip Counter ///////////////////////////////// //////////////////////////////////////////////////////////////////////////////// localparam PEAK_ABS_DELAY = 6; // 5 for multiplier, 1 for adder reg [$clog2(PRBS_POS)-1 : 0] cnt_chip = 'b0; reg [$clog2(PRBS_POS)-1 : 0] cnt_chip_delay [0 : PEAK_ABS_DELAY-1]; always @(posedge clk) begin if (rst) begin cnt_chip <= 'b0; end else if (dmf_busy) begin cnt_chip <= (dmf_o_valid) ? cnt_chip + 1'b1 : cnt_chip; end else begin cnt_chip <= 'b0; end end always @(posedge clk) begin for (i = 1; i < PEAK_ABS_DELAY; i = i + 1) begin cnt_chip_delay[i] <= cnt_chip_delay[i-1]; end cnt_chip_delay[0] <= cnt_chip; end //////////////////////////////////////////////////////////////////////////////// ////////////////////////// Accumulation & Peak Search ////////////////////////// //////////////////////////////////////////////////////////////////////////////// // moduli wire moduli_valid; reg moduli_valid_delay [0 : PEAK_ABS_DELAY-1]; wire [ABS_WIDTH-1 : 0] moduli_real; wire [ABS_WIDTH-1 : 0] moduli_imag; reg [ABS_WIDTH-1 : 0] moduli_real_delay [0 : PEAK_ABS_DELAY-1]; reg [ABS_WIDTH-1 : 0] moduli_imag_delay [0 : PEAK_ABS_DELAY-1]; assign moduli_valid = dmf_o_valid; if (ABS_WIDTH > DMF_O_WIDTH) begin // append zeros assign moduli_real = {dmf_o_real,{(ABS_WIDTH-DMF_O_WIDTH){1'b0}}}; assign moduli_imag = {dmf_o_imag,{(ABS_WIDTH-DMF_O_WIDTH){1'b0}}}; end else begin // truncate msb assign moduli_real = dmf_o_real[DMF_O_WIDTH-1 -: ABS_WIDTH]; assign moduli_imag = dmf_o_imag[DMF_O_WIDTH-1 -: ABS_WIDTH]; end always @(posedge clk) begin for (i = 1; i < PEAK_ABS_DELAY; i = i + 1) begin moduli_valid_delay[i] <= moduli_valid_delay[i-1]; moduli_real_delay[i] <= moduli_real_delay[i-1]; moduli_imag_delay[i] <= moduli_imag_delay[i-1]; end moduli_valid_delay[0] <= moduli_valid; moduli_real_delay[0] <= moduli_real; moduli_imag_delay[0] <= moduli_imag; end wire [2*ABS_WIDTH-1 : 0] moduli_real_square; wire [2*ABS_WIDTH-1 : 0] moduli_imag_square; reg [ABS_WIDTH-1 : 0] moduli_abs_square = 'b0; wire [DATA_O_WIDTH-1 : 0] moduli_abs; wire moduli_abs_valid; assign moduli_abs_valid = moduli_valid_delay[PEAK_ABS_DELAY-1]; if (DATA_O_WIDTH > ABS_WIDTH) begin // append zeros assign moduli_abs = {moduli_abs_square,{(DATA_O_WIDTH-ABS_WIDTH){1'b0}}}; end else begin // truncate msb assign moduli_abs = moduli_abs_square[ABS_WIDTH-1 -: DATA_O_WIDTH]; end always @(posedge clk) begin if (rst) begin moduli_abs_square <= 'b0; end else begin moduli_abs_square <= moduli_real_square[(2*ABS_WIDTH-1) -: (ABS_WIDTH-1)] + moduli_imag_square[(2*ABS_WIDTH-1) -: (ABS_WIDTH-1)]; end end mult_18x18 u_mult_real ( .CLK(clk ), // input wire CLK .A (moduli_real ), // input wire [17 : 0] A .B (moduli_real ), // input wire [17 : 0] B .P (moduli_real_square ) // output wire [17 : 0] P ); mult_18x18 u_mult_imag ( .CLK(clk ), // input wire CLK .A (moduli_imag ), // input wire [17 : 0] A .B (moduli_imag ), // input wire [17 : 0] B .P (moduli_imag_square ) // output wire [17 : 0] P ); // wire accum_current_valid; // assign accum_current_valid = dmf_o_valid; // wire [APPROX_ABS_WIDTH-2 : 0] accum_moduli_real; // wire [APPROX_ABS_WIDTH-2 : 0] accum_moduli_imag; // wire [APPROX_ABS_WIDTH-1 : 0] accum_moduli_abs ; // wire accum_current_abs_valid; // wire [DATA_O_WIDTH-1 : 0] accum_current_abs; // if (APPROX_ABS_WIDTH-1 > DMF_O_WIDTH) begin // // append zeros // assign accum_moduli_real = {dmf_o_real,{(APPROX_ABS_WIDTH-1-DMF_O_WIDTH){1'b0}}}; // assign accum_moduli_imag = {dmf_o_imag,{(APPROX_ABS_WIDTH-1-DMF_O_WIDTH){1'b0}}}; // end // else begin // // truncate msb // assign accum_moduli_real = dmf_o_real[DMF_O_WIDTH-1 -: (APPROX_ABS_WIDTH-1)]; // assign accum_moduli_imag = dmf_o_imag[DMF_O_WIDTH-1 -: (APPROX_ABS_WIDTH-1)]; // end // if (DATA_O_WIDTH > APPROX_ABS_WIDTH) begin // // append zeros // assign accum_current_abs = {accum_moduli_abs,{(DATA_O_WIDTH-APPROX_ABS_WIDTH){1'b0}}}; // end // else begin // // truncate msb // assign accum_current_abs = accum_moduli_abs[APPROX_ABS_WIDTH-1 -: DATA_O_WIDTH]; // end // ApproxAbs u_accum_abs // ( // .clk (clk ), // .Nd (accum_current_valid ), // .Dini (accum_moduli_real ), // .Dinq (accum_moduli_imag ), // .Abs_Rdy (accum_current_abs_valid), // .Abs_Dout (accum_moduli_abs ) // ); // peak record reg [DATA_O_WIDTH-1 : 0] moduli_max_abs = 'b0; reg [DATA_O_WIDTH-1 : 0] moduli_max_real = 'b0; reg [DATA_O_WIDTH-1 : 0] moduli_max_imag = 'b0; reg [$clog2(PRBS_POS)-1 : 0] moduli_max_index = 'b0; always @(posedge clk) begin if (rst) begin moduli_max_valid <= 'b0; moduli_max_abs <= 'b0; moduli_max_real <= 'b0; moduli_max_imag <= 'b0; moduli_max_index <= 'b0; end else if (dmf_busy) begin if (moduli_abs_valid) begin if ((cnt_chip_delay[PEAK_ABS_DELAY-1] == 0) || (moduli_abs > moduli_max_abs)) begin // reset when chip counter reaches zero or record peak moduli_max_abs <= moduli_abs ; moduli_max_real <= moduli_real_delay[PEAK_ABS_DELAY-1]; moduli_max_imag <= moduli_imag_delay[PEAK_ABS_DELAY-1]; moduli_max_index <= cnt_chip_delay[PEAK_ABS_DELAY-1] ; end else begin moduli_max_abs <= moduli_max_abs; moduli_max_real <= moduli_max_real; moduli_max_imag <= moduli_max_imag; moduli_max_index <= moduli_max_index; end if (cnt_chip_delay[PEAK_ABS_DELAY-1] == PRBS_POS-1) begin // peak record valid when chip counter reaches maximum value moduli_max_valid <= 1'b1; end else begin moduli_max_valid <= 'b0; end end else begin moduli_max_valid <= 'b0; moduli_max_abs <= moduli_max_abs; moduli_max_real <= moduli_max_real; moduli_max_imag <= moduli_max_imag; moduli_max_index <= moduli_max_index; end end else begin moduli_max_valid <= 'b0; moduli_max_abs <= 'b0; moduli_max_real <= 'b0; moduli_max_imag <= 'b0; moduli_max_index <= 'b0; end end // register output reg peak_valid_buf = 1'b0; reg last_peak_valid_buf = 1'b0; reg [DATA_O_WIDTH-1 : 0] peak_value_abs_buf = 'b0; reg [DATA_O_WIDTH-1 : 0] peak_value_i_buf = 'b0; reg [DATA_O_WIDTH-1 : 0] peak_value_q_buf = 'b0; reg [$clog2(PRBS_POS)-1 : 0] peak_index_buf = 'b0; assign peak_valid = peak_valid_buf ; assign last_peak_valid = last_peak_valid_buf; assign peak_value_abs = peak_value_abs_buf ; assign peak_value_i = peak_value_i_buf ; assign peak_value_q = peak_value_q_buf ; assign peak_index = peak_index_buf ; always @(posedge clk) begin if (rst) begin peak_valid_buf <= 'b0; last_peak_valid_buf <= 'b0; peak_value_abs_buf <= 'b0; peak_value_i_buf <= 'b0; peak_value_q_buf <= 'b0; peak_index_buf <= 'b0; end else if (moduli_max_valid && (cnt_dmf_num != 'b0)) begin peak_valid_buf <= 1'b1; peak_value_abs_buf <= moduli_max_abs ; peak_value_i_buf <= moduli_max_real ; peak_value_q_buf <= moduli_max_imag ; peak_index_buf <= moduli_max_index; last_peak_valid_buf<= ((cnt_dmf_num == dmf_num) && ~dmf_continuous) ? 1'b1 : 'b0; end else begin peak_valid_buf <= 1'b0; last_peak_valid_buf <= 1'b0; peak_value_abs_buf <= peak_value_abs_buf; peak_value_i_buf <= peak_value_i_buf ; peak_value_q_buf <= peak_value_q_buf ; peak_index_buf <= peak_index_buf ; end end endmodule