origen 0.31.0 → 0.32.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 437a40756e6194c3308c3044388d48c4c0b0c31c
4
- data.tar.gz: d9341d81f27c7bbbf014fe26844d67f53bc1facf
3
+ metadata.gz: 4b636bd97c227eecf2344ccbdc17e29712953325
4
+ data.tar.gz: cd69d451f85313509d2a2c4a31887cbf81fb57c2
5
5
  SHA512:
6
- metadata.gz: 8ce734ee2bd0f466c05c38c7a9205ac7bfbee4165780bfdca9c57f4eb3390b49819882a43e0c24f512f4b65c866a2edf5c057b6c6eb4015d7bea48c1cbbfa3a5
7
- data.tar.gz: 967eafac1a53b990d7b8b5f689278f27527ece71e76cfd6db63faecd7d8b4dbce5a8c87c96c037c1451925267b52d5216179f1108d06183c9dd21ad65188f996
6
+ metadata.gz: 4e104a65bd531b7e52ec7f9f74d002b822d004fb0c46556e412392c3b874101a8be06c27fd83bb05e35acad5fa3b2c62251d9ba729a5de08dc472004ffd11b57
7
+ data.tar.gz: dc29b4f3a4567780af525fc9b023bae374af3d71ed96f241e294ae35aba9d241009f4496d4c7f25c112dca0a8d3978b3f6223b378488b8dc967bbabff6501af6
data/config/version.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  module Origen
2
2
  MAJOR = 0
3
- MINOR = 31
3
+ MINOR = 32
4
4
  BUGFIX = 0
5
5
  DEV = nil
6
6
 
@@ -79,6 +79,7 @@ module Origen
79
79
  else
80
80
  if options[:action] == :program
81
81
  Origen.generator.generate_program(expand_lists_and_directories(options[:files], options), options)
82
+ Origen.app.listeners_for(:program_generated).each(&:program_generated)
82
83
  else
83
84
  temporary_plugin_from_options = options[:current_plugin]
84
85
  expand_lists_and_directories(options[:files], options).each do |file|
@@ -1,16 +1,18 @@
1
1
  module Origen
2
2
  module Clocks
3
3
  class Clock
4
- attr_accessor :id, :owner, :users, :startup_freq, :nominal_freq, :frequency_range, :setpoint
4
+ attr_accessor :id, :owner, :users, :freq_target, :freq_range, :setpoint, :instantiate_users
5
5
 
6
6
  def initialize(id, owner, options = {}, &block)
7
7
  @id = id
8
8
  @owner = owner
9
9
  @id = @id.symbolize unless id.is_a? Symbol
10
+ @instantiate_users = true
10
11
  options.each { |k, v| instance_variable_set("@#{k}", v) }
11
12
  (block.arity < 1 ? (instance_eval(&block)) : block.call(self)) if block_given?
12
13
  @users = [@users] unless @users.is_a? Array
13
- instantiate_users
14
+ add_users_sub_blocks if @instantiate_users
15
+ @setpoint = @freq_target
14
16
  end
15
17
 
16
18
  def name
@@ -21,29 +23,30 @@ module Origen
21
23
  def users
22
24
  @users
23
25
  end
24
- alias_method :ips, :users
25
- alias_method :sub_blocks, :users
26
26
 
27
27
  def setpoint=(val)
28
- setpoint_ok?
29
- @setpoint = val
28
+ if val == :gated || val == 'gated'
29
+ @setpoint = :gated
30
+ else
31
+ setpoint_ok?(val) # This just warns if the clock is set out of range
32
+ @setpoint = val
33
+ end
30
34
  end
31
35
 
32
36
  def setpoint_ok?(val = nil)
33
- return nil if val.nil? && setpoint.nil?
34
- if freq_range == :fixed
35
- if val.nil? || val == nominal_frequency
37
+ val = @setpoint if val.nil?
38
+ if @freq_range == :fixed
39
+ if val == @freq_target
36
40
  return true
37
41
  else
38
- Origen.log.warn("Clock '#{id}' is a fixed clock with a nominal frequency of #{nominal_frequency.as_Hz}, setting it to #{val.as_Hz}")
42
+ Origen.log.warn("Clock '#{id}' is a fixed clock with a target frequency of #{@freq_target.as_Hz}")
39
43
  return false
40
44
  end
41
45
  else
42
- val = setpoint if val.nil?
43
- if freq_range.include?(val)
46
+ if @freq_range.include?(val)
44
47
  return true
45
48
  else
46
- Origen.log.warn("Setpoint (#{setpoint_string(val)}) for clock '#{id}' is not within the frequency range (#{freq_range_string}), setting it to #{val.as_Hz}")
49
+ Origen.log.warn("Setpoint (#{setpoint_string(val)}) for clock '#{id}' is not within the frequency range (#{freq_range_string})")
47
50
  return false
48
51
  end
49
52
  end
@@ -51,17 +54,10 @@ module Origen
51
54
  alias_method :value_ok?, :setpoint_ok?
52
55
  alias_method :val_ok?, :setpoint_ok?
53
56
 
54
- # Set the clock to the nominal frequency
55
- def setpoint_to_nominal
56
- @setpoint = nominal_frequency
57
- end
58
-
59
- # Nominal frequency
60
- def nominal_frequency
61
- @nominal_frequency
57
+ # Set the clock to the target frequency
58
+ def setpoint_to_target
59
+ @setpoint = @freq_target
62
60
  end
63
- alias_method :nominal, :nominal_frequency
64
- alias_method :nom, :nominal_frequency
65
61
 
66
62
  # Current setpoint, defaults top nil on init
67
63
  def setpoint
@@ -71,11 +67,34 @@ module Origen
71
67
  alias_method :value, :setpoint
72
68
 
73
69
  # Acceptable frequency range
74
- def frequency_range
75
- @frequency_range
70
+ def freq_range
71
+ @freq_range
72
+ end
73
+ alias_method :range, :freq_range
74
+
75
+ # Acceptable frequency range
76
+ def freq_target
77
+ @freq_target
78
+ end
79
+ alias_method :target, :freq_target
80
+
81
+ # min method
82
+ def min
83
+ if @freq_range == :fixed
84
+ @freq_target
85
+ else
86
+ @freq_range.first
87
+ end
88
+ end
89
+
90
+ # max method
91
+ def max
92
+ if @freq_range == :fixed
93
+ @freq_target
94
+ else
95
+ @freq_range.last
96
+ end
76
97
  end
77
- alias_method :freq_range, :frequency_range
78
- alias_method :range, :frequency_range
79
98
 
80
99
  # Check if the clock users are defined anywhere in the DUT
81
100
  def users_defined?
@@ -108,12 +127,12 @@ module Origen
108
127
  private
109
128
 
110
129
  # Instantiate and IP/users that use/access the clock
111
- def instantiate_users
112
- users.each do |ip|
113
- if owner.respond_to? ip
130
+ def add_users_sub_blocks
131
+ @users.each do |ip|
132
+ if @owner.respond_to? ip
114
133
  next
115
134
  else
116
- owner.sub_block ip
135
+ @owner.sub_block ip
117
136
  end
118
137
  end
119
138
  end
@@ -130,20 +149,20 @@ module Origen
130
149
  end
131
150
 
132
151
  def frequencies_ok?
133
- if nominal_frequency.nil?
152
+ if @freq_target.nil?
134
153
  false
135
- elsif frequency_range.nil?
154
+ elsif @freq_range.nil?
136
155
  Origen.log.error("Missing frequency range for clock '#{name}'!")
137
156
  false
138
- elsif frequency_range.is_a? Range
139
- if frequency_range.include?(nominal_frequency)
157
+ elsif freq_range.is_a? Range
158
+ if freq_range.include?(@freq_target)
140
159
  true
141
160
  else
142
- Origen.log.error("PPEKit: Nominal frequency #{nominal_frequency} is not inbetween the frequency range #{frequency_range} for clock '#{name}'!")
161
+ Origen.log.error("PPEKit: Frequency target #{@freq_target} is not inbetween the frequency range #{freq_range} for clock '#{name}'!")
143
162
  false
144
163
  end
145
164
  else
146
- Origen.log.error("Clock attribute 'frequency_range' must be a Range!")
165
+ Origen.log.error("Clock attribute 'freq_range' must be a Range!")
147
166
  return_value = false
148
167
  end
149
168
  end
@@ -157,24 +176,24 @@ module Origen
157
176
  end
158
177
 
159
178
  def freq_range_string
160
- start_freq = freq_range.first
161
- end_freq = freq_range.last
179
+ start_freq = @freq_range.first
180
+ end_freq = @freq_range.last
162
181
  "#{start_freq.as_Hz}\.\.#{end_freq.as_Hz}"
163
182
  end
164
183
 
165
184
  def clock_freqs_ok?
166
- if nominal_freq.nil?
185
+ if @freq_target.nil?
167
186
  false
168
- elsif freq_range == :fixed
187
+ elsif @freq_range == :fixed
169
188
  true
170
189
  else
171
- if freq_range.nil?
190
+ if @freq_range.nil?
172
191
  Origen.log.error("PPEKit: Missing frequency target or range for clock '#{id}'!")
173
192
  false
174
- elsif freq_range.include?(nominal_freq)
193
+ elsif @freq_range.include?(@freq_target)
175
194
  true
176
195
  else
177
- Origen.log.error("PPEKit: Frequency target #{nominal_freq} is not inbetween the freq range #{freq_range} for clock '#{id}'!")
196
+ Origen.log.error("PPEKit: Frequency target #{@freq_target} is not inbetween the freq range #{@freq_range} for clock '#{id}'!")
178
197
  false
179
198
  end
180
199
  end
@@ -92,7 +92,11 @@ The following options are available:
92
92
  server.close
93
93
  # Start the server
94
94
  puts ''
95
- puts "Point your browser to this address: http://#{host}#{domain.empty? ? '' : '.' + domain}:#{port}"
95
+ if host.include? domain
96
+ puts "Point your browser to this address: http://#{host}:#{port}"
97
+ else
98
+ puts "Point your browser to this address: http://#{host}#{domain.empty? ? '' : '.' + domain}:#{port}"
99
+ end
96
100
  puts ''
97
101
  puts 'To shut down the server use CTRL-C'
98
102
  puts ''
@@ -210,9 +210,10 @@ module Origen
210
210
  file = inject_import_path(file, type: :template)
211
211
  file = add_underscore_to(file)
212
212
  file = add_extension_to(file)
213
+ web_file = file =~ /\.(html|md)(\.|$)/
213
214
  begin
214
215
  # Allow relative references to templates/web when compiling a web template
215
- if Origen.lsf.current_command == 'web'
216
+ if Origen.lsf.current_command == 'web' || web_file
216
217
  clean_path_to(file, load_paths: "#{Origen.root}/templates/web")
217
218
  else
218
219
  clean_path_to(file)
@@ -220,7 +221,7 @@ module Origen
220
221
  rescue
221
222
  # Try again without .erb
222
223
  file = file.gsub('.erb', '')
223
- if Origen.lsf.current_command == 'web'
224
+ if Origen.lsf.current_command == 'web' || web_file
224
225
  clean_path_to(file, load_paths: "#{Origen.root}/templates/web")
225
226
  else
226
227
  clean_path_to(file)
@@ -349,10 +349,21 @@ module Origen
349
349
 
350
350
  unless options[:inhibit] || !Origen.tester.generate? || job.test?
351
351
  stats.collect_for_pattern(job.output_pattern) do
352
- File.open(job.output_pattern, 'w') do |f|
353
- [:header, :body, :footer].each do |section|
354
- Origen.tester.format(stage.bank(section), section) do |line|
355
- f.puts line
352
+ # If the tester is going to deal with writing out the final pattern. The use case for this is when
353
+ # the pattern is comprised of multiple files instead of the more conventional case here which each
354
+ # pattern is one file.
355
+ if tester.respond_to?(:open_and_write_pattern)
356
+ tester.open_and_write_pattern(job.output_pattern) do
357
+ [:header, :body, :footer].each do |section|
358
+ Origen.tester.format(stage.bank(section), section)
359
+ end
360
+ end
361
+ else
362
+ File.open(job.output_pattern, 'w') do |f|
363
+ [:header, :body, :footer].each do |section|
364
+ Origen.tester.format(stage.bank(section), section) do |line|
365
+ f.puts line
366
+ end
356
367
  end
