⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 rxd.v

📁 一个串口的完整FPGA工程
💻 V
字号:
/*********************************************************************************************************
 **                                  All right reserve 2008-2009(C) 
 **                             Created &maintain by http://www.edaok.net
 **=======================================================================================================
 ** 模 块 名:   rxd
 ** 描    述:   实现UART的接收功能, 可能选择数据位, 检查奇偶检验, 帧错误检查功能, 实现了ARV单片机的异步串
 **             口(UART)
 **
 ** 原 作 者:   Adeko (http://www.edaok.net)
 ** 参 与 者:   (...welcome you join in)
 **
 **=======================================================================================================
 ********************************************************************************************************/

`define STAT_WIDTH      7
module rxd (
    clk,
    rst_n,

    clk_en,
    data_o,

    rxd_xi,

    ctrl_i,
    frame_bits_i,
    stat_o,
    
    enable,
    
    debug_o
);

input               clk;                                        //  全局时钟线
input               rst_n;                                      //  全局复位线, 低电平有效

input               clk_en;
output  [7:0]       data_o;                                     //  数据的输出口(8bits)

input               rxd_xi;                                     //  串口接收引脚

input   [2:0]       ctrl_i;                                     //  控制信号输入
input   [3:0]       frame_bits_i;                               //  帧位数参数输入
output  [3:0]       stat_o;                                     //  状态信号输出

input               enable;                                     //  模块使能线

output  [7:0]       debug_o;

wire                wParEn;                                     //  使能奇偶校验
wire                wParMod;                                    //  0表示偶校验, 1为奇校验
wire    [3:0]       wDatWid;                                    //  UART的数据帧长数
wire                wDatBufRdEn;                                //  读UART数据缓冲器使能


assign  wParEn      = ctrl_i[2];
assign  wParMod     = ctrl_i[1];
assign  wDatBufRdEn = ctrl_i[0];
assign  wDatWid[3:0]    = frame_bits_i[3:0];


reg     [7:0]   rRxDat;                                         //  串口接收到的数据

reg             rParVal;                                        //  计算得到的每个位的奇偶校检值


wire            wRxdVal;

reg             rFlgFraErr;                                     //  帧错误标记, Frame Error
reg             rFlgOverRun;                                    //  接收缓冲超出范围
reg             rFlgDone;                                       //  完成一帧数据的接收
reg             rFlgParErr;                                     //  校验错误


reg             rPlsBaudTick;                                   //  UART波特率标记, 持续一个全局时钟

reg     [3:0]   rRxClkCnt;                                      //  波特数的计数器, 输入的时钟是波特率的16倍
reg     [3:0]   rRxBitCnt;                                      //  接收rxd数据位的计数值
reg     [7:0]   rRxDatBuf;                                      //  接收数据的缓冲寄存器, 数据接收过程中的临时值


/********************************************************************************************************
 ** 检测总线上的电平 
 ** 原理:   在中间取三个点的电平, 取它的多数值(两个或以上个点为高电平, 则认为取到的是高电平, 否则认为
 **         是低电平) 
 ********************************************************************************************************/
reg             rRxdVal[2 : 0];
always @(posedge clk or negedge rst_n)
begin : RXD_VAL_READ
    integer     i;

    if (~rst_n) begin
        for (i = 0; i < 3; i = i+1) 
            rRxdVal[i]  <= 0;
    end
    else begin
        case (rRxClkCnt)                                        //  根据当前的UART时钟, 读取RXD线上的电平
        4'h7:
            rRxdVal[0]  <= rxd_xi;
        4'h8:
            rRxdVal[1]  <= rxd_xi;
        4'h9:
            rRxdVal[2]  <= rxd_xi;
        endcase
    end
end
assign  wRxdVal = (rRxdVal[0] & rRxdVal[1]) | (rRxdVal[1] & rRxdVal[2]) | (rRxdVal[2] & rRxdVal[0]);


/********************************************************************************************************
 ** 定义UART的接收器的状态机编码, 使用OneHot coding
 ** 技巧:状态机的状态值可以不用自己编码, 综合可以根据你选择的coding style, 重新对状态机进行编码, 
 ** 这里为了方便阅读, 手动进行了编码
 ** _______n________n________n________n________n________n________n________n________n
 ** _______    0     _________________          ________          ___________
 **  IDLE  \________/                 \___~~___<________>--------<________/
 **  空闲  | 开始位 | <----      5~8位数据     -------> | parity | stop   |
 ********************************************************************************************************/
parameter
    RX_IDLE     = `STAT_WIDTH'b000_0001,                        //  空闲状态, 等侍开始位(start bit)
    RX_SYNC     = `STAT_WIDTH'b000_0010,                        //  同步状态, 检查开始位是否有效等...
    RX_DATA     = `STAT_WIDTH'b000_0100,                        //  接收数据状态
    RX_PARITY   = `STAT_WIDTH'b000_1000,                        //  校验位检查, 检查校验错误等...
    RX_STOP     = `STAT_WIDTH'b001_0000,                        //  停止位, 检查帧错误等...
    RX_ENDING   = `STAT_WIDTH'b010_0000,                        //  帧接收结束, 检查缓冲超出等...
    RX_DONE     = `STAT_WIDTH'b100_0000;                        //  完成一帧接收, 保存数据等...


