Module: Origen::Specs::Checkers

Included in:
Origen::Specs, Spec, Spec
Defined in:
lib/origen/specs/checkers.rb

Instance Method Summary collapse

Instance Method Details

#evaluate_limit(limit) ⇒ Object


72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/origen/specs/checkers.rb', line 72

def evaluate_limit(limit)
  return limit if limit.is_a?(Numeric)
  return nil if limit.nil?
  if limit.is_a? Symbol
    limit = ':' + limit.to_s
  else
    limit.gsub!("\n", ' ')
    limit.scrub!
  end
  result = false
  if limit.match(/\:\S+/)
    limit_items = limit.split(/\:|\s+/).reject(&:empty?)
    references = limit.split(/\:|\s+/).select { |var| var.match(/^[a-zA-Z]\S+$/) }
    new_limit_items = [].tap do |limit_ary|
      limit_items.each do |item|
        if references.include? item
          # See if the limit is referencing a power domain, this should be extended to clocks
          # TODO: Expand limit references to check Origen::Clocks
          if Origen.top_level.respond_to? :power_domains
            if Origen.top_level.power_domains.include? item.to_sym
              limit_ary << Origen.top_level.power_domains(item.to_sym).nominal_voltage
              next
            end
          end
          if Origen.top_level.respond_to? :clocks
            if Origen.top_level.clocks.include? item.to_sym
              limit_ary << Origen.top_level.clocks(item.to_sym).freq_target
              next
            end
          end
          limit_ary << item
        else
          limit_ary << item
        end
      end
    end
    new_limit = new_limit_items.join(' ')
    new_limit_references = new_limit.split(/\:|\s+/).select { |var| var.match(/^[a-zA-Z]\S+$/) }
    if new_limit_references.empty?
      result = eval(new_limit).round(4)
    else
      return limit
    end
  elsif !!(limit.match(/^\d+\.\d+$/)) || !!(limit.match(/^-\d+\.\d+$/))
    result = Float(limit).round(4) rescue false # Use the same four digits of accuracy as the Spec model
  elsif !!(limit.match(/\d+\.\d+\s+\d+\.\d+/)) # workaround for multiple specs authoring bug
    Origen.log.debug "Found two numbers without an operator in the limit string '#{limit}', choosing the first..."
    first_number = limit.match(/(\d+\.\d+)\s+\d+\.\d+/).captures.first
    result = Float(first_number).round(4) rescue false # Use the same four digits of accuracy as the Spec model
  # elsif !!(limit.match(/^tbd$/i)) # unique case of TBD or To Be Determined, will convert to symbol
  #  limit = limit.downcase.to_sym
  else
    result = Integer(limit) rescue false
  end
  if result == false
    # Attempt to eval the limit because users could write a limit like "3.3 + 50.mV"
    # which would not work with the code above but should eval to a number 3.35
    begin
      result = eval(limit)
      return result.round(4) if result.is_a? Numeric
      rescue ::SyntaxError, ::NameError, ::TypeError
        Origen.log.debug "Limit '#{limit}' had to be rescued, storing it as a #{limit.class}"
        if limit.is_a? Symbol
          return limit
        else
          return "#{limit}"
        end
    end
  else
    return result
  end
end

#get_modeObject


58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/origen/specs/checkers.rb', line 58

def get_mode
  spec_mode = nil
  if current_mode.nil?
    if self == Origen.top_level
      spec_mode = :global
    else
      spec_mode = :local
    end
  else
    spec_mode = current_mode.name
  end
  spec_mode
end

#limits_ok?Boolean

Check that min, max are not mixed with typ. If a user wants a baseline value for a spec use target as it will not be checked against pass/fail

Returns:

  • (Boolean)

26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/origen/specs/checkers.rb', line 26

def limits_ok?
  status = true
  if (@min.exp.to_s.include? '/') || (@max.exp.to_s.include? '/')
    return status
  end
  if @min.exp.nil? ^ @max.exp.nil?
    @limit_type = :single_sided
    if @typ.exp
      # status = false
      Origen.log.debug "Spec #{@name} has a typical limit defined with either min or max.  They are mutually exclusive, use 'target' when using min or max"
    end
  elsif @min.exp && @max.exp
    @limit_type = :double_sided
    # Both min and max must be numerical to compare them
    if @min.value.is_a?(Numeric) && @max.value.is_a?(Numeric)
      # Check that min and max make sense
      if @max.value <= @min.value || @min.value >= @max.value
        status = false
        Origen.log.debug "Spec #{@name} has min (#{@min.value}) and max (#{@max.value}) reversed"
      end
      # Check that target is OK
      unless @target.nil?
        if @target.value <= @min.value || @target.value >= @max.value
          status = false
          Origen.log.debug "Spec #{@name} has a target (#{@target.value}) that is not within the min (#{@min.value}) and max #{@max.value}) values"
        end
      end
    end
  end
  status
end

#name_audit(name) ⇒ Object

rubocop:disable Style/RescueModifier:


5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# File 'lib/origen/specs/checkers.rb', line 5

def name_audit(name)
  return name if name.nil?
  return nil unless name.is_a?(Symbol) || name.is_a?(String)
  if name == :inspect
    Origen.log.debug ':inspect is a reserved spec name'
    return nil
  end
  if name.match(/^\d/)
    Origen.log.debug "Spec #{name} starts with a number"
    return nil
  end
  if name.match(/\s+/)
    Origen.log.debug "Spec #{name} contains white space, removing it"
    name.delete!(/\s+/)
  end
  name.is_a?(String) ? name.downcase.to_sym : name
end