Verification Martial Arts: A Verification Methodology Blog

VMM-to-SystemC Communication Using the TLI

Posted by John Aynsley on March 22nd, 2011

John Aynsley, CTO, Doulos

I have said several times on this blog that the presence of TLM-2.0 features in VMM 1.2 should ease the task of communicating between a SystemVerilog test bench and a SystemC reference model. Now, at last, let’s see how to do this – using the VCS TLI or Transaction Level Interface from Synopsys.

The parts of the TLI in question are the VCS TLI Adapters between SystemVerilog and SystemC. These adapters exploit the TLM-2.0-inspired features introduced into VMM 1.2 on the SystemVerilog side and the OSCI TLM-2.0 standard itself on the SystemC side in order to pass transactions between the two language domains within a VCS simulation run. The TLI Adapters do not provide a completely general solution out-of-the-box for passing transactions between languages in that they are restricted to passing TLM-2.0 generic payload transactions (as discussed in a previous blog post). However, the Adapters can be extended by the user with a little work.

Clearly, the VCS TLI solution will only be of interest to VCS users. As an alternative to the VCS TLI, it is possible to pass transactions between SystemVerilog and SystemC using the SystemVerilog DPI as described in SystemVerilog Meets C++: Re-use of Existing C/C++ Models Just Got Easier, and the Accellera VIP Technical Subcommittee are discussing a proposal to add a similar capability to UVM.

If you want to pass user-defined transactions between SystemVerilog and SystemC you are going to have to jump through some hoops, whether you choose to use the VCS TLI or the DPI. However, for the TLM-2.0 generic payload, the VCS TLI provides a simple ready-to-use solution. Let’s see how it works.

image

The TLI Adapters are provided as part of VCS. All you have to do is to include the appropriate file headers in your source code on both the SystemVerilog and SystemC sides, as shown on the diagram. The adapters themselves get compiled and instantiated automatically. The SystemVerilog side needs to use the VMM TLM ports, exports, or channels (as described in previous blog posts). The SystemC side needs to use the standard TLM-2.0 sockets. You then need to add a few extra lines of code on each side to bind the two sets of sockets together, and the TLI Adapters take care of the rest.
From the point of view of the source code, the adapter is invisible apart from the presence of the header files. Each binding needs to be identified by giving it a name, with identical names being used on the SystemVerilog and SystemC sides to tie the two sets of ports or sockets together. Here is a trivial example:

