• 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

Pattern Generator

Timing and Waiting


A common workflow is to have a tester timing file defined and managed outside of Origen, in which case the main concern within your Origen source code is simply to refer to required timeset(s) by name.

However, Origen does also support a more complex timing definition where waveforms can be defined and mastered in Origen source code. That is discussed later on in the Complex Timing section.

Simple Timing

This guide gives an overview of some of the most common timing related methods, but to get a complete overview of what is available consult the OrigenTesters::Timing API.

A timeset declaration is used to provide information to Origen about what timeset to use for future test cycles and what period of time each cycle represents.

Normally this would be initialized within the startup method before generating any vectors as shown below:

class MySoCController
  include Origen::Controller

  def startup(options)
    $tester.set_timeset("mode_entry", 40)
  end
end

The first argument is the name of the timeset, this should correspond to how the timeset will be named within the test program, and the second argument is the cycle period in nano-seconds.

This method also accepts a block in which case the contained vectors will generate with the supplied timeset and subsequent vectors will return to the previous timeset automatically.

$tester.set_timeset("bist_50mhz", 20) do
  # Any cycles generated in here will use 20ns for the period
end

The arguments can also be supplied as a single array, or not at all. In the latter case the existing timeset will simply be preserved. This is useful if you have timesets that can be conditionally set based on the target.

# Target 1
$dut.readout_timeset = ["readout", 120]
# Target 2
$dut.readout_timeset = false

# This code is compatible with both targets, in the first case the timeset will switch
# over, in the second case the existing timeset will be preserved.
$tester.set_timeset($dut.readout_timeset) do
  # Generate readout vectors...
end

Creating a Timing Set

Currently the creation of the timing set for a given test platform must be done independently of Origen, however adding an Origen API for this is on the roadmap.

Waiting

All $tester models will support the following API to generate wait states in the test patterns.

Wait for specific number of cycles:

$tester.wait(cycles: 1000)

Wait for a period of time:

$tester.wait(time_in_us: 500)
$tester.wait(time_in_ms: 10)

A shorthand for the above cases is available:

500.us!  # Wait for 500us
10.ms!   # Wait for 10ms

Multiple times specified in different units will be added together, this can be useful if the delay is based on a complex calculation:

# Wait for 500us + 100 cycles
$tester.wait(time_in_us: 500, cycles: 100)

Waiting for an Event

All testers provide an API for generating match loops, these can be used to make the pattern wait dynamically for a pin-based or even a register-based event.

To do this enable the :match option and supply a block, within the block generate the vectors that will test if the condition has been met. Any time options passed in will be applied as a timeout, i.e. the maximum time to wait for the required condition to resolve.

Here are some examples:

# Wait for up to 1 second for the done bit to be set
$tester.wait(match: true, time_in_s: 1) do
  reg(:status_reg).bit(:done).read!(1)
end

# Wait for up to 1 second for the done pin to be set
$tester.wait(match: true, time_in_s: 1) do
  pin(:done).assert!(1)
end

Complex Timing

Defining Timesets

A wave object defines the waveform that should be applied to a pin, and each pin will have two wave objects assigned to it - one for drive cycles and one for cycles where the pin is being read/compared.

A timeset object is used to collect the wave objects for all pins. Multiple timesets can be defined and therefore the waveforms being applied to the pins can be changed by changing the timeset. The API to change the timeset is the same as that already discussed above:

tester.set_timeset('func', 100)

The period of the timeset can be referenced in the wave definitions to make the waveforms keep the same shape with different period settings.

Here is the most basic timeset definition, this will give all pins a default waveform which drives for the whole period on drive cycles, and strobes at 50% of compare cycles:

# Simple definition, all pins have default waves
add_timeset :t1

Here is a more complex definition, which changes the default compare for all pins to be at 25% of the period, and which adds a unique drive wave to the :tck pin to create a clock:

# Complex definition, defines an alternative default compare wave and specific timing for :tck
timeset :func do |t|
  t.compare_wave do |w|
    w.compare :data, at: "period / 4"
  end

  t.drive_wave :tck do |w|
     w.drive :data, at: 0
     w.drive 0, at: 25
     w.dont_care at: "period - 10"  # Just to show that dont_care can be used
  end
end

Pin groups can also be referenced in the timeset definition, here to add common drive timing to all pins in :gpio, and with a special compare waveform for :gpio5 only:

# Another timeset to show the wave assignment to pin groups
timeset :t2 do |t|
  t.compare_wave :gpio5 do |w|
    w.compare :data, at: 100
  end

  t.drive_wave :gpio do |w|
    w.drive :data, at: 200
  end
end

Note that the API currently only supports edge compares, i.e. window compares cannot be defined.

Additional drive and compare waves can be defined for any pin by giving them a code which can be used to select the given waveform in a pattern - i.e. the approach used by the V93K tester to select from multiple available waveforms.

Here is an example with a single pulse waveform defined for a clk pin which will be applied when the pin is driven to 1, and a double pulse waveform that will be applied when the pin is drive to T:

timeset :func do |t|
  t.drive_wave :tck do |w|
    w.drive 1, at: 0
    w.drive 0, at: 20
  end

  t.drive_wave :tck, code: 'T' do |w|
    w.drive 1, at: 0
    w.drive 0, at: 10
    w.drive 1, at: 20
    w.drive 0, at: 30
  end
