一、信号说明
因为是接收端,所以输入的是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来发数据的,结果写完了不太对,就采用了这种朴素的方式验证,兄弟们有需求的自己改一下吧。