一、 uart 原理

协议由:起始位、数据(8位)、校验位、停止位,共11位组成,空闲时保持高位。
详细的协议不做赘述。

二、使用步骤

设计发送和接收模块,顶层模块给出发送使能和发送数据。

​ uart没有时钟线,我们只需要规定发送和接收方的波特率相同即可。例如波特率选择115200,时钟100M,那么1位传输所需要保持的时间是100M / 115200 = 868 (周期),所以我们需要计数器 baud_cnt ,它的最大值是867,记录单比特传输时间。

1.uart发送模块


输入输出:

​ 1、clk,rstn,tx_de,[7:0]data

​ 2、tx,tx_done

​ 定义若干个变量(重点):

​ 1、baud_cnt : 传输1bit需要维持的时间。

​ 2、baud_time :完整的传输一次协议中传输1bit的次数。

​ 3、tx_flag:表征一次协议传输有效,传输完成一次协议需要拉低,由顶层给的rx_de控制拉高。

​ 4、bit_flag : 在传输1bit 的中间时刻拉高。

​ 5、bit_cnt:用来表示传输的位数,和baud_time类似,但是是在bit_flag的控制下产生的(中间时刻采样),tx输出的数据也是在bit_cnt的控制下输出的。

​ 6、tx : 当bit_flag为1时,对应bit_cnt 把tx_data_latch的对应位赋值给tx。

​ 几个变量:

​ 1、tx_data_latch:由顶层发的tx_data锁存而来,保证后续的时序。

​ 2、baud_end,baud_mid,check,check_sum,

​ 语法:

​ 1、generate

​ if(check==0)

​ begin : “label1”

​ else if(check==1)

​ begin: “label2”

​ endgenerate

​ 顶层传参check后,在generate中只会执行其中一条。

2、uart 接收模块

输入输出:

​ 1、clk,rstn,rx

​ 2、[7:0] rx_data, rx_de

​ 几个重要模块:

​ 1、baud_cnt : 传输1bit需要维持的时间。

​ 2、baud_time :完整的传输一次协议中传输1bit的次数。

​ 3、rx_flag:表征一次协议传输有效,传输完成一次协议需要拉低,由rx的下降沿给出。

​ 4、bit_flag : 在传输1bit 的中间时刻拉高。

​ 5、bit_cnt:用来表示传输的位数,和baud_time类似,但是是在bit_flag的控制下产生的(中间时刻采样),tx输出的数据也是在bit_cnt的控制下输出的。

​ 6、rx : 是顶层给的输入,==tx。

​ 几个变量:

​ 1、rx_r1、rx_r2、rx_r3:打拍。

​ 2、baud_end,baud_mid,check,check_sum,

​ 方法:

​ 在接收模块中没有像发送那样用case语句按bit_cnt按位分配bit输出到rx_data,用了下面的方法:

​ 定义了两个变量:recv_data recv_de。用以下语句构造recv_data:

recv_data <= {rx,recv_data[7:1]};

​ 思路:首先明确recv_de 和recv_data的作用,他们不需要考虑check,只要计数正确就赋值。recv_de 当bit_cnt==8,bit_flag == 1拉高。bit_flag这个条件保证是bit_cnt == 8持续了完整的1bit传输。recv_data中的bit_flag作用同理。rx_de和rx_data则用于输出,分check讨论即可。
​ 核心代码:

always @(posedge clk or negedge rstn) begin
    if(!rstn)
        recv_de <= 1'b0;
    else if(bit_cnt == 8'd8 && bit_flag == 1'b1 )
        recv_de <= 1'b1;
    else
        recv_de <= 1'b0;
end

always @(posedge clk or negedge rstn) begin
    if(!rstn)
        recv_data <= 8'd0;
    else if(bit_flag == 1'b1 && bit_cnt >= 8'd1 && bit_cnt <= 8'd8)
        recv_data <= {rx,recv_data[7:1]};
    else                                 
        recv_data <= recv_data;
end

always @(posedge clk or negedge rstn) begin
    if(!rstn)
        rx_de <= 1'd0;
    else if(recv_de == 1'b1 && check == 1'd0)
        rx_de <= 1'd1;
    else if(bit_flag == 1'b1 && bit_cnt == bit_num - 2 && check != 1'b0 && check_sum==rx)
        rx_de <= 1'd1;
    else
        rx_de <= 1'd0;
end

always @(posedge clk or negedge rstn) begin
    if(!rstn)
        rx_data <= 8'd0;
    else if(recv_de == 1'b1 && check ==1'b0)
        rx_data <= recv_data;
    else if(bit_flag == 1'b1 && bit_cnt == bit_num - 2 && check != 1'b0&& check_sum==rx)
        rx_data <= recv_data;
    else
        rx_data <= recv_data;
end

3、顶层模块

主要是写为发送模块提供的tx_de和tx_data以及传参这两件事。

​ 写一个cnt,负责第一次的tx_de拉高,或上 tx_done == 1 就可以让tx自己为后续的协议发送提供tx_de了。

​ 传参只需要定义好 baud 和check 即可。

​ rx和tx相连不要忘记。assign rx = tx;

4、仿真结果

在这里插入图片描述
在这里插入图片描述

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