/*********************************************************************************************************
** 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
********************************************************************************************************/