以前写过一个AD7606数据采集的模块,支持最大8片AD,64路的AD数据采集,今天,对这个模块进行一下仿真,具体思路如下:
通过RTL模块控制AD采样,然后写FIFO,最后通过DMA发送到ZYNQ的DDR内存,本人时菜鸟级别,里面也许会存在很多问题,欢迎一起探讨,不喜勿喷
RTL模块程序如下(模块中有些语句不能综合,需要时改一下):
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2020/12/09 11:30:54
// Design Name:
// Module Name: AD7606_CTRL
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module AD7606_CTRL(
input s_axis_aclk,//输入时钟
input s_axis_aresetn,//复位信号
input [31:0]frq_data0,//系统频率数据0
input [31:0]frq_data1,//系统频率数据1
input S_AXIS_tready,//FIFO接受就绪
output S_AXIS_tvalid,//发送数据有效
output S_AXIS_tlast,//数据发送结束
output [15:0]S_AXIS_tdata,//FIFO写数据
input [7:0]adDataLine,//AD数据线
input [7:0]adBusy,//AD忙信号
output adReset,//AD复位信号
output adConvst,//AD转换启动信号
output [7:0]adCs,//AD片选
output adRead//AD读使能信号
);
reg S_AXIS_tvalid;
reg S_AXIS_tlast;
reg [15:0]S_AXIS_tdata;
parameter [1:0]DATATRANS_WAIT = 2'b00;
parameter [1:0]DATATRANS_START = 2'b01;
parameter [1:0]DATATRANS_END = 2'b10;
reg [1:0]currentState;
reg [1:0]nextState;
//时序逻辑
always @(posedge s_axis_aclk or negedge s_axis_aresetn)begin
if(!s_axis_aresetn)begin
currentState <= 2'b00;
end
else begin
currentState <= nextState;
end
end
//组合逻辑
always @(posedge s_axis_aclk or negedge s_axis_aresetn)begin
if (!s_axis_aresetn)begin
S_AXIS_tvalid <= 1'b0;
S_AXIS_tlast <= 1'b0;
S_AXIS_tdata <= 32'd0;
nextState <= DATATRANS_WAIT;
end
else begin
case(currentState)
DATATRANS_WAIT:begin
if(S_AXIS_tready)begin
S_AXIS_tvalid <= 1'b1;
nextState <= DATATRANS_START;
end
else begin
S_AXIS_tvalid <= 1'b0;
nextState <= DATATRANS_WAIT;
end
end
DATATRANS_START:begin
if(S_AXIS_tready)begin //FIFO可写
if(smpRdIndex != smpIndex)begin
if(smpRdIndex >= AD_SUM*AD_CHN_SUM)begin
smpRdIndex <= 8'b0000_0000;
S_AXIS_tlast <= 1'b1;
nextState <= DATATRANS_END;
end
else begin
S_AXIS_tdata <= smpDataTmpBuf[smpRdIndex];
smpRdIndex <= smpRdIndex +1'b1;
end
end
else begin
S_AXIS_tlast <= 1'b0;
nextState <= DATATRANS_START;
end
end
else begin //等待FIFO可写
S_AXIS_tdata <= S_AXIS_tdata;
nextState <= DATATRANS_START;
end
end
DATATRANS_END:begin
if(!S_AXIS_tready)begin //FIFO满则等待
S_AXIS_tlast <= 1'b1;
S_AXIS_tvalid <= 1'b1;
S_AXIS_tdata <= S_AXIS_tdata;
nextState <= DATATRANS_END;
end
else begin // 写入结束
S_AXIS_tlast <= 1'b0;
S_AXIS_tvalid <= 1'b0;
nextState <= DATATRANS_WAIT;
end
end
default:
nextState <= DATATRANS_WAIT;
endcase
end
end
//由于输入时钟为100Mhz,所以对输入时钟进行2分频,得到AD采样需要的50MHZ时钟
reg clk_50M;
always @(posedge s_axis_aclk or negedge s_axis_aresetn)begin
if(!s_axis_aresetn)begin
clk_50M <= 1'b0;
end
else begin
clk_50M <=~clk_50M;
end
end
//测频跟踪信号,根据输入频率(2路测频输入信号)动态调整采样周期
reg [2:0]frqErrCnt;//系统频率出错计数
reg [21:0]smp_cnt;//采样周期计数
//系统测频数据是以100MHZ为基准时钟,所以当系统频率为50HZ时,测频计数值为100000000/50=2000000
//所以,当系统频率为44.98hz时,测频计数值为100000000/44.98=2223210,当系统频率为55.02hz时,测频计数值为100000000/55.02=1817520
parameter PARAM_4498HZ = 2223210;
parameter PARAM_5502HZ = 1817520;
//采样计算:采样为200点,采样频率10K,如果以50Mhz的时钟进行采样计数,采样计数值=2000000/400,在本程序中调用系统乘法器实现/400操作
//2000000/400 = (2000000*10)>>12
always @(posedge clk_50M or negedge s_axis_aresetn)begin
if(!s_axis_aresetn)begin
smp_cnt <= (24'd2000000*10)>>12;
frqErrCnt <= 3'd0;
end
else if(frq_data0 > PARAM_4498HZ || frq_data0 < PARAM_5502HZ)begin
if(frq_data1 > PARAM_4498HZ || frq_data1 < PARAM_5502HZ)begin//两路测频均不在44.98至55.02的频率范围
if(frqErrCnt == 3'd3)begin
smp_cnt <= (24'd2000000*10)>>12;
frqErrCnt <= 3'd0;
end
else begin
frqErrCnt <= frqErrCnt +1'b1;
end
end
else begin
smp_cnt <= (frq_data1*10)>>12;
frqErrCnt <= 3'd0;
end
end
else begin
smp_cnt <= (frq_data0*10)>>12;
frqErrCnt <= 3'd0;
end
end
//AD采样模块调用,字节操作模式,每个通道为数据高8位+数据低8位
wire [0:0]ready;
wire [7:0]adSmpData;
AD7606_SMP AD7606_COMP(
.rst_n(s_axis_aresetn),
.clk_50m(clk_50M),
.sampleCnt(smp_cnt),
.inS_data(adDataLine),
.buzy(adBusy),
.rst(adReset),
.convst(adConvst),
.cs_n(adCs),
.rd_n(adRead),
.IsDataReady(ready),
.DataOut(adSmpData)
);
//采样到的8位AD数据,组合成16位AD采样数据,然后发送到FIFO
always @(s_axis_aresetn)begin
if(!s_axis_aresetn)begin
smpCnt <= 2'b00;
smpIndex <= 8'b0000_0000;
smpRdIndex <= 8'b0000_0000;
end
end
//
parameter AD_SUM = 6;
parameter AD_CHN_SUM = 8;
reg [15:0]smpDataTmpBuf[0:AD_SUM*AD_CHN_SUM-1];
reg [7:0]smpIndex;
reg [7:0]smpRdIndex;
reg [0:0]dataReady;
reg [15:0]dataTmp;
reg [1:0]smpCnt;
always @(posedge clk_50M)begin
if(smpIndex >= AD_SUM*AD_CHN_SUM)begin
smpIndex <= 8'b0000_0000;
end
end
always @(posedge dataReady)begin
smpIndex <= smpIndex +1'b1;
end
///
always @(posedge ready)begin
if(smpCnt >= 2'b01)begin
dataTmp = (dataTmp << 8)+adSmpData;//此处使用阻塞语句,否则不能使用最新的数据
smpDataTmpBuf[smpIndex] <= dataTmp;
smpCnt <= 2'b00;
dataReady <= 1'b1;
end
else begin
smpCnt <= smpCnt +1'b1;
dataReady <= 1'b0;
dataTmp <= adSmpData;
end
end
endmodule
testbench 模块如下:
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2020/12/09 11:32:39
// Design Name:
// Module Name: AD7606_SM
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module AD7606_SM();
//输入信号
reg clk_i;
reg clk_rst;
reg [31:0]freq_data1;
reg [31:0]freq_data2;
reg tready;
reg [7:0]ad_dataLine;
reg [7:0]adBusy;
//输出信号
wire tvalid;
wire tlast;
wire [15:0]tdata;
wire adReset;
wire adConvst;
wire [7:0]adCs;
wire adRead;
AD7606_CTRL uut(
.s_axis_aclk(clk_i),
.s_axis_aresetn(clk_rst),
.frq_data0(freq_data1),
.frq_data1(freq_data2),
.S_AXIS_tready(tready),
.S_AXIS_tvalid(tvalid),
.S_AXIS_tlast(tlast),
.S_AXIS_tdata(tdata),
.adDataLine(ad_dataLine),
.adBusy(adBusy),
.adReset(adReset),
.adConvst(adConvst),
.adCs(adCs),
.adRead(adRead)
);
//占空比50%的时钟
parameter CLOCK_PERIOD = 10;
initial
begin
clk_i = 0;
clk_rst = 0;
tready = 0;
freq_data1 = 32'd2000000;
freq_data2 = 32'd2000000;
ad_dataLine = 8'd0;
#97;
clk_rst =1;
end
always
begin
#(CLOCK_PERIOD/2) clk_i = ~clk_i;
end
reg clk_50m;
always @(posedge clk_i or clk_rst)begin
if(!clk_rst)begin
clk_50m <= 1'b0;
end
else begin
clk_50m <= ~clk_50m;
end
end
always @(posedge clk_50m or clk_rst) begin
if(!clk_rst)begin
adBusy = 8'b0000_0000;
tready = 0;
end
else begin
if(clk_rst == 1)begin
#7;
tready = 1;
end
if(adConvst == 1)begin
#2;
adBusy = 8'b0011_1111;
end
if(adBusy == 8'b0011_1111)begin
#4000;//AD转化时间为4us
adBusy = 8'b0000_0000;
end
end
end
always @(negedge adRead)begin
ad_dataLine <= $random%127;
end
endmodule
仿真波形如下:
上图中,仿真功能主要模拟AD7606读写时序,当adread信号为低时,采样AD总线数据,合成16位数据后,通过reg [15:0]smpDataTmpBuf[0:AD_SUM*AD_CHN_SUM-1]缓存,最后经过S_AXIS_tdata数据输出到FIFO,仿真结果可以看出数据输出正常,实现了当初的想法。