DQPSK调制

一 码型变换

该部分主要参考《数字调制解调计数的MATLAB与FPGA实现》第七章的内容,将输入单比特数据进行串并转换、差分编码、极性变换以及插值。区别主要在于,书中系统时钟是输入数据速率的4倍,使用8倍插值。为了方便使用,让输入数据时钟等于系统时钟clk,差分变换后是数据时钟为2*clk,2倍插值后要求的数据时钟为clk。

1.软件设计

软件代码如下:

module CodeTrans(
    input rst_n,    //复位信号,低电平有效
    input sclk, //采样频率
    input din,  //输入串行信号
    
//    output reg [1:0]ab,
//    output wire [1:0]cd,
    output signed[1:0] di,  //串并转换、差分编码、插值处理后的同相支路信号
    output signed[1:0] dq   //串并转换、差分编码、插值处理后的正交支路信号
    );

//串并转换,sclk速率的单比特数据转换为sclk/2速率的双比特码元
reg [1:0]ab;    //双比特码元
reg cnt;
reg dint;
always@(posedge sclk or negedge rst_n) begin
    if(!rst_n) begin
        ab <= 2'd0;
        cnt <= 1'b0;
        dint <= 1'b0;
    end
    else begin
        cnt <= cnt + 1'b1;
        if(!cnt)
            dint <= din;
        else
            ab <= {din,dint};
    end
end

//绝对码ab变成相对码cd,码元时钟由cnt给出
wire [1:0]cd;
ab2cd ab2cd_inst(
    .rst_n(rst_n),
    .clk(cnt),
    .ab(ab),
    .cd(cd)
);

//对相对码进行插值和极性变换
reg [1:0]dit,dqt;
always@(posedge sclk or negedge rst_n) begin
    if(!rst_n) begin
        dit <= 1'b0;
        dqt <= 1'b0;
    end
    else begin
        if(!cd[0])
            dit <= 2'b01;   //+1
        else
            dit <= 2'b11;   //-1
        if(!cd[1])
            dqt <= 2'b01;
        else
            dqt <= 2'b11;
    end
end
assign di = dit;
assign dq = dqt;

endmodule

//这是ab2cd.v文件的程序清单
module ab2cd (
	rst_n,clk,ab,
	cd); 
	
	input		rst_n;       //复位信号,低电平有效
	input		clk;       //FPGA系统时钟
	input	 [1:0]	ab;  //输入的绝对码数据
	output [1:0]	cd;  //转换后的相对码数据

	reg [1:0] ef;
	always @(posedge clk or negedge rst_n)
		if (!rst_n)
		   begin
			   ef <= 2'd0;
			end
		else
			begin
				if ((ab==2'b10) && (ef==2'b00))
					ef <= 2'b10;
				else if ((ab==2'b10) && (ef==2'b10))
					ef <= 2'b11;
				else if ((ab==2'b10) && (ef==2'b11))
					ef <= 2'b01;
				else if ((ab==2'b10) && (ef==2'b01))
					ef <= 2'b00;


				else if ((ab==2'b01) && (ef==2'b00))
					ef <= 2'b01;
				else if ((ab==2'b01) && (ef==2'b10))
					ef <= 2'b00;
				else if ((ab==2'b01) && (ef==2'b11))
					ef <= 2'b10;
				else if ((ab==2'b01) && (ef==2'b01))
					ef <= 2'b11;

				else if ((ab==2'b11) && (ef==2'b00))
					ef <= 2'b11;
				else if ((ab==2'b11) && (ef==2'b10))
					ef <= 2'b01;
				else if ((ab==2'b11) && (ef==2'b11))
					ef <= 2'b00;
				else if ((ab==2'b11) && (ef==2'b01))
					ef <= 2'b10;

				else if ((ab==2'b00) && (ef==2'b00))
					ef <= 2'b00;
				else if ((ab==2'b00) && (ef==2'b10))
					ef <= 2'b10;
				else if ((ab==2'b00) && (ef==2'b11))
					ef <= 2'b11;
				else if ((ab==2'b00) && (ef==2'b01))
					ef <= 2'b01;
			end
			
	assign cd = ef;
	
endmodule
	

2.软件仿真

首先生成单比特数据流文件,使用matlab将8bit类型0-255整体转换成二进制数据保存在txt文件中,参考的链接和代码如下

matlab生成数据以二进制数据格式写入txt文件中_者乎之类的的博客-优快云博客_matlab生成二进制txt文件

QB=8;
data = (0:255);
fid=fopen('E:/.../.../.../din.txt','w');
for k=1:length(data)
    b_data=dec2bin(data(k),QB);
    for q=1:QB
        if b_data(q)=='1'
            tb=1;
        else
            tb=0;
        end
        fprintf(fid,'%d',tb);
        fprintf(fid,'\r\n');
    end
end
fclose(fid);

然后编写testbanch,读取二进制数据并输出,关于如何使用verilog读取文件,可以参考如下链接

Verilog读写文件 - 全栈程序员必看 (javaforall.cn)

Verilog中的文件的读取和写入 - match_boy - 博客园 (cnblogs.com)

 代码如下

`timescale 1ns / 1ps

module tb_CodeTrans();

reg clk;
reg rst_n;
reg din;
integer fp_r;
reg [15:0]cnt;

initial begin
    clk = 1'b1;
    forever
    #10
    clk = ~clk;
end

initial begin
    rst_n = 1'b0;
    #40
    rst_n = 1'b1;
end

initial begin
    fp_r = $fopen("E:/.../.../.../din.txt","r");
end

always@(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        din <= 1'b0;
        cnt <= 16'd0;
    end
    else if(cnt < 16'd2048) begin
        $fscanf(fp_r,"%b",din);
        cnt <= cnt +1;
    end
    else
        $fclose(fp_r);
end

//wire [1:0]ab;
//wire [1:0]cd;
wire [1:0]di;
wire [1:0]dq;

CodeTrans CodeTrans_inst(
    .rst_n(rst_n),    //复位信号,低电平有效
    .sclk(clk), //采样频率
    .din(din),  //输入串行信号
    
//    .ab(ab),
//    .cd(cd),
    .di(di),  //串并转换、差分编码、插值处理后的同相支路信号
    .dq(dq)   //串并转换、差分编码、插值处理后的正交支路信号
    );

endmodule

仿真结果如下

二 FIR滤波

1.Vivado FIR IP核的参考链接

关于Vivado FIR IP核的使用可以参考以下链接

Xilinx FIR IP使用方法总结_wuzhirui志锐的博客-优快云博客_fir ip核 抽取功能

FPGA数字信号处理(五)Vivado FIR IP核实现_FPGADesigner的博客-优快云博客_数字信号处理的fpga实现

vivado FIR ip核使用,vivado FIR滤波器设计_bjffn的博客-优快云博客_vivado的fir滤波器ip核用法

关于插值的相关链接可以参考

数字信号处理基础----插值、抽取滤波器_black_pigeon的博客-优快云博客_插值滤波器

 FPGA信号处理系列文章——FIR半带插值滤波器_十年老鸟的博客-优快云博客_fir插值滤波器

 FPGA实现信号n倍插值(内插0)_ML__LM的博客-优快云博客_fpga插值滤波

2.MATLAB的滤波器设计工具生成滤波器系数coe文件

首先使用MATLAB的滤波器设计工具(调用filterDesigner命令),生成所需滤波器参数。在设计滤波器界面选择响应类型为“升余弦”,幅值设置为平方根。滤波器阶数设置的不宜过大,否者会占用FPGA太多DSP资源,这里设置为32。根号升余弦滚降系数设置为0.25,频率设置中,Fs对应的是输入信号采样率,这里设置为20MHz;Fc是指3dB带宽频率,假设输入符号速率为fs=1/Ts=10MHz,则根号升余弦滤波器的带宽为(1+alpha)/2Ts=6.25,Fc对应的是(1-alpha)/2Ts和(1+alpha)/2Ts中点的位置为Fc=fs/2=5MHz。缩放通带的设置不清楚作用,先不管。

 生成滤波器后打开“设置量化参数”的界面,如下图所示,滤波器算法选择“定点”滤波器精度设置为全,系数字长设置为14,这样2bit输入对应16bit输出,符合后续设计要求,勾选最佳精度小数长度。点击“应用”后,可以观察到量化后的频谱基本贴合参考频谱。

 最后点击工具栏中的“目标->XILINX系数(.COE)文件”,就可以生成根号升余弦滤波器系数的coe文件,其中滤波器系数使用16进制表示。

3.设置FIR IP核

首先使用COE文件加载滤波器系数,左侧可以看到滤波器响应和MATLAB滤波器设计工具中的一致。由于插值是使用verilog代码实现的,所以这里将滤波器类型设置为单速率。

 由于需要对I路和Q路同时滤波,所以设置并行通道路径数量为2。设置采样率和时钟频率一致。

 滤波器系数位宽设置要和MATLAB中设置的一致,由于导入的是16进制滤波器系数,这里的量化方式为默认的“整数系数”。输入信号位宽为2bit,输出选择全精度,输出信号位宽为16bit,到时候要舍弃后4位。

 FIR滤波器的实现方式和接口则保持默认。

4.设置Data Width Converter IP核

由于后续需要替换掉官方HDL工程里的dac_dma模块,因此需要先通过ILA分析util_upack2模块,观察s_axis和fifo_rd_data_x接口的工作方式。

这里在进行调试的时候需要注意,如果直接进行debug会报出warning并且不显示波形,

WARNING: [Labtools 27-3361] The debug hub core was not detected.

因此需要先运行软件部分,完成ad9361初始化(因为ILA使用的时钟是分频后的时钟,在初始化完成以前,该信号不是free running clock)以后再打开Hardware Manager并Open Target,这样就能正确观察波形了。可以参考以下链接

WARNING: [Labtools 27-3361] The debug hub core was not detected. (xilinx.com)

从时序图中可以看出, 隔一个时钟周期dac_upack才准备好接收信号。由于使用的是单发单收,因此只有data_0和data_1通道使能。先输出的是低32位数据,后输出高32位数据。与下面链接的仿真结果一致:

使用官方的HDL试验ADC数据 之一_mcupro的博客-优快云博客

由于AD9361的dac_upack IP核输入的是64位数据,并且在单发单收的情况下,先发送低32位,后发送高32位数据,而FIR IP核的输出是32位的,因此需要AXI4-Stream Data Width Converter IP核将32位数转换为64位数,先输入的数据放在低位,后输入的数据放在高位,符合先输入先发送的顺序。IP核的配置如下,只需要设置位宽,其余默认

5.代码编写与仿真

例化CodeTrans、Fir Complier和AXI4-Stream Data Width Converter IP核,并将fir的s_axis_data_tvalid设置为1,让fir的输入数据一直有效,将converter的m_axis_tready设置为1,让converter可以一直输出数据。使用位拼接将di和dq拼成16bit位宽的data_tdata,其中di放在高8位,dq放在低8位,fir的输出数据作为converter的输入数据,整体代码如下:

`timescale 1ns / 1ps

module dqpsk_mod(
    input rst_n,    //复位信号,低电平有效
    input sclk, //采样频率
    input din,  //输入串行信号
    
    output [1:0]di,
    output [1:0]dq,
    
    output fir_s_axis_data_tvalid,
    output fir_s_axis_data_tready,
    output [15:0]fir_s_axis_data_tdata,
    output fir_m_axis_data_tvalid,
    output [31:0]fir_m_axis_data_tdata,
    
    output converter_s_axis_tready,
    output converter_m_axis_tvalid,
    output converter_m_axis_tready,
    output [63:0]converter_m_axis_tdata
    );

//wire [1:0]di;
//wire [1:0]dq;

CodeTrans CodeTrans_inst(
    .rst_n(rst_n),    //复位信号,低电平有效
    .sclk(sclk), //采样频率
    .din(din),  //输入串行信号
    .di(di),  //串并转换、差分编码、插值处理后的同相支路信号
    .dq(dq)   //串并转换、差分编码、插值处理后的正交支路信号
    );

//wire s_axis_data_tvalid;
assign fir_s_axis_data_tvalid=1'b1;
//wire [15:0]s_axis_data_tdata;
assign fir_s_axis_data_tdata={6'd0,di,6'd0,dq};
fir_compiler_0 fir_compiler_inst (
  .aclk(sclk),                              // input wire aclk
  .s_axis_data_tvalid(fir_s_axis_data_tvalid),  // input wire s_axis_data_tvalid
  .s_axis_data_tready(fir_s_axis_data_tready),  // output wire s_axis_data_tready
  .s_axis_data_tdata(fir_s_axis_data_tdata),    // input wire [15 : 0] s_axis_data_tdata
  .m_axis_data_tvalid(fir_m_axis_data_tvalid),  // output wire m_axis_data_tvalid
  .m_axis_data_tdata(fir_m_axis_data_tdata)    // output wire [31 : 0] m_axis_data_tdata
);

