改进说明:
最早的第一版:使用baud_clk,没有FIFO
第二版:使用FIFO,在FIFO的wr_clk是用户的高速时钟,FIFO的rd_clk是uart_tx_engine的低速baud_clk。
功能
用户端接口为FIFO接口,用户将要发送数据写入FIFO,该UART_TX模块将并行数据转成串行数据,依次发出。串口的“串”字,就是这个意思,并转串,通过一根信号线把数据发送出去
需求分析
关于FIFO
- FIFO的位宽:是否可以让用户配置,8bit是默认的,如果可以让用户可配置,有时候32bit可能用起来更方便
- FIFO的深度(容量):考虑到常见应用场景:
- 打印:一次打印一句话,20个单词,约140个字母char(8bit),深度可以选择256,容量为256×8bit;如果考虑到一次打印句子较多(如3句话)或一句较长,容量可以设置为1024×8bit。
- 通信:跟上层应用关系较大,可以让用户端等,深度无特别要求
- 两边的时钟:FIFO写端口,为用户端口,写时钟为高速的用户时钟,如50MHz;FIFO读端口,为模块内部端口,读时钟为低速的UART处理时钟,即波特率的1倍时钟(发送端不需要16倍)
- FWFT:让read latency=0
关于架构
- uart_tx为顶层
- 底层由FIFO和uart_tx_engine组成
接口
信号名 | 方向 | 含义 |
reset | input | 模块复位信号 |
fifo_wr_clk | input | 用户端接口时钟 |
fifo_din | Input | 用户端要发送并行数据 |
fifo_wr_en | Input | 用户端写使能 |
fifo_full | output | 用户端FIFO满标志 |
fifo_wr_ack | output | 用户端写响应状态 |
fifo_overflow | output | 用户端FIFO写溢出标志 |
baud_clk | input | 发送波特率的时钟 |
uart_txd | output | 发送的串行数据 |
实现思路
uart_tx_engine设计
- 功能:若现在空闲,就判断FIFO状态,若不空,就取一个字节,开始发送;发送完再去判断FIFO状态
- 设计要点:
- 由于没有分支,可以不用状态机,而采用可控线性序列机(计数器控制)
- 时钟采用波特率时钟,这个时钟由外部传递,系统顶层通过mmcm产生,节省整个系统的资源,由于波特率速率太低,低于mmcm的最低输出频率(约4M),需要自己写时钟分频的逻辑
调试过程中总结
- 整个的思路可以先用时序图画出来,这样思路很清晰
- 写代码时,对于一些判断条件可以先不用想的特别清楚,先写出来,仿真时,自然就能发现问题,然后再修改,这样效率高一些
- 善于用计数器控制状态
调试中已解决问题的总结:
- 为什么我的FIFO复位信号不起作用?——因为我给FIFO复位时,FIFO的一个端口时钟也被复位了。FIFO复位时,必须时钟要有,所以逻辑设计时,整个逻辑的时钟(比如用clk_wiz)是不能复位的
- 为什么FIFO的full一直是1?——因为复位的后,不能马上使用FIFO,有一个间隔(NO ACCESS ZONE),手册pg057是说是复位宽度+60个最慢的时钟周期——这里需要实测一下
- FIFO的复位信号宽度多长才是有效的?——对于异步复位(两边时钟不同时就是异步复位),需要至少3个最慢时钟周期
目前模块问题
reset信号不好用
- 原因:因为reset要同时复位FIFO,FIFO的复位需要至少3个最慢时钟周期,对于波特率来说这样很长;而且复位后需要等6-7个波特率时钟周期,FIFO才能使用,这个时间也太长了。
- 解决思路:
- 提高FIFO_RD_CLK频率——这样对现在代码结构改动比较大;
- FIFO复位只在上电/重新加载/重大错误时进行,复位时间长一些——这也是佐证为什么上电加载好后,很多模块需要很久才能ready的原因,如mmcm需要ms级别。