UART的RTL逻辑设计部分 - uart_tx
目录
- 1. IDLE:等待并数据到来
- 1.1. valid&ready 的Slave写握手
- 1.2. 异步FIFO 的Master读握手
- 2. TRANS:并串转换
- 2.1. 打包tx_data_frm
- 2.2. 并串转换
- 2.3. 发送完毕标志
- 3. 基于valid&ready握手 的UART_TX代码
- 4. 基于异步FIFO 的UART_TX代码
当UART完成了模块分解和输出之后,就可以开始设计逻辑了,设计好了再写代码!!!!
还是先从外围模块开始,UART_TX模块的功能比较简单,将多bit数据按照波特率解构成单bit发送到tx上即可。
由于tx端每一拍的数据都不同,即当前tx输出会与之前tx_data有关,因此输出输入是时序逻辑关系,此处使用状态机进行设计。
状态设计两种:IDLE和TRANS。咱先用语言描述每个状态要干啥 以及状态转移条件,再翻译成时序图,最终形成代码
IDLE状态下,一直等待要发送的数据出现,出现了就转入TRANS状态
TRANS状态下,将待发送的数据按照帧格式和波特率单bit发送,发完了转回IDLE。
状态转换图如下:
1. IDLE:等待并数据到来
IDLE状态什么都不用做,只需找到一个条件判断有待发送数据即可进入TRANS状态。
1.1. valid&ready 的Slave写握手
UART_TX的接口都给出了tx_data、tx_data_valid和tx_ready,摆明了告诉你将UART_TX当作Slave通过写握手判断数据到来。
标准握手时序
然后就是常见的写握手套路了,tx发完单bit数据了,就把tx_ready拉高,等待tx_data_valid有效时缓存tx_data
所以IDLE本来就是等待数据到来,所以IDLE与ready的时序对齐即可,如下图
红色箭头就表示将tx_data直接按照帧格式缓存,并转入TRANS状态。
绿色箭头表示tx_data_val拉低,所以在此之后tx_data可能发生了变化
这样的话,状态机就变成了
进入TRANS状态UART_TX要单bit发数据了,ready应该是一直拉低的,那么此时Master写过来数据那只能展宽等待了,效率不高。
解决办法是,咱就别缓存成一个tx_data_r变量了,咱直接一个异步FIFO缓存。
1.2. 异步FIFO 的Master读握手
如果中间弄个异步FIFO那么,UART_TX就不是被写的Slave了,而变成了Master与FIFO进行读握手!
那么进入IDLE状态后,rd_en一直拉高,直到empty为低,表示异步FIFO必定会读出数据,这时候拉低rd_en,并进入TRANS状态。
按照标准Master读握手时序,应该是rd_en一直拉高,直到ready为高,则表示数据必会被读出,在下一拍获取数据。只不过此处的ready变成了empty非
使用异步FIFO后,Master与UART_TX的时钟就可以不同了
时序图如下,注意empty为空时要延长rd_en
所以异步FIFO方案的状态机变成:
2. TRANS:并串转换
OK现在有要发送的数据了,TRANS这边需要作一下几个工作:
获取tx_data值,计算奇偶校验位,并根据帧结构打包成tx_data_frm
根据波特率和tx_clk频率,实现tx_data_frm到tx的并串转换
确定发送完毕条件,返回IDLE状态
由于我们提出了握手和FIFO两种方案,因此将分别做出解释。
2.1. 打包tx_data_frm
如果使用的valid&ready 握手的话,那么在检测到tx_data_val && ready
就直接将tx_data打包成tx_data_frm,这个之前已经讲到了。
如果使用异步FIFO的话,在TRANS状态下检测到valid有效,就可以缓存数据了
这个FIFO的valid标志只拉高一拍,要确认
奇偶校验位直接按位异或即可,结果为1表示有rdata有奇数个1,否则为偶数个。
2.2. 并串转换
在之后TRANS的时间内,要通过tx_data_frm移位的方式实现单bit发送
别忘了波特率!波特率其实就是tx数据更新的频率!需对tx_clk计数的来判断什么时候移位1次
一般来说tx_clk频率远高于波特率,所以一定可以计数的。
那么什么时候开始计数呢?
无论是ready握手还是异步FIFO,计数器的时序要与tx_data_frm对齐,所以开始计数的条件与tx_data_frm更新的条件 相同即可
ready握手时序图如下
异步FIFO的时序图如下
细节就是baud_cnt什么时候开始+1?如果是ready握手,那么baud_cnt+1的条件是
cur_state == TRANS
。如果是异步FIFO,TRANS下第二拍才能开始+1。
2.3. 发送完毕标志
完成TX_DATA_WIDTH+3
个位发送,就可以转入IDLE状态了。
我们怎么知道发送了这么多个bit?看来我们还需要另一个计数器bit_cnt计算发了多少bit,显然是baud_cnt加完一轮bit_cnt再加1。
以每16拍tx变换一次为例,时序图如下,所以说TRANS返回IDLE的条件就是bit_cnt == 10 && baud_cnt == 15
最终得到UART_TX的ready握手状态机如下:
异步FIFO的状态机如下
3. 基于valid&ready握手 的UART_TX代码
代码写的可能有点冗杂,但是逻辑是清晰的,并且相信DC综合。
module uart_tx_handshake#(parameter BAUD_RATE = 115200, //bit per secondparameter TX_CLK_FREQ = 50000000, //HZparameter TX_DATA_WIDTH = 16,parameter ASYNC_FIFO_DEPTH = 4096)(input rstn,input tx_clk,input [TX_DATA_WIDTH-1:0] tx_data,input tx_data_val,output tx_ready,output tx);localparam IDLE = 1'b0;
localparam TRANS = 1'b1;localparam FRAME_WIDTH = TX_DATA_WIDTH + 3;
localparam BIT_CNT_WIDTH = $clog2(FRAME_WIDTH); //if FRAME_WIDTH == 16 but width of bit_cnt is 4localparam TX_RATE = TX_CLK_FREQ/BAUD_RATE;
localparam BAUD_CNT_WIDTH = $clog2(TX_RATE);reg cur_state;
reg nxt_state;
reg [FRAME_WIDTH-1:0] tx_data_frm;
reg [BIT_CNT_WIDTH-1:0] bit_cnt;
reg [BAUD_CNT_WIDTH-1:0] baud_cnt;
reg tx_ready_r;
wire parity;always@(posedge tx_clk) beginif(!rstn)cur_state <= IDLE;else cur_state <= nxt_state;
endalways@(*) begincase(cur_state)IDLE:if(tx_ready_r && tx_data_val)nxt_state = TRANS;elsenxt_state = IDLE;TRANS:if(bit_cnt == FRAME_WIDTH-1 && baud_cnt == TX_RATE-1) nxt_state = IDLE;elsenxt_state = TRANS;default: nxt_state = IDLE;endcase
endalways@(posedge tx_clk) beginif(!rstn)tx_ready_r <= 1'b1;else if(cur_state == IDLE) beginif(tx_ready_r && tx_data_val)tx_ready_r <= 1'b0;endelse if(cur_state == TRANS) beginif(bit_cnt == FRAME_WIDTH-1 && baud_cnt == TX_RATE-1)tx_ready_r <= 1'b1;end
endassign tx_ready = tx_ready_r;always@(posedge tx_clk) beginif(!rstn)tx_data_frm <= 'b1;else if(cur_state == IDLE) beginif(tx_ready_r && tx_data_val)tx_data_frm <= {1'b1,parity,tx_data,1'b0};endelse if(cur_state == TRANS) beginif(baud_cnt == TX_RATE-1)tx_data_frm <= {1'b1,tx_data_frm[FRAME_WIDTH-1:1]};end
endassign parity = (tx_ready_r && tx_data_val)?(^tx_data+1'b1):1'b0;assign tx = tx_data_frm[0];always@(posedge tx_clk) beginif(!rstn)baud_cnt <= 'b0;else if(cur_state == IDLE)baud_cnt <= 'b0;else if(cur_state == TRANS) beginif(baud_cnt < TX_RATE-1)baud_cnt <= baud_cnt + 1'b1;elsebaud_cnt <= 'b0;end
endalways@(posedge tx_clk) beginif(!rstn)bit_cnt <= 'b0;else if(cur_state == IDLE)bit_cnt <= 'b0;else if(cur_state == TRANS) beginif(baud_cnt == TX_RATE-1) beginif(bit_cnt == FRAME_WIDTH-1) bit_cnt <= 1'b0;elsebit_cnt <= bit_cnt + 1'b1;end end
endendmodule
4. 基于异步FIFO 的UART_TX代码
module uart_tx_fifo#(parameter BAUD_RATE = 115200, //bit per secondparameter TX_CLK_FREQ = 50000000, //HZparameter TX_DATA_WIDTH = 16,parameter ASYNC_FIFO_DEPTH = 4096)(input rstn,input user_clk, //FIFO WRITE CLKinput tx_clk,input [TX_DATA_WIDTH-1:0] tx_data,input tx_data_val,output tx_ready,output tx);localparam IDLE = 1'b0;
localparam TRANS = 1'b1;localparam FRAME_WIDTH = TX_DATA_WIDTH + 3;
localparam BIT_CNT_WIDTH = $clog2(FRAME_WIDTH);localparam TX_RATE = TX_CLK_FREQ/BAUD_RATE;
localparam BAUD_CNT_WIDTH = $clog2(TX_RATE);reg cur_state;
reg nxt_state;
reg [FRAME_WIDTH-1:0] tx_data_frm;
reg [BIT_CNT_WIDTH-1:0] bit_cnt;
reg [BAUD_CNT_WIDTH-1:0] baud_cnt;
wire parity;wire fifo_full;
reg fifo_rd_en;
wire [TX_DATA_WIDTH-1:0] fifo_rdata;
wire fifo_rdata_val;
wire fifo_empty;always@(posedge tx_clk) beginif(!rstn)cur_state <= IDLE;else cur_state <= nxt_state;
endalways@(*) begincase(cur_state)IDLE:if(fifo_rd_en && !fifo_empty)nxt_state = TRANS;elsenxt_state = IDLE;TRANS:if(bit_cnt == FRAME_WIDTH-1 && baud_cnt == TX_RATE-1) //if FRAME_WIDTH == 16 but width of bit_cnt is 4nxt_state = IDLE;elsenxt_state = TRANS;default: nxt_state = IDLE;endcase
endalways@(posedge tx_clk) beginif(!rstn)fifo_rd_en <= 1'b0;else if(cur_state == IDLE) beginif(fifo_rd_en && !fifo_empty)fifo_rd_en <= 1'b0;endelse if(cur_state == TRANS) beginif(bit_cnt == FRAME_WIDTH-1 && baud_cnt == TX_RATE-1)fifo_rd_en <= 1'b1;end
endalways@(posedge tx_clk) beginif(!rstn)tx_data_frm <= 'b1;else if(cur_state == IDLE)tx_data_frm <= tx_data_frm;else if(cur_state == TRANS) beginif(fifo_rdata_val)tx_data_frm <= {1'b1,parity,fifo_rdata,1'b0};else if(baud_cnt == TX_RATE-1)tx_data_frm <= {1'b1,tx_data_frm[FRAME_WIDTH-1:1]};end
endassign parity = (fifo_rdata_val)?(^fifo_rdata+1'b1):1'b0;assign tx = tx_data_frm[0];always@(posedge tx_clk) beginif(!rstn)baud_cnt <= 'b0;else if(cur_state == IDLE)baud_cnt <= 'b0;else if(cur_state == TRANS) beginif(fifo_rdata_val)baud_cnt <= 'b0;else if(baud_cnt < TX_RATE-1)baud_cnt <= baud_cnt + 1'b1;elsebaud_cnt <= 'b0;end
endalways@(posedge tx_clk) beginif(!rstn)bit_cnt <= 'b0;else if(cur_state == IDLE)bit_cnt <= 'b0;else if(cur_state == TRANS) beginif(fifo_rdata_val)bit_cnt <= 'b0;else if(baud_cnt == TX_RATE-1)bit_cnt <= bit_cnt + 1'b1;end
endasync_fifo#(.ASYNC_FIFO_WIDTH (TX_DATA_WIDTH),.ASYNC_FIFO_DEPTH (ASYNC_FIFO_DEPTH)) u_async_fifo(.rstn (rstn),.wclk (user_clk),.wr_en (tx_data_val),.wdata (tx_data), .full (fifo_full),.rclk (tx_clk),.rd_en (fifo_rd_en),.rdata (fifo_rdata),.valid (fifo_rdata_val),.empty (fifo_empty)
);assign tx_ready = !fifo_full;endmodule
UART的RTL逻辑设计部分 - uart_tx相关推荐
- UART的RTL逻辑设计部分 - uart_rx
目录 1. IDLE:等待串数据到来 2. REC:串并转换 3. ARB:裁决校验位 3.1. valid&ready 的Slave读握手 3.2. 异步FIFO 的Master写握手 3. ...
- FPGA逻辑设计回顾(12)RAM以及ROM的RTL设计及其验证
前言 本文首发:FPGA逻辑设计回顾(12)RAM以及ROM的RTL设计及其验证 RAM以及ROM在FPGA中的实现大体有两种方式,一种是使用IP核定制,一种是RTL设计. 也许有人会反驳,那原语呢? ...
- 逻辑设计中复位的稳妥处理方法?
前言 看别人的好设计,能让自己从细节上提升,所谓好的设计,除了从各种指标上评判,我们都知道,设计的兼容性(参数化),可读性,可扩展性等等,可是既然是学习,我们本身就不知道什么样的才是好的,这些指标也分 ...
- FPGA逻辑设计回顾(11)FPGA以及PC中的RAM与ROM
文章目录 前言 RAM以及ROM在计算机中的应用 什么是存储器? 什么是硬盘驱动器? 其他类型的存储器 什么是RAM? RAM的类型 SRAM DRAM 什么是ROM? ROM的类型 掩膜ROM PR ...
- FPGA逻辑设计回顾(8)单比特信号的CDC处理方式之Toggle同步器
文章目录 前言 脉冲反馈展宽同步器技术补充说明 RTL代码 行为仿真 低电平脉冲的展宽处理 切换同步器的原理与实现 RTL实现 前言 本文首发自:FPGA逻辑设计回顾(8)单比特信号的CDC处理方式之 ...
- FPGA逻辑设计回顾(9)DDR的前世今生以及演变过程中的技术差异
文章目录 前言 DDR的前世SDRAM DDR的今生以及演变版本:DDR/DDR2/DDR3 DDR/DDR2/DDR3/DDR4之间简单对比 速度对比 电压对比 延迟对比 预取差异 电阻端接对比 物 ...
- FPGA逻辑设计回顾(1)新手易犯的逻辑综合错误之always块
前言 注:本文首发自FPGA逻辑设计回顾(1)新手易犯的逻辑综合错误之always块 本文中用到了如下的小标题: "心中有路"与综合推断 "心中无路"与无从推断 ...
- FPGA逻辑设计回顾(3)多比特信号上升沿检测的设计方式与陷阱?
前言 注:本文首发自FPGA逻辑设计回顾(3)多比特信号上升沿检测的设计方式与陷阱? 在总结本文最后的多比特上升沿检测之前,我们先把备用知识讲清楚,摊开来,以免造成模糊不清的默许! 逻辑运算符与位元运 ...
- 串行外设接口(Serial Peripheral Interface, SPI)逻辑设计部分 - spi_master
目录 1. baud_clk_gen 1.1. 代码 2. spi_master 2.1. IDLE:等待写入 2.2. WAIT_CS:等待选通 2.3. TRANS:传输 (CPOL ^ CPHA ...
最新文章
- windowsclient开发--为你的client进行国际化
- 批命令 set /a与set /p有哪些区别
- SAP HU 序列号里的Sales Order号码不一致导致PGI失败问题之对策
- macaca web(4)
- python之decorator理解
- jq获取页面高度_JQuery获取页面高度宽度
- Angular2-路由重定向的办法
- “朝三暮四”与“BPO”
- Prototype.js 1.4中文使用手册PDF版下载
- 服务器上怎么安虚拟主机呀,上线虚拟主机产品步骤
- 爱荷华大学计算机科学专业,爱荷华大学计算机科学本科.pdf
- OSG 场景图(Scene Graph) 类图
- 计算机毕业设计JAVA二手物品置换平台mybatis+源码+调试部署+系统+数据库+lw
- 永恒之蓝漏洞攻击完整步骤
- echarts r 地图_用R与Stata绘制地图,让文稿shinly起来
- 推荐到Oracle YEP计划
- 论文解读:SuperPoint: Self-Supervised Interest Point Detection and Description
- 1652:牡牛和牝牛
- C++中你不知道的namespace和using的用法
- linux设置nexus开机自启动_linux nexus自启动