`timescale 1ns / 1ps ////////////////////////////////////////////////////////////////////////////////// // Company: // Engineer: // // Create Date: 05/06/2023 02:33:07 PM // Design Name: // Module Name: freq_comp // Project Name: // Target Devices: // Tool Versions: // Description: // // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // ////////////////////////////////////////////////////////////////////////////////// module freq_comp #( parameter DATA_I_WIDTH = 12, parameter DATA_O_WIDTH = 18, parameter FREQ_WORD_WIDTH = 32, parameter FREQ_WORD_LOWER = -1048576, parameter FREQ_WORD_UPPER = 1048575, parameter FREQ_WORD_STEP = 1024, parameter MAX_DMF_NUM = 64, parameter CHANNEL_NUM = 4, // 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 ) ( // clock & reset input clk , input clk_4x , input rst , // module enable switch input freq_comp_start , output freq_comp_busy , // input data input rx_data_valid , input [DATA_I_WIDTH-1 : 0] rx_ch1_i , input [DATA_I_WIDTH-1 : 0] rx_ch1_q , input [DATA_I_WIDTH-1 : 0] rx_ch2_i , input [DATA_I_WIDTH-1 : 0] rx_ch2_q , input [DATA_I_WIDTH-1 : 0] rx_ch3_i , input [DATA_I_WIDTH-1 : 0] rx_ch3_q , input [DATA_I_WIDTH-1 : 0] rx_ch4_i , input [DATA_I_WIDTH-1 : 0] rx_ch4_q , // configuration input [$clog2(MAX_DMF_NUM)-1 : 0] dmf_num_per_ch , input dmf_continuous , input [FREQ_WORD_WIDTH-1 : 0] freq_manual , input freq_manual_en , // output frequency result output [FREQ_WORD_WIDTH-1 : 0] freq_target , output freq_target_valid , // output dmf result output ch_peak_valid , output [DATA_O_WIDTH-1 : 0] ch1_peak_value_abs , output [DATA_O_WIDTH-1 : 0] ch1_peak_value_i , output [DATA_O_WIDTH-1 : 0] ch1_peak_value_q , output [$clog2(PRBS_POS)-1 : 0] ch1_peak_index , output [DATA_O_WIDTH-1 : 0] ch2_peak_value_abs , output [DATA_O_WIDTH-1 : 0] ch2_peak_value_i , output [DATA_O_WIDTH-1 : 0] ch2_peak_value_q , output [$clog2(PRBS_POS)-1 : 0] ch2_peak_index , output [DATA_O_WIDTH-1 : 0] ch3_peak_value_abs , output [DATA_O_WIDTH-1 : 0] ch3_peak_value_i , output [DATA_O_WIDTH-1 : 0] ch3_peak_value_q , output [$clog2(PRBS_POS)-1 : 0] ch3_peak_index , output [DATA_O_WIDTH-1 : 0] ch4_peak_value_abs , output [DATA_O_WIDTH-1 : 0] ch4_peak_value_i , output [DATA_O_WIDTH-1 : 0] ch4_peak_value_q , output [$clog2(PRBS_POS)-1 : 0] ch4_peak_index ); localparam STATE_NUM = 5; localparam [$clog2(STATE_NUM)-1:0] STATE_IDLE = 0, STATE_FREQ_SW = 1, STATE_FREQ_DMF = 2, STATE_CH_SW = 3, STATE_CH_DMF = 4; localparam FREQ_WORD_LOWER_WIDTH = $clog2(FREQ_WORD_LOWER); localparam FREQ_WORD_UPPER_WIDTH = $clog2(FREQ_WORD_UPPER); localparam FREQ_WORD_STEP_WIDTH = $clog2(FREQ_WORD_STEP ); localparam DDS_DATA_WIDTH = 8; localparam STABLE_COUNT_MOD = 16; localparam CMPY_A_WIDTH = DATA_I_WIDTH; localparam CMPY_B_WIDTH = DDS_DATA_WIDTH; localparam CMPY_P_WIDTH = CMPY_A_WIDTH+CMPY_B_WIDTH; localparam CMPY_P_FULL_WIDTH = CMPY_P_WIDTH+1; localparam CMPY_OUTPUT_WIDTH = DATA_I_WIDTH; genvar m; // input data clock domain crossing wire [DATA_I_WIDTH*CHANNEL_NUM*2-1 : 0] fifo_din; wire [DATA_I_WIDTH*CHANNEL_NUM*2-1 : 0] fifo_dout; reg [1:0] fifo_rd_cnt = 'b0; reg fifo_rd_en = 'b0; wire [DATA_I_WIDTH-1 : 0] rx_cdc_ch1_i; wire [DATA_I_WIDTH-1 : 0] rx_cdc_ch1_q; wire [DATA_I_WIDTH-1 : 0] rx_cdc_ch2_i; wire [DATA_I_WIDTH-1 : 0] rx_cdc_ch2_q; wire [DATA_I_WIDTH-1 : 0] rx_cdc_ch3_i; wire [DATA_I_WIDTH-1 : 0] rx_cdc_ch3_q; wire [DATA_I_WIDTH-1 : 0] rx_cdc_ch4_i; wire [DATA_I_WIDTH-1 : 0] rx_cdc_ch4_q; assign fifo_din = { rx_ch1_i, rx_ch1_q, rx_ch2_i, rx_ch2_q, rx_ch3_i, rx_ch3_q, rx_ch4_i, rx_ch4_q}; assign { rx_cdc_ch1_i, rx_cdc_ch1_q, rx_cdc_ch2_i, rx_cdc_ch2_q, rx_cdc_ch3_i, rx_cdc_ch3_q, rx_cdc_ch4_i, rx_cdc_ch4_q } = fifo_dout; always @(posedge clk_4x) begin if (rst) begin fifo_rd_cnt <= 'b0; end else begin fifo_rd_cnt <= fifo_rd_cnt + 1'b1; end end always @(posedge clk_4x) begin if (rst) begin fifo_rd_en <= 'b0; end else begin fifo_rd_en <= (fifo_rd_cnt == 2'd3) ? 1'b1 : 'b0; end end fifo_rx_data u_fifo_rx_data ( .rst (rst ), // input wire rst .wr_clk (clk ), // input wire wr_clk .rd_clk (clk_4x ), // input wire rd_clk .din (fifo_din ), // input wire [95 : 0] din .wr_en (1'b1 ), // input wire wr_en .rd_en (fifo_rd_en ), // input wire rd_en .dout (fifo_dout ), // output wire [95 : 0] dout .full ( ), // output wire full .empty ( ) // output wire empty ); //////////////////////////////////////////////////////////////////////////////// ///////////////////////////// Center State Control ///////////////////////////// //////////////////////////////////////////////////////////////////////////////// // current and next state reg [$clog2(STATE_NUM)-1 : 0] state_curr = STATE_IDLE; reg [$clog2(STATE_NUM)-1 : 0] state_next = STATE_IDLE; // edge detection reg freq_comp_start_d1 = 'b0; reg freq_comp_start_d2 = 'b0; // stable counter, for dds frequency and complex multiplier to be ready reg [$clog2(STABLE_COUNT_MOD)-1 : 0] cnt_stable = 'b0; // dmf finish indicator wire dmf_freq_last_peak_valid; wire dmf_ch_last_peak_valid [0 : CHANNEL_NUM-1]; // current frequency word reg signed [FREQ_WORD_WIDTH-1 : 0] freq_word = 'b0; // frequency scan finish indicator reg freq_end = 'b0; // ----------------------------------------------------------------------------- // 1st always block, sequential state transition // ----------------------------------------------------------------------------- always @(posedge clk_4x) begin if (rst) begin state_curr <= STATE_IDLE; end else begin state_curr <= state_next; end end // ----------------------------------------------------------------------------- // 2nd always block, combinational condition judgment // ----------------------------------------------------------------------------- always @(*) begin case (state_curr) STATE_IDLE: begin if (rx_data_valid && (freq_comp_start_d2 != freq_comp_start_d1)) begin // when input data valid & either edge of start signal is detected state_next = STATE_FREQ_SW; end else begin state_next = STATE_IDLE; end end STATE_FREQ_SW: begin if (cnt_stable == STABLE_COUNT_MOD-1) begin // wait util dds frequency and complex multiplier is ready state_next = STATE_FREQ_DMF; end else begin state_next = STATE_FREQ_SW; end end STATE_FREQ_DMF: begin if (dmf_freq_last_peak_valid) begin // when dmf is done, check if frequency compensation is over state_next = freq_end ? STATE_CH_SW : STATE_FREQ_SW; end else begin state_next = STATE_FREQ_DMF; end end STATE_CH_SW : begin if (cnt_stable == STABLE_COUNT_MOD-1) begin // wait util dds frequency and complex multiplier is ready state_next = STATE_CH_DMF; end else begin state_next = STATE_CH_SW; end end STATE_CH_DMF : begin if (dmf_ch_last_peak_valid[0]) begin // wait until dmf is done state_next = STATE_IDLE; end else begin state_next = STATE_CH_DMF; end end default: begin // hold state_next = state_next; end endcase end // ----------------------------------------------------------------------------- // 3rd always block, the sequential FSM output // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- // edge detection // ----------------------------------------------------------------------------- always @(posedge clk_4x) begin freq_comp_start_d1 <= freq_comp_start; freq_comp_start_d2 <= freq_comp_start_d1; end // ----------------------------------------------------------------------------- // busy indicator // ----------------------------------------------------------------------------- reg freq_comp_busy_buf = 'b0; assign freq_comp_busy = freq_comp_busy_buf; always @(posedge clk_4x) begin if (rst) begin freq_comp_busy_buf <= 'b0; end else begin case (state_next) // change between states STATE_IDLE : freq_comp_busy_buf <= 1'b0; STATE_FREQ_SW : freq_comp_busy_buf <= 1'b1; STATE_FREQ_DMF: freq_comp_busy_buf <= 1'b1; STATE_CH_SW : freq_comp_busy_buf <= 1'b1; STATE_CH_DMF : freq_comp_busy_buf <= 1'b1; default : freq_comp_busy_buf <= freq_comp_busy_buf; endcase end end // ----------------------------------------------------------------------------- // stable counter, for dds frequency and complex multiplier to be ready // ----------------------------------------------------------------------------- always @(posedge clk_4x) begin if (rst) begin cnt_stable <= 'b0; end else case (state_curr) // change within one state STATE_FREQ_SW: cnt_stable <= cnt_stable + 1'b1; STATE_CH_SW : cnt_stable <= cnt_stable + 1'b1; default : cnt_stable <= 'b0; endcase end // ----------------------------------------------------------------------------- // frequency word control // ----------------------------------------------------------------------------- // record the frequency word generating the maximum peak reg [FREQ_WORD_WIDTH-1 : 0] max_freq_peak_freq = 'b0; always @(posedge clk_4x) begin if (rst) begin freq_word <= FREQ_WORD_LOWER; end else case (state_next) // change between states STATE_FREQ_SW: begin if (state_curr == STATE_IDLE) begin // initialization freq_word <= FREQ_WORD_LOWER; end else if (state_curr == STATE_FREQ_DMF) begin // switch frequency freq_word <= freq_word + FREQ_WORD_STEP; end end STATE_CH_SW: begin freq_word <= (freq_manual_en) ? freq_manual : max_freq_peak_freq; end default: begin freq_word <= freq_word; end endcase end // ----------------------------------------------------------------------------- // frequency scan finish indicator // ----------------------------------------------------------------------------- always @(posedge clk_4x) begin if (rst) begin freq_end <= 'b0; end else if (freq_word > FREQ_WORD_UPPER) begin freq_end <= 1'b1; end else begin freq_end <= 'b0; end end //////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////// DDS ////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// localparam AXIS_FREQ_WORD_WIDTH = 8*((FREQ_WORD_WIDTH-1)/8+1); localparam AXIS_DDS_DATA_WIDTH = 8*((DDS_DATA_WIDTH-1)/8+1); wire [DDS_DATA_WIDTH-1:0] sin_dds, cos_dds; wire dds_valid; wire dds_s_axis_config_tvalid; wire [AXIS_FREQ_WORD_WIDTH-1 : 0] dds_s_axis_config_ttdata; wire dds_m_axis_data_tvalid ; wire [2*AXIS_DDS_DATA_WIDTH-1 : 0] dds_m_axis_data_ttdata ; assign dds_s_axis_config_tvalid = freq_comp_busy; assign dds_s_axis_config_ttdata = freq_word; assign dds_valid = dds_m_axis_data_tvalid; assign {sin_dds,cos_dds} = dds_m_axis_data_ttdata; dds_32b_8b u_dds_bb_shift ( .aclk (clk_4x ), // input wire aclk .s_axis_config_tvalid (dds_s_axis_config_tvalid ), // input wire s_axis_config_tvalid .s_axis_config_tdata (dds_s_axis_config_ttdata ), // input wire [31 : 0] s_axis_config_tdata .m_axis_data_tvalid (dds_m_axis_data_tvalid ), // output wire m_axis_data_tvalid .m_axis_data_tdata (dds_m_axis_data_ttdata ) // output wire [15 : 0] m_axis_data_tdata ); //////////////////////////////////////////////////////////////////////////////// ///////////// Complex Multiplier for Frequency Shift Compensation ////////////// //////////////////////////////////////////////////////////////////////////////// localparam AXIS_CMPY_A_WIDTH = 8*((CMPY_A_WIDTH-1)/8+1); localparam AXIS_CMPY_B_WIDTH = 8*((CMPY_B_WIDTH-1)/8+1); localparam AXIS_CMPY_P_WIDTH = 8*((CMPY_P_FULL_WIDTH-1)/8+1); // input data wire [DATA_I_WIDTH-1 : 0] din_freq_real; wire [DATA_I_WIDTH-1 : 0] din_freq_imag; assign din_freq_real = rx_cdc_ch2_i; assign din_freq_imag = rx_cdc_ch2_q; // cmpy wire [CMPY_OUTPUT_WIDTH-1 : 0] din_freq_cxr_real; wire [CMPY_OUTPUT_WIDTH-1 : 0] din_freq_cxr_imag; wire din_freq_cxr_valid; // declared ahead wire cmpy_a_valid; wire [2*AXIS_CMPY_A_WIDTH-1 : 0] cmpy_a ; wire cmpy_b_valid; wire [2*AXIS_CMPY_B_WIDTH-1 : 0] cmpy_b ; wire cmpy_p_valid; wire [2*AXIS_CMPY_P_WIDTH-1 : 0] cmpy_p ; assign cmpy_a_valid = freq_comp_busy; assign cmpy_b_valid = dds_valid; assign din_freq_cxr_valid = cmpy_p_valid; // sign bit extension to fit axis 8-bit data format assign cmpy_a = {{(AXIS_CMPY_A_WIDTH-CMPY_A_WIDTH){din_freq_imag[DATA_I_WIDTH-1]}},din_freq_imag, {(AXIS_CMPY_A_WIDTH-CMPY_A_WIDTH){din_freq_real[DATA_I_WIDTH-1]}},din_freq_real}; assign cmpy_b = {{(AXIS_CMPY_B_WIDTH-CMPY_B_WIDTH){sin_dds[DDS_DATA_WIDTH-1]}},sin_dds, {(AXIS_CMPY_B_WIDTH-CMPY_B_WIDTH){cos_dds[DDS_DATA_WIDTH-1]}},cos_dds}; if (CMPY_OUTPUT_WIDTH > CMPY_P_WIDTH) begin // append zeros assign din_freq_cxr_real = {cmpy_p[0 +: CMPY_P_WIDTH],{(CMPY_OUTPUT_WIDTH-CMPY_P_WIDTH){1'b0}}}; assign din_freq_cxr_imag = {cmpy_p[AXIS_CMPY_P_WIDTH +: CMPY_P_WIDTH],{(CMPY_OUTPUT_WIDTH-CMPY_P_WIDTH){1'b0}}}; end else begin // truncate valid msb assign din_freq_cxr_real = cmpy_p[CMPY_P_WIDTH-1 -: CMPY_OUTPUT_WIDTH]; assign din_freq_cxr_imag = cmpy_p[AXIS_CMPY_P_WIDTH+CMPY_P_WIDTH-1 -: CMPY_OUTPUT_WIDTH]; end cmpy_12x8 u_cmpy_12x8_freq ( .aclk (clk_4x ), // input wire aclk .s_axis_a_tvalid (cmpy_a_valid ), // input wire s_axis_a_tvalid .s_axis_a_tdata (cmpy_a ), // input wire [31 : 0] s_axis_a_tdata .s_axis_b_tvalid (cmpy_b_valid ), // input wire s_axis_b_tvalid .s_axis_b_tdata (cmpy_b ), // input wire [15 : 0] s_axis_b_tdata .m_axis_dout_tvalid (cmpy_p_valid ), // output wire m_axis_dout_tvalid .m_axis_dout_tdata (cmpy_p ) // output wire [47 : 0] m_axis_dout_tdata ); //////////////////////////////////////////////////////////////////////////////// /////////// Digital Matched Filter for Frequency Shift Compensation //////////// //////////////////////////////////////////////////////////////////////////////// reg dmf_freq_start = 'b0; wire dmf_freq_busy; wire dmf_freq_din_valid; wire [DATA_I_WIDTH-1 : 0] dmf_freq_din_real; wire [DATA_I_WIDTH-1 : 0] dmf_freq_din_imag; wire dmf_freq_peak_valid; wire [DATA_O_WIDTH-1 : 0] dmf_freq_peak_value_abs; wire [DATA_O_WIDTH-1 : 0] dmf_freq_peak_value_i; wire [DATA_O_WIDTH-1 : 0] dmf_freq_peak_value_q; wire [$clog2(PRBS_POS)-1 : 0] dmf_freq_peak_index; wire [DATA_O_WIDTH-1 : 0] dmf_freq_moduli_abs; assign dmf_freq_din_valid = din_freq_cxr_valid; if (DATA_I_WIDTH > CMPY_OUTPUT_WIDTH) begin // append zeros assign dmf_freq_din_real = {din_freq_cxr_real,{(DATA_I_WIDTH-CMPY_OUTPUT_WIDTH){1'b0}}}; assign dmf_freq_din_imag = {din_freq_cxr_imag,{(DATA_I_WIDTH-CMPY_OUTPUT_WIDTH){1'b0}}}; end else begin // truncate msb assign dmf_freq_din_real = din_freq_cxr_real[CMPY_OUTPUT_WIDTH-1 -: DATA_I_WIDTH]; assign dmf_freq_din_imag = din_freq_cxr_imag[CMPY_OUTPUT_WIDTH-1 -: DATA_I_WIDTH]; end // ----------------------------------------------------------------------------- // dmf start control // ----------------------------------------------------------------------------- always @(posedge clk_4x) begin if (rst) begin dmf_freq_start <= 'b0; end else case (state_next) STATE_FREQ_DMF: dmf_freq_start <= (state_curr == STATE_FREQ_SW) ? ~dmf_freq_start : dmf_freq_start; default: dmf_freq_start <= dmf_freq_start; endcase end // dmf instantiation rx_dmf #( .PRBS_LEN (PRBS_LEN ), .INTERP_RATE (INTERP_RATE ), .PROCESS_INTERP (PROCESS_INTERP ), .DATA_I_WIDTH (DATA_I_WIDTH ), .DATA_O_WIDTH (DATA_O_WIDTH ) ) u_rx_dmf_freq ( .clk (clk_4x ), .rst (rst ), .dmf_start (dmf_freq_start ), .dmf_busy (dmf_freq_busy ), .dmf_num (1 ), .dmf_continuous (1'b0 ), .din_valid (dmf_freq_din_valid ), .din_real (dmf_freq_din_real ), .din_imag (dmf_freq_din_imag ), .peak_valid (dmf_freq_peak_valid ), .last_peak_valid(dmf_freq_last_peak_valid ), .peak_value_abs (dmf_freq_peak_value_abs ), .peak_value_i (dmf_freq_peak_value_i ), .peak_value_q (dmf_freq_peak_value_q ), .peak_index (dmf_freq_peak_index ) ); assign dmf_freq_moduli_abs = u_rx_dmf_freq.moduli_abs; // ----------------------------------------------------------------------------- // peak finding at different frequencies // ----------------------------------------------------------------------------- reg max_freq_peak_valid = 'b0; reg [DATA_O_WIDTH-1 : 0] max_freq_peak_value_abs = 'b0; reg [DATA_O_WIDTH-1 : 0] max_freq_peak_value_i = 'b0; reg [DATA_O_WIDTH-1 : 0] max_freq_peak_value_q = 'b0; reg [$clog2(PRBS_POS)-1 : 0] max_freq_peak_index = 'b0; // output result to port assign freq_target_valid = max_freq_peak_valid; assign freq_target = max_freq_peak_freq; always @(posedge clk_4x) begin if (rst) begin max_freq_peak_valid <= 'b0; max_freq_peak_value_abs <= 'b0; max_freq_peak_value_i <= 'b0; max_freq_peak_value_q <= 'b0; max_freq_peak_index <= 'b0; max_freq_peak_freq <= 'b0; end else case (state_next) STATE_IDLE: begin max_freq_peak_valid <= 'b0; max_freq_peak_value_abs <= 'b0; max_freq_peak_value_i <= 'b0; max_freq_peak_value_q <= 'b0; max_freq_peak_index <= 'b0; max_freq_peak_freq <= 'b0; end STATE_FREQ_SW: begin if ((state_curr == STATE_FREQ_DMF) && (dmf_freq_peak_value_abs > max_freq_peak_value_abs)) begin // at the end of [STATE_FREQ_DMF] state, check if peak is valid max_freq_peak_value_abs <= dmf_freq_peak_value_abs; max_freq_peak_value_i <= dmf_freq_peak_value_i ; max_freq_peak_value_q <= dmf_freq_peak_value_q ; max_freq_peak_index <= dmf_freq_peak_index ; max_freq_peak_freq <= freq_word ; end else begin // hold otherwise max_freq_peak_value_abs <= max_freq_peak_value_abs; max_freq_peak_value_i <= max_freq_peak_value_i ; max_freq_peak_value_q <= max_freq_peak_value_q ; max_freq_peak_index <= max_freq_peak_index ; max_freq_peak_freq <= max_freq_peak_freq ; end end STATE_CH_SW: begin if (state_curr == STATE_FREQ_DMF) begin // indicate the target frequency word is found max_freq_peak_valid <= 1'b1; end else begin max_freq_peak_valid <= 'b0; end end default: begin max_freq_peak_valid <= 'b0; max_freq_peak_value_abs <= max_freq_peak_value_abs; max_freq_peak_value_i <= max_freq_peak_value_i ; max_freq_peak_value_q <= max_freq_peak_value_q ; max_freq_peak_index <= max_freq_peak_index ; max_freq_peak_freq <= max_freq_peak_freq ; end endcase end //////////////////////////////////////////////////////////////////////////////// //////////////////// Complex Multiplier for Signal Channels //////////////////// //////////////////////////////////////////////////////////////////////////////// // cmpy input wire [CMPY_OUTPUT_WIDTH-1 : 0] din_ch_real [0 : CHANNEL_NUM-1]; wire [CMPY_OUTPUT_WIDTH-1 : 0] din_ch_imag [0 : CHANNEL_NUM-1]; assign din_ch_real[0] = rx_cdc_ch1_i; assign din_ch_real[1] = rx_cdc_ch2_i; assign din_ch_real[2] = rx_cdc_ch3_i; assign din_ch_real[3] = rx_cdc_ch4_i; assign din_ch_imag[0] = rx_cdc_ch1_q; assign din_ch_imag[1] = rx_cdc_ch2_q; assign din_ch_imag[2] = rx_cdc_ch3_q; assign din_ch_imag[3] = rx_cdc_ch4_q; // cmpy output wire [CMPY_OUTPUT_WIDTH-1 : 0] din_ch_cxr_real [0 : CHANNEL_NUM-1]; wire [CMPY_OUTPUT_WIDTH-1 : 0] din_ch_cxr_imag [0 : CHANNEL_NUM-1]; wire din_ch_cxr_valid [0 : CHANNEL_NUM-1]; generate for (m = 0; m < CHANNEL_NUM; m = m + 1) begin: cmpy_ch wire cmpy_a_valid; wire [2*AXIS_CMPY_A_WIDTH-1 : 0] cmpy_a ; wire cmpy_b_valid; wire [2*AXIS_CMPY_B_WIDTH-1 : 0] cmpy_b ; wire cmpy_p_valid; wire [2*AXIS_CMPY_P_WIDTH-1 : 0] cmpy_p ; assign cmpy_a_valid = freq_comp_busy; assign cmpy_b_valid = dds_valid; assign din_ch_cxr_valid[m] = cmpy_p_valid; // sign bit extension to fit axis 8-bit data format assign cmpy_a = {{(AXIS_CMPY_A_WIDTH-CMPY_A_WIDTH){din_ch_imag[m][DATA_I_WIDTH-1]}},din_ch_imag[m], {(AXIS_CMPY_A_WIDTH-CMPY_A_WIDTH){din_ch_real[m][DATA_I_WIDTH-1]}},din_ch_real[m]}; assign cmpy_b = {{(AXIS_CMPY_B_WIDTH-CMPY_B_WIDTH){sin_dds[DDS_DATA_WIDTH-1]}},sin_dds, {(AXIS_CMPY_B_WIDTH-CMPY_B_WIDTH){cos_dds[DDS_DATA_WIDTH-1]}},cos_dds}; if (CMPY_OUTPUT_WIDTH > CMPY_P_WIDTH) begin // append zeros assign din_ch_cxr_real[m] = {cmpy_p[0 +: CMPY_P_WIDTH],{(CMPY_OUTPUT_WIDTH-CMPY_P_WIDTH){1'b0}}}; assign din_ch_cxr_imag[m] = {cmpy_p[AXIS_CMPY_P_WIDTH +: CMPY_P_WIDTH],{(CMPY_OUTPUT_WIDTH-CMPY_P_WIDTH){1'b0}}}; end else begin // truncate valid msb assign din_ch_cxr_real[m] = cmpy_p[CMPY_P_WIDTH-1 -: CMPY_OUTPUT_WIDTH]; assign din_ch_cxr_imag[m] = cmpy_p[AXIS_CMPY_P_WIDTH+CMPY_P_WIDTH-1 -: CMPY_OUTPUT_WIDTH]; end cmpy_12x8 u_cmpy_12x8_ch ( .aclk (clk_4x ), // input wire aclk .s_axis_a_tvalid (cmpy_a_valid ), // input wire s_axis_a_tvalid .s_axis_a_tdata (cmpy_a ), // input wire [31 : 0] s_axis_a_tdata .s_axis_b_tvalid (cmpy_b_valid ), // input wire s_axis_b_tvalid .s_axis_b_tdata (cmpy_b ), // input wire [15 : 0] s_axis_b_tdata .m_axis_dout_tvalid (cmpy_p_valid ), // output wire m_axis_dout_tvalid .m_axis_dout_tdata (cmpy_p ) // output wire [47 : 0] m_axis_dout_tdata ); end endgenerate //////////////////////////////////////////////////////////////////////////////// ////////////////// Digital Matched Filter for Signal Channels ////////////////// //////////////////////////////////////////////////////////////////////////////// reg dmf_ch_start = 'b0; wire dmf_ch_busy [0 : CHANNEL_NUM-1]; wire dmf_ch_din_valid [0 : CHANNEL_NUM-1]; wire [DATA_I_WIDTH-1 : 0] dmf_ch_din_real [0 : CHANNEL_NUM-1]; wire [DATA_I_WIDTH-1 : 0] dmf_ch_din_imag [0 : CHANNEL_NUM-1]; wire dmf_ch_peak_valid [0 : CHANNEL_NUM-1]; wire [DATA_O_WIDTH-1 : 0] dmf_ch_peak_value_abs [0 : CHANNEL_NUM-1]; wire [DATA_O_WIDTH-1 : 0] dmf_ch_peak_value_i [0 : CHANNEL_NUM-1]; wire [DATA_O_WIDTH-1 : 0] dmf_ch_peak_value_q [0 : CHANNEL_NUM-1]; wire [$clog2(PRBS_POS)-1 : 0] dmf_ch_peak_index [0 : CHANNEL_NUM-1]; wire [DATA_O_WIDTH-1 : 0] dmf_ch_moduli_abs [0 : CHANNEL_NUM-1]; // ----------------------------------------------------------------------------- // dmf start control // ----------------------------------------------------------------------------- always @(posedge clk_4x) begin if (rst) begin dmf_ch_start <= 'b0; end else case (state_next) STATE_CH_DMF: dmf_ch_start <= (state_curr == STATE_CH_SW) ? ~dmf_ch_start : dmf_ch_start; default: dmf_ch_start <= dmf_ch_start; endcase end generate for (m = 0; m < CHANNEL_NUM; m = m + 1) begin: dmf_ch assign dmf_ch_din_valid[m] = din_ch_cxr_valid[m]; if (DATA_I_WIDTH > CMPY_OUTPUT_WIDTH) begin // append zeros assign dmf_ch_din_real[m] = {din_ch_cxr_real[m],{(DATA_I_WIDTH-CMPY_OUTPUT_WIDTH){1'b0}}}; assign dmf_ch_din_imag[m] = {din_ch_cxr_imag[m],{(DATA_I_WIDTH-CMPY_OUTPUT_WIDTH){1'b0}}}; end else begin // truncate msb assign dmf_ch_din_real[m] = din_ch_cxr_real[m][CMPY_OUTPUT_WIDTH-1 -: DATA_I_WIDTH]; assign dmf_ch_din_imag[m] = din_ch_cxr_imag[m][CMPY_OUTPUT_WIDTH-1 -: DATA_I_WIDTH]; end // dmf instantiation rx_dmf #( .PRBS_LEN (PRBS_LEN ), .INTERP_RATE (INTERP_RATE ), .PROCESS_INTERP (PROCESS_INTERP ), .DATA_I_WIDTH (DATA_I_WIDTH ), .DATA_O_WIDTH (DATA_O_WIDTH ) ) u_rx_dmf_ch ( .clk (clk_4x ), .rst (rst ), .dmf_start (dmf_ch_start ), .dmf_busy (dmf_ch_busy[m] ), .dmf_num (dmf_num_per_ch ), .dmf_continuous (dmf_continuous ), .din_valid (dmf_ch_din_valid[m] ), .din_real (dmf_ch_din_real[m] ), .din_imag (dmf_ch_din_imag[m] ), .peak_valid (dmf_ch_peak_valid[m] ), .last_peak_valid(dmf_ch_last_peak_valid[m] ), .peak_value_abs (dmf_ch_peak_value_abs[m] ), .peak_value_i (dmf_ch_peak_value_i[m] ), .peak_value_q (dmf_ch_peak_value_q[m] ), .peak_index (dmf_ch_peak_index[m] ) ); assign dmf_ch_moduli_abs[m] = u_rx_dmf_ch.moduli_abs; end endgenerate assign ch_peak_valid = dmf_ch_peak_valid[0]; assign ch1_peak_value_abs = dmf_ch_peak_value_abs[0]; assign ch1_peak_value_i = dmf_ch_peak_value_i[0] ; assign ch1_peak_value_q = dmf_ch_peak_value_q [0] ; assign ch1_peak_index = dmf_ch_peak_index[0] ; assign ch2_peak_value_abs = dmf_ch_peak_value_abs[1]; assign ch2_peak_value_i = dmf_ch_peak_value_i[1] ; assign ch2_peak_value_q = dmf_ch_peak_value_q [1] ; assign ch2_peak_index = dmf_ch_peak_index[1] ; assign ch3_peak_value_abs = dmf_ch_peak_value_abs[2]; assign ch3_peak_value_i = dmf_ch_peak_value_i[2] ; assign ch3_peak_value_q = dmf_ch_peak_value_q [2] ; assign ch3_peak_index = dmf_ch_peak_index[2] ; assign ch4_peak_value_abs = dmf_ch_peak_value_abs[3]; assign ch4_peak_value_i = dmf_ch_peak_value_i[3] ; assign ch4_peak_value_q = dmf_ch_peak_value_q [3] ; assign ch4_peak_index = dmf_ch_peak_index[3] ; endmodule