// SystemVerilog
`include “tli_sv_bindings.sv”

vmm_tlm_b_transport_port #(my_xactor, vmm_tlm_generic_payload) m_b_port;

tli_tlm_bind(m_xactor.m_b_port, vmm_tlm::TLM_BLOCKING_EXPORT, “abc”);

// SystemC
#include “tli_sc_bindings.h”

tlm_utils::simple_target_socket<scmod>  targ_socket_lt;

tli_tlm_bind_target (m_scmod->targ_socket_lt, LT, “abc”, true);


Note that the same name “abc” has been used on both the SystemVerilog and SystemC sides to tie the two ports/sockets together. On the SystemVerilog side we can now construct transactions and send them out through the TLM port:

// SystemVerilog
tx = new;
assert( tx.randomize() with { m_command != 2; } );
m_b_port.b_transport(tx, delay);


On the SystemC side, we receive the incoming transaction:

// SystemC
void scmod::b_transport(tlm::tlm_generic_payload& tx, sc_time& delay) {

tx.set_response_status(tlm::TLM_OK_RESPONSE);
}

The TLI Adapter takes care of converting the generic payload transaction from SystemVerilog to SystemC, and also takes care of the synchronization between the two languages. The VCS TLI provides a great ready-made solution for this particular use case. In the next blog post I will look at the VCS TLI support for various TLM modeling styles.

Share and Enjoy:
  • Print
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Google Buzz
  • LinkedIn
  • RSS
  • Twitter

Posted in SystemC/C/C++, Transaction Level Modeling (TLM) | 1 Comment »

1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading ... Loading ...

A RAL example with Designware VIP

Posted by S. Varun on March 17th, 2011

I often get asked how best RAL ought to be used with Designware VIP. Since several of these VIPs provide a mechanism to
program registers across different DUTs, I felt it would be useful to create an example with Designware AMBA AHB VIP and
RAL. 

The example has a structure as shown in the block diagram below,

     ---------------------------------------------
    |                                             |
    |         Register Abstraction Layer          |
    |                                             |
     ---------------------------------------------
       |
  ----------------
 |                |
 |    RAL2AHB     |
 |  ------------  |     ------------------       -----------      -----------
 | | AHB MASTER | |----| HDL Interconnect |-----| AHB SLAVE |----| Resp Gen  |
 |  ------------  |     ------------------       -----------      -----------
 |                |
  ----------------


It uses a dummy HDL interconnect that has two AHB interfaces to connect a VIP AHB master component with a VIP AHB slave
component. I have created a dummy register specification in "ahb_advanced_ral_slave.ralf". 

This example lacks a real AHB based DUT with real registers and hence the AHB slave VIP components' internal memory is
used to model the register space of the system. The RALF specification is then used to generate the System Verilog RAL
model using the "ralgen" utility as shown by the command-line below.

% ralgen -l sv -t ahb_advanced_slave ahb_advanced_ral_slave.ralf -c b -c a -c f

The above command will dump an SV based RAL model in a file named "ral_ahb_advanced_slave.sv". This model is instantiated
within the top-level environment class, which by the way is an extension of the "vmm_ral_env" class. You may already be
aware that a "vmm_ral_env" based environment has to be used for RAL verification. Once instantiated it is registered using
vmm_ral_access::set_model() method. This would complete the RAL model instantiation and registration leaving only the
translation logic which is the crux of the task.

Note: "vmm_ral_env" is extended from "vmm_env". Check the RAL userguide to see the additional members.

In the block diagram above, the block RAL2AHB is the block that translates a generic RAL access in to a command, in this
case  the command being an AHB transfer. The functional logic that translates generic READ/WRITE into an AHB master
transfer is within the "vmm_rw_xactor::execute_single()". The code snippet below shows how the translation is done.

// --------------------------------------------------------------------
task ral2ahb_xlate::execute_single(vmm_rw_access tr);
  ...

  // The generic read/write being translated into a AHB transfer.
  ahb_xact_fact.data_id        = tr.data_id;
  ahb_xact_fact.scenario_id    = tr.scenario_id;
  ahb_xact_fact.stream_id      = tr.stream_id;

  // Copying over the data width from the system configuration
  ahb_xact_fact.m_enHdataWidth = vip_cfg.m_oSystemCfg.m_enHdataWidth;

  // Setting the burst type to SINGLE & number of beats to 1
  ahb_xact_fact.m_enBurstType  = dw_vip_ahb_transaction::SINGLE;
  ahb_xact_fact.m_nNumBeats    = 1;

  // Copying the address generated by RAL into the AHB address of the AHB transfer
  ahb_xact_fact.m_bvAddress    = tr.addr;

  // Copying the size information over to the AHB transaction. RAL provides
  // the size in terms of bits and the dw_vip_ahb_master_transaction class takes
  // it in terms of bytes.
  ahb_xact_fact.m_nNumBytes    = tr.n_bits/8;
  ahb_xact_fact.m_enXferSize   = dw_vip_ahb_transaction::xfer_size_enum'(func_log(tr.n_bits) - 3);
  ahb_xact_fact.m_bvvData      = new[ahb_xact_fact.m_nNumBytes];

  // Setting the transfer type WRITE/READ based on the kind value generated by RAL
  if (tr.kind == vmm_rw::WRITE) begin
    ahb_xact_fact.m_enXactType = dw_vip_ahb_transaction::WRITE;
  end
  else begin
    ahb_xact_fact.m_enXactType = dw_vip_ahb_transaction::READ;
  end

  // Unpacking the RAL write data element into the byte sized data queue available within
  // dw_vip_ahb_master_transaction class
  if(tr.kind == vmm_rw::WRITE) begin
    for (int i = 0; i < tr.n_bits/8; j++) begin
       ahb_xact_fact.m_bvvData[i] = tr.data >> 8*i;
    end
  end

  // Put the AHB transaction object into the AHB master's input channel
  ahb_xactor.m_oTransactionInputChan.put(xact);

  // Wait for the transfer to END
  xact.notify.wait_for(vmm_data::ENDED);

  // Pack the READ data from the AHB transfer back into the RAL transactions data
  // member
  if (tr.kind == vmm_rw::READ) begin
     int i;
     tr.data = 0;
     for (i = 0; i < xact.m_nNumBytes; i++) begin
        tr.data += xact.m_bvvData[i] << 8*i;
     end
  end

  // Collecting the status of the transfer and returning it to RAL
  if (xact.m_nvRespLast[0] == 0)
     tr.status = vmm_rw::IS_OK;
  else
     tr.status  = vmm_rw::ERROR;

endtask: execute_single
// --------------------------------------------------------------------

The created translator class also has to be instantiated within the environment class and has to be registered using
"vmm_rw_access::add_xactor()" as shown below,

// --------------------------------------------------------------------
class ahb_advanced_ral_env extends vmm_ral_env ;

  ral2ahb_xlate    ral_to_ahb;

    ...

  virtual function void build() ;
  begin
    super.build();
    ral_to_ahb      = new("AHB RAL MASTER XACTOR", master_mp, cfg.cfg_master);

    this.ral.add_xactor(ral_to_abb);
  end
  endfunction

endclass
// --------------------------------------------------------------------

At this juncture, we are set to run the RAL tests and easily program the registers using the Abstarcted model
with simple APIs as shown below;

     env.ral_model.slave_block.REGA.set( ... );
     env.ral_model.slave_block.REGB.set( ... );
     env.ral_model.update();

The above section shows how to setup for single transfers. For burst transfer, there is a slight variation
wherein you provide the burst translation within the vmm_rw_xactor::execute_burst() task. The code snippet
below shows this.

// --------------------------------------------------------------------
task ral2ahb_xlate::execute_burst(vmm_rw_burst tr);
 ...

  ahb_xact_fact.data_id        = tr.data_id;
  ahb_xact_fact.scenario_id    = tr.scenario_id;
  ahb_xact_fact.stream_id      = tr.stream_id;
  ahb_xact_fact.m_enHdataWidth = vip_cfg.m_oSystemCfg.m_enHdataWidth;
  ahb_xact_fact.m_nNumBeats    = tr.n_beats;
  ahb_xact_fact.m_bvAddress    = tr.addr;
  ahb_xact_fact.m_nNumBytes    = tr.n_bits/8*tr.n_beats;
  ahb_xact_fact.m_enXferSize   = dw_vip_ahb_transaction::xfer_size_enum'(func_log(tr.n_bits) - 3);

  if (tr.kind == vmm_rw::WRITE) begin
    ahb_xact_fact.m_bvvData    = new[ahb_xact_fact.m_nNumBytes];
    ahb_xact_fact.m_enXactType = dw_vip_ahb_transaction::WRITE;
  end
  else begin
    ahb_xact_fact.m_enXactType = dw_vip_ahb_transaction::READ;
  end

  case(tr.n_beats)
    4  : begin
               ahb_xact_fact.m_enBurstType = dw_vip_ahb_transaction::INCR4;
         end
    8  : begin
               ahb_xact_fact.m_enBurstType = dw_vip_ahb_transaction::INCR8;
         end
    16 : begin
               ahb_xact_fact.m_enBurstType = dw_vip_ahb_transaction::INCR16;
         end
    default : begin
               ahb_xact_fact.m_enBurstType = dw_vip_ahb_transaction::INCR;
              end
  endcase

  // Write cycle
  if(tr.kind == vmm_rw::WRITE) begin
     for(int i=0; i<tr.n_beats; i++) begin
       for(int j = 0; < tr.n_bits/8; j++) begin
         ahb_xact_fact.m_bvvData[i*(tr.n_bits/8) + j] = tr.data[i] >> 8*j;
       end
     end
  end

  // Put the AHB burst transaction into the AHB master's input channel
 ahb_xactor.m_oTransactionInputChan.put(xact);

 // Read cycle
 if(tr.kind == vmm_rw::READ) begin
     tr.data = new[tr.n_beats];
     for(int i=0; i<tr.n_beats; i++) begin
        tr.data[i] = 0;
        for(int j = 0; j<tr.n_bits/8; j++) begin
           tr.data[i] += xact.m_bvvData[i*tr.n_bits/8 +  j] <<8*j;
        end
     end
 end

  // Collecting the status of the transfer and returning it to RAL
  if (xact.m_nvRespLast[0] == 0)
     tr.status = vmm_rw::IS_OK;
  else
     tr.status  = vmm_rw::ERROR;

endtask: execute_burst
// --------------------------------------------------------------------

For invoking burst transfers in RAL, the vmm_ral_access::burst_write() & vmm_ral_access::burst_read() tasks
have to be used as shown below;

     env.ral.burst_write(status, 8'h00, 4, 8'h0f, exp_data, , 32);
     env.ral.burst_read(status, 8'h00, 4, 8'h0f, 4, act_data, , 32);

The complete example is downloadable from solvnet "tb_ahb_vmm_10_advanced_ral_sys.tar.gz". You will need DesignWare
licenses to compile and run the example. Please follow the instructions in the README to compile and run this example.
The example can be used as a reference for creating a RAL environment with other Designware VIP titles as well.
Do write to me if you have any questions on the example.
Share and Enjoy:
  • Print
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Google Buzz
  • LinkedIn
  • RSS
  • Twitter

Posted in Register Abstraction Model with RAL, VMM | 1 Comment »

1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading ... Loading ...

Transaction Debugging with Discovery Visualization Environment (DVE) Part-2

Posted by JL Gray on March 8th, 2011

Asif Jafri, Verilab Inc.

In my previous blog post, I introduced how to dump waves and how to use $tblog for dynamic data and message recording. If you need more control over scope sensitive transaction debugging, $msglog task is very useful. This blog has been divided into two sections: in the the first section, I talk about how to use $msglog. In the second section, I will discuss how VMM performs transaction recording by calling $msglog from within the VMM library. The call is protected so as not to confuse other simulators or tools. You can use $msglog in any of your code as well.

•    The advantage of using $msglog is that we have more control over the debug messaging. If a transaction can be divided into start and finish, it is possible to identify cause and effect.
•    Parent and child relationship can be shown
•    Identify execution stream with start and end time.

The following steps need to be followed to invoke $msglog.

Include msglog.svh in testbench code
Add +incdir+${VCS_HOME}/include in the compile line

1) The example below shows how to call the $msglog task in the testbench. The first msglog statement creates a transaction (read) on a stream (stream1) which has an attribute addr. It also sets the header text (RD) and body text (text 1). This statement can be placed in a read task of your transactor.  The second msglog statement once again can be placed in the read task and it shows when the read completes. Streams are global and do not need to be created explicitly. They are created implicitly as they are needed.

$msglog (“stream1”, XACTION, “read”, NORMAL, “RD”, “text 1”, START, addr);

clip_image001[19]

$msglog (“stream1”, XACTION, “read”, NORMAL, “”, FINISH);

The table below shows the various possible parameters for the type, severity and relation field in the $msglog task:

Type

Severity

Relation

FAILURE

FATAL

START

NOTE

ERROR

FINISH

DEBUG

WARNING

PRED

REPORT

NORMAL

SUCC

NOTIFY

TRACE

SUB

TIMING

DEBUG

PARENT

XHANDLING

VERBOSE

CHILD

XACTION

HIDDEN

XTEND

PROTOCOL

IGNORE

USER

COMMAND

CYCLE

As shown above you can also place $msglog tasks in the response task of the responding transactor if the transaction needs to be followed into the response transactor.

$msglog(“stream1″, XACTION, “resp”, NORMAL, “RESP”, START, data);

2) VMM provides build-in transaction recording. To enable it use “+define+VMM_TR_RECORD” when compiling your code. At simulation runtime, recording of transactions is controlled by setting “+vmm_tr_verbosity=debug” in the command line.
The following VMM base classes have build-in recording support:
vmm_channel, vmm_voter, vmm_env, vmm_subenv, vmm_timeline

The figure below shows an example of the recorded transactions as viewed in the waveform viewer:

image


You can also do your own transaction recording by using the following VMM functions:

vmm_tr_record::open_stream
vmm_tr_record::open_sub_stream
vmm_tr_record::close_stream
vmm_tr_record::start_tr
vmm_tr_record::extend_tr
vmm_tr_record::end_tr


e.g.
mystream = vmm_tr_record::open_stream(get_instance(), “MyChannel”);

vmm_tr_record::start_tr(mystream, “Read”, “Text line 1\nText line 2”);

vmm_tr_record::end_tr(mystream);

vmm_tr_record::close_stream(mystream);


As shown in the two part blogs on transaction debugging, $tblog and $msglog can be very useful transaction debugging constructs. You can choose to dump transactions and follow them through the environment, dump channel data, notification ID, phase names etc. To be able to see all this information on the waveform viewer has been a blessing for me.  I hope it is helpful to you.

Share and Enjoy:
  • Print
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Google Buzz
  • LinkedIn
  • RSS
  • Twitter

Posted in Debug, VMM infrastructure | No Comments »

1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading ... Loading ...

Transaction Debugging with Discovery Visualization Environment (DVE) Part-1

Posted by JL Gray on February 25th, 2011

Asif Jafri, Verilab Inc., Austin, TX

The art of verification has evolved dramatically over the last decade. What used to be a very simple verilog testbench which could not possibly cover the vast solution space has evolved into the current monstrosity (Random testbenches) which is a very powerful tool, but the complexity to debug has gone up exponentially.

VMM has introduced various debug constructs to aid in the debug of the design as well as the test environment such as:
•    Messaging: Report regular, debug, or error information.
•    Recording: Transaction and components have built-in recording facilities that enable transaction and environment debugging.

Today I want to spend some time looking at DVE as a powerful debug tool in our tool box.

To start things off lets look at some simple calls used to invoke dumping waves.

1) $vcdpluson() : This call is used to start dumping design signals into a .vpd (VCD plus) format. “vpd” is a proprietary Synopsys format (binary, highly compressed) that is generated by vcs, which solves the issue of generating excessively large .vcd (IEEE standard) format files.

Eg:
initial
    begin
        $vcdpluson();
    end

When compiling, specify -debug_pp (for post process debug), -debug (for interactive debug), -debug_all (for interactive debug with line stepping and breakpoints) to enable VCS Dumping.

The code snippets shown above will generate waves of all design signals for viewing in the DVE waveform viewer. You can also use the UCLI (unified command line interface) command ‘dump’ for dumping design signals interactively or in scripts.

Won’t it be great if we can also view dynamic variables as waveforms?

2) $tblog() system task is used for recording dynamic (or static) data and simple messages.  No additional environment setup is required. $tblog() has to be called in the testbench where you want to record a message or a variable. The next example shows how to record a message in the send_packet task of a transactor.

// Foo transactor
task send_packet();
    int id; // local variable
    …
    $tblog(-1, “Sending packet”); // record all local and class variables
    …
    cnt = cnt + 1; // cnt is a class variable
    if (cnt < 50)
       $tblog(0, “Count is less than 50”, cnt, id); // record variable cnt and id

endtask: send_packet

Along with the message and variable values, $tblog automatically records the time and the call stack. To view these messages and variables in the waveform viewer select a recording from transaction browser and add it as a waveform.
The figure below shows how a message is displayed in a DVE waveform window.

image

Another useful tool for transaction debugging is using the $msglog task which will be discussed in the next article “Hyperlink….”.

Share and Enjoy:
  • Print
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Google Buzz
  • LinkedIn
  • RSS
  • Twitter

Posted in Debug, VMM infrastructure | No Comments »

1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading ... Loading ...

Register Programming using RAL package

Posted by Vidyashankar Ramaswamy on February 15th, 2011

There are many methods available to program (read/write) registers in a design using RAL.

1.    ral_model::read()/write(): This is the old fashion method where you specify the address and the data. No need to know the register by name.

              EX:    ral_model.ral.read(status, addr, data, . . .);

 2.    ral_model::read_by_name()/write_by_name(): You have to specify the register name and data to execute this method. Here the register name is hard-coded.

              EX: ral_model.read_by_name(status, “reg_name”, data, . . .);

 3.    ral_reg::read()/write(): Here you have to specify the hierarchical path to the register to execute the task. Only value needs to be specified.

             EX:   ral_model.block_name.reg1.read(status,data);

 Option 1 works fine at all levels of the test bench — block, core or system – if a portable addressing scheme is used for register programming. The only issue is that it is not self documented. For instance, a sequence of writes/reads to program a DDR memory controller makes it hard to understand and debug the code when the controller does not behave as expected.

Option 2, uses the register name to perform the read/write process. This code is self documented and works great in the block level test bench. However, this option breaks when used at the core or system level. Why? Let us analyze the following situation:

Unit level test bench is using read_by_name()/write_by_name().  This works as the register names are unique. However, when multiple instances of the same block  exist at the system level, multiple registers exist with the same name, thus creating name conflict. To ensure that the name is properly scoped and that the same scope is used from block to top, read_by_name()/write_by_name() should not be used as it uses a flat name space.  To reuse the same code across different levels of test bench, one should use option 3 which is scalable.  The following code segment demonstrates this concept:

task init_ddr_controller (vmm_ral_block ddr_block);

     vmm_ral_reg  reg_in_use;

     reg_in_use = ddr_block.mode_reg_0;

     reg_in_use.write(…);

      . . .

endtask 

Note: You can  use,  reg_in_use = ddr_block.get_reg_by_name(“reg_name”); if the task is written to take in register name as well.

Then in core/integration or in SoC  level:

   init_ddr_controller(system.blk1);
   init_ddr_controller (system.blk2);

Please do share your comments/ideas on this.

Share and Enjoy:
  • Print
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Google Buzz
  • LinkedIn
  • RSS
  • Twitter

Posted in Register Abstraction Model with RAL | 1 Comment »

1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading ... Loading ...

Migrating Legacy File-Based Testbenches to VMM

Posted by JL Gray on February 9th, 2011

Scott Roland, Verilab Inc, Austin, TX

As a verification engineer, it is common to be given a design to test that is based on earlier design. Presumably, that existing design also comes with a proven verification environment and suite of tests. Unfortunately, the legacy verification environment might be rather rudimentary. If we are going to create a modern, VMM-based, verification environment for the new design, then what can and should be done with the existing “simple” tests? As Joel Spolsky once said, “the single worst strategic mistake [is deciding] to rewrite the code from scratch.”

Verification reuse is just as valuable as design reuse. This can be true even if the legacy tests are a set of text command files that are read in at runtime and perform “dumb” directed testing. If the original tests are of good quality, then they will still cover important functionality of the design. They might also tests critical corner cases or problems that were seen during the initial development of the design being reused. In addition, reusing existing directed tests could help you achieve some early testing of your device faster than if you had started your verification effort from scratch.

The first way you can leverage a legacy testbench is to reuse some of the code responsible for stimulating and monitoring the DUT. As you build the new VMM environment you can properly encapsulate the existing code into the relevant VMM transactors.

Next, it would be nice to reuse the original tests themselves. The tests could be a set of tasks calls or a text file containing commands, as mentioned before. You could translate each test individually into a proper VMM test that generates transaction directly, but it would be better to create an adaption layer between the original tests and the new environment. That would obviate the need to modify the tests and allow the VMM environment to also handle new tests written for the old testbench.

To illustrate an example of using a file-based test in a VMM environment, I chose to extend the memsys_cntrlr example that is distributed with VMM release 1.2.1 in the directory sv/examples/std_lib. The example contains a number of scenarios that are implemented as extensions of the VMM Multi-stream Scenario class. I want to create an additional scenario that reads in a command file and generates transaction based on the file. First, assume that my command file contains lines that specify the command, address and data:

WRITE 8888_8888 2A
READ  2222_2222 24
WRITE 3333_3333 1A
READ  5555_5555 81
READ  7777_7777 42

Using the existing cpu_directed_scenario as a template, I created a new cpu_filebased_scenario. The execute() task, that is responsible for defining what the scenario does, takes care of reading in the test file and calling the proper write/read tasks based on the individual commands. Since the original command file specified the expected return value of every read command, the read task checks the actual return value against the given expected value. Eventually, you might create a reference model that would enable the VMM environment to predict the expected read values. Implementing the directed check in the scenario enables you to run the legacy tests before a reference model is completed and later validate the initial tests and reference model against each other. Here is the implementation of the scenario:

/// Scenario that executes commands read from a directed test file.
class cpu_filebased_scenario extends cpu_rand_scenario;
…
  /// Overloaded version of vmm_ms_scenario::execute(). Body of our scenario.
  /// Reads each line in the file and performs the specified action.
  task execute(ref int n);
    integer      fileID;
    bit [8*5:1]  cmd_str;
    bit   [7:0]  data;
    bit  [31:0]  addr;
    if (chan == null) chan = get_channel("cpu_chan");
    fileID = $fopen("test.file", "r" );
    // Lines look like: "CMD ADDRESS DATA"
    while ($fscanf(fileID, "%s %h %h", cmd_str, addr, data) != -1) begin
      unique case (cmd_str)
        "WRITE": this.write(addr, data);
        "READ" : this.read (addr, data);
        default: `vmm_error(this.log, $psprintf("Unknown command %s", cmd_str));
      endcase
      n += 1;
    end
    $fclose(fileID);
  endtask

  /// Send a write transaction for the given address and data.
  task write(input bit [31:0]  addr,
             input bit [ 7:0]  data);
    cpu_trans  tr = new();
    tr.randomize() with {tr.address == addr; tr.kind == WRITE;tr.data == data;};
    chan.put(tr);
  endtask

  /// Send a read transaction for the given address and check for the given expected data.
  task read(input bit [31:0]  addr,
            input bit [ 7:0]  exp_data);
    cpu_trans  tr = new();
    tr.randomize() with {tr.address == addr; tr.kind == READ;};
    chan.put(tr);
    if (tr.data !== exp_data) begin
      `vmm_error(this.log, $psprintf("READ(A:%X, D:%X) did not match expected:%X",
                                     addr, tr.data, exp_data));
    end
  endtask
endclass

After defining the scenario class, I created an extension of vmm_test that tells the VMM factory to use the file-based scenario for this test and run it once. The VMM architecture and factory makes it possible to run the legacy tests just like any randomized VMM tests. Plus, it does not require modifying any other component in the environment. Here is the implementation of the test class:

class test_filebased extends vmm_test;
…
  function void configure_test_ph();
    // Tell the factory which scenario class to use for this test.

    cpu_rand_scenario::override_with_new("@%*:CPU:rand_scn",

          cpu_filebased_scenario::this_type(), log, `__FILE__, `__LINE__);
  endfunction

  function void build_ph();
    // Run the scenario only once.
    vmm_opts::set_int("%*:num_scenarios", 1);
  endfunction
endclass

Since the directed test file is read into a new environment, it stands to reason that the driver in the new environment could act differently than the one in the legacy environment. For example, the driver might try to combine multiple transactions into bursts or reorder them. While this should probably be done in a specific scenario in the VMM environment, not the driver, you should compare the final stimulus that is performed on the DUT between the two environments. Only after you have done such an assessment can you say that the testcase is being reused for it’s original intent.

Once you have the legacy tests running in a modern VMM environment, you can enhance the environment to have randomization, self-checking and functional coverage. You can analyze the existing tests with a coverage model to determine what new tests you need to write to verify functionality that was initially missed or has been added or modified. The coverage model can also tell you which legacy tests duplicate functionality in other tests, providing justification for getting rid of legacy tests and giving confidence in the quality of your VMM environment.

Share and Enjoy:
  • Print
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Google Buzz
  • LinkedIn
  • RSS
  • Twitter

Posted in VMM | 3 Comments »

1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading ... Loading ...

Performance appraisal time – Getting the analyzer to give more feedback

Posted by Amit Sharma on January 28th, 2011

S. Prashanth, Verification & Design Engineer, LSI Logic

Performance appraisal time – Getting the analyzer to give more feedback

We wanted to use the VMM performance analyzer to analyze the performance of the bus matrix we are verifying. To begin with, we wanted these information while accessing a shared resources (slave memory).

· Throughput/Effective Bandwidth for each master in terms of Mbytes/sec

· Worst case latency for each master

· Initiator and Target information associated with every transaction

By default, the performance analyzer records the initiator id, target id, start time and end time of each tenure (associated with a corresponding transaction) in the SQL data base. In addition to the useful information provided by the Performance Analyzer, we needed the number of bytes transferred for each transaction to be dumped in the SQL data base. This was required for calculating throughput which in our case was the number of bytes transferred from the start time of the first tenure until the end time of the last tenure of a master. Given that we had a complex interconnect with 17 initiators, it was difficult for us to correlate an initiator id with their names. So we wanted to add initiator names as well in the SQL data base. Let’s see how these information can be added from the environment.

An earlier blog on performance analyzer “Performance and Statistical analysis from HDL simulations using the VMM Performance Analyzer”  provides useful information on how to use VMM performance analyzer in verification environment. Now, starting with that, let me outline the additional steps we took for getting the statistical analysis we desired

Step 1: Define the fields and their data types required to be added to the data base in a string (user_fields). i.e., “MasterName VARCHAR(255)” for initiator name and “NumBytes SMALLINT” for number of bytes. Provide this string to the performance analyzer instance during initialization.

class tb_env extends vmm_env;
vmm_sql_db_sqlite db; //Sqlite data base
vmm_perf_analyzer bus_perf;
string user_fields;
virtual function void build();
super.build();
db = new(“perf_data”); //Initializing the data base
user_fields = “MasterName VARCHAR(255), NumBytes SMALLINT”;
bus_perf = new(“BusPerfAnalyzer”, db, , , , user_fields);
endfunction
endclass

Step 2: When each transaction ends, get information about the initator name and the number of bytes transferred in a string variable (user_values) . Then provide the variable to the performance analyzer through the end_tenure() method.

fork begin

vmm_perf_tenure perf_tenure = new(initiator_id, target_id, txn);

string user_values;

bus_perf.start_tenure(perf_tenure);

txn.notify.wait_for(vmm_data::ENDED);

user_values = $psprintf(“%s, %0d”, initiator.get_object_name(), txn.get_num_bytes());

bus_perf.end_tenure(perf_tenure, user_values);

end

join_none




With this, the performance analyzer dumps the additional user information in an SQL data base. The blog “Analyzing results of Performance Analyzer with Excel”  explains how to extract information from the SQL database generated. Using the spreadsheet, we could create our own plots and ensure that  management has all the analysis it needs to provide the perfect appraisal.

Share and Enjoy:
  • Print
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Google Buzz
  • LinkedIn
  • RSS
  • Twitter

Posted in Optimization/Performance, Performance Analyzer, Verification Planning & Management | 1 Comment »

1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading ... Loading ...

Managing coverage grading in complex multicore microprocessor environments

Posted by Shankar Hemmady on January 26th, 2011

Something as simple as coverage grading, which we often take for granted, starts showing its exponential complexity when dealing with cutting-edge designs where quality and timeliness are essential. 

An article in EE Times by James Young and Michael Sanders of AMD along with Paul Graykowski and Vernon Lee of Synopsys describes how they created a coverage grading solution, Quickgrade, that scales to meet the complexity of multicore multiprocessor design environments:

http://www.eetimes.com/design/eda-design/4212292/Managing-coverage-grading-in-complex-multicore-microprocessor-environments?pageNumber=0

 

Share and Enjoy:
  • Print
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Google Buzz
  • LinkedIn
  • RSS
  • Twitter

Posted in Coverage, Metrics | No Comments »

1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading ... Loading ...

Cool Things You Can Do With DVE – The Videos

Posted by Yaron Ilani on January 19th, 2011

DVE is now on YouTube !! Here’s a collection of short videos demonstrating some the coolest features in DVE that you need to know about. If you wish to learn more, check out the the recent blog articles. Enjoy!

Debugging UVM Sequences

Searching & Cool GUI tips

Interactive Rewind

Debugging SystemVerilog Macros

Debugging FSMs & The Grid

Debugging Your Source Code

Debugging SystemVerilog Assertions (SVA)

Tracing Drivers and Active Driver

More videos coming up soon…

Share and Enjoy:
  • Print
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Google Buzz
  • LinkedIn
  • RSS
  • Twitter

Posted in Debug | 1 Comment »

1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading ... Loading ...

Verification in the trenches: Transform your sc_module into a vmm_xactor

Posted by Ambar Sarkar on January 19th, 2011

Dr. Ambar Sarkar, Chief Verification Technologist, Paradigm Works Inc.

Say you have SystemC VIP lying around, tried and true. More likely than not, they are BFMs that connect at the signal level to the DUT and have a procedural API supporting transaction level abstraction.

What would be the best way to hook these components up with a VMM environment? With VMM now being available in SystemC as well, you really want to make these models look and behave as vmm_xactor derived objects that interact seamlessly across the SystemC/SystemVerilog language boundary. Your VMM environment can thus take full advantage of your existing SystemC components. And your sc_module can still be used, just as before, in other non VMM environments!

Enough motivation. Can this be done? Since Syst
emC is really C++, and it supports multiple inheritance, is there a way to just create a class that inherits from both your SystemC component as well vmm_xactor?

Here is an example..

Originally, suppose you had a consumer bfm defined(keeping the example simple for illustration purposes).

   1:  SC_MODULE(consumer) {
   2:    sc_out<sc_logic>   reset;
   3:    sc_out<sc_lv<32> > sample;
   4:    
   5:    sc_in_clk    clk;
   6:      SC_CTOR(consumer_wrapper):    clk("clk"),    reset("reset"),   sample("sample") {
   7:    }
   8:    
   9:    . . .  
  10:  };

Solution Attempt 1) The first thing to try would be to simply create a new class called consumer_vmm as follows and define the required vmm_xactor methods.

   1:  class consumer_vmm : public consumer, public vmm_xactor 
   2:  {
   3:    consumer(vmm_object* parent, sc_module_name _nm) 
   4:           : vmm_xactor(_nm,"consumer",0,parent)
   5:              ,reset("reset") 
   6:              ,sample("sample") 
   7:              ,clk("clk")   
   8:       {   
   9:           SC_METHOD(entry);
  10:           sensitive << clk.pos();
  11:          . . .
  13:        
  14:       }
  15:      . . . define the remaining vmm_xactor methods as needed . . .
  16:  };
Unfortunately, this does not work. Reason? As it turns out, vmm_xactor also inherits from sc_module.So consumer_vmm will end up inheriting same sc_module through two separate classes, the consumer and the vmm_xactor. This is known as the Diamond Problem.  Check out for some fun reading 
at http://en.wikipedia.org/wiki/Diamond_problem. 
 
Okay, so what can be done? Well, luckily, we can get all of this to work reasonably well with some additional tweaks/steps. Yes, you will need to very slightly modify the original source code, but in a backward compatible way. 
 

Solution Attempt 2) Make the original consumer class  derive from vmm_xactor instead of sc_module. This is the only change to existing code, and this will be backward compatible since vmm_xactor inherits from sc_module as well. Of course, add any further vmm_xactor:: derived methods using the old api as needed.

   1:  class consumer: public vmm_xactor
   2:  {
   3:   public:
   4:    sc_out<sc_logic>   reset;
   5:    sc_out<sc_lv<32> > sample;
   6:    sc_in_clk    clk;
   7:    . . . 
   8:  }
 
 
Solution) Here are all the steps. It looks like quite a few steps, but other than creating the 
wrappers and hooking them, the rest of the steps remain the same regardless of whether you use 
the sc_module or the vmm_xactor. 

Step 1. Make the original consumer class  derive from vmm_xactor instead of sc_module. This is the only change to existing code, and this will be backward compatible since vmm_xactor inherits from sc_module as well. Of course, add any further vmm_xactor:: derived methods using the old api as needed.

   1:  class consumer: public vmm_xactor
   2:  {
   3:   public:
   4:    sc_out<sc_logic>   reset;
   5:    sc_out<sc_lv<32> > sample;
   6:    sc_in_clk    clk;
   7:    . . . 
   8:  }

step 2. define sc_module(consumer_wrapper) declare class that has the same set of pins as needed by consumer.

   1:  sc_module(consumer_wrapper) {
   2:    sc_out<sc_logic>   reset;
   3:    sc_out<sc_lv<32> > sample;
   4:    sc_in_clk    clk;
   5:    
   6:    sc_ctor(consumer_wrapper):    clk("clk"),    reset("reset"),   sample("sample") {
   7:    }
   8:      
   9:  };
 
step 3. declare pointers to instances(not instances)  to these wrappers in env class
   1:  class env: public vmm_group
   2:  {
   3:  public:
   4:     consumer *consumer_inst0;
   5:     consumer *consumer_inst1;
   6:     consumer_wrapper *wrapper0, *wrapper1;
   7:   . . .
   8:  }

step 4. in the connect_ph phase, connect the pins of consumer instances and the corresponding wrappers instances

   1:  virtual void env::connect_ph() {
   2:      consumer_inst0->reset(wrapper0->reset);
   3:      consumer_inst0->clk(wrapper0->clk);
   4:      consumer_inst0->sample(wrapper0->sample);
   5:   
   6:      consumer_inst1->reset(wrapper1->reset);
   7:      consumer_inst1->clk(wrapper1->clk);
   8:      consumer_inst1->sample(wrapper1->sample);
   9:  }
  10:     

Step 5. In the constructor for sc_top, after the  vmmm_env instance is created, make sure the pointers in the env point to the these wrappers

   1:  class sc_top : public sc_module
   2:  {
   3:  public: 
   4:    
   5:    vmm_timeline*  t1;
   6:    env*           e1;
   7:   
   8:    sc_out<sc_logic>   reset0;
   9:    sc_out<sc_lv<32> > sample0;
  10:    sc_in_clk    clk;
  11:   
  12:    sc_out<sc_logic>   reset1;
  13:    sc_out<sc_lv<32> > sample1;
  14:      
  15:    consumer_wrapper wrapper0;
  16:    consumer_wrapper wrapper1;
  17:   
  18:    SC_CTOR(sc_top):
  19:      wrapper0("wrapper0")
  20:      ,wrapper1("wrapper1")
  21:      ,reset0("reset0")
  22:      ,sample0("sample0")
  23:      ,reset1("reset1")
  24:      ,sample1("sample1")
  25:      ,clk("clk")
  26:     {
  27:        t1 = new vmm_timeline("timeline","t1");
  28:        e1 = new env("env","e1",t1);
  29:   
  30:        e1->wrapper0 = &wrapper0;
  31:        e1->wrapper1 = &wrapper1;
  32:   
  33:        vmm_simulation::run_tests();
  34:   
  35:        wrapper0.clk(clk);
  36:        wrapper0.reset(reset0);
  37:        wrapper0.sample(sample0);
  38:   
  39:        wrapper1.clk(clk);
  40:        wrapper1.reset(reset1);
  41:        wrapper1.sample(sample1);
  42:   
  43:     }
  44:   
  45:  };
 
 
So while it looks like a few more than we had hoped, you do it only once, and mechanically. Small price to pay for reuse. Maybe someone can create a simple script. 


 

Also, contact me if you want the complete example. The example also shows how you can add tlm ports as well.
 

This article is the 10th in the Verification in the trenches series. Hope you found this article useful. If you would like to hear about any other related topic, please comment or drop me a line at ambar.sarkar@paradigm-works.com. Also, if you are starting out fresh, please check out the free VMM1.2 environment generator at http://resourceworks.paradigm-works.com/svftg/vmm .

 
Share and Enjoy:
  • Print
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Google Buzz
  • LinkedIn
  • RSS
  • Twitter

Posted in Interoperability, SystemC/C/C++, VMM | 1 Comment »

1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading ... Loading ...

Channels versus Non-blocking Transport in VMM 1.2

Posted by John Aynsley on January 17th, 2011

 

John Aynsley, CTO, Doulos

The original VMM book recommended ways of using the VMM channel to model non-atomic, pipelined, and out-of-order transaction execution. The TLM-2.0 standard addresses the same issues in a very different way. Now that TLM-2.0-like features are part of VMM and the Accellera Verification IP Technical Committee is looking at how to incorporate similar features into UVM, I thought it might be worthwhile to compare and contrast the two approaches. Choice is a good thing as long as you are properly equipped to understand the choices on offer.

Both the VMM channel and the TLM-2.0 standard address non-atomic and pipelined transactions using a non-blocking completion model. Whenever a producer sends out a transaction, control is immediately returned to the producer so that it has the option to send further transactions while the first is still being processed downstream. With the VMM channel, in-flight transactions may be held in the channel until they are processed, or may be buffered within some transactor downstream of the channel. The VMM channel can become full or empty, and dealing with this behavior is a necessary part of the interface between transactors. With TLM-2.0 there is no channel: any buffering is part of the behavior of the transactors rather than being part of the communication interface.

With VMM, the automatic garbage collection of SystemVerilog takes care of re-using the transaction object when there are no remaining references. In the SystemC implementation of TLM-2.0 each transaction has an explicit memory manager which provides a hook to represent the lifetime of the transaction, and in-flight transactions continue to live until the protocol has played itself out and every component has relinquished access to the transaction object.

In VMM, a downstream transactor is expected to signal to an upstream transactor when a transaction is complete. All VMM transactions that extend the base class vmm_data have embedded within them VMM notifications vmm_data::STARTED and vmm_data::ENDED which can be used for this purpose (and there are other related notifications within the channel itself). Effectively, the VMM transaction carries with it synchronization objects which the transactors can agree to use to signal significant timing points during the lifetime of the transaction. Any status information that needs to be associated with these timing points can be either attached to the notifications or sent as separate objects through the channel.

With both VMM and TLM-2.0, transactors can track outstanding transactions by maintaining references to the transaction objects. With the TLM-2.0 standard, each transaction is represented by exactly one transaction object which holds all the attributes associated with the transaction and with any response. With VMM, it is possible to create separate response objects (each of which should include a reference to the original transaction) and send these back to the producer through a separate channel in the reverse direction.

The mechanisms used to model multiple timing points within the lifetime of a single transaction are very different between classic VMM and TLM-2.0. As we have seen above, VMM uses notifications embedded within the transaction object (and channel) and also allows separate response or status descriptors to be associated with a transaction. TLM-2.0, on the other hand, does not allow either of these approaches. With TLM-2.0, multiple timing points are represented by the phase, which is an argument to the non-blocking transport call and is not part of the transaction object. With TLM-2.0, a set a phases is chosen to model the timing points of any given protocol and those phases are annotated onto the non-blocking transport method calls in order to indicate the state of each hop, or point-to-point link between transactors. All communication between transactors is signaled by making explicit calls to nb_transport_fw or nb_transport_bw. All associated attributes and status information are part of the transaction object, and the value of the phase argument is used by each transactor to determine the kind of access it is allowed to the transaction object, that is, which attributes are valid and which attributes may be updated at that time.

So, we have seen that classic VMM and the TLM-2.0 standard address the issue of having multiple timing points within the lifetime of a transaction in rather different ways. The VMM channel is usually the preferred approach within native VMM testbenches, but the VMM user can now choose between using channels and using the TLM-2.0 standard when interfacing the external models.

Share and Enjoy:
  • Print
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Google Buzz
  • LinkedIn
  • RSS
  • Twitter

Posted in Transaction Level Modeling (TLM) | 1 Comment »

1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading ... Loading ...

The VMM TLM Reactive Interface

Posted by John Aynsley on January 10th, 2011

John Aynsley, CTO, Doulos

In an earlier post I showed how to bind a VMM channel to a new-style TLM port using the vmm_connect utility. This is useful if you already have old-style and new-style transactors to be connected together. I have also tried to emphasize that the VMM channel is still the preferred way to write slave transactors that need to work on a transaction while it remains in-flight between producer and consumer. However, the VMM channel mechanism could be regarded as being a bit heavyweight when compared with just passing transactions around using TLM ports and exports. The VMM TLM reactive interface plugs this gap by providing a simple, streamlined mechanism for use in consumer transactors that are to be driven from TLM ports and where you want to benefit from a mechanism similar to the active slot of the VMM channel.Unlike a TLM export, the reactive interface allows the consumer transactor to queue up incoming transactions and process them only when it is ready. This mechanism allows a consumer to be fed a stream of transactions generated by multiple producers simultaneously, and then to queue them up to be processed one-at-a-time. By choosing to use the blocking transport interface in each producer you can benefit from the simple completion model of blocking transport while at the same time having a consumer that is able to handle multiple outstanding transactions or pipelined transactions.

In order to set up an example showing the VMM TLM reactive interface in action, the first step is to have a producer that sends out transactions through either a blocking or a non-blocking transport port:

 

 

 

 

class producer extends vmm_xactor;
vmm_tlm_b_transport_port #(producer, vmm_tlm_generic_payload) m_port;

m_port.b_transport(tx, delay);


Then we need multiple such producers each connected to a single consumer that is to process the transaction stream:

class tb_env extends vmm_xactor;
producer  m_producer1;
producer  m_producer2;
producer  m_producer3;
consumer  m_consumer;

virtual function void connect_ph; 
m_consumer.reactive_export.tlm_bind(
    m_producer1.m_port, vmm_tlm::TLM_BLOCKING_EXPORT );
m_consumer.reactive_export.tlm_bind(
    m_producer2.m_port, vmm_tlm::TLM_BLOCKING_EXPORT );
m_consumer.reactive_export.tlm_bind(
    m_producer3.m_port, vmm_tlm::TLM_BLOCKING_EXPORT );
endfunction: connect_ph
 


Each of the three producers can generate and send transactions to the consumer, possibly in parallel, such that the consumer may have to handle multiple incoming transactions. Now we come to the meat of the example, the vmm_tlm_reactive_if itself, which is instantiated within the consumer:

class consumer extends vmm_xactor;
vmm_tlm_reactive_if #(vmm_tlm_generic_payload, 4) reactive_export;

virtual function void build_ph;
  super.build_ph();
  reactive_export = new( this, “reactive_export”);
endfunction: build_ph


As you can see, the reactive interface is parameterized with the type of the incoming transaction object as well as with the depth of the transaction queue, which in general should be made large enough so that it does not overflow. The consumer can attempt to grab the next incoming transaction from the queue that is implicit in the reactive interface:

 

reactive_export.get(tx);


The get method is blocking, so will only return when an incoming transaction is available. The consumer may then spend some time processing the transaction, and must signal back to the producer when it has finished:

#10 … do stuff
reactive_export.completed();


Only at this point is control returned from the corresponding b_transport call in one of the three producers. The consumer can then loop back and attempt to get the next transaction.If the reactive interface queue happens to be full at the time b_transport is called, the response status within the generic payload transaction will be set to TLM_INCOMPLETE_RESPONSE and b_transport will return immediately. In terms of the TLM-2.0 standard this is a bad thing because it means that the transaction has failed, so it is better to make the queue sufficiently large that it cannot overflow.

If the queue happens to be empty at the time the consumer calls get, the call will block indefinitely. If the consumer needs to service incoming transactions on other interfaces, an alternative coding style would be to have the consumer use the non-blocking method try_get, as follows:

 

tx = reactive_export.try_get();
while (tx == null)
begin
   `vmm_note(log, “…consumer is idling…”);
   #1;
   tx = reactive_export.try_get();
