Menu Close

UART数据缓冲(FIFO)

在UART 综合应用-协议与数据包文章中,我们介绍了从FPGA 中通过uart 读取数据,CPU 写入数据到FPGA 外设的过程。在一些中大型项目中,往往会增加数据链路层,用来隔离应用数据和具体的硬件传输方式。 也就是说,应用程序只是负责数据发送,处理,而不关系具体使用什么样的硬件来发送(UART , I2C ,SPI, ETHERNET)。 而phy模块只管数据的接收和发送, 但是接收和发送的数据,phy 模块不再关心。这样,就需要不同的模块来进行分层处理,使得整个工程结构明晰,同时根据不同的项目需要, 模块可以在不同的项目上重复使用。

1 对功模块能重新分层

之前的文章中,uart项目的结构为:

图1

经过分层处理后,重新规划的模块结构为:

 

图2

从图2 中可以看出,整个工程由2个大模块构成:uart_app, uart_wrapper。这2 个模块直接是通过fifo 相关的数据信号来传递信息的。

  • uart_app 模块主要负责对数据的解析,发送等,不再关心具体使用什么样的phy 来进行通讯。
  • uart_wrapper 模块主要是接收rx来的数据, 或者是将数据发送到tx上,实现完整的UART phy 功能,而不再关心数据具体是什么含义,也不对数据做任何的解析。

通过这样的分层设计,可以看出, uart_app 模块可以给很多项目使用, 具体使用UART, SPI,I2C 等,都可以正常通讯。 uart_wrapper 模块也是可以给很多项目来重用的。因为这个模块值负责接收,发送数据,不对数据解析。 这样的分层结构,每个模块的功能清晰,重用性高,从而实现了phy层和数据链路层的分离。

2 uart_wrapper 结构

图3

uart_wrapper 封装了完整的uart 模块的接口。 通过数据总线方式 和外部设备通讯。 它是有uart_engine 和uart_phy 两个部分组成。 其中uart_phy部分,在之前的文章中做过详细的介绍。一端是uart_engine 部分负责接收、发送信号到uart_phy,另外一端是通过数据总线和外部设备沟通。uart_engine 主要负责uart 接收来的数据存储到rx fifo中,同时提供包接收指示。 同时,也负责从tx fifo 中得到数据, 按照uart_phy 所需要的时序,发送数据到uart_phy。

uart_wrapper 代码:


 

 

uart_engine 模块包括tx_fifo.v 和rx_fifo.v 两个部分。其中

rx_fifo.v 代码如下:


 

 

其中fifo IP的定义 适用于 tx_fifo.v 和rx_fifo.v  这个fifo 定义为 读写时钟分离(异步时钟),深度为256,读写数据宽度为9bit , 增加了写counter 属性。

图4

状态机部分比较简单,主要是

负责接收uart模块传递上来的数据有效使能rx_rdy、8bit数据rx_data、包结束信号rx_eop。将数据以一定格式写入rx端的FIFO 中,等待应用层给读使能。程序实现流程如下:

初始化:reset作为复位信号对变量进行初始化操作

状态0:FIFO深度0-255,包长范围0-255,为确保每次能存入一个完整数据包,需要保证剩余空间256。判断wrusedw 为0跳转到状态1

状态1:等待rx_rdy 即数据有效使能信号,来一个信号收8bit数据,给一个1作为控制字信息合并为9bit数据 {1’b1, rx_data},同时给写使能,将数据写入FIFO。如果一直没有rx_rdy信号并且来了包结束信号rx_eop,写9’b0作为包隔断,同时给上层应用一个rx_pkg_plus指示FIFO 里面存入了一个完整的数据包。然后跳转到状态0。(由于rx_eop与rx_rdy至少隔了12个波特率使能信号的时间,所以当回到状态0再去判断wrusedw 时,已经可以准确拿到wrusedw 的值了)

rx_fifo 流程图

 

tx_fifo.v 代码:


 

 

fifo IP 核部分和rx fifo中的定义相同(9bit 数据, 异步时钟, 增加了写counter)。

状态机部分:

当底层uart有空可以发数据时,读出应用层写入FIFO 的数据,并给出数据有效使能信号。具体实现流程如下:

初始化:reset作为复位信号对状态机进行初始化操作

状态0:FIFO有数就到状态1,同时给出读使能

状态1:复位读使能跳转到状态2

状态2:如果tx_fifo_dout[8] == 1,截取低8位发送给uart模块同时给出数据有效使能信号,uart模块反馈收到数据后将使能信号拉低,跳转到状态3,否则跳转到状态0

状态3:等待uart将ack信号拉低,然后跳回状态0(由于ack是一个长电平信号,不等待ack拉低直接到0状态会导致重复给uart发送数据)

 

tx_fifo 流程图

 

3 user_app 结构