reg [`STAT_WIDTH - 1 : 0]   rStatRxNext;                        //  状态机的下一个状态
reg [`STAT_WIDTH - 1 : 0]   rStatRxCur;                         //  状态机的当前状


/********************************************************************************************************
 ** 更新状态机状态, 同时更新状态的相关条件寄存器
 **
 ** [注意点]:
 ** 这里状态机的表达方式使用为大家熟悉的三段式状态, 同步设计, 状态的切换和状态的动作执行滞后2个时钟
 ********************************************************************************************************/
always @(posedge clk or negedge rst_n)
begin : RX_STATE_ASSIGN
    if (~rst_n) begin
        rStatRxCur  <= RX_IDLE;                                 //  设置寄存器的初始值(复位值)
    end
    else begin
        rStatRxCur  <= rStatRxNext;                             //  更新状态值
    end
end


/********************************************************************************************************
 ** 根据条件,计算下一个状态机的状态值, (综合成组合逻辑)
 **
 ** [特别注意]: 
 ** 状态值使用OneHot编码, 这种编码一般会优为移位寄存器, 为了防止状态出现00000的情况(如时钟质量差), 
 ** 在综合器中要设置'safe stat mechine = on'(安全状态机)
 ** 
 ********************************************************************************************************/
always @(
    rStatRxCur or
    rxd_xi or
    rRxClkCnt or
    rRxBitCnt or
    rPlsBaudTick or
    wRxdVal or
    wDatWid or
	wParEn
    )
