• Guides
  • Videos
  • Publications
  • API
  • Github
  • Community
  • Release Notes
  • Plugins
Installing Origen
  • Introduction
  • How to Install
  • How to Install (Windows)
  • Company Customization
  • Understanding Gems
  • Invoking Considerations
  • Workspace Management
Getting Started with Origen
  • Core concepts
  • Creating a New App
  • Directory Structure
  • The Initial Commit
  • Creating New Files
  • Understanding Blocks
  • Application Architecture
Runtime Environment
  • Introduction
  • Mode
  • Environment
  • Target
  • Production Targets
  • Global Setup
  • Load Order
  • Programming
Models
  • Introduction
  • Naming
  • Definition & Hierarchy
  • Adding Attributes
  • Versioning
  • Bugs & Features
  • Package, Mode & Configuration
  • Registers
  • Pins
  • Power Domains
  • Hardware Attributes
  • Parameters
  • Specifications
  • Fuses
  • Generic Components
  • Creating Your Own Components
Compiler (Views)
  • Introduction
  • Creating Templates
  • Using Sub-Templates
  • Helpers
  • Running The Compiler
  • Inline Compiler
Controllers
  • Introduction
  • Shadow Controllers
  • Direct Controllers
Pattern Generator
  • Introduction
  • Creating Patterns
  • Pins
  • Timing and Waiting
  • Registers
  • Documenting Patterns
  • Generating by Name
  • Common API
  • J750 API
  • V93K API
  • UltraFlex API
  • STIL & Other Formats
  • Custom Testers
  • Running The PatGen
  • Concurrent Patterns
Test Program Generator
  • Introduction
  • Philosophy
  • Creating Flows
  • Managing Flow Control
  • Creating an Interface
  • Additional Resources
  • Dynamic Custom Code
  • Characterization API
  • J750 API
  • V93K Common API
  • V93K SMT7 API
  • V93K SMT8 API
  • UltraFLEX API
  • Documenting the Program
  • Creating Custom Testers
  • Running the ProgGen
Decompilation
  • Overview & Example
  • Decompiling, Adding Pins, & Executing
  • Working with Decompiled Patterns
  • Platform Specifics
Simulation
  • Introduction
  • How It Works
  • Compiling the DUT
  • AMS Support
  • Environment Setup
  • Application Setup
  • Simulating Patterns
  • Simulating Flows
  • Direct DUT Manipulation
  • Simulator Log Output
  • Artifacts
  • Debugging
Documentation Generator
  • Introduction
  • Markdown
  • Linking
  • Styling
  • Testing
  • API Generation
  • Deploying
Plugins
  • Introduction
  • Using a Plugin
  • Creating a Plugin
  • Current & Default Plugins
  • Dev Environment
  • Dev Considerations
  • Paths & Origen.root
  • Config & Origen.app
Miscellaneous
  • Revision Control
  • Origen Remotes
  • Lint Testing
  • Session Store
  • LSF API
  • Users, Emails & Maillists
  • Utilities & Helpers
  • Ruby Extensions
  • Logger
  • Adding Commands
  • Overriding Commands
  • Callbacks
  • Application Callbacks
  • Miscellaneous Topics
Advanced Topics
  • Introduction
  • Invocation Customization
  • Custom App Generators

Simulation

Debugging


This guide describes some features that are useful for debugging a failing simulation or when running an interactive ad-hoc simulation from the console.

Identifying Points in Time

All major step comments in pattern source code will be simulation time-stamped and output to the console as shown below:

ss "Test basic 2-pin match loop"

# ...

ss "Test a block match loop"

# ...

The above will produce this output where the simulation time when each operation occurs is given in ns:

[INFO]       6.710[0.000]    ||      991700 ns: ===========================================================
[INFO]       6.710[0.001]    ||      991700 ns: Test basic 2-pin match loop
[INFO]       6.711[0.001]    ||      991700 ns: ===========================================================
[INFO]       6.711[0.001]    ||     1207500 ns: ===========================================================
[INFO]       6.712[0.000]    ||     1207500 ns: Test a block match loop
[INFO]       6.712[0.000]    ||     1207500 ns: ===========================================================