357
368
  end
358
369
  end
@@ -362,7 +373,7 @@ module Origen
362
373
  log.info "Pattern vectors: #{stats.number_of_vectors_for(job.output_pattern).to_s.ljust(10)}"
363
374
  log.info 'Execution time'.ljust(15) + ': %.6f' % stats.execution_time_for(job.output_pattern)
364
375
  log.info '----------------------------------------------------------------------'
365
- check_for_changes(job.output_pattern, job.reference_pattern)
376
+ check_for_changes(job.output_pattern, job.reference_pattern) unless tester.try(:disable_pattern_diffs)
366
377
  stats.record_pattern_completion(job.output_pattern)
367
378
  end
368
379
 
@@ -0,0 +1,126 @@
1
+ module Origen
2
+ module Limits
3
+ class Limit
4
+ attr_accessor :expr, :value, :owner, :type
5
+
6
+ def initialize(expr, type, owner, options = {})
7
+ @expr = expr
8
+ @owner = owner
9
+ @type = type
10
+ @value = evaluate_expr
11
+ end
12
+
13
+ # If the value is still a String then it could be
14
+ # due to referencing another LimitSet that is
15
+ # yet to be defined
16
+ def value
17
+ if @value.is_a?(String)
18
+ evaluate_expr
19
+ else
20
+ @value
21
+ end
22
+ end
23
+
24
+ # Backwards compatibility
25
+ def exp
26
+ @expr
27
+ end
28
+
29
+ private
30
+
31
+ def fetch_reference_value(ref)
32
+ ref = ref.to_sym unless ref.is_a?(Symbol)
33
+ # Check if the reference is to another limit set
34
+ if owner.limits.include? ref
35
+ return owner.limits(ref).send(type).value
36
+ # Check if the reference is to a power domain
37
+ end
38
+ if Origen.top_level.respond_to? :power_domains
39
+ if Origen.top_level.power_domains.include? ref
40
+ # Need to check the limit type and retrieve the appropriate value
41
+ case @type.to_s
42
+ when /target|typ/
43
+ return Origen.top_level.power_domains(ref).nominal_voltage
44
+ else
45
+ return Origen.top_level.power_domains(ref).send(@type)
46
+ end
47
+ end
48
+ end
49
+ # Check if the reference is to a clock
50
+ if Origen.top_level.respond_to? :clocks
51
+ if Origen.top_level.clocks.include? ref
52
+ # Need to check the limit type and retrieve the appropriate value
53
+ case @type.to_s
54
+ when /target|typ/
55
+ return Origen.top_level.clocks(ref).freq_target
56
+ else
57
+ return Origen.top_level.clocks(ref).send(@type)
58
+ end
59
+ end
60
+ end
61
+ end
62
+
63
+ def evaluate_expr
64
+ return @expr if @expr.is_a?(Numeric)
65
+ return nil if @expr.nil?
66
+ if @expr.is_a? Symbol
67
+ @expr = ':' + @expr.to_s
68
+ else
69
+ @expr.gsub!("\n", ' ')
70
+ @expr.scrub!
71
+ end
72
+ result = false
73
+ if @expr.match(/\:\S+/)
74
+ limit_items = @expr.split(/\:|\s+/).reject(&:empty?)
75
+ if limit_items.size == 1
76
+ return fetch_reference_value(limit_items.first)
77
+ else
78
+ references = @expr.split(/\:|\s+/).select { |var| var.match(/^[a-zA-Z]\S+$/) }
79
+ new_limit_items = [].tap do |limit_ary|
80
+ limit_items.each do |item|
81
+ if references.include? item
82
+ limit_ary << fetch_reference_value(item)
83
+ next
84
+ else
85
+ limit_ary << item
86
+ end
87
+ end
88
+ end
89
+ new_limit = new_limit_items.join(' ')
90
+ new_limit_references = new_limit.split(/\:|\s+/).select { |var| var.match(/^[a-zA-Z]\S+$/) }
91
+ if new_limit_references.empty?
92
+ result = eval(new_limit).round(4)
93
+ else
94
+ return @expr
95
+ end
96
+ end
97
+ elsif !!(@expr.match(/^\d+\.\d+$/)) || !!(@expr.match(/^-\d+\.\d+$/))
98
+ result = Float(@expr).round(4) rescue false # rubocop:disable Style/RescueModifier
99
+ elsif !!(@expr.match(/\d+\.\d+\s+\d+\.\d+/))
100
+ Origen.log.debug "Found two numbers without an operator in the @expr string '#{@expr}', choosing the first..."
101
+ first_number = @expr.match(/(\d+\.\d+)\s+\d+\.\d+/).captures.first
102
+ result = Float(first_number).round(4) rescue false # rubocop:disable Style/RescueModifier
103
+ else
104
+ result = Integer(@expr) rescue false # rubocop:disable Style/RescueModifier
105
+ end
106
+ if result == false
107
+ # Attempt to eval the @expr because users could write a @expr like "3.3 + 50.mV"
108
+ # which would not work with the code above but should eval to a number 3.35
109
+ begin
110
+ result = eval(@expr)
111
+ return result.round(4) if result.is_a? Numeric
112
+ rescue ::SyntaxError, ::NameError, ::TypeError
113
+ Origen.log.debug "Limit '#{@expr}' had to be rescued, storing it as a #{@expr.class}"
114
+ if @expr.is_a? Symbol
115
+ return @expr
116
+ else
117
+ return "#{@expr}"
118
+ end
119
+ end
120
+ else
121
+ return result
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,120 @@
1
+ require_relative './limit'
2
+ module Origen
3
+ module Limits
4
+ class LimitSet
5
+ attr_accessor :id, :min, :typ, :max, :target, :description, :static, :owner, :type
6
+
7
+ def initialize(id, owner, options)
8
+ @id = id
9
+ @description = options[:description]
10
+ @owner = owner
11
+ @min = Limit.new(options[:min], :min, @owner) unless options[:min].nil?
12
+ @typ = Limit.new(options[:typ], :typ, @owner) unless options[:typ].nil?
13
+ @max = Limit.new(options[:max], :max, @owner) unless options[:max].nil?
14
+ @target = Limit.new(options[:target], :target, @owner) unless options[:target].nil?
15
+ unless options[:static].nil?
16
+ unless [true, false].include? options[:static]
17
+ Origen.log.error("Static option must be set to 'true' or 'false'!")
18
+ fail
19
+ end
20
+ end
21
+ @static = options[:static].nil? ? false : options[:static]
22
+ fail unless limits_ok?
23
+ end
24
+
25
+ # Common alias
26
+ def name
27
+ @id
28
+ end
29
+
30
+ def frozen?
31
+ @static
32
+ end
33
+
34
+ def min=(val)
35
+ if frozen?
36
+ Origen.log.warn('Cannot change a frozen limit set!')
37
+ else
38
+ @min = Limit.new(val, :min, @owner)
39
+ end
40
+ end
41
+
42
+ def max=(val)
43
+ if frozen?
44
+ Origen.log.warn('Cannot change a frozen limit set!')
45
+ else
46
+ @max = Limit.new(val, :max, @owner)
47
+ end
48
+ end
49
+
50
+ def typ=(val)
51
+ if frozen?
52
+ Origen.log.warn('Cannot change a frozen limit set!')
53
+ else
54
+ @typ = Limit.new(val, :typ, @owner)
55
+ end
56
+ end
57
+
58
+ def target=(val)
59
+ if frozen?
60
+ Origen.log.warn('Cannot change a frozen limit set!')
61
+ else
62
+ @target = Limit.new(val, :target, @owner)
63
+ end
64
+ end
65
+
66
+ private
67
+
68
+ # Check that min, max are not mixed with typ. If a user wants
69
+ # a baseline value for a spec use target as it will not be
70
+ # checked against pass/fail
71
+ def limits_ok?
72
+ status = true
73
+ # Must have at least one of the limit types defined
74
+ if @min.nil? && @max.nil? && @typ.nil? && @target.nil?
75
+ Origen.log.error("Limit set #{@id} does not have any limits defined!")
76
+ return false
77
+ end
78
+ if @min.nil? ^ @max.nil?
79
+ @type = :single_sided
80
+ unless @typ.nil?
81
+ status = false
82
+ Origen.log.error "Limit set #{@id} has a typical limit defined with either min or max. They are mutually exclusive, use 'target' when using min or max"
83
+ end
84
+ # Check if the target is OK
85
+ unless @target.nil?
86
+ if @min.nil?
87
+ unless @target < @max
88
+ status = false
89
+ Origen.log.error("Limit set #{@id} has the target value #{@target} set greater than the max value #{@max}!")
90
+ end
91
+ else
92
+ unless @target > @min
93
+ status = false
94
+ Origen.log.error("Limit set #{@id} has the target value #{@target} set less than the min value #{@min}!")
95
+ end
96
+ end
97
+ end
98
+ elsif @min.expr && @max.expr
99
+ @type = :double_sided
100
+ # Both min and max must be numerical to compare them
101
+ if @min.value.is_a?(Numeric) && @max.value.is_a?(Numeric)
102
+ # Check that min and max make sense
103
+ if @max.value <= @min.value || @min.value >= @max.value
104
+ status = false
105
+ Origen.log.error "Limit set #{@id} has min (#{@min.value}) and max (#{@max.value}) reversed"
106
+ end
107
+ # Check that target is OK
108
+ unless @target.nil?
109
+ if @target.value <= @min.value || @target.value >= @max.value
110
+ status = false
111
+ Origen.log.error "Limit set #{@id} has a target (#{@target.value}) that is not within the min (#{@min.value}) and max #{@max.value}) values"
112
+ end
113
+ end
114
+ end
115
+ end
116
+ status
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,37 @@
1
+ require_relative './limits/limit'
2
+ require_relative './limits/limit_set'
3
+ module Origen
4
+ module Limits
5
+ TYPES = [:min, :typ, :max, :target]
6
+
7
+ def add_limits(set, options)
8
+ @_limits ||= {}
9
+ options.ids.each do |limit_type|
10
+ unless TYPES.include? limit_type
11
+ Origen.log.error("Limit type '#{limit_type}' not supported, choose from #{TYPES}!")
12
+ fail
13
+ end
14
+ end
15
+ if @_limits.include? set
16
+ # Limit set already exists, modify it unless it is frozen
17
+ unless @_limits[set].frozen?
18
+ options.each do |limit_type, limit_expr|
19
+ @_limits[set].send("#{limit_type}=", limit_expr)
20
+ end
21
+ end
22
+ else
23
+ # Create a default limit set
24
+ @_limits[set] = LimitSet.new(set, self, options)
25
+ end
26
+ end
27
+
28
+ def limits(set = nil)
29
+ @_limits ||= {}
30
+ if set.nil?
31
+ @_limits
32
+ else
33
+ @_limits[set]
34
+ end
35
+ end
36
+ end
37
+ end
@@ -11,16 +11,13 @@ module Origen
11
11
  }.merge(options)
