一、简介
因为我们本次进行串口回环的实验的对象是FPGA开发板和PC端,所以在接收和发送模块中先编写接收模块,这样可以在后面更好的进行发送模块的验证。(其实这里先编写哪个模块)都不影响,这里看自己心情,反正都可以独立进行仿真。)
在上一篇文章中,我们对于UART回环实现的总体系统框架做了一盒简单的构建,所以在实现时我们也按照那个框架来。这里就先对于接收模块进行一个设计。
二、接收模块的基本设计
本次设计我们采用状态机的实现方式,将状态机划分为四个,第一个就是空闲状态,表示设备没有接收数据,第二个是开始状态,表示设备接收到起始位,第三个接收数据过程状态,用于表示设备接收数据的过程,最后一个就是停止位,表示设备接受数据完成。
三、接收模块的波形图绘制
根据上面的状态机,我们可以据此展开波形图的绘制,分别就是对于信号进行打拍,下降沿检测,两个状态,以及bit和波特率、输出数据等的表示。
使用三级打拍,利用后两拍信号实现下降沿检测,当检测到下降沿,状态机由IDLE进入到START,然后利用波特率计数器计数1bit的起始位,来到DATA,利用波特率计数器和bit计数器用于接收数据,接收完数据之后进入STOP,最后利用波特率计数器计数1bit的停止位,状态又回到初始的IDLE状态。
四、代码实现
1、设计文件的编写
新建一个uart_rx.v文件,如下:在代码编写的过程中我们还需要注意的就是UART在进行通信时是串行通信,二我们的设备中数据时并行的,所以在代码中我们还要实现数据串并型的转换。
//---------<模块及端口声名>-------------------------------------------
module uart_rx(
input clk ,
input rst_n ,
input din_rx ,
output [7:0] dout_data,
output dout_flag
);
//---------<参数定义>------------------------------------------------
parameter CLK_CLY=50_000_000;
parameter BAUD_115200=115200;
parameter BPS_CNT_MAX=CLK_CLY/BAUD_115200;
parameter IDLE =4'b0001,
START =4'b0010,
DATA =4'b0100,
STOP =4'b1000;
//---------<内部信号定义>--------------------------------------------
reg uart_rx_d1;//对异步信号进行同步处理
reg uart_rx_d2;
reg uart_rx_d3;
reg [3:0] state_c;
reg [3:0] state_n;
wire nedge;//起始位下降沿检测
reg [8:0] cnt_bps;//波特率计数器
wire add_cnt_bps;
wire end_cnt_bps;
reg [2:0] cnt_bit;//bit数据计数器
wire add_cnt_bit;
wire end_cnt_bit;
reg [7:0] uart_rx_r;//用于存储接收到的数据
reg rx_flag;//接收数据完成标志位
//第一段:同步时序描述状态转移
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
state_c <=IDLE ;
end
else begin
state_c <= state_n;
end
end
//第二段:组合逻辑判断状态转移条件,描述状态转移规律
always @(*) begin
case(state_c)
IDLE : begin
if (nedge)
state_n=START ;
else
state_n=IDLE ;
end
START : begin
if (end_cnt_bps)
state_n=DATA ;
else
state_n=START ;
end
DATA : begin
if (end_cnt_bit)
state_n=STOP ;
else
state_n=DATA ;
end
STOP : begin
if (end_cnt_bps)
state_n=IDLE ;
else
state_n=STOP ;
end
default : state_n=IDLE ;
endcase
end
//对uart_rx进行打拍同步处理
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
uart_rx_d1 <= 1'b1;
uart_rx_d2 <= 1'b1;
uart_rx_d3 <= 1'b1;
end
else begin
uart_rx_d1 <=din_rx;
uart_rx_d2 <=uart_rx_d1;
uart_rx_d3 <=uart_rx_d2;
end
end
//nedge下降沿检测
assign nedge=~uart_rx_d2 & uart_rx_d3;
//波特率计数器
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_bps <= 9'd0;
end
else if(add_cnt_bps)begin
if(end_cnt_bps)begin
cnt_bps <= 'd0;
end
else begin
cnt_bps <= cnt_bps + 1'b1;
end
end
end
assign add_cnt_bps =(state_c != IDLE) ;
assign end_cnt_bps = add_cnt_bps && (cnt_bps ==(BPS_CNT_MAX-1)) ;
//bit计数器
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_bit <= 9'd0;
end
else if(add_cnt_bit)begin
if(end_cnt_bit)begin
cnt_bit <= 'd0;
end
else begin
cnt_bit <= cnt_bit + 1'b1;
end
end
end
assign add_cnt_bit =(state_c == DATA)&& end_cnt_bps ;
assign end_cnt_bit = add_cnt_bit && (cnt_bit ==(8-1)) ;
//将串行数据变为并行数据
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
uart_rx_r <= 1'b0;
end
else if((state_c==DATA)&&(cnt_bps==BPS_CNT_MAX/2-1))begin
uart_rx_r<={uart_rx_d3,uart_rx_r[7:1]};
//uart_rx_r[cnt_bit]<=uart_rx_d3;
end
end
//接收数据完成标志wei
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
rx_flag <= 1'b0;
end
else if(end_cnt_bit)begin
rx_flag<= 1'b1;
end
else begin
rx_flag<= 1'b0;
end
end
assign dout_data = uart_rx_r;
assign dout_flag = rx_flag;
endmodule
2、测试文件的编写
`timescale 1us/1us
module uart_rx_tb();
//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//reg define
reg clk ;
reg rst_n ;
reg din_rx ;
wire [7:0] dout_data ;
wire dout_flag ;
uart_rx uart_rx_inst(
/*input */ .clk (clk ),
/*input */ .rst_n (rst_n ),
/*input */ .din_rx (din_rx ),
/*output reg */ .dout_data (dout_data ),
/*output reg [7:0]*/ .dout_flag (dout_flag )
);
parameter CLOCK_CYCLE=20;
//产生时钟
initial clk = 1'b0;
always #10 clk = ~clk;
//产生激励
initial begin
rst_n = 1'b1;
din_rx = 1;//空闲为高电平
#(CLOCK_CYCLE*2);
rst_n = 1'b0;
#(CLOCK_CYCLE*20);
rst_n = 1'b1;
#1002;
//模拟UART接收模块的串行输入
//起始位
din_rx = 0;
#(434*CLOCK_CYCLE);
//数据位:8'b1011_0011
din_rx = 1;//LSB
#(434*CLOCK_CYCLE);
din_rx = 1;
#(434*CLOCK_CYCLE);
din_rx = 0;
#(434*CLOCK_CYCLE);
din_rx = 0;
#(434*CLOCK_CYCLE);
din_rx = 1;
#(434*CLOCK_CYCLE);
din_rx = 1;
#(434*CLOCK_CYCLE);
din_rx = 0;
#(434*CLOCK_CYCLE);
din_rx = 1;
#(434*CLOCK_CYCLE);
//停止位
din_rx = 1;
#(434*CLOCK_CYCLE);
#(CLOCK_CYCLE*100);
$stop;
end
endmodule
五、波形图仿真
在波形图中我们观察到dout_data的数据和发送数据不一样,这是因为UART是低位先发,所以在波形图中我们看到的输入和输出数据时相反的,这里需要我们注意一下。