begin : RX_STAT_CALCULATE

    case (rStatRxCur)

    RX_IDLE: begin
        if (~rxd_xi)                                              //  当检测到有低电平, 启动状态机, 开始同步
            rStatRxNext <= RX_SYNC;
        else 
            rStatRxNext <= RX_IDLE;
    end

    RX_SYNC: begin
        if (rPlsBaudTick) begin
            if (wRxdVal)                                        //  检查'开始位'是否有效(低电平有效)
                rStatRxNext <= RX_IDLE;
            else
                rStatRxNext <= RX_DATA;
        end
        else begin
            rStatRxNext <= RX_SYNC;
        end
    end

    RX_DATA: begin
        if (rPlsBaudTick) begin                                 //  当接收的位数满时, 进入接收停止位
            if (rRxBitCnt[3:0] == wDatWid[3:0]) begin
                if (wParEn)
                    rStatRxNext <= RX_PARITY;
                else
                    rStatRxNext <= RX_STOP;
            end
            else begin
                rStatRxNext  <= RX_DATA;
            end
        end
        else begin
            rStatRxNext <= RX_DATA;
        end
        
    end

    RX_PARITY: begin
        if (rPlsBaudTick)
            rStatRxNext <= RX_STOP;
        else
            rStatRxNext <= RX_PARITY;
    end

    RX_STOP: begin
        if (rRxClkCnt >= 4'd10)
            rStatRxNext <= RX_ENDING;
        else
            rStatRxNext <= RX_STOP;
    end

    RX_ENDING: begin
        rStatRxNext <= RX_DONE;
    end

    RX_DONE: begin
        rStatRxNext <= RX_IDLE;
    end

    default: begin
        rStatRxNext <= RX_IDLE;
    end

    endcase
end


/********************************************************************************************************
 ** 根据不同的状态, 处理相应的数据
 ********************************************************************************************************/
always @(posedge clk or negedge rst_n)
begin : RX_STATE_ACTION
    if (~rst_n) begin
        rRxDatBuf   <= 8'h00;
        rRxDat      <= 8'h00;

        rParVal     <= 1'b0;

        rFlgFraErr  <= 1'b0;
        rFlgOverRun <= 1'b0;
        rFlgDone    <= 1'b0;
        rFlgParErr  <= 1'b0;

        rRxClkCnt   <= 4'h0;
        rRxBitCnt   <= 4'h0;
        rPlsBaudTick   <= 1'b0;
        
    end
    else begin

        
        rPlsBaudTick   <= 1'b0;

        if (rStatRxCur == RX_IDLE) begin
            rRxClkCnt   <= 4'h00;
            rRxBitCnt   <= 4'h00;
        end
        else if (clk_en) begin

            rRxClkCnt   <= rRxClkCnt + 4'h01;
            
            if (rRxClkCnt == 4'h0F) begin
                rPlsBaudTick   <= 1'b1;
                rRxBitCnt   <= rRxBitCnt + 4'h1;
            end
        end

        if (wDatBufRdEn | (~enable) ) begin                     //  当读数据缓冲时, 清除相关的标志
            rFlgFraErr  <= 1'b0;
            rFlgOverRun <= 1'b0;
            rFlgParErr  <= 1'b0;
            rFlgDone    <= 1'b0;
        end

        case (rStatRxCur)

        RX_IDLE: begin
            rParVal <= 1'b0;
        end

        RX_SYNC: begin
        end

        RX_DATA: begin
            if (rPlsBaudTick) begin
                rRxDatBuf <= {wRxdVal, rRxDatBuf[7:1]};
                rParVal <= rParVal ^ wRxdVal;                   //  计算奇偶检验, (异或操作)
            end
        end

        RX_PARITY: begin
            if (rPlsBaudTick) begin
                if (wParMod)                                    //  奇校验
                    rParVal <= ((~rParVal) != wRxdVal);
                else                                            //  偶检验
                    rParVal <= (rParVal != wRxdVal);
            end    
        end

        RX_STOP: begin
            if (rRxClkCnt >= 4'd10) begin
                if (~wRxdVal)
                    rFlgFraErr  <= 1'b1;
            end
        end

        RX_ENDING: begin
            if (rFlgDone) begin                                 //  如果数据未读, 则标志数超出标记
                rFlgOverRun    <= 1'b1;
            end
        end

        RX_DONE: begin
            /*
             *  根据不同的帧数据位数, 调整接收到的数据(异位操作)
             */
            case (wDatWid[3:0])
            4'h6:   rRxDat  <= {3'h0, rRxDatBuf[7:3]};

            4'h7:   rRxDat  <= {2'h0, rRxDatBuf[7:2]};

            4'h8:   rRxDat  <= {1'h0, rRxDatBuf[7:1]};

            4'h9:   rRxDat  <= rRxDatBuf[7:0];
            endcase


            rFlgDone    <= 1'b1;                                //  标志帧接收完成标记

            if (wParEn) begin                                   //  如果使能了奇偶检验, 则输出检查结果
                rFlgParErr  <= rFlgParErr | rParVal;            
            end
        end
        
        endcase
    end
end

assign  data_o  = rRxDat;

assign  debug_o = 8'h00;

assign  stat_o[3:0] = {rFlgFraErr, rFlgOverRun, rFlgParErr, rFlgDone};

endmodule

/*********************************************************************************************************
 ** End Of File
 ********************************************************************************************************/

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -