Do the uart fifos require clock domain cross and gray code?

85 views Asked by At

If the UART module has a single clock input (call it axi_aclk), and the baudrate clock is generated from axi_aclk according to divisor registers (DLL and DLM).

Let's take the clocks for xmit fifo as an example below (the write port of the fifo is driven by axi_clk, and the read port is driven by baudclk:

        +------------>---------------------+
        |                                  |
        |                                  v
--> axi_aclk ---> [baudrate generator]     +-->[xmt fifo] <--
                         |                                  ^
                         |                                  |
                         +----------------------------------+
                                        baudclk

My question is, in such case, does the xmt fifo implementation require clock-domain-crossing logic (and gray code for read/write pointers), and why?

PS: my baud clock generating module is below (the i_clk into brg module is axi_aclk):

/*
 * baud-rate generate (brg) is a clock divisor which takes an input clock
 * and generate an output clock (baud16), with the following freq relationship:
 *
 *  baud16 = clk / divisor
 *
 * 
 * limitations:
 * - The minimum divisor for this module is 1. For this case, baud16 ties to i_clk.
 * - For divisor=0, the output is undefined.
 * - For odd divisors greater than 1, the duty cycle of the output will be slightly less than 50%.
 */

`default_nettype none

module uart_brg #(
    parameter DIVISOR_WIDTH = 16   // 16-bit integer, per 16550 standard
) (
    input  wire                             i_rst_n,
    input  wire                             i_clk,
    input  wire [DIVISOR_WIDTH - 1 : 0]     i_divisor,   // >0
    output reg                              o_clk        // baud16
);

reg [DIVISOR_WIDTH - 1 : 0] count = 0;
reg out;

// update count
always @(posedge i_clk, negedge i_rst_n) begin
    if (~i_rst_n)
        count <= {DIVISOR_WIDTH{1'b0}};
    else begin
        if (count == i_divisor - 1)
            count <= 0;
        else
            count <= count + 1;
    end
end

// update out
always @(posedge i_clk, negedge i_rst_n) begin
    if (~i_rst_n)
        out <= 1'b0;
    else begin
        if (count == 0 || count == i_divisor >> 1)
            out <= ~out;
    end
end

//assign o_clk = ~i_rst_n? 1'b0 : ((i_divisor == 1)? i_clk : out);
always @(*) begin
    if (~i_rst_n)                 o_clk = 1'b0;
    else if (i_divisor == 1)      o_clk = i_clk;
    else                          o_clk = out;
end

endmodule

`default_nettype wire

This design is targeting a Xilinx FPGA.

It uses the beh_fifo (w/o gray code) found at: Sunburst FIFO

If CDC is not necessary, can CDC logic and/or the FIFO be removed?

I am implementing a simplified version of Xilinx axi_uart16550.

1

There are 1 answers

4
sharvian On

These two clocks may be synchronous. baudclk may be implemented by gated clock. For example, mask 3 out of 4 axi_aclk clock pulses to generate a divide-by-4 baudclk.

           _   _   _   _   _   _   _   _   _   _
axi_aclk _| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_
           _               _               _
baudclk  _| |_____________| |_____________| |_____


axi_aclk -> clk buffer -+---------------> DFF (axi_aclk)
                        |
                        +-> clk gating -> DFF (baudclk)

Create a clock tree on axi_aclk, the DFFs of axi_aclk and baudclk domains will be balanced.