end


So, in summary, we have connected multiple producers that each have a regular TLM-style port to a consumer that has a reactive interface. The reactive interface allows the consumer to poll for incoming transactions and process them one-at-a-time rather than being forced to respond immediately to each incoming transaction as would have been the case when using the blocking transport interface.

Share and Enjoy:
  • Print
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Google Buzz
  • LinkedIn
  • RSS
  • Twitter

Posted in Transaction Level Modeling (TLM) | 2 Comments »

1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading ... Loading ...

Verification in the trenches: A SystemC implementation of VMM1.2

Posted by Ambar Sarkar on December 16th, 2010

Dr. Ambar Sarkar, Chief Verification Technologist, Paradigm Works Inc.

VMM1.2 class library is now also implemented in SystemC(VMM-SC).

Will it help your project? Please take a few minutes to consider this, especially if you have been using or thinking about C/C++/SystemC models in your environment.

Following are the some use cases that I have come across or can anticipate among our clients. With each use case, I have put down some thoughts on why VMM-SC may(or may not) be of any benefit. Do you agree?

Use Case 1. Using SystemC as the primary verification language for unit level verification

Many teams today use SystemC as their primary verification language for all unit and system-level tests. However, with the increasing popularity of SystemVerilog, they often adopt a hybrid model. They do use C++/SystemC as the primary language for testbench creation, but complement it using SystemVerilog language features for assertions, functional coverage analysis, and constrained random generation.

