Test Program Generator
Creating an Interface
To re-cap the purpose of an interface is to translate your behavioral description of the test flow into a test program for a specific ATE platform. This step is where most of the work comes in and interfaces can grow to be quite large pieces of code depending on the complexity of your flow.
An interface is a regular Ruby class which would be created in your lib directory. Continuing with the vreg example and assuming that all of our code was namespaced in the Vreg module, we could create an initial interface like this:
# lib/vreg/interface.rb
module Vreg
class Interface
include OrigenTesters::ProgramGenerators
# The methods called by the flow files will be implemented here
end
end
For an interface to run it must implement all of the methods that will be called
by your flow.
It is also customary to create an initialize method that will capture any options
that are passed in to Flow.create
(such as declaring the environment
as probe in our flow example).
Here is an interface shell to handle the test flow that we created in the previous section:
# lib/vreg/interface.rb
module Vreg
class Interface
include OrigenTesters::ProgramGenerators
# Add a regular attribute to our interface to allow us to query the
# execution environment
attr_reader :environment
# Any options passed to Flow.create can be captured here and assigned
# to instance variables which can be used later to modify the output from
# your other interface methods.
def initialize(options={})
options = {
environment: :ft, # Set the environment to FT unless otherwise specified
}.merge(options)
@environment = options[:environment]
end
# Record the given message to the datalog
def log(msg, options={})
end
# Create a functional test and call it from the flow
def func(name, options={})
end
# Create a parametric test and call it from the flow
def para(name, options={})
end
end
end
Note that you do not need to define methods to handle the Flow Control API, the included generator module will already take care of those.
At this point you can now generate your flow for the first time to make sure that there are no methods missing:
origen p path/to/your/flow.rb
All being well this should run cleanly without actually generating any of your tests, if you get some errors your should be able to work out what methods need to be added to your interface from the error messages.
The contents of these methods will be discussed in the following platform-specific section of the test program guide.
The astute reader may at this point note that the intention of the above methods is to both generate a test and to add it to the flow. Well how do we avoid duplicate tests from being generated if the same method is called multiple times within the same flow?
The answer is that you don’t need to worry about this, Origen will take care of suppressing duplicate entries in things like a test instance or a test pattern file, depending on the needs of the target platform. So your interface does not need to keep track of details like whether a test has previously been generated or not. Just generate all the resources required to run a particular test every time that your method is called and Origen will take care of optimizing it.
When assessing whether or not a test already exists Origen does not simply just look at the name, all of the attributes of a particular test are considered as well. If a test of the same name already exists but with different attributes then Origen will create a new test and apply a version of ‘_v1’ to the original test and ‘_v2’ to the new one. The flow will call the correct version at the correct place as you would expect.
Generally you should keep an eye on what is being generated and if you start to see ‘v1’ or ‘v2’ tests being generated then it is a sign that your test naming convention is not including some detail that is required to uniquely identify each test. So while the program generated will be functionally correct, it will not be obvious to a user of the test program what the difference is between ‘test_v1’ and ‘test_v2’. For example say that your test name does not include the vdd, yet your flow generates the same test to run at min, nom and max vdd. In that case Origen will generate these as versions 1, 2 and 3. Adding the vdd to you test names would resolve this problem and the program would now include tests called ‘test_min’, ‘test_nom’ and ‘test_max’ which is much more descriptive.
If you are not comfortable with this approach for some reason and would prefer separate control over test generation and flow insertion then Origen also supports a a more traditional workflow where you can generate a library of tests and then call them from a separate flow definition. How to do that is discussed in the Resources section. However the combined test generation and flow insertion is the recommended way and fully leverages Origen’s unique ability to completely generate a new test from adding a single line to the test flow (once you have an interface already setup).
The interface can also define whatever additional methods it needs to help implement the main flow API. In this example, let’s just add the following method to help us generate the full test names:
# lib/vreg/interface.rb
def namer(basename, options={})
name = basename
if options[:vdd]
# In our world let's have the convention that if the vdd is not included in
# the name then it is nominal, otherwise it will be in the name
unless options[:vdd] == :nom
name = "#{name}_#{options[:vdd]}"
end
end
name
end
In reality some interfaces can get quite complex, and breaking the code down to additional Ruby class or modules is common.
The interface, or portions of it, can be easily extracted to a plugin for future use in another application.
See the Origen Plugins section for details on how to do this.
The execution context of a test is the name given to the rules that decide whether it will be executed or not, and these are generally the attributes exposed by the Flow Control API. i.e. enable flag, job, failed flag, etc.
If the context applied to two adjacent tests is different, then it means that the second test cannot be guaranteed that the first test will always run before it. This could be a concern if the second test is expecting to inherit some state from the previous test, such as the vdd/levels selection for example.
Therefore, an interface helper exists to help you identify when such context switches occur.
The next pages will cover the creation of an interface in more detail, but generally it is recommended to have a single method responsible for actually adding the generated tests into the flow, like this:
# my_interface.rb
# Add the given test into the flow
def add_to_flow(test_object, options = {})
flow.test test_object, options
end
By funneling all tests through a single point like this, it naturally gives you a place to look
for context changes and take corrective action.
Use the context_changed?
helper as shown in this example:
def add_to_flow(test_object, options = {})
# Give this all options that you are about to send to the flow.test method
if context_changed?(options)
# Can't rely on inheriting from the previous test, configure this test to re-apply the levels
# (where add_levels_configuration is a fictional method within your interface)
add_levels_configuration(test_object)
end
flow.test test_object, options
end
A similar helper method called parameter_changed?
exists to detect changes in your application-specific test attributes. For
example, let’s say that your test flow always defines the vdd settings via two parameters called
:vdde
and :vddc
:
# my_flow.rb
func :march_a, vdde: :nom, :vddc: :nom
func :march_b, vdde: :nom, :vddc: :nom
func :march_b, vdde: :nom, :vddc: :max
Then if your level switching had to be implemented by the injection of a dedicated test instance, it could be easily handled like this:
def add_to_flow(test_object, options = {})
# Give this all options that you are about to send to the flow.test method, supply as many parameter
# names as you want up front
if parameter_changed?(:vdde, :vddc, options)
# The upcoming test requires a vdd change, inject this into the flow before inserting it, again
# using a fictional switch_levels method
switch_levels(options)
end
flow.test test_object, options
end
Also, a convenience method exists that will trigger if either the context or one of your specified parameters changes:
if context_or_parameter_changed?(:vdde, :vddc, options)
# ...
By default, Origen reloads the target whenever a new flow file (top level or sub-flow) is encountered. This can lead to
increased flow generation time if the flow is large and calls many sub-flows. To turn off reloading of the target, configure
the test interface to set the attribute reload_target
to false
. Here is an example from the origen_testers spec tests:
class MyInterface
include OrigenTesters::ProgramGenerators
attr_accessor :reload_target
def initialize(options = {})
@reload_target = false
end
end
This feature also exists at the flow file level, however the test interface will take precendence. For example, the following flow file option
will not have any effect if the test interface has the `reload_target` attribute set to `false`.
~~~ruby
Flow.create interface: 'MyInterface', reload_target: true do
# Tests inserted here
end
If the test interface does not set the reload_target
attribute to false
, then a flow file may do so to prevent reloading of the target.
Flow.create interface: 'MyInterface', reload_target: false do
# Tests inserted here
end