/*******************************************************************************
 * Module: round_robin
 * Date:2015-07-10  
 * Author: Andrey Filippov     
 * Description: Round-robin arbiter
 *
 * Copyright (c) 2015 Elphel, Inc .
 * round_robin.v is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 *  round_robin.v is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/> .
 *******************************************************************************/
`timescale 1ns/1ps

module  round_robin #(
    parameter FIXED_PRIORITY = 0, // 0 - round-robin, 1 - fixed channel priority (0 - highest)
    parameter BITS =           2  // number of bits to encode channel number (1 << BITS) - number of inputs
)(
    input                         clk,
    input                         srst, // sync. reset - needed to reset current channel
    input      [(1 << BITS) -1:0] rq,   // request vector
    input                         en,    // enable to grant highest priority request (should be reset by grant)
    output reg                    grant, // changed to 1-cycle long (was: stays on until reset by !en)
    output             [BITS-1:0] chn,
    output reg [(1 << BITS) -1:0] grant_chn);  // 1-hot grant output per-channel, single-clock pulse
    
    reg           [BITS-1:0] last_chn;
    wire                     valid;
    wire          [BITS-1:0] next_chn;
    wire                     pre_grant_w;
    reg                      grant_r;
    
    assign pre_grant_w = en && valid &&!grant_r;
//    assign grant = grant_r;
    assign chn =   last_chn;
    
    assign {valid, next_chn}= func_selrr (rq, FIXED_PRIORITY?((1 << BITS) -1):last_chn);
    always @ (posedge clk) begin
        if      (srst)        last_chn <= (1 << BITS) -1;
        else if (pre_grant_w) last_chn <= next_chn;
        
        if (srst || !en) grant_r <= 0;
        else if (valid)  grant_r <= 1; // grant will stay on until reset by !en
        
        grant_chn <= func_demux (!srst && pre_grant_w, next_chn);

        grant <= !srst && pre_grant_w;
        
    end
    
    // round-robin priority encode
    function [BITS : 0] func_selrr; // returns {valid, chn}
        input [(1 << BITS) -1:0] rq;       // request vector
        input         [BITS-1:0] cur_chn;  // current (last served) channel - lowest priority
        reg                      valid;    // at least one request
        reg         [BITS - 1:0] chn, sample_chn;
        integer                  i;
        begin
            valid = 0;
            chn   = 0;
            for (i = 0; i < (1 << BITS); i = i+1) begin
                sample_chn = (cur_chn - i) % (1 << BITS);
                if (rq[sample_chn]) begin
                    valid = 1;
                    chn = sample_chn;
                end    
            end
            func_selrr = {valid,chn};
        end
    endfunction
    
    function  [(1 << BITS) -1:0] func_demux;
        input              en;
        input [BITS - 1:0] sel;
        integer i;
        begin
            for (i=0; i < (1 << BITS); i = i + 1) begin
                func_demux[i] = en && (sel == i); 
            end
        end
    endfunction
    
endmodule    