Such teams often consider moving to SystemVerilog based environments, but are concerned about the reuse of their existing code, and cannot justify the ROI against the resources needed to make the transition to SystemVerilog completely. Ideally, they would like to have a multi-language solution that can mix and match SystemVerilog and SystemC components.

For this class of users, VMM-SystemC can ease their adoption of a multi-language solution. First, they can create newer environments in VMM-SC, and encapsulate preexisting VIP within VMM-SC components using thin wrappers. Once transitioned to VMM-SC, they can easily interoperate with other VMM-SV based environments and components. As an added benefit of the SystemVerilog-SystemC interoperability, the VMM-SV applications such as RAL/Scoreboarding etc. can now be made available to the SystemC side.

Use Case 2. Using SystemC as the primary verification language for system-level verification

Some teams decide to write their system level verification environments primarily in C/C++, as it is often used by the SW team or in the lab. Depending on the sophistication of these teams, these environments may range from being simple directed C testbenches to those that are highly sophisticated and use SystemC. Adopting VMM-SC as the system-level methodology can make it easier to cleanly pull in various environments and components by encapsulating them as VMM objects and making them work together taking advantage of VMM-SC support for phasing, TLM ports etc.

For this class of users, adopting VMM-SC as the top-level glue environment will be a big win.

Use Case 3. Planning to move to SystemVerilog and adopt industry standard practices.