12
12
  # file_path is for internal use, don't pass it from the application, use the :dir option if you
13
13
  # want to change where the exported files are
14
- if options[:file_path]
15
- file = File.join(options[:file_path], "#{name}.rb")
16
- else
17
- file = export_path(name, options)
18
- end
19
- file = Pathname.new(file)
20
- FileUtils.rm_rf(file.sub_ext('').to_s) if File.exist?(file.sub_ext('').to_s)
21
- FileUtils.rm_rf(file.to_s) if File.exist?(file.to_s)
22
- FileUtils.mkdir_p(file.dirname)
23
- File.open(file, 'w') do |f|
14
+ file = options[:file_path] || export_path(name, options)
15
+ dir = options[:dir_path] || export_dir(options)
16
+ path_to_file = Pathname.new(File.join(dir, file))
17
+ FileUtils.rm_rf(path_to_file.sub_ext('').to_s) if File.exist?(path_to_file.sub_ext('').to_s)
18
+ FileUtils.rm_rf(path_to_file.to_s) if File.exist?(path_to_file.to_s)
19
+ FileUtils.mkdir_p(path_to_file.dirname)
20
+ File.open(path_to_file, 'w') do |f|
24
21
  export_wrap_with_namespaces(f, options) do |indent|
25
22
  f.puts((' ' * indent) + 'def self.extended(model)')
26
23
  indent += 2
@@ -51,12 +48,15 @@ module Origen
51
48
  ground_pin_groups.each do |id, pins|
