/*********************************************************************************************************
** All right reserve 2008-2009(C)
** Created & maintain by http://www.edaok.net
**=======================================================================================================
** 模 块 名: uart.v
** 描 述: 实现UART的发送功能, 数据帧字节长度可设置, 支持奇偶检验功能; 实现了AVR单片机的通用异步串口
** 的大部分功能, (没有多机通信, 同步发送方式)
**
** 原 作 者: Adeko (http://www.edaok.net)
** 参 与 者: (...welcome you join in)
**
**=======================================================================================================
********************************************************************************************************/
module txd (
clk,
rst_n,
clk_en,
data_i,
enable,
txd_xo,
ctrl_i,
frame_bits_i,
stat_o,
busy_o
);
input clk; // 全局时钟时
input rst_n; // 全局复位时, 低有效
input clk_en;
input [7:0] data_i; // 将要发送到UART总线上的数据
input enable; // 模块使能线
output txd_xo; // UART数据发送端口
input [4:0] ctrl_i; // 控制信号输入
input [3:0] frame_bits_i; // 帧数据位设置输入
output [1:0] stat_o; // UART发送器状态输出
output busy_o; // 发送器正在工作, 非空闲
/*********************************************************************************************************
** UART发送器状态机状态定义
********************************************************************************************************/
`define TX_STAT_WIDTH 8
parameter [`TX_STAT_WIDTH - 1 : 0]
TX_IDLE = `TX_STAT_WIDTH'b0000_0001, // 总线空闲状态, 等待发送数据
TX_READY = `TX_STAT_WIDTH'b0000_0010, // 准备发送状态, 把数据放入发送缓冲区
TX_START = `TX_STAT_WIDTH'b0000_0100, // 发送起始化状态, 把总线拉低(清0)
TX_DATA = `TX_STAT_WIDTH'b0000_1000, // 发送数据位状态, 往总线逐位输出数据, LSB
TX_PARITY = `TX_STAT_WIDTH'b0001_0000, // 发送校验位状态, 根据帧格式配置输出
TX_STOP1 = `TX_STAT_WIDTH'b0010_0000, // 发送停止位状态, 往总线写"1"
TX_STOP2 = `TX_STAT_WIDTH'b0100_0000,
TX_DONE = `TX_STAT_WIDTH'b1000_0000; // 发送完志状态
reg [`TX_STAT_WIDTH - 1 : 0] rStatTxCur, // UART发送器当前的状态, 默认为空闲状态
rStatTxNext; // UART发送器下一个状态
reg [7:0] rTxDatSft;
reg [3:0] rTxClkCnt;
reg [3:0] rTxBitCnt;
reg rPlsBaudTick;
reg rPlsStatChanged;
initial // 为仿真测试初始化
begin
rStatTxCur rStatTxNext rTxDatSft rTxClkCnt rTxBitCnt rPlsBaudTick rPlsStatChanged end
wire wFlgParEn = ctrl_i[4]; // 1=使能奇偶校验位, 0=禁能
wire wFlgParMod = ctrl_i[3]; // 1=奇校验, 0=偶校验
wire wStopBits = ctrl_i[2]; // 1=2位停止位, 0=1位停止位
wire wPlsDoneClr = ctrl_i[1]; // 清除发送完成标志的脉冲
wire wFlgTxStart = ctrl_i[0]; // 启动发送信号
wire [3:0] wDatWid = frame_bits_i; // 帧的数据位长度, 实际位数为输入值-'1', 即"8"为'7'
reg rFlgDone; // 发送器完成发送标记
reg rFlgNewDat; // 发送器的发送缓冲器数据已载入, 可以申请新的数据
reg rParVal; // 奇偶校验的运算结果, 为偶校验结果, 奇校验则取反
initial
begin
rFlgDone rFlgNewDat rParVal end
/*********************************************************************************************************
** 更新当前的状态机, 并处理相关的关键数据
********************************************************************************************************/
always @(posedge clk or negedge rst_n)
begin : TX_STAT_UPDATE
if (~rst_n) begin
rStatTxCur rPlsStatChanged end
else begin
rStatTxCur rPlsStatChanged end
end
/*********************************************************************************************************
** 根据多个信号和当前状态机的状态, 判断发送器状态机的下一个状态
**
** [特别注意]:
** 状态值使用OneHot编码, 而这种编码一般会优为移位寄存器, 为了防止状态出现00000的情况(如时钟质量差),
** 在综合器中要设置'safe stat mechine = on'(安全状态机)
********************************************************************************************************/
always @(
rPlsBaudTick or
wFlgTxStart or
rStatTxCur or
rTxClkCnt or
rTxBitCnt or
wDatWid or
wFlgParEn or
wFlgParMod or
clk_en or
wStopBits or
enable
)
begin : TX_NEXT_STAT_JUDGE
case (rStatTxCur) // 根据当前状态机的状态, 判断输入信号, 得
// 到发送器状态机的下一个状态
TX_IDLE: begin
if (wFlgTxStart && enable) // 当模块端口的发送数据标志有效, 则启动发送事件
rStatTxNext else
rStatTxNext end
TX_READY: begin
if (clk_en) begin // 同步UART时钟
rStatTxNext end
else begin
rStatTxNext end
end
TX_START: begin
if (rPlsBaudTick) // 持续一个波特位, 进入帧的数据位状态
rStatTxNext else
rStatTxNext end
TX_DATA:
begin
if ( (rTxBitCnt == wDatWid) && (rPlsBaudTick) ) begin // 当逐位发送完帧格式设置的位数后, 进入下一状态
if (wFlgParEn)
rStatTxNext else
rStatTxNext end
else begin
rStatTxNext end
end
TX_PARITY: begin
if (rPlsBaudTick)
rStatTxNext else
rStatTxNext end
TX_STOP1: begin
if (rPlsBaudTick) begin
if (wStopBits) // 如果设置了2个停止位, 则再进入停止位
rStatTxNext else
rStatTxNext end
else begin
rStatTxNext end
end
TX_STOP2: begin
if (rPlsBaudTick)
rStatTxNext else
rStatTxNext end
TX_DONE: begin
rStatTxNext end
default: begin
rStatTxNext end
endcase
end
reg rTxdTmp;
reg rFlgBusy;
always @(posedge clk or negedge rst_n)
begin : TX_STAT_PROCESS
if (~rst_n) begin
rTxDatSft rTxdTmp
rFlgDone rFlgNewDat
rParVal
rFlgBusy
rTxClkCnt rTxBitCnt rPlsBaudTick
end
else begin
rPlsBaudTick
if ( (rStatTxCur == TX_IDLE) ||
(rStatTxCur == TX_READY) ) begin // 下一状态为空闲时, 复位计数器
rTxClkCnt rTxBitCnt end
else if (clk_en) begin
rTxClkCnt
if (rTxClkCnt == 4'hF) begin // UART波特率为clk_en / 16
rTxBitCnt rPlsBaudTick end
end
rFlgBusy rFlgNewDat
if (wPlsDoneClr) begin // 当模块端口清除完成标志有效
rFlgDone end
case (rStatTxCur)
TX_IDLE: begin
rTxdTmp rParVal rFlgBusy end
TX_READY: begin
rTxdTmp
if (rPlsStatChanged) begin
rTxDatSft rFlgNewDat end
end
TX_START: begin
rTxdTmp end
TX_DATA: begin
if ( (rPlsBaudTick || rPlsStatChanged) &&
(rTxBitCnt != wDatWid) ) begin
rParVal
rTxdTmp
rTxDatSft > 1;
end
end
TX_PARITY: begin
if (~wFlgParMod) // 偶校验
rTxdTmp else
rTxdTmp end
TX_DONE: begin
rTxdTmp rFlgDone end
default: begin
rTxdTmp end
endcase
end
end
assign txd_xo = rTxdTmp; // 输出UART的发送数据 (TxD)
assign stat_o = {rFlgDone, rFlgNewDat};
assign busy_o = rFlgBusy;
endmodule
/*********************************************************************************************************
** End Of File
********************************************************************************************************/