// 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.

// Wrapper package so class can be used after `import rand_id_queue_pkg::*;`.
package rand_id_queue_pkg;

// ID Queue with Randomizing Output
class rand_id_queue #(
  type          data_t = logic,
  int unsigned  ID_WIDTH = 0
);

  localparam int unsigned N_IDS = 2**ID_WIDTH;
  localparam type id_t = logic[ID_WIDTH-1:0];

  data_t queues[N_IDS-1:0][$];
  int unsigned size;

  function new();
    size = 0;
  endfunction

  // Push a data element to the queue with the given ID.
  function void push(id_t id, data_t data);
    queues[id].push_back(data);
    size++;
  endfunction

  // Determine if the ID queue is empty.
  function bit empty();
    return (size == 0);
  endfunction

  // Pick a non-empty queue at random and return the front element.  Not defined if the ID queue is
  // empty.
  function data_t peek();
    return queues[rand_id()][0];
  endfunction

  // Pick a non-empty queue at random, remove the front element from that queue, and return that
  // element.  Not defined if the ID queue is empty.
  function data_t pop();
    return pop_id(rand_id());
  endfunction

  // Remove the front element of the queue with the given ID and return that element.  Not defined
  // if the queue with the given ID is empty.
  function data_t pop_id(id_t id);
    size--;
    return queues[id].pop_front();
  endfunction

  // Pick a non-empty queue at random and return the ID of that queue.  Not defined if the ID queue
  // is empty.
  function id_t rand_id();
    if (!empty()) begin
      id_t id;
      do begin
        void'(std::randomize(id));
      end while (queues[id].size() == 0);
      return id;
    end else begin
      return 'x;
    end
  endfunction

  // Set the front element of the queue with the given ID.  Not defined if the queue with the given
  // ID is empty; instead use `push()` to insert a new element.
  function void set(id_t id, data_t data);
    queues[id][0] = data;
  endfunction

  // Get the front element of the queue with the given ID.  Not defined if the queue with the given
  // ID is empty.
  function data_t get(id_t id);
    return queues[id][0];
  endfunction

endclass

endpackage