数字IC验证 - Portable Stimulus Standard(PSS) Uart IP级测试用例

测试用例简介:

       uart ip来自于openc910项目的uart源代码,本测试用例通过配置uart寄存器来实现uart的组件的发送和接收以及数据的检查,使用dsl语言抽象描述apb组件和vip_uart组件,并通过PSS的action infer feature来推断生成各种合法的配置场景。

测试用例资源:

1. uart ip

2. uart vip

3. apb vip

验证组件抽象:

1. testbench中总共有 apb vip,uart ip 和 uart vip组件,driver transactions的组件分别是apb vip和uart vip,因此需要使用dsl语言对这两个组件做描述。

2. PSS IP验证需要约束生成代码是uvm domain的代码,因此将executor_s属性配置为UVM_TYPE类型来让PSS工具生成支持uvm平台的代码,并配置thread id。uart vip组件和apb组件的atomic action将继承core_action_a,将thread和component binding。

3. 定义apb vip的HSI接口,HSI接口将以import target的接口形式导入到model中调用,我们需要定义一个apb的写接口xpss_scalar_write32和读接口xpss_scalar_read32。

4. 定义uart vip的HSI接口,HSI接口将以import target的接口形式导入到model中调用,我们需要定义vip uart 配置接口vip_uart_config,vip uart读字符接口vip_uart_receive和vip uart写字符接口vip_uart_transmit。

/* executor pkg */
package insts_pkg {
  import executor_pkg::*;

  struct inst_trait_s : executor_trait_s {
    rand int in [0:1] thread_id;
  };
  abstract action insts_action_a {
    rand executor_cleaim_s<inst_trait_s> inst;
  };
};

package apb_sv_binding_pkg {
  import target C function void xpss_scalar_read32(bit[64] addr, output bit[32] data);
  import target C function void xpss_scalar_write32(bit[64] addr, input bit[32] data);
};

/* apb uart */
component uart_c {
  import insts_pkg::*;
  
  abstract action uart_base_a : insts_action_a {
    constraint default inst.trait.thread_id == 0;
  };

  action uart_config_a : uart_base_a { 
    output uart_config_s cfg_stream; // configure data stream
  };
  action uart_tx_a : uart_base_a { 
    output uart_tx_stream to_chs;  // transmit data byte stream 
    rand int in [1..127] size;     // constraint data bytes
  };
  action uart_rx_a : uart_base_a { 
    input uart_rx_stream from_chs; // receive data byte stream
  };
}; // apb_uart_c

package vip_uart_sv_binding_pkg {
  import target C function void vip_uart_config(int char_len, int nb_stop, int parity_en int parity_mode);
  import target C function void vip_uart_transmit(bit[8] payload);
  import target C function void vip_uart_receive(bit[8] expected_data, bit[8] actual_data);
}

/* vip uart */ 
component vip_uart_c {
  import insts_pkg::*;

  abstract action uart_base_a : insts_action_a {
    constraint default inst.trait.thread_id == 1;
  }; 

  action uart_config_a : uart_base_a { 
    input uart_config_s cfg_stream;

  };
  action uart_tx_a : uart_base_a { 
    output uart_rx_stream to_chs; // transmit data byte stream 
    rand int in [1..127] size;    // constraint data bytes    
  };
  action uart_rx_a : uart_base_a { 
    input uart_tx_stream from_chs; // receive data byte stream
  };
}; // vip_uart_c

     PSS组件间数据交互是通过Flow objects来实现的,DSL提供了一些Flow objects数据类型,例如Buffer objects,Stream objects,State objects和Resource objects,这些objects具体有什么作用就不做说明了。我们将使用到stream objects,因为uart的发送和接收是并行执行的,具有瞬态交换属性性质。

package uart_pkg {
  
  stream uart_tx_stream {
    list<bit[8]> data;
  };

  stream uart_rx_stream {
    list<bit[8]> data;
  };

  stream uart_config_s {
    rand bit[2] char_leng;
    rand bit[2] parity_mode;
    rand bit[2] in [0..3] nb_stop;
  };

  enum baud_rate_e {
    BAUD_RATE_9600 = 9600,
    BAUD_RATE_115200 = 115200,
  };
};

        apb vip和uart vip接口HSI,HSI接口实现了一个承上启下的作用,承上对应的是modeling的调用,建模时可以不关心HSI接口的具体实现,只需要知道HSI接口的具体功能和传入参数就可以, 启下对应的是HSI接口可以使用如下uvm/C/systemC等语法来进行具体的功能实现,工具不提供任何具体实现。下面分别实现了vip_uart_sv_binding_pkg.pss和apb_sv_binding_pkg.pss,为uart vip提供的HSI接口和apb vip提供的HSI接口。

