Menu Close

uart通信tx模块在FPGA实现的一种Verilog状态机写法(二)

上一个帖子中我做了固定参数的uart方案,如果模块的参数需要可设置该怎么做呢?奇偶校验位,停止位可变如何实现的呢?今天我们就来看看,使用状态机的写法如何实现这些。作者看过各种写法,对比来说,使用状态机的写法简单而又功能完善,易于学习和理解。话不多说,上代码。
%title插图%num
uart字符帧结构

`define NONE    0
`define ODD     1
`define EVEN    2
`define MARK    3
`define SPACE   4

`define STOPBIT_ONE         0
`define STOPBIT_ONEHALF     1
`define STOPBIT_TWO         2


module uart_tx #
(
    parameter BAUD = 9600,                    //波特率
    parameter CLK_F = 50_000_000,              //时钟频率
    parameter PARITY = `ODD,                    //校验方式
    parameter DATABITS = 8,                       //数据位长度
    parameter STOPBIT = `STOPBIT_ONEHALF         //停止位长度
)
(
    input wire clk,
    input wire rst,
    input wire [ DATABITS - 1: 0 ] data,
    input wire enable,

    output reg tx,
    output reg busy    //指示外部调用模块,在忙状态时候,不能接受新的数据
);


localparam CNT_MAX = CLK_F / BAUD;

//【1】缓存外部数据 data
reg [ DATABITS - 1 : 0 ] data_r;


always@( posedge clk or posedge rst )
begin
    if ( rst )
        data_r <= 0;
    else if ( enable && busy == 0 )
        data_r <= data;
    else
        data_r <= data_r;
end



//【2】用状态机来完成发送
reg [ 31: 0 ] cnt;
reg [ 3: 0 ] num;
//[1] 定义状态空间
localparam FSM_IDLE = 0; //空闲状态
localparam FSM_START = 1; //起始位状态
localparam FSM_DATA = 2; //数据位状态
localparam FSM_PARITY = 3; //校验位状态
localparam FSM_STOP = 4; //停止位状态


//[2]   定义状态变量
reg [ 2: 0 ] cstate;


//[3]   完成状态机
//每一个状态里:状态转移和判断、根据状态输出
always@( posedge clk or posedge rst )
begin
    if ( rst )
    begin
        cstate <= FSM_IDLE;
        busy <= 0;
        tx <= 1;
        cnt <= 0;
        num <= 0;
    end
    else
    case ( cstate )
        FSM_IDLE :
        begin                           //空闲状态
            tx <= 1;                    //空闲时发送1
            if ( enable )                   //需要发送数据
            begin
                cstate <= FSM_START;
                busy <= 1;
                cnt <= 0;
            end
            else
                cstate <= FSM_IDLE;
        end

        FSM_START :
        begin
            tx <= 0;                    //发送起始位

            if ( cnt == CNT_MAX - 1 )
            begin
                cnt <= 0;
                cstate <= FSM_DATA;
            end
            else
            begin
                cnt <= cnt + 1;
                cstate <= FSM_START;
            end
        end

        FSM_DATA :
        begin
            tx <= data_r[ num ];          //并串转换

            if ( cnt == CNT_MAX - 1 && num == DATABITS - 1 ) //单bit位接受完成,并且接受到指定bit位长度
            begin
                cstate <= ifparity ? FSM_PARITY : FSM_STOP; //根据有无校验位来决定状态跳转
                cnt <= 0;
                num <= 0;
            end
            else if ( cnt == CNT_MAX - 1 && num < DATABITS - 1 ) //单bit位接受完成,但还没接收完所有的bit位
            begin
                num <= num + 1;
                cnt <= 0;
            end
            else
            begin
                cnt <= cnt + 1;
                cstate <= FSM_DATA;
                num <= num;
            end
        end

        FSM_PARITY :
        begin
            tx <= parity_value;                 //将计算出来的校验位发送出去

            if ( cnt == CNT_MAX - 1 )
            begin
                cnt <= 0;
                cstate <= FSM_STOP;
            end
            else
            begin
                cnt <= cnt + 1;
                cstate <= FSM_PARITY;
            end
        end

        FSM_STOP :
        begin
            tx <= 1;                            //停止位的值是1,下面是控制发送长度(时间)
            case ( STOPBIT )
            `STOPBIT_ONE :
                if ( cnt == CNT_MAX - 1 ) //1位停止位
                begin
                    cnt <= 0;
                    cstate <= FSM_IDLE;
                    busy <= 0;
                end
                else
                begin
                    cnt <= cnt + 1;
                    cstate > 1 ) - 1 ) ) //1.5位停止位,CNT_MAX + CNT_MAX>>1
                    begin
                        cnt <= 0;
                        cstate <= FSM_IDLE;
                        busy <= 0;
                    end
                    else
                    begin
                        cnt <= cnt + 1;
                        cstate <= FSM_STOP;
                    end


                    `STOPBIT_TWO : if ( cnt == ( ( CNT_MAX << 1 ) - 1 ) ) //2位停止位,CNT_MAX<<1
                begin
                    cnt <= 0;
                    cstate <= FSM_IDLE;
                    busy <= 0;
                end
                else
                begin
                    cnt <= cnt + 1;
                    cstate <= FSM_STOP;
                end

                default : if ( cnt == CNT_MAX - 1 )
                begin
                    cnt <= 0;
                    cstate <= FSM_IDLE;
                    busy <= 0;
                end
                else
                begin
                    cnt <= cnt + 1;
                    cstate <= FSM_STOP;
                end
            endcase
        end

        default :
        begin
            cstate <= FSM_IDLE;
            busy <= 0;
            tx <= 1;
            cnt <= 0;
            num <= 0;
        end
    endcase
end


//校验位
reg ifparity;                           //是否有检验位
reg parity_value;                       //校验位的值

always@( * )
begin
    if ( rst )
    begin
        ifparity = 0 ;
        parity_value = 0 ;
    end
    else
    case ( PARITY )
        `NONE :
        begin
            ifparity = 0 ;
            parity_value = 0 ;
        end
        `ODD :
        begin
            ifparity = 1 ;
            parity_value = ~( ^ data_r ) ;
        end
        `EVEN :
        begin
            ifparity = 1 ;
            parity_value = ^ data_r ;
        end
        `MARK :
        begin
            ifparity = 1 ;
            parity_value = 1 ;
        end
        `SPACE :
        begin
            ifparity = 1 ;
            parity_value = 0 ;
        end
        default :
        begin
            ifparity = 0 ;
            parity_value = 0 ;
        end
    endcase
end


endmodule

 

–Nikola
–2021.01.28

Posted in FPGA, FPGA, Verilog, Verilog

发表评论

相关链接