52
49
  f.puts export_pin_group(id, pins, indent: indent, method: :add_ground_pin_group)
53
50
  end
51
+ virtual_pins.each do |id, pin|
52
+ f.puts export_pin(id, pin, indent: indent, method: :add_virtual_pin)
53
+ end
54
54
  f.puts
55
55
  end
56
56
  end
57
57
  if options[:include_sub_blocks]
58
58
  sub_blocks.each do |name, block|
59
- f.puts export_sub_block(name, block, options.merge(indent: indent, file_path: file))
59
+ f.puts export_sub_block(name, block, options.merge(indent: indent, file_path: file, dir_path: dir))
60
60
  end
61
61
  f.puts unless sub_blocks.empty?
62
62
  end
@@ -74,10 +74,18 @@ module Origen
74
74
  end
75
75
 
76
76
  def import(name, options = {})
77
- path = export_path(name, options)
77
+ path = File.join(export_dir(options), export_path(name, options))
78
78
  if File.exist?(path)
79
79
  require path
80
- extend "#{Origen.app.namespace.underscore.camelcase}::#{name.to_s.camelcase}".constantize
80
+ if options.key?(:namespace) && !options[:namespace]
81
+ extend name.to_s.gsub('.', '_').camelcase.constantize
82
+ else
83
+ if options[:namespace]
84
+ extend "#{options[:namespace].to_s.gsub('.', '_').camelcase}::#{name.to_s.gsub('.', '_').camelcase}".constantize
85
+ else
86
+ extend "#{Origen.app.namespace}::#{name.to_s.gsub('.', '_').camelcase}".constantize
87
+ end
88
+ end
81
89
  true
82
90
  else
83
91
  if options[:allow_missing]
@@ -143,16 +151,16 @@ module Origen
143
151
  if n == ''
144
152
  nil
145
153
  else
146
- n.camelcase
154
+ n.to_s.gsub('.', '_').camelcase
147
155
  end
148
156
  end.compact
149
157
  end
150
158
 
151
159
  def export_path(name, options = {})
152
160
  if options.key?(:namespace) && !options[:namespace]
153
- File.join(export_dir(options), "#{name.to_s.underscore}.rb")
161
+ "#{name.to_s.underscore}.rb"
154
162
  else
155
- File.join(export_dir(options), (options[:namespace] || Origen.app.namespace).to_s.underscore, "#{name.to_s.underscore}.rb")
163
+ File.join((options[:namespace] || Origen.app.namespace).to_s.underscore, "#{name.to_s.underscore}.rb")
156
164
  end
157
165
  end
158
166
 
@@ -173,8 +181,11 @@ module Origen
173
181
  line << ", #{pkg_meta}" unless pkg_meta == ''
174
182
  Array(options[:attributes]).each do |attr|
175
183
  unless (v = pin.send(attr)).nil?
176
- if v.is_a?(Numeric)
184
+ case v
185
+ when Numeric, Array, Hash
177
186
  line << ", #{attr}: #{v}"
187
+ when Symbol
188
+ line << ", #{attr}: :#{v}"
178
189
  else
179
190
  line << ", #{attr}: '#{v}'"
180
191
  end
@@ -183,8 +194,11 @@ module Origen
183
194
  unless pin.meta.empty?
184
195
  line << ', meta: { '
185
196
  line << pin.meta.map do |k, v|
186
- if v.is_a?(Numeric)
197
+ case v
198
+ when Numeric, Array, Hash
187
199
  "#{k}: #{v}"
200
+ when Symbol
201
+ "#{k}: :#{v}"
188
202
  else
189
203
  "#{k}: '#{v}'"
190
204
  end
@@ -208,9 +222,9 @@ module Origen
208
222
 
209
223
  def export_sub_block(id, block, options = {})
210
224
  indent = ' ' * (options[:indent] || 0)
211
- file = File.join(options[:file_path].sub_ext(''), "#{id}.rb")
212
- local_file = file.to_s.sub("#{export_dir}/", '')
213
- line = indent + "model.sub_block :#{id}, file: '#{local_file}', lazy: true"
225
+ file_path = File.join(Pathname.new(options[:file_path]).sub_ext(''), "#{id}.rb")
226
+ dir_path = options[:dir_path]
227
+ line = indent + "model.sub_block :#{id}, file: '#{file_path}', dir: '#{dir_path}', lazy: true"
214
228
  unless block.base_address == 0
215
229
  line << ", base_address: #{block.base_address.to_hex}"
216
230
  end
@@ -223,7 +237,7 @@ module Origen
223
237
  line << ", #{key}: #{value}"
224
238
  end
225
239
  end
226
- block.export(id, options.merge(file_path: options[:file_path].sub_ext('').to_s))
240
+ block.export(id, options.merge(file_path: file_path, dir_path: dir_path))
227
241
  line
228
242
  end
229
243
 
data/lib/origen/model.rb CHANGED
@@ -34,6 +34,7 @@ module Origen
34
34
  include Origen::Clocks
35
35
  include Origen::Model::Exporter
36
36
  include Origen::Component
37
+ include Origen::Limits
37
38
  end
38
39
 
39
40
  module ClassMethods
@@ -205,6 +206,7 @@ module Origen
205
206
  unless top_level?
206
207
  # Need to do this in case a class besides SubBlock includes Origen::Model
207
208
  obj_above_self = parent.nil? ? Origen.top_level : parent
209
+ return nil if obj_above_self.nil?
208
210
  if obj_above_self.current_mode
209
211
  _modes[obj_above_self.current_mode.id] if _modes.include? obj_above_self.current_mode.id
210
212
  end
@@ -216,6 +218,10 @@ module Origen
216
218
  # Set the current mode configuration of the current model
217
219
  def current_mode=(id)
218
220
  @current_mode = id.is_a?(ChipMode) ? id.id : id
221
+ Origen.app.listeners_for(:on_mode_changed).each do |listener|
222
+ listener.on_mode_changed(mode: @current_mode)
223
+ end
224
+ @current_mode
219
225
  end
220
226
  alias_method :mode=, :current_mode=
221
227
 
@@ -69,6 +69,18 @@ module Origen
69
69
  _parameter_sets.empty? ? false : true
70
70
  end
71
71
 
