• 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

Decompilation

Working with Decompiled Patterns


The Universal API attempts to tie all supported platforms together and provide a generic yet flexible interface with which decompiled patterns from any source or any platform can be treated the same.

This obvisously has its limitations and various hooks are in place to still allow for platform-specifics to peek through the universal API but the API detailed in this section should be applicable across all supported platforms.

The OrigenTesters::Decompiler::Pattern base class provides this interface. All supported platforms should return a class which inherits from this. For example,

OrigenTesters::IGXLBasedTester::Pattern.ancestors.include?(OrigenTesters::Decompiler::Pattern)
  #=> true

Decompiling

The preferred method to decompile a pattern source is to use the Decompiler API but you can choose to instantiate the decompiler yourself, provided you know the decompiler you want to use.

To decompile a source from the J750, we’ll instantiate a new OrigenTesters::IGXLBasedTester::Pattern class:

pat = OrigenTesters::IGXLBasedTester::Pattern.new('path/to/src.atp')

Instantiating the decompiler does not automatically decompile it. However, a call to #decompile will do just that.

pat.decompiled?
  #=> false

pat.decompile
pat.decompiled?
  #=> true
Recall that #decompile on the Decompiler API does this automatically.

Any decompiler will accept the pattern source as either:

  1. A string containing the filename of the pattern source.
  2. A Pathname containing the filename of the pattern source.

At times, such as in the example, you’ll want to decompile a text source directly. Indicate this by providing the direct_source: true option during instantiation:

direct_pat = OrigenTesters::IGXLBasedTester::Pattern.new('path/to/src.atp', direct_source: true)

The decompiled pattern will keep track of its source and whether it was direct or not:

pat.direct_source?
  #=> false
pat.source
  #=> /home/origen_stuff/origen_testers/approved/j750/decompiler/sample/sample.atp

direct_pat.direct_source?
  #=> true
direct_pat.source
  #=>
"// Sample pattern text for the J750
// Source located at: lib/origen_testers/igxl_based_tester/decompiler

import tset tp0;
svm_only_file = no;
opcode_mode = extended;
compressed = yes;

vector ($tset, tclk, tdi, tdo, tms)
{
start_label pattern_st:
// Start of vector body
repeat 2 > tp0 X X X X ; // First Vector
repeat 5 > tp0 1 0 X 1 ;
end_module   > tp0 X X X X ; // Last Vector
}"
The entire direct source is stored in the decompiler as the source, so its not recommended to use direct sources for large patterns.
Although it is possible to instantiate a OrigenTesters::Decompiler::Pattern object yourself, this is just the base class and has no grammars. Any attempts to actually work with this class directly will not end well. See the supported platforms page for the class names of the actual decompiler implementations.

Adding Pins and Executing

Both of these have been covered in the example, but for a quick review:

# Add any missing pins to the DUT, returning the pins that were added
dut.pins
  #=> {}
pat.add_pins
  #=> [:tclk, :tdi, :tdo, :tms]
dut.pins
  #=> {:tclk=><Origen::Pins::Pin:47002367230860>, :tdi=><Origen::Pins::Pin:47002367845200>, :tdo=><Origen::Pins::Pin:47002367937280>, :tms=><Origen::Pins::Pin:47002368138760>}

# Execute the vectors
pat.execute

Sections

As also shown in the example, the pattern is divided into sections: the frontmatter, the pinlist, and the vector body elements.

The frontmatter and pinlist are parsed fully, stored in memory, and can be accessed directly.

# Access the pattern's frontmatter
pat.frontmatter
  #=> #<OrigenTesters::Decompiler::Pattern::Frontmatter:0x0000557f30c91170>

# Retrieve the pattern header
pat.frontmatter.pattern_header
  #=> ["// Sample pattern text for the J750", "// Source located at: lib/origen_testers/igxl_based_tester/decompiler"]
# Access the pattern's pinlist
pat.pinlist
  #=> #<OrigenTesters::Decompiler::Pattern::Pinlist:0x0000557f30c90360>

# pat.pinlist.pins
  #=> [:tclk, :tdi, :tdo, :tms]

Methods to retrieve the pins are also available directly on the decompiled pattern:

