一个串口的完整FPGA工程

源代码在线查看: rxd.v

软件大小: 2023 K
上传用户: archimedes88
关键词: FPGA 串口 工程
下载地址: 免注册下载 普通下载 VIP

相关代码

				/*********************************************************************************************************
				 **                                  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]  				    end
				    else begin
				        case (rRxClkCnt)                                        //  根据当前的UART时钟, 读取RXD线上的电平
				        4'h7:
				            rRxdVal[0]  				        4'h8:
				            rRxdVal[1]  				        4'h9:
				            rRxdVal[2]  				        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  \________/                 \___~~___--------				 **  空闲  | 开始位 |  | 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  				    end
				    else begin
				        rStatRxCur  				    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 				        else 
				            rStatRxNext 				    end
				
				    RX_SYNC: begin
				        if (rPlsBaudTick) begin
				            if (wRxdVal)                                        //  检查'开始位'是否有效(低电平有效)
				                rStatRxNext 				            else
				                rStatRxNext 				        end
				        else begin
				            rStatRxNext 				        end
				    end
				
				    RX_DATA: begin
				        if (rPlsBaudTick) begin                                 //  当接收的位数满时, 进入接收停止位
				            if (rRxBitCnt[3:0] == wDatWid[3:0]) begin
				                if (wParEn)
				                    rStatRxNext 				                else
				                    rStatRxNext 				            end
				            else begin
				                rStatRxNext  				            end
				        end
				        else begin
				            rStatRxNext 				        end
				        
				    end
				
				    RX_PARITY: begin
				        if (rPlsBaudTick)
				            rStatRxNext 				        else
				            rStatRxNext 				    end
				
				    RX_STOP: begin
				        if (rRxClkCnt >= 4'd10)
				            rStatRxNext 				        else
				            rStatRxNext 				    end
				
				    RX_ENDING: begin
				        rStatRxNext 				    end
				
				    RX_DONE: begin
				        rStatRxNext 				    end
				
				    default: begin
				        rStatRxNext 				    end
				
				    endcase
				end
				
				
				/********************************************************************************************************
				 ** 根据不同的状态, 处理相应的数据
				 ********************************************************************************************************/
				always @(posedge clk or negedge rst_n)
				begin : RX_STATE_ACTION
				    if (~rst_n) begin
				        rRxDatBuf   				        rRxDat      				
				        rParVal     				
				        rFlgFraErr  				        rFlgOverRun 				        rFlgDone    				        rFlgParErr  				
				        rRxClkCnt   				        rRxBitCnt   				        rPlsBaudTick   				        
				    end
				    else begin
				
				        
				        rPlsBaudTick   				
				        if (rStatRxCur == RX_IDLE) begin
				            rRxClkCnt   				            rRxBitCnt   				        end
				        else if (clk_en) begin
				
				            rRxClkCnt   				            
				            if (rRxClkCnt == 4'h0F) begin
				                rPlsBaudTick   				                rRxBitCnt   				            end
				        end
				
				        if (wDatBufRdEn | (~enable) ) begin                     //  当读数据缓冲时, 清除相关的标志
				            rFlgFraErr  				            rFlgOverRun 				            rFlgParErr  				            rFlgDone    				        end
				
				        case (rStatRxCur)
				
				        RX_IDLE: begin
				            rParVal 				        end
				
				        RX_SYNC: begin
				        end
				
				        RX_DATA: begin
				            if (rPlsBaudTick) begin
				                rRxDatBuf 				                rParVal 				            end
				        end
				
				        RX_PARITY: begin
				            if (rPlsBaudTick) begin
				                if (wParMod)                                    //  奇校验
				                    rParVal 				                else                                            //  偶检验
				                    rParVal 				            end    
				        end
				
				        RX_STOP: begin
				            if (rRxClkCnt >= 4'd10) begin
				                if (~wRxdVal)
				                    rFlgFraErr  				            end
				        end
				
				        RX_ENDING: begin
				            if (rFlgDone) begin                                 //  如果数据未读, 则标志数超出标记
				                rFlgOverRun    				            end
				        end
				
				        RX_DONE: begin
				            /*
				             *  根据不同的帧数据位数, 调整接收到的数据(异位操作)
				             */
				            case (wDatWid[3:0])
				            4'h6:   rRxDat  				
				            4'h7:   rRxDat  				
				            4'h8:   rRxDat  				
				            4'h9:   rRxDat  				            endcase
				
				
				            rFlgDone    				
				            if (wParEn) begin                                   //  如果使能了奇偶检验, 则输出检查结果
				                rFlgParErr  				            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
				 ********************************************************************************************************/
				
							

相关资源