一 码型变换
该部分主要参考《数字调制解调计数的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)测试发射功能