发送模块
- 划分为5个状态:空闲状态、发送起始位、发送数据位、发送校验位、发送停止位。
- 通过检测上升沿,检测发送模块是否被使能。需要注意的是上升沿检测的两个寄存器uart_en_cur、uart_en_pre的初始值问题。如果两个寄存器初始值为0,当uart_en初始信号为1时,就会错误的检测到一个上升沿信号。
- 注意三段式状态机里面,状态转移的判断里面尽量不要出现其他变量的赋值,只是判断状态的转移。先前有过在里面对data_cnt(data_cnt = data_cnt + 1)进行赋值,但是被综合成为了一个锁存器。而锁存器对于毛刺比较敏感,组合逻辑电路中可能存在竞争冒险,产生了毛刺,所以data_cnt的值不受控制。之后将状态切换的输出单独写在一个always模块中(时序逻辑)。
- uart的时钟通过对系统时钟的计数实现。
- 状态输出和状态切换的处理其实可以放在一个always模块里面。
- 实现代码
module uart_send(
input sys_clk,
input rst_n,
input uart_en,
input [7:0] uart_din,
output uart_status,
output reg uart_txd
);
localparam VERIFY_NONE = 0;
localparam VERIFY_ODD = 1;
localparam VERIFY_EVEN = 2;
localparam STATUS_IDLE = 3'b000;
localparam STATUS_START = 3'b100;
localparam STATUS_DATA = 3'b101;
localparam STATUS_VERIFY = 3'b110;
localparam STATUS_STOP = 3'b111;
parameter SYS_FREQ = 50_000_000;
parameter UART_BAUDRATE = 115200;
parameter PERIOD_CNT = SYS_FREQ/UART_BAUDRATE;
parameter DATA_SIZE = 8;
parameter STOP_SIZE = 1;
parameter VERIFY_SIZE = 0;
parameter VERIFY_METHOD = VERIFY_NONE;
reg uart_en_cur;
reg uart_en_pre;
reg uart_verify_bit;
reg [7:0] uart_data;
reg [2:0] present_status;
reg [2:0] next_status;
reg [2:0] data_cnt;
reg [1:0] stop_cnt;
reg [15:0]clk_cnt;
always @(posedge sys_clk or negedge rst_n)begin
if(!rst_n)begin
uart_en_cur <= 1'b1;
uart_en_pre <= 1'b1;
end
else begin
uart_en_cur <= uart_en;
uart_en_pre <= uart_en_cur;
end
end
assign uart_status = (present_status == STATUS_IDLE)?1'b0:1'b1;
always @(posedge sys_clk or negedge rst_n)begin
if(!rst_n)begin
clk_cnt <= 16'b0;
end
else if(present_status != STATUS_IDLE)begin
if(clk_cnt < PERIOD_CNT)
clk_cnt <= clk_cnt + 16'b1;
else
clk_cnt <= 16'b0;
end
else
clk_cnt <= 16'b0;
end
always @(posedge sys_clk or negedge rst_n)begin
if(!rst_n)
present_status <= STATUS_IDLE;
else
present_status <= next_status;
end
always @(*) begin
case(present_status)
STATUS_IDLE://空闲状态
begin
if((~uart_en_pre) & uart_en_cur)
next_status = STATUS_START;
else
next_status = STATUS_IDLE;
end
STATUS_START:
begin
if(clk_cnt == PERIOD_CNT)
next_status = STATUS_DATA;
else
next_status = STATUS_START;
end
STATUS_DATA://发送数据
begin
if(clk_cnt == PERIOD_CNT && data_cnt == DATA_SIZE - 1)begin
if(VERIFY_METHOD == VERIFY_NONE)
next_status = STATUS_STOP;
else
next_status = STATUS_VERIFY;
end
else
next_status = STATUS_DATA;
end
STATUS_VERIFY://发送校验位状态
begin
if(clk_cnt == PERIOD_CNT)
next_status = STATUS_STOP;
else
next_status = STATUS_VERIFY;
end
STATUS_STOP:
begin
if(clk_cnt == (PERIOD_CNT >> 1) && stop_cnt == STOP_SIZE - 1)
next_status = STATUS_IDLE;
else
next_status = STATUS_STOP;
end
default:next_status = STATUS_IDLE;
endcase
end
always @(posedge sys_clk or negedge rst_n)begin
if(!rst_n)begin
data_cnt <= 3'b0;
stop_cnt <= 2'b0;
end
else begin
case(present_status)
STATUS_IDLE:
begin
if(next_status == STATUS_START)
uart_data <= uart_din;
end
STATUS_START:
begin
if(next_status == STATUS_DATA)begin
data_cnt <= 3'b0;
stop_cnt <= 2'b0;
if(VERIFY_METHOD == VERIFY_EVEN)
uart_verify_bit = ^uart_data;
else if(VERIFY_METHOD == VERIFY_ODD)
uart_verify_bit = ~(^uart_data);
end
end
STATUS_DATA:
begin
if(clk_cnt == PERIOD_CNT && data_cnt < DATA_SIZE - 1)
data_cnt <= data_cnt + 3'b1;
end
STATUS_STOP:
begin
if(clk_cnt == PERIOD_CNT && stop_cnt < STOP_SIZE - 1)
stop_cnt <= stop_cnt + 2'b1;
end
default:;
endcase
end
end
always @(posedge sys_clk or negedge rst_n)begin
if(!rst_n)
uart_txd <= 1'b1;
else begin
case(present_status)
STATUS_START:uart_txd <= 1'b0;
STATUS_DATA:uart_txd <= uart_data[data_cnt];
STATUS_VERIFY:uart_txd <= uart_verify_bit;
STATUS_STOP:uart_txd <= 1'b1;
default:uart_txd <= 1'b1;
endcase
end
end
endmodule
接收模块
module uart_recv(
input sys_clk,
input rst_n,
input uart_rxd,
output reg uart_done,
output reg [7:0] uart_dout
);
localparam VERIFY_NONE = 0;
localparam VERIFY_ODD = 1;
localparam VERIFY_EVEN = 2;
localparam STATUS_IDLE = 3'b000;
localparam STATUS_START = 3'b100;
localparam STATUS_DATA = 3'b101;
localparam STATUS_VERIFY = 3'b110;
localparam STATUS_STOP = 3'b111;
parameter SYS_FREQ = 50_000_000;
parameter UART_BAUDRATE = 115200;
parameter PERIOD_CNT = SYS_FREQ/UART_BAUDRATE;
parameter DATA_SIZE = 8;
parameter STOP_SIZE = 1;
parameter VERIFY_SIZE = 0;
parameter VERIFY_METHOD = VERIFY_NONE;
reg uart_rxd_cur;
reg uart_rxd_pre;
reg [2:0] present_status;
reg [2:0] next_status;
reg [2:0] data_cnt;
reg [1:0] stop_cnt;
reg [15:0]clk_cnt;
always@(posedge sys_clk or negedge rst_n)begin
if(!rst_n)begin
uart_rxd_cur <= 1'b0;
uart_rxd_pre <= 1'b0;
end
else begin
uart_rxd_cur <= uart_rxd;
uart_rxd_pre <= uart_rxd_cur;
end
end
always @(posedge sys_clk or negedge rst_n)begin
if(!rst_n)begin
clk_cnt <= 16'b0;
end
else if(present_status != STATUS_IDLE)begin
if(clk_cnt < PERIOD_CNT)
clk_cnt <= clk_cnt + 16'b1;
else
clk_cnt <= 16'b0;
end
else
clk_cnt <= 16'b0;
end
always @(posedge sys_clk or negedge rst_n)begin
if(!rst_n)
present_status <= STATUS_IDLE;
else
present_status <= next_status;
end
always @(*) begin
case(present_status)
STATUS_IDLE://空闲状态
begin
if((~uart_rxd_cur) & uart_rxd_pre)
next_status = STATUS_START;
else
next_status = STATUS_IDLE;
end
STATUS_START:
begin
if(clk_cnt == (PERIOD_CNT>>1))begin
if(uart_rxd == 1'b0)
next_status = STATUS_DATA;
else
next_status = STATUS_IDLE;
end
else
next_status = STATUS_START;
end
STATUS_DATA:
begin
if(clk_cnt == (PERIOD_CNT>>1) && data_cnt == DATA_SIZE-1)begin
if(VERIFY_METHOD == VERIFY_NONE)
next_status = STATUS_STOP;
else
next_status = STATUS_VERIFY;
end
else
next_status = STATUS_DATA;
end
STATUS_VERIFY://接收校验位状态
begin
if(clk_cnt == (PERIOD_CNT>>1))
next_status = STATUS_STOP;
else
next_status = STATUS_VERIFY;
end
STATUS_STOP:
begin
if(clk_cnt == (PERIOD_CNT>>1) && stop_cnt == STOP_SIZE-1)
next_status = STATUS_IDLE;
else
next_status = STATUS_STOP;
end
default:next_status = STATUS_IDLE;
endcase
end
always @(posedge sys_clk or negedge rst_n)begin
if(!rst_n)begin
data_cnt <= 3'b0;
stop_cnt <= 2'b0;
uart_done <= 1'b0;
end
else begin
case(present_status)
STATUS_IDLE:uart_done <= 1'b0;
STATUS_START:
begin
if(next_status == STATUS_DATA)begin
data_cnt <= 3'b0;
stop_cnt <= 2'b0;
end
end
STATUS_DATA:
begin
if(clk_cnt == (PERIOD_CNT>>1))begin
data_cnt <= data_cnt + 3'b1;
uart_dout[data_cnt] <= uart_rxd;
end
if(next_status == STATUS_STOP)
uart_done <= 1'b1;
else
uart_done <= 1'b0;
end
STATUS_VERIFY:
begin
if(next_status == STATUS_STOP)begin
if((VERIFY_METHOD == VERIFY_EVEN && uart_rxd == ^uart_dout)||
(VERIFY_METHOD == VERIFY_ODD && uart_rxd == ~(^uart_dout)))
uart_done <= 1'b1;
else
uart_done <= 1'b0;
end
end
STATUS_STOP:
begin
if(clk_cnt == (PERIOD_CNT>>1))
stop_cnt <= stop_cnt + 2'b1;
end
default:
begin
data_cnt <= 3'b0;
stop_cnt <= 2'b0;
uart_done <= 1'b0;
end
endcase
end
end
endmodule
测试:实现数据回环
- PC发送数据给FPGA串口,串口返回其发送的数据。
- 实现代码
module uart_test(
input sys_clk,
input sys_rst_n,
input uart_rxd,
output uart_txd
);
wire uart_tx_st;
wire uart_recv_done;
wire [7:0] uart_data_r;
reg uart_recv_done_cur;
reg uart_recv_done_pre;
reg uart_en_w;
reg [7:0] uart_data_w;
reg [7:0] pulse_cnt;
uart_send uart_send_instance(
.sys_clk (sys_clk),
.rst_n (sys_rst_n),
.uart_en (uart_recv_done),
.uart_din (uart_data_r),
.uart_status(uart_tx_st),
.uart_txd (uart_txd)
);
uart_recv uart_recv_instance(
.sys_clk (sys_clk),
.rst_n (sys_rst_n),
.uart_rxd (uart_rxd),
.uart_done (uart_recv_done),
.uart_dout (uart_data_r)
);
endmodule