ad7606的八通道modelsim仿真
解決的疑問主要有一下幾點:
(1)之前一直困惑於如何確定AD的採樣頻率,後來在朋友的提示下,在程序中加入了定時器,每50us讀取一次數據,實現採樣頻率變爲20K。
(2)對ad_reset信號,之前一直以爲計數器記滿之後,會自動復位,似的該信號會馬上變爲高電平,仿真後才發現並未如此,滿量程的計數器將會一直保持在ff的狀態,使得ad-reset信號保持爲低,也就進入了循環採樣的狀態。cnt計數器只是對ad復位進行操作,這正決定採樣頻率的是cnt50us計數器。
(3)使用$random函數模擬採樣信號,使得仿真結果更真實可觀。
ad的採樣程序如下:
`timescale 1ns / 1ns
//////////////////////////////////////////////////////////////////////////////////
// Module Name: ad7606
//////////////////////////////////////////////////////////////////////////////////
module ad7606(
input clk, //50mhz
input rst_n,
input [15:0] ad_data, //ad7606 採樣數據
input ad_busy, //ad7606 忙標誌位
input first_data, //ad7606 第一個數據標誌位
output [2:0] ad_os, //ad7606 過採樣倍率選擇
output reg ad_cs, //ad7606 AD cs
output reg ad_rd, //ad7606 AD data read
output reg ad_reset, //ad7606 AD reset
output reg ad_convstab, //ad7606 AD convert start
output reg [15:0] ad_ch1, //AD第1通道的數據
output reg [15:0] ad_ch2, //AD第2通道的數據
output reg [15:0] ad_ch3, //AD第3通道的數據
output reg [15:0] ad_ch4, //AD第4通道的數據
output reg [15:0] ad_ch5, //AD第5通道的數據
output reg [15:0] ad_ch6, //AD第6通道的數據
output reg [15:0] ad_ch7, //AD第7通道的數據
output reg [15:0] ad_ch8, //AD第8通道的數據
output reg [3:0] state
//output reg [3:0] cnt
);
reg [7:0] cnt = 0 ;
reg [15:0] cnt50us = 0;
reg [5:0] i;
//reg [3:0] state;
parameter IDLE=4'd0;
parameter AD_CONV=4'd1;
parameter Wait_1=4'd2;
parameter Wait_busy=4'd3;
parameter READ_CH1=4'd4;
parameter READ_CH2=4'd5;
parameter READ_CH3=4'd6;
parameter READ_CH4=4'd7;
parameter READ_CH5=4'd8;
parameter READ_CH6=4'd9;
parameter READ_CH7=4'd10;
parameter READ_CH8=4'd11;
parameter READ_DONE=4'd12;
//parameter display=4'd13;
assign ad_os=3'b000; //無過採樣
//ad復位
always@(posedge clk)
begin
if(cnt<8'hff) begin
cnt<=cnt+1;
ad_reset<=1'b1;
end
else
ad_reset<=1'b0; //計數器達到ff後停止,ad_reset恆爲零
end
//使用定時器來設置採樣頻率
always @(posedge clk or negedge rst_n) //每50us讀取一次數據,ad的採樣率爲20K
begin
if(rst_n == 0)
cnt50us <= 0;
else begin
if(cnt50us < 16'd2499)
begin
cnt50us <= cnt50us + 1;
end
else
cnt50us <= 0;
end
end
always @(posedge clk)
begin
if (ad_reset==1'b1) begin //初始化ad
state<=IDLE;
ad_ch1<=0;
ad_ch2<=0;
ad_ch3<=0;
ad_ch4<=0;
ad_ch5<=0;
ad_ch6<=0;
ad_ch7<=0;
ad_ch8<=0;
ad_cs<=1'b1;
ad_rd<=1'b1;
ad_convstab<=1'b1; //8通道同步採樣
i<=0;
end
else begin
case(state) //need time:(20+2+5+1+3*8+1)*20ns=1060ns, fmax=1/1060ns=1MHZ
IDLE: begin
ad_cs<=1'b1;
ad_rd<=1'b1;
ad_convstab<=1'b1;
if(i==20) begin //延時20個時鐘後開始轉換
i<=0;
state<=AD_CONV;
end
else
i<=i+1'b1;
end
AD_CONV: begin
if(i==2) begin //等待2個lock,convstab的下降沿最少爲25ns,故至少需要兩個時鐘
i<=0;
state<=Wait_1;
ad_convstab<=1'b1;
end
else begin
i<=i+1'b1;
ad_convstab<=1'b0; //啓動AD轉換
end
end
Wait_1: begin
if(i==5) begin //等待5個clock, 等待busy信號爲高(tconv)
i<=0;
state<=Wait_busy;
end
else
i<=i+1'b1;
end
Wait_busy: begin
if(ad_busy==1'b0) begin //等待busy爲低電平 即轉換之後讀取模式
i<=0;
state<=READ_CH1;
end
end
READ_CH1: begin
ad_cs<=1'b0; //cs信號有效 直到讀取8通道結束
if(i==3) begin // 低電平持續3個時鐘,完成通道1的讀入
ad_rd<=1'b1;
i<=0;
ad_ch1<=ad_data; //讀CH1
state<=READ_CH2;
end
else begin
ad_rd<=1'b0;
i<=i+1'b1;
end
end
READ_CH2: begin
if(i==3) begin
ad_rd<=1'b1;
i<=0;
ad_ch2<=ad_data; //讀CH2
state<=READ_CH3;
end
else begin
ad_rd<=1'b0;
i<=i+1'b1;
end
end
READ_CH3: begin
if(i==3) begin
ad_rd<=1'b1;
i<=0;
ad_ch3<=ad_data; //讀CH3
state<=READ_CH4;
end
else begin
ad_rd<=1'b0;
i<=i+1'b1;
end
end
READ_CH4: begin
if(i==3) begin
ad_rd<=1'b1;
i<=0;
ad_ch4<=ad_data; //讀CH4
state<=READ_CH5;
end
else begin
ad_rd<=1'b0;
i<=i+1'b1;
end
end
READ_CH5: begin
if(i==3) begin
ad_rd<=1'b1;
i<=0;
ad_ch5<=ad_data; //讀CH5
state<=READ_CH6;
end
else begin
ad_rd<=1'b0;
i<=i+1'b1;
end
end
READ_CH6: begin
if(i==3) begin
ad_rd<=1'b1;
i<=0;
ad_ch6<=ad_data; //讀CH6
state<=READ_CH7;
end
else begin
ad_rd<=1'b0;
i<=i+1'b1;
end
end
READ_CH7: begin
if(i==3) begin
ad_rd<=1'b1;
i<=0;
ad_ch7<=ad_data; //讀CH7
state<=READ_CH8;
end
else begin
ad_rd<=1'b0;
i<=i+1'b1;
end
end
READ_CH8: begin
if(i==3) begin
ad_rd<=1'b1;
i<=0;
ad_ch8<=ad_data; //讀CH8
state<=READ_DONE;
end
else begin
ad_rd<=1'b0;
i<=i+1'b1;
end
end
READ_DONE:begin //完成讀,回到idle狀態
ad_rd<=1'b1;
ad_cs<=1'b1;
if(cnt50us == 16'd2499) //不加此條件,則ad完成一次讀取需1280ns,採樣頻率781.25K,但需注意ad每通道的追高採樣只能爲200K
state<=IDLE;
else
state<=READ_DONE;
end
default: state<=IDLE;
endcase
end
end
endmodule
仿真測試程序如下:
`timescale 1 ns/ 1 ns
module ad7606_vlg_tst();
// constants
// general purpose registers
// test vector input registers
reg ad_busy;
reg [15:0] ad_data;
reg clk;
reg first_data;
reg rst_n;
// wires
wire [15:0] ad_ch1;
wire [15:0] ad_ch2;
wire [15:0] ad_ch3;
wire [15:0] ad_ch4;
wire [15:0] ad_ch5;
wire [15:0] ad_ch6;
wire [15:0] ad_ch7;
wire [15:0] ad_ch8;
wire ad_convstab;
wire ad_cs;
wire [2:0] ad_os;
wire ad_rd;
wire ad_reset;
wire [3:0] state;
// assign statements (if any)
ad7606 i1 (
// port map - connection between master ports and signals/registers
.ad_busy(ad_busy),
.ad_ch1(ad_ch1),
.ad_ch2(ad_ch2),
.ad_ch3(ad_ch3),
.ad_ch4(ad_ch4),
.ad_ch5(ad_ch5),
.ad_ch6(ad_ch6),
.ad_ch7(ad_ch7),
.ad_ch8(ad_ch8),
.ad_convstab(ad_convstab),
.ad_cs(ad_cs),
.ad_data(ad_data),
.ad_os(ad_os),
.ad_rd(ad_rd),
.ad_reset(ad_reset),
.clk(clk),
.first_data(first_data),
.rst_n(rst_n),
.state(state)
//.cnt(cnt)
);
initial
begin
ad_busy = 0;
first_data = 0;
clk = 0;
forever //50MHz
#10
clk = ~clk;
$display("Running testbench");
end
initial
begin
rst_n = 1;
#10;
rst_n = 0;
#20;
rst_n = 1;
end
always@(posedge clk)
begin
ad_data <= $random; //使用隨機數模擬採樣信號
end
endmodule
仿真結果如下:
ad程序的狀態圖如下: