基于不同收发时钟频率的UART实现

1、目的:

(1)熟练应用PLL来产生不同的时钟频率

(2)熟练应用FIFO对多比特数据进行跨时钟域(Clock Domain Crossing,CDC)的进行处理

(3)熟练应用同步器对单比特数据进行跨时钟域的进行处理

(3)加深对UART的理解和使用

2、特点:

(1)接收时钟频率为100m,发送时钟频率为50m(不然理论上不需要FIFO做缓冲,但其实作用也不大,因为比特率是固定的)

(2)使用FIFO做数据缓冲和跨时钟域处理

(3)PLL采用Xilinx的Clocking Wizard IP核,FIFO采用Xilinx的FIFO Generator IP核

3、常见问题:

(1)为什么跨时钟域需要进行同步处理?

在数字电路设计中,一个时钟域(clock domain)是由一个时钟信号控制的一组逻辑元件的集合。时钟信号的上升沿或下降沿触发这些元件的操作。在复杂的系统中,可能存在多个时钟域,这些时钟域可能由不同的时钟信号驱动。

以下是跨时钟域同步处理的主要原因:

  1. 时序不同步: 不同时钟域的时钟信号可能由于时钟源的不同、时钟树路径的不同等原因导致时序存在差异。如果直接将信号从一个时钟域传递到另一个时钟域,可能会发生时序不同步,导致数据的采样错误。

  2. 元件操作时钟: 不同时钟域的元件可能在不同的时钟信号的上升沿或下降沿触发。如果信号跨越时钟域,可能导致接收端在错误的时钟边沿进行采样,造成数据错误。

  3. 时钟域边界: 在时钟域之间进行信号传递时,时钟域的边界可能存在不同的延迟,这可能导致数据不同步。

(2)为什么要进行延迟打拍?

在数字电路设计中,延迟打拍(Delay Insertion)是指向电路中有意地引入延迟元素或延迟锁存器的过程,以满足设计中的时序要求、解决时序相关问题,或者实现特定的功能。

以下是一些延迟打拍的常见原因:

  1. 时序要求满足: 在一些高性能数字电路中,需要确保电路在时序方面满足特定的要求。通过在关键路径上插入延迟元素,可以调整信号传播速度,使得整个电路的时序性能得到改善。

  2. 解决时序相关问题: 在时序相关的设计中,可能会出现由于不同逻辑路径的信号传播速度不同而导致的时序问题,如时序违规或数据不稳定。通过延迟打拍,可以调整信号的到达时间,以解决这些时序相关的问题。

  3. 时钟域对齐: 在异步时钟域之间进行信号传递时,为了避免时序不同步的问题,可能需要在信号路径上引入延迟元素,以确保在接收端的时钟边沿上进行合适的采样。

  4. 时序兼容性: 在某些情况下,不同的电路或模块可能有不同的时序性能。通过引入延迟元素,可以使得这些模块在整体上具有更好的时序兼容性。

4、电路结构:

(1)顶层模块uart

延迟计数使能模块

延迟计数同步模块(不然不能正常接收start_cnt)

延迟计数和uart_rx_done_d0信号模块

延迟打拍,消除亚稳态,确保数据正确传输

module uart(

	input				clk,
	input				rst_n,
	input				uart_rxd,

	output				uart_txd


);

wire			uart_rx_done	;
wire	[7:0]	uart_rx_data    ;
wire	[7:0]	uart_tx_data    ;
wire			full			;
wire			empty			;
wire			locked			;
wire			clk_100m		;
wire			clk_50m			;

reg		[4:0]	delay_cnt		;
reg				uart_rx_done_d0	;
reg				uart_rx_done_d1	;
reg				uart_rx_done_d2	;
reg				start_cnt		;
reg				start_cnt_d0	;
reg				start_cnt_d1	;

