• 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

Pattern Generator

Registers


The majority of patterns are concerned with reading and writing to registers to make the DUT do something and consequently more than 90% of your time developing an Origen test application should be concerned with writing code at the register level.

All test transaction drivers such as the ARM Debug driver are expected to support the basic Origen register API. This allows the test engineer to develop code at register level and independently of the underlying test communication protocol which can be easily changed to something else as required.

Basic Concept

See the Registers section of the Models guide for details and examples of how to add and manipulate register states within your model logic.

Interaction with the registers should be limited to the model’s controller - no one else should reach into a model to manipulate its register state (this is not prevented but recommended), and the controllers should instead expose interface methods for the outside world to use. Internally such methods will work by manipulating registers in a sequence.

The read! and write! methods when called on a register (or bit) will automatically fire off a request for the register to be written using the following rules:

  • If the model or controller that owns the register implements a write_register method then call this with the target register passed in as the argument
  • Otherwise if the object that owns the register implements an owner method, then see if the object that this returns provides a suitable write_register method
  • If not then if a currently instantiated model includes the Origen::TopLevel module then see if it (or its controller) has a write_register method available
  • If the request has still not been fulfilled then raise an error

Read works in the same way except that it looks for a method called read_register.

What this all means is that within a controller you will build your test logic from methods that look like this:

def measure_vref(setting=nil)
  if setting
    ss "Measure the Vref voltage for setting #{setting}"
    vref.level.write(setting)
  else
    ss "Measure the default Vref voltage"
  end
  vref.test_enable.write!(1)
end

# From a pattern call like this:
Pattern.create do
  $dut.nvm.measure_vref(5)
end

The actual mechanism for how the registers are written is abstracted away from the test logic itself and therefore the test logic is generally independent from the communication protocol. This is a very powerful concept that allows plugins to be created that provide test sequences for a specific silicon module and these can then be re-used on DUTs that employ completely different register access protocols.

Recommended Architecture

This is the recommended architecture for modern Origen applications that will lend itself to working well within a plugin-based environment.

Define registers in the child models that own them and create test methods to manipulate them in the controller:

class NVM
  include Origen::Model

  def initialize
    reg :vref, 0x0003, size: 16 do |reg|
      reg.bit 15,   :test_enable
      reg.bit 7..0, :level
    end
  end
end

class NVMController
  include Origen::Controller

  def measure_vref(setting=nil)
    if setting
      ss "Measure the Vref voltage for setting #{setting}"
      vref.level.write(setting)
    else
      ss "Measure the default Vref voltage"
    end
    vref.test_enable.write!(1)
  end
end

Defer how to actually write the register to the top-level SoC controller and normally this would be done via one of the available protocol plugins. Here for example is an SoC which will write the register via the Nexus protocol:

class MySoC
  include Origen::TopLevel  # Indicate to Origen that this model represents a top-level device object
end

class MySoCController
  include Origen::Controller
  include Nexus

  # Process register reads using the Nexus protocol
  def read_register(reg, options={})
    nexus.read_register(reg, options)
  end

  # As above for write requests
  def write_register(reg, options={})
    nexus.write_register(reg, options)
  end
end

And that is all that is required, Origen takes care of the hook up and the behind the scenes communication to make it all work.

Another target may then instantiate a different SoC model which could use a completely different protocol like ARM Debug, in which case the NVM test module would still work although the generated pattern would look completely different.

Bit Level Access

All registers support bit level updates, we have seen an example of this already:

vref.test_enable.write!(1)

What this will do is update the value held by the given bits and then send the parent register object to the write_register method for processing. All other bits in the register will maintain the state that they had prior to this operation commencing.

Since it is not generally possible to update only a subset of bits on a device the entire register will still be updated on silicon. However in the case of performing a bit-level read things get a bit more interesting.

The equivalent read operation will update the data values of the bits in the same way, but it will also set a flag on those bits marking that they have been requested for read.

vref.test_enable.read!(1)

