前记:师夷长技以自强
一、需求
1.使用ADC以最高速度采集2000个数据,将这些数据存储在scfifo中
2.等待着2000个数据采集完成之后,从scfifo中读取数据出来使用串口持续发送直到2000个数据全部发完。
二、分析
.2.1系统框图
当系统模块多了后,最好画一个框图,以示数据在各模块中的流动关系。
2.2状态分析
2.2.1 状态的划分
1.空闲 IDEL
2.持续采样状态
(1)使能单次采样 Sample_T
(2)等待单次采样完成 Wait_Conv_Done
3.串口持续发送数据状态
(1)使能单次发送 Tx_T
(2)等待单次发送完成 Wait_Tx_Done
2.2.2 关键信号选取
Key_flag Key_state:空闲态到工作态的转换
Conv_done Conv_times_cnt:采样状态到新的采样触发状态或者数据发送触发状态的跳转条件
Tx_Done Tx_times_cnt usedw:发送状态到新的发送触发状态或者空闲状态的跳转条件
2.3 注意问题
(1)端口信号延迟问题
我们都知道时序电路是有延迟的,因此从fifo中读数据到串口发送数据应该注意,发送读请求给fifo需要延迟一个时钟周期后数据才能输出,而fifo输出端口的数据又需要延迟一个时钟周期才能到达串口中的缓存。所以串口的发送使能信号应该在fifo的读请求信号后两个时钟周期。
(2)波特率设置
因为计时是从0开始的,所以对系统时钟分频后应记得减1.
(3)仿真问题
为了能在modelsim中仿真及早发现问题,可以利用系统函数$readmemh函数从指定的文件中输入数据,模拟ADC器件的DOUT端输出的数据,而每个采样值产生的时间应该是conv_start和conv_done之间。
为了能在modesim中看到串口发送模块发送的数据,可以设计一个Uart_Rx_Model模块用于接收Rs232_Tx发来的数据,数据的采集开始以Rs232_Tx的下降沿开始,然后在每一bit的中间采集数据。
为了控制仿真系统能够停止,可以捕捉fifo的empty信号,当出现上升沿时说明fifo中的数据已经读完,可以稍微延迟一段时间后就停止仿真。
为了精确控制ADC采样的每个数据和Uart_Rx_Model模块接收的每个数据,可利用系统函数$display输出到modelsim的仿真文本数据中。
3.代码设计
这里只给出状态机和仿真的代码设计,ADC,filtter和UART模块的代码可以参考之前的博客。
module FSM(
input Clk,
input Rst_n,
input Conv_Done,
input full,
input [11:0]Redata,
input [10:0]UsedW,
input empty,
input Tx_Done,
input Key_flag,
input Key_state,
output reg Conv_Start,
output reg [7:0]ADC_DIV_PARAM,
output reg [2:0]Channel,
output reg Sclr,
output reg Wrreq,
output reg Rdreq,
output [7:0]Data,
output Tx_Start,
output reg [2:0]Baud_set
);localparam
IDEL = 5'b00001,
Sample_T = 5'b00010,
Wait_Conv_Done = 5'b00100,
Tx_T = 5'b01000,
Wait_Tx_Done = 5'b10000;
reg [4:0]state;
reg [10:0]Conv_times_cnt;
reg [10:0]Tx_times_cnt;
reg [2:0]Tx_Start_r;
always@(posedge Clk,negedge Rst_n)
if(!Rst_n)
Tx_Start_r <= 0;
else
Tx_Start_r <= {Tx_Start_r[1:0],Rdreq};
assign Tx_Start = Tx_Start_r[2];
assign Data = Redata[11:4];
always@(posedge Clk,negedge Rst_n)
if(!Rst_n)
Conv_times_cnt <= 0;
else if(Conv_Done)
if(Conv_times_cnt == 11'd2000)
Conv_times_cnt <= 0;
else
Conv_times_cnt <= Conv_times_cnt + 11'b1;
else
Conv_times_cnt <= Conv_times_cnt;always@(posedge Clk,negedge Rst_n)
if(!Rst_n)
Tx_times_cnt <= 0;
else if(Tx_Done)
if(Tx_times_cnt == 11'd2000)
Tx_times_cnt <= 0;
else
Tx_times_cnt <= Tx_times_cnt + 11'b1;
else
Tx_times_cnt <= Tx_times_cnt;
always@(posedge Clk,negedge Rst_n)
if(!Rst_n)begin
state <= IDEL;
ADC_DIV_PARAM <= 8'd13;
Channel <= 3'b0;
Baud_set <= 3'd7;
Rdreq <= 0;
end
else begin
case(state)
IDEL:
begin
if(Key_flag&&!Key_state)
state <= Sample_T;
end
Sample_T:
begin
Wrreq <= 0;
Conv_Start <= 1'b1;
state <= Wait_Conv_Done;
end
Wait_Conv_Done:
begin
Conv_Start <= 1'b0;
if(Conv_Done)begin //write to fifo
Wrreq <= 1;
if(Conv_times_cnt == 11'd2000)
state <= Tx_T;
else
state <= Sample_T;
end
else
state <= Wait_Conv_Done;
end
Tx_T:
begin
Wrreq <= 0;
Rdreq <= 1;
state <= Wait_Tx_Done;
end
Wait_Tx_Done:
begin
Rdreq <= 0;
if(Tx_Done && Tx_times_cnt == 11'd2000)
state <= IDEL;
else if(Tx_Done)
state <= Tx_T;
else
state <= Wait_Tx_Done;
end
endcase
endendmodule
仿真代码
module Sample_Voltage(
input Clk,
input Rst_n,
input Key_in,
input DOUT,
output CS_N,
output SCLK,
output DIN,
output Rs232_Tx
);
wire Key_flag;
wire Key_state;
wire Conv_Start;
wire Conv_Done;
wire [11:0] Vout; //ADC result
wire [7:0]ADC_DIV_PARAM;
wire [2:0]Channel;
wire Wrreq;
wire [11:0]Wrdata;
wire Rdreq;
wire [11:0]Redata;
wire [11:0]UsedW;
wire empty;
wire full;
wire Sclr;
wire [7:0]Data2Uart;
wire Tx_Start;
wire [2:0]Baud_set;
wire Tx_Done;
key_filter key_filter(
.Clk(Clk),
.Rst_n(Rst_n),
.Key_in(Key_in),
.key_flag(Key_flag),
.key_state(Key_state)
);ADC ADC(
.Clk(Clk),
.Rst_n(Rst_n),
.DIV_PARAM(ADC_DIV_PARAM),
.Channel(Channel),
.Start(Conv_Start),
.DOUT(DOUT),
.Conv_Done(Conv_Done),
.ADC_STATE(), //0-idle,1-busy
.Data(Vout),
.SCLK(SCLK),
.DIN(DIN),
.CS_N(CS_N)
);
fifo fifo(
.clock(Clk),
.data(Vout),
.rdreq(Rdreq),
.sclr(Sclr),
.wrreq(Wrreq),
.empty(empty),
.full(full),
.q(Redata),
.usedw(UsedW)
);
UART UART(
.Clk(Clk),
.Rst_n(Rst_n),
.Send_En(Tx_Start),
.data_byte(Data2Uart),
.baud_set(Baud_set),
.uart_state(),
.Tx_done(Tx_Done),
.Rs232_Tx(Rs232_Tx)
);
FSM FSM(
.Clk(Clk),
.Rst_n(Rst_n),
.Conv_Done(Conv_Done),
.full(full),
.Redata(Redata),
.UsedW(UsedW),
.empty(empty),
.Tx_Done(Tx_Done),
.Key_flag(Key_flag),
.Key_state(Key_state),
.Conv_Start(Conv_Start),
.ADC_DIV_PARAM(ADC_DIV_PARAM),
.Channel(Channel),
.Sclr(Sclr),
.Wrreq(Wrreq),
.Rdreq(Rdreq),
.Data(Data2Uart),
.Tx_Start(Tx_Start),
.Baud_set(Baud_set)
);
endmodule
`timescale 1ns/1ns
`define clk_period 20module Uart_Rx_Model(Baud_Set,Uart_Rx);
input [2:0]Baud_Set;
input Uart_Rx;
reg Clk;
reg Rst_n;
wire Mid_Flag_Receive; /*数据中点标志*/
reg Receive_Baud_Start;/*接受波特率生成使能*/
reg [7:0]Rx_Data;/*接受数据移位寄存器*/
reg [7:0]Rx_Byte;/*最终接收结果*/
initial Clk = 1;
always #(`clk_period/2) Clk = ~Clk;
/*例化波特率设置模块*/
Baud_Select Baud_Select_Receive(
.Clk(Clk),
.Rst_n(Rst_n),
.Baud_Set(Baud_Set),
.Baud_Start(Receive_Baud_Start),
.Mid_Flag(Mid_Flag_Receive)
);
initial begin
Rst_n = 0;
Rx_Byte = 0;
Rx_Data = 0;
#100 Rst_n = 1;
end
/*接收一个字节的数据*/
initial begin
forever begin
@(negedge Uart_Rx)
begin
Receive_Baud_Start = 1;
@(posedge Mid_Flag_Receive);
@(posedge Mid_Flag_Receive)Rx_Data[0] = Uart_Rx;
@(posedge Mid_Flag_Receive)Rx_Data[1] = Uart_Rx;
@(posedge Mid_Flag_Receive)Rx_Data[2] = Uart_Rx;
@(posedge Mid_Flag_Receive)Rx_Data[3] = Uart_Rx;
@(posedge Mid_Flag_Receive)Rx_Data[4] = Uart_Rx;
@(posedge Mid_Flag_Receive)Rx_Data[5] = Uart_Rx;
@(posedge Mid_Flag_Receive)Rx_Data[6] = Uart_Rx;
@(posedge Mid_Flag_Receive)Rx_Data[7] = Uart_Rx;
@(posedge Mid_Flag_Receive)begin Receive_Baud_Start = 0;Rx_Byte = Rx_Data;end
$display("Master_receive Data = %0h",Rx_Byte);
end
end
end
endmodule/*此模块用于生成仿真模型接受数据所需波特率时钟信号*/
module Baud_Select(Clk,Rst_n,Baud_Set,Baud_Start,Mid_Flag);
input Clk;
input Rst_n;
input [2:0]Baud_Set;
input Baud_Start;
output Mid_Flag;
parameter system_clk = 1000_000_000/`clk_period;
localparam bps9600 = system_clk/9600 - 1;
localparam bps19200 = system_clk/19200 - 1;
localparam bps38400 = system_clk/38400 - 1;
localparam bps57600 = system_clk/57600 - 1;
localparam bps115200 = system_clk/115200 - 1;
localparam bps230400 = system_clk/230400 - 1;
localparam bps460800 = system_clk/460800 - 1;
localparam bps921600 = system_clk/921600 - 1;localparam bps9600_2 = system_clk/9600/2 - 1;
localparam bps19200_2 = system_clk/19200/2 - 1;
localparam bps38400_2 = system_clk/38400/2 - 1;
localparam bps57600_2 = system_clk/57600/2 - 1;
localparam bps115200_2 = system_clk/115200/2 - 1;
localparam bps230400_2 = system_clk/230400/2 - 1;
localparam bps460800_2 = system_clk/460800/2 - 1;
localparam bps921600_2 = system_clk/921600/2 - 1;reg [31:0]BPS_PARA;
reg [31:0]BPS_PARA_2;
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)begin
BPS_PARA <= bps9600;
BPS_PARA_2 <= bps9600_2;
end
else begin
case(Baud_Set)
3'd0:begin BPS_PARA <= bps9600;BPS_PARA_2 <= bps9600_2;end
3'd1:begin BPS_PARA <= bps19200;BPS_PARA_2 <= bps19200_2;end
3'd2:begin BPS_PARA <= bps38400;BPS_PARA_2 <= bps38400_2;end
3'd3:begin BPS_PARA <= bps57600;BPS_PARA_2 <= bps57600_2;end
3'd4:begin BPS_PARA <= bps115200;BPS_PARA_2 <= bps115200_2;end
3'd5:begin BPS_PARA <= bps230400;BPS_PARA_2 <= bps230400_2;end
3'd6:begin BPS_PARA <= bps460800;BPS_PARA_2 <= bps460800_2;end
3'd7:begin BPS_PARA <= bps921600;BPS_PARA_2 <= bps921600_2;end
endcase
end
reg [31:0]cout;
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
cout<=32'd0;
else if((cout==BPS_PARA)||!Baud_Start)
cout<=32'b0;
else
cout<=cout+1'b1;
reg Mid_Flag_1;
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
Mid_Flag_1 <= 1'b0;
else if((cout==BPS_PARA_2))
Mid_Flag_1 <= 1'b1;
else
Mid_Flag_1 <= 1'b0;
assign Mid_Flag=Mid_Flag_1;
endmodule
为了直观感受一下系统的运行结果,下面给出仿真波形图,这里软件出来点问题,波形叠加在了一起,不过不影响对波形的观察
有什么问题欢迎在下方留言~