状态机实现uart发送
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2024/05/22 10:42:06
// Design Name:
// Module Name: uart_tx_fsm
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module uart_tx_fsm(
input clk,
input rst_n,
input [7:0] data,
input data_en,
output reg uart_tx
);
parameter SYS_FREQ = 50_000_000; //系统时钟
parameter UART_B = 115200; //波特率
parameter CNT_PERIOD = 50_000_000/115200; //计数器最大值
parameter CNT_PERIOD_HALF = CNT_PERIOD >> 1; //计数器中间值
parameter BIT_CNT = 8; //一个字节
parameter IDLE = 3'd0; //空闲状态
parameter START = 3'd1; //接收到下降沿,完成同步,开始接收数据
parameter DATA = 3'd2; //接收数据
parameter VERIFY = 3'd3; //接收完成校验
parameter STOP = 3'd4; //接收完成
//rx 打两拍,检测上升沿,完成同步,在收到使能信号后开始,字节传送,避免亚稳态
reg status_pre;
reg status_post;
//时钟计时,在一个波特中计数
reg [8:0] cnt_data;
//波特计数,在一个接收周期中记录波特数
reg [3:0] cnt_bit_num;
//有限状态机,现态和次态
reg [2:0] now_status;
reg [2:0] next_status;
wire up_edge;
//data_en 上升沿检测
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
status_pre <= 0;
status_post <= 0;
end
else
begin
status_pre <= status_post;
status_post <= data_en;
end
end
assign up_edge = (~status_pre) & status_post;
//时钟计数,时钟计数应该对齐上升沿
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
cnt_data <= 0;
cnt_bit_num <= 0;
end
else if(now_status != IDLE)
begin
if(cnt_data <CNT_PERIOD)
cnt_data <= cnt_data + 1;
else begin
cnt_data <= 0;
if(cnt_bit_num <= BIT_CNT)
cnt_bit_num <= cnt_bit_num + 1;
else
cnt_bit_num <= 0;
end
end
else
cnt_data <= 0;
end
//状态转换
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
now_status <= IDLE;
else
now_status <= next_status;
end
//状态转移条件
always @(*) begin
case(now_status)
IDLE:
begin
if(up_edge)
next_status = START;
else
next_status = IDLE;
end
START:
begin
if(cnt_bit_num == 1 && cnt_data ==0 )
next_status = DATA;
else
next_status = START;
end
DATA:
if(cnt_bit_num == 9 && cnt_data ==0 )
next_status = IDLE;
else
next_status = DATA;
default:now_status = IDLE;
endcase
end
//每个状态下的动作
always @(posedge clk or negedge rst_n) begin
case(now_status)
IDLE:
begin
uart_tx <= 1;
cnt_bit_num <= 0;
end
START:
begin
if(cnt_data <= CNT_PERIOD && cnt_data >= 0)
uart_tx <= 0;
else
uart_tx <= 1;
end
DATA:
begin
case(cnt_bit_num)
1: uart_tx <= data[0];
2: uart_tx <= data[1];
3: uart_tx <= data[2];
4: uart_tx <= data[3];
5: uart_tx <= data[4];
6: uart_tx <= data[5];
7: uart_tx <= data[6];
8: uart_tx <= data[7];
default: uart_tx <= 1;
endcase
end
default: uart_tx <= 1;
endcase
end
endmodule
testbench
`timescale 1ns / 1ns
module tb_uart_tx_fsm;
// uart_tx_fsm Parameters
parameter PERIOD = 20 ;
parameter SYS_FREQ = 50_000_000 ;
parameter UART_B = 115200 ;
parameter CNT_PERIOD = 50_000_000/115200;
parameter CNT_PERIOD_HALF = CNT_PERIOD >> 1 ;
parameter BIT_CNT = 8 ;
parameter IDLE = 3'd0 ;
parameter START = 3'd1 ;
parameter DATA = 3'd2 ;
parameter VERIFY = 3'd3 ;
parameter STOP = 3'd4 ;
// uart_tx_fsm Inputs
reg clk = 0 ;
reg rst_n = 0 ;
reg [7:0] data = 8'b01101100;
reg data_en = 0 ;
// uart_tx_fsm Outputs
wire uart_tx ;
initial
begin
forever #(PERIOD/2) clk=~clk;
end
initial
begin
#(PERIOD*2) rst_n = 1;
end
initial
begin
#100 data_en = 1;
#8650 data_en = 0;
end
uart_tx_fsm #(
.SYS_FREQ ( SYS_FREQ ),
.UART_B ( UART_B ),
.CNT_PERIOD ( CNT_PERIOD ),
.CNT_PERIOD_HALF ( CNT_PERIOD_HALF ),
.BIT_CNT ( BIT_CNT ),
.IDLE ( IDLE ),
.START ( START ),
.DATA ( DATA ),
.VERIFY ( VERIFY ),
.STOP ( STOP ))
u_uart_tx_fsm (
.clk ( clk ),
.rst_n ( rst_n ),
.data ( data [7:0] ),
.data_en ( data_en ),
.uart_tx ( uart_tx )
);
endmodule
传统仿真
状态机仿真
传统UART代码来自正点原子例程