//延迟计数使能模块
always@(posedge clk_100m or negedge rst_n)
	if(rst_n == 1'b0)
		start_cnt <= 1'b0;
	else
		if(delay_cnt == 5'd20)
			start_cnt <= 1'b0;
		else if(uart_rx_done)
			start_cnt <= 1'b1;
		else
			start_cnt <= start_cnt;

//延迟计数同步模块,不然不能正常接收start_cnt
always@(posedge clk_50m or negedge rst_n)
	if(rst_n == 1'b0) begin
		start_cnt_d0 <= 1'b0;
		start_cnt_d1 <= 1'b0;
	end
	else begin
		start_cnt_d0 <= start_cnt;
		start_cnt_d1 <= start_cnt_d0;
	end

//延迟计数和uart_rx_done_d0信号模块
always@(posedge clk_50m or negedge rst_n)
	if(rst_n == 1'b0) begin
		delay_cnt <= 5'd0;
		uart_rx_done_d0 <= 1'b0;
	end
	else
		if(start_cnt_d1)
			if(delay_cnt == 5'd20) begin
				delay_cnt <= 5'd0;
				uart_rx_done_d0 <= 1'b1;
			end
			else begin
				delay_cnt <= delay_cnt + 1'b1;
				uart_rx_done_d0 <= 1'b0;
			end
		else begin
			delay_cnt <= 1'b0;
			uart_rx_done_d0 <= 1'b0;
		end

//延迟打拍,消除亚稳态,确保数据正确传输
always@(posedge clk_50m or negedge rst_n)
	if(rst_n == 1'b0)begin
		uart_rx_done_d1	<= 1'b0;
		uart_rx_done_d2	<= 1'b0;
	end
	else begin
		uart_rx_done_d1	<= uart_rx_done_d0;
		uart_rx_done_d2	<= uart_rx_done_d1;
	end

  clk_wiz_0 u_clk_wiz_0
   (
    // Clock out ports
    .clk_100m(clk_100m),     // output clk_100m
    .clk_50m(clk_50m),     // output clk_50m
    // Status and control signals
    .locked(locked),       // output locked
   // Clock in ports
    .clk_in1(clk)      // input clk_in1
);
		
fifo_generator_0 u_fifo_generator_0 (
  .rst(~rst_n),        // input wire rst
  .wr_clk(clk_100m),  // input wire wr_clk
  .rd_clk(clk_50m),  // input wire rd_clk
  .din(uart_rx_data),        // input wire [7 : 0] din
  .wr_en(uart_rx_done&&(~full)),    // input wire wr_en
  .rd_en(uart_rx_done_d1&&(~empty)),    // input wire rd_en
  .dout(uart_tx_data),      // output wire [7 : 0] dout
  .full(full),      // output wire full
  .empty(empty)    // output wire empty
);

uart_rx u_uart_rx(

	.clk			(clk_100m),
	.rst_n			(rst_n),

	.uart_rxd		(uart_rxd),
	.uart_rx_done	(uart_rx_done),
	.uart_rx_data	(uart_rx_data)

);

uart_tx u_uart_tx(

	.clk			(clk_50m),
	.rst_n			(rst_n),
	.uart_tx_en		(uart_rx_done_d2),
	.uart_tx_data	(uart_tx_data),
	
	.uart_tx_busy	(),
	.uart_txd       (uart_txd)
);

endmodule

(2)串口接收模块uart_rx

捕获接收端口下降沿(起始位),得到一个时钟周期的脉冲信号

多级同步模块

rx_flag信号模块

波特率计数模块

接收数据计数模块

接收数据模块

接收完成模块

module uart_rx(

	input				clk			,
	input				rst_n		,
	
	input				uart_rxd	,
	output	reg			uart_rx_done,
	output	reg	[7:0]	uart_rx_data

);

parameter	CLK_FREQ = 100000000;				//时钟频率
parameter	UART_BPS = 115200;					//串口波特率
localparam	BAUD_CNT_MAX = CLK_FREQ/UART_BPS;	//为得到指定波特率,对系统时钟计数 BPS_CNT 次,相当于分频,由高频到低频

reg			uart_rxd_d0;
reg			uart_rxd_d1;
reg			uart_rxd_d2;
reg			rx_flag;		//接受标志寄存器
reg	[3:0]	rx_data_cnt;	//接收数据计数器
reg	[15:0]	baud_cnt;		//波特率计数器
reg	[7:0]	rx_data_t;		//接收数据寄存器

wire		start_en;		//开始标志位

//捕获接收端口下降沿(起始位),得到一个时钟周期的脉冲信号
assign start_en = uart_rxd_d2 && (~uart_rxd_d1) && (~rx_flag);

//多级同步模块,消除亚稳态
always@(posedge clk or negedge rst_n)
	if(rst_n == 1'b0)begin
		uart_rxd_d0 <= 1'b1;
        uart_rxd_d1 <= 1'b1;
        uart_rxd_d2 <= 1'b1;
	end
	else begin
		uart_rxd_d0 <= uart_rxd;
        uart_rxd_d1 <= uart_rxd_d0;
        uart_rxd_d2 <= uart_rxd_d1;
	end

//rx_flag信号模块
always@(posedge clk or negedge rst_n)
	if(rst_n == 1'b0)
		rx_flag <= 1'b0;
	else
		if(start_en)
			rx_flag <= 1'b1;
		else if((uart_rxd_d1 == 1'b1) && (rx_data_cnt >= 4'd9) && (baud_cnt == BAUD_CNT_MAX/2 - 1'b1))
			rx_flag <= 1'b0;
		else
			rx_flag <= rx_flag;

//波特率计数模块
always@(posedge clk or negedge rst_n)
	if(rst_n == 1'b0)
		baud_cnt <= 'b0;
	else
		if(rx_flag)
			if(baud_cnt < BAUD_CNT_MAX - 1'b1)
				baud_cnt <= baud_cnt + 16'b1;
			else
				baud_cnt <= 'b0;
		else
			baud_cnt <= 'b0;

//接收数据计数模块
always@(posedge clk or negedge rst_n)
	if(rst_n == 1'b0)
		rx_data_cnt <= 'b0;
	else
		if(rx_flag)
			if(baud_cnt == BAUD_CNT_MAX - 1'b1)
				rx_data_cnt <= rx_data_cnt + 1'b1;
			else
				rx_data_cnt <= rx_data_cnt;
		else
			rx_data_cnt <= 'b0;
	
//接收数据模块
always@(posedge clk or negedge rst_n)
	if(rst_n == 1'b0)
		rx_data_t <= 'b0;
	else begin
		if(rx_flag)
			if(baud_cnt == BAUD_CNT_MAX/2 - 1'b1)
				case(rx_data_cnt)
					4'd1 : rx_data_t[0] <= uart_rxd_d1;
					4'd2 : rx_data_t[1] <= uart_rxd_d1;
					4'd3 : rx_data_t[2] <= uart_rxd_d1;
					4'd4 : rx_data_t[3] <= uart_rxd_d1;
					4'd5 : rx_data_t[4] <= uart_rxd_d1;
					4'd6 : rx_data_t[5] <= uart_rxd_d1;
					4'd7 : rx_data_t[6] <= uart_rxd_d1;
					4'd8 : rx_data_t[7] <= uart_rxd_d1;
					default:rx_data_t <= rx_data_t;
				endcase
			else
				rx_data_t <= rx_data_t;
		else
			rx_data_t <= 'b0;
	end			
				
//接收完成模块
always@(posedge clk or negedge rst_n)
	if(rst_n == 1'b0) begin
		uart_rx_done <= 1'b0;
		uart_rx_data <= 'b0;
	end
	else
		if((uart_rxd_d1 == 1'b1) && (rx_data_cnt >= 4'd9) && (baud_cnt == BAUD_CNT_MAX/2 - 1'b1)) begin
			uart_rx_done <= 1'b1;
			uart_rx_data <= rx_data_t;
		end
		else begin
			uart_rx_done <= 1'b0;
			uart_rx_data <= uart_rx_data;
		end
				
				
				
endmodule

(3)串口发送模块uart_tx

uart_tx_busy和tx_data_t信号,开始发送标志模块

波特率计数模块

发送数据计数模块

发送数据模块

module uart_tx(

	input				clk,
	input				rst_n,
	input				uart_tx_en,
	input	[7:0]		uart_tx_data,
	
	output	reg			uart_tx_busy,
	output	reg			uart_txd

);

parameter	CLK_FREQ = 50000000;
parameter	UART_BPS = 115200;
localparam	BAUD_CNT_MAX = CLK_FREQ/UART_BPS;

reg	[7:0] 	tx_data_t;
reg [15:0]	baud_cnt;
reg [3:0]	tx_cnt;

//uart_tx_busy和tx_data_t信号,开始发送标志模块
always@(posedge clk or negedge rst_n)
	if(rst_n == 1'b0) begin
		uart_tx_busy <= 1'b0;
		tx_data_t <= 8'b0;	
	end
	else
		if(uart_tx_en) begin
			uart_tx_busy <= 1'b1;
			tx_data_t <= uart_tx_data;	
		end
		else if(tx_cnt == 4'd9 && baud_cnt >= BAUD_CNT_MAX - BAUD_CNT_MAX/16) begin
			uart_tx_busy <= 1'b0;
			tx_data_t <= 8'b0;	
		end		
		else begin
			uart_tx_busy <= uart_tx_busy;
			tx_data_t <= tx_data_t;	
		end

//波特率计数模块
always@(posedge clk or negedge rst_n)
	if(rst_n == 1'b0)
		baud_cnt <= 16'b0;
	else
		if(uart_tx_busy)
			if(baud_cnt < BAUD_CNT_MAX - 1'b1)
				baud_cnt <= baud_cnt + 1'b1;
			else
				baud_cnt <= 16'b0;
		else
			baud_cnt <= 16'b0;

//发送数据计数模块
always@(posedge clk or negedge rst_n)
	if(rst_n == 1'b0)
		tx_cnt <= 16'b0;
	else
		if(uart_tx_busy)
			if(baud_cnt == BAUD_CNT_MAX - 1'b1)
				tx_cnt <= tx_cnt + 1'b1;
			else
				tx_cnt <= tx_cnt;
		else
			tx_cnt <= 16'b0;

//发送数据模块
always@(posedge clk or negedge rst_n)
	if(rst_n == 1'b0)
		uart_txd <= 1'b1;
	else
		if(uart_tx_busy)
			case(tx_cnt)
				4'd0 : uart_txd <= 1'b0;	//起始位
				4'd1 : uart_txd <= tx_data_t[0];	//数据1
				4'd2 : uart_txd <= tx_data_t[1];	//数据2
				4'd3 : uart_txd <= tx_data_t[2];	//数据3
				4'd4 : uart_txd <= tx_data_t[3];	//数据4
				4'd5 : uart_txd <= tx_data_t[4];	//数据5
				4'd6 : uart_txd <= tx_data_t[5];	//数据6
				4'd7 : uart_txd <= tx_data_t[6];	//数据7
				4'd8 : uart_txd <= tx_data_t[7];	//数据8
				4'd9 : uart_txd <= 1'b1;	//结束位
				default: uart_txd <= 1'b1;
			endcase
		else
			uart_txd <= 1'b1;
			
endmodule
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值