AHBRAM验证(3)——AHB master driver初步实现

一. lvc_ahb_driver代码

  1. 在这段代码中,主要是接收到seq后将他发送到对应的sequencer中
  2. 在uvm_driver中,uvm专门定义了端口uvm_seq_item_pull_port#(REQ,)RSP seq_item_port和方法get_next_item(output REQ)item_done(input RSP),用于接收数据;
  3. 在代码中,通过端口seq_item_port调用get_next_item方法来获取item,获得了item后将调用函数drive_transfer将item发送出去,最后driver调用 item_done告知sequence自己完成数据的传输
  4. 需要注意的是 void'($cast(rsp, req.clone()));,虽然在定义时使用了参数指定了req是子类句柄,但是clone函数的返回值是uvm_object,所以必须调用$cast将句柄转换为子类句柄
  5. rsp.set_sequence_id(req.get_sequence_id()); rsp.set_transaction_id(req.get_transaction_id());这段代码的作用是防止存在多个sequence在同一个sequencer上启动的清况,sequencer不知道将response返回给哪个sequence。
`ifndef LVC_AHB_DRIVER_SV
`define LVC_AHB_DRIVER_SV

class lvc_ahb_driver #(type REQ = lvc_ahb_transaction, RSP = REQ) extends uvm_driver #(REQ, RSP);
  
  `uvm_component_utils(lvc_ahb_driver)

  function new(string name = "lvc_ahb_driver", uvm_component parent );
    super.new(name, parent);
  endfunction

  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
  endfunction

  function void connect_phase(uvm_phase phase);
    super.connect_phase(phase);
  endfunction

  task run_phase(uvm_phase phase);
    super.run_phase(phase);
    fork
      get_and_drive();
      reset_listener();
    join_none
  endtask

  virtual task get_and_drive();
    forever begin
      seq_item_port.get_next_item(req);
      `uvm_info(get_type_name(), "sequencer got next item", UVM_HIGH)
      drive_transfer(req);
      void'($cast(rsp, req.clone()));
      rsp.set_sequence_id(req.get_sequence_id());
      rsp.set_transaction_id(req.get_transaction_id());
      seq_item_port.item_done(rsp);
      `uvm_info(get_type_name(), "sequencer item_done_triggered", UVM_HIGH)
   end
 endtask : get_and_drive

 virtual task drive_transfer(REQ r);
   //TODO implementation in child class
 endtask 


 virtual task reset_listener();
   //TODO implementation in child class
 endtask 
endclass
`endif //LVC_AHB_DRIVER_SV

二. lvc_ahb_master_driver代码

`ifndef LVC_AHB_MASTER_DRIVER_SV
`define LVC_AHB_MASTER_DRIVER_SV

