基于FPGA的UART接收设计
UART回环设计系统框图
首先介绍一下接下来几篇博客是设计一个串口循环的小项目,串口的电路与详细的协议这里不再介绍。详细绝大多数同学都明白。本次使用的硬件环境与软件环境如下:
FPGA开发板:米联客MA7035FA 100T版本
软件环境:vivado2019.1
我们要养好良好的FPGA设计习惯,首先是进行模块化设计画出实验的框图,然后对实验框图中的每个小模块进行时序设计。只有养成了良好的设计习惯才可能在复杂项目中不至于茫然无措,本次实验的实验框图如下:
通过观察实验框图可以看出此次项目需要设计两个模块,分别是uart_rx和uart_tx模块。这篇文章我们主要设计uart_rx模块,设计的详细过程如下。
uart_rx模块的时序图
因为FPGA程序特别考验逻辑性,没有好的逻辑几乎寸步难行,所以一个很好的方法是先把模块的时序图画出来。所以,uart_rx模块的时序图如下:
从时序图中可以看出,我们采用的主要是线性序列机的设计方法,有关线性序列机的概念我们这里不多赘述。
uart_rx模块的代码实现
相信有了上述的时序图,各位同学们就很容易可以设计出来相应的代码了。这里废话不多说,直接附上我写的代码,大家对应时序图和代码可以很容易看懂:
`timescale 1ns / 1ps
// *********************************************************************************
// Project Name : OSXXXX
// Author : zhangningning
// Email : nnzhang1996@foxmail.com
// Website :
// Module Name : uart_tx.v
// Create Time : 2020-01-04 13:01:46
// Editor : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date By Version Change Description
// -----------------------------------------------------------------------
// XXXX zhangningning 1.0 Original
//
// *********************************************************************************
module uart_rx(
input sclk ,
input rst_n ,
input rx ,
output reg po_flag ,
output reg [ 7:0] po_data
);
//========================================================================================\
//**************Define Parameter and Internal Signals**********************************
//========================================================================================/
parameter BAUD_NUM = 433 ;
reg rx1 ;
reg rx2 ;
reg rx2_reg ;
reg rx_flag ;
reg [14:0] cnt_baud ;
reg [ 3:0] bit_cnt ;
reg bit_flag ;
//========================================================================================\
//************** Main Code **********************************
//========================================================================================/
always @(posedge sclk)
rx1 <= rx;
always @(posedge sclk)
rx2 <= rx1;
always @(posedge sclk)
rx2_reg <= rx2;
always @(posedge sclk or posedge rst_n)
if(rst_n == 1'b0)
rx_flag <= 1'b0;
else if(bit_cnt == 4'd8 && bit_flag == 1'b1)
rx_flag <= 1'b0;
else if(rx2 == 1'b0 && rx2_reg == 1'b1)
rx_flag <= 1'b1;
else
rx_flag <= rx_flag;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
cnt_baud <= 'd0;
else if(rx_flag == 1'b0 || cnt_baud == BAUD_NUM)
cnt_baud <= 'd0;
else if(rx_flag == 1'b1)
cnt_baud <= cnt_baud + 1'b1;
else
cnt_baud <= cnt_baud;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
bit_flag <= 1'b0;
else if(cnt_baud == BAUD_NUM/2)
bit_flag <= 1'b1;
else
bit_flag <= 1'b0;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
bit_cnt <= 4'd0;
else if(bit_cnt == 4'd8 && bit_flag == 1'b1)
bit_cnt <= 4'd0;
else if(bit_flag)
bit_cnt <= bit_cnt + 1'b1;
else
bit_cnt <= bit_cnt;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
po_data <= 8'd0;
else if(bit_flag == 1'b1)
po_data <= {rx2,po_data[7:1]};
else
po_data <= po_data;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
po_flag <= 1'b0;
else if(bit_cnt == 4'd8 && bit_flag == 1'b1)
po_flag <= 1'b1;
else
po_flag <= 1'b0;
endmodule
强烈建议大家使用我上述的代码风格,对应时序图看简洁明了。
uart_rx测试模块的代码设计
如果只有上面的模块代码,对于一些刚入门的同学来说不容易观察也不容易调试,所以我将程序的测试代码附上。同学们联系两个程序,可以很容易的学会uart_rx模块的设计方法。
`timescale 1ns / 1ps
`define CLOCK 20
// *********************************************************************************
// Project Name : OSXXXX
// Author : zhangningning
// Email : nnzhang1996@foxmail.com
// Website :
// Module Name : uart_tx_tb.v
// Create Time : 2020-01-04 13:35:06
// Editor : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date By Version Change Description
// -----------------------------------------------------------------------
// XXXX zhangningning 1.0 Original
//
// *********************************************************************************
module uart_rx_tb();
reg sclk ;
reg rst_n ;
reg rx ;
wire po_flag ;
wire [ 7:0] po_data ;
initial begin
sclk <= 1'b0;
rst_n = 1'b0;
rx = 1'b1;
#(100*`CLOCK)
rst_n = 1'b1;
data_send(0);
data_send(1);
data_send(2);
data_send(3);
data_send(4);
data_send(5);
data_send(10);
data_send(11);
data_send(12);
data_send(13);
data_send(14);
data_send(15);
data_send(100);
data_send(101);
data_send(102);
data_send(103);
data_send(104);
data_send(105);
end
always #(`CLOCK/2) sclk <= ~sclk;
task data_send ;
input [ 7:0] data ;
integer i ;
begin
rx <= 1'b0 ;
#(433*`CLOCK);
for(i = 0;i <= 7;i = i+1)begin
rx <= data[i];
#(433*`CLOCK);
end
rx <= 1'b1 ;
#(433*`CLOCK);
end
endtask
uart_rx uart_rx_inst(
.sclk (sclk ),
.rst_n (rst_n ),
.rx (rx ),
.po_flag (po_flag ),
.po_data (po_data )
);
endmodule
测试模块使用了一个较常用的语法,即task语句。相信大家从上面的程序中可以学到task的使用规则。
结束语
对文章有什么看法或者需要更近一步交流的同学,可以加入下面的群: