UART+FIFO
目标:接收外部串口数据并通过fifo缓存,通过按钮发送缓存的数据
Source
top_module
- uart模块
- 按钮按下检测电路
- 此处可以改进增加消抖电路
- fifo读写数据控制
`timescale 1ns / 1ps
/**
串口收发+fifo缓冲
*/
module top_module(
clk,
reset_in,
rx,
tx,
en, // 使能信号 拨码开关
mode_trans, // 模式切换 按钮
rx_full, // 接收满信号 LED1
rx_empty, // 接收空信号 LED2
tx_full, // 发射满信号 LED3
tx_empty, // 发送空信号 LED4
tx_rx_mode // 发送接收模式 LED8
);
input clk;
input reset_in;
input rx;
output wire tx;
input en;
input mode_trans;
output wire rx_full;
output wire tx_empty;
output wire rx_empty;
output wire tx_full;
wire reset;
assign reset = ~reset_in;
output reg tx_rx_mode=1; // 默认为串口接收状态
reg [7:0] data_tx_fifo = 0;
wire [7:0] data_rx_fifo;
reg fifo_tx_wr_en = 0;
reg fifo_rx_rd_en = 0;
wire fifo_tx_full;
wire fifo_tx_empty;
wire fifo_rx_empty;
wire fifo_rx_full;
wire fifo_tx_wr_rst_busy;
wire fifo_tx_rd_rst_busy;
wire fifo_rx_rd_rst_busy;
wire fifo_rx_wr_rst_busy;
wire fifo_rx_valid;
assign rx_full = fifo_rx_full;
assign tx_empty = fifo_tx_empty;
assign rx_empty = fifo_rx_empty;
assign tx_full = fifo_tx_full;
uart
#(
.baud_rate(115200),
.clk_freq(50000000)
)uart_inst(
.clk(clk),
.reset(reset),
.tx_rx_mode(tx_rx_mode),
.en(en),
.rx(rx),
.tx(tx),
.data_tx_fifo(data_tx_fifo),
.fifo_tx_wr_en(fifo_tx_wr_en),
.fifo_tx_full(fifo_tx_full),
.fifo_tx_empty(fifo_tx_empty),
.fifo_tx_wr_rst_busy(fifo_tx_wr_rst_busy), // 后期可以把rx_fifo的wr_busy引脚接到uart_rx中
.fifo_tx_rd_rst_busy(fifo_tx_rd_rst_busy),
.data_rx_fifo(data_rx_fifo),
.fifo_rx_rd_en(fifo_rx_rd_en),
.fifo_rx_full(fifo_rx_full),
.fifo_rx_empty(fifo_rx_empty),
.fifo_rx_valid(fifo_rx_valid),
.fifo_rx_rd_rst_busy(fifo_rx_rd_rst_busy), // 后期可以把tx_fifo的rd_busy引脚接到uart_tx中
.fifo_rx_wr_rst_busy(fifo_rx_wr_rst_busy)
);
// detect the tx_rx_mode transition
reg [1:0] mode_trans_cap = 1;
wire mode_trans_negedge ;
always @(posedge clk or posedge reset) begin
if (reset) begin
mode_trans_cap <= 2'b11;
end
else begin
mode_trans_cap[1] <= mode_trans_cap[0];
mode_trans_cap[0] <= mode_trans;
end
end
assign mode_trans_negedge = mode_trans_cap[1]&~mode_trans_cap;
always @(posedge clk or posedge reset) begin
if (reset) begin
tx_rx_mode <= 1; // 默认接收模式
end
else begin
if(mode_trans_negedge) begin
tx_rx_mode <= ~tx_rx_mode;
end
end
end
// write data to tx_fifo or read data from rx_fifo
// read control
always @(posedge clk or posedge reset) begin
if (reset) begin
end
else begin
if(tx_rx_mode == 0) begin // 发送模式下从rx_fifo读数据并写入到tx_fifo中
if(~fifo_rx_empty) begin
fifo_rx_rd_en <= 1;
end
else begin
fifo_rx_rd_en <= 0;
end
end
end
end
// read data from rx_fifo
always @(posedge clk or posedge reset) begin
if(reset) begin
data_tx_fifo <= 0;
end
else begin
if(tx_rx_mode == 0) begin
if(fifo_rx_valid) begin
data_tx_fifo <= data_rx_fifo;
end
end
end
end
// write control
always @(posedge clk or posedge reset) begin
if(reset) begin
fifo_tx_wr_en <= 0;
end
else begin
if(tx_rx_mode == 0) begin
if(fifo_rx_valid & ~fifo_tx_full) begin //读出数据并且fifo_tx未满
fifo_tx_wr_en <= 1;
end
else begin
fifo_tx_wr_en <= 0;
end
end
end
end
endmodule
uart
内部例化uart_tx和uart_rx模块,以及对应的fifo模块
并实现uart_tx从fifo_uart_tx中读数据 及 uart_rx向fifo_uart_rx中写数据
uart_tx输出1字节开始发送信号tx_start,当串口处于tx_mode且tx_start有效且此时fifo_tx中的数据不为空时,向fifo_tx发送读请求
uart_rx输出1字节接收完成信号rx_done,当uart_rx接收到完整的一个字节数据并且fifo未满并且处于rx模式,向fifo_rx发送写请求
-
uart_tx
-
uart_rx
-
fifo_juart_tx 缓存串口发送数据
-
fifo_uart_rx 缓存串口接收数据
其实实现需求用一块fifo即可,此处用两块fifo便于之后的拓展
`timescale 1ns / 1ps
module uart(
// control signal
clk,
reset, // reset uart when in high level
tx_rx_mode, // 0--tx_mode 1--rx_mode
en, // enable the uart tx or rx mode
// for tx, en start the transmit of data in transmit fifo
// for rx, en enable uart_rx to write data received to fifo
// data signal
rx, // rx bus
tx, // tx bus
// control signal
data_tx_fifo, // write data to fifo_tx
fifo_tx_wr_en, // fifo_tx_data 写使能
fifo_tx_full,
fifo_tx_empty,
fifo_tx_wr_rst_busy,
fifo_tx_rd_rst_busy,
data_rx_fifo, // read data from fifo_rx
fifo_rx_rd_en, // uart_
fifo_rx_full,
fifo_rx_empty,
fifo_rx_valid,
fifo_rx_rd_rst_busy,
fifo_rx_wr_rst_busy
);
parameter baud_rate = 115200; // default baudrate [Hz]
parameter clk_freq = 50000000; // default clock frequency [Hz]
input clk;
input reset;
input tx_rx_mode;
input en;
input rx;
output tx;
input [7:0] data_tx_fifo;
output wire [7:0] data_rx_fifo;
// uart tx module
wire [7:0] data_tx;
wire tx_start;
wire uart_inst_tx_en;
input wire fifo_tx_wr_en;
wire fifo_tx_rd_en;
output wire fifo_tx_full;
wire fifo_tx_al_full;
// output wire fifo_tx_wr_ack;
output wire fifo_tx_empty;
wire fifo_tx_al_empty;
wire fifo_tx_valid;
output wire fifo_tx_wr_rst_busy;
output wire fifo_tx_rd_rst_busy;
// enable the uart_tx when in tx mode, en is valid and fifo_tx is not empty
assign uart_tx_inst_en = en & ~tx_rx_mode & ~fifo_tx_empty;
uart_tx
#(
.baud_rate(baud_rate),
.clk_freq(50000000)
)
uart_tx_inst(
.clk(clk),
.reset(reset),
.tx(tx),
.data(data_tx),
.en(uart_tx_inst_en),
.tx_start(tx_start)
);
// uart_tx 每次发送开始 & fifo非空 & tx mode
assign fifo_tx_rd_en = tx_start & (~fifo_tx_empty) & (~tx_rx_mode);
// transmit data fifo
fifo_uart fifo_uart_tx(
.clk(clk),
.rst(reset),
.din(data_tx_fifo),
.dout(data_tx),
.wr_en(fifo_tx_wr_en),
.full(fifo_tx_full),
.almost_full(fifo_tx_al_full),
// .wr_ack(fifo_tx_wr_ack),
.wr_rst_busy(fifo_tx_wr_rst_busy),
.rd_en(fifo_tx_rd_en),
.empty(fifo_tx_empty),
.almost_empty(fifo_tx_al_empty),
.valid(fifo_tx_valid),
.rd_rst_busy(fifo_tx_rd_rst_busy)
);
// uart rx module
wire [7:0] data_rx;
wire rx_done;
wire uart_rx_inst_en;
assign uart_rx_inst_en = en & tx_rx_mode & ~fifo_rx_full;
// wire rx_valid;
uart_rx
#(
.baud_rate(baud_rate),
.clk_freq(50000000)
)
uart_rx_inst(
.clk(clk),
.reset(reset),
.rx(rx),
.data(data_rx),
.rx_done(rx_done),
.en(uart_rx_inst_en)
);
// receive data fifo
wire fifo_rx_wr_en;
input fifo_rx_rd_en;
output wire fifo_rx_full;
wire fifo_rx_al_full;
wire fifo_rx_wr_ack;
output wire fifo_rx_empty;
wire fifo_rx_al_empty;
output wire fifo_rx_valid;
output wire fifo_rx_rd_rst_busy;
output wire fifo_rx_wr_rst_busy;
fifo_uart fifo_uart_rx(
.clk(clk),
.rst(reset),
.din(data_rx),
.dout(data_rx_fifo),
.wr_en(fifo_rx_wr_en),
.full(fifo_rx_full),
.almost_full(fifo_rx_al_full),
.wr_ack(fifo_rx_wr_ack),
.overflow(fifo_rx_overflow),
.wr_rst_busy(fifo_rx_wr_rst_busy),
.rd_en(fifo_rx_rd_en),
.empty(fifo_rx_empty),
.almost_empty(fifo_rx_al_empty),
.valid(fifo_rx_valid),
.underflow(fifo_rx_underflow),
.rd_rst_busy(fifo_rx_rd_rst_busy)
);
// write data to fifo_uart_rx
// uart_rx 接收到完整的一个字节数据 && fifo未满 && 处于rx模式
assign fifo_rx_wr_en = rx_done & en & ~fifo_rx_full & tx_rx_mode;
endmodule
uart_tx
`timescale 1ns / 1ps
module uart_tx(
clk,
tx,
reset, // 高电平复位
data,
en,
tx_start // 开始进入发送状态
);
parameter clk_freq = 50000000;
parameter baud_rate = 115200; // 默认波特率为115200Hz
parameter counter_max = clk_freq/baud_rate - 1;
input clk;
input [7:0] data;
input reset;
input en;
output reg tx = 1;
// data transmitter
/*
state 0: idle
state 1: start bit
state 2~9: data bit
state 10: stop bit
*/
reg [3:0] state = 0;
reg [3:0] next_state = 0;
// counter
reg [15:0] counter = 0;
always @(posedge clk or posedge reset) begin
if (reset) begin // when reset is effective
counter <= 0;
end
else if (state != 0) begin
if (counter == counter_max) begin
counter <= 0;
end
else begin
counter <= counter + 1;
end
end
end
// next_state logic
always @(posedge clk or posedge reset) begin
if (reset) begin
next_state <= 0;
end
else begin
if (en == 1 & state == 0)begin
next_state <= 1;
end
else if(counter == counter_max) begin
if (next_state == 10) begin
if(en == 1) begin
next_state <= 1;
end
else
next_state <= 0;
end
else begin
next_state <= next_state + 1;
end
end
end
end
// state change logic
always @(posedge clk or posedge reset) begin
if (reset) begin
state <= 0;
end
else begin
state <= next_state;
end
end
//output logic
always @(*) begin
case (state)
4'd0: begin
tx = 1;
end
4'd1: begin
tx = 0;
end
4'd2: begin
tx = data[0];
end
4'd3: begin
tx = data[1];
end
4'd4: begin
tx = data[2];
end
4'd5: begin
tx = data[3];
end
4'd6: begin
tx = data[4];
end
4'd7: begin
tx = data[5];
end
4'd8: begin
tx = data[6];
end
4'd9: begin
tx = data[7];
end
4'd10: begin
tx = 1;
end
default: begin
tx = 1;
end
endcase
end
// 采用state切换检测一个字节发送完成
reg[3:0] state_last_clk = 0;
reg[3:0] state_now = 0;
always @(posedge clk or posedge reset) begin
if(reset) begin
state_last_clk <= 0;
state_now <= 0;
end
else begin
state_last_clk <= state_now;
state_now <= state;
end
end
output wire tx_start; // 1字节开始传输,长达一个时钟周期
assign tx_start = (state_last_clk == 4'd0 | state_last_clk == 4'd10)&(state_now == 4'd1);
endmodule
uart_rx
`timescale 1ns / 1ps
module uart_rx(
clk,
reset, // 高电平复位
rx,
data,
en, // en使能后才会进入接收状态
rx_done
);
parameter clk_freq = 50000000;
parameter baud_rate = 115200; // 默认波特率为115200Hz
parameter counter_max = clk_freq/baud_rate - 1;
input clk;
output reg [7:0] data;
input reset;
input rx;
input en;
output wire rx_done;
// rx negedge detect
reg [1:0] rx_cap = 2'b11;
wire rx_negedge;
always @(posedge clk or posedge reset) begin
if (reset) begin
rx_cap <= 2'b11;
end
else begin
rx_cap[1] <= rx_cap[0];
rx_cap[0] <= rx;
end
end
assign rx_negedge = rx_cap[1]&&(~rx_cap[0]);
// data receiver
/*
state 0: idle
state 1: start bit
state 2~9: data bit
state 10: stop bit
*/
reg [3:0] state = 0;
reg [3:0] next_state = 0;
// counter
reg [15:0] counter = 0;
always @(posedge clk or posedge reset) begin
if(reset) begin
counter <= 0;
end
else if (state != 0) begin
if(counter == counter_max) begin
counter <= 0;
end
else begin
counter <= counter + 1;
end
end
end
// next state logic
always @(posedge clk or posedge reset) begin
if (reset) begin
next_state <= 0;
end
else begin
if (rx_negedge == 1 && en == 1 && (state == 0|| state == 10)) begin // rx_negedge captured and is idle state
next_state <= 1;
end
else if(counter == counter_max) begin
if(next_state == 10) begin
next_state <= 0;
end
else begin
next_state <= next_state + 1;
end
end
end
end
// state change logic
always @(posedge clk or posedge reset) begin
if(reset) begin
state <= 0;
end
else begin
state <= next_state;
end
end
//output logic
always @(posedge clk or posedge reset) begin
if(reset) begin
data <= 0;
end
else begin
if(counter == counter_max/2) begin
case (state)
4'd2: begin
data[0] <= rx;
end
4'd3: begin
data[1] <= rx;
end
4'd4: begin
data[2] <= rx;
end
4'd5: begin
data[3] <= rx;
end
4'd6: begin
data[4] <= rx;
end
4'd7: begin
data[5] <= rx;
end
4'd8: begin
data[6] <= rx;
end
4'd9 : begin
data[7] <= rx;
end
default:;
endcase
end
end
end
// 采用state切换检测发送状态
reg [3:0] state_last_clk = 0;
reg [3:0] state_now = 0;
always @(posedge clk or posedge reset) begin
if(reset) begin
state_last_clk <= 0;
state_now <= 0;
end
else begin
state_last_clk <= state_now;
state_now <= state;
end
end
// 1字节数据接收完成信号
assign rx_done = (state_last_clk == 9)&(state_now == 10);
endmodule
Testbench
top_module_tb
`timescale 1ns / 1ps
module top_module_tb(
);
reg clk = 0;
always #10 clk <= ~clk;
reg reset = 0;
wire pc_tx,pc_rx;
reg en = 0;
reg mode_button = 1; // 按钮未按下为高电平
wire rx_full,tx_empty;
top_module inst(
.clk(clk),
.reset(reset),
.rx(pc_tx),
.tx(pc_rx),
.en(en),
.mode_trans(mode_button),
.rx_full(rx_full),
.tx_empty(tx_empty)
);
reg[11:0] counter = 1000; // 循环1000次
initial begin
reset <= 0;
#500;
reset <= 1;
#1000;
reset <= 0;
#100;
en <= 1;
while (counter >=1) begin
counter <= counter - 1;
wait(rx_full == 1) begin // 等待rx_fifo接受满时按下mode button,切换成发送状态
mode_button <= 0;
#100;
mode_button <= 1;
end
#100;
wait(tx_empty == 1) begin // 等待tx_fifo发送空时按下mode button,切换成接收状态
mode_button <= 0;
#100;
mode_button <= 1;
end
end
end
// pc发送数据
reg [7:0] pc_data = 8'b00000001;
wire tx_start;
uart_tx uart_tx_inst(
.clk(clk),
.reset(reset),
.tx(pc_tx),
.data(pc_data),
.tx_start(tx_start),
.en(en)
);
always @(posedge clk or posedge reset) begin
if (reset == 1) begin
pc_data <= 8'b00000001;
end
else begin
if (tx_start == 1) begin
pc_data <= {pc_data[6:0],pc_data[7]};
end
end
end
endmodule
uart_tb
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2023/08/09 21:45:40
// Design Name:
// Module Name: uart_tb
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module uart_tb(
);
// 50MHz时钟信号-20ns
reg clk = 0;
always #10 clk = ~clk;
reg reset = 1;
reg tx_rx_mode = 1;
reg en = 0;
wire rx;
wire tx;
// ports for fifo_uart_tx
reg [7:0] data_tx_fifo = 8'b01001001;
reg fifo_tx_wr_en = 0;
wire fifo_tx_full;
wire fifo_tx_empty;
wire fifo_tx_wr_rst_busy;
wire fifo_tx_rd_rst_busy;
// ports for fifo_uart_rx
wire [7:0] data_rx_fifo;
reg fifo_rx_rd_en = 0;
wire fifo_rx_full;
wire fifo_rx_empty;
wire fifo_rx_rd_rst_busy;
wire fifo_rx_wr_rst_busy;
// 测试往fifo_uart_tx写入数据并通过串口发送
uart
#(
.baud_rate(115200),
.clk_freq(50000000)
)uart_inst(
.clk(clk),
.reset(reset),
.tx_rx_mode(tx_rx_mode),
.en(en),
.rx(rx),
.tx(tx),
.data_tx_fifo(data_tx_fifo),
.fifo_tx_wr_en(fifo_tx_wr_en),
.fifo_tx_full(fifo_tx_full),
.fifo_tx_empty(fifo_tx_empty),
.fifo_tx_rd_rst_busy(fifo_tx_rd_rst_busy),
.fifo_tx_wr_rst_busy(fifo_tx_wr_rst_busy),
.data_rx_fifo(data_rx_fifo),
.fifo_rx_rd_en(fifo_rx_rd_en),
.fifo_rx_full(fifo_rx_full),
.fifo_rx_empty(fifo_rx_empty),
.fifo_rx_rd_rst_busy(fifo_rx_rd_rst_busy),
.fifo_rx_wr_rst_busy(fifo_rx_wr_rst_busy)
);
// verify the uart_rx and fifo_uart_rx module
reg [7:0] data_tx_test = 8'b01001010;
wire tx_start;
reg uart_tx_inst_en = 1;
uart_tx
#(
.clk_freq(50000000),
.baud_rate(115200)
)uart_tx_inst(
.clk(clk),
.tx(rx),
.reset(reset),
.data(data_tx_test),
.en(uart_tx_inst_en),
.tx_start(tx_start)
);
// change the tx port data by byte
always @(posedge tx_start) begin
if (tx_start) begin
data_tx_test <= {data_tx_test[6:0],data_tx_test[7]};
end
end
// read data from rx_fifo
always @(posedge clk) begin // 只要非空就从fifo里面读数据
if(~fifo_rx_empty) begin
fifo_rx_rd_en <= 1;
end
else if(fifo_rx_empty) begin
fifo_rx_rd_en <= 0;
end
end
initial begin
reset <= 1;
tx_rx_mode <= 1;
#500;
reset = 0;
en = 1;
uart_tx_inst_en <= 1;
#1000000000;
end
// verify the uart_tx and fifo_uart_tx_module
// write data to the tx_fifo
// reg [11:0] counter = 1000;
// always @(posedge clk) begin
// if (~fifo_tx_full && fifo_tx_wr_en) begin
// data_tx_fifo <= {data_tx_fifo[6:0],data_tx_fifo[7]};
// end
// end
// initial begin
// reset = 1;
// #200;
// reset = 0;
// wait(~fifo_tx_wr_rst_busy) begin
// #1000;
// fifo_tx_wr_en = 1;
// en = 1;
// end
// #1000000000;
// end
endmodule
uart_tx_tb
`timescale 1ns / 1ps
module uart_tx_tb(
);
reg clk = 0;
always #10 clk = ~clk;
wire tx;
reg reset = 1;
reg [7:0] data = 8'b00001111;
reg en = 0;
wire tx_done;
uart_tx
#(
.baud_rate(115200)
)uart_tx_inst
(
.clk(clk),
.tx(tx),
.reset(reset),
.data(data),
.en(en),
.tx_done(tx_done)
);
event tx_finish,tx_start;
always @(posedge tx_done) begin
->tx_finish;
end
always @(negedge tx_done) begin
->tx_start;
end
always @(tx_finish) begin
data = {data[6:0],data[7]};
en <= 1;
end
always @(tx_start) begin
en <= 0;
end
initial begin
reset = 1;
#1000;
reset = 0;
en = 1;
#100000000;
end
endmodule
uart_rx_tb
`timescale 1ns / 1ps
module uart_rx_tb(
);
reg clk = 0;
always #10 clk = ~clk;
reg reset = 1;
wire rx;
wire[7:0] data_rx;
wire rx_done;
wire rx_valid;
uart_rx
#(
.clk_freq(50000000),
.baud_rate(115200)
) uart_rx_inst(
.clk(clk),
.reset(reset),
.rx(rx),
.data(data_rx),
.rx_done(rx_done),
.rx_valid(rx_valid)
);
reg [7:0] data_tx = 8'b10100011;
reg tx_en = 0;
wire tx_done;
uart_tx
#(
.baud_rate(115200)
)uart_tx_inst
(
.clk(clk),
.tx(rx),
.reset(reset),
.data(data_tx),
.en(tx_en),
.tx_done(tx_done)
);
event tx_start;
event tx_finish;
always @(posedge tx_done) begin
-> tx_finish;
end
always @(negedge tx_done) begin
-> tx_start;
end
always @(tx_finish) begin
data_tx <= {data_tx[6:0],data_tx[7]};
tx_en <= 1;
end
always @(tx_start) begin
tx_en <= 0;
end
initial begin
reset <= 1;
#1000;
reset <= 0;
tx_en <= 0;
#1000000000;
end
endmodule
板上验证
reset后
发送512字节数据直至rx_fifo满
按下mode_trans进入tx状态,同时rx_fifo被读出并写入tx_fifo