Skip to Content

Offsite blog roll

Posted on  by  from the site Verification Martial Arts
Yaron Ilani, Apps. Consultant, Synopsys If you missed part 1 or part 2 of this series don’t worry, you can go on reading and catch up with the previous parts later on. Today I’m going to show you a small, yet very powerful feature in DVE that you may not be aware of. Remember the last time you had to count clock cycles in the waveform window? Sometimes this is a quick way to verify that an internal counter behaves correctly or that a signal goes up just at the right clock edge. Remember how frustrating it is when you lose count for some reason and have to start over? Remember how you’re never 100% sure about the result even if you calculated the time difference between the left and right cursors and divided by the clock period? If your answer was yes to any of those questions then you’re going to love the Grid feature. What Grid simply does, as its name suggests, is draw a grid on top of the waveform. Here’s what it looks like: If you click on the Grid button (in the red circle) the Grid will show up as dotted lines. As you can see, the Grid can count clock cycles for you. You can set it up to count falling edges, rising edges or any edge. You can also set the range either by entering the start time and end time, or simply by placing the cursors at the desired points and clicking on the “Get Time From C1/C2” button in the Grid Properties window: The Grid Properties window lets you have even more control over the grid. For example – you can set its cycle time to a custom value. This could be very useful if you want to be able to visually inspect drifting clocks or duty-cycle issues, etc. In short, the Grid is one of those little things that make a big difference when it comes to efficient debugging and you should definitely become familiar with it. If you ever have to count clock cycles again, remember that you no longer have to do this manually. You don’t even have to make any calculations. Simply launch the Grid and voila! If you’d like to learn more about DVE’s advanced debug features check out part 1 and part 2 of this series. ...
Automation, Debug, DVE, grid, waveform
Posted on  by  from the site Verification Martial Arts
Yaron Ilani, Apps. Consultant, Synopsys In Part 1 of this series we discussed how SystemVerilog macros might add complexity when it comes to debugging your test bench and how DVE can make your life much easier in that area. Today we’re going to show you another cool feature in DVE that if used wisely, could save you a significant amount of time when debugging. Let’s recall for a moment the two main use models of DVE – Post Processing and Interactive. The former is where you’re debugging your simulation results after it has completed. The latter is where you’re running and debugging your simulation simultaneously, trading off performance for enhanced debug capabilities. Today we shall focus on the interactive mode. We’re about to see how the Interactive Rewind feature will help you minimize your debug turnaround time. So during a typical interactive session you put a breakpoint somewhere in your code and let the simulation run until it reaches your breakpoint. From that point and on you take the controls and advance the simulation step by step which allows you to inspect your signals or variables very closely. You may assign different values to signals along the way to try out potential workarounds or fixes. Now here’s the tricky part: simulation can only advance forward! So if your breakpoint occurs late in the simulation, every time you want to restart your debug trail you’re bound to wait for the simulation to rerun from the beginning. Why would you want to start over? Well, you might want to change a signal value (remember you can force signals interactively in DVE). But more often than not, stepping through your code gets very complicated and you might miss the interesting point where the bug occurs, or you lose track of the debug trail and need to start fresh. In certain cases your breakpoints occur periodically (e.g. every time a packet is transmitted) and you just wish you could go back in time to the previous occurrence to step through the code again. The good news is that DVE allows you to navigate backwards in simulation!  We call it Interactive Rewind. All you have to do is set up one or more checkpoints along the simulation. Upon each checkpoint a snapshot of the simulation is saved and you may use it later on to literally go back in time. To give you a sense of how easy it is to work with checkpoints, here’s how it looks like – the left button is used to add a new checkpoint. The right button will be used later to rewind to any of the previously added checkpoints. Selecting a checkpoint to rewind to is easy: simply select one from the drop-down menu: You can also control your checkpoints via UCLI, where you’ll find many advanced features such as the ability to add periodic checkpoints automatically. To sum up, interactive debugging with DVE becomes much more efficient with Interactive Rewind. Simply add checkpoints at strategic points along the debug trail. Then use Step/Next to advance simulation time and Rewind to go back. This will keep your debug turnaround time to minimum, thus enabling you to focus on debugging and not waiting. ...
Posted on  by  from the site Verification Martial Arts
Yaron Ilani, Apps. Consultant, Synopsys A SystemVerilog test bench could get quite complex. Typical projects today have thousands of lines of code, and the number is constantly on the rise. However, standard base class libraries such as VMM and UVM can help you minimize the amount of code that needs to be rewritten by providing a rich set of macros that substitute long lines of code with a single line. For example, the simple line `vmm_channel(atm_cell) defines a standard VMM channel for an ATM cell with all the necessary fields and methods, all under the hood. All you have to do is instantiate the newly defined channel wherever you need it.A close cousin to the SV macro is the `include directive which basically substitutes an entire file with a single line. This is a neat way to reuse files and enhance code clarity. But what happens when you need to debug your source code? Indeed, macros and `includes allow for less clutter and enhanced readability, but at the same time hide from you pieces of code you might actually need access to during debug. Fortunately enough, DVE ships with some new cool features that give you quick and easy access to any underlying code and thus taking the pain out of debugging a SystemVerilog test bench. Let’s see some of them: Macro Tooltips Hover your mouse over a macro statement and a tooltip window will pop up displaying the underlying code – very useful for short macros. The tooltip’s height can be customized to your liking! Macro Expand/Collapse Macros can be expanded interactively so that the underlying source code is presented in the source file you are viewing. Very powerful! Hyperlinks / Back / Forward Clicking on a macro/include statement will take you to the original source code or file. Don’t worry, you can always go back and forth using the browser-like Back/Forward buttons. To sum up, DVE offers a really comfortable way to debug your SystemVerilog source code – be it plain code, macros or `included files. While keeping you focused on the important part of your code, DVE provides quick and easy access to any underlying code. And thanks to the Back & Forward buttons you can skip back and forth between macros, `included files and your main source file as smoothly as you would in your internet browser. This really takes the pain out of debugging a modern SystemVerilog test bench. ...
Posted on  by  from the site Verification Martial Arts
John Aynsley, CTO, Doulos In the previous blog post I introduced the VCS TLI Adapters for transaction-level communication between SystemVerilog and SystemC. Now let’s look at the various coding styles supported by the TLI Adapters, and at the same time review the various communication options available in VMM 1.2. We will start with the options for sending transactions from SystemVerilog to SystemC. VMM 1.2 allows transactions to be sent through the classic VMM channel or through the new-style TLM ports, which come in blocking- and non-blocking flavors. Blocking means that the entire transaction completes in one function call, whereas non-blocking interfaces may required multiple function calls in both directions to complete a single transaction: On the SystemVerilog side, transactions can be sent out through blocking or non-blocking TLM ports, through VMM channels or through TLM analysis ports. On the SystemC side, transactions can be received by b_transport or nb_transport, representing the loosely-timed (LT) and approximately-timed (AT) coding styles, respectively, or through analysis exports. In the TLM-2.0 standard any socket supports both the LT and AT coding styles, although SystemVerilog does not offer quite this level of flexibility, and hence neither does the TLI. Now we will look at the options for sending transactions from SystemC back to SystemVerilog. Not surprisingly, they mirror the previous case: On the SystemC side, transactions can be sent out from LT or from AT initiators or through analysis ports. On the SystemVerilog side, transactions can be received by exports for blocking- or non-blocking transport, by vmm_channels, or by analysis subscribers. Note the separation of the transport interfaces from the analysis interfaces in either direction. The transport interfaces are used for modeling transactions in the target application domain, whereas the analysis interfaces are typically used internally within the verification environment for coverage collection or checking. In the SystemVerilog and SystemC source code, the choice of which TLI interface to use is made when binding ports, exports, or sockets to the TLI Adapter, for example: // SystemVerilog `include “tli_sv_bindings.sv” import vmm_tlm_binds::*;           // For port/export import vmm_channel_binds::*;       // For channel tli_tlm_bind(m_xactor.m_b_port,    vmm_tlm::TLM_BLOCKING_EXPORT,    “sv_tlm_lt”); tli_tlm_bind(m_xactor.m_nb_port,   vmm_tlm::TLM_NONBLOCKING_EXPORT, “sv_tlm_at”); tli_tlm_bind(m_xactor.m_b_export,  vmm_tlm::TLM_BLOCKING_PORT,      “sc_tlm_lt”); tli_tlm_bind(m_xactor.m_nb_export, vmm_tlm::TLM_NONBLOCKING_PORT,   “sc_tlm_at”); tli_channel_bind(m_xactor.m_out_at_chan, “sv_chan_at”, SV_2_SC_NB); // SystemC #include “tli_sc_bindings.h” tli_tlm_bind_initiator(m_scmod->init_socket_lt, LT, “sc_tlm_lt”,true); tli_tlm_bind_initiator(m_scmod->init_socket_at, AT, “sc_tlm_at”,true); tli_tlm_bind_target   (m_scmod->targ_socket_lt, LT, “sv_tlm_lt”,true); tli_tlm_bind_target   (m_scmod->targ_socket_at, AT, “sv_tlm_at”,true); tli_tlm_bind_target   (m_scmod->targ_socket_chan_at, AT, “sv_chan_at”,true); Note how the tli_tlm_bind calls require you to specify in each case whether the LT or AT coding style is being used. The root cause of this inflexibility is certain language restrictions in SystemVerilog, in particular the lack of multiple inheritance, which makes it harder to create sockets that support multiple interfaces. Hence, in SystemVerilog, the blocking- and non-blocking interfaces get partitioned across multiple ports and exports. In the SystemC TLM-2.0 standard there is only a single kind of initiator socket and a single kind of target socket, each able to forward method calls of any of the core interfaces, namely, the blocking transport, non-blocking transport, direct memory, and debug interfaces. In summary, the VCS TLI provides a simple and straightforward mechanism for passing transaction in both directions between SystemVerilog and SystemC by exploiting the TLM-2.0 standard. ...
Posted on  by  from the site Verification Martial Arts
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. 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. ...
Posted on  by  from the site Verification Martial Arts
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. ...
Posted on  by  from the site Verification Martial Arts
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);  $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: 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. ...
Uncategorized
Posted on  by  from the site Verification Martial Arts
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. Another useful tool for transaction debugging is using the $msglog task which will be discussed in the next article “Hyperlink….”. ...
Posted on  by  from the site Verification Martial Arts
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. ...
Uncategorized
Posted on  by  from the site Verification Martial Arts
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. ...
VMM