Verilog——FPGA串口的Verilog编码

FPGA串口的Verilog编码

目标

  • 串口接收数据缓存到FIFO后重新发送回去,内部模块包括:
  1. 串口发送模块
  2. 串口接收模块
  3. FIFO缓存模块
  4. top模块
  • 串口接收、发送模块的编码极为简单,通过计时器直接指定每一个比特位的操作时间;

代码

串口发送

`timescale 1ns / 1ps

module uart_tx
#(
    parameter       CLK_FREQ    =   50,     //输入时钟频率,MHz
    parameter       BAUD_RATE   =   9600    //波特率,bps
)
(
    input           clk                     ,
    input           rst_n                   ,
    //input
    input           tx_trig                 ,//串口发送触发,上升沿有效
    input   [7:0]   tx_data                 ,//串口发送数据
    //output
    output  reg     tx_idle                 ,//空闲态,高电平有效
    output  reg     tx_txd                   //txd发送端
);

localparam  BIT     = CLK_FREQ * 1000_000 / BAUD_RATE;
localparam  BIT0    = BIT * 1 - 1;
localparam  BIT1    = BIT * 2 - 1;
localparam  BIT2    = BIT * 3 - 1;
localparam  BIT3    = BIT * 4 - 1;
localparam  BIT4    = BIT * 5 - 1;
localparam  BIT5    = BIT * 6 - 1;
localparam  BIT6    = BIT * 7 - 1;
localparam  BIT7    = BIT * 8 - 1;
localparam  STOP    = BIT * 9 - 1;
localparam  FINISH  = BIT * 10 - 1;

reg         tx_trig_r;
reg [31:0]  count;
reg [7:0]   data_in;
wire        start_flag;

//start_flag,串口发送启动标志
assign start_flag = tx_trig & ~tx_trig_r & tx_idle;//tx_trig上升沿到来且串口空闲

//tx_trig_r,串口发送触发打节拍
always @(posedge clk) begin
    tx_trig_r = tx_trig;
end
//data_in
always @(posedge clk or negedge rst_n) begin
    if(~rst_n)
        data_in <= 8'h00;
    else if(start_flag)//串口发送启动
        data_in <= tx_data;
end
//tx_idle
always @(posedge clk or negedge rst_n) begin
    if(~rst_n)
        tx_idle <= 1'b1;
    else if(start_flag)//串口发送启动
        tx_idle <= 1'b0;
    else if(count == FINISH)
        tx_idle <= 1'b1;
end
//count
always @(posedge clk or negedge rst_n) begin
    if(~rst_n)
        count <= 'd0;
    else if(~tx_idle)
        count <= count + 'd1;
    else
        count <= 'd0;
end
//tx_txd
always @(posedge clk or negedge rst_n) begin
    if(~rst_n)
        tx_txd <= 1'b1;
    else if(start_flag)//串口发送启动
        tx_txd <= 1'b0;
    else if(count == BIT0)
        tx_txd <= data_in[0];
    else if(count == BIT1)
        tx_txd <= data_in[1];
    else if(count == BIT2)
        tx_txd <= data_in[2];
    else if(count == BIT3)
        tx_txd <= data_in[3];
    else if(count == BIT4)
        tx_txd <= data_in[4];
    else if(count == BIT5)
        tx_txd <= data_in[5];
    else if(count == BIT6)
        tx_txd <= data_in[6];
    else if(count == BIT7)
        tx_txd <= data_in[7];
    else if(count == STOP)
        tx_txd <= 1'b1;
    else
        tx_txd <= tx_txd;
end

endmodule

串口接收

`timescale 1ns/1ps

module uart_rx
#(
    parameter       CLK_FREQ    = 50        ,//输入时钟频率,MHz
    parameter       BAUD_RATE   = 9600       //波特率,bps
)
(
    input               clk         ,
    input               rst_n       ,
    //---
    input               rx_rxd      ,//接收端口
    output   reg [7:0]  rx_data     ,//接收到的1字节数据
    output   reg        rx_flag      //1字节数据接收完成标志
);
localparam      BIT     =   CLK_FREQ * 1000_000 / BAUD_RATE;
localparam      HBIT    =   BIT / 2;
localparam      BIT0    =   BIT * 1 + HBIT - 1;
localparam      BIT1    =   BIT * 2 + HBIT - 1;
localparam      BIT2    =   BIT * 3 + HBIT - 1;
localparam      BIT3    =   BIT * 4 + HBIT - 1;
localparam      BIT4    =   BIT * 5 + HBIT - 1;
localparam      BIT5    =   BIT * 6 + HBIT - 1;
localparam      BIT6    =   BIT * 7 + HBIT - 1;
localparam      BIT7    =   BIT * 8 + HBIT - 1;
localparam      FINISH  =   BIT * 9 + HBIT - 1;

reg         rx_rxd_r;
reg         rx_rxd_rr;
wire        start_flag;
reg         rx_busy;
reg [31:0]  count;

//rx_rxd_r, rx_rxd_rr, 接收端口打节拍
always @(posedge clk) begin
    rx_rxd_r <= rx_rxd;
    rx_rxd_rr <= rx_rxd_r;
end
//start_flag, 串口接收开始标志
assign start_flag = (rx_rxd_rr & ~rx_rxd_r) & ~rx_busy;//rx_rxd下降沿且串口接收不忙碌
//rx_busy, 接收忙碌态
always @(posedge clk or negedge rst_n) begin
    if(~rst_n)
        rx_busy <= 1'b0;
    else if(start_flag)
        rx_busy <= 1'b1;
    else if(count == FINISH)
        rx_busy <= 1'b0;
end
//count,计数器
always @(posedge clk or negedge rst_n) begin
    if(~rst_n)
        count <= 'd0;
    else if(rx_busy)
        count <= count + 'd1;
    else
        count <= 'd0;
end
//rx_flag
always @(posedge clk or negedge rst_n) begin
    if(~rst_n)
        rx_flag <= 1'b0;
    else if(count == FINISH)
        rx_flag <= 1'b1;
    else
        rx_flag <= 1'b0;
end
//rx_data
always @(posedge clk or negedge rst_n) begin
    if(~rst_n)
        rx_data <= 8'h00;
    else if(count == BIT0)
        rx_data[0] <= rx_rxd_r;
    else if(count == BIT1)
        rx_data[1] <= rx_rxd_r;
    else if(count == BIT2)
        rx_data[2] <= rx_rxd_r;
    else if(count == BIT3)
        rx_data[3] <= rx_rxd_r;
    else if(count == BIT4)
        rx_data[4] <= rx_rxd_r;
    else if(count == BIT5)
        rx_data[5] <= rx_rxd_r;
    else if(count == BIT6)
        rx_data[6] <= rx_rxd_r;
    else if(count == BIT7)
        rx_data[7] <= rx_rxd_r;
    else
        rx_data <= rx_data;
end
endmodule

FIFO模块


module fifo
#
(
    parameter           POINTER_N = 7   //读写指针位宽,FIFO容量 = 2^POINTER_N,默认容量为2^7=128字节
)
(
    //系统信号
    input               clk50M          ,
    input               rst_n           ,
    //FIFO信号
    input               wr_en           ,//写使能,高电平有效
    input       [7:0]   buf_in          ,//写数据
    input               rd_en           ,//读使能,高电平有效
    output reg  [7:0]   buf_out         ,//读数据
    //FIFO状态输出
    output              buf_empty       ,//FIFO空
    output              buf_full        ,//FIFO满
    output reg  [7:0]   fifo_cnt         //FIFO已使用字节数
);
    //参数设置(修改FIFO容量时以下两个参数必须同时修改)
    localparam BUF_NUM = 1 << POINTER_N;//FIFO容量=2^POINTER_N,默认128字节
    //公共变量
    reg [POINTER_N - 1 : 0] rd_prt;             //读指针,位宽必须与BUF_NUM完全匹配
    reg [POINTER_N - 1 : 0] wr_prt;             //写指针,位宽必须与BUF_NUM完全匹配
    reg [7:0] buf_mem [0 : BUF_NUM - 1];     //8位宽,BUF_NUM容量 
    
    //buf_empty
    assign buf_empty = (fifo_cnt == 'd0) ? 'b1 : 'b0;    
    //buf_full
    assign buf_full = (fifo_cnt == BUF_NUM) ? 'b1 : 'b0;
    
    //fifo_cnt
    always @(posedge clk50M or negedge rst_n) begin
        if(~rst_n)
            fifo_cnt <= 'd0;
        else if(wr_en & rd_en) //Simultaneously read and write
            fifo_cnt <= fifo_cnt;
        else if(wr_en & ~buf_full) // Write
            fifo_cnt <= fifo_cnt + 'd1;
        else if(rd_en & ~buf_empty)    // Read
            fifo_cnt <= fifo_cnt - 'd1;
        else
            fifo_cnt <= fifo_cnt;
    end    
    
    //buf_out, Read data operation
    always @(posedge clk50M or negedge rst_n) begin
        if(~rst_n)
            buf_out <= 'h00;
        else if(rd_en & ~buf_empty)
            buf_out <= buf_mem[rd_prt];
    end
    
    //buf_mem, Write data operation
    always @(posedge clk50M) begin
        if(wr_en & ~buf_full)
            buf_mem[wr_prt] <= buf_in;
    end
    
    //rd_prt
    always @(posedge clk50M or negedge rst_n) begin
        if(~rst_n)
            rd_prt <= 0;
        else if(rd_en & ~buf_empty)
            rd_prt <= rd_prt + 1;
    end
    
    //wr_prt
    always @(posedge clk50M or negedge rst_n) begin
        if(~rst_n)
            wr_prt <= 0;
        else if(wr_en & ~buf_full)
            wr_prt <= wr_prt + 1;
    end

endmodule

TOP

`timescale 1ns / 1ps

module top(
    input           sys_clk50M      ,
    input           sys_rst_n       ,
    input           uart_rxd        ,
    output          uart_txd
);

localparam      UART_CLK_FREQ   =   50          ;//串口时钟频率, MHz
localparam      UART_BAUD_RATE  =   460800      ;//串口波特率, bps


wire [7:0]  rx_data;
wire        rx_flag;

wire        buf_empty;
reg         fifo_rd;
wire [7:0]  fifo_out;
reg  [3:0]  state;
reg         tx_trig;
wire        tx_idle;

//state, fifo_rd, tx_trig
always @(posedge sys_clk50M or negedge sys_rst_n) begin
    if(~sys_rst_n) begin
        state <= 0;
        fifo_rd <= 1'b0;
        tx_trig <= 1'b0;
    end
    else
        case(state)
            0://读FIFO数据
                begin
                    tx_trig <= 1'b0;
                    if(~buf_empty) begin//等待串口接收到数据并存入FIFO
                        fifo_rd <= 1'b1;//读出FIFO数据
                        state <= 1;
                    end
                end
            1://触发串口发送
                begin
                    fifo_rd <= 1'b0;
                    if(tx_idle) begin//等待串口发送端空闲
                        tx_trig <= 1'b1;
                        state <= 0;
                    end
                end

        endcase
end

//FIFO先入先出缓存实例
fifo
#
(
    .POINTER_N          (1          )//读写指针位宽,FIFO容量 = 2^POINTER_N,默认容量为2^7=128字节
)
fifo_inst
(
    //系统信号
    .clk50M             (sys_clk50M ),
    .rst_n              (sys_rst_n  ),
    //FIFO信号
    .wr_en              (rx_flag    ),//写使能,高电平有效
    .buf_in             (rx_data    ),//写数据
    .rd_en              (fifo_rd    ),//读使能,高电平有效
    .buf_out            (fifo_out   ),//读数据
    //FIFO状态输出
    .buf_empty          (buf_empty  ),//FIFO空
    .buf_full           (),//FIFO满
    .fifo_cnt           () //FIFO已使用字节数
);
//串口接收模块实例
uart_rx
#(
    .CLK_FREQ       (UART_CLK_FREQ      ),//输入时钟频率,MHz
    .BAUD_RATE      (UART_BAUD_RATE     ) //波特率,bps
)
uart_rx_inst
(
    .clk            (sys_clk50M         ),
    .rst_n          (sys_rst_n          ),
    //---
    .rx_rxd         (uart_rxd           ),//接收端口
    .rx_data        (rx_data            ),//接收到的1字节数据
    .rx_flag        (rx_flag            ) //1字节数据接收完成标志
);
//串口发送模块实例
uart_tx
#(
    .CLK_FREQ       (UART_CLK_FREQ      ),//输入时钟频率,MHz
    .BAUD_RATE      (UART_BAUD_RATE     ) //波特率,bps
)
uart_tx_inst
(
    .clk            (sys_clk50M         ),
    .rst_n          (sys_rst_n          ),
    //input
    .tx_trig        (tx_trig            ),//串口发送触发,上升沿有效
    .tx_data        (fifo_out           ),//串口发送数据
    //output
    .tx_idle        (tx_idle            ),//空闲态,高电平有效
    .tx_txd         (uart_txd           ) //txd发送端
);

endmodule

posted @ 2021-05-23 15:59  fxz_abc  阅读(1067)  评论(0编辑  收藏  举报