class Origen::Specs::Spec

Constants

ATTRS
Limit
SpecAttribute
TYPES

Public Class Methods

new(name, type, mode, owner_name, &block) click to toggle source

There are at least three attributes needed to define a unique spec.

1) name (e.g. :vdd)
2) type (e.g. :dc)  Possible values are [:dc, :ac, :temperature]
3) mode (e.g. :global).  mode defaults to the current mode found for the parent object

A mode is defined as a device state that requires some sequence of actions to be enabled. A type is a classification moniker that exists without any stimulus required. Some specs require a fourth attribute sub_type to be uniquely defined. For example, a global device level VDD specification would require four attributes to be unique. Here is an example of two spec definitions for a VDD power supply

name = :vdd, type: :dc, mode: :global, sub_type: typical_operating_conditions, typ = "1.0V +/- 30mV"
name = :vdd, type: :dc, mode: :global, sub_type: maximum_operating_conditions, min = -0.3V, max = 1.8V

Whereas a typical DDR timing specification might only need three attributes to be unique

name: :tddkhas, type: :ac, mode: ddr4dr2400, sub_type: nil
# File lib/origen/specs/spec.rb, line 83
def initialize(name, type, mode, owner_name, &block)
  @name = name_audit(name)
  fail 'Specification names must be of types Symbol or String and cannot start with a number' if @name.nil?

  @type = type
  @sub_type = nil # not necessary to be able to find a unique spec, but required for some specs
  @mode = mode
  @ip_name = owner_name
  @symbol = nil # Meant to be populated with HTML representing the way the spec name should look in a document
  @description = nil
  @min, @typ, @max, @target = nil, nil, nil, nil
  @min_ovr, @typ_ovr, @typ_ovr = nil, nil, nil
  @audience = nil
  @notes = {}
  @exhibits = {}
  @testable = nil
  @guardband = nil
  (block.arity < 1 ? (instance_eval(&block)) : block.call(self)) if block_given?
  fail "Spec type must be one of #{TYPES.join(', ')}" unless TYPES.include? type

  @min = Limit.new(@min)
  @max = Limit.new(@max)
  @typ = Limit.new(@typ)
  @min_ovr = Limit.new(@min_ovr)
  @max_ovr = Limit.new(@max_ovr)
  @typ_ovr = Limit.new(@typ_ovr)
  @guardband = Limit.new(@guardband)
  fail "Spec #{name} failed the limits audit!" unless limits_ok?
end

Public Instance Methods

add_note(id, options = {}) click to toggle source

Add a specification note

# File lib/origen/specs/spec.rb, line 256
def add_note(id, options = {})
  options = {
    type: :spec
  }.update(options)
  # Create the Note instance and add to the notes attribute
  @notes[id] = Origen::Specs::Note.new(id, options[:type], options)
end
diff(compare_spec) click to toggle source

Do a ‘diff’ from the current spec (self) and the compare spec Returns a hash with attribute as key and an array of the attribute values that differed

# File lib/origen/specs/spec.rb, line 203
def diff(compare_spec)
  diff_results = Hash.new do |h, k|
    h[k] = []
  end
  # Loop through self's isntance variables first
  instance_variables.each do |ivar|
    ivar_sym = ivar.to_s.gsub('@', '').to_sym
    next if ivar_sym == :notes # temporarily disable until notes diff method written

    ivar_str = ivar.to_s.gsub('@', '')
    if compare_spec.respond_to? ivar_sym
      # Check if the instance variable is a Limit and if so then find
      # all instance_variables and diff them as well
      if instance_variable_get(ivar).class == Origen::Specs::Spec::Limit
        limit_diff_results = diff_limits(instance_variable_get(ivar), compare_spec.instance_variable_get(ivar))
        # Extract the limit diff pairs and merge with updated keys with the diff_results hash
        limit_diff_results.each do |k, v|
          limit_diff_key = "#{ivar_str}_#{k}".to_sym
          diff_results[limit_diff_key] = v
        end
      else
        unless instance_variable_get(ivar) == compare_spec.instance_variable_get(ivar)
          diff_results[ivar_sym] = [instance_variable_get(ivar), compare_spec.instance_variable_get(ivar)]
          Origen.log.debug "Found spec difference for instance variable #{ivar} for #{self} and #{compare_spec}"
        end
      end
    else
      # The compare spec doesn't have the current instance variable
      # so log a difference
      if instance_variable_get(ivar).class == Origen::Specs::Spec::Limit
        limit_diff_results = diff_limits(instance_variable_get(ivar), compare_spec.instance_variable_get(ivar))
        # Extract the limit diff pairs and merge with updated keys with the diff_results hash
        limit_diff_results.each do |k, v|
          limit_diff_key = "#{ivar_str}_#{k}".to_sym
          diff_results[limit_diff_key] = v
        end
      else
        Origen.log.debug "Instance variable #{ivar} exists for #{self} and does not for #{compare_spec}"
        diff_results[ivar_sym] = [instance_variable_get(ivar), '']
      end
    end
  end
  # Loop through unique instance variables for compare_spec
  diff_results
end
include?(s) click to toggle source

Monkey patch of hash/array include? method needed because Origen::Specs#specs can return a single Spec instance or an Array of Specs

# File lib/origen/specs/spec.rb, line 251
def include?(s)
  s == @name ? true : false