user_app模块包含:tx_app,rx_app, seg_dev,cmd_decoder 等, 其中seg_dev, cmd_decoder 在之前的文章中有所介绍。这里主要介绍一下tx_app.v 和rx_app.v模块。

  • rx_app模块:当底层的FIFO有完整数据包时,读取数据包内容,主要由状态机完成;按照双方约定的协议格式对读出的数据进行解析,最后将正确的读写命令、数据等信号传递给上层应用模块,主要由从状态机完成。具体流程如下:

 

rx_app.v 代码:


 

 

端口信号解释:

sys_clk:时钟域模块传递进来的系统复位信号

rx_pkg_plus:rx_fifo传递的单脉冲信号,表示rx_fifo已经写入一个完整数据包

rx_fifo_dout:从rx_fifo读出的数据

rx_fifo_rd :读rx_fifo 使能,当rx_fifo写入一个完整包开始读

pkg_type:从设备地址,表示计算机准备对那个设备进行操作

pkg_op:读/写 控制字

pkg_data:计算机写入到fpga的数据

pkg_valid:传递给应用层的数据和命令有效信号

rst:时钟域模块传递进来的系统复位信号

 

状态机详解:

初始化:rst信号对整个状态机进行复位

  • 状态0:如果有完整数据包写入fifo完成,发读使能,跳转到状态1
  • 状态1:等待一个时钟周期,跳转到状态2
  • 状态2:判断读出数据最高控制bit是否为高,为高表示数据有效,为高且读出的length小于3,则不满足数据包协议,跳转到状态11,将fifo读空;如果控制bit为高且length 大于等于3,给读使能,跳转到状态3;如果最高控制bit为低,表示fifo读空,表示数据包有问题,跳转到错误指示状态14
  • 状态3:等待一个时钟周期,跳转到状态4
  • 状态4:判断读出的数据最高位是否为高,为高表示数据有效,根据数据包协议将[7:0]数据存入pkg_op,跳转到状态5;为低,表示数据包错误,跳转到状态14
  • 状态5:等待一个时钟周期,跳转到状态6
  • 状态6:判断读出的数据最高位是否为高为高,根据数据包协议将[7:0]数据存入pkg_type,跳转到状态7;为低,表示数据包错误,跳转到状态14
  • 状态7:等待一个时钟周期,跳转到状态8
  • 状态8:判断读出最高控制bit是否为高,为高且此时rx_len == rx_cnt,表示实际写入FIFO 数据比lenth 长,命令错误,同时需要将FIFO清空,跳转到状态11;控制bit为高且rx_len >= rx_cnt,表示数据未读完,返回状态7等待一个时钟。如果控制bit为低,且rx_len == rx_cnt 表示数据包正确,读完,跳转到状态15;如果控制bit为低但是rx_len >= rx_cnt,说明实际写入FIFO 数据比lenth短,跳转到状态14
  • 状态11-状态12:将剩余fifo中的数据读完,为下次写入fifo准备空间
  • 状态14:数据包错误指示状态
  • 状态15:各种数据清0,等待一个时钟,确保packets_count 拿到准确的数据。

tx_app 模块

当有读命令时,将读设备(SW)反馈的并行数据按照一定格式写入tx_fifo。

 

端口信号解释:

sys_clk:时钟域模块传递进的系统时钟

tx_fifo_cnt:tx_fifo 中写入了多少字节计数,以此判断fifo剩余空间是否足够

tx_fifo_wr:写tx_fifo 的写使能

tx_fifo_din:写tx_fifo的写数据

rd_sw_cmd:计算机发送给fpga的读设备命令

SW:读设备发送的读数据

rst:时钟域模块传递进来的系统复位信号

状态机详解:

在确保FIFO 有足够空间后,如果接收到读命令,就将读设备数据同步到系统时钟,并且将数据按照一定格式存入FIFO,等待底层模块读取。具体流程如下:

步骤1:将读设备输入信号打两拍同步到系统时钟

步骤2:利用状态机实现数据的存储

  • 初始化:reset作为复位信号对状态机进行初始化操作
  • 状态0:判断FIFO可用空间是否足够,足够就跳转到状态1
  • 状态1:收到读命令,给出写FIFO使能,同时给出9bit写数据{1’b1,key_r1}最高位1作为控制字,表示后面的8bit设备数据有效。跳转到状态2
  • 状态2:由于本次工程的目的是接收一次命令读一次,给出9bit写数据{1’b0, 8’00},然后跳转到状态3
  • 状态3:由于本次工程的目的是接收一次命令读一次,所以需要等待写命令拉低,然后跳转到状态4
  • 状态4:由于tx_fifo_cnt 生效需要等待2个时钟,保险期间,状态4作为等待状态。跳转到状态0

tx_app.v 代码:


 

 

下载调试:

1)08 00 01 aa bb cc ee ff

2)05 00 01 12 34

Quartus 程序代码:

[content_control]

uart_fifo_2022_0729.zip

[/content_control]

Posted in FPGA, FPGA 教材教案, Verilog, 教材与教案, 文章