UART的RTL逻辑设计部分 - uart_rx
目录
- 1. IDLE:等待串数据到来
- 2. REC:串并转换
- 3. ARB:裁决校验位
- 3.1. valid&ready 的Slave读握手
- 3.2. 异步FIFO 的Master写握手
- 3. 基于valid&ready握手 的UART_RX代码
- 4. 基于异步FIFO 的UART_RX代码
当UART完成了模块分解和输出之后,就可以开始设计逻辑了,设计好了再写代码!!!!
接受模块的流程应该是这样的:先是空闲,然后rx出现低电平表示单bit数据来了,开始串转并,转好之后判断校验位,未通过直接丢掉,通过了就等待下级的ready信号,当ready为高就发出去,
可以看到整个过程包含了三个状态
IDLE:等待rx低电平到来
REC:串并转换过程
ARB:根据校验位裁决收到的数据是否有效,有效就等待ready将收集的数据发出,否则直接抛弃。
转换图如下:
可不可以在等待发送rx_data的时候进行新的串并转换呢?可以,但你无法保证新的串并转换之后rx_data还未发送。所以UART_RX也分两种方式——valid&ready写握手 和 异步FIFO
其实将ARB纳入到REC也是可以的,UART_TX那边不把计算校验位纳入到TRANS里了嘛
1. IDLE:等待串数据到来
刚开始闲着,等待rx那边低电平出现,出现了就说明来数据了,然后转入REC阶段开始接受。
但是这里要注意一个点:rx来信号是不是跨时钟域的?
虽然说商议好了波特率,即我方UART_RX.rx和对方UART_TX.tx连线上信号的变化频率相同,但是时钟很可能不同
UART常用于芯片级模块通信,时钟都不同源,必然跨时钟域
UART_TX就不用考虑跨时钟域问题,因为UART_TX的Master一般与UART_TX是同时钟,要是异时钟直接上异步FIFO
那么想到单bit跨时钟域问题,由于rx_clk频率一般会远高于波特率,所以一定满足“3个沿”条件,故直接打两拍完事
异步时钟亚稳态 的解决方案——单bit信号
所以说开始串转并,或进入REC状态的条件则是rx_d2为低,如下时序图
2. REC:串并转换
这个阶段就是不断的收集rx_d2,组成一个多bit并数据rx_data,并获取校验位。
那么在什么时候将rx_d2的值拼入rx_data呢?
还得依靠俩计数器baud_cnt和bit_cnt,为了采样到稳定的rx_d2,可每一bit的中间时刻计入rx_data,这样一般是稳定的电平。
采样脉冲就是这样,采样脉冲头和尾都容易因为电压太低采成逻辑0,所以应该采中间。
假设rx_data宽度是8、rx每16拍变化一次,时序图如下
所以baud_cnt一开始一直是0,当检测到rx_d2为低时开始+1,在bit_cnt为1~8时记录串并转换成数据,
在bit_cnt为9时记录校验位,在bit_cnt为10时为终止位
实际上在bit_cnt为9时(此时rx为校验位)且校验位被记录下来就可以进入ARB状态了,这里为了让综合的电路简单一点所以就让终止位存在
而rx_d2拼入rx_data的时刻是bit_cnt为1~8且baud_cnt为7时,此时对应rx_d2每个bit的中间时刻。
此时状态机更新为
3. ARB:裁决校验位
这个部分要做的就是根据收取的校验位核验收取的并数据是否是正确的,如果是就与外界握手发出去,否则扔掉。
实际上,在baud_cnt == 7 && bit_cnt == 9
的那一拍直接看rx_d2和rx_data校验对不对就行,如果不对直接进入IDLE,否则就握手去。
握手传输rx_data又是套路。
3.1. valid&ready 的Slave读握手
这回是UART_RX被外部读,UART_RX是Slave。所以校验过了之后,就将rx_ready拉高,等待外部的读请求信号rx_req,当其为高时驱动rx_data,并将rx_data_val拉高一拍,时序图如下:
其实UART_RX也可以改成Master写握手,那么就要去掉rx_req,将rx_ready变成输入
时序就是进入ARB时直接拉高rx_data_val,等待对方的rx_ready,为高时认为写成功并转入IDLE
3.2. 异步FIFO 的Master写握手
时序和上图类似,不过就是将rx_data_val连接到FIFO的wr_en端,把rx_ready变成!empty即可。当rx_data_val && !empty
时表示成功写入,转入IDLE状态。
状态机最终变成
3. 基于valid&ready握手 的UART_RX代码
module uart_rx_handshake#(parameter BAUD_RATE = 115200, //bit per secondparameter RX_CLK_FREQ = 50000000, //HZparameter RX_DATA_WIDTH = 16,parameter ASYNC_FIFO_DEPTH = 4096)(input rstn,input rx_clk,input rx,input rx_req,output rx_ready,output [RX_DATA_WIDTH-1:0] rx_data,output rx_data_val);localparam IDLE = 2'b00;
localparam REC = 2'b01;
localparam TRANS = 2'b11;localparam FRAME_WIDTH = RX_DATA_WIDTH + 3;
localparam BIT_CNT_WIDTH = $clog2(FRAME_WIDTH);localparam RX_RATE = RX_CLK_FREQ/BAUD_RATE;
localparam BAUD_CNT_WIDTH = $clog2(RX_RATE);reg [1:0] cur_state;
reg [1:0] nxt_state;
reg rx_d1;
reg rx_d2;
reg rx_ready_r;
reg rx_parity;
reg [RX_DATA_WIDTH-1:0] rx_data_r;
reg rx_data_val_r;
reg [BIT_CNT_WIDTH-1:0] bit_cnt;
reg [BAUD_CNT_WIDTH-1:0] baud_cnt;always@(posedge rx_clk) beginif(!rstn) beginrx_d1 <= 1'b1;rx_d2 <= 1'b1;endelse beginrx_d1 <= rx;rx_d2 <= rx_d1;end
endalways@(posedge rx_clk) beginif(!rstn)cur_state <= IDLE;else cur_state <= nxt_state;
endalways@(*) begincase(cur_state)IDLE:if(!rx_d2)nxt_state = REC;elsenxt_state = IDLE;REC:if(bit_cnt == FRAME_WIDTH-1 && baud_cnt == RX_RATE-1) beginif(rx_parity == ^rx_data + 1'b1) //parity checknxt_state = TRANS;elsenxt_state = IDLE;endelsenxt_state = REC;TRANS:if(rx_data_val)nxt_state = IDLE;elsenxt_state = TRANS;default: nxt_state = IDLE;endcase
endalways@(posedge rx_clk) beginif(!rstn)rx_data_r <= 'b0;else if(cur_state == REC) beginif(bit_cnt >= 'd1 && bit_cnt <= FRAME_WIDTH-3 && baud_cnt == (RX_RATE-1)/2)rx_data_r <= {rx_d2,rx_data_r[RX_DATA_WIDTH-1:1]};end
endassign rx_data = rx_data_r;always@(posedge rx_clk) beginif(!rstn)rx_parity <= 'b0;else if(cur_state == REC) beginif(bit_cnt == FRAME_WIDTH-2 && baud_cnt == (RX_RATE-1)/2)rx_parity <= rx_d2;end
endalways@(posedge rx_clk) beginif(!rstn)baud_cnt <= 'b0;else if(cur_state == IDLE) beginif(!rx_d2)baud_cnt <= 'b1;endelse if(cur_state == REC) beginif(baud_cnt == RX_RATE-1)baud_cnt <= 'b0;elsebaud_cnt <= baud_cnt + 'b1; end
endalways@(posedge rx_clk) beginif(!rstn)bit_cnt <= 'b0;else if(cur_state == IDLE && !rx_d2)bit_cnt <= 'b0;else if(cur_state == REC && baud_cnt == RX_RATE-1)bit_cnt <= bit_cnt + 1'b1;
endalways@(posedge rx_clk) beginif(!rstn)rx_ready_r <= 1'b0;else if(cur_state == REC) beginif(bit_cnt == FRAME_WIDTH-1 && baud_cnt == RX_RATE-1 && (rx_parity == ^rx_data + 1'b1))rx_ready_r <= 1'b1;endelse if(cur_state == TRANS && rx_req)rx_ready_r <= 1'b0;
endassign rx_ready = rx_ready_r;always@(posedge rx_clk) beginif(!rstn)rx_data_val_r <= 1'b0;else if(cur_state == TRANS) beginif(rx_req && rx_ready_r)rx_data_val_r <= 1'b1;elserx_data_val_r <= 1'b0;end
endassign rx_data_val = rx_data_val_r;endmodule
4. 基于异步FIFO 的UART_RX代码
module uart_rx_handshake#(parameter BAUD_RATE = 115200, //bit per secondparameter RX_CLK_FREQ = 50000000, //HZparameter RX_DATA_WIDTH = 16,parameter ASYNC_FIFO_DEPTH = 4096)(input rstn,input user_clk,input fifo_rd_en,input rx_clk,input rx,input rx_ready,output [RX_DATA_WIDTH-1:0] rx_data,output rx_data_val);localparam IDLE = 2'b00;
localparam REC = 2'b01;
localparam TRANS = 2'b11;localparam FRAME_WIDTH = RX_DATA_WIDTH + 3;
localparam BIT_CNT_WIDTH = $clog2(FRAME_WIDTH);localparam RX_RATE = RX_CLK_FREQ/BAUD_RATE;
localparam BAUD_CNT_WIDTH = $clog2(RX_RATE);reg cur_state;
reg nxt_state;
reg rx_d1;
reg rx_d2;
reg rx_parity;
reg [RX_DATA_WIDTH-1:0] fifo_wdata;
reg fifo_wr_en;
reg [BIT_CNT_WIDTH-1:0] bit_cnt;
reg [BAUD_CNT_WIDTH-1:0] baud_cnt;
wire fifo_empty;
wire fifo_full;always@(posedge rx_clk) beginif(!rstn) beginrx_d1 <= 1'b1;rx_d2 <= 1'b1;endelse beginrx_d1 <= rx;rx_d2 <= rx_d1;end
endalways@(posedge rx_clk) beginif(!rstn)cur_state <= IDLE;else cur_state <= nxt_state;
endalways@(*) begincase(cur_state)IDLE:if(!rx_d2)nxt_state = REC;elsenxt_state = IDLE;REC:if(bit_cnt == FRAME_WIDTH-1 && baud_cnt == RX_RATE-1) beginif(rx_parity == ^rx_data + 1'b1) //parity checknxt_state = TRANS;elsenxt_state = IDLE;endelsenxt_state = REC;TRANS:if(fifo_wr_en && !fifo_full)nxt_state = IDLE;elsenxt_state = TRANS;default: nxt_state = IDLE;endcase
endalways@(posedge rx_clk) beginif(!rstn)fifo_wdata <= 'b0;else if(cur_state == TRANS) beginif(bit_cnt >= 'd1 && bit_cnt <= FRAME_WIDTH-3 && baud_cnt == (RX_RATE-1)/2)fifo_wdata <= {rx_d2,fifo_wdata[RX_DATA_WIDTH-1:1]};end
endalways@(posedge rx_clk) beginif(!rstn)rx_parity <= 'b0;else if(cur_state == REC) beginif(bit_cnt == FRAME_WIDTH-2 && baud_cnt == (RX_RATE-1)/2)rx_parity <= rx_d2;end
endalways@(posedge rx_clk) beginif(!rstn)baud_cnt <= 'b0;else if(cur_state == IDLE) beginif(!rx_d2)baud_cnt <= 'b1;endelse if(cur_state == REC) beginif(baud_cnt == RX_RATE-1)baud_cnt <= 'b0;elsebaud_cnt <= baud_cnt + 'b1; end
endalways@(posedge rx_clk) beginif(!rstn)bit_cnt <= 'b0;else if(cur_state == IDLE && !rx_d2)bit_cnt <= 'b0;else if(cur_state == REC && baud_cnt == RX_RATE-1)bit_cnt <= bit_cnt + 1'b1;
endalways@(posedge rx_clk) beginif(!rstn)fifo_wr_en <= 1'b0;else if(cur_state == REC) beginif(bit_cnt == FRAME_WIDTH-1 && baud_cnt == RX_RATE-1 && (rx_parity == ^rx_data + 1'b1))fifo_wr_en <= 1'b1;endelse if(cur_state == TRANS) beginif(fifo_wr_en && fifo_full)fifo_wr_en <= 1'b0;end
endasync_fifo#(.ASYNC_FIFO_WIDTH (RX_DATA_WIDTH),.ASYNC_FIFO_DEPTH (ASYNC_FIFO_DEPTH)) u_async_fifo(.rstn (rstn),.wclk (rx_clk),.wr_en (fifo_wr_en),.wdata (fifo_wdata), .full (fifo_full),.rclk (user_clk),.rd_en (fifo_rd_en),.rdata (rx_data),.valid (rx_data_val),.empty (fifo_empty)
);assign rx_ready = !fifo_empty;endmodule
UART的RTL逻辑设计部分 - uart_rx相关推荐
- UART的RTL逻辑设计部分 - uart_tx
目录 1. IDLE:等待并数据到来 1.1. valid&ready 的Slave写握手 1.2. 异步FIFO 的Master读握手 2. TRANS:并串转换 2.1. 打包tx_dat ...
- 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 ...
最新文章
- boyer moore算法 java_Boyer-Moore算法
- Android10.0 Binder通信原理(六)-Binder数据如何完成定向打击
- 深入理解angularjs $watch ,$apply 和 $digest --- 理解数据绑定过程
- 一个月内取得多项技术进展,详解阿里AI背后的故事
- 计算机英语四六级对调剂有影响吗,英语四六级对考研有什么影响?不过会被歧视吗?...
- easyui datagrid 绑定json对象属性的属性
- TQ210 —— 点亮LED
- 70. Climbing Stairs 题解
- Silverlight/WPF/WP7一周学习导读(12月6-12月12)
- 计算机毕业设计论文资料查找
- C# 添加Excel水印
- 微信信用分-服务商模式(免密代扣-免确认订单模式——智能零售-称重柜)
- 用css hack解决IE5 IE5.5 IE6 Firefox浏览器兼容性
- gitlab项目自动同步到测试服务器
- 2021-2027中国工业物联网通信产品市场现状及未来发展趋势
- CAD2018安装计算机黑屏,3dmax2018一打开就闪退的三种原因和解决方法
- EMD方法基本基本知识
- 用matlab画OCC控制电路,基于单周期(OCC)控制的CCM PFC
- JAVA中break和continue用法
- 解决error: unknown type name ‘__u8’问题,认识__u8,__u16,__u32,__u64的大小及作用