一、信号说明
因为是接收端,所以输入的是RX,发送端一次发8位串行数据,在本模块中,要接收这8位数据并转换为并行数据,因为最终要实现数据的回环,这8位并行数据会在下一个模块中被转换为串行数据再发出去,需要一个数据有效信号,当它拉高时表示八位数据接收完成,可以进行并串转换并发送了。
时钟采用50Mhz,下面是信号列表
reg1,reg2,reg3 | rx打拍后的信号 |
work_en | 拉高表示正在接收信号 |
bote_cnt | 波特计数器,记到最大表示一个波特的结束 |
bit_flag | 信号稳定标志 |
rx_data | 并行数据 |
二、代码
上一篇我们简要介绍了UART,讲了UART的基本时序,下面给出UART接收端的代码。
module uart_rx
#(parameter bote=115200,
parameter period=50_000_000
)
(
input clk ,
input rst ,
input rx ,
output reg [7:0] data ,
output reg data_valid);
reg reg1;
reg reg2;
reg reg3;
reg start_flag;
reg work_en;
reg [15:0] bote_cnt;
reg bit_flag;
reg [3:0] bit_cnt;
reg [7:0] rx_data;
reg rx_flag;
parameter max=period/bote;
always@(posedge clk or negedge rst) begin
if(!rst)
reg1<=1'b1;
else
reg1<=rx;
end
always@(posedge clk or negedge rst) begin
if(!rst)
reg2<=1'b1;
else
reg2<=reg1;
end
always@(posedge clk or negedge rst) begin
if(!rst)
reg3<=1'b1;
else
reg3<=reg2;
end
always@(posedge clk or negedge rst) begin
if(!rst)
start_flag<=1'b0;
else if((reg2==1'b0)&&(reg3==1'b1)&&(work_en==1'b0))
start_flag<=1'b1;
else
start_flag<=1'b0;
end
always@(posedge clk or negedge rst) begin
if(!rst)
work_en<=1'b0;
else if(start_flag==1'b1)
work_en<=1'b1;
else if((bit_cnt==4'd8)&&(bit_flag==1'b1))
work_en<=1'b0;
else
work_en<=work_en;
end
always@(posedge clk or negedge rst) begin
if(!rst)
bote_cnt<=16'b0;
else if((bit_cnt==4'd8)&&(bit_flag==1'b1))
bote_cnt<=16'b0;
else if((work_en==1'b1)&&(bote_cnt==max-1))
bote_cnt<=16'b0;
else if(work_en==1'b1)
bote_cnt<=bote_cnt+1'b1;
else
bote_cnt<=16'b0;
end
always@(posedge clk or negedge rst) begin
if(!rst)
bit_flag<=1'b0;
else if(bote_cnt==max/2)
bit_flag<=1'b1;
else
bit_flag<=1'b0;
end
always@(posedge clk or negedge rst) begin
if(!rst)
bit_cnt<=4'b0;
else if((bit_flag==1'b1)&&(bit_cnt==4'd8))
bit_cnt<=4'b0;
else if(bit_flag==1'b1)
bit_cnt<=bit_cnt+1'b1;
else
bit_cnt<=bit_cnt;
end
always@(posedge clk or negedge rst) begin
if(!rst)
rx_data<=8'b0;
else if((bit_flag==1'b1)&&(bit_cnt>=4'd1)&&(bit_cnt<=4'd8))
rx_data<={reg3,rx_data[7:1]};
else
rx_data<=rx_data;
end
always@(posedge clk or negedge rst) begin
if(!rst)
rx_flag<=1'b0;
else if((bit_flag==1'b1)&&(bit_cnt==4'd8))
rx_flag<=1'b1;
else
rx_flag<=1'b0;
end
always@(posedge clk or negedge rst) begin
if(!rst)
data<=8'b0;
else if(rx_flag==1'b1)
data<=rx_data;
else
data<=data;
end
always@(posedge clk or negedge rst) begin
if(!rst)
data_valid<=1'b0;
else
data_valid<=rx_flag;
end
endmodule
在接收数据时有一个要注意的地方,就是什么时候读取,由于一个波特维持的时间很长,所以在波特的1/2处左右接收数据最好,这时候数据最稳定,对应代码中的bit_flag,就是实现这一功能的。
三、测试代码
测试代码如下:
`timescale 1ns/1ns
module uart_rx_tb();
parameter dada=5208*20;
reg clk;
reg rst;
reg rx ;
wire [7:0] data;
wire data_valid;
uart_rx
#(.bote(9600),
.period(50_000_000)
)
uart_rx_inst
(
.clk (clk ),
.rst (rst ),
.rx (rx ),
.data (data ),
.data_valid(data_valid));
always#10 clk=~clk;
initial begin
clk=1'b0;
rst=1'b0;
#20
rst=1'b1;
end
initial begin
rx=1'b1;
#200
rx=1'b0;
#dada
rx=1'b1;
#dada
rx=1'b1;
#dada
rx=1'b1;
#dada
rx=1'b1;
#dada
rx=1'b1;
#dada
rx=1'b1;
#dada
rx=1'b1;
#dada
rx=1'b1;
#dada
rx=1'b1;
#dada
rx=1'b1;
end
/* task rxx (input [7:0]test_data); begin:loop
integer i;
for(i=0;i<10;i=i+1) begin
case(i)
4'd0:rx<=1'b0;
4'd1:rx<=data[0];
4'd2:rx<=data[1];
4'd3:rx<=data[2];
4'd4:rx<=data[3];
4'd5:rx<=data[4];
4'd6:rx<=data[5];
4'd7:rx<=data[6];
4'd8:rx<=data[7];
4'd9:rx<=1'b1;
endcase
end
#(5208*20);
end
endtask */
/* initial begin
#200
rxx(8'd0);
rxx(8'd1);
rxx(8'd2);
rxx(8'd3);
rxx(8'd4);
rxx(8'd5);
rxx(8'd6);
rxx(8'd7);
end */
endmodule
四、总结
代码实际比较简单,只有一点是要解释一下的,UART是异步通信,从其他设备传来的RX是一个不同步的数据,为了减小信号在不同时钟域的亚稳态问题(涉及到数字集成电路设计的专业知识,感兴趣的兄弟们可以自己查一查),这里将输入的RX打了三拍,为了同步数据,也是为了减小亚稳态带来的问题。
关于测试代码,本来是想写一个task来发数据的,结果写完了不太对,就采用了这种朴素的方式验证,兄弟们有需求的自己改一下吧。
版权声明:本文为博主作者:毛豆仙人原创文章,版权归属原作者,如果侵权,请联系我们删除!
原文链接:https://blog.csdn.net/weixin_62966975/article/details/136486550