All log output created by application code will also be simulation time-stamped and synchronized to simulator progress:

Origen.log.info "Something important is happening!"
[INFO]       6.712[0.000]    ||    24210200 ns: Something important is happening!

Note that applications should avoid the use of puts to output debug or status information to the console because that will occur immediately and will not be time-stamped or synchronized with the simulator.

Additionally, the Origen testbench contains a register that can be used to mark events like this:

tester.marker = 0x1234

You can then view signal origen.debug.marker in a wave viewer and search for the appropriate value to locate the specific point in time when the mark was applied.

When generating for non-simulation tester targets that statement will do nothing and it can be safely left in production code.

Failed Register Reads

The following information is logged whenever a pin mis-compare occurs within a register read transaction:

  • The path (name) of the register
  • Expected data
  • Received (actual) data - requires support from the physical driver, see below
  • Application stack trace to identify where in the application code the read originated

Here is an example:

ss "Now try and read and write a register"
dut.cmd.write!(0x1234_5678)
dut.cmd.read!(0x1234_5078)  # Note that we just wrote ..567.. but are now expecting ..507..
[INFO]       4.098[0.000]    ||       86500 ns: ======================================================================
[INFO]       4.098[0.001]    ||       86500 ns: Now try and read and write a register
[INFO]       4.099[0.001]    ||       86500 ns: ======================================================================
[ERROR]      4.100[0.001]    ||      117400 ns: Miscompare on pin tdo, expected 0 received 1
[ERROR]      4.101[0.001]    ||      117600 ns: Miscompare on pin tdo, expected 0 received 1
[ERROR]      4.102[0.001]    ||      129100 ns: Errors occurred reading register cmd:
[ERROR]      4.102[0.001]    ||      129100 ns: cmd.d: expected 12345078 received 12345678
[ERROR]      4.103[0.001]    ||      129100 ns:
[ERROR]      4.104[0.001]    ||      129100 ns: /home/stephen/Code/github/origen_sim/pattern/test.rb:39:in `block in <top (required)>'
[ERROR]      4.110[0.006]    ||      129100 ns: /home/stephen/Code/github/origen_sim/pattern/test.rb:1:in `<top (required)>'

The received data resolution does depend on the physical protocol driver supplying meta-data when creating pin assertions (reads) that correspond to register bits, like this:

pin(:tdo).assert(bit.data, meta: { position: bit.position })

For reference, here is the update that was made to the JTAG driver to support this feature - OrigenJTAG - Add meta data for OrigenSim

If the driver has not provided this then a warning will be output and no received data will be given.

In some cases the protocol being used may generate failed compares that contain bit position meta data that does not map to the register being read. In this case a generic message will be displayed with the bit position that failed along with the register that was being read. It may be desirable to have application specific code interpret these bits (for example if it is a status bit). Origen provides a hook for this. Below is an example of how to implement a custom interpreter.

if tester.sim?
  tester.out_of_bounds_handler = proc do |position, received, expected, reg|
    Origen.log.error "Got data ouside of #{reg.name} during the transaction, bit[#{position}]: expected #{expected}, received #{received}"
    Origen.log.error "ECC error during read of #{reg.path}.#{reg.name}" if position == 39
  end
end

This feature will automatically be enabled for any reads launched via the register object itself, e.g. my_reg.read!, but not for reads launched by calling the read_register method manually, e.g. dut.read_register(my_reg).

If your application has a tendency to do the latter, then the following modification can be made to your read_register method to make OrigenSim aware of such transactions:

def read_register(reg_or_value, options = {})
  # Make OrigenSim aware of all transactions to enable failed transaction reporting
  tester.read_register(reg_or_value, options) do

    # Existing implementation here

  end
end

This feature will also work in the case of the read object being a value rather than a register object.

Interactive Debugging