class lvc_ahb_master_driver extends lvc_ahb_driver;
  
  lvc_ahb_agent_configuration cfg;
  virtual lvc_ahb_if vif;
  `uvm_component_utils(lvc_ahb_master_driver)

  function new(string name = "lvc_ahb_master_driver", uvm_component parent );
    super.new(name, parent);
  endfunction

  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
  endfunction

  function void connect_phase(uvm_phase phase);
    super.connect_phase(phase);
  endfunction

  task run_phase(uvm_phase phase);
    super.run_phase(phase);
  endtask
 
  virtual task drive_transfer(REQ r);
      case(r.burst_type)
       	SINGLE  : do_automic_transfer(r); 
        INCR    : `uvm_error("TYPEERR", "burst type no supported yet")
        WRAP4   : `uvm_error("TYPEERR", "burst type no supported yet")
        WRAP8   : `uvm_error("TYPEERR", "burst type no supported yet")
        INCR8   : `uvm_error("TYPEERR", "burst type no supported yet")
        WRAP16  : `uvm_error("TYPEERR", "burst type no supported yet")
        INCR16  : `uvm_error("TYPEERR", "burst type no supported yet")
        default : `uvm_error("TYPEERR", "burst type no defined")
      endcase
  endtask

  virtual task do_automic_transfer(REQ r);
    case(r.xact_type)
      READ      : do_read(r);
      WRITE     : do_write(r);
      IDLE_XACT : `uvm_error("TYPEERR", "burst type no supported yet")
      default   : `uvm_error("TYPEERR", "xact type no defined")
    endcase
  endtask
  
  virtual task do_read(REQ r);
    do_init_read(r);
    do_proc_read(r);
  endtask
  
  virtual task do_write(REQ r);
    do_init_write(r);
    do_proc_write(r);
  endtask

  virtual task wait_for_bus_grant();
    @(vif.cb_mst iff vif.cb_mst.hgrant === 1'b1);
  endtask

  virtual task do_init_read(REQ r);
      wait_for_bus_grant();
      @(vif.cb_mst);
      vif.htrans <= NSEQ;
      vif.haddr  <= r.addr;
      vif.hburst <= r.burst_type;
      vif.hsize  <= r.burst_size;
      vif.hwrite <= 2'b00;
      @(vif.cb_mst);
      forever begin
        @(negedge vif.hclk);
        if(vif.hready === 1'b1) begin
          break;
        end
        else
          @(vif.cb_mst);
      end
      r.data = new[r.current_data_beat_num + 1](r.data);
      r.data[0] <= vif.hrdata;
    //updata
      r.trans_type   = NSEQ;
      r.current_data_beat_num = 0;
      r.all_beat_response = new[r.current_data_beat_num + 1];
      r.all_beat_response[r.current_data_beat_num ] = response_type_enum'(vif.hresp);
  endtask

  virtual task do_proc_read(REQ r);
    do_init_idle();
  endtask

  virtual task do_init_write(REQ r);
    wait_for_bus_grant();
    @( vif.cb_mst);
    vif.htrans <= NSEQ;
    vif.haddr  <= r.addr;
    vif.hburst <= r.burst_type;
    vif.hsize  <= r.burst_size;
    vif.hwrite <= 2'b01;
    @(vif.cb_mst);
    vif.hwdata <= r.data[0];
    forever begin
      @(negedge vif.hclk);
      if(vif.hready === 1'b1) begin
        break;
      end
      else
        @(vif.cb_mst);
    end

    //updata
    r.trans_type   = NSEQ;
    r.current_data_beat_num = 0;
    r.all_beat_response = new[r.current_data_beat_num + 1];
    r.all_beat_response[r.current_data_beat_num ] = response_type_enum'(vif.hresp);
  endtask

  virtual task do_proc_write(REQ r);
    do_init_idle();
  endtask

  virtual task do_init_idle();
     @(vif.cb_mst);
    _do_init_idle();
  endtask

  virtual task _do_init_idle();
     vif.htrans <= IDLE;
     vif.haddr  <= 0;
     vif.hwrite <= 0;
     vif.hsize  <= 0;
     vif.hport  <= 0;

  endtask
  virtual task reset_listener();
      `uvm_info(get_type_name(), "reset_listener ....", UVM_HIGH)
      fork
        forever begin
          @(negedge vif.hresetn);
          _do_init_idle();
        end
      join_none
  endtask  
endclass
`endif //LVC_AHB_MASTER_DRIVER_SV

  1. 上面的代码中作为公共的driver,只是实现了driver与sequencer之间的握手,对于真正的数据的发送将放在lvc_ahb_master_driver中
  2. 首先实现reset_listener代码,根据协议有如下代码
  virtual task reset_listener();
      `uvm_info(get_type_name(), "reset_listener ....", UVM_HIGH)
      fork
        forever begin
          @(negedge vif.hresetn);
           vif.htrans <= IDLE;
           vif.haddr  <= 0;
           vif.hwrite <= 0;
           vif.hsize  <= 0;
  		   vif.hport  <= 0;
        end
      join_none
  endtask  
  1. 在实现item的发送drive_transfer时,需要考量如何的点有:item发送的模式(即burst的类型是single还是incr,还是wrap,不同的模型对于的时序是不同的)和数据传输的方向(是读还是写)
  2. 在ahb_ram中他只是支持single的模式,代码如下,在single模式下,调用do_automic_transfer做数据的传输,然后在do_automic_transfer下区分读写
  virtual task drive_transfer(REQ r);
      case(r.burst_type)
       	SINGLE  : do_automic_transfer(r); 
        INCR    : `uvm_error("TYPEERR", "burst type no supported yet")
        WRAP4   : `uvm_error("TYPEERR", "burst type no supported yet")
        WRAP8   : `uvm_error("TYPEERR", "burst type no supported yet")
        INCR8   : `uvm_error("TYPEERR", "burst type no supported yet")
        WRAP16  : `uvm_error("TYPEERR", "burst type no supported yet")
        INCR16  : `uvm_error("TYPEERR", "burst type no supported yet")
        default : `uvm_error("TYPEERR", "burst type no defined")
      endcase
  endtask
  1. 在do_automic_transfer中区分了读写
  virtual task do_automic_transfer(REQ r);
    case(r.xact_type)
      READ      : do_read(r);
      WRITE     : do_write(r);
      IDLE_XACT : `uvm_error("TYPEERR", "burst type no supported yet")
      default   : `uvm_error("TYPEERR", "xact type no defined")
    endcase
  endtask
  1. do_write()
    在构建do_write时,由于HBURST = SINGLE所以HTRANS只能是NONSEQIDLE,并且NONSEQ是每一次传输的一个起始标志;所以定义两个函数do_init_write(r) do_proc_write(r);前面的一个定义为单词的传输,后面的为连续传输
 virtual task do_write(REQ r);
    do_init_write(r);
    do_proc_write(r);
  endtask

在这里插入图片描述

  1. do_init_read
    在这段代码中,实现了协议时序,在拿到总线的权限后的一拍将拿到的item赋值到接口上vif.htrans vif.haddr ; vif.hburst;vif.hsize ; vif.hwrite;,特别需要注意的是关于Hready的处理,在上面的时序图中T5----T7,在T6时采样到hready为低,即表示数据slave并没有接受成功,所以data;trans;addr都要保持一拍直到hready拉高;如何去检测hready拉高的状态尤为准要,在这里采取在时钟的下降沿采样;等到hready为高后,将数据读出;在读出数据时,首先要将动态数组data开辟空间初始化,然后将数据从接口赋值到item中读出
    这里需要注意何时用时钟块采样何时用时钟
  virtual task do_init_read(REQ r);
       @(vif.cb_mst iff vif.cb_mst.hgrant === 1'b1);
      @(vif.cb_mst);
      vif.htrans <= NSEQ;
      vif.haddr  <= r.addr;
      vif.hburst <= r.burst_type;
      vif.hsize  <= r.burst_size;
      vif.hwrite <= 2'b00;
      @(vif.cb_mst);
      forever begin
        @(negedge vif.hclk);
        if(vif.hready === 1'b1) begin
          break;
        end
        else
          @(vif.cb_mst);
      end
      r.data = new[r.current_data_beat_num + 1](r.data);
      r.data[0] <= vif.hrdata;
    //updata
 r.trans_type   = NSEQ;
  r.current_data_beat_num = 0;
  r.all_beat_response = new[r.current_data_beat_num + 1];
  r.all_beat_response[r.current_data_beat_num ] = response_type_enum'(vif.hresp);

  endtask
  1. do_proc_read
    在这里因为是single模式,所以只是作为IDLE模式处理即可
 virtual task do_proc_read(REQ r);
     @(vif.cb_mst);
      vif.htrans <= IDLE;
     vif.haddr  <= 0;
     vif.hwrite <= 0;
     vif.hsize  <= 0;
     vif.hport  <= 0;
  endtask

三. lvc_ahb_if 更新

添加了时钟块和一些debug信号
debug信号的存在在于便于观察,某些需要观察的信号出现的形式是数字,但是在协议中是以字符串的形式出现的,所以利用枚举类型的静态转换将其转换成字符串

`ifndef LVC_AHB_IF_SV
`define LVC_AHB_IF_SV

interface lvc_ahb_if;
  
`include "lvc_ahb_defines.svh"
import lvc_ahb_pkg::*;

	logic                                   hclk;
	logic                                   hresetn;
	
	logic                                   hgrant;
	logic [(`LVC_AHB_MAX_DATA_WIDTH - 1):0] hrdata;
	logic                                   hready;
	logic [1:0]                             hresp;
	logic [(`LVC_AHB_MAX_ADDR_WIDTH - 1):0] haddr;
	logic [2:0]                             hburst;
	logic                                   hbusreq;
	logic                                   hlock;
	logic [3:0]                             hport;
	logic [2:0]                             hsize;
	logic [1:0]                             htrans;
	logic [(`LVC_AHB_MAX_DATA_WIDTH - 1):0] hwdata;
	logic                                   hwrite;

  response_type_enum    debug_hresp;
  trans_type_enum       debug_htrans;
  burst_size_enum       debug_hsize;
  burst_type_enum       debug_hburst;
  xact_type_enum        debug_xact;
  status_enum           debug_status;

  // debug signals assignment
  assign debug_hresp  = response_type_enum'(hresp);
  assign debug_htrans = trans_type_enum'(htrans);
  assign debug_hsize  = burst_size_enum'(hsize);
  assign debug_hburst = burst_type_enum'(hburst);
  
  //the below signals to be assigned by monitor
  //debug_status ..
  //debug_xact ..
  clocking cb_mst @(posedge hclk);
      default input #1ps output #1ps;
      input hgrant, hready, hresp, hrdata;
      output hbusreq, hlock, htrans, haddr, hwrite, hsize, hburst, hport, hwdata;
   endclocking

    clocking cb_slv @(posedge hclk);
      default input #1ps output #1ps;
      output hgrant, hready, hresp, hrdata;
      input hbusreq, hlock, htrans, haddr, hwrite, hsize, hburst, hport, hwdata;
   endclocking

    clocking cb_mon @(posedge hclk);
      default input #1ps output #1ps;
      output hgrant, hready, hresp, hrdata;
      output hbusreq, hlock, htrans, haddr, hwrite, hsize, hburst, hport, hwdata;
   endclocking


endinterface

`endif //LVC_AHB_IF_SV