//wire s_axis_data_tvalid;
assign converter_m_axis_tready=1'b1;
axis_dwidth_converter_0 axis_dwidth_converter_inst (
  .aclk(sclk),                    // input wire aclk
  .aresetn(rst_n),              // input wire aresetn
  .s_axis_tvalid(fir_m_axis_data_tvalid),  // input wire s_axis_tvalid
  .s_axis_tready(converter_s_axis_tready),  // output wire s_axis_tready
  .s_axis_tdata(fir_m_axis_data_tdata),    // input wire [31 : 0] s_axis_tdata
  .m_axis_tvalid(converter_m_axis_tvalid),  // output wire m_axis_tvalid
  .m_axis_tready(converter_m_axis_tready),  // input wire m_axis_tready
  .m_axis_tdata(converter_m_axis_tdata)    // output wire [63 : 0] m_axis_tdata
);
endmodule

测试代码如下,需要产生频率为20MHz的时钟信号,从单比特数据流文件中读取串行数据,代码如下:

`timescale 1ns / 1ps

module tb_dqpsk_mod();

reg clk;
reg rst_n;
reg din;
integer fp_r;
reg [15:0]cnt;

//20MHz时钟
initial begin
    clk = 1'b1;
    forever
    #25
    clk = ~clk;
end

initial begin
    rst_n = 1'b0;
    #40
    rst_n = 1'b1;
end

initial begin
    fp_r = $fopen("E:/VivadoProject/2022/code_trans/din.txt","r");
end

always@(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        din <= 1'b0;
        cnt <= 16'd0;
    end
    else if(cnt < 16'd2048) begin
        $fscanf(fp_r,"%b",din);
        cnt <= cnt +1;
    end
    else
        $fclose(fp_r);
end

wire [1:0]di;
wire [1:0]dq;
wire fir_s_axis_data_tvalid;
wire fir_s_axis_data_tready;
wire [15:0]fir_s_axis_data_tdata;
wire fir_m_axis_data_tvalid;
wire [31:0]fir_m_axis_data_tdata;
wire [15:0]fil_di;
wire [15:0]fil_dq;

wire converter_s_axis_tready;
wire converter_m_axis_tvalid;
wire converter_m_axis_tready;
wire [63:0]converter_m_axis_tdata;

assign fil_di=fir_m_axis_data_tdata[31:16];
assign fil_dq=fir_m_axis_data_tdata[15:0];

dqpsk_mod dqpsk_mod_inst(
    .rst_n(rst_n),    //复位信号,低电平有效
    .sclk(clk), //采样频率
    .din(din),  //输入串行信号
    
    .di(di),
    .dq(dq),
    
    .fir_s_axis_data_tvalid(fir_s_axis_data_tvalid),
    .fir_s_axis_data_tready(fir_s_axis_data_tready),
    .fir_s_axis_data_tdata(fir_s_axis_data_tdata),
    .fir_m_axis_data_tvalid(fir_m_axis_data_tvalid),
    .fir_m_axis_data_tdata(fir_m_axis_data_tdata),
    
    .converter_s_axis_tready(converter_s_axis_tready),
    .converter_m_axis_tvalid(converter_m_axis_tvalid),
    .converter_m_axis_tready(converter_m_axis_tready),
    .converter_m_axis_tdata(converter_m_axis_tdata)
    );
endmodule

仿真的时候遇到一个问题,直接使用行为级仿真的话,仿真的时候会卡住,并且报warning,经查由于使用了fir IP核,需要先综合,再执行综合后功能仿真"Run Post-Synthesis Functional Simulation",参考以下链接:

【FPGA_004】用vivado自带仿真工具,仿真FFTip核时,一直失败,停在检查点_刻一的博客-优快云博客

仿真结果如下,其中fil_di[15:0]是fir输出的高16位,表示滤波后的I路数据,fil_dq[15:0]是fir输出的低16位,表示滤波后的Q路数据。可以看出,滤波后的数据能与滤波前差分信号对应。fil_di[15:4]是I路数据的前12位,其波形基本与截取前的一致,说明截取对精度影响不大。滤波器输出信号可以作为AD9361的输入信号。

再观察converter输出的信号,FIR先输出的32位数位于converter输出数据的低32位,后输出的位于高32位,符合前面的要求。但是为了能使converter的m_axis_tvalid信号和upack的s_axis_valid信号对齐,可能需要将converter的m_axis_tvalid信号取反

三 整体DQPSK TX设计

1.修改官方HDL工程

为了减小占用的FPGA资源,对ADI官方给出的AD9361+ZC706的HDL工程进行修改。修改时注意将make后的工程另存为新工程,可以对该新工程进行修改并且可以正确生成bitstream文件。但是如果将新工程继续另存为,并对该工程进行修改的话则会出现问题“multi block run fail”。

对原工程的改动为:删除hdmi、spdif、iic相关模块,重新生成system_wrapper.v,修改system_top.v和zc706_system_constr.xdc,修改ZYNQ核的接口部分。修改后运行CONSOLE_COMMANDS或者DMA例程都没问题。

2.模块级联

将修改后的AD9361官方HDL工程和之前做的并转串模块、帧头添加和检测模块、串转并模块,以及上述DQPSK调制模块进行级联。这些模块可以参考之前的链接:

串行数据添加帧头以及帧头检测_yoga1020的博客-优快云博客_帧头检测

级联后遇到一个严重问题:IMPLEMENTATION的时序不通过

"[Timing 38-328] The design falied to meet the timing requirements."

通过观察时序报告,时序报告的阅读方法参考以下链接,发现之前的设计存在以下几个问题:

Vivado下如何阅读时序报告 - 知乎 (zhihu.com)

FPGA建立时间检测机理及影响因素分析_wuzhirui志锐的博客-优快云博客

1.FIFO的wr_en和rd_en不要由组合逻辑驱动,应该在组合逻辑后面打一拍,可以减小时序上误差;(不打拍也能通过时序)

2.跨时钟域控制信号。关于跨时钟域的问题可以参考之前“帧头检测”的连接,在后面补充了解决跨时钟域控制信号问题的方法。

(1)封装并转串Parallel to Serial模块

该模块将axis_dwidth_converter模块(将32位转换成8位),fifo_generator模块(将8位转换成1位,并完成时钟域转换)和add_frame_header模块(添加帧头)级联封装在一起。

FIFO模块的复位设置于之前不同,这里将复位同步禁用,读写使用独立的复位信号。其中写复位使用快时钟下的dma复位信号,读复位使用慢时钟下ind_clk_fifo_rst模块产生的fifo_rst信号。

在封装的时候需要注意将s_axis接口与快时钟关联。并将fifo_rst设置为ACTIVE_HIGH。

软件代码如下:

module parallel_to_serial(
    input p_aclk, //快时钟(并行信号)
    input p_sys_rstn,    //快时钟的复位信号
    input s_aclk,  //慢时钟(串行信号)
    input s_sys_rstn,  //慢时钟的复位信号
    input fifo_rst, //fifo的慢时钟复位信号
    input s_axis_tvalid,    //axis接口信号
    input [31:0]s_axis_tdata,   //axis接口信号
    input [3:0]s_axis_tkeep,    //axis接口信号
    input s_axis_tlast, //axis接口信号
    input rst_done_s,   //慢时钟复位完成标志
    input rst_done_p,   //快时钟复位完成标志
    input dma_rstn_s, //dma输出的复位信号(同步到慢时钟下)
    input dma_rstn_p, //dma输出的复位信号(快时钟)
    
    output s_axis_tready,   //axis接口信后
    output out_serial_data  //输出串行数据
    
    );
    
wire m_axis_tvalid;
reg m_axis_tready;
wire [7:0]m_axis_tdata;
wire m_axis_tkeep;
wire m_axis_tlast;

reg fifo_wr_en;
reg fifo_rd_en;
wire fifo_full;
wire fifo_empty;
wire fifo_dout;
wire header_end;

//m_axis_tready是快时钟信号
always@(posedge p_aclk or negedge p_sys_rstn) begin
    if(!p_sys_rstn)
        m_axis_tready <= 1'b0;
    else
        m_axis_tready <= rst_done_p & (~fifo_full);
end

axis_dwidth_converter_0 axis_dwidth_converter_inst (
  .aclk(p_aclk),                    // input wire aclk
  .aresetn(p_sys_rstn),              // input wire aresetn
  .s_axis_tvalid(s_axis_tvalid),  // input wire s_axis_tvalid
  .s_axis_tready(s_axis_tready),  // output wire s_axis_tready
  .s_axis_tdata(s_axis_tdata),    // input wire [31 : 0] s_axis_tdata
  .s_axis_tkeep(s_axis_tkeep),    // input wire [3 : 0] s_axis_tkeep
  .s_axis_tlast(s_axis_tlast),    // input wire s_axis_tlast
  .m_axis_tvalid(m_axis_tvalid),  // output wire m_axis_tvalid
  .m_axis_tready(m_axis_tready),  // input wire m_axis_tready
  .m_axis_tdata(m_axis_tdata),    // output wire [7 : 0] m_axis_tdata
  .m_axis_tkeep(m_axis_tkeep),    // output wire [0 : 0] m_axis_tkeep
  .m_axis_tlast(m_axis_tlast)    // output wire m_axis_tlast
);
//fifo_wr_en是快时钟信号
always@(posedge p_aclk or negedge p_sys_rstn) begin
    if(!p_sys_rstn)
        fifo_wr_en <= 1'b0;
    else
        fifo_wr_en <= m_axis_tvalid & rst_done_p & (~fifo_full);
end
//fifo_rd_en是慢时钟信号
always@(posedge s_aclk or negedge s_sys_rstn) begin
    if(!s_sys_rstn)
        fifo_rd_en <= 1'b0;
    else
        fifo_rd_en <= header_end & rst_done_s;
end
fifo_generator_0 fifo_generator_inst (
  .wr_clk(p_aclk),  // input wire wr_clk
  .wr_rst(~dma_rstn_p),  // input wire wr_rst
  .rd_clk(s_aclk),  // input wire rd_clk
  .rd_rst(fifo_rst),  // input wire rd_rst
  .din(m_axis_tdata),        // input wire [7 : 0] din
  .wr_en(fifo_wr_en),    // input wire wr_en
  .rd_en(fifo_rd_en),    // input wire rd_en
  .dout(fifo_dout),      // output wire [0 : 0] dout
  .full(fifo_full),      // output wire full
  .empty(fifo_empty)    // output wire empty
);

add_frame_header_0 add_frame_header_inst (
  .clk(s_aclk),                // input wire clk
  .rst_n(dma_rstn_s),            // input wire rst_n
  .fifo_data(fifo_dout),    // input wire fifo_data
  .fifo_empty(fifo_empty),  // input wire fifo_empty
  .header_end(header_end),  // output wire header_end
  .out_data(out_serial_data)      // output wire out_data
);

endmodule

(2)封装串转并Serial to Parallel模块

该模块将detect_frame_header模块(检测帧头),fifo_generator模块(将1位转换成8位,并完成时钟域转换),axis_dwidth_converter模块(将8位转换成32位)和counter_tlast模块(产生tlast信号)级联封装在一起。

其中一些注意事项与前面相同。

module serial_to_parallel(
    
    input p_aclk,   //快时钟(并行信号)
    input p_sys_rstn,    //快时钟的复位信号
    input s_aclk,  //慢时钟(串行信号)
    input s_sys_rstn,  //慢时钟的复位信号
    input m_axis_tready,    //axis接口信号
    input dma_rstn_p, //dma输出的复位信号(快时钟)
    input dma_rstn_s, //dma输出的复位信号(同步到慢时钟下)
    input serial_data_in,   //输入串行数据
    input fifo_rst, //fifo的慢时钟复位信号
    input rst_done_s,   //慢时钟复位完成标志
    input rst_done_p,   //快时钟复位完成标志
    
    output m_axis_tvalid,    //axis接口信号
    output [31:0]m_axis_tdata,    //axis接口信号
    output last_sig //输出last信号
    );

reg s_axis_tvalid;
wire [7:0]s_axis_tdata;
wire s_axis_tready;
wire detect_valid_en;
wire detect_data_out;
wire fifo_full;
wire fifo_empty;
reg fifo_wr_en;
reg fifo_rd_en;

detect_frame_header_0 detect_frame_header_inst (
  .clk(s_aclk),              // input wire clk
  .rst_n(dma_rstn_s),          // input wire rst_n
  .data_in(serial_data_in),      // input wire data_in
  .axis_last(last_sig),  // input wire axis_last
  .valid_en(detect_valid_en),    // output wire valid_en
  .data_out(detect_data_out)    // output wire data_out
);
//fifo_wr_en是在慢时钟下的
always@(posedge s_aclk or negedge s_sys_rstn)begin
    if(!s_sys_rstn)
        fifo_wr_en <= 1'b0;
    else
        fifo_wr_en <= (~fifo_full) & detect_valid_en & rst_done_s;
end
//fifo_rd_en是在快时钟下的
always@(posedge p_aclk or negedge p_sys_rstn)begin
    if(!p_sys_rstn)
        fifo_rd_en <= 1'b0;
    else
        fifo_rd_en <= (~fifo_empty) & s_axis_tready & rst_done_p;
end
s_axis_tvalid是在快时钟下的
always@(posedge p_aclk or negedge p_sys_rstn)begin
    if(!p_sys_rstn)
        s_axis_tvalid <= 1'b0;
    else
        s_axis_tvalid <= (~fifo_empty) & rst_done_p;
end

fifo_generator_0 fifo_generator_inst (
  .wr_clk(s_aclk),  // input wire wr_clk
  .wr_rst(fifo_rst),  // input wire wr_rst
  .rd_clk(p_aclk),  // input wire rd_clk
  .rd_rst(~rst_done_p),  // input wire rd_rst
  .din(detect_data_out),        // input wire [0 : 0] din
  .wr_en(fifo_wr_en),    // input wire wr_en
  .rd_en(fifo_rd_en),    // input wire rd_en
  .dout(s_axis_tdata),      // output wire [7 : 0] dout
  .full(fifo_full),      // output wire full
  .empty(fifo_empty)    // output wire empty
);

axis_dwidth_converter_0 axis_dwidth_converter_inst (
  .aclk(p_aclk),                    // input wire aclk
  .aresetn(rst_done_p),              // input wire aresetn
  .s_axis_tvalid(s_axis_tvalid),  // input wire s_axis_tvalid
  .s_axis_tready(s_axis_tready),  // output wire s_axis_tready
  .s_axis_tdata(s_axis_tdata),    // input wire [7 : 0] s_axis_tdata
  .m_axis_tvalid(m_axis_tvalid),  // output wire m_axis_tvalid
  .m_axis_tready(m_axis_tready),  // input wire m_axis_tready
  .m_axis_tdata(m_axis_tdata)    // output wire [31 : 0] m_axis_tdata
);

counter_tlast_0 counter_tlast_inst (
  .sys_clk(p_aclk),      // input wire sys_clk
  .sys_rst_n(dma_rstn_p),  // input wire sys_rst_n
  .cnt_sig(m_axis_tvalid),      // input wire cnt_sig
  .last_sig(last_sig)    // output wire last_sig
);

endmodule

(3)DQPSK调制模块

将前面提到的DQPSK调制代码进行模块化,主要注意位数的拼接。

(4)整体框图

整体框图如下所示,主要分为AD9361部分和位宽转换以及调制部分。

3.代码部分

(1)测试dma_fifo_loop功能

该功能主要是实现数据的串并转换,并没有对数据进行调制。进行该功能测试的时候,首先要完成AD9361初始化,主要是为了产生串行数据的时钟。然后再配置DMA完成数据发送和接收,这部分代码与之前相同。主要改动是删除了官方程序中关于axi_ad9361_dac_dma的控制函数,并将数据生成改为随机数生成。

其中发现一个关于中断的问题,首先是发现没有进入中断处理函数,但是通过ILA观察发现,在传输完成后DMA是产生中断的,如下图所示,说明与DMA配置无关,只能与中断配置有关。

最后发现没有检查到中断的原因在于xparameters.h里生成的中断ID号与硬件连接的对应不上。连接的是末三位91,90,89,但是中断ID却是头三位61,62,63。根据ug585文档中说明的PL中断对应的ID是63:61,68:64,91:84,且MSB对应的是91。不清楚为什么软件生成的ID号与实际硬件连接的不同。

 解决方法是直接将DMA的中断ID定义为硬件连接对应的中断号,

#define RX_INTR_ID			XPS_FPGA14_INT_ID
#define TX_INTR_ID			XPS_FPGA15_INT_ID

 最后程序的运行结果如下,表示可以正确实现数据流的串并转换。

 (2)测试发射功能

A modem method known as p/4 offset, differentially encoded quadrature phase shift keying (p/4-DQPSK) has been adopted for the United States and Japanese digital cellular time division multiple access (TDMA) systems and Personal Communications Systems (PCS). The rationale for such a choice is the high bit rate-bandwidth ratio and its applicability to noncoherent detection. Most current systems are implemented with analog hardware. In the p/4-DQPSK modem described in this thesis, DSP solutions are employed to process not only the baseband signal but also the intermediate frequency (IF) signal. The DSP technologies used in this modem include (1) Digital Complex Sampling, (2) Polyphase Filters, (3) Canonic Signed Digit Multipliers for FIR Filter, (4) Non-Data- Aided Timing Parameter Estimation, (5) Multirate Signal Processing. With these DSP solutions, various problems in analog and baseband DSP approaches, such as dc offset voltages, dc voltage drifts, analog filter phase distortions, quadrature phase and gain imbalance, and amplifier and mixer nonlinearities, are eliminated and very precise and controllable performance can be achieved without sophisticated compensation techniques. Other advantages of the DSP solutions include the ability to easily program the hardware to accommodate different data rates, modulation formats and filter specifications. The DSP solution is also an efficient method to minimize power consumption, size and cost of the systems. The mathematical model, simulation results, hardware designs in Very High Speed Integrated Circuit Hardware Description Language (VHDL), and testing results are presented in this thesis. The DSP-based p/4-DQPSK modem is designed and implemented on two Altera FLEX10K70 chips. The chip size is 4,428 logical cells (approximately 82k gates). The maximum bit rate is 5Mbit/sec. For an additive white Gaussian noise channel, the results of a bit error rate (BER) measurement indicate that the BER performance of the modem degrades about 1.5 dB from theory when 17th-order square root raised cosine matched iv filters are used. The performance degradation due to a carrier frequency offset is also investigated in terms of the BER.
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值