/* vip uart HSI function */
export "DPI-C" task uvm_set_config_int;
task automatic uvm_set_config_int(input string path, string field, int value);
  uvm_config_int::set(null, path, field, value);
endtask;

export "DPI-C" vip_uart_config;
task vip_uart_config(input int char_len, input int nb_stop, input int parity_en, input int parity_mode);
  uart_env uart;
  static string path = {"*.demo_tb.", inst_name};
  uart.uart_config(char_len, nb_stop, parity_en, parity_mode);
endtask; // vip_uart_config

export "DPI-C" task vip_uart_transmit;
task vip_uart_transmit(input int unsinged payload);
  static string tx_agent = {"*.", inst_name, ".tx"};
  uart_tx_agent tx;
  uart_pkg::uart_transmit_seq uart_tx;
  
  uart_tx = uart_pkg::uart_transmit_seq::type_id::create("uart_transmit", tx);
  uart_tx.num_of_tx = 1;
  uart_tx.payload = payload;
  uart_tx.start(tx.sequencer);
endtask; // vip_uart_transmit

export "DPI-C" task vip_uart_receive;
task vip_uart_receive(input int unsigned expected_data, input int unsigned actual_data);
  demo_tb tb;
  tb = find_demo_tb(".demo_tb");
  tb.scoreboard.data_check(expected_data, actual_data);
endtask; // vip_uart_receive

/* apb vip HSI function */
typedef longint unsigned addr_t;
typedef int unsigned     data_t;

task automic apb_write(input apb_master_agent master, input addr_t addr, input data_t data, input int width);
  case (width)
    1: begin
      apb_pkg::write_byte_seq byte_seq;
      byte_seq = apb_pkg::write_byte_seq::type_id::create("write_byte_seq", maser);
      byte_seq.addr = addr[39:0];
      byte_seq.data = data[7:0];
      byte_seq.start(master.sequencer);
    end
    2: begin
      apb_pkg::write_hword_seq hword_seq;
      hword_seq = apb_pkg::write_hword_seq::type_id::create("write_hword_seq", maser);
      hword_seq.addr = addr[39:0];
      hword_seq.data = data[15:0];
      hword_seq.start(master.sequencer);
    end
    4: begin
      apb_pkg::write_word_seq word_seq;
      word_seq = apb_pkg::write_word_seq::type_id::create("write_word_seq", maser);
      word_seq.addr = addr[39:0];
      word_seq.data = data[15:0];
      word_seq.start(master.sequencer);
    end
endtask;

task automic apb_read(input apb_master_agent master, input addr_t addr, output data_t data, input int width);
  case (width)
    1: begin
      apb_pkg::read_byte_seq byte_seq;
      byte_seq = apb_pkg::read_byte_seq::type_id::create("read_byte_seq", maser);
      byte_seq.addr = addr[39:0];
      byte_seq.start(master.sequencer);
      data = byte_seq.rsp.data;
    end
    2: begin
      apb_pkg::read_hword_seq hword_seq;
      hword_seq = apb_pkg::read_hword_seq::type_id::create("read_hword_seq", maser);
      hword_seq.addr = addr[39:0];
      hword_seq.start(master.sequencer);
      data = hword_seq.rsp.data;
    end
    4: begin
      apb_pkg::read_word_seq word_seq;
      word_seq = apb_pkg::read_word_seq::type_id::create("read_word_seq", maser);
      word_seq.addr = addr[39:0];
      word_seq.start(master.sequencer);
      data = word_seq.rsp.data;
    end
endtask;


export "DPI-C" task xpss_scalar_write8;
task xpss_scalar_write8(input longint unsigned addr, input byte unsigned data);
  static string apb_master = {"*.", inst_name, ".master"};
  apb_master_agent master;
  master = find_apb_master_agent(apb_master);
  apb_write(master, addr, 1, data);
endtask;

export "DPI-C" task xpss_scalar_write32;
task xpss_scalar_write32(input longint unsigned addr, input int unsigned data);
  static string apb_master = {"*.", inst_name, ".master"};
  apb_master_agent master;
  master = find_apb_master_agent(apb_master);
  apb_write(master, addr, 4, data); 
endtask;

export "DPI-C" task xpss_scalar_read8;
task xpss_scalar_read8(input longint unsigned addr, output byte unsigned data);
  static string apb_master = {"*.", inst_name, ".master"};
  apb_master_agent master;
  master = find_apb_master_agent(apb_master);
  apb_read(master, addr, 1, data);
endtask;

