veryl-std 0.20.0

A modern hardware description language
Documentation
import async_fifo_pkg::calc_default_depth;

/// Asynchronous FIFO
///
/// Asynchronous FIFO based on gray-code pointer.
/// Generic parameter `S` is a module name of synchrinozer.
pub module async_fifo::<S: synchronizer> #(
    param WIDTH             : u32   = 8                         , /// Data width
    param TYPE              : type  = logic<WIDTH>              , /// Data type
    param STAGES            : u32   = 2                         , /// Stages of synchronizer
    param DEPTH             : u32   = calc_default_depth(STAGES), /// Depth of FIFO
    param THRESHOLD         : u32   = DEPTH                     , /// Threshold for almost full signal
    param USE_OUT_DATA_RESET: bbool = false                     , /// Whether output data is reset
    param INITIAL_OUT_DATA  : TYPE  = 0 as TYPE                 , /// Reset value of output data
    param MERGE_RESET       : bbool = false                     , /// Whether both reset are merged
    param RESET_SYNC_STAGES : u32   = 2                         , /// Stages of reset synchronizer
) (
    is_clk        : input  's clock, /// src clock
    is_rst        : input  's reset, /// src reset
    os_almost_full: output 's logic, /// FIFO is almost full
    os_full       : output 's logic, /// FIFO is full
    is_push       : input  's logic, /// push to FIFO
    is_data       : input  's TYPE , /// push data
    id_clk        : input  'd clock, /// dst clock
    id_rst        : input  'd reset, /// dst reset
    od_empty      : output 'd logic, /// FIFO is empty
    id_pop        : input  'd logic, /// pop from FIFO
    od_data       : output 'd TYPE , /// pop data
) {
    const POINTER_WIDTH: u32 = $clog2(DEPTH) + 1;

    var srst           : 's reset               ;
    var wp_sclk        : 's logic<POINTER_WIDTH>;
    var wp_sclk_next   : 's logic<POINTER_WIDTH>;
    var wp_sclk_gray   : 's logic<POINTER_WIDTH>;
    var wp_sclk_gray_ff: 's logic<POINTER_WIDTH>;
    var rp_sclk        : 's logic<POINTER_WIDTH>;
    var rp_sclk_gray   : 's logic<POINTER_WIDTH>;
    var drst           : 'd reset               ;
    var wp_dclk        : 'd logic<POINTER_WIDTH>;
    var wp_dclk_gray   : 'd logic<POINTER_WIDTH>;
    var rp_dclk        : 'd logic<POINTER_WIDTH>;
    var rp_dclk_next   : 'd logic<POINTER_WIDTH>;
    var rp_dclk_gray   : 'd logic<POINTER_WIDTH>;
    var rp_dclk_gray_ff: 'd logic<POINTER_WIDTH>;

    //--------------------------------------------------------------
    //  Reset
    //--------------------------------------------------------------
    inst u_reset_sync: async_fifo_reset_sync::<S> #(
        MERGE_RESET        ,
        RESET_SYNC_STAGES  ,
    ) (
        is_clk      ,
        is_rst      ,
        os_rst: srst,
        id_clk      ,
        id_rst      ,
        od_rst: drst,
    );

    //--------------------------------------------------------------
    //  FIFO Control (Write Side)
    //--------------------------------------------------------------
    var word_count : 's logic<POINTER_WIDTH>;
    var almost_full: 's logic               ;
    var full       : 's logic               ;
    var push       : 's logic               ;

    always_comb {
        os_almost_full = almost_full;
        os_full        = full;
    }

    always_comb {
        push = if !full ? is_push : '0;
    }

    always_comb {
        word_count = wp_sclk_next - rp_sclk;
    }

    always_ff (is_clk, srst) {
        if_reset {
            almost_full = '0;
            full        = '0;
        } else {
            almost_full = (word_count >= THRESHOLD as POINTER_WIDTH);
            full        = word_count[POINTER_WIDTH - 1];
        }
    }

    always_comb {
        if (push) {
            wp_sclk_next = wp_sclk + 1 as POINTER_WIDTH;
        } else {
            wp_sclk_next = wp_sclk;
        }
    }

    always_ff (is_clk, srst) {
        if_reset {
            wp_sclk = 0 as POINTER_WIDTH;
        } else {
            wp_sclk = wp_sclk_next;
        }
    }

    always_ff (is_clk, srst) {
        if_reset {
            wp_sclk_gray_ff = 0 as POINTER_WIDTH;
        } else {
            wp_sclk_gray_ff = wp_sclk_gray;
        }
    }

    inst u_s_bin2gray: gray_encoder #(
        WIDTH: POINTER_WIDTH,
    ) (
        i_bin : wp_sclk     ,
        o_gray: wp_sclk_gray,
    );

    inst u_s_gray2bin: gray_decoder #(
        WIDTH: POINTER_WIDTH,
    ) (
        i_gray: rp_sclk_gray,
        o_bin : rp_sclk     ,
    );

    //--------------------------------------------------------------
    //  FIFO Control (Read Side)
    //--------------------------------------------------------------
    var empty     : 'd logic;
    var empty_next: 'd logic;
    var pop       : 'd logic;

    always_comb {
        od_empty = empty;
    }

    always_comb {
        if pop {
            empty_next = wp_dclk == rp_dclk_next;
        } else {
            empty_next = wp_dclk == rp_dclk;
        }
    }

    always_ff (id_clk, drst) {
        if_reset {
            empty = '1;
        } else {
            empty = empty_next;
        }
    }

    always_comb {
        pop = if !empty ? id_pop : '0;
    }

    always_ff (id_clk, drst) {
        if_reset {
            rp_dclk      = 0 as POINTER_WIDTH;
            rp_dclk_next = 1 as POINTER_WIDTH;
        } else if pop {
            rp_dclk      =  rp_dclk_next;
            rp_dclk_next += 1 as POINTER_WIDTH;
        }
    }

    always_ff (id_clk, drst) {
        if_reset {
            rp_dclk_gray_ff = 0 as POINTER_WIDTH;
        } else {
            rp_dclk_gray_ff = rp_dclk_gray;
        }
    }

    inst u_d_bin2gray: gray_encoder #(
        WIDTH: POINTER_WIDTH,
    ) (
        i_bin : rp_dclk     ,
        o_gray: rp_dclk_gray,
    );

    inst u_d_gray2bin: gray_decoder #(
        WIDTH: POINTER_WIDTH,
    ) (
        i_gray: wp_dclk_gray,
        o_bin : wp_dclk     ,
    );

    //--------------------------------------------------------------
    //  Synchronizer
    //--------------------------------------------------------------
    inst u_synchronizer_wp: S #(
        WIDTH : POINTER_WIDTH,
        STAGES: STAGES       ,
    ) (
        i_clk: id_clk         ,
        i_rst: drst           ,
        i_d  : wp_sclk_gray_ff,
        o_d  : wp_dclk_gray   ,
    );

    inst u_synchronizer_rp: S #(
        WIDTH : POINTER_WIDTH,
        STAGES: STAGES       ,
    ) (
        i_clk: is_clk      ,
        i_rst: srst        ,
        i_d  : rp_dclk_gray,
        o_d  : rp_sclk_gray,
    );

    var ram: 's logic<$bits(TYPE)> [DEPTH];
    var q  : 'd logic<$bits(TYPE)>        ;

    always_ff (is_clk) {
        if push {
            ram[wp_sclk[POINTER_WIDTH - 2:0]] = is_data;
        }
    }

    // ram access from 'd domain is safe
    // because read_pointer points stable data
    unsafe (cdc) {
        if USE_OUT_DATA_RESET :g_q {
            always_ff (id_clk, drst) {
                if_reset {
                    q = INITIAL_OUT_DATA;
                } else if empty && (!empty_next) {
                    q = ram[rp_dclk[POINTER_WIDTH - 2:0]];
                } else if pop && (!empty_next) {
                    q = ram[rp_dclk_next[POINTER_WIDTH - 2:0]];
                }
            }
        } else {
            always_ff (id_clk) {
                if empty && (!empty_next) {
                    q = ram[rp_dclk[POINTER_WIDTH - 2:0]];
                } else if pop && (!empty_next) {
                    q = ram[rp_dclk_next[POINTER_WIDTH - 2:0]];
                }
            }
        }
    }

    always_comb {
        od_data = q as TYPE;
    }
}