• 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

Registers


When modelling semiconductor IP, there will almost always be a need to define register and bit maps and Origen provides an API specifically for this purpose.

When modelling silicon with Origen, the hardware should be the guide as to where logic or attributes should go and if you have followed the advice so far then you will already have a model framework that closely resembles the physical hardware. The registers then, are simply instantiated within the model of the corresponding hardware that owns them.

Defining Registers

Registers should be defined as part of the object initialization process, but to avoid the initialize method growing out of hand it is recommended that you instantiate registers in a dedicated method that is called upon initialization.

When defining registers you can either take the approach of defining them all up front or defining them individually on an as-needed basis - the latter is recommended as it is the most efficient in terms of effort.

Here is an example of how to define this register in our memory map:

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

    def initialize(options={})
      instantiate_registers(options)
    end

    def instantiate_registers(options={})
      reg :ctrl, 0x0024, size: 16 do |reg|
        reg.bit 7, :coco, access: :ro
        reg.bit 6, :aien
        reg.bit 5, :diff
        reg.bit 4..0, :adch, reset: 0x1F
      end
    end
  end
end

Some points to note in the above code:

  • Even though we have not used it we have continued the practice of setting up every method with an optional option argument and we have passed this between method calls - i.e. we passed the initialize options when calling instantiate_registers. This keeps things extremely flexible for the future and means that it is easy to influence the register instantiation externally, for example: NVM::ANALOG_T921.new(include_test_registers: true)
  • The reg method takes a name, address and size as arguments. The size is optional and is 32 by default.
  • Bit definitions are contained within the do..end block passed to the reg method. By default bits are writable and will reset to 0, but this can be overridden as shown in the example.
  • When defining bits the size is 1 by default and the reset state is 0 by default, these can be overridden on an as needed basis as shown above.

Accessing Registers

Origen will automatically create accessor methods for the register and its bits, the above register would be accessed like this:

$dut.nvm.analog.ctrl         # => <register object>
$dut.nvm.analog.ctrl.adch    # => <subset of bits>

The Origen console will show a nice graphical representation of the register which reflects its current state, this can be very useful when debugging or more generally as an interactive version of the block guide:

> origen i

>> $dut.nvm.analog.ctrl
=>
0x24 - :ctrl
  ================================================================================================================
  |     15      |     14      |     13      |     12      |     11      |     10      |      9      |      8      |
  |             |             |             |             |             |             |             |             |
  |             |             |             |             |             |             |             |             |
  ----------------------------------------------------------------------------------------------------------------
  |      7      |      6      |      5      |      4      |      3      |      2      |      1      |      0      |
  |    :coco    |    :aien    |    :diff    |                             :adch[4:0]                              |
  |    0(RO)    |      0      |      0      |                                0x1F                                 |
  ----------------------------------------------------------------------------------------------------------------

Origen supports some more verbose APIs that you may see used in some codebases or documentation, these are now considered legacy APIs but are equivalent to the above:

$dut.nvm.analog.reg(:ctrl)
$dut.nvm.analog.reg(:ctrl).bits(:adch)
$dut.nvm.analog.reg(:ctrl)[:adch]

Origen supports Verilog style part selects to access an ad-hoc subset of bits, although unfortunately [3:0] is not valid Ruby and [3..0] is used instead.

$dut.nvm.analog.ctrl[1]           # => bit 1 of the register
$dut.nvm.analog.ctrl.adch[1]      # => bit 1 of the adch bits
$dut.nvm.analog.ctrl[3..0]
$dut.nvm.analog.ctrl.adch[1..0]

Bit Order and Serial Processing

By default, a register is defined with LSB0 bit numbering, this can be changed to the MSB0 numbering by setting the bit_order attribute as shown below:

# identical register description as above, except described with MSB0 numbering
reg :ctrl, 0x0024, size: 16, bit_order: :msb0 do |reg|
  reg.bit 8,      :coco, access: :ro
  reg.bit 9,      :aien
  reg.bit 10,     :diff
  reg.bit 11..15, :adch, reset: 0x1F
end

Origen, however will store and access the register with LSB0 bit numbering. This allows the ecosystem of plugins to behave consistently regardless of how the register was described without needing to constantly check and recalculate indexes.

A register defined with MSB0 bit numbering will appear in the console view with LSB0 numbering. These bit numbers can be used to access bits by number. If you have worked with MSB0 numbering before you can appreciate how confusing it can be, especially when switching back and forth. So, Origen provides an API to “say what you mean” and handles the converting.

  # use .with_lsb0 to explicitly refer to bits using LSB0 numbering
  $dut.nvm.analog.ctrl.with_lsb0[7].write 1       # set :coco by referring to the bit number
  
  # the equivalent code without explicitly using .with_lsb0
  $dut.nvm.analog.ctrl[7].write 1       # set :coco by referring to the bit number

  # use .with_msb0 to explicitly refer to bits using MSB0 numbering
  $dut.nvm.analog.ctrl.with_msb0[8].write 1       # set :coco by referring to the bit number

