`timescale 1ns / 1ps ////////////////////////////////////////////////////////////////////////////////// // Company: // Engineer: // _ _ ____ // | | ___ ___| |__ / ___| __ _ ___ // _ | |/ _ \/ __| '_ \ | | _ / _` |/ _ \ // | |_| | (_) \__ \ | | | | |_| | (_| | (_) | // \___/ \___/|___/_| |_| \____|\__,_|\___/ // // Create Date: 2022/03/23 20:17:56 // Last Modified Date: 2023/03/10 15:41 // Design Name: // Module Name: tx_qpsk // Project Name: // Target Devices: // Tool Versions: // Description: // // Dependencies: // // Revision: // Revision 0.01 - File Created // 20230228-1541 - 修改不扫频、无基带频偏时 [baseband_i/q] 的数据输出,原来从 // 乘法器引出,会导致频率为 0 时输出数据错误,现在直接从基带数据引出。 // 20230310-1542 - 增加输入参数,发送数据更改为直接由参数输入,发送方波改为 8 // 倍过采样,发送双音信号中 1MHz 信号的频率控制字可以根据参数中的时钟频率计 // 算得出。 // Additional Comments: // ////////////////////////////////////////////////////////////////////////////////// module tx_qpsk #( parameter DATA_WIDTH = 12, // need to be <= 16 parameter BIT_DATA = 128'b00000011000010100011110010001011001110101001111101000011100010010011011010110111101100011010010111011100110010101011111110000001, // inverse pseudorandom bit sequence, [0,flip(PRSB7)] parameter BIT_DATA_LEN = 128, parameter SQUARE_INTERP = 8, parameter CLK_FREQ = 245760000 // in Hertz ) ( input clk , input rst , output [DATA_WIDTH-1:0] tx_data_i , output [DATA_WIDTH-1:0] tx_data_q , // debug ports input [ 1:0] waveform_sel , input sweep_on , input [31:0] freq_set , input [ 1:0] iq_sel , input [ 3:0] bb_rshift ); localparam RCOS_INTERP = 64; localparam INTERP = (RCOS_INTERP > SQUARE_INTERP) ? RCOS_INTERP : SQUARE_INTERP; localparam FREQ_WORD_1M = 64'd2**64'd32*64'd1000000/CLK_FREQ; //////////////////////////////////////////////////////////////////////////////// ///////////////////////////////// Data Counter ///////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // symbol & interpolation counter reg [$clog2(INTERP*BIT_DATA_LEN)-1 : 0] cnt = 'b0; always @(posedge clk) begin if (rst) begin cnt <= 'b0; end else begin cnt <= (cnt == INTERP*BIT_DATA_LEN - 1) ? 'b0 : cnt + 16'b1; end end //////////////////////////////////////////////////////////////////////////////// ////////////////////// Root-Rasied Cosine Shaping Filter /////////////////////// //////////////////////////////////////////////////////////////////////////////// // address reg [$clog2(BIT_DATA_LEN)-1 : 0] hrc_data_addr_i = 'b0; reg [$clog2(BIT_DATA_LEN)-1 : 0] hrc_data_addr_q = 'b1; always @(posedge clk) begin if (rst) begin hrc_data_addr_i <= 'b0; hrc_data_addr_q <= 'b1; end else begin hrc_data_addr_i <= {cnt[$clog2(RCOS_INTERP) +: ($clog2(BIT_DATA_LEN)-1)],1'b0}; hrc_data_addr_q <= {cnt[$clog2(RCOS_INTERP) +: ($clog2(BIT_DATA_LEN)-1)],1'b1}; end end // bit data reg hrc_data_i = 'b0; reg hrc_data_q = 'b0; always @(posedge clk) begin if (rst) begin hrc_data_i <= 'b0; hrc_data_q <= 'b0; end else begin hrc_data_i <= BIT_DATA[hrc_data_addr_i]; hrc_data_q <= BIT_DATA[hrc_data_addr_q]; end end // polarization wire [ 7:0] hrc_data_polar_i; wire [ 7:0] hrc_data_polar_q; assign hrc_data_polar_i = {{7{hrc_data_i}},1'b1}; assign hrc_data_polar_q = {{7{hrc_data_q}},1'b1}; // root raised cosine shaping-filter wire [23:0] hrc_wave_i; wire [23:0] hrc_wave_q; hrc_64_12_025 u_hrc_i ( .aclk (clk ), // input wire aclk .s_axis_data_tvalid (~rst ), // input wire s_axis_data_tvalid .s_axis_data_tready ( ), // output wire s_axis_data_tready .s_axis_data_tdata (hrc_data_polar_i ), // input wire [7 : 0] s_axis_data_tdata .m_axis_data_tvalid ( ), // output wire m_axis_data_tvalid .m_axis_data_tdata (hrc_wave_i ) // output wire [23 : 0] m_axis_data_tdata ); hrc_64_12_025 u_hrc_q ( .aclk (clk ), // input wire aclk .s_axis_data_tvalid (~rst ), // input wire s_axis_data_tvalid .s_axis_data_tready ( ), // output wire s_axis_data_tready .s_axis_data_tdata (hrc_data_polar_q ), // input wire [7 : 0] s_axis_data_tdata .m_axis_data_tvalid ( ), // output wire m_axis_data_tvalid .m_axis_data_tdata (hrc_wave_q ) // output wire [23 : 0] m_axis_data_tdata ); //////////////////////////////////////////////////////////////////////////////// //////////////////////////// Square Shaping Filter ///////////////////////////// //////////////////////////////////////////////////////////////////////////////// // address reg [$clog2(BIT_DATA_LEN)-1 : 0] hsq_data_addr = 'b0; always @(posedge clk) begin if (rst) begin hsq_data_addr <= 'b0; end else hsq_data_addr <= {cnt[$clog2(SQUARE_INTERP) +: $clog2(BIT_DATA_LEN)]}; end // bit data reg hsq_data = 'b0; always @(posedge clk) begin if (rst) begin hsq_data <= 'b0; end else begin hsq_data <= BIT_DATA[hsq_data_addr]; end end // square shaping-filter reg [17:0] hsq_wave_i ='b0; reg [17:0] hsq_wave_q ='b0; always @(posedge clk) begin if (rst) begin hsq_wave_i <= 'b0; hsq_wave_q <= 'b0; end else begin hsq_wave_i <= hsq_data ? 18'b01_1111_1111_1111_1111 : 18'b10_0000_0000_0000_0001; hsq_wave_q <= hsq_data ? 18'b10_0000_0000_0000_0001 : 18'b01_1111_1111_1111_1111; end end //////////////////////////////////////////////////////////////////////////////// /////////////////////////////// Two Tone Siganl //////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // cosine wave with freq of 1 MHz wire signed [7:0] cos_1m; wire signed [7:0] sin_1m; dds_32b_8b u_dds_1m ( .aclk (clk ), // input wire aclk .s_axis_config_tvalid (1'b1 ), // input wire s_axis_config_tvalid .s_axis_config_tdata (FREQ_WORD_1M[31:0] ), // input wire [31 : 0] s_axis_config_tdata .m_axis_data_tvalid ( ), // output wire m_axis_data_tvalid .m_axis_data_tdata ({sin_1m,cos_1m} ) // output wire [15 : 0] m_axis_data_tdata ); // two-tone signal reg signed [16:0] two_tone_wave_i = 'b0; reg signed [16:0] two_tone_wave_q = 'b0; always @(posedge clk) begin if (rst) begin two_tone_wave_i <= 'b0; two_tone_wave_q <= 'b0; end else begin two_tone_wave_i <= 17'b0_0111_1111_1111_1111 + {cos_1m[7],cos_1m,8'd0}; two_tone_wave_q <= 17'b0 + {sin_1m[7],sin_1m,8'd0}; end end //////////////////////////////////////////////////////////////////////////////// ///////////////////// Waveform Selection & Hopping Control ///////////////////// //////////////////////////////////////////////////////////////////////////////// // baseband signal select reg [17:0] baseband_sel_i = 'b0; reg [17:0] baseband_sel_q = 'b0; always @(posedge clk) begin if (rst) begin baseband_sel_i <= 'b0; baseband_sel_q <= 'b0; end else begin case (waveform_sel[1:0]) 2'd0: begin baseband_sel_i <= {hrc_wave_i[16:0],1'b0}; baseband_sel_q <= {hrc_wave_q[16:0],1'b0}; end 2'd1: begin baseband_sel_i <= hsq_wave_i[17:0]; baseband_sel_q <= hsq_wave_q[17:0]; end 2'd2: begin baseband_sel_i <= 18'b01_1111_1111_1111_1111; baseband_sel_q <= 18'b0; end 2'd3: begin baseband_sel_i <= {two_tone_wave_i[16:0],1'b0}; baseband_sel_q <= {two_tone_wave_q[16:0],1'b0}; end default: begin baseband_sel_i <= {hrc_wave_i[16:0],1'b0}; baseband_sel_q <= {hrc_wave_q[16:0],1'b0}; end endcase end end // hopping control, 3750hop/s reg [23:0] sweep_rom_addr = 'b0; wire [31:0] freq_sweep; always @(posedge clk) begin if (rst) begin sweep_rom_addr <= 'b0; end else if (sweep_on) begin sweep_rom_addr <= sweep_rom_addr + 24'b1; end else begin sweep_rom_addr <= 'b0; end end hop_rom u_sweep_rom ( .clka (clk ), // input wire clka .addra (sweep_rom_addr[23:16] ), // input wire [7 : 0] addra .douta (freq_sweep ) // output wire [31 : 0] douta ); // choose the actual frequency word reg [31:0] freq_word = 'b0; always @(posedge clk) begin if (rst) begin freq_word <= 'b0; end else if (sweep_on) begin // frequency sweep freq_word <= freq_sweep; end else begin // normal operation freq_word <= freq_set; end end // baseband frequency shift carrier generation wire [ 7:0] cos_dds; wire [ 7:0] sin_dds; reg [ 7:0] cos = 'b0; reg [ 7:0] sin = 'b0; dds_32b_8b u_dds_bb_shift ( .aclk (clk ), // input wire aclk .s_axis_config_tvalid (~rst ), // input wire s_axis_config_tvalid .s_axis_config_tdata (freq_word ), // input wire [31 : 0] s_axis_config_tdata .m_axis_data_tvalid ( ), // output wire m_axis_data_tvalid .m_axis_data_tdata ({sin_dds,cos_dds}) // output wire [31 : 0] m_axis_data_tdata ); always @(posedge clk) begin if (rst) begin cos <= 'b0; sin <= 'b0; end else if (freq_word == 'b0) begin // to make sure the carriers stay at the right position when freq = 0 cos <= 8'b0111_1111; sin <= 8'b0111_1111; end else begin cos <= cos_dds; sin <= sin_dds; end end // baseband frequency shift modulation wire [47:0] mod_cmpy_a; wire [15:0] mod_cmpy_b; wire [63:0] mod_cmpy_out; wire [26:0] baseband_mult_i; wire [26:0] baseband_mult_q; assign mod_cmpy_a = {6'b0,baseband_sel_q,6'b0,baseband_sel_i}; assign mod_cmpy_b = {-sin,cos}; assign baseband_mult_i = mod_cmpy_out[58:32]; assign baseband_mult_q = mod_cmpy_out[26: 0]; cmpy_18x8 u_cmpy_18x8 ( .aclk (clk ), // input wire aclk .s_axis_a_tvalid (1'b1 ), // input wire s_axis_a_tvalid .s_axis_a_tdata (mod_cmpy_a ), // input wire [47 : 0] s_axis_a_tdata .s_axis_b_tvalid (1'b1 ), // input wire s_axis_b_tvalid .s_axis_b_tdata (mod_cmpy_b ), // input wire [15 : 0] s_axis_b_tdata .m_axis_dout_tvalid ( ), // output wire m_axis_dout_tvalid .m_axis_dout_tdata (mod_cmpy_out ) // output wire [63 : 0] m_axis_dout_tdata ); // truncate to 16-bit reg signed [15:0] baseband_i = 'b0; reg signed [15:0] baseband_q = 'b0; always @(posedge clk) begin if (rst) begin baseband_i <= 'b0; baseband_q <= 'b0; end else begin case (waveform_sel[1:0]) 2'd0: begin // scale rasied-cosine-shaped bpsk signal by 1.125 or 1.625 baseband_i <= (sweep_on == 0 && freq_set == 0) ? (baseband_sel_i[17 -: 16] + {{3{baseband_sel_i[17]}},baseband_sel_i[17 -: 13]}) : (baseband_mult_i[25:10] + {baseband_mult_i[25],baseband_mult_i[25:11]} + {{3{baseband_mult_i[25]}},baseband_mult_i[25:13]}); baseband_q <= (sweep_on == 0 && freq_set == 0) ? (baseband_sel_q[17 -: 16] + {{3{baseband_sel_q[17]}},baseband_sel_q[17 -: 13]}) : (baseband_mult_q[25:10] + {baseband_mult_q[25],baseband_mult_q[25:11]} + {{3{baseband_mult_q[25]}},baseband_mult_q[25:13]}); end 2'd1: begin baseband_i <= (sweep_on == 0 && freq_set == 0) ? baseband_sel_i[17 -: 16] : (baseband_mult_i[25:10] + {{2{baseband_mult_i[25]}},baseband_mult_i[25:12]}); baseband_q <= (sweep_on == 0 && freq_set == 0) ? baseband_sel_q[17 -: 16] : (baseband_mult_q[25:10] + {{2{baseband_mult_q[25]}},baseband_mult_q[25:12]}); end 2'd2: begin baseband_i <= baseband_mult_i[24:9]; baseband_q <= baseband_mult_q[24:9]; end 2'd3: begin baseband_i <= (sweep_on == 0 && freq_set == 0) ? baseband_sel_i[17 -: 16] : baseband_mult_i[24:9]; baseband_q <= (sweep_on == 0 && freq_set == 0) ? baseband_sel_q[17 -: 16] : baseband_mult_q[24:9]; end default: begin // scale rasied-cosine-shaped bpsk signal by 1.125 baseband_i <= baseband_mult_i[25:10] + {{3{baseband_mult_i[25]}},baseband_mult_i[25:13]}; baseband_q <= baseband_mult_q[25:10] + {{3{baseband_mult_q[25]}},baseband_mult_q[25:13]}; end endcase end end // iq selection & baseband right shift reg signed [15:0] baseband_data_i = 'b0; reg signed [15:0] baseband_data_q = 'b0; always @(posedge clk) begin if (rst) begin baseband_data_i <= 'b0; baseband_data_q <= 'b0; end else begin // inphase selection if (iq_sel[1]) begin baseband_data_i <= baseband_i >>> bb_rshift; end else begin baseband_data_i <= 'b0; end // quadrature selection if (iq_sel[0]) begin baseband_data_q <= baseband_q >>> bb_rshift; end else begin baseband_data_q <= 'b0; end end end // output data to ports assign tx_data_i = baseband_data_i[15 -: DATA_WIDTH]; assign tx_data_q = baseband_data_q[15 -: DATA_WIDTH]; endmodule `timescale 1ns / 1ps ////////////////////////////////////////////////////////////////////////////////// // Company: // Engineer: // // Create Date: 2020/12/08 12:43:46 // Design Name: // Module Name: tb_tx_qpsk // Project Name: // Target Devices: // Tool Versions: // Description: // // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // ////////////////////////////////////////////////////////////////////////////////// // module tb_tx_qpsk; // reg clk = 1'b0 ; // reg rst = 1'b1 ; // reg tx_on = 1'b1 ; // reg [31:0] freq_set ; // reg [ 1:0] waveform_sel = 2'b00; // reg [ 1:0] iq_sel = 2'b11 ; // wire [11:0] tx_data_i ; // wire [11:0] tx_data_q ; // reg [ 3:0] bb_rshift = 5'b0 ; // tx_qpsk u_tx_qpsk // ( // .clk (clk ), // .rst (rst ), // .tx_data_i (tx_data_i ), // .tx_data_q (tx_data_q ), // .waveform_sel (waveform_sel ), // .sweep_on (1'b0 ), // // .freq_set (32'd447392426 ), // .freq_set (32'd0 ), // .iq_sel (iq_sel ), // .bb_rshift (bb_rshift ) // ); // initial begin // #102 // rst = 1'b0; // // freq_set = 32'd873813333; // // waveform_sel = 2'b00; // // bb_rshift = 5'd0; // // #5000 // // bb_rshift = 5'd1; // // #5000 // // bb_rshift = 5'd2; // // #5000 // // bb_rshift = 5'd3; // // #5000 // // bb_rshift = 5'd4; // // #5000 // // bb_rshift = 5'd5; // // #5000 // // bb_rshift = 5'd6; // // #5000 // // bb_rshift = 5'd7; // // #5000 // // bb_rshift = 5'd8; // // #5000 // // bb_rshift = 5'd9; // // #5000 // // bb_rshift = 5'd14; // // #5000 // // bb_rshift = 5'd15; // // #5000 // // bb_rshift = 5'd16; // // freq_set = 32'd0; // // func_sel = 3'b010; // // #7777 // // freq_set = 32'd536870912; // // func_sel = 3'b100; // // #7777 // // freq_set = 32'd0; // // func_sel = 3'b100; // // #7777 // // freq_set = 32'd536870912; // // func_sel = 3'b010; // // #7777 // // freq_set = 32'd0; // // func_sel = 3'b010; // // freq_set = 32'd536870912; // // iq_sel = 2'b00; // // #7777 // // freq_set = 32'd536870912; // // iq_sel = 2'b01; // // #7777 // // freq_set = 32'd536870912; // // iq_sel = 2'b10; // // #7777 // // freq_set = 32'd536870912; // // iq_sel = 2'b11; // end // always #5 clk = ~clk; // // integer i; // // integer w_file; // // initial begin // // w_file = $fopen("D:\\qpsk_josh.txt","w"); // // for (i = 0; i <= 35000; i = i + 1) begin // // wait(clk); // // #10; // // $fdisplay(w_file,"%d %d",$signed(tx_data_i), // // $signed(tx_data_q)); // // if (i == 35000) begin // // $fclose(w_file); // // $stop; // // end // // end // // end // endmodule