# Return an array of pins, in the order they appear in the pattern
pat.pins
  #=> [:tclk, :tdi, :tdo, :tms]

# Return the pin names and their respective size
pat.pin_sizes
  #=> {:tclk=>1, :tdi=>1, :tdo=>1, :tms=>1}

See the OrigenTesters API for full details on the Frontmatter and the Pinlist.

Platform Specifics

The frontmatter and pinlist may contain platform-specific setup information. Any platform-specifics should be documented on the supported platforms page, but some methods are provided to programmatically check what’s available. For the example pattern, using the J750 decompiler:

pat.frontmatter.platform_nodes
  #=> [:variable_assignments, :imports]

pat.pinlist.platform_nodes
  #=> []

Any platform node will have an accessor associated with it:

pat.frontmatter.imports
  #=> {"tp0"=>"tset"}

pat.frontmatter.variable_assignments
  #=> {"svm_only_file"=>"no", "opcode_mode"=>"extended", "compressed"=>"yes"}

Obviously, these nodes are decompiler specific, but what's less obvious is that the implementation, and return values, are not defined by the universal API. Two decompilers that both implement the same platform node may do so differently and even have different meanings.

This is also used in conjunction with vectors.

Initial State

For some functions, it’ll be essential to know the initial state of the pattern. This may include the initial pin states, the initial timeset, or the first vector, in its entirety.

First Vector

An exception to the don’t store vectors in memory rule is the first vector. The first vector contains the initial state of the pattern, the initial timeset, and, in many text representations, the sizes of the pins in the pinlist. This vector is always available:

# Access the first vector
pat.first_vector
  #=> OrigenTesters::Decompiler::Pattern::Vector

An important observation is that this returns the first vector, not the first vector body element.

Some platforms, such as the v93k, do not actually require a vector in the pattern source. In these cases, retrieving the first vector or querying aspects of the decompiled pattern that relies on the first vector will raise an OrigenTesters::Decompiler::ParseError exception with a message containing "Could not locate the first vector". See the approved V93K simple pattern for an example of such a pattern.
First Pin States and First Timeset

Assuming the first vector is available, you can retrieve the first pin states and the first timeset directly. In the context of the example pattern:

# Retrieve the initial pin states
pat.initial_pin_states
  #=> ["X", "X", "X", "X"]

# Retrieve the initial timeset
pat.initial_timeset
  #=> tp0

Vector Body Elements

The final section is the vector body elements. This is a collection of not just the vectors, but everything that may appear interweaved with them.

The most common non-vector will most likey be comment blocks which may be scattered about among the actual vectors. Other non-vectors will be platform specific. Some examples include labels, on the J750 or Ultraflex, or sequencer instructions, on the V93K.

Anything in the vector body will have a class of OrigenTesters::Decompiler::VectorBodyElement, which serves as a placeholder for anything that may come along and provides the means to further decide how this particular element should be interfaced with.

Types

Every vector body element will have a type, which is assigned during decompilation - all you need to do is retrieve it, using the #type method. For a comment this will be :comment_block. For a bonafide vector, this will be :vector.

Platforms can, and will, interject their own types. A label in a .atp pattern source will have type :label. Retrieving and interacting with element types will be shown throughout the remainder of this section.

Retrieving the Element

Knowing what type the element is lets you know what kind of accessors this element should have. You would expect anything of type :vector to have pin_states, but you would not expect the :comment_block type to. However, the vector body element is just a placeholder, so the #element method must be used to retrieve the underlying element that actually contains the content. Examples of this are shown in the sections below.

Elements

The universal API supports two built-in vector types: comment blocks and vectors.

Comments

The simplest vector body element to start working with is a comment_block. The decompiler will mash sequential comment lines in the pattern together to form a single vector body element of type :comment block. A shorthand method is provided to indicate when a comment block is encountered:

vector_body_element.is_a_comment?
  #=> true/false

There’s only so much which can be done with comments. The main operation will be retrieving them:

comment_block.comments
  #=> [
  #=>   "Any comments in the block..."
  #=>   "Separated by newlines (or whatever the platform separator is)"
  #=>   "Will be its own array entry."
  #=> ]