Below is the console output when displaying the register (first with LSB0 numbering (default) then MSB0):

> origen i

>> $dut.nvm.analog.ctrl

[WARNING]    0.038[0.038]    || Register displayed with lsb0 numbering, but defined with msb0 numbering
[WARNING]    0.039[0.000]    || Access (and display) this register with explicit numbering like this:
[WARNING]    0.039[0.000]    ||
[WARNING]    0.039[0.000]    ||    reg(:ctrl).with_msb0        # bit numbering scheme is msb0
[WARNING]    0.039[0.000]    ||    reg(:ctrl).with_lsb0        # bit numbering scheme is lsb0 (default)
[WARNING]    0.040[0.000]    ||    reg(:ctrl)                  # bit numbering scheme is lsb0 (default)

0x24 - :ctrl
  ================================================================================================================
  |     15      |     14      |     13      |     12      |     11      |     10      |      9      |      8      |
  |             |             |             |             |             |             |             |             |
  |             |             |             |             |             |             |             |             |
  ----------------------------------------------------------------------------------------------------------------
  |      7      |      6      |      5      |      4      |      3      |      2      |      1      |      0      |
  |    :coco    |    :aien    |    :diff    |                             :adch[4:0]                              |
  |    0(RO)    |      0      |      0      |                                0x1F                                 |
  ----------------------------------------------------------------------------------------------------------------

  
  
>> $dut.nvm.analog.ctrl.with_msb0

0x24 - :ctrl
  ================================================================================================================
  |      0      |      1      |      2      |      3      |      4      |      5      |      6      |      7      |
  |             |             |             |             |             |             |             |             |
  |             |             |             |             |             |             |             |             |
  ----------------------------------------------------------------------------------------------------------------
  |      8      |      9      |     10      |     11      |     12      |     13      |     14      |     15      |
  |    :coco    |    :aien    |    :diff    |                             :adch[0:4]                              |
  |    0(RO)    |      0      |      0      |                                0x1F                                 |
  ----------------------------------------------------------------------------------------------------------------

When serially processing a register (in a pattern driver for example), there are APIs that will always return bits in the desired order regardless of the .with_lsb0 or .with_msb0 setting. Here is a summary of the APIs that are available for serially processing a register and how they behave in respect to this attribute:

  • shift_out / shift_out_with_index - This will process the bits with the LSB coming out first
  • shift_out_right / shift_out_right_with_index - Equivalent to shift_out, it will process the bottom-right bit of the register (as displayed in the console) first.
  • reverse_shift_out / reverse_shift_out_with_index - As above, but MSB first.
  • shift_out_left / shift_out_left_with_index - Equivalent to reverse_shift_out, it will process the top-left bit of the register (as displayed in the console) first.

All of these APIs behave like this:

myreg.shift_out_left_with_index do |bit, i|
  # This block will execute for each bit in the register
  # *bit* is a bit object (instance of Origen::Reg::Bit)
  # *i* is the iteration index, 0, 1, 2, etc. Note that this is only available for the _with_index versions.
  bit_shift(bit)
end

Base Addresses

When defining registers the address specified should be the local address within the owning IP block.

Base addresses can be passed in within the sub block definition, base_address is an official option that Origen will recognize and automatically deal with.

# lib/nvm/nvm_m682.rb
sub_block :analog, class_name: "ANALOG_T921", base_address: 0x4000_0000

Registers will automatically pick up the base address of their parent, the local address can be accessed via the offset method:

$dut.nvm.analog.ctrl.address   # => 0x4000_0024
$dut.nvm.analog.ctrl.offset    # => 0x24

Note that when resolving a base address Origen will look right up the hierarchy until it reaches the top level object and all base addresses in that branch of the tree will be added together.

Register Domains

An API is available to model register domains, where domain could mean what bus or clk domain that the register is on, or any similar concept.

Domains must first be declared in the object that owns them and then they can be passed to sub block definitions:

domain: :ips
domain: :ahb, endian: :little

sub_block :analog, class_name: "ANALOG_T921", base_address: 0x4000_0000, domain: :ips

The domain attribute will also accept an array of multiple domain names as required.

The child block and any of its registers will then be associated with the given domains:

$dut.nvm.analog.domains            # => {ips: <object>}
$dut.nvm.analog.ctrl.domains       # => {ips: <object>}

Note that the domains method returns a hash pointing to objects that represent the domains, this can therefore be associated with attributes such as the :endian attribute in the example above.

See the Domain Class API for an up to date list of supported attributes.

