FPGA串口的Verilog编码
目标
- 串口接收数据缓存到FIFO后重新发送回去,内部模块包括:
- 串口发送模块
- 串口接收模块
- FIFO缓存模块
- top模块
- 串口接收、发送模块的编码极为简单,通过计时器直接指定每一个比特位的操作时间;
代码
串口发送
`timescale 1ns / 1ps
module uart_tx
#(
parameter CLK_FREQ = 50, //输入时钟频率,MHz
parameter BAUD_RATE = 9600 //波特率,bps
)
(
input clk ,
input rst_n ,
//input
input tx_trig ,//串口发送触发,上升沿有效
input [7:0] tx_data ,//串口发送数据
//output
output reg tx_idle ,//空闲态,高电平有效
output reg tx_txd //txd发送端
);
localparam BIT = CLK_FREQ * 1000_000 / BAUD_RATE;
localparam BIT0 = BIT * 1 - 1;
localparam BIT1 = BIT * 2 - 1;
localparam BIT2 = BIT * 3 - 1;
localparam BIT3 = BIT * 4 - 1;
localparam BIT4 = BIT * 5 - 1;
localparam BIT5 = BIT * 6 - 1;
localparam BIT6 = BIT * 7 - 1;
localparam BIT7 = BIT * 8 - 1;
localparam STOP = BIT * 9 - 1;
localparam FINISH = BIT * 10 - 1;
reg tx_trig_r;
reg [31:0] count;
reg [7:0] data_in;
wire start_flag;
//start_flag,串口发送启动标志
assign start_flag = tx_trig & ~tx_trig_r & tx_idle;//tx_trig上升沿到来且串口空闲
//tx_trig_r,串口发送触发打节拍
always @(posedge clk) begin
tx_trig_r = tx_trig;
end
//data_in
always @(posedge clk or negedge rst_n) begin
if(~rst_n)
data_in <= 8'h00;
else if(start_flag)//串口发送启动
data_in <= tx_data;
end
//tx_idle
always @(posedge clk or negedge rst_n) begin
if(~rst_n)
tx_idle <= 1'b1;
else if(start_flag)//串口发送启动
tx_idle <= 1'b0;
else if(count == FINISH)
tx_idle <= 1'b1;
end
//count
always @(posedge clk or negedge rst_n) begin
if(~rst_n)
count <= 'd0;
else if(~tx_idle)
count <= count + 'd1;
else
count <= 'd0;
end
//tx_txd
always @(posedge clk or negedge rst_n) begin
if(~rst_n)
tx_txd <= 1'b1;
else if(start_flag)//串口发送启动
tx_txd <= 1'b0;
else if(count == BIT0)
tx_txd <= data_in[0];
else if(count == BIT1)
tx_txd <= data_in[1];
else if(count == BIT2)
tx_txd <= data_in[2];
else if(count == BIT3)
tx_txd <= data_in[3];
else if(count == BIT4)
tx_txd <= data_in[4];
else if(count == BIT5)
tx_txd <= data_in[5];
else if(count == BIT6)
tx_txd <= data_in[6];
else if(count == BIT7)
tx_txd <= data_in[7];
else if(count == STOP)
tx_txd <= 1'b1;
else
tx_txd <= tx_txd;
end
endmodule
串口接收
`timescale 1ns/1ps
module uart_rx
#(
parameter CLK_FREQ = 50 ,//输入时钟频率,MHz
parameter BAUD_RATE = 9600 //波特率,bps
)
(
input clk ,
input rst_n ,
//---
input rx_rxd ,//接收端口
output reg [7:0] rx_data ,//接收到的1字节数据
output reg rx_flag //1字节数据接收完成标志
);
localparam BIT = CLK_FREQ * 1000_000 / BAUD_RATE;
localparam HBIT = BIT / 2;
localparam BIT0 = BIT * 1 + HBIT - 1;
localparam BIT1 = BIT * 2 + HBIT - 1;
localparam BIT2 = BIT * 3 + HBIT - 1;
localparam BIT3 = BIT * 4 + HBIT - 1;
localparam BIT4 = BIT * 5 + HBIT - 1;
localparam BIT5 = BIT * 6 + HBIT - 1;
localparam BIT6 = BIT * 7 + HBIT - 1;
localparam BIT7 = BIT * 8 + HBIT - 1;
localparam FINISH = BIT * 9 + HBIT - 1;
reg rx_rxd_r;
reg rx_rxd_rr;
wire start_flag;
reg rx_busy;
reg [31:0] count;
//rx_rxd_r, rx_rxd_rr, 接收端口打节拍
always @(posedge clk) begin
rx_rxd_r <= rx_rxd;
rx_rxd_rr <= rx_rxd_r;
end
//start_flag, 串口接收开始标志
assign start_flag = (rx_rxd_rr & ~rx_rxd_r) & ~rx_busy;//rx_rxd下降沿且串口接收不忙碌
//rx_busy, 接收忙碌态
always @(posedge clk or negedge rst_n) begin
if(~rst_n)
rx_busy <= 1'b0;
else if(start_flag)
rx_busy <= 1'b1;
else if(count == FINISH)
rx_busy <= 1'b0;
end
//count,计数器
always @(posedge clk or negedge rst_n) begin
if(~rst_n)
count <= 'd0;
else if(rx_busy)
count <= count + 'd1;
else
count <= 'd0;
end
//rx_flag
always @(posedge clk or negedge rst_n) begin
if(~rst_n)
rx_flag <= 1'b0;
else if(count == FINISH)
rx_flag <= 1'b1;
else
rx_flag <= 1'b0;
end
//rx_data
always @(posedge clk or negedge rst_n) begin
if(~rst_n)
rx_data <= 8'h00;
else if(count == BIT0)
rx_data[0] <= rx_rxd_r;
else if(count == BIT1)
rx_data[1] <= rx_rxd_r;
else if(count == BIT2)
rx_data[2] <= rx_rxd_r;
else if(count == BIT3)
rx_data[3] <= rx_rxd_r;
else if(count == BIT4)
rx_data[4] <= rx_rxd_r;
else if(count == BIT5)
rx_data[5] <= rx_rxd_r;
else if(count == BIT6)
rx_data[6] <= rx_rxd_r;
else if(count == BIT7)
rx_data[7] <= rx_rxd_r;
else
rx_data <= rx_data;
end
endmodule
FIFO模块
module fifo
#
(
parameter POINTER_N = 7 //读写指针位宽,FIFO容量 = 2^POINTER_N,默认容量为2^7=128字节
)
(
//系统信号
input clk50M ,
input rst_n ,
//FIFO信号
input wr_en ,//写使能,高电平有效
input [7:0] buf_in ,//写数据
input rd_en ,//读使能,高电平有效
output reg [7:0] buf_out ,//读数据
//FIFO状态输出
output buf_empty ,//FIFO空
output buf_full ,//FIFO满
output reg [7:0] fifo_cnt //FIFO已使用字节数
);
//参数设置(修改FIFO容量时以下两个参数必须同时修改)
localparam BUF_NUM = 1 << POINTER_N;//FIFO容量=2^POINTER_N,默认128字节
//公共变量
reg [POINTER_N - 1 : 0] rd_prt; //读指针,位宽必须与BUF_NUM完全匹配
reg [POINTER_N - 1 : 0] wr_prt; //写指针,位宽必须与BUF_NUM完全匹配
reg [7:0] buf_mem [0 : BUF_NUM - 1]; //8位宽,BUF_NUM容量
//buf_empty
assign buf_empty = (fifo_cnt == 'd0) ? 'b1 : 'b0;
//buf_full
assign buf_full = (fifo_cnt == BUF_NUM) ? 'b1 : 'b0;
//fifo_cnt
always @(posedge clk50M or negedge rst_n) begin
if(~rst_n)
fifo_cnt <= 'd0;
else if(wr_en & rd_en) //Simultaneously read and write
fifo_cnt <= fifo_cnt;
else if(wr_en & ~buf_full) // Write
fifo_cnt <= fifo_cnt + 'd1;
else if(rd_en & ~buf_empty) // Read
fifo_cnt <= fifo_cnt - 'd1;
else
fifo_cnt <= fifo_cnt;
end
//buf_out, Read data operation
always @(posedge clk50M or negedge rst_n) begin
if(~rst_n)
buf_out <= 'h00;
else if(rd_en & ~buf_empty)
buf_out <= buf_mem[rd_prt];
end
//buf_mem, Write data operation
always @(posedge clk50M) begin
if(wr_en & ~buf_full)
buf_mem[wr_prt] <= buf_in;
end
//rd_prt
always @(posedge clk50M or negedge rst_n) begin
if(~rst_n)
rd_prt <= 0;
else if(rd_en & ~buf_empty)
rd_prt <= rd_prt + 1;
end
//wr_prt
always @(posedge clk50M or negedge rst_n) begin
if(~rst_n)
wr_prt <= 0;
else if(wr_en & ~buf_full)
wr_prt <= wr_prt + 1;
end
endmodule
TOP
`timescale 1ns / 1ps
module top(
input sys_clk50M ,
input sys_rst_n ,
input uart_rxd ,
output uart_txd
);
localparam UART_CLK_FREQ = 50 ;//串口时钟频率, MHz
localparam UART_BAUD_RATE = 460800 ;//串口波特率, bps
wire [7:0] rx_data;
wire rx_flag;
wire buf_empty;
reg fifo_rd;
wire [7:0] fifo_out;
reg [3:0] state;
reg tx_trig;
wire tx_idle;
//state, fifo_rd, tx_trig
always @(posedge sys_clk50M or negedge sys_rst_n) begin
if(~sys_rst_n) begin
state <= 0;
fifo_rd <= 1'b0;
tx_trig <= 1'b0;
end
else
case(state)
0://读FIFO数据
begin
tx_trig <= 1'b0;
if(~buf_empty) begin//等待串口接收到数据并存入FIFO
fifo_rd <= 1'b1;//读出FIFO数据
state <= 1;
end
end
1://触发串口发送
begin
fifo_rd <= 1'b0;
if(tx_idle) begin//等待串口发送端空闲
tx_trig <= 1'b1;
state <= 0;
end
end
endcase
end
//FIFO先入先出缓存实例
fifo
#
(
.POINTER_N (1 )//读写指针位宽,FIFO容量 = 2^POINTER_N,默认容量为2^7=128字节
)
fifo_inst
(
//系统信号
.clk50M (sys_clk50M ),
.rst_n (sys_rst_n ),
//FIFO信号
.wr_en (rx_flag ),//写使能,高电平有效
.buf_in (rx_data ),//写数据
.rd_en (fifo_rd ),//读使能,高电平有效
.buf_out (fifo_out ),//读数据
//FIFO状态输出
.buf_empty (buf_empty ),//FIFO空
.buf_full (),//FIFO满
.fifo_cnt () //FIFO已使用字节数
);
//串口接收模块实例
uart_rx
#(
.CLK_FREQ (UART_CLK_FREQ ),//输入时钟频率,MHz
.BAUD_RATE (UART_BAUD_RATE ) //波特率,bps
)
uart_rx_inst
(
.clk (sys_clk50M ),
.rst_n (sys_rst_n ),
//---
.rx_rxd (uart_rxd ),//接收端口
.rx_data (rx_data ),//接收到的1字节数据
.rx_flag (rx_flag ) //1字节数据接收完成标志
);
//串口发送模块实例
uart_tx
#(
.CLK_FREQ (UART_CLK_FREQ ),//输入时钟频率,MHz
.BAUD_RATE (UART_BAUD_RATE ) //波特率,bps
)
uart_tx_inst
(
.clk (sys_clk50M ),
.rst_n (sys_rst_n ),
//input
.tx_trig (tx_trig ),//串口发送触发,上升沿有效
.tx_data (fifo_out ),//串口发送数据
//output
.tx_idle (tx_idle ),//空闲态,高电平有效
.tx_txd (uart_txd ) //txd发送端
);
endmodule