Note that the comments are returned as an Array of all the comments mashed together. This is standard though, so a standalone, single-line comment will be an Array of size 1.

You can view the API here, but there’s not much more to comment blocks than that.

Vectors

Vectors, on the other hand, have a bit more going on.These will have type :vector and a vector body element shorthand method is available here as well:

vector_body_element.is_a_vector?
  #=> true/false

Any vector, from any platform, is expected to provide a timeset, repeat, pin_states, and comment accessor. The comment in this case is a end-of-line comment, sharing the same line with the vector.

Each of these can be retrieved for any vector, but its best to see it as an example. Returning to the example pattern, we can retrieve all this content from the first vector:

pat.first_vector.timeset
  #=> tp0

pat.first_vector.pin_states
  #=> ["X", "X", "X", "X"]

pat.first_vector.repeat
  #=> 2

pat.first_vector.comment
  #=> First Vector

Every vector is expected to contain at least these accessors. Some may be empty, but the accessor should always work (no undefined method... errors).

The platform, will likey want to throw in its own content that it considers part of a standard vector, such as opcodes, for the J750. The platform will register these as platform nodes, and any platform node will have a corresponding accessor. For example, the J750 registers opcode and opcode_arguments as platform nodes so, when we’ve decompiled using the J750 decompiler, we’ll have access those as well:

pat.first_vector.opcode
  #=> repeat

pat.first_vector.opcode_arguments
  #=> ["2"]

Trying these platform nodes on other decompilers is not guaranteed to give you anything. Check the platform specifics for any additional platform nodes placed on the vector type.

You can also list the platform nodes programmatically using #platform_nodes:

pat.first_vector.platform_nodes
  #=> [:opcode, :opcode_arguments]
Platform

The question of ‘what was the decompiler?’ may come up for complex scripts geared towards handling different pattern sources or supporting various platforms. The #decompiler method will return the decompiler used (which is, not coincidentally, the class of the decompiled pattern object).

pat.decompiler
  #=> OrigenTesters::IGXLBasedTester::Pattern

In cases where decisions are made depending on the decompiler, the #decompiler?(<platform>) method queries if the decompiled pattern was decompiled using the given platform:

pat.decompiler?(OrigenTesters::IGXLBasedTester::Pattern)
  #=> true

pat.decompiler?(OrigenTesters::SmartestBasedTester::Pattern)
  #=> false

Now that we have all the tools to deal with vector types and the underlying elements, we can begin to interface with the vector body itself.

Iterating Through Vectors

The simplest operation is just to iterate through all the available vector body elements. The given block will be run for each one sequentially:

# Iterate through all vector body elements, running the given block for each one.
# For example, to print the type of each vector body element:
pat.each_vector { |v| puts v.type }
:start_label
:comment_block
:vector
:vector
:vector

# Do the above, but with the index
pat.each_vector_with_index { |v, i| puts "Type at index #{i}: #{v.type}" }
"Type at index 0: start_label"
"Type at index 1: comment_block"
"Type at index 2: vector"
"Type at index 3: vector"
"Type at index 4: vector"

This is the basis for working with the vector body elements section and from this more complex operations are derived. For example, to cycle the tester for each vector in the vector body:

pat.each_vector do |v|
  if v.is_a_vector?
    tester.cycle(repeat: v.element.repeat)
  end
end

EnumerableExt

Due to the non-standard #each method implementation, the Enumerable mixin cannot be used directly. However, some select Enumerable methods are implemented:

# Collect all the vectors, after having run the given block.
# For example, to collect all the types that appear in the example pattern:
pat.collect { |v| v.type }
  #=> [:start_label, :comment_block, :vector, :vector, :vector]

# Filting out duplicates...:
pat.collect { |v| v.type }.uniq
  #=> [:start_label, :comment_block, :vector]

# Find the first vector for which the block returns true.
# For example, to find the first vector body element that is of type vector:
pat.find { |v| v.is_a_vector? }
  #=> OrigenTesters::Decompiler::Pattern::VectorBodyElement
pat.find { |v| v.is_a_vector? }.type
  #=> :vector

