基于verilog实现的UART

UART介绍

通用异步收发器(Univeral Asynchronous Receiver/Transmitter,UART)。UART相关特点的总结如下表所示。

特点UART解释
信号线RX TXRX负责接收、TX负责发送。
通信方式全双工通信通信允许数据在两个方向上同时传输,即RX接收数据时,TX可以发送数据。
通信特征异步串行数据以帧为单位串行传输,一帧可以包含5/6/7/8位数据。由于UART为异步通信协议,不含同步时钟,收发双方通过约定相同的波特率来传输数据,波特率的单位为bit/s(位/秒),常用的波特率为115200 bit/s或9600 bit/s。
传输单位UART以帧为单位传输数据,一个帧由4部分组成,分别是起始位数据位奇偶校验位停止位。起始位表示一帧数据的开始,一般为1 bit的0;数据位保存需要传递的数据,一般为5/6/7/8 bit数据;奇偶检验位负责对传输的数据进行检查,用于检验数据在传输时是否出错;停止位表示一帧数据的结束,一般为1 bit、1.5 bit或2 bit的1。

UART实现

约定UART的帧格式和波特率。

  • 帧格式
    • 起始位:1 bit,0有效。
    • 数据位:8 bit。
    • 奇偶校验位:0 bit
    • 停止位:1 bit,1有效。
  • 波特率
    • 9600 bit/s

传输模块

`timescale 1ns / 1ps
module uart_tx (
input wire clk_i,
input wire rst_i,

input wire [7:0] data_i,
input wire tx_en_i,

output reg tx_data_o
);
// 波特率 9600
parameter BPS = 9600;
// 时钟频率 50MHz
parameter CLK_FREQ = 50000000;

localparam BPS_CNT = CLK_FREQ / BPS;

localparam IDLE = 0;
localparam START = 1;
localparam SEND = 2;
localparam STOP = 3;

// 分频计数
reg [31:0] count;
// 已传输的bit数
reg [31:0] send_count;

reg [1:0] cur_state;
reg [1:0] next_state;
// 三段式状态机
// cur_state
always @ (posedge clk_i) begin
    if(rst_i == 1'b1) begin
        cur_state <= IDLE;
    end else begin
        cur_state <= next_state;
    end
end
// next_state
always @ (*) begin
case(cur_state)
    IDLE : begin
        if(tx_en_i == 1'b1) begin
            next_state = START;
        end else begin
            next_state = IDLE;
        end
    end
    START : begin
        if(count == BPS_CNT - 1) begin
            next_state = SEND;
        end else begin
            next_state = START;
        end
    end
    SEND : begin
        if(send_count == 8 - 1 && count == BPS_CNT - 1) begin
            next_state = STOP;
        end else begin
            next_state = SEND;
        end
    end
    STOP : begin
        if(count == BPS_CNT - 1) begin
            next_state = IDLE;
        end else begin
            next_state = STOP;
        end
    end
    default : next_state = IDLE;
endcase
end
// count
always @ (posedge clk_i) begin
    if(rst_i == 1'b1) begin
        count <= 0;
    end else begin
        if(cur_state != IDLE) begin
            if(count == BPS_CNT - 1) begin
                count <= 0;
            end else begin
                count <= count + 1;
            end
        end else begin
            count <= 0;
        end
    end
end 
// send_count
always @ (posedge clk_i) begin
    if(rst_i == 1'b1) begin
        send_count <= 0;
    end else begin
        if(cur_state == SEND) begin
            if(count == BPS_CNT - 1) begin
                send_count <= send_count + 1;
            end else begin
                send_count <= send_count;
            end
        end else begin
            send_count <= 0;
        end
    end
end
// tx_data_o
always @ (*) begin
case(cur_state)
    IDLE : tx_data_o = 1;
    START : tx_data_o = 0;
    SEND : begin
        case(send_count)
            0 : tx_data_o = data_i[0];
            1 : tx_data_o = data_i[1];
            2 : tx_data_o = data_i[2];
            3 : tx_data_o = data_i[3];
            4 : tx_data_o = data_i[4];
            5 : tx_data_o = data_i[5];
            6 : tx_data_o = data_i[6];
            7 : tx_data_o = data_i[7];
            default : tx_data_o = 1;
        endcase
    end
    STOP : tx_data_o = 1;
    default : tx_data_o = 1;
endcase
end

endmodule

接收模块

`timescale 1ns / 1ps
module uart_rx (
input wire clk_i,
input wire rst_i,

input wire rx_data_i,

output reg rx_done_o,
output reg [7:0] data_o
);
// 波特率 9600
parameter BPS = 9600;
// 时钟频率 50MHz
parameter CLK_FREQ = 50000000;

localparam BPS_CNT = CLK_FREQ / BPS;

localparam IDLE = 0;
localparam START = 1;
localparam RECV = 2;
localparam STOP = 3;

// 分频计数
reg [31:0] count;
// 已接收的bit数
reg [31:0] recv_count;
// 下降沿检测
reg rx_data_i_ff;

reg [1:0] cur_state;
reg [1:0] next_state;