72
+ # Return value of param if it exists, nil otherwise.
73
+ def param?(name)
74
+ _param = name.to_s =~ /^params./ ? name.to_s : 'params.' + name.to_s
75
+ begin
76
+ val = eval("self.#{_param}")
77
+ rescue
78
+ nil
79
+ else
80
+ val
81
+ end
82
+ end
83
+
72
84
  # @api private
73
85
  def _parameter_current
74
86
  if path = self.class.parameters_context
@@ -109,9 +109,22 @@ module Origen
109
109
  v = tester.capture do
110
110
  store!(sync: true)
111
111
  end
112
- reverse_shift_out_with_index do |bit, i|
113
- bit.instance_variable_set('@updated_post_reset', true)
114
- bit.instance_variable_set('@data', v.first[i])
112
+ if v.first
113
+ # Serial shift
114
+ if v.size == 1
115
+ reverse_shift_out_with_index do |bit, i|
116
+ bit.instance_variable_set('@updated_post_reset', true)
117
+ bit.instance_variable_set('@data', v.first[i])
118
+ end
119
+ # Parallel shift
120
+ else
121
+ reverse_shift_out_with_index do |bit, i|
122
+ bit.instance_variable_set('@updated_post_reset', true)
123
+ bit.instance_variable_set('@data', v[i].to_i)
124
+ end
125
+ end
126
+ else
127
+ Origen.log.warning "No data was captured when attempting to sync register #{owner.name}, this is probably because the current read_register driver method does not implement store requests"
115
128
  end
116
129
  end
117
130
  if size
@@ -119,8 +119,47 @@ module Origen
119
119
  end
120
120
  end
121
121
 
122
+ # Fetches any defined remotes, regardless of whether they are dirty or not
123
+ def resolve_remotes!
124
+ resolve_remotes
125
+ process_remotes
126
+ end
127
+
122
128
  private
123
129
 
130
+ # Process each remote
131
+ def process_remotes
132
+ remotes.each do |_name, remote|
133
+ dir = workspace_of(remote)
134
+ rc_url = remote[:rc_url] || remote[:vault]
135
+ tag = remote[:tag].nil? ? Origen::VersionString.new(remote[:version]) : Origen::VersionString.new(remote[:tag])
136
+ version_file = dir.to_s + '/.current_version'
137
+ begin
138
+ if File.exist?("#{dir}/.initial_populate_successful")
139
+ FileUtils.rm_f(version_file) if File.exist?(version_file)
140
+ rc = RevisionControl.new remote: rc_url, local: dir
141
+ rc.send rc.remotes_method, version: prefix_tag(tag), force: true
142
+ File.open(version_file, 'w') do |f|
143
+ f.write tag
144
+ end
145
+ else
146
+ rc = RevisionControl.new remote: rc_url, local: dir
147
+ rc.send rc.remotes_method, version: prefix_tag(tag), force: true
148
+ FileUtils.touch "#{dir}/.initial_populate_successful"
149
+ File.open(version_file, 'w') do |f|
150
+ f.write tag
151
+ end
152
+ end
153
+ rescue Origen::GitError, Origen::DesignSyncError, Origen::PerforceError => e
154
+ # If Git failed in the remote, its usually easy to see what the problem is, but now *where* it is.
155
+ # This will prepend the failing remote along with the error from the revision control system,
156
+ # then rethrow the error
157
+ e.message.prepend "When updating remotes for #{remote[:importer].name}: "
158
+ raise e
159
+ end
160
+ end
161
+ end
162
+
124
163
  # Returns the name of the given import (a lower cased symbol)
125
164
  def name_of(remote)
126
165
  dir_defined?(remote)
@@ -243,6 +282,11 @@ module Origen
243
282
  # * If multiple versions of the same remote are found the most
244
283
  # recent one wins.
245
284
  def add_remote(new)
285
+ # Cannot have both a tag and a version defined for a remote
286
+ if ([:tag, :version] - new.keys).empty?
287
+ Origen.log.error('Cannot define both a tag and a version for a remote!')
288
+ fail
289
+ end
246
290
  name = name_of(new)
247
291
  # If the current remote has been imported by one of it's dev dependencies
248
292
  # then always use the local workspace
@@ -289,28 +333,27 @@ module Origen
289
333
  end
290
334
  if remote[:path]
291
335
  create_symlink(remote[:path], dir)
292
-
293
336
  else
294
337
  rc_url = remote[:rc_url] || remote[:vault]
295
- tag = Origen::VersionString.new(remote[:version])
338
+ tag = remote[:tag].nil? ? Origen::VersionString.new(remote[:version]) : Origen::VersionString.new(remote[:tag])
296
339
  version_file = dir.to_s + '/.current_version'
297
340
  begin
298
341
  if File.exist?("#{dir}/.initial_populate_successful")
299
342
  FileUtils.rm_f(version_file) if File.exist?(version_file)
300
343
  rc = RevisionControl.new remote: rc_url, local: dir
301
- rc.checkout version: prefix_tag(tag), force: true
344
+ rc.send rc.remotes_method, version: prefix_tag(tag), force: true
302
345
  File.open(version_file, 'w') do |f|
303
346
  f.write tag
304
347
  end
305
348
  else
306
349
  rc = RevisionControl.new remote: rc_url, local: dir
307
- rc.checkout version: prefix_tag(tag), force: true
350
+ rc.send rc.remotes_method, version: prefix_tag(tag), force: true
308
351
  FileUtils.touch "#{dir}/.initial_populate_successful"
309
352
  File.open(version_file, 'w') do |f|
310
353
  f.write tag
311
354
  end
312
355
  end
313
- rescue Origen::GitError, Origen::DesignSyncError => e
356
+ rescue Origen::GitError, Origen::DesignSyncError, Origen::PerforceError => e
314
357
  # If Git failed in the remote, its usually easy to see what the problem is, but now *where* it is.
315
358
  # This will prepend the failing remote along with the error from the revision control system,
316
359
  # then rethrow the error
@@ -16,6 +16,8 @@ module Origen
16
16
  attr_reader :remote
17
17
  # Returns a pointer to the local location (a Pathname object)
18
18
  attr_reader :local
19
+ # Method to use by Origen::RemoteManager to handle fetching a remote file
20
+ attr_reader :remotes_method
19
21
 