Often, teams are willing to start from scratch for new projects as they realize their existing environments have become outdated and simply cannot scale. With the maturity of SystemVerilog, they would likely adopt it as the primary HVL language of choice. However, they would still like to preserve a number of previously developed components.

This is an ideal application for using VMM-SC. Pre-existing transactors can be converted to VMM-SC components by deriving them from vmm_xactor/vmm_unit classes(see 4.3 below). The new environment can now be written in VMM-SV, which can pull in the legacy components using VMM-SC/SV interoperability solution. All VMM features, such as cross language option setting, communicating transactions in a language agnostic way between source and target components using TLM, coordinated phasing etc. make the SystemVerilog adoption process much easier.

Use Case 4.  Using SystemC to develop ESL environments and reference models.

There are users who are primarily interested in creating models at various levels of abstraction. These models can be used in multiple applications, such as performance modeling and architectural exploration. In some cases, such models may not be of sufficient detail to use in functional verification.

At the minimum, such models should be created using TLM2.0 ports to enable easy plug and play in ESL contexts. Adopting VMM-SC as the primary methodology for developing such components can be of benefit, since it offers a rich set of features that can help in controlling and coordinating the components, while maintaining strong interoperability with verification environments.

 

So, to which one of these use cases do you belong? Any other use case that I missed?

This article is the 9th in the Verification in the trenches series. Hope you found this article useful. If you would like to hear about any other related topic, please comment or drop me a line at ambar.sarkar@paradigm-works.com. Also, if you are starting out fresh, please check out the free VMM1.2 environment generator at http://resourceworks.paradigm-works.com/svftg/vmm .

Share and Enjoy:
  • Print
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Google Buzz
  • LinkedIn
  • RSS
  • Twitter

Posted in SystemC/C/C++, Transaction Level Modeling (TLM) | 1 Comment »

1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading ... Loading ...

How you can figure how you configure (a VMM testbench): Part 2

Posted by JL Gray on December 13th, 2010

Jonathan Bromley, Verilab Inc, Austin, Tx

Part 2: An interesting way to use VMM 1.2’s configuration features

Part 1

examined the use of descriptor objects to pass configuration information around the testbench.  In VMM 1.2, though, the available toolkit has grown dramatically, inviting us to reconsider our approach.
 

 

Configuration in VMM 1.2

VMM1.2 provides powerful additional configuration features.  Other articles in this series, Verification in the trenches: Configuring your environment using VMM1.2 and Sharing RTL Configuration with the Testbench

, offer a great overview of the new mechanisms and how to use them.  The vmm_opts facilities make it easy to control configuration values deep in the test environment from top-level code, or even from scripts thanks to the built-in command-line and file reading features.  Within any vmm_unit you can use the vmm_unit_config macros to grab configuration values that were set up from the top level, picking up default values if the values were not set.  Within any vmm_object you can use vmm_opts::get_object_***() function calls to discover whether a given option has been explicitly set, and set a variable appropriately.
Given this new ability to set and grab arbitrary configuration values by name, is there still a role for the trusty configuration object?  I believe there is, for at least two reasons.

 

Legacy components

First, you will surely have some traditional transactors and subenvs in your VMM1.2 environments.  They will use config objects.  The two mechanisms can happily coexist, using VMM1.2 options to populate fields of the config objects before passing them to the transactors that need them:
class MagicToEth_gp extends vmm_group;
  `vmm_typename(MagicToEth_gp)
  MagicPacketXactor magic_xactor;
  EthernetXactor eth_xactor;
  bit has_voodoo;
  …
  function void build_ph();
    …
    MagicPacketXactor_cfg magic_cfg = new;
    …
    magic_cfg.SupportsVoodooMode = has_voodoo;
    magic_xactor = new(…., magic_cfg);
  endfunction
  …
  `vmm_unit_config_boolean(has_voodoo,
         “turn this on to enable MagicVoodoo”,
         0, MagicToEth_gp)
endclass

 

Configure just the variables you care about

Configuration using the new mechanisms is wonderfully flexible, but it needs to be thought through.  Used carelessly it can turn your testbench configuration into a grab-bag of hundreds of individual configuration values with no obvious relationships among them.  Using objects to group together a bunch of related configuration values allows you to use constrained randomization to enforce relationships among the values that were not specified from above, so that you can specify just those values you want to nail down and allow the remaining values to be randomized.  Making that work requires just a little bit of extra effort, and just a tiny bit of guru-level SystemVerilog randomization.
 

 

Selective configuration and randomization

For example, let’s go back to the configuration of our MagicPacket transactor.  We already have a configuration object for it – but now we must take care to derive from vmm_object so that the hierarchical configuration mechanism knows about it:
class MagicPacketXactor_cfg extends vmm_object;
  rand shortint unsigned MaxLength;
  rand bit SupportsVoodooMode;
    constraint c_valid_packet {
      if (SupportsVoodooMode) { MaxLength inside {[64:16384]}; }
      else                    { MaxLength == 1024; }
    }

Now we can play some interesting games with VMM configuration and SystemVerilog constrained randomization to make the configuration as flexible as possible:

function new(string name, vmm_object parent = null);
  bit is_set;
  super.new(parent, name);  // as usual for any vmm_object
  SupportsVoodooMode = vmm_opts::get_object_int(
    is_set,   // reports whether the option was set from above
    this,     // we’re interested in options applying to this object
    “SupportsVoodooMode”,  // identifying name of the option
    0,        // default value
    “some helpful documentation text” );
  if (is_set)  // This variable was explicitly configured.
               // Disable randomization of this variable.
    SupportsVoodooMode.rand_mode(0);
  MaxLength = vmm_opts::get_object_int(
    is_set,   // reports whether the option was set from above
    this,     // we’re interested in options applying to this object
    “MaxLength”,  // identifying name of the option
    1024,     // default value
    “some helpful documentation text” );
  if (is_set)  // This variable was explicitly configured.
               // Disable randomization of this variable.
    MaxLength.rand_mode(0);
endfunction

Why all this complexity?  Because of the huge flexibility it gives us.  The key is in understanding what happens later, when we randomize() this object.  If neither of the options was set, then rand_mode is true (its default) on both variables and randomization proceeds as normal.  If just one of the variables was configured, that variable now has rand_mode(0) and so it will not be altered by randomization – you have fixed it from the options.  The other variable is randomized, but respecting all constraints imposed by the variable that is already configured.  For example, suppose we configure MaxLength=5000 from the command line.  Thanks to our ingenious constructor code, that variable is fixed and won’t be randomized.  Other variables in the object are randomized, though, so the SystemVerilog constraint solver tries to find a value for SupportsVoodooMode that will satisfy the constraints.  Of course, that value must be true because of the if…else constraint – a false value is possible only if MaxLength is equal to 1024.

Finally, suppose we configure both variables from option values.  Now, any attempt to randomize() the configuration object will have no effect on the variables, because randomization has been disabled for both.  But randomization is still useful to us, because it will give a constraint violation error if the two options have been given contradictory values.

Getting the balance right is tricky – if you try to enforce too tight a structure on your collections of configuration data it will prove inflexible as verification requirements change, but leaving the configuration as a bunch of scattered, unrelated variables will soon become a maintenance nightmare.  Creative use of get_object_* options methods and randomization can offer a useful new set of compromises between flexibility and ease of deployment, by allowing you to specify just a few of an object’s configuration values and allowing randomization to choose a consistent set of values for the remaining variables.

Share and Enjoy:
  • Print
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Google Buzz
  • LinkedIn
  • RSS
  • Twitter

Posted in Configuration | No Comments »

1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading ... Loading ...

How you can figure how you configure (a VMM testbench): Part 1

Posted by JL Gray on December 7th, 2010

Jonathan Bromley, Verilab Inc, Austin, Tx

Part 1: Using configuration objects


Each VMM transactor class should have a companion config class, where you create data members for all critical “vital statistics” of the transactor.  Just before constructing the transactor object, an enclosing subenv would create one of these config objects and then pass it to the transactor as a constructor argument.  Every transactor should also provide a reconfigure method allowing other parts of the environment to set up a new configuration for it at any time.

 

Objects to control objects
Here’s an imaginary example of a transactor configuration class:

class MagicPacketXactor_cfg;
  rand shortint unsigned MaxLength;
  rand bit SupportsVoodooMode;
    constraint c_valid_packet {
      if (SupportsVoodooMode) { MaxLength inside {[64:16384]}; }
      else { MaxLength == 1024; }
    }
endclass

Note how the class contains rand data members for all the transactor’s configurable attributes, and also has constraints so that it will yield a meaningful and interesting set of configuration values when randomized.  Of course, further constraints can easily be added to enforce specific configurations.

The corresponding transactor code might be something like this:

class MagicPacketXactor extends vmm_xactor;
  …
  local MagicPacketXactor_cfg cfg;
  function new( … // usual VMM constructor arguments, and then…
                MagicPacketXactor_cfg cfg = null);
    …
    if (cfg == null) begin
      cfg = new;
      cfg.randomize();
    end
    this.cfg = cfg;
    …
  endfunction
  function void reconfigure(MagicPacketXactor_cfg cfg);
    this.cfg = cfg;
  endfunction

In the transactor’s main() task, or elsewhere, we can now do things like

if (this.cfg.SupportsVoodooMode)
  castSpell(…);

It’s really important to note that the transactor takes no responsibility for setting up its own configuration.  It merely assumes that a configuration object has been supplied from outside, and then makes use of the values within that configuration object.  If the enclosing environment fails to provide a configuration object, as a last resort the transactor constructs its own randomized configuration.

This is a good example of the notion of encapsulating configuration in a single object.  By gathering all important attributes of a component into a class:
•    we can pass the entire configuration around as a single object or reference;
•    it becomes easy to write randomization constraints that establish relationships among configuration attributes;
but most important of all:
•    it is straightforward to assemble several configuration objects into a single, larger configuration object suitable for use at the next level up the hierarchy– typically at the vmm_subenv level.

Environment configuration
A verification environment for a MagicPacket-to-Ethernet bridge will of course contain both MagicPacket and Ethernet transactors.  The environment or subenv gets its own configuration class, containing references to configuration objects for both its transactors:

class MagicToEthernet_subenv_cfg;
  rand MagicPacketXactor_cfg magic_cfg;
  rand Ethernet_cfg eth_cfg;
endclass

Configuration proceeds in much the same way as for the transactors.  The environment’s constructor takes one of these objects as an argument, and then passes on its inner configuration objects to the corresponding transactors as it constructs them.  Reconfiguring the environment is equally straightforward:

class MagicToEthernet_subenv extends vmm_subenv;
  MagicPacketXactor magic_xactor;
  EthernetXactor eth_xactor;
  …
  function void reconfigure(MagicToEthernet_subenv_cfg cfg);
    magic_xactor.reconfigure(cfg.magic_cfg);
    eth_xactor.reconfigure(cfg.eth_cfg);
  endfunction

Finally, the code that launches your VMM test can create and populate all the necessary configuration objects, assemble them into environment-wide configuration objects, and pass them into constructors as needed.  It is also very easy to configure multiple transactors to match a common configuration simply by passing the same configuration object to all of them.

Testbench configuration vs. test configuration
The configuration mechanism we’ve explored is neat, powerful and straightforward.  However, it doesn’t offer very much help in separating the two rather different concerns of configuring the test environment and configuring the test.

Test environment configuration gets you the right structure – correct number of transactors, correct choice of active vs. passive transactors, choice of reference model or scoreboard – to match the chosen device-under-test.  Using that test environment, though, you probably wish to run a large and ever-growing battery of testcases as you develop new stimulus to hit elusive coverage objectives.  From a programming point of view, configuring the test is not very different from configuring the testbench –a matter of passing appropriate configuration objects to the constructors of various components.  From an organizational point of view, though, there are big differences.   Testbench configuration generally needs to be at least partly driven by parameters of the DUT and test harness, whereas testcase configuration is likely to be much more flexible and dynamic, and will vary from run to run.

When using configuration objects in this style, I’ve learnt the value of keeping a strict separation between these two kinds of configuration to simplify those organizational concerns.

In the second part of this article we’ll look at the impact of the extensive new configuration facilities in VMM 1.2, and explore a novel way to manage groups of inter-related configuration options.

 

Share and Enjoy:
  • Print
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Google Buzz
  • LinkedIn
  • RSS
  • Twitter

Posted in Configuration | 1 Comment »

1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading ... Loading ...

