//------------------------------------------------------------------------------
// Copyright 2008 Mentor Graphics Corporation
// All Rights Reserved Worldwide
// 
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.  You may obtain
// a copy of the License at
// 
//        http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the 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.
//------------------------------------------------------------------------------

`include "ovm_apb_rw.sv"

//------------------------------------------------------------------------------
// Title: OVM Consumers
//
// This file defines the following OVM consumer components.
//
//------------------------------------------------------------------------------


//------------------------------------------------------------------------------
//
// Group: ovm_consumer
//
// A generic OVM driver/consumer component that can get transactions from
// a blocking get port or receive transactions via is blocking put export.
// If the <blocking_get_port> has been connected, this consumer will continually
// get and execute transactions from that port. Transactions coming in from the
// <blocking_get_export> will also be executed. A semaphore is used to arbitrate
// these two potential sources of transactions.
//
//------------------------------------------------------------------------------

// (begin inline source)
class ovm_consumer #(type T=int) extends ovm_component;

  typedef ovm_consumer #(T) this_type;

  `ovm_component_param_utils(this_type)

  // Port: blocking_get_port
  //
  // When connected, a consumer process will continually get from
  // the port and call <put> with the received transaction. This
  // port may be left unconnected.

  ovm_blocking_get_port #(T) blocking_get_port;

  // Port: analysis_port
  //
  // The <put> method will also write the transaction to this port
  // for coverage, scoreboarding or other analysis. This port may
  // be left unconnected.

  ovm_analysis_port #(T) analysis_port;

  // Port: blockling_put_export
  //
  // Transactions put to this export will be forwarded to the <put>
  // method, where the received transaction is executed. (In this
  // case, we merely print the fact a transaction was received.)
  // A semaphore is used to govern access by both this port and the
  // active process that putting transaction that were gotten from
  // the blocking_get_port.

  ovm_blocking_put_imp #(T,this_type) blocking_put_export;

  function new(string name, ovm_component parent=null);
    super.new(name,parent);
    blocking_put_export   = new("blocking_put_export",this);
    blocking_get_port     = new("blocking_get_port",this,0);
    analysis_port         = new("analysis_port",this);
  endfunction

  const string type_name  = {"ovm_consumer #(",T::type_name,")"};

  virtual function string get_type_name();
    return type_name;
  endfunction

  int num_trans=0;

  local semaphore lock = new(1);


  // Task: run
  //
  // If the <blocking_get_port> is connected, a the run task will
  // continually get from the port and <put> the transaction for
  // immediate execution. If <put> is busy with a transaction
  // recevied from the blocking_put_export, this process will block
  // until the transaction is complete.

  task run();
    T tr;

    if (blocking_get_port.size()<=0)
      return;

    forever begin
      blocking_get_port.get(tr);
      put(tr);
    end

  endtask

  // Task: put
  //
  // Called via the <blocking_put_port> or <run> process, this
  // process "executes" the transaction by printing a message
  // and waiting a bit of time. It uses a semaphore to prevent
  // multiple callers from colliding.

  task put (T tr);
    lock.get();
    num_trans++;
    ovm_report_info("recevied", tr.convert2string());
    analysis_port.write(tr);
    #100;
    lock.put();
  endtask 
endclass
// (end inline source)


//------------------------------------------------------------------------------
//
// Group: ovm_driver_req
//
// This consumer's run task will continually retrieve and execute 
// transaction items from this port in one of two ways, chosen
// randomly ofr -- either using get_next_item/item_done or peek/get.
//------------------------------------------------------------------------------

