Controllers
Shadow Controllers
There are two flavors of controller. These provide exactly the same functionality, but they are setup slightly differently and are designed to support two distinct approaches of creating models within a test engineering application.
The first type, the so called Shadow Controller, supports the case where the model is created and maintained by the test engineer for the given IP block.
So for example, let’s say we have a test engineer who is responsible for an analog-to-digital converter (ATD) IP, and they have decided to create an Origen plugin which will provide a complete test solution for this IP. This plugin will provide both the model and the controller as shown below:
Here is a simple ATD model implementation which defines some register:
# lib/atd_test_block/atd.rb
module ATDTestBlock
class ATD
include Origen::Model
def initialize(options = {})
instantiate_registers
end
def instantiate_registers
# ATD control register
reg :ctrl, 0x0024 do |reg|
reg.bit 7, :coco, access: :ro
reg.bit 6, :aien
reg.bit 5, :diff
reg.bit 4..0, :adch, reset: 0x1F
end
# ATD data result register
reg :result, 0x0028 do |reg|
reg.bit 15..0, :d
end
end
end
end
A common thing that users of our test block might want to do is to use it to make the ATD perform a conversion.
Let’s create a controller to provide the user with the following API:
result = $dut.atd.convert(10) # Perform a conversion on the voltage at ATD input channel 10 and return the result
# The caller can then process the result as they wish, here for example to capture it to the tester
result.store!
A controller is implemented as follows, this is just a regular Ruby class that includes the
Origen::Controller
module:
# lib/atd_test_block/atd_controller.rb
module ATDTestBlock
class ATDController
include Origen::Controller
end
end
Naming of controllers can be significant, here Origen will know that this this the controller for the
ATD
model since we have followed the naming convention:
<model name>Controller
If you cannot follow this convention (the main reason would be if the model lives in a different namespace), then you can supply the class of the model like this:
# lib/atd_test_block/atd_controller.rb
module ATDTestBlock
class ATDController
include Origen::Controller
model class_name: "SomeOtherNamespace::ATD_M325"
end
end
The controller now gives us the place to create our pattern API, here is the covert
method
implementation:
# lib/atd_test_block/atd_controller.rb
module ATDTestBlock
class ATDController
include Origen::Controller
# Convert the input on the given channel to a digital representation.
#
# An Origen::Register::BitCollection will be returned containing the result.
def convert(channel)
# Launch the conversion by setting the channel
ctrl.adch.write!(channel)
# Wait to complete
tester.wait(time_in_us: 100)
# Verify the conversion has completed
ctrl.coco.assert!(1)
# Return the result (the data bits from the result register)
result.d
end
end
end
A key point to note from the above is that the controller has direct access to the registers of
the model that it controls, that is you can call ctrl
instead of having to drill
down through the global namespace ($dut.atd.ctrl
).
Shadow controllers are never instantiated directly, instead Origen will automatically materialize them whenever a new instance of the model comes into being.
atd = ATDTestBlock::ATD.new # Instantiates the model, a controller will also be created automatically
This is where the name comes from, whenever a model is created it is shadowed by a corresponding controller.
So how do we invoke our controller API? We simply call the methods on the model:
atd = ATDTestBlock::ATD.new
atd.convert(10) # => <BitCollection>
Origen employs some Ruby trickery such that the object assigned to atd
above will
respond to the methods available to the model and the controller - models will proxy (send) methods
that they don’t recognize to their controller and vice versa.
Most of the time then, it doesn’t matter whether the atd
object is actually the model
or the controller, they will respond the same.
However if for some reason you really do need to deal directly with one or the other then you can use the following API:
atd.model # => <ATD instance>
atd.controller # => <ATDController instance>
Such a test block would normally be plugged into a top-level application in order to generate patterns for a target device.
The ATD test block’s integration guide should instruct the top-level test engineer to instantiate the ATD model within the top-level model like this:
# lib/eagle.rb
class Eagle
include Origen::TopLevel
def initialize(options = {})
sub_block :atd, class_name: "ATDTestBlock::ATD", base_address: 0x1000_2000
end
end
The ATD API provided by the ATD test engineer is now available to the Eagle device’s test engineers:
$dut = Eagle.new
$dut.atd.convert(10) # => <BitCollection>
If at some point the ATD test engineer gets access to an XML-based or similar definition of the ATD registers from the design team, then the model can be simplified like this:
# lib/atd_test_block/atd.rb
module ATDTestBlock
class ATD
include Origen::Model
include CrossOrigen
def initialize(options = {})
cr_import(path: "#{Origen.root!}/ipxact_files/atd_regs.xml")
end
end
end
See the Cross Origen plugin for more details on importing 3rd party data formats.
If on the other hand someone comes along and says, “ok everyone, from now on a full IP-XACT representation will be available for every device”, then it would be time to move to a Direct Controller…