• 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

Models

Definition & Hierarchy


Creating A Model

A model can be any class that includes the Origen::Model module, this will give you access to all Origen APIs for defining registers, pins, etc. and generally makes Origen aware of any instances of the model when you instantiate them.

class MyModel
  include Origen::Model
end

As a general rule include the Origen::Model module every time you create a new class in Origen, even if the model is not of an SoC or an IP block and the concept of registers and pins does not apply. There is not really any downside to including these APIs if you are not going to use them and this module is the main mechanism to hook your class into the Origen ecosystem.

Creating A Top Level Model

If a given model represents the top-level of an device then you should also include the Origen::TopLevel module, this specifically lets Origen know that it is a top-level model and as such it holds a special place in the Origen runtime environment - for example any register write requests will be sent to this object for dispatch.

Internally the Origen::TopLevel module includes the Origen::Model module and therefore it does not need to be specifically included in top-level model classes (although there is no harm from doing so).

class MySoCModel
  include Origen::TopLevel
end
Warning! While you can define multiple top-level models only one of them can be instantiated per target or Origen thread of execution. Attempts to instantiate a 2nd top-level object will raise an error.

Regardless of what name is given to an instance of a top-level class it can always be looked up via Origen.top_level or more simply $dut. All Origen developers can rely on this convention to access the current top-level object:

soc = MySoCModel.new
Origen.top_level == soc      # => true
$dut == soc                  # => true

Modelling Hierarchy by Example

As your experience with your application grows, the way to partition your models for easiest maintenance and development will start to become clear. At that point you may feel decide to start introducing abstract classes to handle the concepts that exist within your domain and which don’t necessarily have a direct counterpart in the physical domain.

However to get started we recommend that you closely follow the physical architecture of your target device, where each of the main IP blocks that you care about has an associated model.

In this example we are going to set up an Origen model structure that will allow us to write some test code for a fictional NVM module contained within an SoC.

The Top Level

We are going to call our application ‘NVM’ and all of our code will reside in the NVM namespace. However the top-level SoC model is something of a special case because potentially it could be shared by many applications - e.g. an application concerned with testing the SRAM could re-use our SoC model.

So to set us up to handle that eventuality in the future let’s put all of our top-level definitions into their own namespace which we will call SOC. Here is how to define the top level model:

# lib/soc/eagle_m352.rb
module SOC
  class EAGLE_M352
    include Origen::TopLevel

    def initialize(options={})
    end
  end
end

The initialize method will automatically be called by Ruby whenever a new instance of this class is instantiated - so this is a place to do any setup or initialization that is required whenever a new Eagle model comes into being.

At this point we have also chosen to have this method optionally accept a hash of options, we will do this almost every time we define a method since it builds in great flexibility and the ability to handle additional arguments in future that we may not have thought about when first defining a new method.

Interacting with Our New Model

Our first model is now defined and we can now go and talk to it for the first time, to do so start an interactive Origen session from your terminal:

origen i

This command loads up an interactive Ruby terminal and automatically loads Origen and your application, so we can now experiment with our models:

$dut = SOC::EAGLE_M352.new

$dut.is_a?(SOC::EAGLE_M352)     # => true

Above we simply instantiated a new instance of our class, and then asked it if it was an instance of SOC::EAGLE_M352, to which it replied: ‘yes’.

Adding Sub Blocks

Sub-blocks should be used to model IP blocks or indeed any sub-components within that IP, a generic sub-block can be declared very simply within the top-level’s initialize method:

# lib/soc/eagle_m352.rb
def initialize(options={})
  sub_block :nvm
end

By default this will instantiate an object that includes all of the usual Origen APIs (regs, pins, etc.) and this can then be decorated as required by the application (see example below). Most importantly this wires up everything internally such that the relationship between the child and the parent are known to Origen and it will automatically build an accessor to get the child module:

$dut = SOC::EAGLE_M352.new

