// Copyright 2018 ETH Zurich and University of Bologna. // Copyright and related rights are licensed under the Solderpad Hardware // License, Version 0.51 (the "License"); you may not use this file except in // compliance with the License. You may obtain a copy of the License at // http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law // or agreed to in writing, software, hardware and materials distributed under // this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. // // Author: Michael Schaffner <schaffner@iis.ee.ethz.ch>, ETH Zurich // Date: 16.08.2018 // Description: Round robin arbiter with lookahead // // this unit is a generic round robin arbiter with "look ahead" - i.e. it jumps // to the next valid request signal instead of stepping around with stepsize 1. // if the current req signal has been acknowledged in the last cycle, and it is // again valid in the current cycle, the arbiter will first serve the other req // signals (if there is a valid one) in the req vector before acknowledging the // same signal again (this prevents starvation). // // the arbiter has a request signal vector input (req_i) and an ack // signal vector ouput (ack_o). to enable the arbiter the signal // en_i has to be asserted. vld_o is high when one of the req_i signals is // acknowledged. // // the entity has one register which stores the index of the last request signal // that was served. // // the lock-in feature prevents the arbiter from changing the arbitration decision // when the arbiter is disabled - i.e., the index of the first request that wins the // arbitration will be locked until en_i is asserted again. // // dependencies: relies on fast leading zero counter tree "lzc" in common_cells module rrarbiter #( parameter int unsigned NUM_REQ = 13, parameter int unsigned LOCK_IN = 0 ) ( input logic clk_i, input logic rst_ni, input logic flush_i, // clears the fsm and control signal registers input logic en_i, // arbiter enable input logic [NUM_REQ-1:0] req_i, // request signals output logic [NUM_REQ-1:0] ack_o, // acknowledge signals output logic vld_o, // request ack'ed output logic [$clog2(NUM_REQ)-1:0] idx_o // idx output ); localparam SEL_WIDTH = $clog2(NUM_REQ); logic [SEL_WIDTH-1:0] arb_sel_d, arb_sel_q; logic [SEL_WIDTH-1:0] arb_sel_lock_d, arb_sel_lock_q; // only used in case of more than 2 requesters logic [NUM_REQ-1:0] mask_lut[NUM_REQ-1:0]; logic [NUM_REQ-1:0] mask; logic [NUM_REQ-1:0] masked_lower; logic [NUM_REQ-1:0] masked_upper; logic [SEL_WIDTH-1:0] lower_idx; logic [SEL_WIDTH-1:0] upper_idx; logic [SEL_WIDTH-1:0] next_idx; logic no_lower_ones; logic lock_d, lock_q; // shared assign idx_o = arb_sel_d; assign vld_o = (|req_i) & en_i; if (LOCK_IN > 0) begin : g_lock_in // latch decision in case we got at least one req and no acknowledge assign lock_d = (|req_i) & ~en_i; assign arb_sel_lock_d = arb_sel_d; end else begin // disable assign lock_d = '0; assign arb_sel_lock_d = '0; end // only 2 input requesters if (NUM_REQ == 2 && !LOCK_IN) begin : g_rrlogic assign arb_sel_d = (( arb_sel_q) | (~arb_sel_q & ~req_i[0])) & req_i[1]; assign ack_o[0] = ((~arb_sel_q) | ( arb_sel_q & ~req_i[1])) & req_i[0] & en_i; assign ack_o[1] = arb_sel_d & en_i; end else begin // this mask is used to mask the incoming req vector // depending on the index of the last served index assign mask = mask_lut[arb_sel_q]; // get index from masked vectors lzc #( .WIDTH ( NUM_REQ ) ) i_lower_ff1 ( .in_i ( masked_lower ), .cnt_o ( lower_idx ), .empty_o ( no_lower_ones ) ); lzc #( .WIDTH ( NUM_REQ ) ) i_upper_ff1 ( .in_i ( masked_upper ), .cnt_o ( upper_idx ), .empty_o ( ) ); // wrap around assign next_idx = (no_lower_ones) ? upper_idx : lower_idx; assign arb_sel_d = (lock_q) ? arb_sel_lock_q : (next_idx < NUM_REQ) ? next_idx : unsigned'(NUM_REQ-1); end for (genvar k=0; (k < NUM_REQ) && (NUM_REQ > 2 || LOCK_IN); k++) begin : g_mask assign mask_lut[k] = unsigned'(2**(k+1)-1); assign masked_lower[k] = (~mask[k]) & req_i[k]; assign masked_upper[k] = mask[k] & req_i[k]; assign ack_o[k] = ((arb_sel_d == k) && vld_o ); end always_ff @(posedge clk_i or negedge rst_ni) begin : p_regs if(~rst_ni) begin arb_sel_q <= '0; lock_q <= 1'b0; arb_sel_lock_q <= '0; end else begin if (flush_i) begin arb_sel_q <= '0; lock_q <= 1'b0; arb_sel_lock_q <= '0; end else begin lock_q <= lock_d; arb_sel_lock_q <= arb_sel_lock_d; if (vld_o) begin arb_sel_q <= arb_sel_d; end end end end // pragma translate_off `ifndef VERILATOR // check parameterization, enable and hot1 property of acks // todo: check RR fairness with sequence assertion initial begin : p_assertions assert (NUM_REQ>=2) else $fatal ("minimum input width of req vecor is 2"); end ack_implies_vld: assert property (@(posedge clk_i) disable iff (~rst_ni) |ack_o |-> vld_o) else $fatal (1,"an asserted ack signal implies that vld_o must be asserted, too"); vld_implies_ack: assert property (@(posedge clk_i) disable iff (~rst_ni) vld_o |-> |ack_o) else $fatal (1,"an asserted vld_o signal implies that one ack_o must be asserted, too"); en_vld_check: assert property (@(posedge clk_i) disable iff (~rst_ni) !en_i |-> !vld_o) else $fatal (1,"vld must not be asserted when arbiter is disabled"); en_ack_check: assert property (@(posedge clk_i) disable iff (~rst_ni) !en_i |-> !ack_o) else $fatal (1,"ack_o must not be asserted when arbiter is disabled"); ack_idx_check: assert property (@(posedge clk_i) disable iff (~rst_ni) vld_o |-> ack_o[idx_o]) else $fatal (1,"index / ack_o do not match"); hot1_check: assert property (@(posedge clk_i) disable iff (~rst_ni) ((~(1<<idx_o)) & ack_o) == 0 ) else $fatal (1,"only one ack_o can be asserted at a time (i.e. ack_o must be hot1)"); `endif // pragma translate_on endmodule : rrarbiter