Communication Options in VMM 1.2

Posted by John Aynsley on November 30th, 2010

 

John Aynsley, CTO, Doulos

Having given an example of transaction-level communication in VMM in my previous post, I am now going to say a little about each of the communication options available in VMM 1.2. The VMM user is presented with an array of communication options from which to chose, but which-is-which and which is best?

Blocking Transport

Blocking transport is used to send a single payload representing a transaction from an initiator to a target where very little timing information is required. Each transaction can have a begin time and an end time, but there is no more structure or detail to the lifetime of the transaction than that. It is possible to annotate timing information onto the b_transport call to offset the begin and end times of the transaction from the actual times as which the function is called and returned, but not to add further timing points or events during the lifetime of the transaction. The b_transport function is called with a transaction object as an argument, and returns only when the processing of that transaction at the target is complete. Blocking transport is the cleanest and simplest way to send a payload from A to B.

Non-blocking Transport

Like blocking transport, non-blocking transport is used to send a single payload representing a transaction from an initiator to a target. Unlike blocking transport, non-blocking transport allows significant events within the lifetime of the transaction to be modeled to whatever degree of timing granularity you like, and also allows multiple pipelined transactions to be in-flight at the same time. This is achieved by allowing multiple calls to nb_transport_fw and nb_transport_bw, in the forward and backward directions respectively, associated with a single transaction. The ability to model multiple timing points and pipelining comes at a cost in terms of the complexity of the interface, however. In the context of VMM, non-blocking transport is only of interest if you need to communicate with an existing SystemC TLM-2.0 model that uses this same interface.

Analysis Interface

The analysis interface is the third import to VMM from the TLM-2.0 standard. The analysis interface is used to broadcast a single payload to passive subscribers (aka observers or listeners). The defining characteristics of the analysis interface are firstly that there can be any number of subscribers to a single call and secondly that the subscribers are not permitted to modify the transaction object. Hence the analysis interface is ideal for distributing transactions to passive components such as checkers, scoreboards, and coverage collectors within a verification environment. The analysis interface is non-blocking and does not have any associated timing or status information.

Channels

Channels are the classic way to send payloads around a VMM verification environment. Unlike the three transaction-level interfaces described above, a channel is more than a function call. A channel is a FIFO buffer that can store multiple outstanding transactions. A transaction can be accessed by both producer and consumer transactors while it remains in the so-called active slot of the channel, and it is possible to model multiple timing points at that stage by having VMM notifications associated with the channel or with the transaction object. Channels are still the preferred mechanism used to model the timing and synchronization details of specific protocols within VMM, while blocking transport is preferred where a more abstract model is adequate.

Callbacks

In some respects callbacks play the same role as the analysis interface described above, while in other respects there are fundamentally different. Callbacks are like the analysis interface in the sense that they distribute function calls to any subscriber that has register itself with the transactor making the calls. They are different in that a callback is allowed to modify transactions passed as arguments to the function call whereas the analysis interface does not allow transactions to be modified. Also, callbacks are more flexible in the sense that both the callback function names and their argument lists are user-defined, which is not true of the analysis interface. In VMM, callbacks are the preferred mechanism where the behavior of a transactor needs to be modified by tweaking the contents of a transaction.

Notifications

VMM notifications are preferred for data-less synchronization where transactors need to synchronize without passing a payload. The analysis interface is preferred when passing around payloads. VMM notifications are also built into the transaction and channel base classes, so can be used to introduce additional timing points during the lifetime of a transaction when using VMM channels for communication as described above.

So, those are the six main choices for communication available to you in VMM. You can read more details in the VMM 1.2 Standard Library User Guide.

Share and Enjoy:
  • Print
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Google Buzz
  • LinkedIn
  • RSS
  • Twitter

Posted in Communication, Transaction Level Modeling (TLM) | 1 Comment »

1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading ... Loading ...

Planning for Functional Coverage

Posted by JL Gray on November 23rd, 2010

JL Gray, Vice President, Verilab, Austin, Texas, and Author of Cool Verification

Functional coverage cures cancer and the common cold. It helps kittens down from trees and old ladies up the stairs. Functional coverage is the greatest thing since sliced bread, right? So all we need to do is put some in our verification environments and our projects will be a success.

Well… perhaps not.

Many teams I’ve worked with have struggled with various aspects of functional coverage. The area I’d like to address today is the part most engineers spend the least amount of time on – planning (as opposed to implementing) your functional coverage model. One of the critically important uses for functional coverage is to help us (engineers and project managers alike) know when we are done with verification. We all know it’s impossible to 100% verify a complex chip. But it is possible to define what “done” means for our individual teams and projects. The functional coverage model gives us a way to correlate what we’ve accomplished in the testbench with some definition of “done” in our verification plan.

What that means is that the verification plan itself is the key to understanding where we are relative to where we need to be to finish a project. It is the document that managers, designers, verification engineers and others can all interact and agree on what constitutes “good enough.” Verification plans often contain lists of design features and statements about what types of tests should be run to verify these features. Unfortunately, that is almost never sufficient to give stakeholders insight into what should be covered.

Verification plans need to contain a sufficient level of detail to meet the following goals:

  • 1. Allow key stakeholders who may not know anything about SystemVerilog to see what will be covered and then provide a platform for them to give feedback to the verification engineer.
  • 2. Allow this discussion to take place before any SystemVerilog code has been written.
  • 3. Allow an engineer other than the author of the document to understand and implement the coverage model.
  • 4. Allow the project manager to understand what coverage goals should be reached for the module to be considered verified.

Point 3 is important in ways some engineers may not realize. During my verification planning seminars I often ask students if they’ve ever heard of a project’s “truck factor”. Basically, a project’s truck factor is the number of people on the project who could get hit by a truck before the project is in trouble. A verification plan should specify coverage goals in enough detail that someone besides you (this could include the you 6 months from now who’s forgotten all relevant details about the block in question) can understand what the original intent of the document was. For example, some plans I’ve seen make comments like “test all valid packet lengths”. What are the valid lengths? They should be specified in the plan. Are there certain corner cases you think are important to cover? Make sure you call out this information with sufficient detail so that the project manager can tell if you’ve actually accomplished your goals!

Adding this extra level of detail to your verification plan takes time. But without the detail, how can you easily share with your colleagues what you plan to do? And how can you know which features your testbench needs to support in order to meet your coverage goals?

As always, questions and comments welcome.

Share and Enjoy:
  • Print
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Google Buzz
  • LinkedIn
  • RSS
  • Twitter

Posted in Coverage, Metrics, Verification Planning & Management | 2 Comments »

1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading ... Loading ...

You can also “C” with RAL.

Posted by S. Varun on November 18th, 2010

The RAL C interface provides a convenient way for verification engineers to develop firmware code which can be debugged on a RTL simulation of the design. This interface provides a rich set of C API’s using which one can access fields, registers and memories included within the RAL model. The developed firmware code can be used to interface with the RAL model running on a SystemVerilog simulator using DPI (Direct Programming Interface) and can also be re-used as application-level code to be compiled on the target processor in the final application as illustrated in the figure below.

image

The “-gen_c” option available with “ralgen“ has to be used for this and that would cause the generator to generate the necessary files containing the API’s to interface to the C domain. The generated API’s can be used in one of two forms.

1. To interface to the System Verilog RAL model running on a System Verilog simulator using DPI and

2. As a pure standalone-C code designed to compile on the target processor.

Typically firmware runs a set of pre-defined sequences of writes/reads to the registers on a device performing functions for boot-up, servicing interrupts etc. You generally have these functions coded in C, and these would need to access the registers in the DUT. Using the RAL C model, these functions can be generated so that the firmware can now perform the register access through the SystemVerilog RAL model. Thus, this allows firmware and application-level code to be developed and debugged on a simulation of the design and the same functions can later be used as part of the device drivers to perform the same tasks on the hardware.

In the first scenario, when executing the C code within a simulation, it is necessary for the C code to be called by the simulation to be executed and hence the application software’s main() routine must be replaced by one or more entry points known to the simulation. All entry points must take at least one argument that will receive the base address of the RAL model to be used by the C code. The C-side reference is then used by the RAL C API to access required fields, registers or memories.

image

Consider a sequence of register accesses performed at the boot time of a router as defined in function system_bootup() shown below,

File : router_test.c

#ifdef VMM_RAL_PURE_C_MODEL

/*when used as a pure C model for the target processor */

int main() {

void *blk = calloc(410024,1);

system_bootup((void *) (((size_t)blk)>>2)+1);

}

#endif

void system_bootup (unsigned int blk){

unsigned int dev_id = 0xABCD;

unsigned int ver_num = 0×1234;

/* Invoking the generated RAL C APIs */

ral_write_DEV_ID_in_ROUTER_BLK(blk, &dev_id);

ral_write_VER_NUM_in_ROUTER_BLK(blk, &ver_num);

}

In the above example “ral_write_DEV_ID_in_ROUTER_BLK()” & “ral_write_VER_NUM_in_ROUTER_BLK()” are C API’s generated by “ralgen” to access registers named “DEV_ID” & “VER_NUM” located in a block named “ROUTER_BLK”. In general registers can be accessed using “ral_read_<reg>_in_<blk>” & “ral_write_<reg>_in_<blk>” macros present within the RAL-C interface.

The above C function can also be called from within a System Verilog testbench via DPI

File : ral_boot_seq_test.sv

import “DPI-C” context task system_bootup(int unsigned ral_model_ID);

program boot_seq_test();

initial

begin

router_blk_env env = new();

// Configuring the DUT

env.cfg_dut();

// Calling the C function using DPI

system_bootup(env.ral_model.get_block_ID()); // ‘ral_model ‘is the instance of the generated SV model in the env class

end

endprogram

In the example above, system_bootup is the service entry point which is called from the SV simulation and is passed the RAL Model reference. The ‘C’ code that is executed and simulation freezes till one of the registers accesses is made which then shifts the execution to the SV side.

image

The entire execution in ‘C’ is in ‘0’ time in the simulation timeline. The RAL C API hides the physical addresses of registers and the position and size of fields. The hiding is performed by functions and macros rather than an object-oriented structure like the native RAL model in SystemVerilog. This eliminates the need to compile a complete object-oriented model in an embedded processor object code with a limited amount of memory

Thus by using the RAL C interface one can develop a compact and efficient firmware code, while preserving as much as possible of the abstraction offered by RAL. I hope this was useful and do let me know your thoughts on the same if you plan to use this flow to meet some of your requirements of having your firmware and application-level code to be developed and debugged on a simulation of the design.

Please refer to the RAL userguide for more information. You can also refer to the example present within VCS installation located at $VCS_HOME/doc/examples/vmm/applications/vmm_ral/C_api_ex.

Share and Enjoy:
  • Print
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Google Buzz
  • LinkedIn
  • RSS
  • Twitter

Posted in Register Abstraction Model with RAL, SystemC/C/C++, SystemVerilog, VMM | No Comments »

1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading ... Loading ...

Reusing Your Block Level Testbench

Posted by JL Gray on November 12th, 2010

JL Gray, Vice President, Verilab, Austin, Texas, and Author of Cool Verification

Building reusable testbench components is a desirable, but not always achievable goal on most verification projects. Engineers love the idea of not having to rewrite the same code over and over again, and managers love the idea that they can get more work out of their existing team by simply reusing code within a project and/or between projects. It is interesting, then, that I frequently encounter code written by engineers that cannot work in a reusable way.

Consider the following scenario. You’ve just coded up a new block level testbench to verify an OCP to AHB bridge. As any good engineer knows, your environment must be self-checking, so you create a VMM scenario that does the following:

class my_dma_scenario extends vmm_scenario;

  rand ocp_trans ocp;
  ahb_trans ahb;

  // ... 

  virtual task execute(ref int n);
    vmm_channel ocp_chan = this.get_channel("OCP");
    vmm_channel ahb_chan = this.get_channel("AHB");
    this.ocp.randomize();
    ocp_chan.put(this.ocp.copy());

    // Wait for the transaction to complete...

    ahb_chan.get(ahb) 

    // Compare actual and expected values
    // in the stimulus instead of the scoreboard.
    my_compare_function(ocp, ahb);

  endtask

endclass

Or, perhaps you decide to get fancy and update and create a situation where you add the expected transaction from the scenario directly to a scoreboard, which compares the results with transactions on the AHB interface as observed by a monitor. Either way, you go merrily about your business verifying the bridge. You get excellent coverage numbers, and eventually, with the help of the block’s designer, you feel you’ve fully verified the module at the block level. However, things are not going well in the full chip environment. Full chip tests are failing in unexpected ways, and other engineers debugging the issue feel it must be a bug in the brigde! They start assigning bugs to you and the block designer. If only you had some checkers operating at the full chip level you could prove your module was operating correctly…