// rx_data_i_ff
always @ (posedge clk_i) begin
    if(rst_i == 1'b1) begin
        rx_data_i_ff <= 0;
    end else begin
        rx_data_i_ff <= rx_data_i;
    end
end

// 三段式状态机
// cur_state
always @ (posedge clk_i) begin
    if(rst_i == 1'b1) begin
        cur_state <= 0;
    end else begin
        cur_state <= next_state;
    end
end
// next_state
always @ (*) begin
case(cur_state) 
    IDLE : begin
        // 下降沿检测
        if(rx_data_i_ff == 1'b1 && rx_data_i == 1'b0) begin
            next_state = START;
        end else begin
            next_state = IDLE;
        end
    end
    START : begin
        // 下降沿检测占用一个周期
        if(count == BPS_CNT - 2) begin
            next_state = RECV;
        end else begin
            next_state = START;
        end
    end
    RECV : begin
        if(recv_count == 8 - 1 && count == BPS_CNT - 1) begin
            next_state = STOP;
        end else begin
            next_state = RECV;
        end
    end
    STOP : begin
        if(count == BPS_CNT - 1) begin
            next_state = IDLE;
        end else begin
            next_state = STOP;
        end
    end
    default : next_state = IDLE;
endcase
end
// count
always @ (posedge clk_i) begin
    if(rst_i == 1'b1) begin
        count <= 0;
    end else begin
        case(cur_state)
            IDLE : count <= 0;
            START : begin
                if(count == BPS_CNT - 2) begin
                    count <= 0;
                end else begin
                    count <= count + 1;
                end
            end
            RECV : begin
                if(count == BPS_CNT - 1) begin
                    count <= 0;
                end else begin
                    count <= count + 1;
                end
            end
            STOP : begin
                if(count == BPS_CNT - 1) begin
                    count <= 0;
                end else begin
                    count <= count + 1;
                end
            end
            default : count <= 0;
        endcase
    end
end
// recv_count
always @ (posedge clk_i) begin
    if(rst_i == 1'b1) begin
        recv_count <= 0;
    end else begin
        if(cur_state == RECV) begin
            if(count == BPS_CNT - 1) begin
                recv_count <= recv_count + 1;
            end else begin
                recv_count <= recv_count;
            end
        end else begin
            recv_count <= 0;
        end
    end
end
// data_o
always @ (posedge clk_i) begin
    if(rst_i == 1'b1) begin
        data_o <= 0;
    end
    if(cur_state == RECV) begin
        if(count == BPS_CNT / 2 - 1) begin
            case(recv_count)
                0 : data_o[0] <= rx_data_i;
                1 : data_o[1] <= rx_data_i;
                2 : data_o[2] <= rx_data_i;
                3 : data_o[3] <= rx_data_i;
                4 : data_o[4] <= rx_data_i;
                5 : data_o[5] <= rx_data_i;
                6 : data_o[6] <= rx_data_i;
                7 : data_o[7] <= rx_data_i;
                default : data_o <= data_o;
            endcase
        end else begin
            data_o <= data_o;
        end
    end else begin
        data_o <= data_o;
    end
end
// rx_done_o
always @ (*) begin
    if(rst_i == 1'b1) begin
        rx_done_o = 0;
    end else begin
        if(cur_state == STOP) begin
            rx_done_o = 1;
        end else begin
            rx_done_o = 0;
        end
    end
end
endmodule

测试文件

测试传输模块(uart_tx)接收模块(uart_rx)uart_tx发送数据给uart_rx,检查uart_rx接收的数据和uart_tx发送数据是否一致。

`timescale 1ns / 1ps
module tx2rx_testbench ();

// 时钟频率50MHz,周期为20ns
localparam CLK_PERIOD = 20; 

reg rst;
reg clk;

// 测试数据
reg [7:0] test_data = 8'b00000000;
// 开始传输标志
reg tx_en;
reg first_flag;
// tx传输数据
wire tx_data;
// 下降沿检测
reg rx_done_ff;
// 接收完成
wire rx_done;
// 接收数据
wire [7:0] data;


initial begin
    clk = 1;
    rst = 1;
    tx_en = 0;
    # 100
    rst = 0;
    # 40
    first_flag = 1;
    # 20
    first_flag = 0;
end

always @(*) begin
    if(rst == 1'b1) begin
        test_data <= test_data;
    end else begin
        if(tx_en == 1) begin
            test_data = test_data + 1;
        end else begin
            test_data = test_data;
        end
    end
end

// 下降沿检测
always @(posedge clk) begin
    if(rst == 1'b1) begin
        rx_done_ff <= 0;
    end else begin
        rx_done_ff <= rx_done;
    end
end

always @ (*) begin
    if(rst == 1'b1) begin
        tx_en = 0;
    end else begin
        if(first_flag == 1'b1) begin
            tx_en = 1;
        end else begin
            if(rx_done == 1'b0 && rx_done_ff == 1'b1) begin
                tx_en = 1;
            end else begin
                tx_en = 0;
            end
        end
    end
end

always begin
    # (CLK_PERIOD / 2) clk = ~clk;
end

uart_tx uart_tx_inst(
.clk_i(clk),
.rst_i(rst),

.data_i(test_data),
.tx_en_i(tx_en),

.tx_data_o(tx_data)
);

uart_rx uart_rx_inst(
.clk_i(clk),
.rst_i(rst),

.rx_data_i(tx_data),

.rx_done_o(rx_done),
.data_o(data)
);

endmodule
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值