$dut.nvm         # => Generic Origen object
$dut.nvm.parent  # => $dut

$dut.children    # => {:nvm => <object>}

# Decorate as required by calling Origen APIs on the object
$dut.nvm.reg :reg1, 0x30 do |reg|
  reg.bits 31..0, :data
end

# The NVM now has a register available...
$dut.nvm.reg1.write(0x1234)

The above approach is ideal where the models are being built from a 3rd party data source (e.g. standard XML) and all that is required is to get an Origen object representation of the same data.

However in cases where more native richness is required you can supply a class for the object, let’s create a dedicated model for our NVM IP:

# lib/nvm/nvm_m682.rb
module NVM
  class NVM_M682
    include Origen::Model

    def initialize(options={})
      # Add an example register
      reg :reg1, 0x30 do |reg|
        reg.bits 31..0, :data, reset: 0xFFFF_FFFF
      end
    end
  end
end

This follows the same pattern as our first model, note the use of the NVM namespace and the subsequent storage of the file in the lib/nvm directory rather than lib/soc.

We can now refer to this class in our sub-block definition and verify that the register we added is available:

# lib/soc/eagle_m352.rb
def initialize(options={})
  sub_block :nvm, class_name: "NVM_M682"
end


$dut = SOC::EAGLE_M352.new
$dut.nvm.reg1.address        # => 0x30
$dut.nvm.reg1.data           # => 0xFFFF_FFFF

Adding Sub Block Groups

If needing to add several similar sub blocks, a sub_block_group can be used.

# lib/soc/eagle_m352.rb
def initialize(options={})
  sub_block_group :memories do
    sub_block :memory0,        class_name: "MEMORY_128_B954"
    sub_block :memory1,        class_name: "MEMORY_64_B954"
    sub_block :memory2,        class_name: "MEMORY_32_B954"
  end
end

$dut = SOC::EAGLE_M352.new
$dut.memory0.class               # MEMORY_128_B954
$dut.memory1.class               # MEMORY_64_B954
$dut.memory2.class               # MEMORY_32_B954
$dut.memories.class              # Array (Array of all 3 memory sub_blocks)

# By default uses sub_block_group uses Array class, so all Array methods are available
memories                         # => [<memory 0 instance>, <memory 1 instance>, <memory 2 instance>]
memories.size                    # => 3
memories.first                   # => <memory 0 instance>

In addition, a custom container class can also be used instead of an Array to contain the sub_block objects if desired. To do this, pass the desired container class as follows:

# lib/soc/eagle_m352.rb
def initialize(options={})
  sub_block_group :memories, class_name: "MEMORIES_B294" do
    sub_block :memory0,        class_name: "MEMORY_128_B954"
    sub_block :memory1,        class_name: "MEMORY_64_B954"
    sub_block :memory2,        class_name: "MEMORY_32_B954"
  end
end


$dut = SOC::EAGLE_M352.new
$dut.memory0.class               # MEMORY_128_B954
$dut.memory1.class               # MEMORY_64_B954
$dut.memory2.class               # MEMORY_32_B954
$dut.memories.class              # MEMORIES_B954 (custom class containing all 3 memory sub_blocks)

The recommended way to create a custom container class is to sub-class the Array class, then add additional methods, like this:

# lib/memories.rb
class MEMORIES_B294 < ::Array
  # Example of a custom method to print the names of all contained memories
  def print_ids
    # Since this class is based on Array, we can use all Array methods like 'each'
    # on the contained objects
    each { |mem| puts mem.id }
  end
end

# All conventional Array methods are supported since we sub-classed Array
memories.size                   # => 3
memories.first                  # => <memory 0 instance>
memories.map { |mem| mem.id }   # => [:memory0, :memory1, :memory2]

# Plus we have our custom method(s)
memories.print_ids
=>
memory0
memory1
memory2

If you do not sub-class Array, then you will need to be sure to define a << push type method:

# lib/memories.rb
class MEMORIES_B294
  def initialize
    @sub_blocks = {}
  end
  # Example of a custom << method to permit adding sub_blocks to container
  # Required for use with sub_block_group
  def <<(sub_block)
    @sub_blocks[sub_block.id] = sub_block
  end
  # Example of a custom method to print the names of all contained memories
  def print_ids
    each { |mem| puts mem.id }
  end
  # Example of custom 'each' method
  def each
    @sub_blocks.each_value do |sub_block|
      yield sub_block
    end
  end
end

# Drawback is you only have methods defined in this class.

Wash, Rinse, Repeat

This process can now be repeated to model the complete design hierarchy, the sub_block method can be used within sub-blocks themselves with no limit on depth.

Here are the complete set of initial stub models for the Eagle and it’s NVM module:

# lib/soc/eagle_m352.rb
module SOC
  class EAGLE_M352
    include Origen::TopLevel

    def initialize(options={})
      sub_block :nvm, class_name: "NVM_M682"
    end
  end
end

# lib/nvm/nvm_m682.rb
module NVM
  class NVM_M682
    include Origen::Model

    def initialize(options={})
      sub_block  :analog,        class_name: "ANALOG_T921"
      sub_block_group :memories, class_name: "MEMORIES_B294" do
        sub_block :memory0,        class_name: "MEMORY_128_B954"
        sub_block :memory1,        class_name: "MEMORY_64_B954"
        sub_block :memory2,        class_name: "MEMORY_32_B954"
      end
      sub_block  :state_machine, class_name: "CONTROL_D345"
    end

  end
end

# lib/nvm/analog_t921.rb
module NVM
  class ANALOG_T921
    include Origen::Model

    def initialize(options={})
    end
  end
end

# lib/nvm/memory_128_b954.rb
module NVM
  class MEMORY_128_B954
    include Origen::Model

    def initialize(options={})
      @size_in_kB = 128
    end
  end
end

# lib/nvm/memory_64_b954.rb
module NVM
  class MEMORY_64_B954
    include Origen::Model

    def initialize(options={})
      @size_in_kB = 64
    end
  end
end

# lib/nvm/memory_32_b954.rb
module NVM
  class MEMORY_32_B954
    include Origen::Model

    def initialize(options={})
      @size_in_kB = 32
    end
  end
end

# lib/memories.rb
class MEMORIES_B294 < ::Array
  def print_ids
    each { |mem| puts mem.id }
  end
end

# file: lib/nvm/control_d345.rb
module NVM
  class CONTROL_D345
    include Origen::Model

    def initialize(options={})
    end
  end
end

A couple of points are worth noting from the above code:

  • The namespace reference is not required when making references to other models/classes within the same namespace.
  • Multiple instances of the memory sub-block have been defined. By convention use the plural for the group name, i.e. :memories and singular with an incrementing digit for the sub_blocks contained therein: memory0, memory1, etc.
  • The classes for the various NVM sub-blocks are all empty right now and as such they did not really need to be defined, however these are placeholders for us to go on and add more logic in the future.

Even though we have not yet added any logic to our models they are starting to become useful, for example we can now ask the Eagle how many NVM memory blocks that it has:

$dut = SOC::EAGLE_M352.new

$dut.nvm.memories.size    # => 4

Overriding and Inheriting

WARNING: This is a beta feature, there still may be some bugs/undefined behavior when overriding or inheriting blocks.

Ideally, you will not need to override or inherit blocks. But if you need to make some application specific overrides to methods defined in blocks that were instantiated in another application that you don’t have control over, overriding may be the solution. Similarly, if you need to make an application specific derivative of a block thats been defined in another app, and it doesn’t make sense to define the derivative in that app, then you may want to utilize the inherit functionality.

Override