end

Complex waveforms also support an initial, or default, period_in_ns, allowing set_timeset to be used without providing a period_in_ns:

dut.timeset :func do |t|
  t.period_in_ns = 10
end
  
tester.set_timeset(:func)
tester.timeset.period_in_ns
  #=> 10

dut.timesets[:func].period_in_ns = 11
tester.timeset.period_in_ns
  #=> 11

Consuming Timeset Data

Waveform Details

The currently selected timeset can be retrieved via the following methods which are aliases:

dut.timeset               # => instance of OrigenTesters::Timing::Timeset
dut.current_timeset

Even though this returns a OrigenTesters::Timing::Timeset object, its linked back to the corresponding Origen::Pins::Timing::Timeset object underneath. To access it directly:

dut.timeset.dut_timeset    #=> instance of Origen::Pins::Timing::Timeset

tester.timeset.dut_timeset #=> instance of Origen::Pins::Timing::Timeset

However, this is generally not needed, as anything that the OrigenTesters::Timing::Timeset cannot do is given to the corresponding Origen::Pins::Timing::Timeset instance.

Using the plural returns a hash containing all timesets, thereby allowing access to specific timesets regardless of the current selection:

dut.timesets          # => Hash
dut.timesets[:func]   # => instance of Origen::Pins::Timing::Timeset
# This will also work:
dut.timeset(:func)   

Each timeset stores the waves in two arrays, one for drive waves and another for the compare waves:

dut.timeset(:func).drive_waves    # => Array containing instances of Origen::Pins::Timing::Wave
dut.timeset(:func).compare_waves

Each wave object has an events array, and the pins assigned to it can also be retrieved:

drive_wave = dut.timeset(:func).drive_waves.first
compare_wave = dut.timeset(:func).compare_waves.first

drive_wave.events    # => [[0, :data], [50, 0], [75, :x]]
drive_wave.pins      # => Array of Origen::Pins::Pin instances

compare_wave.events  # => [["period / 2", :data]]

The method evaluated_events can be used to get the events with the formulas evaluated based on the current period:

compare_wave.events            # => [["period / 2", :data]]
compare_wave.evaluated_events  # => [[50, :data]]

The waves assigned to a given pin by the current timeset can also be accessed via the pin API:

dut.pin(:tms).compare_wave.events[0]   # => ["period / 2", :data]

The waveform associated with a particular code can be accessed as follows:

# The wave for driving 0 and 1 is returned by default, these are all aliases:
dut.pin(:clk).drive_wave.events       # => [[0, 1], [20, 0]]
dut.pin(:clk).drive_wave(0).events    # => [[0, 1], [20, 0]]
dut.pin(:clk).drive_wave(1).events    # => [[0, 1], [20, 0]]

dut.pin(:clk).drive_wave('T').events  # => [[0, 1], [10, 0], [20, 1], [30, 0]]
Timeset Details

You can retrieve the same current period information from this scope as from the tester.timeset scope:

tester.set_timeset(:test, 10)

tester.current_period
  #=> 10

dut.current_timeset_period
  #=> 10
  
dut.current_period_in_ns
  #=> 10

dut.current_period_in_seconds
  #=> 1/100000000

Timing During Execution

You can freely swap between timesets during pattern execution. For example, a sequence resembling below is perfectly valid:

# Start with an initial timeset for pattern startup
tester.set_timeset("timing_for_startup", 100)
tester.cycle
# ...

# Switch to a timeset for ramloading
tester.set_timeset("timing_for_ramload", 200)
tester.cycle
# ...

# Switch to a timeset for actual execution
tester.set_timeset("timing_for_execution", 100)
tester.cycle
# ...

The period for that timeset will be remembered, allowing simpler timeset switches back to existing ones:

# Execute using timing for SWD
tester.set_timeset("timing_for_swd", 40)
tester.timeset.period_in_ns
  #=> 40
# ...

# Execute using timing for an APB protocol
tester.set_timeset("timing_for_apb", 80)
tester.timeset.period_in_ns
  #=> 80
# ...

# Switch back to using SWD
tester.set_timeset("timing_for_swd")
tester.timeset.period_in_ns
  #=> 40
# ...

# Switch back to using APB
tester.set_timeset("timing_for_apb")
tester.timeset.period_in_ns
  #=> 80
# ...

# End the pattern using SWD
tester.set_timeset("timing_for_swd")
tester.timeset.period_in_ns
  #=> 40
# ...

The #cycled? method to denotes if the timeset has been cycled or not:

tester.set_timeset(:cycle_test, 10)
tester.timeset.name
  #=> "cycle_test"
tester.timeset.cycled?
  #=> false

tester.cycle
tester.timeset.cycled?
  #=> true

tester.set_timeset(:cycle_test_2, 11)
tester.timeset.cycled?
  #=> false

tester.timesets[:cycle_test].cycled?
  #=> true
tester.timesets[:cycle_test_2].cycled?
  #=> false

Comments

Generated with the Origen Semiconductor Developer's Kit

Origen is released under the terms of the MIT license