// (begin inline source)
class ovm_driver_req extends ovm_component;

  `ovm_component_utils(ovm_driver_req)

  // Port: seq_item_port
  //
  // Transactions are fetched from this port. Although this port is bidirectional,
  // this component will not return responses.

  ovm_seq_item_pull_port #(ovm_apb_rw) seq_item_port;

  // Port: ap
  //
  // Processed requests are published to this port. 

  ovm_analysis_port #(ovm_apb_rw) ap;

  function new(string name, ovm_component parent=null);
    super.new(name,parent);
    seq_item_port = new("seq_item_port",this);
    ap = new("ap",this);
  endfunction

  int max_trans = 100;
  int num_trans = 0;

  local integer unsigned m[int];

  function void mem_model(ref ovm_apb_rw tr);
    if(tr.cmd == ovm_apb_rw::WR)
      m[tr.addr] = tr.data;
    else begin
      if (m.exists(tr.addr))
        tr.data = m[tr.addr];
      else
        tr.data = 'hx;
    end
  endfunction
  
  task run();

    ovm_apb_rw req;
    ovm_apb_rw pop;
    
    while (num_trans < max_trans) begin 

      randcase

        // get_next_item/item_done
        1: begin
          seq_item_port.get_next_item(req);
          mem_model(req);
          #10;
          seq_item_port.item_done();
          ovm_report_info("OVM Consumer",
            {"via GET_NEXT_ITEM/ITEM_DONE - ", req.convert2string()});
        end

        // peek/get
        1: begin
          seq_item_port.peek(req);
          mem_model(req);
          #10;
          seq_item_port.get(pop);
          ovm_report_info("OVM Consumer", 
            {"via PEEK/GET                - ", req.convert2string()});
        end

      endcase

      ap.write(req);

      num_trans++;

    end
  endtask

endclass
// (end inline source)



//------------------------------------------------------------------------------
//
// Group: ovm_driver_rsp
//
// This consumer's run task will continually retrieve, execute, and send back
// a response in one of three ways, chosen randomly-- either using peek/get,
// get/delay/put, or get_next_item/item_done.
//
//------------------------------------------------------------------------------

// (begin inline source)
class ovm_driver_rsp extends ovm_component;

  `ovm_component_param_utils(ovm_driver_rsp)

  // Port: seq_item_port
  //
  // When connected, a consumer process will continually get from
  // the port and call <put> with the response.

  ovm_seq_item_pull_port #(ovm_apb_rw) seq_item_port;

  function new(string name, ovm_component parent=null);
    super.new(name,parent);
    seq_item_port = new("seq_item_port",this);
  endfunction

  int max_trans=100;
  int num_trans=0;
  
  local integer unsigned m[int];

  function ovm_apb_rw mem_model(ref ovm_apb_rw tr);
    ovm_apb_rw rsp;
    $cast(rsp,tr.clone());
    if(tr.cmd == ovm_apb_rw::WR) begin
      m[tr.addr]  = tr.data;
    end
    else begin
      if (m.exists(tr.addr))
        rsp.data = m[tr.addr];
      else
        rsp.data = 'hx;
    end
    rsp.set_id_info(tr);
    return rsp;
  endfunction // mem_model

  task run();

    ovm_apb_rw req;
    ovm_apb_rw rsp;
    
    while (num_trans < max_trans) begin 

      randcase

        // peek/get
        1: begin
          ovm_report_info("OVM Consumer","Using peek/get");
          seq_item_port.peek(req);
          rsp = mem_model(req);
          seq_item_port.get(req);
          #10;
          seq_item_port.put_response(rsp);
          ovm_report_info("OVM Consumer",
               { " via PEEK/GET                             - ",
               "req=",req.convert2string(), " rsp=", rsp.convert2string() });
        end

        // get-delay-put
        1: begin
          ovm_report_info("OVM Consumer","Using get-delay-put");
          seq_item_port.get(req);
          rsp = mem_model(req);
          #10 seq_item_port.put_response(rsp);
          ovm_report_info("OVM Consumer",
               { " via GET/DELAY/PUT                        - ",
               "req=",req.convert2string(), " rsp=", rsp.convert2string() });
        end

        // get_next_item/item_done
        1: begin
          ovm_report_info("OVM Consumer","Using get_next_item/item_done");
          seq_item_port.get_next_item(req);
          rsp = mem_model(req);
          seq_item_port.item_done();
          #10;
          seq_item_port.put_response(rsp);
          ovm_report_info("OVM Consumer",
               { " via GET_NEXT_ITEM/ITEM_DONE/PUT_RESPONSE - ",
               "req=",req.convert2string(), " rsp=", rsp.convert2string() });
        end

      endcase
    end
  endtask

endclass
// (end inline source)


//----------------------------------------------------------------------------------
//
// Group: ovm_subscribe
//
// This consumer receives transactions via the ~ovm_analysis_export~ inherited from
// its ~ovm_subscriber~ base class. 
//----------------------------------------------------------------------------------

// (begin inline source)
class ovm_subscribe #(type T=int) extends ovm_subscriber #(T);

  // Port: analysis_export
  //
  // Transactions are received via this inherited analysis export.

  ovm_analysis_port #(T) ap;

  function new(string name, ovm_component parent=null);
    super.new(name, parent);
    ap = new("ap",this);
  endfunction

  virtual function void write(T t);
     ovm_transaction o_tr;
     vmm_data v_tr;
     if ($cast(o_tr,t)) begin
       ovm_report_info("received_ovm",o_tr.convert2string());
       ap.write(t);
     end
     else if ($cast(v_tr,t))
       ovm_report_info("received_vmm",v_tr.psdisplay());
  endfunction

endclass
// (end inline source)