Unfortunately, you have a problem. In your block level testbench, your checkers only work in the presence of stimulus, and in the full chip environment the stimulus components cannot be used. And at this stage of the project, there is no time to go back and create the additional infrastructure (monitor(s) and possibly a new scoreboard) required to run your self-checking module-level testbench at the full chip level.

Fortunately, there is another way to build a module level testbench so that it will be guaranteed to work at the full chip. Follow these simple rules:

  1. Always create a monitor, driver, and scenario generator for every testbench component.
  2. Never populate a scoreboard from a driver or scenario. Always pull in this information from a passive monitor.

In general, when writing your testbench components always assume they will need to run in the absence of testbench stimulus. Yes – this means additional up-front work. Writing checkers that work with passive monitors instead of the information you have available to you in the driver can be time consuming. Often, though, the amount of additional work required is much less than the time you will spend debugging issues without checkers at the full chip level. That being said, sometimes it is too difficult to implement purely passive checkers. You will have a good idea of whether or not this is the case based on the upfront work you did writing a comprehensive testplan. You do have a testplan, right?

Share and Enjoy:
  • Print
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Google Buzz
  • LinkedIn
  • RSS
  • Twitter

Posted in Reuse, VMM, Verification Planning & Management | 3 Comments »

1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading ... Loading ...

Modeling ISRs with VMM RAL

Posted by Amit Sharma on November 4th, 2010

In a verification environment, different components may be trying to access the DUT registers and memories. For example, the BFM might be programming some registers while the bus monitor might be sampling the values of these registers. In specific cases, there may be an interrupt monitor which triggers an Interrupt Service Routine (ISR) whenever it sees an Interrupt pin toggling in the interface. The ISR might end up having to read the Interrupt registers and end up clearing the Interrupt bit/s through a front door access.

To ensure that different components in a verification environment can access the DUT registers at any given point in time, the RAL model instantiated in the environment can be passed to different VMM components. These different components whose methods are executing in separate parallel threads can now access the same set of registers in the DUT through the RAL model. A question many folks ask is: when there are multiple parallel register accesses, how do they get scheduled through the RAL layer?  Here is an explanation of how threads are scheduled in RAL:

A Register read/write from different threads is comparable to an ‘atomic’ channel.put() from  different threads. Hence it gets scheduled in the order of pipelining of the threads.

A write/read would basically consist of the following atomic operations:

- A generic vmm_rw_access transaction with its fields (addr, data, kind etc) being populated and posted onto the execute_single()   task of the Translate Transactor

- The transaction being translated in the execute_single() task and pushed into the input channel of the user BFM

- The transaction being retrieved through get/activate in the User BFM main thread and then subsequently driven to the DUT interface

Thus ‘posting’ of RAL accesses whenever a Read/Write/Mirror/Update is invoked is in the same order they are issued. Subsequently, the execute_single() task just translates the generic RW RAL transaction to a User BFM comprehensible transaction, and doesn’t change the order.

Now, how do we handle a scenario when specific register accesses like those coming from an ISR need to be given a higher priority than accesses coming from other threads in a verification environment?  VMM and RAL methods give us specific hooks to achievethis requirement. Here is one of the options on how this can be done.

If we look at the vmm_ral_reg::write method, we have the given signature:

virtual task write( output vmm_rw::status_e status,

    input bit [63:0] value,

    input vmm_ral::path_e path = vmm_ral::DEFAULT,

    input string domain = "",

    input int data_id = -1,

    input int scenario_id = -1,

    input int stream_id = -1)

Now, a generic RAL transaction that gets created through any Register access has the same data_id, scenario_id, stream_id arguments which get passed on from the read/write call. These arguments help us tag and track the transactions if that is so desired. Now, these arguments can be made use of in the execute_single() task to ensure that accesses from ISRs have the highest priority. But, first, if we go back to the earlier post by Varun, Issuing concurrent READ/WRITE accesses to the same register on two physical interfaces using RAL, Issuing concurrent accesses to the same registers on two physical interfaces using RAL , we note that if the RAL model is processing a ‘register access’ , it will not initiate the next one until the earlier one is completed. So, this is what we need to get done.We first use the RAL Proxy transactor to schedule the ISR register access simultaneously. After that, we flush out any existing accesses and prevent any new register access through RAL until access from the ISR is completed

This is how it, will be done:

image

For a normal register access, a read/write method will be invoked as follows:

          ral_model.<reg_name>.write(stat,wdata, “AHB”); //’wdata’ is the value to be driven, “AHB” is the domain /physical interface

For a register access in an ISR modeled in a MS Scenario, we have:

         env.bfm.to_ahb.grab(this); //grabs the channel

         env.ral.read(status, env.ral_model.<reg_name>.get_address_in_system("AHB"), data, 32, "AHB",,,1); // The last argument is again the “stream_id” argument

         env.bfm.to_ahb.ungrab(this); //allows the channel to be accessed from other threads once the ISR is completed

Once, this is done, the execute_single() task inside the translate Transcator will know if an access is through an ISR and can ensure that ISRs are processed on priority through a combination of functionality provided through the VMM Channel methods in combination with a semaphore:

virtual task execute_single(vmm_rw_access tr);

        AHB_tr cyc;

        AHB_tr cyc_active; //to keep any transactions residing on the active slot

        semaphore sem = new(1); //semaphore with a single key to prevent new accesses when a reg access from an ISR is processed

       // Translate the generic RW into a simple RW

       cyc = new;

       {cyc.dev, cyc.addr} = tr.addr;

       if (tr.kind == vmm_rw::WRITE) begin

         cyc.cycle = simple_tr::WRITE;

         cyc.data = tr.data;

         …

       end

      else begin

         cyc.cycle = simple_tr::READ;

         …

      end

      if (tr.stream_id != 1) sem.get(1); // regular register access gets blocked here when a reg access from an ISR is processed

      else begin

         if(this.bfm.to_ahb.is_full()) begin

           this.bfm.to_ahb.activate(cyc_active); //removes any existing transactions in the channel’s active slot so that the current access can be pushed through

           this.in_chan.start();

           this.in_chan.complete();

           this.in_chan.remove();

           this.bfm.to_ahb.put(cyc);

           this.bfm.to_ahb.put(cyc_active); //restores the original transaction back into the active slot

           cyc = cyc_active;

           sem.put(1); //ths ISR access puts back the key for normal accesses to resume

        end

        else this.bfm.to_ahb.put(cyc);

      end

      // Send the result back to the RAL

      if (tr.kind == vmm_rw::READ) begin

        tr.data = cyc.data;

      end

      endtask: execute_single

Share and Enjoy:
  • Print
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Google Buzz
  • LinkedIn
  • RSS
  • Twitter

Posted in Register Abstraction Model with RAL, VMM infrastructure | No Comments »

1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading ... Loading ...

Accelerating Design Verification with Virtual CPU models

Posted by Srivatsa Vasudevan on October 29th, 2010

Srivatsa Vasudevan, Sr Staff CAE, Synopsys Inc & Filip Thoen, Product Architect, Synopsys Inc.

In today’s SOC architectures, one or more on-chip core(s) or CPU(s), like ARM or MIPS, are often used to provide flexibility in the system, through the use of embedded software. Besides executing software functionality, they program the hardware peripherals and perform I/O with them. Here, from a low-level hardware perspective the CPU is typically performing control and data plane functions.

virtual_models

Let’s look at this from a SoC verification perspective: in such cases, besides being able to drive the appropriate stimuli for the tests, the RTL simulation of the core(s)/CPU(s) doesn’t provide any additional value to a verification engineer. These core(s)/CPU(s) are typically pre-validated by their IP supplier, and hence don’t need to be validated again.

Running a performance profile on simulations of such SoCs reveals that a majority of the events simulated are typically within the CPU, which is often not a portion of the DUT being verified. Consequently, valuable verification time is lost in debug, and no additional information is gained from the simulation of these cores/CPUs. Often, verification teams use embedded software tests running on these core(s)/CPU(s) to validation individual IPs and/or to perform system integration validation. These software tests will program the different peripheral(s) / module(s) being tested, exercise them and observe and validate the result for correctness. Other software scenarios include the testing of the SoC’s boot sequence, which is typically performed by boot code executed by these core(s)/CPU(s).

(Note: Click on the figure if it appears too smal in your browser, Thanks!)

In situations like this, it often makes sense to replace the event-heavy CPU RTL model with a transaction –level model (TLM), typically called an instruction-set simulator (ISS) in case of a CPU or core. These models are written in C/C++ or SystemC, and depending on the abstraction level used, can run up to 100+ MIPS standalone. Such a model will execute the actual embedded software program binaries and issue the appropriate (bus) transactions to the peripheral(s) / module(s) being verified. As a result, the events originating from the simulating the CPU are now removed from the RTL simulation, and the net effect observed is a significant simulation acceleration while obtaining the required results. Moreover, non-interesting events, like the fetching of instructions from off- or on-chip ROM/RAM, requiring a high number of events in the bus-memory controller-memory chain, can be avoided in the RTL simulation by shadowing these memories in the virtual CPU model.

image

Several system level models for popular cores/CPUs are now available as part of the Synopsys Designware® System-Level Library product, including ARM, MIPS and PowerPC architectures. Combined with a pin level transactor, which matches the transaction level interface of the TLM model to the pin-level interface of the CPU bus, a virtual model of the core/CPU present in the SoC can be set up. Integration & deployment of this is straightforward: the virtual CPU model is a direct pin-level replacement of the RTL CPU, simply requiring a SystemC integration as historically supported by VCS.

There are several ways of implementing this keeping in mind re-use requirements, configurations and the stage at which verification is being carried out. In the figure above, the CPU has been replaced by a virtual model along with a pin level transactor. The CPU still runs the same embedded directed software test code as before, but with a significant simulation speed-up, ranging between 3-20x depending on the design specific parameter like RTL size, speed & number of other (high-speed) modules, testbench overhead, etc. Another significant benefit is the gain in debugability, and the associated increase in productivity. These TLM models allow commercial embedded software debuggers to be connected to the simulation, allowing verification (and software) engineers to (interactively) debug the running verification or boot software program. With the RTL of the CPU in place, this is typically not realizable.

What do you think of this concept? Interesting? How do you handle such situations? Drop me a line.

-Srivatsa

Share and Enjoy:
  • Print
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Google Buzz
  • LinkedIn
  • RSS
  • Twitter

Posted in Transaction Level Modeling (TLM) | 1 Comment »

1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading ... Loading ...

Using VMM template Generator to ramp up your testbench development

Posted by Amit Sharma on October 25th, 2010

Amit Sharma, Synopsys
‘vmmgen’, the template generator for creating robust, extensible VMM compliant environments, has been available for a long time with VMM and it was upgraded significantly with VMM1.2. Though the primary functionality of ‘vmmgen’ is to help minimize VIP and environment development cycle by providing detailed templates for developing VMM Compliant verification environments, a lot of folks also use it to quickly understand how different VMM base classes can be used in different contexts. This is done as the templates uses a rich set of the latest VMM features to ensure the appropriate base classes and their features are picked up optimally.

Given that it has a wide user interaction mechanism which provides available features and options to the user, the user can pick up the modes which are most relevant to his or her requirement. It also provides them the option to provide their own templates thus providing a rich layer of customization. Based on the need, one can generate individual templates of different verification components or they can generate a complete verification environment which comes with a ’Makefile’ and an intuitive directory structure, thus propelling them on their way to catch the first set of bugs in their DUTs. I am sure all of you know where to pick up ‘vmmgen’ form. It available in the <VMM_HOME>/Shared/bin area or in $VCS_HOME/bin

Some of the rich set of features available now includes:

• Template Options:

– Complete environment generation

– Individual templates generation

• Options to create Explicitly phased environments or Implicitly phased environment or to mix Implicitly phased components and Explicitly phased components

• Usage of VMM Shorthand macros

• Creating RAL based environment, and providing Multiplexed domain support if required

• Hooking up VMM Performance Analyzer at the appropriate interfaces

• Hooking up the DS Scoreboard at the relevant interfaces (with options to chose from a range of integration options, e.g. : through callbacks, through TLM2.0 analysis ports, connect directly through to transactors, channels or notifications)

• Ability to hook up different generators (atomic, scenario, Multistream generators) at different interfaces

• Creating a scenario library and Multistream scenario creation

• Multi-driver generator support for different kinds of transactions in the same environment

• Factory support for transactions, scenarios and multi stream scenarios. Sample factory testcase which can explain the usage of transaction override from a testcase.