20
22
  # rubocop:disable Lint/UnusedMethodArgument
21
23
 
@@ -27,6 +29,7 @@ module Origen
27
29
  end
28
30
  @remote = Pathname.new(options[:remote])
29
31
  @local = Pathname.new(options[:local]).expand_path
32
+ @remotes_method = :checkout
30
33
  initialize_local_dir(options)
31
34
  end
32
35
 
@@ -192,6 +195,12 @@ module Origen
192
195
  is_a?(Git) # :-)
193
196
  end
194
197
 
198
+ # Returns true if the revision controller object uses Perforce
199
+ def p4?
200
+ is_a?(Perforce)
201
+ end
202
+ alias_method :perforce?, :p4?
203
+
195
204
  # Returns true if the revision controller object uses Subversion
196
205
  def svn?
197
206
  is_a?(Subversion) # :-)
@@ -0,0 +1,110 @@
1
+ require 'P4'
2
+ module Origen
3
+ module RevisionControl
4
+ class Perforce < Base
5
+ # P4 session
6
+ attr_reader :p4
7
+
8
+ # e.g. myfile.txt
9
+ attr_accessor :remote_file
10
+
11
+ def initialize(options = {})
12
+ super
13
+ @remotes_method = :print
14
+ @p4 = P4.new
15
+ @p4.maxresults = 1
16
+ parse_remote
17
+ @p4.connect
18
+ unless @p4.connected?
19
+ Origen.log.error("Could not connect to port #{@p4.port} on client #{@p4.client}!")
20
+ fail
21
+ end
22
+ @p4.run_login
23
+ end
24
+
25
+ # Downloads a file to a local directory, no workspace/client is created. Perfect
26
+ # for read-only access like an application remote
27
+ def print(options = {})
28
+ options = {
29
+ verbose: true,
30
+ version: 'Latest'
31
+ }.merge(options)
32
+ cmd = [__method__.to_s, '-o', "#{@local}/#{@remote_file.to_s.split('/')[-1]}", @remote_file.to_s]
33
+ run cmd
34
+ end
35
+
36
+ def checkout(path = nil, options = {})
37
+ not_yet_supported(__method__)
38
+ end
39
+
40
+ def root
41
+ not_yet_supported(__method__)
42
+ end
43
+
44
+ def current_branch
45
+ not_yet_supported(__method__)
46
+ end
47
+
48
+ def diff_cmd
49
+ not_yet_supported(__method__)
50
+ end
51
+
52
+ def unmanaged
53
+ not_yet_supported(__method__)
54
+ end
55
+
56
+ def local_modifications
57
+ not_yet_supported(__method__)
58
+ end
59
+
60
+ def changes
61
+ not_yet_supported(__method__)
62
+ end
63
+
64
+ def checkin
65
+ not_yet_supported(__method__)
66
+ end
67
+
68
+ def build
69
+ not_yet_supported(__method__)
70
+ end
71
+
72
+ private
73
+
74
+ # Needs to be in the form of an Array with command, as a Sting, as the first argument
75
+ # e.g. ["print", "-o", "pins/myfile.txt", "//depot/myprod/main/doc/pinout/myfile.txt"]
76
+ def run(cmd)
77
+ p4.run cmd
78
+ end
79
+
80
+ def not_yet_supported(m)
81
+ Origen.log.warn("The method #{m} is not currently supported by the Perforce API")
82
+ nil
83
+ end
84
+
85
+ def parse_remote
86
+ (@p4.port, @remote_file) = @remote.to_s.match(/^p4\:\/\/(\S+\:\d+)(.*)/).captures unless @remote.nil?
87
+ end
88
+
89
+ def configure_client(options)
90
+ unless options.include? :local
91
+ Origen.log.error('Need options[:local] to know how to configure the Perforce client!')
92
+ fail
93
+ end
94
+ client_name = "#{Origen.app.name}_#{User.current.id}_#{Time.now.to_i}"
95
+ begin
96
+ client_spec = @p4.fetch_client
97
+ client_spec['Root'] = options[:local].to_s
98
+ client_spec['Client'] = client_name
99
+ client_spec['View'] = ["#{@remote_file} //#{client_name}/#{@remote_file.split('/')[-1]}"]
100
+ client_spec['Host'] = nil
101
+ @p4.save_client(client_spec)
102
+ @p4.client = client_name
103
+ rescue P4Exception
104
+ @p4.errors.each { |e| Origen.log.error e }
105
+ raise
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
@@ -4,6 +4,7 @@ module Origen
4
4
  autoload :DesignSync, 'origen/revision_control/design_sync'
5
5
  autoload :Git, 'origen/revision_control/git'
6
6
  autoload :Subversion, 'origen/revision_control/subversion'
7
+ autoload :Perforce, 'origen/revision_control/perforce'
7
8
 
