Sunday, October 30, 2016

UVM AHB Driver Example.

////////// Sequence Item //////////
class ahb_seq_item extends uvm_sequence_item;
   `uvm_object_utils(ahb_seq_item)
 
   // Master to Slave Data Flow
   rand logic [31:0] HADDR;
   rand logic [31:0] HWDATA;
   rand logic HWRITE;
   ahb_burst_e HBURST;
 
   // Slave to Master Data Flow
   logic [31:0] HRDATA;
   ahb_resp_e HRESP;
 
   // Constructor
   function new (string name);
      super.new(name);
   endfunction: new
 
   // Constraints
   constraint addr_for_32bit {HADDR[1:0] == 0;}
 
endclass: ahb_seq_item


////////// AHB Interface //////////
interface ahb_interface;
 
   // TO both Master & Slave
   logic HCLK;
   logic RESETn;
 
   // Master to Slave
   logic [31:0] HADDR;
   logic [31:0] HWDATA;
   logic HWRITE;
   ahb_burst_e HBURST;
 
   // Slave to Master
   logic [31:0] HRDATA;
   ahb_resp_e HRESP;
   logic HREADY;
 
endinterface: ahb_interface


////////// Pipelined UVM Driver //////////
class ahb_pipelined_driver extends uvm_driver #(ahb_seq_item);
   `uvm_component_utils(ahb_pipelined_driver)
 
   // Virtual Interface
   virtual ahb_interface ahb_if;
 
   // Constructor
   function new (string name, uvm_component parent);
      super.new(name, parent);
   endfunction: new
 
   // Semaphore Declaration
   semaphore pipeline_lock = new(1);
 
   // Run Phase Task
   task run_phase (uvm_phase phase);
 
      @(posedge ahb_if.HRESETn);
      @(posedge ahb_if.HCLK);
 
      fork
         do_pipelined_transfer;
         do_pipelined_transfer;
      join
 
   endtask: run_phase
 
   // do_pipelined_transfer task
   task automatic do_pipelined_transfer;
 
    ahb_seq_item req;
 
    forever begin
      pipeline_lock.get();
      seq_item_port.get(req);
      accept_tr(req, $time);
      void'(begin_tr(req, "pipelined_driver");
      ahb_if.HADDR <= req.HADDR;
      ahb_if.HWRITE <= req.HWRITE;
      ahb_if.HBURST <= req.HBURST;
      @(posedge ahb_if.HCLK);
      while(!ahb_if.HREADY == 1) begin
         @(posedge ahb_if.HCLK);
      end
      // Command phase ends here
      // Unlock semaphore
      pipeline_lock.put();
      // Data phase starts here
      if (req.HWRITE == 0) begin
         @(posedge ahb_if.HCLK);
         while(ahb_if.HREADY != 1) begin
            @(posedge ahb_if.HCLK);
         end
        req.HRDATA = ahb_if.HRDATA;
        req.HRESP = ahb_if.HRESP;
      end
      else begin
         ahb_if.HWDATA <= req.HWDATA;
         @(posedge ahb_if.HCLK);
         while(ahb_if.HREADY != 1) begin
            @(posedge ahb_if.HCLK);
         end
         req.HRESP = ahb_if.HRESP;
      end
      // Return the Request as Response
      seq_item_port.put(req);
      end_tr(req);
    end
   endtask: do_pipelined_transfer
 
 endclass: ahb_pipelined_driver


////////// Pipelined Sequence ////////// 
 class ahb_pipelined_seq extends uvm_sequence  #(ahb_seq_item);
   `uvm_object_utils(ahb_pipelined_seq)
 
   logic [31:0] addr[10]; // To save addresses
   int count; // To ensure that the seq does'nt complete too early
 
   // Constructor
   function new (string name);
      super.new(name);
   endfunction: new
 
   // Task body()
   task body;
 
      ahb_seq_item req;
      req = ahb_seq_item::type_id::create("req", this);
      use_response_handler(1); // Enable Response Handler
      count = 0;
 
      for(int i=0; i<10; i++) begin
        start_item(req);
        assert(req.randomize() 
            with {MWRITE == 1; HBURST == SINGLE; HADDR 
            inside {[32'h0010_1000:32'h0010_1FFC]};});
        addr[i] = req.HADDR;
        finish_item(req);
      end
 
      foreach (addr[i]) begin
        start_item(req);
        req.HADDR = addr[i];
        req.HWRITE = 0;
        finish_item(req);
      end
 
      // Wait till last seq item is over
      wait(count == 20);
   endtask: body
 
   // This response_handler function is enabled 
   // to keep the sequence response FIFO empty
   function void response_handler(uvm_sequence_item response);
      count++;
   endfunction: response_handler
 
 endclass: ahb_pipelined_seq

1 comment:

  1. This code is not going to work with real AHB or AHB-Lite protocol device because AHB protocol requires HTRANS signal that shows that the transfer is valid.


    In addition, it is possible to rewrite this driver without any forks or semaphores, just by using 1 (one) @(posedge ahb_if.HCLK) instead of 7. Pipelining will be still OK (data of previous transaction is processed at the same clock cycle as the address of a new transaction).

    All you have to do is to maintain not a single variable req, but two variables - one for address phase and another for data phase, and doing "req_data = req_adr; req_adr = null;" when HREADY is 1.


    Something like this (I did not compile the code, but this structure works and it has no forks or locks):



    // Run Phase Task
    task run_phase (uvm_phase phase);

    ahb_seq_item req_adr, req_data;

    @(posedge ahb_if.HRESETn);

    req_adr = null;
    req_data = null;

    forever begin
    @(posedge ahb_if.HCLK);

    if (ahb_if.HREADY)
    begin
    if (req_data != null)
    begin
    if (! req_data.HWRITE)
    begin
    req_data.HRDATA = ahb_if.HRDATA;
    req_data.HRESP = ahb_if.HRESP;
    end

    // Return the Request as Response
    seq_item_port.put(req_data);
    end_tr(req_data);
    end

    // Here is important part - we shift address phase transaction to data phase

    req_data = req_adr;
    req_adr = null;
    end

    if (req_adr == null)
    begin
    seq_item_port.try(req_adr); // Retrieve only if available

    if (req_adr != null)
    begin
    accept_tr(req, $time);
    void'(begin_tr(req, "pipelined_driver");
    end
    end

    // Address phase, with req_adr transaction

    if (req_adr != null)
    begin
    ahb_if.HTRANS <= NONSEQ;
    ahb_if.HADDR <= req_adr.HADDR;
    ahb_if.HWRITE <= req_adr.HWRITE;
    ahb_if.HBURST <= req_adr.HBURST;
    end
    else
    begin
    ahb_if.HTRANS <= IDLE;
    ahb_if.HWRITE <= '0;
    end

    // Data phase, with req_data transaction

    if (req_data != null)
    ahb_if.HWDATA <= req_data.HWDATA;
    end
    endtask: run_phase

    endclass: ahb_pipelined_driver

    ReplyDelete