• ‘RTL config’ support for drivers and receivers.

• Various types of unidirectional and bi-directional TLM connections between generator and driver.

• Analysis ports/exports OR parameterized notify observers to broadcast the information from monitor to scoreboard and coverage collectors.

• Multi test concatenation support and management to run the tests

• Creating portable Interface wrapper object, and setting up interface connections to the testbench components using vmm_opts::set_object/get_object_obj

• Creating a Generic slave component

• Option to use default names or user provided names for different components

As you can see the above list itself is quite comprehensive and let me tell you that that it is not exhaustive as there are many more features in vmmgen.

With respect to the usage as well, there are multiple flavors. In the default mode, the user is taken through multiple detailed choices/options as he is creating/connecting different components in his verification environment. However, some folks might want to use ‘vmmgen’ within their own wrapper script/environment and for them there are options to generate the environments by providing all required options in the command line or through a configuration file… Some of these switches include

-SE [y/n] : Generates a complete environment with sub-environments

-RAL [y/n] : Create RAL based verification environments

-RTL [y/n] : Generates RTL configuration for the environment

-ENV <name>, -TR <name> : Provide the name for the environment class and transaction classes. names for multiple transaction class names can be provide as well:

vmmgen –l sv –TR tr1+tr2

-cfg_file <file_name> : Option to provide a configuration file for the options

There is an option to generate an environment quickly by taking the user through the minimum number of questions (-q).

Additionally, the user can provide his or her own templates through the –L <template directory> option.

As far as individual template generation goes, you have the complete list. Here, I am outlining this down for reference:

image

I am sure a lot of you have already been using ‘vmmgen’. For those, who haven’t, I encourage you to try out the different options with it. I am sure you will find this immensely useful and it will not only help you create verification components and environments quickly but will also make sure they are optimal and appropriate based on your requirements.

Share and Enjoy:
  • Print
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Google Buzz
  • LinkedIn
  • RSS
  • Twitter

Posted in Automation, Coding Style, Customization, Modeling Transactions, Organization, Performance Analyzer, Scoreboarding, Tools & 3rd Party interfaces, VMM infrastructure | No Comments »

1 Star2 Stars3 Stars4 Stars5 Stars (3 votes, average: 3.67 out of 5)
Loading ... Loading ...

Setting data members for factory-created objects

Posted by Avinash Agrawal on October 11th, 2010

Avinash Agrawal, Corporate Applications, Synopsys

One question asked by verification engineers is that given that we are using the VMM factory, how do we assign the data members of VMM class factory service while calling the create_instance method ?

For example, the create_instance() method of class factory service always calls
the class constructor with default arguments. Therefore, it would appear that extra arguments
cannot be passed to the class instance class. i.e., there is no way to pass
additional arguments for class members with the following statements:

  tr = ahb_trans::create_instance(this, "Ahb_Tr0", `__FILE__, `__LINE__);

The answer is that while you cannot use the create_instance() function to initialize   ?
data members, you can use any of the following methods to deal with this assignment:
1. By using virtual function/task set_* in the base class and derived class, and the
   arguments are passed from the arguments list of set_* function/task. But there
   is a limitation with this as the set_* function/task arguments for the base
   class and the derived class must be the same.

2. By using vmm_opts::set_* and vmm_opts::get_* combination to set the value of
   properties of class. 

Examples:

1. By using set_* to pass arguments as described in point 1:

////////////////////
//Source code
////////////////////
`define VMM_12
program P;
`include "vmm.sv"

// Define base class
class ahb_trans extends vmm_data;
  `vmm_typename(ahb_trans00)
  rand int addr;
  static vmm_log log = new("ahb_trans", "object");

  `vmm_data_member_begin(ahb_trans)
    `vmm_data_member_scalar(addr,	DO_ALL)
  `vmm_data_member_end(ahb_trans)

  virtual function void set_data(int addr=0, int data=0);	// <-- Two arguments
    this.addr = addr;
  endfunction

  virtual function void display_data();
    `vmm_note(log, $psprintf("ahb_trans.addr=='h%0h", this.addr));
  endfunction

  `vmm_class_factory(ahb_trans)
endclass

// Define derived class
class my_ahb_trans extends ahb_trans;
  `vmm_typename(my_ahb_trans)
  rand int data;

  static vmm_log log = new("my_ahb_trans", "object");

  `vmm_data_member_begin(my_ahb_trans)
    `vmm_data_member_scalar(data,		DO_ALL)
  `vmm_data_member_end(my_ahb_trans)

  virtual function void set_data(int addr=0, int data=0);	// <-- Two arguments
    this.addr = addr;
    this.data = data;
  endfunction

  virtual function void display_data();
    `vmm_note(log, $psprintf("my_ahb_trans.addr=='h%0h, my_ahb_trans.data=='h%0h", this.addr, this.data));
  endfunction

  `vmm_class_factory(my_ahb_trans)
endclass

class env extends vmm_env;

   ahb_trans tr;

   function new;
      super.new("ENV");
   endfunction;

   function void build;
      super.build;

     ahb_trans::override_with_new("@%*", my_ahb_trans::this_type, log, `__FILE__, `__LINE__);
     tr = ahb_trans::create_instance(this, "Ahb_Tr0", `__FILE__, `__LINE__); 

     if(!(tr.get_typename == "class P.my_ahb_trans"))
        `vmm_error(log,"ERROR");

     tr.set_data(32'h5555_5555, 32'haaaa_aaaa);	// <-- Call set_* after create_instance
     tr.display_data();

   endfunction

endclass

initial begin
  env env = new();
  env.run;
end

endprogram

////////////////////
//Simulation Result
////////////////////
Normal[NOTE] on my_ahb_trans(object) at                    0:
    my_ahb_trans.addr=='h55555555, my_ahb_trans.data=='haaaaaaaa
Simulation PASSED on /./ (/./) at                    0 (0 warnings, 0 demoted errors & 0 demoted warnings)

2. By using vmm_opts::set_* and vmm_opts::get_* combination as described in point 2:

////////////////////
//Source code
////////////////////
`define VMM_12
program P;
`include "vmm.sv"

// Define base class
class ahb_trans extends vmm_data;
  `vmm_typename(ahb_trans00)
  rand int addr;
  static vmm_log log = new("ahb_trans", "object");

  `vmm_data_member_begin(ahb_trans)
    `vmm_data_member_scalar(addr,	DO_ALL)
  `vmm_data_member_end(ahb_trans)

  virtual function void set_data();
    this.addr = vmm_opts::get_int ("ADDR", 0, "Value set for addr");
  endfunction

  virtual function void display_data();	//<-- no arguments passed, so there is no limitation on number of arguments
    `vmm_note(log, $psprintf("ahb_trans.addr=='h%0h", this.addr));
  endfunction

  `vmm_class_factory(ahb_trans)
endclass

// Define derived class
class my_ahb_trans extends ahb_trans;
  `vmm_typename(my_ahb_trans)
  rand int data;

  static vmm_log log = new("my_ahb_trans", "object");

  `vmm_data_member_begin(my_ahb_trans)
    `vmm_data_member_scalar(data,		DO_ALL)
  `vmm_data_member_end(my_ahb_trans)

  virtual function void set_data();	//<-- no arguments passed, so there is no limitation on number of arguments
    this.addr = vmm_opts::get_int ("ADDR", 0, "Value set for addr");
    this.data = vmm_opts::get_int ("DATA", 0, "Value set for data");
  endfunction

  virtual function void display_data();
    `vmm_note(log, $psprintf("my_ahb_trans.addr=='h%0h, my_ahb_trans.data=='h%0h", this.addr, this.data));
  endfunction

  `vmm_class_factory(my_ahb_trans)
endclass

class env extends vmm_env;

   ahb_trans tr;

   function new;
      super.new("ENV");
   endfunction;

   function void build;
      super.build;

     vmm_opts::set_int("ADDR", 32'h55,null);	//	<-- Call vmm_opts::set_*
     vmm_opts::set_int("DATA", 32'haa,null);

     ahb_trans::override_with_new("@%*", my_ahb_trans::this_type, log, `__FILE__, `__LINE__);
     tr = ahb_trans::create_instance(this, "Ahb_Tr0", `__FILE__, `__LINE__); 

     if(!(tr.get_typename == "class P.my_ahb_trans"))
        `vmm_error(log,"ERROR");

     tr.set_data();
     tr.display_data();

   endfunction

endclass

initial begin
  env env = new();
  env.run;
end

endprogram

////////////////////
//Simulation Result
////////////////////
Normal[NOTE] on my_ahb_trans(object) at                    0:
    my_ahb_trans.addr=='h55, my_ahb_trans.data=='haa
Simulation PASSED on /./ (/./) at                    0 (0 warnings, 0 demoted errors & 0 demoted warnings)
Share and Enjoy:
  • Print
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Google Buzz
  • LinkedIn
  • RSS
  • Twitter

Posted in VMM, VMM infrastructure | No Comments »

1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading ... Loading ...

The Blind Leading the Deaf

Posted by Andrew Piziali on September 28th, 2010

The Wall of Separation Between Design and Verification

by Andrew Piziali, Independent Consultant

I remember a time in my zealous past, leading a large microprocessor verification team, where one my junior engineers related how they had forcefully resisted examining the floating point unit RTL, explaining to the designer that they did not want to become tainted by the design! The engineer insisted on a dialog with the designer rather than reviewing the RTL. Their position was mine: we must maintain some semblance of separation between the design and verification engineers.

There has been an age old debate between whether or not there ought to be a “wall” between the design and verification teams. “Wall” in this context refers to an information barrier between the teams that minimizes the verification engineer’s familiarity with the details of the implementation (but not the specification!) of the design component being verified. Similarly, the wall minimizes the designer’s familiarity with the verification environment for their implementation.

Re-convergence Model

Re-Convergence Model

The intent of the wall is to allow two pairs of eyes (and ears!)—those of the designer and those of the verification engineer—to independently interpret the specification for a common component and then compare their results. The hypothesis is that if they reach the same conclusion, they are likely to have correctly interpreted the specification. If they do not, one or both are in error. This process is an example of the re-convergence model[1], where a design transformation is verified by performing a second parallel transformation and then comparing the two results. What are the pros and cons of the wall?

The argument in favor of the wall depends upon what we might call original impressions, the fresh insight provided by a person unfamiliar with a concept upon initial exposure. In this context, the verification engineer reading the specification will acquire an understanding of the design intent, independent of the designer, but only if study of its implementation is postponed. Why? Because nearly any implementation will be a plausible interpretation of the specification. The objective is to acquire two independent interpretations for comparison. Hence, influencing a second understanding with an initial implementation would defeat the purpose. What is the opposite position?

The argument against the wall is that a verification engineer and designer, working closely together, are more likely to gain a more precise understanding of the specification than either one working alone. The interactive exploration of possible specification interpretations, each implementing their understanding—the designer the RTL and software, the verification engineer the properties, stimulus, checking and coverage aspects of their verification environment—is argued to lead to convergence more quickly than each party working alone. Well, what should it be? Should verification engineers and designers scrupulously avoid one another, should they collaborate or should they find some intermediate interaction?

Pondering the answer brings to mind the metaphor of the blind leading the deaf, where each of two parties is crippled in a different way such that neither is able to grasp the whole picture. Nevertheless, working together they are able to progress further than working alone. Are the verification engineer and designer the blind leading the deaf? Before I weigh in with my opinion, I’d like to read yours. Type in the “Leave a Reply” box below to respond. Thanks!

—————————
[1] Writing Testbenches Using SystemVerilog, Janick Bergeron, 2006, Springer Science+Business Media, Inc.

Share and Enjoy:
  • Print
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Google Buzz
  • LinkedIn
  • RSS
  • Twitter

Posted in Coverage, Metrics, Verification Planning & Management | 4 Comments »

1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading ... Loading ...

Performance verification of a complex bus arbiter using the VMM Performance Analyzer

Posted by Shankar Hemmady on September 20th, 2010

Performance verification of system bus fabrics is an increasingly complex problem. An article in EE Times by Kelly Larson, John Dickol and Kari O’Brien of MediaTek Wireless describes how they used the VMM Performance Analyzer to complete performance validation for an AXI bus arbiter:

http://www.eetimes.com/design/eda-design/4207764/Performance-verification-of-a-complex-bus-arbiter-using-the-VMM-Performance-Analyzer

Share and Enjoy:
  • Print
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Google Buzz
  • LinkedIn
  • RSS
  • Twitter

Posted in Performance Analyzer, VMM infrastructure | No Comments »

1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading ... Loading ...