Verilog_回顾(2)

关于状态机的三段式:

            
                module uart_tx_ctrl (
                    input           clk,
                    input           rst_n,
                    input           send_start,    // 发送触发信号
                    input     [7:0] tx_data,       // 待发送的8位数据
                    output  reg     tx_pin,        // 串口发送引脚
                    output  reg     tx_done        // 发送完成标志
                );
                // ==========================================
                // 状态机定义 (时间线)
                // ==========================================
                localparam  IDLE    = 2'b00,       // 空闲状态
                            START   = 2'b01,       // 发送起始位
                            DATA    = 2'b10,       // 发送数据位
                            STOP    = 2'b11;       // 发送停止位
                reg [1:0] current_state, next_state;
                // ==========================================
                // 数据路径:计数器定义 (打工仔)
                // ==========================================
                reg [15:0] baud_cnt;               // 波特率计数器(时间计数)
                reg [2:0]  bit_cnt;                // 数据位计数器(数据计数)
                wire baud_done = (baud_cnt == 16'd5207); // 假设时钟50M,波特率9600,分频系数约5208
                wire bit_done  = (bit_cnt == 3'd7);      // 发完了8个bit (0~7)
                // ==========================================
                // 第一段:状态转移 (时序逻辑,管时间)
                // ==========================================
                always @(posedge clk or negedge rst_n) begin
                    if (!rst_n)
                        current_state <= IDLE;
                    else
                        current_state <= next_state;
                end
                // ==========================================
                // 第二段:状态跳转 (组合逻辑,管逻辑)
                // 注意:这里绝对没有 +1 操作,只看信号条件!
                // ==========================================
                always @(*) begin
                    // 默认保持当前状态(防止生成锁存器)
                    next_state = current_state; 
                    case (current_state)
                        IDLE: begin
                            if (send_start) 
                                next_state = START; // 老板说开始,就去START
                        end
                        START: begin
                            if (baud_done)          // 打工仔汇报时间到了,就去DATA
                                next_state = DATA;
                        end
                        DATA: begin
                            if (baud_done && bit_done) // 时间到了,且8个bit发完了,去STOP
                                next_state = STOP;
                        end
                        STOP: begin
                            if (baud_done)          // 停止位时间到了,回IDLE
                                next_state = IDLE;
                        end
                        default: next_state = IDLE;
                    endcase
                end
                // ==========================================
                // 第三段:数据输出与计数 (时序逻辑,管搬砖)
                // 状态机在这里只发号施令,计数器在这里埋头苦干
                // ==========================================
                always @(posedge clk or negedge rst_n) begin
                    if (!rst_n) begin
                        baud_cnt <= 16'd0;
                        bit_cnt  <= 3'd0;
                        tx_pin   <= 1'b1;          // 串口空闲时为高电平
                        tx_done  <= 1'b0;
                    end else begin
                        case (current_state)
                            IDLE: begin
                                tx_pin  <= 1'b1;
                                tx_done <= 1'b0;
                                baud_cnt <= 16'd0;
                                bit_cnt  <= 3'd0;  // 清零,准备下一次发送
                            end
                            START: begin
                                tx_pin <= 1'b0;    // 起始位拉低
                                if (baud_done) 
                                    baud_cnt <= 16'd0; // 时间到了就清零
                                else 
                                    baud_cnt <= baud_cnt + 1'b1; // 没到就接着数
                            end
                            DATA: begin
                                // 移位输出数据
                                tx_pin <= tx_data[bit_cnt]; 
                                if (baud_done) begin
                                    baud_cnt <= 16'd0;
                                    bit_cnt  <= bit_cnt + 1'b1; // 发完一个bit,数据计数器+1
                                end else begin
                                    baud_cnt <= baud_cnt + 1'b1;
                                end
                            end
                            STOP: begin
                                tx_pin <= 1'b1;    // 停止位拉高
                                if (baud_done) begin
                                    baud_cnt <= 16'd0;
                                    tx_done  <= 1'b1; // 发送完成标志
                                end else begin
                                    baud_cnt <= baud_cnt + 1'b1;
                                end
                            end
                        endcase
                    end
                end
                endmodule