export "DPI-C" task xpss_scalar_read32;
task xpss_scalar_read32(input longint unsigned addr, output int unsigned data);
  static string apb_master = {"*.", inst_name, ".master"};
  apb_master_agent master;
  master = find_apb_master_agent(apb_master);
  apb_read(master, addr, 4, data); 
endtask;

uart,vip uart激励实现部分automic action body建模,这里我们需要配置传输控制寄存器控制 UART 的传输和接收,主要控制传输的数据位长度和是否奇偶校验等,并实现发送和接收相关的action body的功能。

extend compont uart_c {
  bit[32] uart_lcr;
  int baud_div;

  extend action config_uart_a {
    exec post_solve {
      baud_div = / BADU_RATE_9600;
      // 
      if (cfg_stream.char_length) {
        uart_lcr |= ;
      }
      if (cfg_stream.parity_mode) {
        uart_lcr |= ;
      }
      if (cfg_stream.char_nb_stop) {
        uart_lcr |= ;
      }
    };

     exec body {
       ...
       // write baudrate register
       xpss_scalar_write32(UART_DDL, (baud_div) &0xff);
       // write contral register
       xpss_scalar_write32(UART_LCR, uart_lcr);
     };    
  };

  extend action tx_uart_data_a {
    exec post_solve {
      repeat (size) {
        to_chs.data.push_back(urandom() & 0xff);
      }
    };
    
    exec body {
      repeat (i: size) {
        // waiting till transmit
        while ([condition, TODO]) xpss_yield();
        xpss_scalar_write32(UART_THR, to_chs.data[i]);
      }
    };

    extend action rx_uart_data_a {
      exec body {
        bit[8] actual_data;
        repeat(i : from_chs.data.size()) {
           // waiting till data incoming fifo 
           while ([condition, TODO]) xpss_yield();
           xpss_scalar_read32(UART_RBR, actual_data);

           // compare excepted data and actual data TODO
        }
      };
    };
  };
};

extend compont vip_uart_c {
  extend action config_uart_a {
    int parity_mode;
    int char_length;
    int nb_stop;

    exec post_solve {
      char_length = cfg_stream.char_length;
      parity_mode = cfg_stream.parity_mode;
      nb_stop = cfg_stream.char_nb_stop;
    };

    exec run_start {
      uvm_sv_binging_pkg::uvm_set_config_int(uart_path, "baud_rate", 0xaf8);
      uvm_sv_binging_pkg::uvm_set_config_int(uart_path, "prity_mode", parity_mode);
      uvm_sv_binging_pkg::uvm_set_config_int(uart_path, "char_length", char_length);
      uvm_sv_binging_pkg::uvm_set_config_int(uart_path, "nb_stop", nb_stop);
    };
    
    exec body {
      vip_config_uart(char_length, parity_mode, nb_stop);
    };
  };  
   
  extend action tx_uart_data_a {
    exec post_solve {
      repeat (size) {
        to_chs.data.push_back(urandom() & 0xff);
      }
    };
    
    exec body {
      repeat (i: size) {
        vip_uart_transmit(to_chs.data[i]);
      }
    };
  };

  extend action rx_uart_data_a {
    exec body {
      bit[8] actual_data;
      repeat(i : from_chs.data.size()) {
        actual_data = vip_uart_receive();
        // compare excepted data and actual data, TODO
      };
    };
  };
};

       到现在我们定义了component组件,并实现了atomic actions,接下来我们再来看看场景scenario是如何实现的。首先在pss_top组件定义object pool资源池,例化组件,并且将threads与executor进行绑定,将thread0分配给apb0线程执行,将thread1分配给uart0线程执行。

component pss_top {
  import uart_pkg::*;
  import insts_pkg::*;
  import addr_reg_pkg::*;

  // bind stream objects polls
  pool uart_config_s uart_cfg_p;
  bind uart_cfg_p *;

  // bind stream objects polls
  pool uart_tx_stream uart_tx_p;
  bind uart_tx_p *; 

  // bind stream objects polls
  pool uart_rx_stream uart_rx_p;
  bind uart_rx_p *;

  // instant component
  insts_c insts;
  uart_c uart;
  vip_uart_c vip_uart;

  transparent_addr_space_c<> sys_mem;

  exec init_down {
    // bind threads to exectors
    insts.inst[0].tag = "apb0";
    insts.inst[0].agent_context_type = UVM_TYPE;
    insts.inst[1].tag = "uart0";
    insts.inst[1].agent_context_type = UVM_TYPE;    

    // set uart component base address
    transparent_addr_region_s<> uart_mmio_region;
    uart_mmio_region.addr = 0x10015000;
    uart.base_addr = sys_mem.add_nonallocatable_region(uart_mio_region);
  };
};

       让后我们再定义uart场景action, 这些action是复合类型action,即不存在body生成具体的实现。

 第一个场景:uart_multi_transmit_a是uart实现多次transmit操作,constraint transmit操作次数在1~10次,发送的字节在1~20字节。

