veryl-std 0.20.1

A modern hardware description language
Documentation
pub module fifo_controller #(
    param TYPE             : type  = logic                                    ,
    param DEPTH            : u32   = 8                                        ,
    param THRESHOLD        : u32   = DEPTH                                    ,
    param FLAG_FF_OUT      : bbool = true                                     ,
    param DATA_FF_OUT      : bbool = true                                     ,
    param PUSH_ON_CLEAR    : bbool = false                                    ,
    param RAM_WORDS        : u32   = if DATA_FF_OUT ? DEPTH - 1 : DEPTH       ,
    param RAM_POINTER_WIDTH: u32   = if RAM_WORDS >= 2 ? $clog2(RAM_WORDS) : 1,
    param MATCH_COUNT_WIDTH: u32   = 0                                        ,
    param POINTER_WIDTH    : u32   = if DEPTH >= 2 ? $clog2(DEPTH) : 1        ,
    const RAM_POINTER      : type  = logic<RAM_POINTER_WIDTH>                 ,
    const POINTER          : type  = logic<POINTER_WIDTH>                     ,
    const COUNTER          : type  = logic<$clog2(DEPTH + 1)>                 ,
) (
    i_clk          : input  clock      ,
    i_rst          : input  reset      ,
    i_clear        : input  logic      ,
    o_empty        : output logic      ,
    o_almost_full  : output logic      ,
    o_full         : output logic      ,
    o_word_count   : output COUNTER    ,
    i_push         : input  logic      ,
    i_data         : input  TYPE       ,
    i_pop          : input  logic      ,
    o_write_pointer: output RAM_POINTER,
    o_write_to_ff  : output logic      ,
    o_write_to_ram : output logic      ,
    o_read_pointer : output RAM_POINTER,
    o_read_from_ram: output logic      ,
) {
    struct s_status_flag {
        empty      : logic,
        almost_full: logic,
        full       : logic,
    }

    var push             : logic           ;
    var pop              : logic           ;
    var clear            : logic        <2>;
    var update_state     : logic           ;
    var word_counter     : COUNTER         ;
    var word_counter_next: COUNTER         ;
    var word_counter_eq_1: logic           ;
    var word_counter_eq_2: logic           ;
    var word_counter_ge_2: logic           ;
    var status_flag      : s_status_flag   ;
    var write_to_ff      : logic           ;
    var write_to_ram     : logic           ;
    var ram_write_pointer: RAM_POINTER     ;
    var read_from_ram    : logic           ;
    var ram_read_pointer : RAM_POINTER     ;
    var ram_empty_next   : logic           ;
    var match_data       : logic           ;
    var last_pop_data    : logic           ;

    always_comb {
        push = i_push && ((PUSH_ON_CLEAR && i_clear) || ((!status_flag.full) && (!match_data)));
        pop  = i_pop && (!status_flag.empty) && last_pop_data;
    }

    always_comb {
        clear[0] = i_clear && ((!PUSH_ON_CLEAR) || (!push));
        clear[1] = i_clear && PUSH_ON_CLEAR && push;
    }

    always_comb {
        update_state = push || pop || i_clear;
    }

    //--------------------------------------------------------------
    //  word counter
    //--------------------------------------------------------------
    function get_word_counter_next (
        push        : input logic     ,
        pop         : input logic     ,
        clear       : input logic  <2>,
        word_counter: input COUNTER   ,
    ) -> COUNTER {
        var up  : logic;
        var down: logic;
        up   = push && (!pop);
        down = (!push) && pop;
        switch {
            clear[0]: return 0 as COUNTER;
            clear[1]: return 1 as COUNTER;
            up      : return word_counter + 1 as COUNTER;
            down    : return word_counter - 1 as COUNTER;
            default : return word_counter;
        }
    }

    always_comb {
        o_word_count = word_counter;
    }

    always_comb {
        word_counter_eq_1 = (DEPTH >= 1) && (word_counter == 1 as COUNTER);
        word_counter_eq_2 = (DEPTH >= 2) && (word_counter == 2 as COUNTER);
        word_counter_ge_2 = (DEPTH >= 2) && (word_counter >= 2 as COUNTER);
    }

    always_comb {
        word_counter_next = get_word_counter_next(push, pop, clear, word_counter);
    }

    always_ff {
        if_reset {
            word_counter = '0;
        } else if update_state {
            word_counter = word_counter_next;
        }
    }

    //--------------------------------------------------------------
    //  status flag
    //--------------------------------------------------------------
    function get_status_flag (
        word_count: input COUNTER,
    ) -> s_status_flag {
        var flag            : s_status_flag;
        flag.empty       = word_count == 0;
        flag.almost_full = word_count >= THRESHOLD;
        flag.full        = word_count >= DEPTH;
        return flag;
    }

    always_comb {
        o_empty       = status_flag.empty;
        o_almost_full = status_flag.almost_full;
        o_full        = status_flag.full && (!match_data);
    }

    if FLAG_FF_OUT :g_flag_ff_out {
        always_ff {
            if_reset {
                status_flag.empty       = '1;
                status_flag.almost_full = '0;
                status_flag.full        = '0;
            } else if update_state {
                status_flag = get_status_flag(word_counter_next);
            }
        }
    } else :g_flag_logic_out {
        always_comb {
            status_flag = get_status_flag(word_counter);
        }
    }

    //--------------------------------------------------------------
    //  write/read pointer
    //--------------------------------------------------------------
    always_comb {
        o_write_pointer = ram_write_pointer;
        o_write_to_ff   = write_to_ff;
        o_write_to_ram  = write_to_ram;
        o_read_pointer  = ram_read_pointer;
        o_read_from_ram = read_from_ram;
    }

    if DATA_FF_OUT :g_data_ff_out {
        always_comb {
            if (word_counter_eq_1 && pop) || status_flag.empty || clear[1] {
                write_to_ff  = push;
                write_to_ram = '0;
            } else {
                write_to_ff  = '0;
                write_to_ram = push;
            }
            read_from_ram  = pop && word_counter_ge_2;
            ram_empty_next = read_from_ram && (!write_to_ram) && word_counter_eq_2;
        }
    } else :g_data_ram_out {
        always_comb {
            write_to_ff    = '0;
            write_to_ram   = push;
            read_from_ram  = pop;
            ram_empty_next = read_from_ram && (!write_to_ram) && word_counter_eq_1;
        }
    }

    if RAM_WORDS >= 2 :g_multi_word_ram {
        always_ff {
            if_reset {
                ram_write_pointer = 0 as RAM_POINTER;
            } else if (clear[0]) {
                ram_write_pointer = 0 as RAM_POINTER;
            } else if (clear[1]) {
                ram_write_pointer = if DATA_FF_OUT ? 0 as RAM_POINTER : 1 as RAM_POINTER;
            } else if (ram_empty_next) {
                ram_write_pointer = ram_read_pointer;
            } else if (write_to_ram) {
                if (ram_write_pointer == (RAM_WORDS - 1) as RAM_POINTER) {
                    ram_write_pointer = 0 as RAM_POINTER;
                } else {
                    ram_write_pointer += 1 as RAM_POINTER;
                }
            }
        }

        always_ff {
            if_reset {
                ram_read_pointer = 0 as RAM_POINTER;
            } else if (i_clear) {
                ram_read_pointer = 0 as RAM_POINTER;
            } else if (ram_empty_next) {
                ram_read_pointer = ram_read_pointer;
            } else if (read_from_ram) {
                if (ram_read_pointer == (RAM_WORDS - 1) as RAM_POINTER) {
                    ram_read_pointer = 0 as RAM_POINTER;
                } else {
                    ram_read_pointer += 1 as RAM_POINTER;
                }
            }
        }
    } else :g_single_word_ram {
        always_comb {
            ram_write_pointer = 0 as RAM_POINTER;
            ram_read_pointer  = 0 as RAM_POINTER;
        }
    }

    //--------------------------------------------------------------
    //  data match
    //--------------------------------------------------------------
    if MATCH_COUNT_WIDTH >: 0 :g_data_match {
        var match_count     : logic  <DEPTH, MATCH_COUNT_WIDTH>;
        var match_count_full: logic  <DEPTH>                   ;
        var match_count_eq_1: logic  <DEPTH>                   ;
        var last_match_data : logic  <DEPTH>                   ;
        var write_pointer   : POINTER<2>                       ;
        var read_pointer    : POINTER                          ;
        var data            : TYPE                             ;

        if DEPTH == RAM_WORDS :g_pointer {
            always_comb {
                write_pointer[0] = ram_write_pointer;
                read_pointer     = ram_read_pointer;
            }
        } else {
            always_ff {
                if_reset {
                    write_pointer[0] = 0 as POINTER;
                } else if clear[0] {
                    write_pointer[0] = 0 as POINTER;
                } else if clear[1] {
                    write_pointer[0] = 1 as POINTER;
                } else if push {
                    if write_pointer[0] == (DEPTH - 1) as POINTER {
                        write_pointer[0] = 0 as POINTER;
                    } else {
                        write_pointer[0] += 1 as POINTER;
                    }
                }
            }

            always_ff {
                if_reset {
                    read_pointer = 0 as POINTER;
                } else if i_clear {
                    read_pointer = 0 as POINTER;
                } else if pop {
                    if read_pointer == (DEPTH - 1) as POINTER {
                        read_pointer = 0 as POINTER;
                    } else {
                        read_pointer += 1 as POINTER;
                    }
                }
            }
        }

        always_comb {
            if write_pointer[0] == 0 as POINTER {
                write_pointer[1] = (DEPTH - 1) as POINTER;
            } else {
                write_pointer[1] = write_pointer[0] - 1 as POINTER;
            }
        }

        always_ff {
            if push {
                data = i_data;
            }
        }

        always_comb {
            match_data    = (!status_flag.empty) && (i_data == data) && (!match_count_full[write_pointer[1]]);
            last_pop_data = last_match_data[read_pointer];
        }

        for i in 0..DEPTH :g_match_count {
            var up_down: logic<3>;

            always_comb {
                match_count_full[i] = match_count[i] == '1;
                match_count_eq_1[i] = match_count[i] == 1 as MATCH_COUNT_WIDTH;
                last_match_data[i]  = match_count_eq_1[i] && (up_down[2:1] == '0);
            }

            always_comb {
                up_down[2] = (match_data == '0) && (write_pointer[0] == i as POINTER) && push;
                up_down[1] = (match_data == '1) && (write_pointer[1] == i as POINTER) && i_push;
                up_down[0] = (!status_flag.empty) && (read_pointer == i as POINTER) && i_pop;
            }

            always_ff {
                if_reset {
                    match_count[i] = 0 as MATCH_COUNT_WIDTH;
                } else if clear[0] || (i_clear && (i >= 1)) {
                    match_count[i] = 0 as MATCH_COUNT_WIDTH;
                } else if clear[1] && (i == 0) {
                    match_count[i] = 1 as MATCH_COUNT_WIDTH;
                } else if inside up_down {3'b1x0, 3'bx10} {
                    match_count[i] += 1 as MATCH_COUNT_WIDTH;
                } else if up_down == 3'b001 {
                    match_count[i] -= 1 as MATCH_COUNT_WIDTH;
                }
            }
        }
    } else :g {
        always_comb {
            match_data    = '0;
            last_pop_data = '1;
        }
    }
}