8
9
  IGNORE_DIRS = %w(
9
10
  .ws .lsf log output web coverage .ref .yardoc .collection .bin
@@ -36,6 +37,8 @@ module Origen
36
37
  DesignSync.new(options)
37
38
  when options[:remote] =~ /git/
38
39
  Git.new(options)
40
+ when options[:remote] =~ /^p4/
41
+ Perforce.new(options)
39
42
  else
40
43
  fail "Could not work out the revision control system for: #{options[:remote]}"
41
44
  end
@@ -1,8 +1,6 @@
1
1
  module Origen
2
2
  module Specs
3
3
  module Checkers
4
- require 'nokogiri'
5
-
6
4
  # rubocop:disable Style/RescueModifier:
7
5
  def name_audit(name)
8
6
  return name if name.nil?
@@ -74,7 +72,6 @@ module Origen
74
72
  def evaluate_limit(limit)
75
73
  return limit if limit.is_a?(Numeric)
76
74
  return nil if limit.nil?
77
- limit = limit.to_s if [Nokogiri::XML::NodeSet, Nokogiri::XML::Text, Nokogiri::XML::Element].include? limit.class
78
75
  if limit.is_a? Symbol
79
76
  limit = ':' + limit.to_s
80
77
  else
@@ -364,11 +364,13 @@ module Origen
364
364
 
365
365
  def materialize
366
366
  file = attributes.delete(:file)
367
+ dir = attributes.delete(:dir) || owner.send(:export_dir)
367
368
  block = owner.send(:instantiate_sub_block, name, klass, attributes)
368
369
  if file
369
- require File.join(owner.send(:export_dir), file)
370
+ require File.join(dir, file)
370
371
  block.extend owner.send(:export_module_names_from_path, file).join('::').constantize
371
372
  end
373
+ block.owner = owner
372
374
  block
373
375
  end
374
376
 
@@ -1,6 +1,9 @@
1
+ require 'origen/limits'
1
2
  module Origen
2
3
  module Tests
3
4
  class Test
5
+ include Limits
6
+
4
7
  attr_accessor :id, :owner, :description, :conditions, :platforms
5
8
 
6
9
  def initialize(id, options = {}, &block)
data/lib/origen.rb CHANGED
@@ -68,6 +68,7 @@ unless defined? RGen::ORIGENTRANSITION
68
68
  autoload :Clocks, 'origen/clocks'
69
69
  autoload :Value, 'origen/value'
70
70
  autoload :OrgFile, 'origen/org_file'
71
+ autoload :Limits, 'origen/limits'
71
72
 
72
73
  attr_reader :switch_user
73
74
 
@@ -79,6 +80,7 @@ unless defined? RGen::ORIGENTRANSITION
79
80
  end
80
81
  end
81
82
 
83
+ class PerforceError < OrigenError; status_code(11); end
82
84
  class GitError < OrigenError; status_code(11); end
83
85
  class DesignSyncError < OrigenError; status_code(12); end
84
86
  class RevisionControlUninitializedError < OrigenError; status_code(13); end
@@ -1,6 +1,7 @@
1
1
  # Misc
2
2
  .irb_history
3
3
  .byebug_history
4
+ /config/waves/**/DVEfiles
4
5
 
5
6
  # Editor cruft
6
7
  *.swp
@@ -31,6 +32,8 @@
31
32
  /.bin
32
33
  /.session
33
34
  /tmp
35
+ /waves
36
+ /simulation
34
37
 
35
38
  # Cruft from other revision control systems
36
39
  .SYNC
@@ -4,7 +4,7 @@ module Origen
4
4
  module Export1
5
5
  module Block1
6
6
  def self.extended(model)
7
- model.sub_block :x, file: 'origen/export1/block1/x.rb', lazy: true, base_address: 0x40000000
7
+ model.sub_block :x, file: 'origen/export1/block1/x.rb', dir: '/home/stephen/Code/github/origen/vendor/lib/models', lazy: true, base_address: 0x40000000
8
8
 
9
9
  end
10
10
  end
@@ -66,8 +66,10 @@ module Origen
66
66
  model.add_ground_pin :gnd2
67
67
  model.add_ground_pin :gnd3
68
68
  model.add_ground_pin_group :gnd, :gnd1, :gnd2, :gnd3
69
+ model.add_virtual_pin :relay1
70
+ model.add_virtual_pin :relay2, packages: { bga: {} }
69
71
 
70
- model.sub_block :block1, file: 'origen/export1/block1.rb', lazy: true
72
+ model.sub_block :block1, file: 'origen/export1/block1.rb', dir: '/home/stephen/Code/github/origen/vendor/lib/models', lazy: true
71
73
 
72
74
  end
73
75
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: origen
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.31.0
4
+ version: 0.32.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stephen McGinty
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-02-12 00:00:00.000000000 Z
11
+ date: 2018-03-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -80,20 +80,6 @@ dependencies:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
82
  version: '1.7'
83
- - !ruby/object:Gem::Dependency
84
- name: nokogiri
85
- requirement: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - '='
88
- - !ruby/object:Gem::Version
89
- version: 1.7.2
90
- type: :runtime
91
- prerelease: false
92
- version_requirements: !ruby/object:Gem::Requirement
93
- requirements:
94
- - - '='
95
- - !ruby/object:Gem::Version
96
- version: 1.7.2
97
83
  - !ruby/object:Gem::Dependency
98
84
  name: rspec
99
85
  requirement: !ruby/object:Gem::Requirement
@@ -366,6 +352,26 @@ dependencies:
366
352
  - - "~>"
367
353
  - !ruby/object:Gem::Version
368
354
  version: 0.8.1
355
+ - !ruby/object:Gem::Dependency
356
+ name: p4ruby
357
+ requirement: !ruby/object:Gem::Requirement
358
+ requirements:
359
+ - - "~>"
360
+ - !ruby/object:Gem::Version
361
+ version: '2015.2'
362
+ - - ">="
363
+ - !ruby/object:Gem::Version
364
+ version: 2015.2.1313860
365
+ type: :runtime
366
+ prerelease: false
367
+ version_requirements: !ruby/object:Gem::Requirement
368
+ requirements:
369
+ - - "~>"
370
+ - !ruby/object:Gem::Version
371
+ version: '2015.2'
372
+ - - ">="
373
+ - !ruby/object:Gem::Version
374
+ version: 2015.2.1313860
369
375
  description:
370
376
  email:
371
377
  - stephen.f.mcginty@gmail.com
@@ -494,6 +500,9 @@ files:
494
500
  - lib/origen/generator/stage.rb
495
501
  - lib/origen/global_app.rb
496
502
  - lib/origen/global_methods.rb
503
+ - lib/origen/limits.rb
504
+ - lib/origen/limits/limit.rb
505
+ - lib/origen/limits/limit_set.rb
497
506
  - lib/origen/location.rb
498
507
  - lib/origen/location/base.rb
499
508
  - lib/origen/location/map.rb
@@ -553,6 +562,7 @@ files:
553
562
  - lib/origen/revision_control/base.rb
554
563
  - lib/origen/revision_control/design_sync.rb
555
564
  - lib/origen/revision_control/git.rb
565
+ - lib/origen/revision_control/perforce.rb
556
566
  - lib/origen/revision_control/subversion.rb
557
567
  - lib/origen/ruby_version_check.rb
558
568
  - lib/origen/site_config.rb
@@ -640,7 +650,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
640
650
  version: 1.8.11
641
651
  requirements: []
642
652
  rubyforge_project:
643
- rubygems_version: 2.6.7
653
+ rubygems_version: 2.6.8
644
654
  signing_key:
645
655
  specification_version: 4
646
656
  summary: The Semiconductor Developer's Kit