第二个场景:uart_tx_rx_a是uart执行一次transmit操作和一次receive操作。

extend component pss_top {
  action uart_multi_transmit_a {
    rand int in [1..10] count;
    rand int in [1..20] size;

    activity {
      repeat (i : count) {
        do uart_c::tx_uart_data_a with { size == this.size; };
      }
    };
  };

  action uart_tx_rx_a {
    rand int in [1..20] size;

    activity {
      do uart_c::tx_uart_data_a with { size == this.size; };
      do uart_c::rx_uart_data_a with { size == this.size; };
    };
  };

  action scenario_test_a {
    do uart_tx_rx_a;
  };
};

        好了,到此使用DSL对Uart IP建模部分就结束了。model已经写好了,那么我们就可以使用PSS工具来解析模型,生成可执行的代码了。这里我们选择使用的是galaxpss这PSS工具。下面是工具使用的filelist:

-dsl insts_pkg.pss
-dsl insts_c.pss
-dsl insts_exec.pss

-dsl uvm_sv_binding_pkg.pss

-dsl uart_pkg.pss
-dsl uart_c.pss
-dsl uart_exec.pss
-dsl vip_uart_c.pss
-dsl vip_uart_exec.pss
-dsl pss_top.pss

-r pss_top::scenario_test_a

-std;
-addr_reg
-pformat h
-debug 2
-msg_level FULL
-uvm
-l

执行:galaxpss -f filelist

      下图是do uart_c::tx_uart_data_a 这个action执行生成的图,这只有一个atomic action啊,怎么会生成出这么多的其它atomic action呢?这就是PSS的一个奇特feature,其拥有强大的action infer推断能力,即使使用者没有将场景补充完全,像这里的仅仅是设想了一个uart transmit的动作,PSS工具自动推断出执行这个uart transmit动作所依赖的其它动作行为,并合法的生成相对应的动作,比如init_uart_a做uart的初始化动作,然后是uart的config动作,同时将vip uart的config动作也推断出来了,根据数据流控的input/output推断出执行流控,能够使其生成的场景更加丰富,即使我们验证工程师没有考虑到的验证点,PSS工具也会根据对数据,数据流和场景的约束而补充各种合法的场景。

     

       

        生成文件目录:

        各个文件的作用:      

    1. test_sv_case.c是执行激励的文件;

    2. test_sv_case.sv是SV实现的HSI接口文件;

    3. lib_entry_top.sv文件是PSS的入口module,主要是启动test_sv_case.c文件中的线程;

    4. lib_startup_pkg.sv文件提供线程与testbench启动平台的同步机制。

       下面介绍如何将上面生成的文件集成到testbench。

   1. 在module tb_top中例化module lib_entry_top;

    2. 在pss_sequence_test类的run_phase,启动C main同步

module tb_top;
  ......

  // instance pss entry module
  lib_entry_top entry_top();
 
  // instance uart interface and apb interface
  uart_if uart_if_0(...);
  apb_if apb_if_0(...);

  uart i_uart(
    .sys_clk(sys_clk),
    .rst_b(reset_n),
    .apb_uart_paddr(apb_if_0.paddr[31:0]),
    .apb_uart_pwdata(apb_if_0.pwdata[31:0]),
    .uart_apb_prdata(apb_if_0.prdata[31:0]),
    .apb_uart_pwrite(apb_if_0.prwd),
    .apb_uart_penable(apb_if_0.penable),
    .apb_uart_psel(apb_if_0.psel[0]),
    .uart_vic_int(),
    .s_in(uart_if_0.txd),
    .s_out(uart_if_0.rxd)
  );

  initial begin
    ......
    run_test();
  end
  
  ......



class pss_sequence_test extends pss_base_test;
  `uvm_component_utils(pss_sequence_test);

   function new(string name = "pss_sequence_test", uvm_component parent);
     super.new(name, parent);
   endfunction: new
 
   virtual function void build_phase(uvm_phase phase);
     super.build_phase(phase);
   endfunction: build_phase

   task run_phase(uvm_phase phase);
     phase.raise_objection(this);
 
     // startup the PSS C test main
     lib_startup_pkg::xpss_sync_entry_main();

     phase.drop_objection(this);
   endtask: run_phase
 
endclass: pss_sequence_test;

        这个case我们是分别使用xrun, vcs和galaxsim仿真器来跑,结果如下:

xrun运行结果:

vcs运行结果:

galaxsim运行结果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值