% render "layouts/guides.html" do % # Test pattern that'll be used throughout these guides. % # Using the actual pattern object itself to get real return values when possible. % Origen.load_target('configurable', dut: OrigenDecompilerDocDut) % pat = OrigenTesters::IGXLBasedTester::Pattern.new(OrigenTesters::IGXLBasedTester.sample_source_atp) % direct_pat = OrigenTesters::IGXLBasedTester::Pattern.new(OrigenTesters::IGXLBasedTester.sample_direct_source, direct_source: true) % testers_api_url = 'https://origen-sdk.org/testers/api/OrigenTesters' 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](<%= "#{testers_api_url}//Decompiler/Pattern.html" %>) base class provides this interface. All supported platforms should return a class which inherits from this. For example, ~~~ruby OrigenTesters::IGXLBasedTester::Pattern.ancestors.include?(OrigenTesters::Decompiler::Pattern) #=> <%= OrigenTesters::IGXLBasedTester::Pattern.ancestors.include?(OrigenTesters::Decompiler::Pattern) %> ~~~ ### Decompiling The preferred method to decompile a pattern source is to use the [Decompiler API](<%= path "guides/decompilation/decompilerapi/#Decompiling" %>) 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: ~~~ruby 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. ~~~ruby pat.decompiled? #=> <%= pat.decompiled? %> pat.decompile pat.decompiled? % pat.decompile #=> <%= pat.decompiled? %> ~~~ 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](<%= path "guides/decompilation/overview#Example_Decompilation" %>), you'll want to decompile a text source directly. Indicate this by providing the `direct_source: true` option during instantiation: ~~~ruby 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: ~~~ruby pat.direct_source? #=> <%= pat.direct_source? %> pat.source #=> /home/origen_stuff/origen_testers/approved/j750/decompiler/sample/sample.atp direct_pat.direct_source? #=> <%= direct_pat.direct_source? %> direct_pat.source #=> "<%= direct_pat.source %>" ~~~ ### Adding Pins and Executing Both of these have been covered [in the example](<%= path "guides/decompilation/overview#Example_Decompilation" %>), but for a quick review: ~~~ruby # Add any missing pins to the DUT, returning the pins that were added dut.pins #=> <%= dut.pins %> pat.add_pins #=> <%= pat.add_pins %> dut.pins #=> <%= dut.pins %> # Execute the vectors pat.execute ~~~ ### Sections As also shown [in the example](<%= path "guides/decompilation/overview#Example_Decompilation" %>), 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. ~~~ruby # Access the pattern's frontmatter pat.frontmatter #=> <%= pat.frontmatter %> # Retrieve the pattern header pat.frontmatter.pattern_header #=> <%= pat.frontmatter.pattern_header %> ~~~ ~~~ruby # Access the pattern's pinlist pat.pinlist #=> <%= pat.pinlist %> # pat.pinlist.pins #=> <%= pat.pinlist.pins %> ~~~ Methods to retrieve the pins are also available directly on the decompiled pattern: ~~~ruby # Return an array of pins, in the order they appear in the pattern pat.pins #=> <%= pat.pins %> # Return the pin names and their respective size pat.pin_sizes #=> <%= pat.pin_sizes %> ~~~ See the [OrigenTesters API](<%= testers_api_url %>) for full details on the [Frontmatter](<%= "#{testers_api_url}/Decompiler/Pattern/Frontmatter.html" %>) and the [Pinlist](<%= "#{testers_api_url}/Decompiler/Pattern/Pinlist.html" %>). ##### Platform Specifics The `frontmatter` and `pinlist` may contain _platform-specific_ setup information. Any platform-specifics should be documented [on the supported platforms page](<%= path "guides/decompilation/platformspecifics" %>), but some methods are provided to programmatically check what's available. [For the example pattern](<%= path "guides/decompilation/overview#Example_Decompilation" %>), using the `J750 decompiler`: ~~~ruby pat.frontmatter.platform_nodes #=> <%= pat.frontmatter.platform_nodes %> pat.pinlist.platform_nodes #=> <%= pat.pinlist.platform_nodes %> ~~~ Any `platform node` will have an accessor associated with it: ~~~ruby pat.frontmatter.imports #=> <%= pat.frontmatter.imports %> pat.frontmatter.variable_assignments #=> <%= pat.frontmatter.variable_assignments %> ~~~ 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. #### Initial State For some functions, it'll be essential to know the _initial state_ of the pattern. This may include the [initial pin states](<%= "#{testers_api_url}/Decompiler/Pattern.html#first_pin_states-instance_method" %>), the [initial timeset](<%= "#{testers_api_url}/Decompiler/Pattern.html#first_timeset-instance_method" %>), or the [first vector](<%= "#{testers_api_url}/Decompiler/Pattern.html#first_vector-instance_method" %>), 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: ~~~ruby # Access the first vector pat.first_vector #=> <%= pat.first_vector.class %> ~~~ An important observation is that this returns the _first vector_, __not__ the first _vector body element_. ##### 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](<%= path "guides/decompilation/overview#Example_Decompilation" %>): ~~~ruby # Retrieve the initial pin states pat.initial_pin_states #=> <%= pat.initial_pin_states %> # Retrieve the initial timeset pat.initial_timeset #=> <%= pat.initial_timeset %> ~~~ ### 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](<%= path "guides/decompilation/platformspecifics#Teradyne_J750_and_Ultraflex" %>), on the `J750` or `Ultraflex`, or [sequencer instructions](<%= path "guides/decompilation/platformspecifics#Advantest_V93K" %>), on the `V93K`. Anything in the `vector body` will have a class of [OrigenTesters::Decompiler::VectorBodyElement](<%= "#{testers_api_url}/Decompiler/Pattern/VectorBodyElement.html" %>), 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](<%= "#{testers_api_url}/Decompiler/Pattern/CommentBlock.html" %>) and [vectors](<%= "#{testers_api_url}/Decompiler/Pattern/Vector.html" %>). #### 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: ~~~ruby 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: ~~~ruby 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](<%= "#{testers_api_url}/Decompiler/Pattern/CommentBlock.html" %>), 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: ~~~ruby 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](<%= path "guides/decompilation/overview#Example_Decompilation" %>), we can retrieve all this content from the [first vector](#Initial_State): ~~~ruby pat.first_vector.timeset #=> <%= pat.first_vector.timeset %> pat.first_vector.pin_states #=> <%= pat.first_vector.pin_states %> pat.first_vector.repeat #=> <%= pat.first_vector.repeat %> pat.first_vector.comment #=> <%= pat.first_vector.comment %> ~~~ 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: ~~~ruby pat.first_vector.opcode #=> <%= pat.first_vector.opcode %> pat.first_vector.opcode_arguments #=> <%= pat.first_vector.opcode_arguments %> ~~~ Trying these platform nodes on other decompilers __is not__ guaranteed to give you anything. [Check the platform specifics](<%= path "guides/decompilation/platformspecifics" %>) for any additional platform nodes placed on the `vector` type. You can also list the platform nodes programmatically using `#platform_nodes`: ~~~ruby pat.first_vector.platform_nodes #=> <%= pat.first_vector.platform_nodes %> ~~~ ##### 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). ~~~ruby pat.decompiler #=> <%= pat.decompiler %> ~~~ In cases where decisions are made depending on the decompiler, the `#decompiler?()` method queries if the decompiled pattern was decompiled using the given platform: ~~~ruby pat.decompiler?(OrigenTesters::IGXLBasedTester::Pattern) #=> <%= pat.decompiler?(OrigenTesters::IGXLBasedTester::Pattern) %> pat.decompiler?(OrigenTesters::SmartestBasedTester::Pattern) #=> <%= pat.decompiler?(OrigenTesters::SmartestBasedTester::Pattern) %> ~~~ 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: ~~~ruby # 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 } <%= pat.collect { |v| ":#{v.type}" }.join("\n") %> # Do the above, but with the index pat.each_vector_with_index { |v, i| puts "Type at index #{i}: #{v.type}" } <%= pat.collect_with_index { |v, i| "Type at index #{i}: #{v.type}" }.map { |v| "\"#{v}\"" }.join("\n") %> ~~~ 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: ~~~ruby 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](https://ruby-doc.org/core-2.6.3/Enumerable.html) mixin cannot be used directly. However, some select `Enumerable` methods are implemented: ~~~ruby # 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 } #=> <%= pat.collect { |v| v.type } %> # Filting out duplicates...: pat.collect { |v| v.type }.uniq #=> <%= pat.collect { |v| v.type }.uniq %> # 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? } #=> <%= pat.find { |v| v.is_a_vector? }.class %> pat.find { |v| v.is_a_vector? }.type #=> :<%= pat.find { |v| v.is_a_vector? }.type %> # 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? } #=> <%= pat.find_all { |v| v.is_a_vector? }.map { |v| v.class } %> # 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? } #=> <%= pat.reject { |v| v.is_a_vector? }.map { |v| v.class } %> ~~~ [See the API for a full listing.](<%= "#{testers_api_url}/Decompiler/Pattern/EnumerableExt.html" %>) #### 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`: ~~~ruby pat.vector_at(3) #=> <%= pat.vector_at(3).class %> pat.vector_at(3).type #=> :<%= pat.vector_at(3).type %> pat.vector_at(3).element #=> <%= pat.vector_at(3).element.class %> pat.vector_at(3).element.repeat #=> <%= pat.vector_at(3).element.repeat %> pat.vector_at(4).element.repeat #=> <%= pat.vector_at(4).element.repeat %> ~~~ 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`. #### 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](https://en.wikipedia.org/wiki/Linked_list#Linked_lists_vs._dynamic_arrays), 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. 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](<%= path "guides/decompilation/platformspecifics" %>) 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:](<%= path "guides/decompilation/overview#Example_Decompilation" %>) ~~~ruby pat.vector_at(0).type #=> :<%= pat.vector_at(0).type %> pat.vector_at(0).is_platform_specific? #=> <%= pat.vector_at(0).is_platform_specific? %> pat.vector_at(1).type #=> :<%= pat.vector_at(1).type %> pat.vector_at(1).is_platform_specific? #=> <%= pat.vector_at(1).is_platform_specific? %> ~~~ For platform-specific nodes, same as `vectors`, the parsed nodes from the decompilation process are retrievable: ~~~ruby pat.vector_at(0).platform_nodes #=> <%= pat.vector_at(0).platform_nodes %> ~~~ Any `platform nodes` can be retrieved using the accessor: ~~~ruby pat.vector_at(0).start_label #=> "<%= pat.vector_at(0).start_label %>" ~~~ % end