Per-Domain Base Addresses

Domain specific base addresses can be assigned to a sub-block by passing a hash to the :base_address option as shown in the example below:

class Top
  include Origen::TopLevel

  def initialize
    domain :ips
    domain :ahb

    sub_block :subx1, class_name: "SubX", base_address: 0x1000_0000
    sub_block :subx2, class_name: "SubX", base_address: { ips: 0x2000_0000, ahb: 0x3000_0000 }
  end
end

class SubX
  include Origen::Model

  def initialize
    reg :reg1, 0x200 do
      bits 31..0, :data
    end
    sub_block :suby1, class_name: "SubY", domain: :ips
    sub_block :suby2, class_name: "SubY", domain: :ahb
  end
end

class SubY
  include Origen::Model

  def initialize
    reg :reg1, 0x300 do
      bits 31..0, :data
    end
  end
end

$dut = Top.new
$dut.subx1.reg1.address                # => 0x1000_0200
$dut.subx1.suby1.reg1.address          # => 0x1000_0300
$dut.subx1.suby2.reg1.address          # => 0x1000_0300
# Where a register falls into the case where multiple domains are specified for it, you must
# indicate the one you want
$dut.subx1.reg1.address(domain: :ips)  # => 0x2000_0200
$dut.subx1.reg1.address(domain: :ahb)  # => 0x3000_0200
$dut.subx2.suby1.reg1.address          # => 0x2000_0300
$dut.subx2.suby2.reg1.address          # => 0x3000_0300

In the case where a register owned by a child sub-block is not in any of the domains listed in the base address hash then it will pick up a base address of 0.

An alternative default can be set by adding a :default key to the hash:

sub_block :subx2, class_name: "SubX", base_address: { default: 0x1000_0000, ips: 0x2000_0000, ahb: 0x3000_0000 }

Documenting Registers

Registers can be documented by Ruby comments like this:

# ** MGATE Clock Divider Register **
# The MCLKDIV register is used to divide down the frequency of the HBOSCCLK input. If the MCLKDIV
# register is set to value "N", then the output (beat) frequency of the clock divider is OSCCLK / (N+1). The
# resulting beats are, in turn, counted by the PTIMER module to control the duration of Flash high-voltage
# operations.
reg :mclkdiv, 0x0003, size: 16 do |reg|
  # **Oscillator (Hi)** - Firmware FMU clk source selection. (Note that in addition to this firmware-controlled bit, the
  # FMU clock source is also dependent on test and power control discretes).
  #
  # 0 | FMU clock is the externally supplied bus clock ipg_clk
  # 1 | FMU clock is the internal oscillator from the TFS hardblock
  reg.bit 15, :osch, reset: 1
end

The descriptions are then programmatically accessible via the following methods:

mclkdiv.full_name                          # => "MGATE Clock Divider Register"
mclkdiv.description(include_name: false)   # => ["The MCLKDIV register is...", "register is set...", ...] 

bit = mclkdiv.osch
bit.full_name  # => "Oscillator (Hi)"
bit.description(include_name: false, include_bit_values: false)  # => ["Firmware FMU clk...", "FMU clock...", ...] 
bit.bit_value_descriptions[0]   # => "FMU clock is the externally supplied bus block ipg_clk"
bit.bit_value_descriptions[1]   # => "FMU clock is the internal oscillator from the TFS hardblock"

Most commonly these descriptions will be used for documentation, for example the Documentation Helpers plugin provides helpers to easily present registers in Origen documentation as shown here - Register Helpers.

The descriptions can also be supplied as in-line arguments, but doing so overrides any comment-based documentation. Thus, it is intended to be used only when the register is being declared programmatically by an importer and humans should stick to the above API for clarity:

reg :mclkdiv, 0x0003, size: 16, description: "** MGATE Clock Divider Register **\nThe MCLKDIV reg..." do |reg|
  reg.bit 15, :osch, reset: 1, description: "** Oscillator (Hi) ** - Firmware FMU clk source selection..."
end

Defining Application-Specific Metadata

Within a given application it may be desired to attach some meta-data to a register or bits to track application-specific properties, for example whether a register is only readable in test mode or not.

The object owning the register can define a default set of custom attributes and then override these for specific registers and bits. Here is an example:

def instantiate_registers(options={})

  default_reg_metadata do |reg|
    reg.user_reg = false
  end

  default_bit_metadata do |bit|
    bit.time_to_respond = 0
  end

  reg :fstat, 0x0001, size: 8, user_reg: true do |reg|
    reg.bit 8, :ccif, time_to_respond: 10.us
    reg.bit 7, :rdcolerr
    reg.bit 6, :accerr, access: :w1c
    reg.bit 5, :fpviol, access: :w1c
    reg.bit 0, :mgstat, access: :ro
  end

  reg :mclkdiv, 0x0002, size: 16 do |reg|
    reg.bit 15, :osch, reset: 1
    reg.bit 13..12, :mode_rdy, writable: false
    reg.bit 10, :eccen, reset: 1
    reg.bit 9..8, :cmdloc
    reg.bit 7..0, :div
  end

