Round Robin arbiter: finds the active request with highest priority and presents its index on the gnt_idx output.
            req_vec - (i) vector of request signals, priority changes dynamically
            gnt_vec - (o) optional, vector of grants, one grant per request, only one grant can be active at at time
            gnt_idx - (o) optional, grant index, index of the granted request
            gnt_vld - (o) optional, grant valid, indicate that there is a granted request
            gnt_rdy - (i) grant ready, indicates that the current grant is consumed and priority should be updated.
                          The priority is updated only if there is a valid grant when gnt_rdy is activated.
                          When priority is updated, the currently granted req_vec[gnt_idx] gets the lowest priority and
                          req_vec[gnt_idx+1] gets the highest priority.
                          gnt_rdy should be activated in the same clock cycle when output the grant is used

def arbiter_roundrobin(rst, clk, req_vec, gnt_vec=None, gnt_idx=None, gnt_vld=None, gnt_rdy=None):
    """ Round Robin arbiter: finds the active request with highest priority and presents its index on the gnt_idx output.
            req_vec - (i) vector of request signals, priority changes dynamically
            gnt_vec - (o) optional, vector of grants, one grant per request, only one grant can be active at at time
            gnt_idx - (o) optional, grant index, index of the granted request
            gnt_vld - (o) optional, grant valid, indicate that there is a granted request
            gnt_rdy - (i) grant ready, indicates that the current grant is consumed and priority should be updated.
                          The priority is updated only if there is a valid grant when gnt_rdy is activated.
                          When priority is updated, the currently granted req_vec[gnt_idx] gets the lowest priority and
                          req_vec[gnt_idx+1] gets the highest priority.
                          gnt_rdy should be activated in the same clock cycle when output the grant is used
    """
    REQ_NUM = len(req_vec)
    ptr = Signal(intbv(0, min=0, max=REQ_NUM))

    gnt_vec_s = Signal(intbv(0)[REQ_NUM:])
    gnt_idx_s = Signal(intbv(0, min=0, max=REQ_NUM))
    gnt_vld_s = Signal(bool(0))

    @always(clk.posedge)
    def ptr_proc():
        if (rst):
            ptr.next = REQ_NUM-1
        elif (gnt_rdy and gnt_vld_s):
            ptr.next = gnt_idx_s

    @always_comb
    def roundrobin_encoder():
        gnt_vec_s.next = 0
        gnt_idx_s.next = 0
        gnt_vld_s.next = 0
        for i in range(REQ_NUM):
            if (i>ptr):
                if ( req_vec[i]==1 ):
                    gnt_vec_s.next[i] = 1
                    gnt_idx_s.next = i
                    gnt_vld_s.next = 1
                    return
        for i in range(REQ_NUM):
            if ( req_vec[i]==1 ):
                gnt_vec_s.next[i] = 1
                gnt_idx_s.next = i
                gnt_vld_s.next = 1
                return

    if gnt_vec!=None: _vec = assign(gnt_vec, gnt_vec_s)
    if gnt_idx!=None: _idx = assign(gnt_idx, gnt_idx_s)
    if gnt_vld!=None: _vld = assign(gnt_vld, gnt_vld_s)

    return instances()