Override allows you to recreate a block that already existed. For example, lets say that your dut included a sub block called ‘dummy_block’ of class OtherApp::Block::BlockA which was created in another app, and it instantiated a sub block called ‘dummy_sub’ of class OtherApp::Block::BlockA::Sub1.

# file: app/blocks/dut/my_dut/sub_blocks.rb
sub_block :dummy_block, class_name: 'OtherApp::Block::BlockA'

Which you can now access dummy_sub via dut.dummy_block.dummy_sub. But now lets say that your application needs a different sub block under dummy block, MyApp::DUT::MyDut::Sub2.

You could override the existing dut.dummy_block.dummy_sub like so:

# file: app/blocks/dut/my_dut/sub_blocks.rb
sub_block :dummy_block, class_name: 'OtherApp::Block::BlockA'
dummy_block.sub_block :dummy_sub, class_name: 'MyApp::DUT::MyDut::Sub2', override: true
skip_require_files

When overriding, previously instantiated subblocks have already initialzed some namespaces, which can result in some funny constant behavior:

NamespaceA::B::C
=>  NameSpaceX::Y::Z

To get around this the override feature will require the relevant block files which returns the constant behavior back to sanity:

NamespaceA::B::C
=>  NameSpaceA::B::C

However, there are many block files which aren’t meant to be required directly (e.g attributes.rb) as they could contain method calls which wouldn’t be available at the time they are required. So by default, these kinds of block files are skipped. Specifically: attributes.rb, parameters.rb, pins.rb, registers.rb, sub_blocks.rb, and timesets.rb

If you have other block files in your modeling that you need require to skip, you can do so by passing an array of files (without the extension) to the sub_block method as an option of skip_require_files:

dummy_block.sub_block :dummy_sub, class_name: 'MyApp::DUT::MyDut::Sub2', override: true, skip_require_files: %w(file1 file2 file3)

Note however that you’ll likely need to include the files that are skipped by default in your custom skip_require_files option as this will override the default array.

Inherit

Continuing the above example from Override, lets now say that you want your new MyApp::DUT::MyDut::Sub2 sub block to inherit OtherApp::Block::BlockA::Sub1 as a starting point. You can already mostly accomplish this the same way that derivatives operate by setting your class definitions in your model.rb and controller.rb files of MyApp::DUT::MyDut::Sub2 to inherit the class of OtherApp::Block::BlockA::Sub1’s model.rb and controller.rb:

# file: app/blocks/dut/my_dut/sub_blocks/sub2/model.rb
class MyAPP::DUT::MyDut::Sub2 < OtherApp::Block::BlockA::Sub1

# file: app/blocks/dut/my_dut/sub_blocks/sub2/controller.rb
class MyAPP::DUT::MyDut::Sub2Controller < OtherApp::Block::BlockA::Sub1Controller

But what this does not do, is inherit the block files (attributes.rb, parameters.rb, etc…) associated with OtherApp::Block::BlockA::Sub1. To do that, you can also pass the inherit option to sub_block, which will instruct the block loader to pick up those files as well:

# file: app/blocks/dut/my_dut/sub_blocks.rb
sub_block :dummy_block, class_name: 'OtherApp::Block::BlockA'
dummy_block.sub_block :dummy_sub, class_name: 'MyApp::DUT::MyDut::Sub2', override: true, inherit: 'OtherApp::Block::BlockA'
Bug and Feature inheritance

By default, if the inherit option is passed it will attempt to propagate any features and bugs from the class specified in the inherit option to the sub block being defined. If you’d like to prevent either from happening, then you can pass either disable_bug_inheritance and/or disable_feature_inheritance options to the sub_block method:

dummy_block.sub_block :dummy_sub, class_name: 'MyApp::DUT::MyDut::Sub2', override: true, inherit: 'OtherApp::Block::BlockA', disable_bug_inheritance: true, disable_feature_inheritance: true

Comments

Generated with the Origen Semiconductor Developer's Kit

Origen is released under the terms of the MIT license