# Find all the vectors for which the block returns true:
# For example, to find the all vector body elements that are of type vector:
pat.find_all { |v| v.is_a_vector? }
  #=> [OrigenTesters::Decompiler::Pattern::VectorBodyElement, OrigenTesters::Decompiler::Pattern::VectorBodyElement, OrigenTesters::Decompiler::Pattern::VectorBodyElement]

# Find all the vectors after filting out those for which the block returns true:
# For example, to find the all vector body elements that are NOT of type vector:
pat.reject { |v| v.is_a_vector? }
  #=> [OrigenTesters::Decompiler::Pattern::VectorBodyElement, OrigenTesters::Decompiler::Pattern::VectorBodyElement]

See the API for a full listing.

If #collect is used without any block provided, all vector body elements will be returned. This may not bode well for large patterns that contain thousands, or even hundreds of thousands, of vectors.

Vector At

At times, you may want to grab a vector body element at a specific index. The method #vector_at(i) decompiles and returns the vector at the index, i:

pat.vector_at(3)
  #=> OrigenTesters::Decompiler::Pattern::VectorBodyElement

pat.vector_at(3).type
  #=> :vector

pat.vector_at(3).element
  #=> OrigenTesters::Decompiler::Pattern::Vector

pat.vector_at(3).element.repeat
  #=> 5

pat.vector_at(4).element.repeat
  #=> 1

Observe that #vector_at is a bit of a misnomer, as it actually returns the vector body elements at i, not necessary one of type vector.

Please, please, please see the contextual notes below if planning to use #vector_at(i) extensively.

Contextual Notes For vector_at

Recall that the vector body is not stored in memory, so direct access to a given vector index is not inheritently supported. Therefore, each time #vector_at(i) is called, the decompiler simply runs #each_vector_with_index and bails once the given index is hit, returning the vector body element at that index. This gives the desired behavior, but comes at a cost…

That cost being runtime. In a normal array, you’d expect to retrieve a vector body element in constant time, O(1); however, iterating through the vector body behaves akin to that of a linked list, where the retrieval time for an arbitrary vector body element is O(n).

Extending this a bit: if using #each_vector, to iterate over vector body elements from 0 to n, you’ll get O(n) runtime. However, if using #vector_at(i) where i is the range from 0 to n, you’ll actually get a O(n^2) runtime, as it starts from the beginning each time it retrieves a vector.

Recall that the goal of not decompiling and storing the entire vector body at once is to decouple the peak memory and CPU usage with the size of pattern source.

So, although more complex operations that require working with single vectors may take a drastic runtime hit as compared to storing all vectors in memory, the resource requirements of such operations will not, allowing the decompiler to operate on absolutely massive patterns, even when run on sub-optimal machines.

If you have the computational resources (such as access to a distributed system, as most corporations will have) or you know that the pattern sizes you'll be working with are limited to what your system can handle, the enumerable method #collect can be used to grab the entire vector body and store it in memory.

tl;dr: if jumping around the vector body, pre-processing, or otherwise working with the entire vector body simultaneously, please be prepared to take a runtime hit.

Platform Specific Elements

Its quite likely that you’ll encounter platform-specific elements when iterating through the vector body. Recall the type attribute on each vector body element. This, in conjunction with the platform’s documentation will tell you what other elements may be encountered.

From a programmatic perspective, the vector body element in question knows if it is platform-specific. Returning to the example pattern:

pat.vector_at(0).type
  #=> :start_label
pat.vector_at(0).is_platform_specific?
  #=> true

pat.vector_at(1).type
  #=> :comment_block
pat.vector_at(1).is_platform_specific?
  #=> false

For platform-specific nodes, same as vectors, the parsed nodes from the decompilation process are retrievable:

pat.vector_at(0).platform_nodes
  #=> [:start_label]

Any platform nodes can be retrieved using the accessor:

pat.vector_at(0).start_label
  #=> "pattern_st"
Although these programmatic methods are provided, platform specifics should be documented and any supported platform in which such documentation is either missing, incorrect, or unclear should be addressed. Please open an issue at the OrigenTesters Github repository so corrections can be made.

Comments

Generated with the Origen Semiconductor Developer's Kit

Origen is released under the terms of the MIT license