end

fstat.user_reg?     # => true
mclkdiv.user_reg?   # => false

fstat.ccif.time_to_respond     # => 10us
fstat.accerr.time_to_respond   # => 0

Global attributes can also be added, when done in this way the meta data will be applied to all registers within the scope of an application.

If the same attribute is later declared within a class as above then the value from the class will take precedence.

As with the above example the attribute values can be overridden when defining registers and bits.

Origen::Registers.default_reg_metadata do |reg|
  reg.attr_x        # Adds the attribute with a default value of nil
  reg.attr_y = 10   # Adds the attribute with a default value of 10
end

Origen::Registers.default_bit_metadata do |reg|
  reg.attr_z = 15
end

Plugins may also use this approach to globally extend the attributes of registers and bits.

Importing Registers

If the register data for the target device/module is already mastered somewhere else, for example in IP-XACT format, then it can be imported directly to save having to manually duplicate the register definitions in Origen. This will generate the exact same models of the registers as if they had been declared directly into Origen.

This import (and export) functionality is provided via the Cross Origen plugin and this should be consulted directly for the latest information on the API and the supported formats.

However here is a brief example of how it can be used to import from a local XML file:

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

    def initialize(options={})
      cr_import(path: "#{Origen.root}/ipxact_files/nvm_m682.xml")
    end
  end
end

Understanding the Origen Register Model

By using the above API Origen has now built an accurate register model that will look and behave like the real register on silicon. This is extremely convenient and useful for pattern generation as we will see later, but it can also be very useful for other applications such as generating documentation.

Each register created by Origen is an instance of the Origen::Registers::Reg class. This does not by itself provide that much functionality and its main purpose is as a container for the individual bits that make up the register.

Each bit is an instance of the Origen::Registers::Bit class. The main function of the bit object is to store a single bit of data and to track the state of various attribute flags that monitor such things as whether the bit is writable, or readable, or whether a particular bit is required to be read during a test pattern operation.

Similar to the Reg the Bit API is mainly for internal use.

Instead the public facing API is implemented by the Origen::Registers::BitCollection class. This class provides a consistent API whether you are working with an entire register or a subset of bits in the register.

By calling ctrl a new BitCollection is generated on the fly and populated with all of the bit objects contained in the register. Similarly if you call ctrl.adch then a BitCollection will be returned that contains only the subset of requested bits.

Examples

Here are a few examples of working with the register that we recently added:

$dut = SOC::EAGLE_M352.new(version: 1)

reg = $dut.nvm.analog.ctrl

reg.data                  # => 31
reg.data.to_hex           # => "0x1F"

# Only bits that are writable can hold data, the same as real silicon
reg.write(0xFFFF)
reg.data.to_hex           # => "0x7F"
  
# Individual bits can be manipulated
reg.adch.data             # => 31
reg.adch.write(0)       
reg.adch.data             # => 0
                          
reg.data.to_hex           # => "0x60"

# Bits can be marked for read
reg.is_to_be_read?        # => false
reg.coco.read
reg.coco.is_to_be_read?   # => true
reg.aien.is_to_be_read?   # => false
reg.is_to_be_read?        # => true

# The register can be reset
reg.reset
reg.is_to_be_read?        # => false
reg.data.to_hex           # => "0x1F"

More Examples

Here are some addtional examples showing how to iterate through a model’s registers

class Top
  include Origen::TopLevel

  def initialize
    reg :adc0_cfg, 0x200 do
      bits 31..0, :data
    end
    reg :adc1_cfg, 0x300 do
      bits 31..0, :data
    end
    reg :dac_cfg, 0x400 do
      bits 31..0, :data
    end
    reg :status, 0x100 do
      bits 31..0, :data
    end
  end
end

$dut = Top.new

# Iterate over ALL registers (:adc0_cfg, :adc1_cfg, :dac_cfg, :status)
$dut.regs.each do |r|
  # do something with register r
end

# Iterate over all registers matching a regular expression
# Ex 1: Only ADC cfg registers (:adc0_cfg, :adc1_cfg)
$dut.regs('/acd\d_cfg/').each do |r|
  # do something with register r
end

# Ex 2: Any cfg registers (:adc0_cfg, :adc1_cfg, :dac_cfg)
$dut.regs('/cfg/').each do |r|
  # do something with register r
end

Comments

Generated with the Origen Semiconductor Developer's Kit

Origen is released under the terms of the MIT license