Archived
773 lines
32 KiB
Verilog
773 lines
32 KiB
Verilog
`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 = -4194304,
|
|
parameter FREQ_WORD_UPPER = 4194303,
|
|
parameter FREQ_WORD_STEP = 1024,
|
|
|
|
parameter MAX_DMF_NUM = 64,
|
|
|
|
// length of pseudorandom bit sequence, need to be a power of 2
|
|
parameter PRBS_LEN = 128,
|
|
// interpolation of input signal, need to be a power of 2
|
|
parameter INTERP_RATE = 8,
|
|
// signal processing interpolation, need to be a power of 2
|
|
parameter PROCESS_INTERP = 4,
|
|
// possible position of pn code
|
|
parameter PRBS_POS = 2*PRBS_LEN*PROCESS_INTERP
|
|
)
|
|
(
|
|
// clock & reset
|
|
input clk ,
|
|
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 [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 reg [DATA_O_WIDTH-1 : 0] ch1_peak_value_abs ,
|
|
output reg [DATA_O_WIDTH-1 : 0] ch1_peak_value_i ,
|
|
output reg [DATA_O_WIDTH-1 : 0] ch1_peak_value_q ,
|
|
output reg [$clog2(PRBS_POS-1) : 0] ch1_peak_index ,
|
|
|
|
output reg [DATA_O_WIDTH-1 : 0] ch2_peak_value_abs ,
|
|
output reg [DATA_O_WIDTH-1 : 0] ch2_peak_value_i ,
|
|
output reg [DATA_O_WIDTH-1 : 0] ch2_peak_value_q ,
|
|
output reg [$clog2(PRBS_POS-1) : 0] ch2_peak_index ,
|
|
|
|
output reg [DATA_O_WIDTH-1 : 0] ch3_peak_value_abs ,
|
|
output reg [DATA_O_WIDTH-1 : 0] ch3_peak_value_i ,
|
|
output reg [DATA_O_WIDTH-1 : 0] ch3_peak_value_q ,
|
|
output reg [$clog2(PRBS_POS-1) : 0] ch3_peak_index ,
|
|
|
|
output reg [DATA_O_WIDTH-1 : 0] ch4_peak_value_abs ,
|
|
output reg [DATA_O_WIDTH-1 : 0] ch4_peak_value_i ,
|
|
output reg [DATA_O_WIDTH-1 : 0] ch4_peak_value_q ,
|
|
output reg [$clog2(PRBS_POS-1) : 0] ch4_peak_index
|
|
);
|
|
|
|
localparam STATE_NUM = 8;
|
|
localparam [$clog2(STATE_NUM)-1:0]
|
|
STATE_IDLE = 0,
|
|
STATE_FREQ_SW = 1,
|
|
STATE_FREQ_DMF = 2,
|
|
STATE_CH_SW = 3,
|
|
STATE_CH1 = 4,
|
|
STATE_CH2 = 5,
|
|
STATE_CH3 = 6,
|
|
STATE_CH4 = 7;
|
|
|
|
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;
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
///////////////////////////// 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;
|
|
|
|
// count the number of times dmf has been executed on each channel
|
|
reg [$clog2(MAX_DMF_NUM)-1 : 0] cnt_dmf_ch = 'b0;
|
|
|
|
// cmpy output data valid indicator
|
|
wire din_cxr_valid;
|
|
|
|
// dmf finish indicator
|
|
wire dmf_peak_valid;
|
|
|
|
// current frequency word
|
|
reg signed [FREQ_WORD_WIDTH-1 : 0] freq_word = 'b0;
|
|
|
|
// frequency scan finish indicator
|
|
reg freq_end = 'b0;
|
|
|
|
// dmf finish indicator for each channel
|
|
reg ch1_peak_valid = 'b0;
|
|
reg ch2_peak_valid = 'b0;
|
|
reg ch3_peak_valid = 'b0;
|
|
reg ch4_peak_valid = 'b0;
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// 1st always block, sequential state transition
|
|
// -----------------------------------------------------------------------------
|
|
always @(posedge clk) 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_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
|
|
// go through each channel
|
|
if (!ch1_peak_valid) begin
|
|
state_next <= STATE_CH1;
|
|
end
|
|
else if (!ch2_peak_valid) begin
|
|
state_next <= STATE_CH2;
|
|
end
|
|
else if (!ch3_peak_valid) begin
|
|
state_next <= STATE_CH3;
|
|
end
|
|
else if (!ch4_peak_valid) begin
|
|
state_next <= STATE_CH4;
|
|
end
|
|
end
|
|
else begin
|
|
state_next <= STATE_CH_SW;
|
|
end
|
|
end
|
|
STATE_CH1 : begin
|
|
if (ch1_peak_valid) begin
|
|
// wait until dmf is done
|
|
state_next <= STATE_CH_SW;
|
|
end
|
|
else begin
|
|
state_next <= STATE_CH1;
|
|
end
|
|
end
|
|
STATE_CH2 : begin
|
|
if (ch2_peak_valid) begin
|
|
// wait until dmf is done
|
|
state_next <= STATE_CH_SW;
|
|
end
|
|
else begin
|
|
state_next <= STATE_CH2;
|
|
end
|
|
end
|
|
STATE_CH3 : begin
|
|
if (ch3_peak_valid) begin
|
|
state_next <= STATE_CH_SW;
|
|
// wait until dmf is done
|
|
end
|
|
else begin
|
|
state_next <= STATE_CH3;
|
|
end
|
|
end
|
|
STATE_CH4 : begin
|
|
if (ch4_peak_valid) begin
|
|
// wait until dmf is done, check if the number of dmf is enough
|
|
state_next <= (cnt_dmf_ch == dmf_num_per_ch-1) ? STATE_IDLE : STATE_CH_SW;
|
|
end
|
|
else begin
|
|
state_next <= STATE_CH4;
|
|
end
|
|
end
|
|
default: begin
|
|
// hold
|
|
state_next = state_next;
|
|
end
|
|
endcase
|
|
end
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// 3rd always block, the sequential FSM output
|
|
// -----------------------------------------------------------------------------
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// edge detection
|
|
// -----------------------------------------------------------------------------
|
|
always @(posedge clk) 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) 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_CH1 : freq_comp_busy_buf <= 1'b1;
|
|
STATE_CH2 : freq_comp_busy_buf <= 1'b1;
|
|
STATE_CH3 : freq_comp_busy_buf <= 1'b1;
|
|
STATE_CH4 : 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) 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
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// count the number of times dmf has been executed on each channel
|
|
// -----------------------------------------------------------------------------
|
|
always @(posedge clk) begin
|
|
if (rst) begin
|
|
cnt_dmf_ch <= 'b0;
|
|
end
|
|
else if (state_next == STATE_IDLE) begin
|
|
cnt_dmf_ch <= 'b0;
|
|
end
|
|
else begin
|
|
cnt_dmf_ch <= (ch_peak_valid) ? cnt_dmf_ch + 1'b1 : cnt_dmf_ch;
|
|
end
|
|
end
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// frequency word control
|
|
// -----------------------------------------------------------------------------
|
|
// record the frequency word generating the maximum peak
|
|
reg [FREQ_WORD_WIDTH-1 : 0] max_peak_freq = 'b0;
|
|
|
|
always @(posedge clk) 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_peak_freq;
|
|
end
|
|
default: begin
|
|
freq_word <= freq_word;
|
|
end
|
|
endcase
|
|
end
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// frequency scan finish indicator
|
|
// -----------------------------------------------------------------------------
|
|
always @(posedge clk) 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
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// complex multiplier input data control
|
|
// -----------------------------------------------------------------------------
|
|
reg [DATA_I_WIDTH-1 : 0] din_real = 'b0;
|
|
reg [DATA_I_WIDTH-1 : 0] din_imag = 'b0;
|
|
|
|
always @(posedge clk) begin
|
|
if (rst) begin
|
|
din_real <= 'b0;
|
|
din_imag <= 'b0;
|
|
end
|
|
else case (state_next)
|
|
// reset when idle; using ch2 data for frequency scan
|
|
STATE_IDLE : begin din_real <= 'b0 ; din_imag <= 'b0 ; end
|
|
STATE_FREQ_SW : begin din_real <= rx_ch2_i; din_imag <= rx_ch2_q; end
|
|
STATE_FREQ_DMF : begin din_real <= rx_ch2_i; din_imag <= rx_ch2_q; end
|
|
STATE_CH_SW : case (state_curr)
|
|
STATE_FREQ_DMF: begin din_real <= rx_ch1_i; din_imag <= rx_ch1_q; end
|
|
STATE_CH1 : begin din_real <= rx_ch2_i; din_imag <= rx_ch2_q; end
|
|
STATE_CH2 : begin din_real <= rx_ch3_i; din_imag <= rx_ch3_q; end
|
|
STATE_CH3 : begin din_real <= rx_ch4_i; din_imag <= rx_ch4_q; end
|
|
STATE_CH4 : begin din_real <= rx_ch1_i; din_imag <= rx_ch1_q; end
|
|
default : begin din_real <= rx_ch1_i; din_imag <= rx_ch1_q; end
|
|
endcase
|
|
STATE_CH1 : begin din_real <= rx_ch1_i; din_imag <= rx_ch1_q; end
|
|
STATE_CH2 : begin din_real <= rx_ch2_i; din_imag <= rx_ch2_q; end
|
|
STATE_CH3 : begin din_real <= rx_ch3_i; din_imag <= rx_ch3_q; end
|
|
STATE_CH4 : begin din_real <= rx_ch4_i; din_imag <= rx_ch4_q; end
|
|
default : begin din_real <= rx_ch2_i; din_imag <= rx_ch2_q; end
|
|
endcase
|
|
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 ), // 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 //////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
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);
|
|
|
|
// cmpy
|
|
wire [CMPY_OUTPUT_WIDTH-1 : 0] din_cxr_real;
|
|
wire [CMPY_OUTPUT_WIDTH-1 : 0] din_cxr_imag;
|
|
// wire din_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_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_imag[DATA_I_WIDTH-1]}},din_imag,
|
|
{(AXIS_CMPY_A_WIDTH-CMPY_A_WIDTH){din_real[DATA_I_WIDTH-1]}},din_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_cxr_real = {cmpy_p[0 +: CMPY_P_WIDTH],{(CMPY_OUTPUT_WIDTH-CMPY_P_WIDTH){1'b0}}};
|
|
assign din_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_cxr_real = cmpy_p[CMPY_P_WIDTH-1 -: CMPY_OUTPUT_WIDTH];
|
|
assign din_cxr_imag = cmpy_p[AXIS_CMPY_P_WIDTH+CMPY_P_WIDTH-1 -: CMPY_OUTPUT_WIDTH];
|
|
end
|
|
|
|
cmpy_12x8 u_cmpy_12x8
|
|
(
|
|
.aclk (clk ), // 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 ////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
reg dmf_start = 'b0;
|
|
wire dmf_busy;
|
|
|
|
wire dmf_din_valid;
|
|
wire [DATA_I_WIDTH-1 : 0] dmf_din_real;
|
|
wire [DATA_I_WIDTH-1 : 0] dmf_din_imag;
|
|
|
|
wire [DATA_O_WIDTH-1 : 0] dmf_peak_value_abs;
|
|
wire [DATA_O_WIDTH-1 : 0] dmf_peak_value_i;
|
|
wire [DATA_O_WIDTH-1 : 0] dmf_peak_value_q;
|
|
wire [$clog2(PRBS_POS-1) : 0] dmf_peak_index;
|
|
|
|
assign dmf_din_valid = din_cxr_valid;
|
|
|
|
if (DATA_I_WIDTH > CMPY_OUTPUT_WIDTH) begin
|
|
// append zerod
|
|
assign dmf_din_real = {din_cxr_real,{(DATA_I_WIDTH-CMPY_OUTPUT_WIDTH){1'b0}}};
|
|
assign dmf_din_imag = {din_cxr_imag,{(DATA_I_WIDTH-CMPY_OUTPUT_WIDTH){1'b0}}};
|
|
end
|
|
else begin
|
|
// truncate msb
|
|
assign dmf_din_real = din_cxr_real[CMPY_OUTPUT_WIDTH-1 -: DATA_I_WIDTH];
|
|
assign dmf_din_imag = din_cxr_imag[CMPY_OUTPUT_WIDTH-1 -: DATA_I_WIDTH];
|
|
end
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// dmf start control
|
|
// -----------------------------------------------------------------------------
|
|
always @(posedge clk) begin
|
|
if (rst) begin
|
|
dmf_start <= 'b0;
|
|
end
|
|
else case (state_next)
|
|
STATE_FREQ_DMF: dmf_start <= (state_curr == STATE_FREQ_SW) ? ~dmf_start : dmf_start;
|
|
STATE_CH1 : dmf_start <= (state_curr == STATE_CH_SW ) ? ~dmf_start : dmf_start;
|
|
STATE_CH2 : dmf_start <= (state_curr == STATE_CH_SW ) ? ~dmf_start : dmf_start;
|
|
STATE_CH3 : dmf_start <= (state_curr == STATE_CH_SW ) ? ~dmf_start : dmf_start;
|
|
STATE_CH4 : dmf_start <= (state_curr == STATE_CH_SW ) ? ~dmf_start : dmf_start;
|
|
default: dmf_start <= dmf_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
|
|
(
|
|
.clk (clk ),
|
|
.rst (rst ),
|
|
|
|
.dmf_start (dmf_start ),
|
|
.dmf_busy (dmf_busy ),
|
|
|
|
.din_valid (dmf_din_valid ),
|
|
.din_real (dmf_din_real ),
|
|
.din_imag (dmf_din_imag ),
|
|
|
|
.peak_valid (dmf_peak_valid ),
|
|
.peak_value_abs (dmf_peak_value_abs ),
|
|
.peak_value_i (dmf_peak_value_i ),
|
|
.peak_value_q (dmf_peak_value_q ),
|
|
.peak_index (dmf_peak_index )
|
|
);
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// peak finding at different frequencies
|
|
// -----------------------------------------------------------------------------
|
|
reg max_peak_valid = 'b0;
|
|
reg [DATA_O_WIDTH-1 : 0] max_peak_value_abs = 'b0;
|
|
reg [DATA_O_WIDTH-1 : 0] max_peak_value_i = 'b0;
|
|
reg [DATA_O_WIDTH-1 : 0] max_peak_value_q = 'b0;
|
|
reg [$clog2(PRBS_POS-1) : 0] max_peak_index = 'b0;
|
|
|
|
// output result to port
|
|
assign freq_target_valid = max_peak_valid;
|
|
assign freq_target = max_peak_freq;
|
|
|
|
always @(posedge clk) begin
|
|
if (rst) begin
|
|
max_peak_valid <= 'b0;
|
|
max_peak_value_abs <= 'b0;
|
|
max_peak_value_i <= 'b0;
|
|
max_peak_value_q <= 'b0;
|
|
max_peak_index <= 'b0;
|
|
max_peak_freq <= 'b0;
|
|
end
|
|
else case (state_next)
|
|
STATE_IDLE: begin
|
|
max_peak_valid <= 'b0;
|
|
max_peak_value_abs <= 'b0;
|
|
max_peak_value_i <= 'b0;
|
|
max_peak_value_q <= 'b0;
|
|
max_peak_index <= 'b0;
|
|
max_peak_freq <= 'b0;
|
|
end
|
|
STATE_FREQ_SW: begin
|
|
if ((state_curr == STATE_FREQ_DMF) && (dmf_peak_value_abs > max_peak_value_abs)) begin
|
|
// at the end of [STATE_FREQ_DMF] state, check if peak is valid
|
|
max_peak_value_abs <= dmf_peak_value_abs;
|
|
max_peak_value_i <= dmf_peak_value_i ;
|
|
max_peak_value_q <= dmf_peak_value_q ;
|
|
max_peak_index <= dmf_peak_index ;
|
|
max_peak_freq <= freq_word ;
|
|
end
|
|
else begin
|
|
// hold otherwise
|
|
max_peak_value_abs <= max_peak_value_abs;
|
|
max_peak_value_i <= max_peak_value_i ;
|
|
max_peak_value_q <= max_peak_value_q ;
|
|
max_peak_index <= max_peak_index ;
|
|
max_peak_freq <= max_peak_freq ;
|
|
end
|
|
end
|
|
STATE_CH_SW: begin
|
|
if (state_curr == STATE_FREQ_DMF) begin
|
|
// indicate the target frequency word is found
|
|
max_peak_valid <= 1'b1;
|
|
end
|
|
else begin
|
|
max_peak_valid <= 'b0;
|
|
end
|
|
end
|
|
default: begin
|
|
max_peak_valid <= 'b0;
|
|
max_peak_value_abs <= max_peak_value_abs;
|
|
max_peak_value_i <= max_peak_value_i ;
|
|
max_peak_value_q <= max_peak_value_q ;
|
|
max_peak_index <= max_peak_index ;
|
|
max_peak_freq <= max_peak_freq ;
|
|
end
|
|
endcase
|
|
end
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// peak record for each channel
|
|
// -----------------------------------------------------------------------------
|
|
// reg [DATA_O_WIDTH-1 : 0] ch1_peak_value_abs = 'b0;
|
|
// reg [DATA_O_WIDTH-1 : 0] ch1_peak_value_i = 'b0;
|
|
// reg [DATA_O_WIDTH-1 : 0] ch1_peak_value_q = 'b0;
|
|
// reg [$clog2(PRBS_POS-1) : 0] ch1_peak_index = 'b0;
|
|
|
|
// reg [DATA_O_WIDTH-1 : 0] ch2_peak_value_abs = 'b0;
|
|
// reg [DATA_O_WIDTH-1 : 0] ch2_peak_value_i = 'b0;
|
|
// reg [DATA_O_WIDTH-1 : 0] ch2_peak_value_q = 'b0;
|
|
// reg [$clog2(PRBS_POS-1) : 0] ch2_peak_index = 'b0;
|
|
|
|
// reg [DATA_O_WIDTH-1 : 0] ch3_peak_value_abs = 'b0;
|
|
// reg [DATA_O_WIDTH-1 : 0] ch3_peak_value_i = 'b0;
|
|
// reg [DATA_O_WIDTH-1 : 0] ch3_peak_value_q = 'b0;
|
|
// reg [$clog2(PRBS_POS-1) : 0] ch3_peak_index = 'b0;
|
|
|
|
// reg [DATA_O_WIDTH-1 : 0] ch4_peak_value_abs = 'b0;
|
|
// reg [DATA_O_WIDTH-1 : 0] ch4_peak_value_i = 'b0;
|
|
// reg [DATA_O_WIDTH-1 : 0] ch4_peak_value_q = 'b0;
|
|
// reg [$clog2(PRBS_POS-1) : 0] ch4_peak_index = 'b0;
|
|
|
|
always @(posedge clk) begin
|
|
if (rst) begin
|
|
ch1_peak_valid <= 'b0;
|
|
ch1_peak_value_abs <= 'b0;
|
|
ch1_peak_value_i <= 'b0;
|
|
ch1_peak_value_q <= 'b0;
|
|
ch1_peak_index <= 'b0;
|
|
|
|
ch2_peak_valid <= 'b0;
|
|
ch2_peak_value_abs <= 'b0;
|
|
ch2_peak_value_i <= 'b0;
|
|
ch2_peak_value_q <= 'b0;
|
|
ch2_peak_index <= 'b0;
|
|
|
|
ch3_peak_valid <= 'b0;
|
|
ch3_peak_value_abs <= 'b0;
|
|
ch3_peak_value_i <= 'b0;
|
|
ch3_peak_value_q <= 'b0;
|
|
ch3_peak_index <= 'b0;
|
|
|
|
ch4_peak_valid <= 'b0;
|
|
ch4_peak_value_abs <= 'b0;
|
|
ch4_peak_value_i <= 'b0;
|
|
ch4_peak_value_q <= 'b0;
|
|
ch4_peak_index <= 'b0;
|
|
end
|
|
else case (state_curr)
|
|
STATE_CH_SW : begin
|
|
ch1_peak_valid <= ch1_peak_valid;
|
|
ch2_peak_valid <= ch2_peak_valid;
|
|
ch3_peak_valid <= ch3_peak_valid;
|
|
ch4_peak_valid <= ch4_peak_valid;
|
|
end
|
|
STATE_CH1 : begin
|
|
ch1_peak_valid <= (dmf_peak_valid) ? 1'b1 : ch1_peak_valid;
|
|
ch1_peak_value_abs <= (dmf_peak_valid) ? dmf_peak_value_abs : ch1_peak_value_abs;
|
|
ch1_peak_value_i <= (dmf_peak_valid) ? dmf_peak_value_i : ch1_peak_value_i ;
|
|
ch1_peak_value_q <= (dmf_peak_valid) ? dmf_peak_value_q : ch1_peak_value_q ;
|
|
ch1_peak_index <= (dmf_peak_valid) ? dmf_peak_index : ch1_peak_index ;
|
|
end
|
|
STATE_CH2 : begin
|
|
ch2_peak_valid <= (dmf_peak_valid) ? 1'b1 : ch2_peak_valid;
|
|
ch2_peak_value_abs <= (dmf_peak_valid) ? dmf_peak_value_abs : ch2_peak_value_abs;
|
|
ch2_peak_value_i <= (dmf_peak_valid) ? dmf_peak_value_i : ch2_peak_value_i ;
|
|
ch2_peak_value_q <= (dmf_peak_valid) ? dmf_peak_value_q : ch2_peak_value_q ;
|
|
ch2_peak_index <= (dmf_peak_valid) ? dmf_peak_index : ch2_peak_index ;
|
|
end
|
|
STATE_CH3 : begin
|
|
ch3_peak_valid <= (dmf_peak_valid) ? 1'b1 : ch3_peak_valid;
|
|
ch3_peak_value_abs <= (dmf_peak_valid) ? dmf_peak_value_abs : ch3_peak_value_abs;
|
|
ch3_peak_value_i <= (dmf_peak_valid) ? dmf_peak_value_i : ch3_peak_value_i ;
|
|
ch3_peak_value_q <= (dmf_peak_valid) ? dmf_peak_value_q : ch3_peak_value_q ;
|
|
ch3_peak_index <= (dmf_peak_valid) ? dmf_peak_index : ch3_peak_index ;
|
|
end
|
|
STATE_CH4 : begin
|
|
if ((state_next == STATE_CH_SW) || (state_next == STATE_IDLE)) begin
|
|
// when another dmf is needed, clear valid indicator
|
|
ch1_peak_valid <= 'b0;
|
|
ch2_peak_valid <= 'b0;
|
|
ch3_peak_valid <= 'b0;
|
|
ch4_peak_valid <= 'b0;
|
|
end
|
|
else begin
|
|
ch4_peak_valid <= (dmf_peak_valid) ? 1'b1 : ch4_peak_valid;
|
|
ch4_peak_value_abs <= (dmf_peak_valid) ? dmf_peak_value_abs : ch4_peak_value_abs;
|
|
ch4_peak_value_i <= (dmf_peak_valid) ? dmf_peak_value_i : ch4_peak_value_i ;
|
|
ch4_peak_value_q <= (dmf_peak_valid) ? dmf_peak_value_q : ch4_peak_value_q ;
|
|
ch4_peak_index <= (dmf_peak_valid) ? dmf_peak_index : ch4_peak_index ;
|
|
end
|
|
end
|
|
default : begin
|
|
ch1_peak_valid <= 'b0 ;
|
|
ch1_peak_value_abs <= ch1_peak_value_abs;
|
|
ch1_peak_value_i <= ch1_peak_value_i ;
|
|
ch1_peak_value_q <= ch1_peak_value_q ;
|
|
ch1_peak_index <= ch1_peak_index ;
|
|
|
|
ch2_peak_valid <= 'b0 ;
|
|
ch2_peak_value_abs <= ch2_peak_value_abs;
|
|
ch2_peak_value_i <= ch2_peak_value_i ;
|
|
ch2_peak_value_q <= ch2_peak_value_q ;
|
|
ch2_peak_index <= ch2_peak_index ;
|
|
|
|
ch3_peak_valid <= 'b0 ;
|
|
ch3_peak_value_abs <= ch3_peak_value_abs;
|
|
ch3_peak_value_i <= ch3_peak_value_i ;
|
|
ch3_peak_value_q <= ch3_peak_value_q ;
|
|
ch3_peak_index <= ch3_peak_index ;
|
|
|
|
ch4_peak_valid <= 'b0 ;
|
|
ch4_peak_value_abs <= ch4_peak_value_abs;
|
|
ch4_peak_value_i <= ch4_peak_value_i ;
|
|
ch4_peak_value_q <= ch4_peak_value_q ;
|
|
ch4_peak_index <= ch4_peak_index ;
|
|
end
|
|
endcase
|
|
end
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// indicate dmf on all 4 channels are finished
|
|
// -----------------------------------------------------------------------------
|
|
reg ch_peak_valid_buf = 'b0;
|
|
|
|
assign ch_peak_valid = ch_peak_valid_buf;
|
|
|
|
always @(posedge clk) begin
|
|
if (rst) begin
|
|
ch_peak_valid_buf <= 'b0;
|
|
end
|
|
else if ((state_curr == STATE_CH4) && dmf_peak_valid) begin
|
|
ch_peak_valid_buf <= 1'b1;
|
|
end
|
|
else begin
|
|
ch_peak_valid_buf <= 'b0;
|
|
end
|
|
end
|
|
|
|
endmodule
|