The execution of an Origen simulation is fully controlled by Origen/Ruby, this means that if you insert a regular Ruby debugger breakpoint into your application code then you can step through the simulation in real time.

When a simulation is running most of the communication is one-way, Origen tells the simulator what to do, and for performance reasons there is no handshake between Origen and the simulator at every instruction. Instead, Origen fires off instructions into a buffer, the simulator executes them as fast as it can, and then Origen periodically waits for the simulator to catch up if it is running too far ahead.

If you have entered a breakpoint and you suspect that the simulator may be still catching up, you can run:

tester.sync_up

When that method returns you are guaranteed that the simulator is at the same point.

Note that most of the time you will not need to do this manually since Origen will automatically sync up for any operation that involves reading data from the simulation, e.g. peeking or reading registers.

If you have a wave viewer open during the debug session it may still look like the simulation is running behind, or the wave viewer may appear to hang if you have tried to refresh it. This is because the simulator is buffering output that has yet to be written to the wave dump.

You can force it to flush the buffer and update the wave viewer by running:

tester.flush

Note that flushing will internally call a sync_up so you don’t have to do both of these manually.

Accessing the DUT’s Internal Nets

The methods tester.simulator.peek and tester.simulator.poke are available for reading and writing values to internal DUT nets respectively.

See the section on Direct DUT Manipulation for more details on these.

Register Reading (and Writing)

Referencing a register from the console will show you what Origen thinks the register currently contains:

dut.my_block.my_reg

=>
0x10008000 - :my_reg
   ===============================================================================================================
  │     15      │     14      │     13      │     12      │     11      │     10      │      9      │      8      │
  │                                                    d[15:8]                                                    │
  │                                                      0x0                                                      │
  ├─────────────┼─────────────┼─────────────┼─────────────┼─────────────┼─────────────┼─────────────┼─────────────┤
  │      7      │      6      │      5      │      4      │      3      │      2      │      1      │      0      │
  │                                                    d[7:0]                                                     │
  │                                                      0x0                                                      │
  └─────────────┴─────────────┴─────────────┴─────────────┴─────────────┴─────────────┴─────────────┴─────────────┘

However, this may not be what the simulation currently reflects. To see what the simulation holds, call sync on the register:

dut.my_block.my_reg.sync

=>
0x10008000 - :my_reg
   ===============================================================================================================
  │     15      │     14      │     13      │     12      │     11      │     10      │      9      │      8      │
  │                                                    d[15:8]                                                    │
  │                                                     0x8E                                                      │
  ├─────────────┼─────────────┼─────────────┼─────────────┼─────────────┼─────────────┼─────────────┼─────────────┤
  │      7      │      6      │      5      │      4      │      3      │      2      │      1      │      0      │
  │                                                    d[7:0]                                                     │
  │                                                     0xFF                                                      │
  └─────────────┴─────────────┴─────────────┴─────────────┴─────────────┴─────────────┴─────────────┴─────────────┘

A convenience API exists for this by appending ! to the register name:

dut.my_block.my_reg!

Note that this works by firing off a conventional read_register request to your DUT model/controller.

That means that it should work for breakpoints in the majority of application code, however if you have stopped it or stepped into a place in low-level code, such as in the middle of a prior transaction, then this feature may not work.

Advancing Time

If you have just written to a register, it may kick off some operation within the DUT and time (clock cycles) will be required before you will be able to observe the response.

It is important to understand that the simulator is also paused during a breakpoint and therefore simulation time is not continuing to run while you are at the console.

Simulation time can be advanced by calling the usual tester.wait API, however these convenience APIs exist for advancing time during a debugger breakpoint (and they can also be used in source code if you wish):

10.cycles

10.ns!

10.us!

10.ms!

10.s!

Note that 10 is just used as an example here, you can apply any number that you want.

Checking The Error Count

You can query the current error count by running:

tester.simulator.error_count   # => 0

Comments

Generated with the Origen Semiconductor Developer's Kit

Origen is released under the terms of the MIT license