The protocol driver can then look out for this flag when generating the readout vectors and only enable a compare on the vectors that correspond to the bits marked for read. Generally this takes a lot of the cognitive overhead out of writing patterns since you can mentally disregard the state of all bits except the ones that you care about.

All standard Origen protocol plugins are expected to support this feature.

The same is true for store (meaning capture the value on the tester) or overlay operations:

# Capture the value of the level bits
vref.level.store!

# Dynamically overlay the value written to the level bits
vref.level.overlay("vref_setting")
vref.write!

Combining Multiple Bit Level Accesses Into A Single Transaction

Multiple individual bits within a register can be accessed/manipulated within a single transaction by making multiple bit-level calls to read (or write), followed by a single register-level read! (or write!) operation:

my_reg.my_bits_x.read(1)
my_reg.my_bits_z.read(0b101)
my_reg.read!

Sometimes you will see application code that combines the final transaction request with the final bit-level operation, this will do the same thing as the above example:

my_reg.my_bits_x.read(1)
my_reg.my_bits_z.read!(0b101)

If you prefer, an equivalent block form API is also available, this is equivalent to the above example:

my_reg.read! do |reg|
  reg.my_bits_x.read(1)
  reg.my_bits_z.read(0b101)
end

Writing a Driver

Generally the code for an existing driver should be reviewed to see how to go about this, the JTAG driver would be a good example to look at, but here are some basic pointers on good driver design.

All drivers should implement read and write register methods:

def write_register(reg, options={})
end

def read_register(reg, options={})
end

The write methods are usually fairly simple, here is a basic example of how to do a parallel and a serial protocol write:

# Writing on a parallel port, let's say we have a 16-bit register and the
# data needs to be written on a pin group called data which is 8-bits
def write_register(reg, options={})
  pin(:din).drive(1)   # Let's say when this pin is high data is captured
  # Drive the data in MSB -> LSB order
  pins(:data).drive!(reg[15..8].data)
  pins(:data).drive!(reg[7..0].data)
  pin(:din).drive(0)   # Turn off capture
end

# Writing on a serial port, same as above only this time the :data port is
# only 1-bit wide
def read_register(reg, options={})
  pin(:din).drive(1)   # Let's say when this pin is high data is captured
  # Drive the data in MSB -> LSB order
  reg.shift_out_left do |bit|
    pin(:data).drive!(bit.data)
  end
  pin(:din).drive(0)   # Turn off capture
end

Read methods are usually a bit more involved to implement the bit-specific read and capture operations. Here is a parallel and serial protocol example which supports bit-level read operations:

# Reading on a parallel port, let's say we have a 16-bit register and the
# data needs to be read on a pin group called data which is 8-bits
def write_register(reg, options={})
  pin(:dout).drive(1)   # Let's say when this pin is high data is presented

  # Compare the data in MSB
  8.times do |i|
    bit = reg.bit(i + 8)
    if bit.is_to_be_read?
      pins(:data)[i].assert(bit.data)
    else
      pins(:data)[i].dont_care
    end
  end
  $tester.cycle

  # Now the data in LSB
  8.times do |i|
    bit = reg.bit(i)
    if bit.is_to_be_read?
      pins(:data)[i].assert(bit.data)
    else
      pins(:data)[i].dont_care
    end
  end
  $tester.cycle

  pin(:dout).drive(0)   # Turn off read out
end

# Reading on a serial port, same as above only this time the :data port is
# only 1-bit wide
def read_register(reg, options={})
  pin(:dout).drive(1)   # Let's say when this pin is high data is presented

  # Drive the data in MSB -> LSB order
  reg.shift_out_left do |bit|
    if bit.is_to_be_read?
      pin(:data).assert!(bit.data)
    else
      pin(:data).dont_care!
    end
  end

  pin(:dout).drive(0)   # Turn off read out
end

Comments

Generated with the Origen Semiconductor Developer's Kit

Origen is released under the terms of the MIT license