当UART完成了模块分解和输出之后,就可以开始设计逻辑了,设计好了再写代码!!!!
接受模块的流程应该是这样的:先是空闲,然后rx出现低电平表示单bit数据来了,开始串转并,转好之后判断校验位,未通过直接丢掉,通过了就等待下级的ready信号,当ready为高就发出去,
可以看到整个过程包含了三个状态
IDLE:等待rx低电平到来
REC:串并转换过程
ARB:根据校验位裁决收到的数据是否有效,有效就等待ready将收集的数据发出,否则直接抛弃。
转换图如下:
可不可以在等待发送rx_data的时候进行新的串并转换呢?可以,但你无法保证新的串并转换之后rx_data还未发送。所以UART_RX也分两种方式——valid&ready写握手 和 异步FIFO
其实将ARB纳入到REC也是可以的,UART_TX那边不把计算校验位纳入到TRANS里了嘛
刚开始闲着,等待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又是套路。
这回是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
时序和上图类似,不过就是将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&#64;(posedge rx_clk) beginif(!rstn) beginrx_d1 <&#61; 1&#39;b1;rx_d2 <&#61; 1&#39;b1;endelse beginrx_d1 <&#61; rx;rx_d2 <&#61; rx_d1;end
endalways&#64;(posedge rx_clk) beginif(!rstn)cur_state <&#61; IDLE;else cur_state <&#61; nxt_state;
endalways&#64;(*) begincase(cur_state)IDLE:if(!rx_d2)nxt_state &#61; REC;elsenxt_state &#61; IDLE;REC:if(bit_cnt &#61;&#61; FRAME_WIDTH-1 && baud_cnt &#61;&#61; RX_RATE-1) beginif(rx_parity &#61;&#61; ^rx_data &#43; 1&#39;b1) //parity checknxt_state &#61; TRANS;elsenxt_state &#61; IDLE;endelsenxt_state &#61; REC;TRANS:if(rx_data_val)nxt_state &#61; IDLE;elsenxt_state &#61; TRANS;default: nxt_state &#61; IDLE;endcase
endalways&#64;(posedge rx_clk) beginif(!rstn)rx_data_r <&#61; &#39;b0;else if(cur_state &#61;&#61; REC) beginif(bit_cnt >&#61; &#39;d1 && bit_cnt <&#61; FRAME_WIDTH-3 && baud_cnt &#61;&#61; (RX_RATE-1)/2)rx_data_r <&#61; {rx_d2,rx_data_r[RX_DATA_WIDTH-1:1]};end
endassign rx_data &#61; rx_data_r;always&#64;(posedge rx_clk) beginif(!rstn)rx_parity <&#61; &#39;b0;else if(cur_state &#61;&#61; REC) beginif(bit_cnt &#61;&#61; FRAME_WIDTH-2 && baud_cnt &#61;&#61; (RX_RATE-1)/2)rx_parity <&#61; rx_d2;end
endalways&#64;(posedge rx_clk) beginif(!rstn)baud_cnt <&#61; &#39;b0;else if(cur_state &#61;&#61; IDLE) beginif(!rx_d2)baud_cnt <&#61; &#39;b1;endelse if(cur_state &#61;&#61; REC) beginif(baud_cnt &#61;&#61; RX_RATE-1)baud_cnt <&#61; &#39;b0;elsebaud_cnt <&#61; baud_cnt &#43; &#39;b1; end
endalways&#64;(posedge rx_clk) beginif(!rstn)bit_cnt <&#61; &#39;b0;else if(cur_state &#61;&#61; IDLE && !rx_d2)bit_cnt <&#61; &#39;b0;else if(cur_state &#61;&#61; REC && baud_cnt &#61;&#61; RX_RATE-1)bit_cnt <&#61; bit_cnt &#43; 1&#39;b1;
endalways&#64;(posedge rx_clk) beginif(!rstn)rx_ready_r <&#61; 1&#39;b0;else if(cur_state &#61;&#61; REC) beginif(bit_cnt &#61;&#61; FRAME_WIDTH-1 && baud_cnt &#61;&#61; RX_RATE-1 && (rx_parity &#61;&#61; ^rx_data &#43; 1&#39;b1))rx_ready_r <&#61; 1&#39;b1;endelse if(cur_state &#61;&#61; TRANS && rx_req)rx_ready_r <&#61; 1&#39;b0;
endassign rx_ready &#61; rx_ready_r;always&#64;(posedge rx_clk) beginif(!rstn)rx_data_val_r <&#61; 1&#39;b0;else if(cur_state &#61;&#61; TRANS) beginif(rx_req && rx_ready_r)rx_data_val_r <&#61; 1&#39;b1;elserx_data_val_r <&#61; 1&#39;b0;end
endassign rx_data_val &#61; rx_data_val_r;endmodule
4. 基于异步FIFO 的UART_RX代码
module uart_rx_handshake#(parameter BAUD_RATE &#61; 115200, //bit per secondparameter RX_CLK_FREQ &#61; 50000000, //HZparameter RX_DATA_WIDTH &#61; 16,parameter ASYNC_FIFO_DEPTH &#61; 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 &#61; 2&#39;b00;
localparam REC &#61; 2&#39;b01;
localparam TRANS &#61; 2&#39;b11;localparam FRAME_WIDTH &#61; RX_DATA_WIDTH &#43; 3;
localparam BIT_CNT_WIDTH &#61; $clog2(FRAME_WIDTH);localparam RX_RATE &#61; RX_CLK_FREQ/BAUD_RATE;
localparam BAUD_CNT_WIDTH &#61; $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&#64;(posedge rx_clk) beginif(!rstn) beginrx_d1 <&#61; 1&#39;b1;rx_d2 <&#61; 1&#39;b1;endelse beginrx_d1 <&#61; rx;rx_d2 <&#61; rx_d1;end
endalways&#64;(posedge rx_clk) beginif(!rstn)cur_state <&#61; IDLE;else cur_state <&#61; nxt_state;
endalways&#64;(*) begincase(cur_state)IDLE:if(!rx_d2)nxt_state &#61; REC;elsenxt_state &#61; IDLE;REC:if(bit_cnt &#61;&#61; FRAME_WIDTH-1 && baud_cnt &#61;&#61; RX_RATE-1) beginif(rx_parity &#61;&#61; ^rx_data &#43; 1&#39;b1) //parity checknxt_state &#61; TRANS;elsenxt_state &#61; IDLE;endelsenxt_state &#61; REC;TRANS:if(fifo_wr_en && !fifo_full)nxt_state &#61; IDLE;elsenxt_state &#61; TRANS;default: nxt_state &#61; IDLE;endcase
endalways&#64;(posedge rx_clk) beginif(!rstn)fifo_wdata <&#61; &#39;b0;else if(cur_state &#61;&#61; TRANS) beginif(bit_cnt >&#61; &#39;d1 && bit_cnt <&#61; FRAME_WIDTH-3 && baud_cnt &#61;&#61; (RX_RATE-1)/2)fifo_wdata <&#61; {rx_d2,fifo_wdata[RX_DATA_WIDTH-1:1]};end
endalways&#64;(posedge rx_clk) beginif(!rstn)rx_parity <&#61; &#39;b0;else if(cur_state &#61;&#61; REC) beginif(bit_cnt &#61;&#61; FRAME_WIDTH-2 && baud_cnt &#61;&#61; (RX_RATE-1)/2)rx_parity <&#61; rx_d2;end
endalways&#64;(posedge rx_clk) beginif(!rstn)baud_cnt <&#61; &#39;b0;else if(cur_state &#61;&#61; IDLE) beginif(!rx_d2)baud_cnt <&#61; &#39;b1;endelse if(cur_state &#61;&#61; REC) beginif(baud_cnt &#61;&#61; RX_RATE-1)baud_cnt <&#61; &#39;b0;elsebaud_cnt <&#61; baud_cnt &#43; &#39;b1; end
endalways&#64;(posedge rx_clk) beginif(!rstn)bit_cnt <&#61; &#39;b0;else if(cur_state &#61;&#61; IDLE && !rx_d2)bit_cnt <&#61; &#39;b0;else if(cur_state &#61;&#61; REC && baud_cnt &#61;&#61; RX_RATE-1)bit_cnt <&#61; bit_cnt &#43; 1&#39;b1;
endalways&#64;(posedge rx_clk) beginif(!rstn)fifo_wr_en <&#61; 1&#39;b0;else if(cur_state &#61;&#61; REC) beginif(bit_cnt &#61;&#61; FRAME_WIDTH-1 && baud_cnt &#61;&#61; RX_RATE-1 && (rx_parity &#61;&#61; ^rx_data &#43; 1&#39;b1))fifo_wr_en <&#61; 1&#39;b1;endelse if(cur_state &#61;&#61; TRANS) beginif(fifo_wr_en && fifo_full)fifo_wr_en <&#61; 1&#39;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 &#61; !fifo_empty;endmodule