end
inspect() click to toggle source
Calls superclass method
# File lib/origen/specs/spec.rb, line 113
def inspect
  $dut.send(:specs_to_table_string, [self])
rescue
  super
end
method_missing(method, *args, &block) click to toggle source
# File lib/origen/specs/spec.rb, line 183
def method_missing(method, *args, &block)
  ivar = "@#{method.to_s.gsub('=', '')}"
  ivar_sym = ":#{ivar}"
  if method.to_s =~ /=$/
    define_singleton_method(method) do |val|
      instance_variable_set(ivar, val)
    end
  elsif instance_variables.include? ivar_sym
    instance_variable_get(ivar)
  else
    define_singleton_method(method) do
      instance_variable_get(ivar)
    end
  end
  send(method, *args, &block)
end
note_count() click to toggle source

Returns the number of notes as an Integer

# File lib/origen/specs/spec.rb, line 272
def note_count
  @notes.size
end
notes(id = nil) click to toggle source

Returns a Note object from the notes hash

# File lib/origen/specs/spec.rb, line 265
def notes(id = nil)
  return nil if @notes.nil?

  @notes.filter(id)
end
trace_matrix_name() click to toggle source

Returns the trace_matrix name. The Trace Matrix Name is composed of

  • @name

  • @type

  • @subtype

  • @mode

# File lib/origen/specs/spec.rb, line 124
def trace_matrix_name
  name_set = trace_matrix_name_choose
  ret_name = ''
  case name_set
  when 0
    ret_name = ''
  when 1
    ret_name = "#{@mode}"
  when 2
    ret_name = "#{@sub_type}"
  when 3
    ret_name = "#{@sub_type}_#{@mode}"
  when 4
    ret_name = "#{@type}"
  when 5
    ret_name = "#{@type}_#{@mode}"
  when 6
    ret_name = "#{@type}_#{@sub_type}"
  when 7
    ret_name = "#{@type}_#{@sub_type}_#{@mode}"
  when 8
    ret_name = "#{small_name}"
  when 9
    ret_name = "#{small_name}_#{@mode}"
  when 10
    ret_name = "#{small_name}_#{@sub_type}"
  when 11
    ret_name = "#{small_name}_#{@sub_type}_#{@mode}"
  when 12
    ret_name = "#{small_name}_#{@type}"
  when 13
    ret_name = "#{small_name}_#{@type}_#{@mode}"
  when 14
    ret_name = "#{small_name}_#{@type}_#{@sub_type}"
  when 15
    ret_name = "#{small_name}_#{@type}_#{@sub_type}_#{@mode}"
  else
    ret_name = 'Bad trace matrix code'
  end
  ret_name
end
trace_matrix_name_to_dita() click to toggle source

This will create the trace matrix name to be placed into a dita phrase element End goal will be {code:xml}

<ph audience="internal">trace_matrix_name</ph>

{code}

# File lib/origen/specs/spec.rb, line 171
def trace_matrix_name_to_dita
  tmp_doc = Nokogiri::XML('<foo><bar /></foo>', nil, 'EUC-JP')

  tmp_node = Nokogiri::XML::Node.new('lines', tmp_doc)
  tmp_node1 = Nokogiri::XML::Node.new('i', tmp_doc)
  tmp_node.set_attribute('audience', 'trace-matrix-id')
  text_node1 = Nokogiri::XML::Text.new("[#{trace_matrix_name}]", tmp_node)
  tmp_node1 << text_node1
  tmp_node << tmp_node1
  tmp_node.at_xpath('.').to_xml
end

Private Instance Methods

diff_limits(limit_one, limit_two = nil) click to toggle source

This assumes the limit objects are Structs

# File lib/origen/specs/spec.rb, line 289
def diff_limits(limit_one, limit_two = nil)
  diff_results = Hash.new do |h, k|
    h[k] = []
  end
  # Only need to loop through limit one ivars because the Limit class cannot
  # be changed in 3rd party files like the Spec class can be
  limit_one.members.each do |m|
    if limit_two.respond_to? m
      unless limit_one.send(m) == limit_two.send(m)
        diff_results[m] = [limit_one.send(m), limit_two.send(m)]
        Origen.log.debug "Found limit difference for member #{m} for #{limit_one} and #{limit_two}"
      end
    else
      # Limit two doesn't have the current instance variable or was not provided
      # as an argument so log a difference
      Origen.log.debug "Member #{m} exists for #{limit_one} and does not for #{limit_two}"
      diff_results[m] = [limit_one.send(m), '']
    end
  end
  diff_results
end
small_name() click to toggle source
# File lib/origen/specs/spec.rb, line 278
def small_name
  if @name.to_s[0..@ip_name.to_s.length].include? @ip_name.to_s
    ret_name = @name.to_s[@ip_name.to_s.length + 1..-1]
  else
    ret_name = @name.to_s
  end
  ret_name = ret_name.partition('-').last if ret_name.include? '-'
  ret_name
end
trace_matrix_name_choose() click to toggle source
# File lib/origen/specs/spec.rb, line 311
def trace_matrix_name_choose
  name_set = 0
  name_set = 8 unless @name.nil?
  name_set += 4 unless @type.nil?
  name_set += 2 unless @sub_type.nil?
  unless @mode.nil?
    unless (@mode.to_s.include? 'local') || (@mode.to_s.include? 'global')
      name_set += 1
    end
  end
  name_set
end