Miscellaneous
Callbacks
Callbacks provide a way for an application to hook into the Origen process and have the opportunity to modify the standard behavior in some way.
This could also be accomplished by redefining any of Origen’s code at application-level but that approach is not recommended since it leaves the application at risk of breaking if Origen ever makes changes to how things work internally.
Callbacks on the other hand are an official API that will always work between releases.
Note that it is very easy to add additional callbacks to Origen, so if you can see the need for one that is not already covered please contact us via the community channels to discuss.
Callbacks work by registering listeners with Origen. As the Origen process executes it will come across various points where callbacks have been enabled and which will cause Origen to pause and ask all of the listeners if they have any code that they wish to execute. If so control will transfer to the listener who will execute something and who may or may not return control back to the Origen process when complete.
In other words Origen will call-back to the application at various points during the process.
A listener can be any object instance within your application and by default the application
itself (as defined in config/application.rb
) and any classes which include
Origen::TopLevel
or Origen::Model
are already registered as listeners.
Additional custom classes can be registered by simply including the callbacks module:
module NVM
class CallbackHandlers
include Origen::Callbacks
end
end
Note that listeners have to be object instances, so in the example above the listener is only
registered when the application creates a new instance of CallbackHandlers
.
If an instance is never created then the callbacks implemented in that class will never be
called.
This is particularly true of any objects that are defined as sub-blocks since the sub-block is not actually instantiated until the point where it is first referenced by an application.
on_create
callback.
Since this is commonly used as a delayed version of initialize, Origen will invoke that method
at the point when the listener object is instantiated if the on_create
callbacks
have already been called.
By default the pool of listeners is cleared out before a target load and they will be replaced by whatever objects are instantiated by the new target. In most cases this is the desired behavior and everything in the application will behave as you would expect.
However in some cases it may be desired to setup callback listeners that will persist across
target loads so that they keep listening for the entire duration of an Origen thread.
This can be achieved by including the PersistentCallbacks
module instead:
module NVM
class PersistentCallbackHandlers
include Origen::PersistentCallbacks
end
# Instantiate an instance of this class immediately when this file is loaded, this object will
# then listen for the remainder of the Origen thread
PersistentCallbackHandlers.new
end
A common use case is to use a persistent callback listener that implements on_load_target
to
implement common target behavior - i.e. implement logic that should run for every target.
Most crucially plugins can take full advantage of this to augment all of the targets of their
host application.
Implementing a callback is simply a matter of implementing an agreed method name within a listener.
So at each callback point the Origen code is basically:
listeners.each do |listener|
if listener.respond_to?(callback_x)
listener.callback_x(arg_x, arg_y)
end
end
So any listeners which don’t implement a specific callback are simply ignored.
To enable the above callback our custom listener would be changed to:
module NVM
class CallbackHandlers
include Origen::Callbacks
def callback_x(arg_x, arg_y)
if arg_x == :ft_pattern
puts "Final test not supported yet!"
exit 0
end
# Otherwise return to Origen
end
end
end
As mentioned the application is already registered as a listener, so the above callback_x
method could be added to config/application.rb
and it would be called at the appropriate
time.
If an application has mutliple listeners for the same callback then they will all get called, however the order that they are called in is undefined unless otherwise specified in the documentation for a particular callback (e.g. startup and shutdown callbacks).
Generally if calling order is important the recommended approach is for the application to implement a single callback and then from there co-ordinate calling any additional application methods that need to be run.
There now follows a listing of all available callbacks, these have been split up by function:
- Environment Setup
- Environment Teardown
- Generation
- Mode Changed
- Reset
- Pattern Generation
- Program Generation
- Web Compiler
- Release Process
- Intializing Models
This callback is triggered immediately before the target is loaded.
The intended use case is to allow any domain specific clean up that needs to be done when reloading or changing the target during an Origen thread of execution. Origen will take care of ensuring that the top-level model, the tester and all callback listeners are safely removed and/or re-instantiated between target loads, and in the vast majority of cases the application should never need to use this.
This callback is triggered immediately after the target is loaded.
The intended use case is to allow the application to complete any target object initialization that may
be dependent on other target objects.
In other words you can think of this like a regular initialize
method but one where
all objects instaniated by the target are guaranteed to be available.
No return value is expected.
include Origen::Callbacks
def initialize
# Would be creating a target instantiation order dependency by doing this here
# tester.do_something
end
def on_create
# But not here
tester.do_something
end
This is basically an alias of on_create and it behaves in exactly the same way.
Conceptually though it reflects a different way of using this callback: on_create can be thought of as an enhanced version of initialize at a model level, whereas on_load_target can be thought of as a way to augment all targets with common behavior.
See the section on Persistent Listeners above for some more discussion on this.
This callback is triggered immediately after a top-level object (an object that includes the
Origen::TopLevel
module, normally an object representing the top-level of the SoC)
is instantiated.
No return value is expected.
include Origen::Callbacks
# Automatically instantiate a RAM model whenever a DUT model is instantiated, the size will
# be determined by the ram_size attribute of the DUT
def on_top_level_instantiated(dut)
$ram = MyPlugin::RAM.new size: dut.ram_size
end
This will be called immediately before giving control to the user when opening an interactive
console session (by running origen i
).
The target will already be loaded prior to calling.
The intended use case is to give the application a chance to perform a device setup sequence in the case where the environment is connected to an Origen Link compatible tester driver.
# This is called automatically at the start of an interactive session (origen i)
def interactive_startup
# Run the regular pattern startup method upon opening the console to put the device in test mode
startup if tester.link?
end
This will be called immediately before shutting down an interactive console session.
The intended use case is to shutdown the device in the case where the environment is connected to an Origen Link compatible tester driver.
# This is called automatically at the end of an interactive session (origen i)
def interactive_shutdown
# Run the regular pattern shutdown method to put the device in reset
shutdown if tester.link?
end
This callback is triggered at the very end of an Origen thread of execution and is guaranteed to be called even if the thread crashes.
An example use cases is to ensure that any temporary files are deleted.
This callback is triggered whenever an object changes its current mode. It takes an options hash as an argument with the recently changed mode being passed. It can be used to change other DUT objects, such as clocks and register settings.
def on_mode_changed(options)
if options[:instance] == self
case options[:mode]
when :mode1
clocks(:coreclk).setpoint = 1.2.Ghz
clocks(:memclk).setpoint = 600.Mhz
when :mode2
clocks(:coreclk).setpoint = 1.0.Ghz
clocks(:memclk).setpoint = 500.Mhz
end
end
end
This callback is triggered immediately before submitting a batch of jobs to the LSF.
No return value is expected.
This callback is triggered immediately before launching a generate operation, that is before starting the current batch of program generation, pattern generation or compile jobs.
No return value is expected.
As above but only called when running locally.
As above but only called when running on a remote LSF machine.
The reset callbacks will kick in when the reset
or reset!
method
is called on the top-level object.
They will be executed in the following order:
- before_top_level_reset
- shutdown (reset! only)
- on_top_level_reset! (reset! only)
- on_top_level_reset
- registers are reset here
- startup (reset! only)
- after_top_level_reset
Generally with all reset callbacks the application code is not expected to take care of reseting registers or any other Origen-owned objects. Only state specifically setup by the application code should need to be teared down and re-established.
Called as soon as the reset
or reset!
method is called on the
top-level object and before any other reset activity takes place.
This callback should not be used to generate reset vectors, use the shutdown
callback for that.
No return value is expected.
def before_top_level_reset
@some_state_attribute = nil
end
Called when the reset
or reset!
method has been called on the
top-level object and after the top-level has been shutdown.
No return value is expected.
def on_top_level_reset
@some_state_attribute = nil
end
Called only when the reset!
method has been called on the top-level object,
otherwise the same as reset
.
Called at the very end of a reset sequence initiated by calling reset
or reset!
on the
top-level object at which point the top-level has already been re-started.
This callback should not be used to generate reset vectors, use the startup
callback for that.
No return value is expected.
def after_top_level_reset
@some_state_attribute = nil
end
Called at the start of a Pattern.create block, this callback provides an opportunity to implement startup vectors.
It will also be called as part of a within-pattern reset sequence initiated by calling
reset!
on the top-level object.
Any options supplied to Pattern.create are passed in as an argument.
The top-level (DUT) object is guaranteed to be called first for this callback, this enables the common case where the top-level will generate the mode entry vectors from this callback and then child modules can generate any additional vectors safe in the knowledge that mode has already been entered.
No return value is expected.
def startup(options)
tester.set_timeset("nvmbist", 40)
dut.pin(:reset).drive!(1)
end
Called at the very end of a Pattern.create block, this callback provides an opportunity to implement reset vectors.
It will also be called as part of a within-pattern reset sequence initiated by calling
reset!
on the top-level object.
Any options supplied to Pattern.create are passed in as an argument.
The top-level (DUT) object is guaranteed to be called last for this callback, this enables the common case where the child modules can insert any shutdown/safe-state vectors before the top-level finally pulls reset.
No return value is expected.
def shutdown(options)
dut.pin(:reset).drive!(0)
end
This callback is triggered immediately before running a Pattern.create block.
At this point the target is fully loaded but the pattern is not yet opened - so you can’t generate vectors from here.
The intended use case for this callback is to establish state within your models based on the pattern name. This can be used in conjunction with a pattern name translator to generate variants of your existing patterns without having to create a new pattern source.
No return value is expected.
# If the requested pattern has 'trimmed' in the name then look for the equivalent
# source file without 'trimmed' in the name.
# The output pattern will still contain 'trimmed' in the name.
config.pattern_name_translator do |name|
if name =~ /trimmed/
{source: name.gsub("trimmed_", ""), output: name}
else
name
end
end
# Setup the dut model differently if generating a pattern for a trimmed device
def before_pattern(pattern_name)
if name =~ /trimmed/
dut.apply_default_trims = false
end
end
This callback is triggered before Origen searches for the requested pattern source.
The intended use case is to give the application a chance to dispatch the pattern itself, or to decide to withhold it based on some properties of the current target.
If the callback returns a value of false or nil then Origen will abort further processing of this pattern and move onto the next one. Origen will proceed to dispatch the pattern as normal if any other value is returned.
def before_pattern_lookup(requested_pattern)
if requested_pattern =~ /blk_1/
# Only proceed with this pattern if the current target has a blk 1
dut.has_blk1?
else # Proceed as normal
requested_pattern
end
end
This callback is triggered at the end of every pattern that is generated, the path to the generated file (wrapped in a Pathname) is passed to the listener.
The intended use case is to give plugins a hook to kick off additional operations at the end of a pattern being generated, such as to go and simulate it or to compile it to a binary. Normally this callback would be implemented in a persistent listener within a plugin to have the plugin called after every pattern that is generated.
No return value is expected.
module VirtualTester
class PersistentCallbackHandlers
include Origen::PersistentCallbacks
def pattern_generated(path_to_generated_pattern)
if $vt_simulation_enabled # Set this back when the command was launched or however you want to enable
VirtualTester::Runner.new(path_to_generated_pattern)
end
end
end
# Instantiate an instance of this class immediately when this file is required, this object will
# then listen for the remainder of the Origen thread
PersistentCallbackHandlers.new
end
Called at the start of a top-level Flow.create block, note that it is not called at the beginning of sub-flows that are included by the top-level flow.
Any options supplied to Flow.create are passed in as an argument.
No return value is expected.
Called at the very end of a top-level Flow.create block, note that it is not called at the end of sub-flows that are included by the top-level flow.
Any options supplied to Flow.create are passed in as an argument.
No return value is expected.
Logically equivalent to on_flow_start, except this one will be called when the top level is a Resource.create block instead of a Flow.create block.
Logically equivalent to on_flow_end, except this one will be called when the top level is a Resource.create block instead of a Flow.create block.
This callback is triggered after a test program has been completely generated and all files have been
written.
No particular arguments are passed in, however the Origen.interface
instance for the program that was
just generated is still live and the application should be able to retrieve any meta data it might want
about the program from there.
No return value is expected.
This is called immediately prior to running the requested origen web compile
operation
and it provides the application with an opportunity to dynamically generate some templates
for the compiler to execute.
The command line arguments are decoded and available in the given options hash.
No return value is expected.
This is called immediately after running the requested origen web compile
operation
and provides the application with an opportunity to add anything else to the web/output
directory.
The command line arguments are decoded and available in the given options hash.
No return value is expected.
Similar to after_web_compile
, except that this one will only be called when
a request has been made to compile the entire web site, i.e. when origen web compile
has been called without a file argument(s).
The command line arguments are decoded and available in the given options hash.
No return value is expected.
This is called immediately prior to the release (origen rc tag) process and gives the application a chance to do any final checks to authorize that the release can go ahead.
No return value is expected but the callback handler is expected to exit the process if it decides that the release should be aborted.
Typically this hook would be used to launch the application’s test suite and if it fails abort the process, here is an example:
# Ensure that all tests pass before allowing a release to continue
def validate_release
if !system("origen specs") || !system("origen examples")
puts "Sorry but you can't release with failing tests, please fix them and try again."
exit 1
else
puts "All tests passing, proceeding with release process!"
end
end
This callback is triggered immediately before the application is tagged, all release properties are available in the arguments.
No return value is expected.
This callback is triggered immediately before the gem is built if the application is a plugin. It is not called at all if the application is a top-level app.
It can be used to prepare files to be included in the gem package.
No return value is expected.
This callback is triggered immediately before the gem is built if the application is a plugin. It is not called at all if the application is a top-level app.
It can be used to clean up after building the gem package.
No return value is expected.
This callback is triggered immediately after the application has been tagged and before the release email has been sent.
If the callback doesn’t return then the release email will not be sent.
No return value is expected.
Called at the very end of the release process.
This callback is commonly used to kick off any doc builds or other post release tasks within the application.
No return value is expected.
# Example of automatically deploying documents after a tag
def after_release_email(tag, note, type, selector, options)
command = "origen web compile --remote --api"
Dir.chdir Origen.root do
system command
end
end
Called immediately before a web site is deployed.
This gives the application a chance to add any additional files to the web site before it is deployed.
No return value is expected.
# Generate a test coverage report and add it to the web site
def before_deploy_site
Dir.chdir Origen.root do
system "origen specs -c"
system "mv #{Origen.root}/coverage #{Origen.root}/web/output/coverage"
end
end
Called only when a class
or module
has a class method
called
origen_model_init
AND when said class mixes in Origen::Model
.
This will be called during the class’ instantiation process, when the class has been allocated but before
it has been initialized using its own initialize
method. This can be used to boot modules or
classes using Origen and leaving a module’s included?
method alone.
This differs from the pre_initialize
callback in that origen_model_init
will recurse
into the inherited modules and classes.
This method should accept a single argument, which is the allocated class instance that called origen_model_init
.
For example:
module A
def self.origen_model_init(instance_of_caller)
puts "init module A - called by #{instance_of_caller.class}"
end
class B
def self.origen_model_init(instance_of_caller)
puts "init class B in module A - called by #{instance_of_caller.class}"
end
end
end
class Parent
include Origen::Model
include A
end
Parent.new
#=> "init module A - called by Parent"
#=> "init class B in module A - called by Parent" <= this is called prior to any A::B initialization.
#=> Instance of Parent class
Note that included modules and classes do not need to mix in Origen::Model
. Only the class including
said modules and classes.
Again, this is for use in booting included classes and modules, not in booting the parent. origen_model_init
will NOT be called in following and pre_initialize
should be used:
class Parent
include Origen::Model
include A
def self.origen_model_init(instance_of_caller)
puts "origen_model_init in Parent"
end
def self.pre_initialize
puts "pre_initialize in Parent"
end
end
Parent.new
#=> "init module A - called by Parent"
#=> "init class B in module A - called by Parent"
#=> "pre_initialize in Parent"
#=> Instance of Parent class