串口:
串口在嵌入式设备里经常会用到,串口主要包括RS232C串口,RS485串口等,他们只是电平不一样。
RS232也称标准串口,是一种比较常用的串口,采用标准的DB9接口,RS232采用的是负逻辑电平,即-12~-5V表示逻辑1,+5~+12V表示逻辑0。
一般的MCU或者FPGA的管脚只能输出TTL电平,所以一般需要采用想MAX232等电平转换芯片将UART的TTL电平转换成RS232电平。
UART:
UART是一种通用串行数据总线,用于异步通信。该总线双向通信,可以实现全双工传输和接收。在嵌入式设计中,UART用于主机与辅助设备通信。
UART传输协议:
UART一般具有11个bit位,包括一个起始位,8个数据位,一个校验位,一个停止位,校验位可以省掉。

verilog代码实现:
现以将FPGA接收到上位机的数据然后再将这些数据发送回上位机显示为例,整个串口部分可以分别分为接收和发送模块。
首先接收模块:

接收模块主要包含时钟复位输入,以及8个串行数据输入,以及将接收到的串行数据转换成并行数据输出,并输出一个接收完成标志位。
同时在接收模块内产生串口接收所需要的波特率。
接收模块代码如下:
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 21:57:52 02/21/2020
// Design Name:
// Module Name: uart_rx
// Project Name:
// Target Devices:
// Tool versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module uart_rx
# (
parameter clk_ref = 50, //参考时钟50M
parameter baud_bps = 115200
)
(
input clk_50,
input Rst_n,
input rx_data,
output reg [7:0] uart_rx,
output reg rx_done
);
localparam bps_cnt = clk_ref*1000000/baud_bps;
reg data_temp0;
reg data_temp1;
wire start_flag;
reg rx_flag;
reg [15:0] baud_cnt;
reg [3:0] bit_cnt;
reg [7:0] rx_reg;
//======================================================
//
//检测RX接收信号的起始位信号,RX信号由高电平变化为低电平
//
//======================================================
always @(posedge clk_50 or negedge Rst_n)
begin
if(!Rst_n)
begin
data_temp0 <= 1'b0;
data_temp1 <= 1'b0;
end
else
begin
data_temp0 <= rx_data;
data_temp1 <= data_temp0;
end
end
assign start_flag = data_temp1 & (~data_temp0);
//=======================================================
//
//数据接收标志位信号___________
// | |
//=======================================================
always @(posedge clk_50 or negedge Rst_n)
begin
if(!Rst_n)
rx_flag<=0;
else
begin
if(start_flag)
rx_flag<=1;
else if((bit_cnt==4'd9)&(baud_cnt==bps_cnt/2))
rx_flag<=0;
else
rx_flag<=rx_flag;
end
end
//=====================================================
//
//波特率产生
//
//=====================================================
always @(posedge clk_50 or negedge Rst_n)
begin
if(!Rst_n)
baud_cnt<=16'd0;
else if(rx_flag)
begin
if(baud_cnt==(bps_cnt-1))
baud_cnt<=16'd0;
else
baud_cnt<=baud_cnt+1'b1;
end
else
baud_cnt<=16'd0;
end
//=======================================================
//
//数据接收位数计数
//
//=======================================================
always @(posedge clk_50 or negedge Rst_n)
begin
if(!Rst_n)
bit_cnt<=4'd0;
else if(rx_flag)
begin
if(baud_cnt==(bps_cnt-1))
bit_cnt<=bit_cnt+1'b1;
else
bit_cnt<=bit_cnt;
end
else
bit_cnt<=4'd0;
end
//=======================================================
//
//数据接收块,将RX信号线上的信号接收并存在一个8位接收寄存器里
//
//=======================================================
always @(posedge clk_50 or negedge Rst_n)
begin
if(!Rst_n)
rx_reg<=8'd0;
else if(rx_flag)
begin
if(baud_cnt==bps_cnt/2)
begin
case(bit_cnt)
4'd1:rx_reg[0]<=rx_data;
4'd2:rx_reg[1]<=rx_data;
4'd3:rx_reg[2]<=rx_data;
4'd4:rx_reg[3]<=rx_data;
4'd5:rx_reg[4]<=rx_data;
4'd6:rx_reg[5]<=rx_data;
4'd7:rx_reg[6]<=rx_data;
4'd8:rx_reg[7]<=rx_data;
default:;
endcase
end
else
rx_reg<=rx_reg;
end
else
rx_reg<=8'd0;
end
//=======================================================
//
//将数据接收寄存器里的数据发送到输出寄存器
//
//=======================================================
always @(posedge clk_50 or negedge Rst_n)
begin
if(!Rst_n)
begin
rx_done<=1'b0;
uart_rx<=8'd0;
end
else if(bit_cnt==9)
begin
uart_rx<=rx_reg;
rx_done<=1'b1;
end
else
begin
uart_rx<=8'd0;
rx_done<=1'b0;
end
end
endmodule
发送模块:

发送模块主要包括系统时钟和复位输入,准备需要发送的并行数据输入,发送使能输入,以及串行数据输出。同时发送模块产生数据发送的波特率。
发送模块检测到发送使能信号以后开始发送,也就是检测到TX_EN的上升沿开始启动发送。
串口发送部分Verilog代码如下:
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 14:16:03 02/22/2020
// Design Name:
// Module Name: uart_tx
// Project Name:
// Target Devices:
// Tool versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module uart_tx
#(
parameter clk_ref = 50, //参考时钟50M
parameter baud_bps = 115200//波特率
)
(
input clk_50,
input Rst_n,
input [7:0] uart_tx,
input tx_en,
output reg tx_data
);
localparam bps_cnt = clk_ref*1000000/baud_bps;
reg en_temp0;
reg en_temp1;
wire start_flag;
reg tx_flag;
reg [15:0] baud_cnt;
reg [3:0] bit_cnt;
//======================================================
//
//数据发送使能信号检测,检测到使能信号后开始发送标志位置1
//这里的使能信号使用接收模块里的接收完成标志信号
//
//======================================================
always @(posedge clk_50 or negedge Rst_n)
begin
if(!Rst_n)
begin
en_temp0 <= 1'b0;
en_temp1 <= 1'b0;
end
else
begin
en_temp0 <= tx_en;
en_temp1 <= en_temp0;
end
end
assign start_flag = (~en_temp1) & en_temp0;
//=======================================================
//
//数据发送标志位信号___________
// | |
//=======================================================
always @(posedge clk_50 or negedge Rst_n)
begin
if(!Rst_n)
tx_flag<=0;
else
begin
if(start_flag)
tx_flag<=1;
else if((bit_cnt==4'd9)&(baud_cnt==bps_cnt/2))
tx_flag<=0;
else
tx_flag<=tx_flag;
end
end
//=====================================================
//
//波特率产生
//
//=====================================================
always @(posedge clk_50 or negedge Rst_n)
begin
if(!Rst_n)
baud_cnt<=16'd0;
else if(tx_flag)
begin
if(baud_cnt==(bps_cnt-1))
baud_cnt<=16'd0;
else
baud_cnt<=baud_cnt+1'b1;
end
else
baud_cnt<=16'd0;
end
//=======================================================
//
//数据发送位数计数
//
//=======================================================
always @(posedge clk_50 or negedge Rst_n)
begin
if(!Rst_n)
bit_cnt<=4'd0;
else if(tx_flag)
begin
if(baud_cnt==(bps_cnt-1))
bit_cnt<=bit_cnt+1'b1;
else
bit_cnt<=bit_cnt;
end
else
bit_cnt<=4'd0;
end
//=======================================================
//
//数据发送块,将需要发送的8位并行数据一位一位的发送到输出
//
//=======================================================
always @(posedge clk_50 or negedge Rst_n)
begin
if(!Rst_n)
tx_data<=1'b1;
else if(tx_flag)
begin
case(bit_cnt)
4'd0:tx_data<=1'b0;
4'd1:tx_data<=uart_tx[0];
4'd2:tx_data<=uart_tx[1];
4'd3:tx_data<=uart_tx[2];
4'd4:tx_data<=uart_tx[3];
4'd5:tx_data<=uart_tx[4];
4'd6:tx_data<=uart_tx[5];
4'd7:tx_data<=uart_tx[6];
4'd8:tx_data<=uart_tx[7];
4'd9:tx_data<=1'b1;
default:;
endcase
end
else
tx_data<=1'b1;
end
endmodule
如果需要实现将接收的数据发送,则在顶层模块里进行连接,将接收模块的输出信号连接到发送模块的输入,同时将接收模块的接收完成标志信号作为发送模块的使能信号。
代码如下:
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 21:54:01 02/21/2020
// Design Name:
// Module Name: uart_top
// Project Name:
// Target Devices:
// Tool versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module uart_top
(
input clk_50,
input Rst_n,
input rx_data,
output tx_data
);
wire [7:0] uart_rx;
wire rx_done;
wire tx_en;
uart_rx
#(
50,
115200
)
(
.clk_50(clk_50),
.Rst_n(Rst_n),
.rx_data(rx_data),
.uart_rx(uart_rx),
.rx_done(rx_done)
);
uart_tx
#(
50,
115200
)
(
.clk_50(clk_50),
.Rst_n(Rst_n),
.uart_tx(uart_tx),
.tx_en(rx_done),
.tx_data(tx_data)
);
endmodule
发送模块的仿真文件:
`timescale 1ns / 1ps
////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 15:18:38 02/22/2020
// Design Name: uart_tx
// Module Name: C:/mydesign/uart/uart_txtb.v
// Project Name: uart
// Target Device:
// Tool versions:
// Description:
//
// Verilog Test Fixture created by ISE for module: uart_tx
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
////////////////////////////////////////////////////////////////////////////////
module uart_txtb;
// Inputs
reg clk_50;
reg Rst_n;
reg [7:0] uart_tx;
reg tx_en;
// Outputs
wire tx_data;
// Instantiate the Unit Under Test (UUT)
uart_tx uut
(
.clk_50(clk_50),
.Rst_n(Rst_n),
.uart_tx(uart_tx),
.tx_en(tx_en),
.tx_data(tx_data)
);
initial begin
// Initialize Inputs
clk_50 = 0;
Rst_n = 0;
uart_tx = 0;
tx_en = 0;
// Wait 100 ns for global reset to finish
#10;
Rst_n = 1;
#10;
tx_en = 1;
#10;
end
always #10 clk_50=~clk_50;
always @(posedge clk_50)
begin
uart_tx<=8'b11001011;
end
// Add stimulus here
endmodule
仿真波形:

来源:https://www.cnblogs.com/xiaozhu5208/p/12371687.html