origen 0.30.0 → 0.31.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: 24fa93bb3395c5d472af5111b4cf2c7424ddb8fb
4
- data.tar.gz: 2115fdaa3ad10e8c5bad07e0693c1aea2f9484d6
3
+ metadata.gz: 437a40756e6194c3308c3044388d48c4c0b0c31c
4
+ data.tar.gz: d9341d81f27c7bbbf014fe26844d67f53bc1facf
5
5
  SHA512:
6
- metadata.gz: 8e2ced12f38f092dfbcc1ff1da469aca527de65decc950050d9b482b4059d1c5613ed8fb627c509519f6275f0c43db4c21d9c20c34012345654ef1006a45d2df
7
- data.tar.gz: 36270d0d55c0f772399a9afbf8380bdc535aa8ad0c047734ab032df1913101c82bb48845e2c56129e757b99c5782e31eca15f7583269a062ff9cc4fa9e8187b0
6
+ metadata.gz: 8ce734ee2bd0f466c05c38c7a9205ac7bfbee4165780bfdca9c57f4eb3390b49819882a43e0c24f512f4b65c866a2edf5c057b6c6eb4015d7bea48c1cbbfa3a5
7
+ data.tar.gz: 967eafac1a53b990d7b8b5f689278f27527ece71e76cfd6db63faecd7d8b4dbce5a8c87c96c037c1451925267b52d5216179f1108d06183c9dd21ad65188f996
data/config/version.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  module Origen
2
2
  MAJOR = 0
3
- MINOR = 30
3
+ MINOR = 31
4
4
  BUGFIX = 0
5
5
  DEV = nil
6
6
 
data/lib/origen.rb CHANGED
@@ -27,6 +27,7 @@ unless defined? RGen::ORIGENTRANSITION
27
27
  require 'option_parser/optparse'
28
28
  require 'bundler'
29
29
  require 'origen/undefined'
30
+ require 'origen/componentable'
30
31
 
31
32
  module Origen
32
33
  autoload :Features, 'origen/features'
@@ -789,4 +790,6 @@ unless defined? RGen::ORIGENTRANSITION
789
790
  # outside the scope of an Origen command
790
791
  require 'origen/global_methods'
791
792
  include Origen::GlobalMethods
793
+
794
+ require 'origen/components'
792
795
  end
@@ -795,7 +795,18 @@ END
795
795
  end
796
796
  @target_instantiated = true
797
797
  Origen.mode = :debug if options[:force_debug]
798
- listeners_for(:on_create).each(&:on_create)
798
+ listeners_for(:on_create).each do |obj|
799
+ unless obj.is_a?(Origen::SubBlocks::Placeholder)
800
+ if obj.try(:is_a_model_and_controller?)
801
+ m = obj.model
802
+ c = obj.controller
803
+ m.on_create if m.respond_to_directly?(:on_create)
804
+ c.on_create if c.respond_to_directly?(:on_create)
805
+ else
806
+ obj.on_create
807
+ end
808
+ end
809
+ end
799
810
  @on_create_called = true
800
811
  # Keep this within the load_event to ensure any objects that are further instantiated objects
801
812
  # will be associated with (and cleared out upon reload of) the current target
@@ -0,0 +1,649 @@
1
+ module Origen
2
+ # Protologism taking after Enumerable, stating that this object can
3
+ # behave like an Origen Component
4
+ module Componentable
5
+ # Custom Componentable Errors
6
+
7
+ # Raised whenever a component name tries to be added but it already exists
8
+ class NameInUseError < StandardError
9
+ end
10
+
11
+ # Raised whenever a component name is used for something but does not exist.
12
+ class NameDoesNotExistError < StandardError
13
+ end
14
+
15
+ # Raised for everything else.
16
+ class Error < StandardError
17
+ end
18
+
19
+ # In order for Origen to bootstrap using a generic callback, we'll work a bit backwards.
20
+ # When this module is included by an 'includer', it will give
21
+ def self.included(othermod)
22
+ add_included_callback(othermod)
23
+ end
24
+
25
+ # These are split in case the included module actually has an :included method defined and the user wants
26
+ # to bootstrap setting up the add_included_callback manually.
27
+ # Note that someone making a module to house a Componentable class is very unlikely to do this, but it could
28
+ # still happen.
29
+ def self.add_included_callback(othermod)
30
+ othermod.define_singleton_method(:origen_model_init) do |klass, options = {}|
31
+ Origen::Componentable.init_parent_class(klass, self)
32
+ end
33
+ end
34
+
35
+ # When Origen's model initializer is included, all Componentable objects will be automatically booted.
36
+ def self.origen_model_init(klass, options = {})
37
+ Origen::Componentable.init_includer_class(klass)
38
+ end
39
+
40
+ # Initializes the class that included Componentable (the 'includer').
41
+ # All of the singleton methods will be added per Ruby, but the includer still needs to be booted by:
42
+ # 1) Creating and initializing the instance attribute (_componentable_container)
43
+ # 2) Defining a method to get the container directly.
44
+ # For example, the Component class will automatically have a :components method that references the
45
+ # _componentable_container.
46
+ def self.init_includer_class(klass_instance)
47
+ klass_instance.instance_variable_set(:@_componentable_container, {}.with_indifferent_access)
48
+ klass_instance.class.class_eval do
49
+ attr_reader :_componentable_container
50
+
51
+ define_method Origen::Componentable.componentable_names(klass_instance)[:plural] do
52
+ @_componentable_container
53
+ end
54
+ end
55
+ end
56
+
57
+ def self.parent_class_evaled?(parent_class, includer_singleton_name)
58
+ evaled = parent_class.instance_variable_get("@_#{includer_singleton_name}_evaled")
59
+ if evaled.nil?
60
+ # Check that this class isn't inheriting from another class that uses Componentable things.
61
+ # If so, the API is inherited and is fine, so set the evaled value for that class to true.
62
+ parent_class.ancestors.each do |ancestor|
63
+ if ancestor.instance_variable_get("@_#{includer_singleton_name}_evaled")
64
+ parent_class.instance_variable_set("@_#{includer_singleton_name}_evaled", true)
65
+ evaled = true
66
+ break
67
+ end
68
+ end
69
+ end
70
+ evaled
71
+ end
72
+
73
+ def self.init_parent_class(parent_class, includer_class)
74
+ # includer_single_name = begin
75
+ # if includer_class.name.include?('::')
76
+ # name = includer_class.name.split('::').last.underscore
77
+ # else
78
+ # name = includer_class.name.underscore
79
+ # end
80
+ # end
81
+ # includer_plural_name = Origen::Componentable.componentable_names(includer_class)[:plural]
82
+
83
+ names = Componentable.componentable_names(includer_class)
84
+ includer_single_name = names[:singleton]
85
+ includer_plural_name = names[:plural]
86
+
87
+ unless parent_class.is_a?(Class)
88
+ inc = parent_class.instance_variable_set("@_#{includer_single_name}".to_sym, includer_class.new)
89
+ inc.parent = parent_class
90
+ parent_class = parent_class.class
91
+ end
92
+
93
+ # If the includer's singleton name is taken (i.e., the parent already has a method <includer_single_name>),
94
+ # raise an error since the 'proper' way to interact with the includer directly is from this method.
95
+ if !Origen::Componentable.parent_class_evaled?(parent_class, includer_single_name) && parent_class.method_defined?(includer_single_name.to_sym)
96
+ fail Origen::Componentable::Error, "Class #{parent_class.name} provides a method :#{includer_single_name} already. Cannot include Componentable class #{includer_class.name} in this object!"
97
+ end
98
+
99
+ # for everything that's not the singleton name method or the @_<singleton_name> instance variable, having the method
100
+ # already exists is a warning, not an error.
101
+ methods_to_add = [
102
+ includer_plural_name.to_sym,
103
+
104
+ # Add methods
105
+ "add_#{includer_single_name}".to_sym,
106
+ "add_#{includer_plural_name}".to_sym,
107
+
108
+ # Listing/Querying methods
109
+ "list_#{includer_plural_name}".to_sym,
110
+ "#{includer_plural_name}_of_class".to_sym,
111
+ "#{includer_plural_name}_instances_of".to_sym,
112
+ "#{includer_plural_name}_of_type".to_sym,
113
+ "#{includer_single_name}?".to_sym,
114
+ "has_#{includer_single_name}?".to_sym,
115
+
116
+ # Enumeration methods
117
+ "each_#{includer_single_name}".to_sym,
118
+ "all_#{includer_plural_name}".to_sym,
119
+ "select_#{includer_plural_name}".to_sym,
120
+ "select_#{includer_single_name}".to_sym,
121
+
122
+ # Copying/Moving methods
123
+ "copy_#{includer_single_name}".to_sym,
124
+ "copy_#{includer_plural_name}".to_sym,
125
+ "move_#{includer_single_name}".to_sym,
126
+ "move_#{includer_plural_name}".to_sym,
127
+
128
+ # Deleting individual item methods
129
+ "delete_#{includer_single_name}".to_sym,
130
+ "delete_#{includer_plural_name}".to_sym,
131
+ "remove_#{includer_single_name}".to_sym,
132
+ "remove_#{includer_plural_name}".to_sym,
133
+ "delete_#{includer_single_name}!".to_sym,
134
+ "delete_#{includer_plural_name}!".to_sym,
135
+ "remove_#{includer_single_name}!".to_sym,
136
+ "remove_#{includer_plural_name}!".to_sym,
137
+
138
+ # Deleting all items methods
139
+ "delete_all_#{includer_plural_name}".to_sym,
140
+ "clear_#{includer_plural_name}".to_sym,
141
+ "remove_all_#{includer_plural_name}".to_sym
142
+ ]
143
+ unless Origen::Componentable.parent_class_evaled?(parent_class, includer_single_name)
144
+ methods_to_add.each do |m|
145
+ if parent_class.method_defined?(m)
146
+ Origen.log.warning "Componentable: Parent class #{parent_class.name} already defines a method #{m}. This method will not be used by Componentable"
147
+ end
148
+ end
149
+ parent_class.instance_variable_set("@_#{includer_single_name}_evaled".to_sym, true)
150
+ end
151
+
152
+ parent_class.class_eval do
153
+ # Note that all of these just trace back to the root method.
154
+
155
+ # Define the root method (singleton-named method)
156
+ # If any arguments are given, it behaves as an :add method.
157
+ # Otherwise, it returns the underlying object itself.
158
+ define_method includer_single_name.to_sym do |*args, &block|
159
+ if args.size == 0
160
+ # No arguments, so just return the class instance
161
+ instance_variable_get("@_#{includer_single_name}".to_sym)
162
+ else
163
+ # Arguments were provided, so treating this as an :add attempt
164
+ instance_variable_get("@_#{includer_single_name}".to_sym).add(*args, &block)
165
+ end
166
+ end
167
+
168
+ # Define the plural-named method.
169
+ # If arguments are given, then it behaves as an :add attempt, with or without a block.
170
+ # If a block is given without any arguments, it will behave as an :each operation and call the block.
171
+ # if no arguments are given, it will return the underlying HASH (not the object).
172
+ # This allows for plural_name[name] to
173
+ define_method "#{includer_plural_name}".to_sym do |*args, &block|
174
+ if block && args.size == 0
175
+ instance_variable_get("@_#{includer_single_name}".to_sym).each(&block)
176
+ elsif args.size == 0
177
+ instance_variable_get("@_#{includer_single_name}".to_sym)._componentable_container
178
+ else
179
+ instance_variable_get("@_#{includer_single_name}".to_sym).add(*args, &block)
180
+ end
181
+ end
182
+
183
+ # define the various 'add' methods
184
+ # what we'll actually do is just define one method then alias all the others together.
185
+ # Currently, this includes:
186
+ # <includer_plural_name>, add_<includer_single_name>, add_
187
+ define_method "add_#{includer_single_name}".to_sym do |name, options = {}, &block|
188
+ instance_variable_get("@_#{includer_single_name}".to_sym).add(name, options, &block)
189
+ end
190
+ alias_method "add_#{includer_plural_name}".to_sym, "add_#{includer_single_name}".to_sym
191
+
192
+ # define listing and getting methods
193
+ define_method "list_#{includer_plural_name}".to_sym do
194
+ instance_variable_get("@_#{includer_single_name}".to_sym).list
195
+ end
196
+
197
+ # define the querying object types
198
+ define_method "#{includer_plural_name}_of_class".to_sym do |klass, options = {}|
199
+ instance_variable_get("@_#{includer_single_name}".to_sym).instances_of(klass, options)
200
+ end
201
+ alias_method "#{includer_plural_name}_instances_of".to_sym, "#{includer_plural_name}_of_class".to_sym
202
+ alias_method "#{includer_plural_name}_of_type".to_sym, "#{includer_plural_name}_of_class".to_sym
203
+
204
+ # define the querying instance existance
205
+ define_method "#{includer_single_name}?".to_sym do |name|
206
+ instance_variable_get("@_#{includer_single_name}".to_sym).has?(name)
207
+ end
208
+ alias_method "has_#{includer_single_name}?".to_sym, "#{includer_single_name}?".to_sym
209
+
210
+ # define some of commonly used enumerate methods
211
+ define_method "each_#{includer_single_name}".to_sym do |&block|
212
+ instance_variable_get("@_#{includer_single_name}".to_sym).each(&block)
213
+ end
214
+ alias_method "all_#{includer_plural_name}".to_sym, "each_#{includer_single_name}".to_sym
215
+
216
+ define_method "select_#{includer_plural_name}".to_sym do |&block|
217
+ instance_variable_get("@_#{includer_single_name}".to_sym).select(&block)
218
+ end
219
+ alias_method "select_#{includer_single_name}".to_sym, "select_#{includer_plural_name}".to_sym
220
+
221
+ # define the copying/moving methods
222
+ define_method "copy_#{includer_single_name}".to_sym do |to_copy, to_location, options = {}|
223
+ instance_variable_get("@_#{includer_single_name}".to_sym).copy(to_copy, to_location, options)
224
+ end
225
+ alias_method "copy_#{includer_plural_name}".to_sym, "copy_#{includer_single_name}".to_sym
226
+
227
+ define_method "move_#{includer_single_name}".to_sym do |to_move, to_location|
228
+ instance_variable_get("@_#{includer_single_name}".to_sym).move(to_move, to_location)
229
+ end
230
+ alias_method "move_#{includer_plural_name}".to_sym, "move_#{includer_single_name}".to_sym
231
+
232
+ # define the deleting single instance methods
233
+ define_method "delete_#{includer_single_name}".to_sym do |name|
234
+ instance_variable_get("@_#{includer_single_name}".to_sym).delete(name)
235
+ end
236
+ alias_method "delete_#{includer_plural_name}".to_sym, "delete_#{includer_single_name}".to_sym
237
+ alias_method "remove_#{includer_single_name}".to_sym, "delete_#{includer_single_name}".to_sym
238
+ alias_method "remove_#{includer_plural_name}".to_sym, "delete_#{includer_single_name}".to_sym
239
+
240
+ define_method "delete_#{includer_single_name}!".to_sym do |name|
241
+ instance_variable_get("@_#{includer_single_name}".to_sym).delete!(name)
242
+ end
243
+ alias_method "delete_#{includer_plural_name}!".to_sym, "delete_#{includer_single_name}!".to_sym
244
+ alias_method "remove_#{includer_single_name}!".to_sym, "delete_#{includer_single_name}!".to_sym
245
+ alias_method "remove_#{includer_plural_name}!".to_sym, "delete_#{includer_single_name}!".to_sym
246
+
247
+ # define the deleting all instances methods
248
+ define_method "delete_all_#{includer_plural_name}".to_sym do
249
+ instance_variable_get("@_#{includer_single_name}".to_sym).delete_all
250
+ end
251
+ alias_method "clear_#{includer_plural_name}".to_sym, "delete_all_#{includer_plural_name}".to_sym
252
+ alias_method "remove_all_#{includer_plural_name}".to_sym, "delete_all_#{includer_plural_name}".to_sym
253
+ end
254
+ end
255
+
256
+ # All of these are generic names and the instantiation. When included, Componentable will add aliases to these
257
+ # methods onto the includer's parent. For example:
258
+ # <includer>.has? becomes $dut.has_<include>?
259
+ # <includer>.delete(name_to_delete) becomes $dut.delete_<includer>(name_to_delete)
260
+ # etc. etc.
261
+ # def self.parent_or_owner(includer)
262
+ # return includer.parent if includer.parent
263
+ # return includer.owner if includer.owner
264
+ # nil
265
+ # end
266
+
267
+ # Gets the plural name of the class.
268
+ def _plural_name
269
+ @plural_name || begin
270
+ @plural_name = Origen::Componentable.componentable_names(self)[:plural]
271
+ @singleton_name = Origen::Componentable.componentable_names(self)[:singleton]
272
+ end
273
+ end
274
+
275
+ # Gets the singleton name of the class.
276
+ def _singleton_name
277
+ @singleton_name || begin
278
+ @plural_name = Origen::Componentable.componentable_names(self)[:plural]
279
+ @singleton_name = Origen::Componentable.componentable_names(self)[:singleton]
280
+ end
281
+ end
282
+
283
+ # Gets the parent of the includer class.
284
+ def parent
285
+ @parent
286
+ end
287
+
288
+ # Sets the parent of the includer class
289
+ def parent=(p)
290
+ @parent = p
291
+ end
292
+
293
+ def self.componentable_names(klass)
294
+ unless klass.is_a?(Class)
295
+ # If we were given an instance of a class, get its actual class.
296
+ klass = klass.class
297
+ end
298
+ names = {}
299
+
300
+ # Evaluate the singleton name. This will be the class name or the class constant
301
+ # COMPONENTABLE_SINGLETON_NAME, if it's defined.
302
+ # The only corner case here is if the class is anonymous, then a COMPONENTABLE_SINGLETON_NAME is required.
303
+ if klass.const_defined?(:COMPONENTABLE_SINGLETON_NAME)
304
+ names[:singleton] = klass.const_get(:COMPONENTABLE_SINGLETON_NAME).downcase.to_sym
305
+ else
306
+ # Check if this is an anonymous class. If so, complain that COMPONENTABLE_SINGLETON_NAME is required
307
+ if !klass.respond_to?(:name) || klass.name.nil? # || klass.name.start_with?('#<Class:')
308
+ if klass.const_defined?(:COMPONENTABLE_PLURAL_NAME)
309
+ # Have a more specific error saying the plural name was found but isn't sufficient.
310
+ fail Origen::Componentable::Error, 'Anonymous classes that include the Componentable module must define COMPONENTABLE_SINGLETON_NAME, even if COMPONENTABLE_PLURAL_NAME is defined'
311
+ else
312
+ fail Origen::Componentable::Error, 'Anonymous classes that include the Componentable module must define COMPONENTABLE_SINGLETON_NAME'
313
+ end
314
+ else
315
+ if klass.name.include?('::')
316
+ names[:singleton] = klass.name.split('::').last.underscore.to_sym
317
+ else
318
+ names[:singleton] = klass.name.underscore.to_sym
319
+ end
320
+ end
321
+ end
322
+
323
+ if klass.const_defined?(:COMPONENTABLE_PLURAL_NAME)
324
+ name = klass.const_get(:COMPONENTABLE_PLURAL_NAME).downcase.to_sym
325
+
326
+ # do a quick check to make sure that the plural name and singleton name aren't set to the same thing
327
+ if name == names[:singleton]
328
+ fail Origen::Componentable::Error, "Componentable including class cannot define both COMPONENTABLE_SINGLETON_NAME and COMPONENTABLE_PLURAL_NAME to '#{name}'"
329
+ end
330
+ else
331
+ name = names[:singleton].to_s
332
+
333
+ # Only deal with a few cases here, I'm not interested in figuring out every
334
+ # english rule to pluralize everything. Examples:
335
+ # deer => deers (not deer, though technically I think deers is actually a word, but an odd one)
336
+ # goose => gooses (not geese)
337
+ # dwarf => dwarfs (not dwarves)
338
+ # If the user is concerned about this, they can supply their own
339
+ # name pluralizing their class name directly.
340
+ if name.match(/is$/)
341
+ # analysis => analyses
342
+ name.gsub!(/is$/, 'es')
343
+ elsif name.match(/[sxz]$|sh$|ch$/)
344
+ # if the names ends with s, h, ch, sh, x, z: append 'es'. Examples:
345
+ # bus => buses
346
+ # stress => stresses
347
+ # box => boxes
348
+ # branch => branches
349
+ # brush => brushes
350
+ # tax => taxes
351
+ # buzz => buzzes
352
+ name += 'es'
353
+ elsif name.match(/on$/)
354
+ # criterion => criteria
355
+ name.gsub!(/on$/, 'a')
356
+ else
357
+ # just append a single 's'. Examples:
358
+ # component => components
359
+ # sub_block => sub_blocks
360
+ # tool => tools
361
+ name += 's'
362
+ end
363
+ name = name.to_sym
364
+ end
365
+ names[:plural] = name
366
+
367
+ names
368
+ end
369
+
370
+ def add(name, options = {}, &block)
371
+ instances = _split_by_instances(name, options, &block)
372
+ return_instances = []
373
+ instances.each do |n, opts|
374
+ return_instances << _add(n, opts)
375
+ end
376
+
377
+ return_instances.size == 1 ? return_instances.first : return_instances
378
+ end
379
+
380
+ def _split_by_instances(name, options = {}, &block)
381
+ if !options[:instances].nil? && options[:instances] > 1
382
+ instances = {}
383
+ options[:instances].times do |i|
384
+ opts = {}
385
+
386
+ # merge the given options with any that are overriden with the block.
387
+ if block_given?
388
+ collector = Origen::Utility::Collector.new
389
+ yield collector
390
+ options.merge!(collector.store)
391
+ end
392
+
393
+ # go through the options one by one now and make sure that each element is either an array to be split
394
+ # by the instances, or is a single object. If not one of these two, complain.
395
+ options.each do |key, val|
396
+ if val.is_a?(Array)
397
+ if val.size == 1
398
+ # An array with a single element. This is fine. Just take that single element as the contents.
399
+ # Note: this is a workaround for the corner case of wanting to pass in an array as an option
400
+ # with a size that doesn't match the number of instances.
401
+ opts[key] = val.first
402
+ elsif val.size != options[:instances]
403
+ # The number of elements in the option doesn't match the number of instances, and it greater than
404
+ # a single element.
405
+ fail Origen::Componentable::Error, "Error when adding #{name}: size of given option :#{key} (#{val.size}) does not match the number of instances specified (#{options[:instances]})"
406
+ else
407
+ # use the index number to grab the correct value for this instance
408
+ opts[key] = val[i]
409
+ end
410
+ else
411
+ opts[key] = val
412
+ end
413
+ end
414
+
415
+ # set the instance's name and add it and its options to the list to be added
416
+ instances["#{name}#{i}".to_sym] = opts
417
+ end
418
+ instances
419
+ else
420
+ if block_given?
421
+ collector = Origen::Utility::Collector.new
422
+ yield collector
423
+ options.merge!(collector.store)
424
+ end
425
+ { name => options }
426
+ end
427
+ end
428
+
429
+ # Adds a new item to the componentable container.
430
+ # @note All options added will be passed to the subclasses instantiation.
431
+ # @note The options is only valid for the stock :add method.
432
+ # @note Any extra options provided are still passed to the subclasses instantiation.
433
+ # @param name [Symbol] Name to reference the new component object.
434
+ # @param options [Hash] Customizations for both the add method and for the class's instantiation.
435
+ # @option options [Class, String] class_name The class to instaniate the component at :name as.
436
+ # @return [ComponentableObject] The instantiated class at :name
437
+ # @raise [Origen::Componentable::NameInUseError] Raised if :name already points to a component.
438
+ def _add(name, options = {}, &block)
439
+ # Add the name and parent to the options if they aren't already given
440
+ # If the parent isn't available on the includer class, it will remain nil.
441
+ options = {
442
+ name: name,
443
+ parent: parent
444
+ }.merge(options)
445
+
446
+ if block_given?
447
+ collector = Origen::Utility::Collector.new
448
+ yield collector
449
+ options.merge!(collector.store)
450
+ end
451
+
452
+ # Instantiate the class. This will place the object in the @_componentable_container at the indicated name
453
+ _instantiate_class(name, options)
454
+
455
+ # Create an accessor for the new item, if indicated to do so.
456
+ _push_accessor(name, options)
457
+
458
+ @_componentable_container[name]
459
+ end
460
+
461
+ def _instantiate_class(name, options)
462
+ if @_componentable_container.key?(name)
463
+ fail Origen::Componentable::NameInUseError, "#{_singleton_name} name :#{name} is already in use."
464
+ end
465
+
466
+ if options[:class_name]
467
+ class_name = options.delete(:class_name)
468
+
469
+ unless Object.const_defined?(class_name.to_s)
470
+ fail Origen::Componentable::NameDoesNotExistError, "class_name option '#{class_name}' cannot be found"
471
+ end
472
+
473
+ # Instantiate the given class
474
+ if class_name.is_a?(String)
475
+ @_componentable_container[name] = eval(class_name).new(options)
476
+ else
477
+ @_componentable_container[name] = class_name.new(options)
478
+ end
479
+ else
480
+ # Instantiate a standard Component if no class given
481
+ @_componentable_container[name] = Origen::Component::Default.new(options)
482
+ end
483
+ @_componentable_container[name]
484
+ end
485
+
486
+ def _push_accessor(name, options)
487
+ if parent
488
+ def push_accessor(name)
489
+ if parent.respond_to?(name.to_sym)
490
+ Origen.log.warn("Componentable: #{_singleton_name} is trying to add an accessor for item :#{name} to parent #{parent.class.name} but that method already exist! No accessor will be added.")
491
+ else
492
+ parent.send(:eval, "define_singleton_method :#{name.to_sym} do; #{_singleton_name}[:#{name}]; end")
493
+ end
494
+ end
495
+
496
+ if self.class.const_defined?(:COMPONENTABLE_ADDS_ACCESSORS) && self.class.const_get(:COMPONENTABLE_ADDS_ACCESSORS)
497
+ if parent.respond_to?(:disable_componentable_accessors)
498
+ if parent.method(:disable_componentable_accessors).arity >= 1
499
+ unless parent.disable_componentable_accessors(self.class)
500
+ push_accessor(name)
501
+ end
502
+ else
503
+ unless parent.disable_componentable_accessors
504
+ push_accessor(name)
505
+ end
506
+ end
507
+ else
508
+ push_accessor(name)
509
+ end
510
+ end
511
+ end
512
+ end
513
+
514
+ # List the items in the componentable container
515
+ # @return [Array<Symbol>] Componentable container item names.
516
+ def list
517
+ @_componentable_container.keys
518
+ end
519
+
520
+ # Checks if a component exist in the componentable container.
521
+ # @param name [Symbol] Name to query existance of.
522
+ # @return [true, false] True if :name exists, False otherwise.
523
+ def has?(name)
524
+ @_componentable_container.include?(name)
525
+ end
526
+
527
+ def [](name)
528
+ @_componentable_container[name]
529
+ end
530
+
531
+ # Copies the component at :to_copy to :to_location. Default is to deep copy (i.e. clone)
532
+ # the to_copy, resulting in two independent components.
533
+ # @param to_copy [Symbol] Name of the component to copy.
534
+ # @param to_location [Symbol] Name to copy to.
535
+ # @param options [Hash] Customization options.
536
+ # @option options [true, false] :deep_copy (true) Indicates whether to deep copy (clone) the component or just copy the a reference to the component.
537
+ # @option options [true, false] :override (false) Indicates if the object at :to_location should be overridden if it already exists.
538
+ # @returns [Symbol] The new object (or reference to) at :to_location.
539
+ # @raise [Origen::Componentable::NameInUseError] Raised if :to_location is already in use and the :override option is not specified.
540
+ # @raise [Origen::Componentable::NameDoesNotExistsError] Raised if :to_copy name does not exists.
541
+ def copy(to_copy, to_location, options = {})
542
+ deep_copy = options.key?(:deep_copy) ? options[:deep_copy] : true
543
+ overwrite = options[:overwrite] || false
544
+
545
+ if @_componentable_container.key?(to_location) && !overwrite
546
+ # The copy location already exists and override was not specified
547
+ fail Origen::Componentable::NameInUseError, "#{_singleton_name} name :#{to_location} is already in use"
548
+ end
549
+
550
+ unless @_componentable_container.key?(to_copy)
551
+ # The to copy name doesn't exist
552
+ fail Origen::Componentable::NameDoesNotExistError, "#{_singleton_name} name :#{to_copy} does not exist"
553
+ end
554
+
555
+ if deep_copy
556
+ @_componentable_container[to_location] = @_componentable_container[to_copy].clone
557
+ else
558
+ @_componentable_container[to_location] = @_componentable_container[to_copy]
559
+ end
560
+ end
561
+
562
+ # Moves a component object from one name to another.
563
+ # @param to_move [Symbol] Component name to move elsewhere.
564
+ # @param new_name [Symbol] New name to give to the component from :to_move.
565
+ # @return [Symbol] The component moved.
566
+ # @raise [Origen::Componentable::NameInUseError] Raised if :new_name is already in use and the :override option is not specified.
567
+ # @raise [Origen::Componentable::NameDoesNotExistsError] Raised if :to_move name does not exists.
568
+ def move(to_move, new_name, options = {})
569
+ overwrite = options[:overwrite] || false
570
+
571
+ if @_componentable_container.key?(new_name) && !overwrite
572
+ # The move location already exists and override was not specified
573
+ fail Origen::Componentable::NameInUseError, "#{_singleton_name} name :#{new_name} is already in use"
574
+ end
575
+
576
+ unless @_componentable_container.key?(to_move)
577
+ # The to_move name doesn't exist
578
+ fail Origen::Componentable::NameDoesNotExistError, "#{_singleton_name} name :#{to_move} does not exist"
579
+ end
580
+
581
+ to_move_object = @_componentable_container.delete(to_move)
582
+ @_componentable_container[new_name] = to_move_object
583
+ end
584
+
585
+ # Deletes a component from the componentable container
586
+ # @param name [Symbol] Name of component to delete
587
+ # @return [Hash(Symbol, <ComponentableItem>)] containing the name of the component deleted and its component.
588
+ # @raise [Origen::Componentable::NameDoesNotExistsError] Raised if :to_copy name does not exists.
589
+ def delete(to_delete)
590
+ obj = delete!(to_delete)
591
+ fail Origen::Componentable::NameDoesNotExistError, "#{_singleton_name} name :#{to_delete} does not exist" if obj.nil?
592
+ obj
593
+ end
594
+ alias_method :remove, :delete
595
+
596
+ # Same as the :delete method but raises an error if :name doesn't exists.
597
+ # @param name [Symbol] Name oof the component to delete.
598
+ # @return [Hash(Symbol, <ComponentableItem>), nil] Hash containing the name of the component deleted and its component. Nil if the name doesn't exist.
599
+ def delete!(to_delete)
600
+ need_to_undef = parent && parent.respond_to?(to_delete) && parent.send(to_delete).eql?(@_componentable_container[to_delete])
601
+
602
+ obj = @_componentable_container.delete(to_delete)
603
+
604
+ if need_to_undef
605
+ parent.instance_eval "undef #{to_delete}"
606
+ end
607
+
608
+ obj
609
+ end
610
+ alias_method :remove!, :delete!
611
+
612
+ # Deletes all of the components in the container.
613
+ # @return [Hash(Symbol, <ComponentableItem>)] Hash containing all of the deleted items.
614
+ def delete_all
615
+ # delete individual objects one by one, making sure to delete all accessors as well
616
+ returns = {}
617
+ @_componentable_container.each do |key, val|
618
+ delete!(key)
619
+ returns[key] = val
620
+ end
621
+ returns
622
+ end
623
+ alias_method :clear, :delete_all
624
+ alias_method :remove_all, :delete_all
625
+
626
+ # Locates all names whose object is a :class_name object.
627
+ # @param class_name [Class, Instance of Class] Class to search componentable container for. This can either be the class name, or can be an instance of the class to search for.
628
+ # @return [Array <Strings>] An array listing all of the names in the componentable container which are a :class_name.
629
+ def instances_of(class_name, options = {})
630
+ unless class_name.is_a?(Class)
631
+ # class_name is actually an instance of the class to search for, not the class itself.
632
+ class_name = class_name.class
633
+ end
634
+ @_componentable_container.select do |name, component|
635
+ component.is_a?(class_name)
636
+ end.keys
637
+ end
638
+
639
+ # Implementation for an each method
640
+ def each(&block)
641
+ @_componentable_container.each(&block)
642
+ end
643
+
644
+ # Implementation for a select method
645
+ def select(&block)
646
+ @_componentable_container.select(&block)
647
+ end
648
+ end
649
+ end
@@ -0,0 +1,34 @@
1
+ module Origen
2
+ # API for dealing with instantiating and keeping track of components or
3
+ # 'component-like object' (e.g. sub blocks).
4
+ # This API can be included as a mixin and extended further extended by the includer
5
+ # to get the top-level component behavior while providing more refined usage at the same time.
6
+ module Component
7
+ # Componentable Component class. This is the general purpose container to just 'add a thing'
8
+ class Component
9
+ include Origen::Componentable
10
+
11
+ COMPONENTABLE_ADDS_ACCESSORS = true
12
+
13
+ # Kind of ironic really, but since we're auto-including this when Origen::Model is included,
14
+ # we can't include Origen::Model here or else we'll get a circular dependency.
15
+ # Note that the parent will still initialize correctly, but we need to initialize Components manually.
16
+ # I.e, the parent will get methods :component, :add_components, :components, etc., but the Component object
17
+ # won't be initialized so everything will fail.
18
+ def initialize
19
+ Origen::Componentable.init_includer_class(self)
20
+ end
21
+ end
22
+
23
+ # Default class instantiate if the class_name is not provided
24
+ class Default
25
+ include Origen::Model
26
+
27
+ attr_reader :options
28
+
29
+ def initialize(options = {})
30
+ @options = options
31
+ end
32
+ end
33
+ end
34
+ end
@@ -78,6 +78,20 @@ module Origen
78
78
  end
79
79
  end
80
80
 
81
+ # When compared to another object, a controller will consider itself equal if either the controller
82
+ # or its model match the given object
83
+ def ==(obj, options = {})
84
+ if obj.is_a?(Origen::SubBlocks::Placeholder)
85
+ obj = obj.materialize
86
+ end
87
+ if options[:called_from_model]
88
+ super(obj)
89
+ else
90
+ super(obj) || model == obj
91
+ end
92
+ end
93
+ alias_method :equal?, :==
94
+
81
95
  # Means that when dealing with a controller/model pair, you can
82
96
  # always call obj.model and obj.controller to get the one you want,
83
97
  # regardless of the one you currently have.
data/lib/origen/model.rb CHANGED
@@ -12,6 +12,7 @@ module Origen
12
12
  included do
13
13
  attr_writer :ip_name
14
14
  attr_accessor :version
15
+ attr_accessor :parent
15
16
  attr_reader :controller
16
17
 
17
18
  include Origen::ModelInitializer
@@ -32,6 +33,7 @@ module Origen
32
33
  include Origen::PowerDomains
33
34
  include Origen::Clocks
34
35
  include Origen::Model::Exporter
36
+ include Origen::Component
35
37
  end
36
38
 
37
39
  module ClassMethods
@@ -52,6 +54,12 @@ module Origen
52
54
  true
53
55
  end
54
56
 
57
+ # Returns true if the instance is an Origen::Model that is wrapped
58
+ # in a controller
59
+ def is_a_model_and_controller?
60
+ !!controller
61
+ end
62
+
55
63
  # Returns true if the model is the current DUT/top-level model
56
64
  def is_top_level?
57
65
  Origen.top_level == self
@@ -66,6 +74,18 @@ module Origen
66
74
  self
67
75
  end
68
76
 
77
+ def ==(obj)
78
+ if obj.is_a?(Origen::SubBlocks::Placeholder)
79
+ obj = obj.materialize
80
+ end
81
+ if controller
82
+ super(obj) || controller.send(:==, obj, called_from_model: true)
83
+ else
84
+ super(obj)
85
+ end
86
+ end
87
+ alias_method :equal?, :==
88
+
69
89
  def log
70
90
  Origen.log
71
91
  end
@@ -121,10 +141,13 @@ module Origen
121
141
  klass = self.class
122
142
  while klass != Object
123
143
  controller_class = "#{klass}Controller"
124
- if eval("defined? #{controller_class}")
125
- return eval(controller_class)
126
- elsif eval("defined? ::#{controller_class}")
127
- return eval("::#{controller_class}")
144
+ unless controller_class.start_with?('#<Class')
145
+ # klass is an anonymous class. Can't support automatic resolution with anonymous classes
146
+ if eval("defined? #{controller_class}")
147
+ return eval(controller_class)
148
+ elsif eval("defined? ::#{controller_class}")
149
+ return eval("::#{controller_class}")
150
+ end
128
151
  end
129
152
  klass = klass.superclass
130
153
  end
@@ -9,12 +9,13 @@ module Origen
9
9
  include_timestamp: true,
10
10
  file_path: nil
11
11
  }.merge(options)
12
+ # file_path is for internal use, don't pass it from the application, use the :dir option if you
13
+ # want to change where the exported files are
12
14
  if options[:file_path]
13
15
  file = File.join(options[:file_path], "#{name}.rb")
14
16
  else
15
- file = export_path(name)
17
+ file = export_path(name, options)
16
18
  end
17
- file += '.rb' unless file =~ /.rb$/
18
19
  file = Pathname.new(file)
19
20
  FileUtils.rm_rf(file.sub_ext('').to_s) if File.exist?(file.sub_ext('').to_s)
20
21
  FileUtils.rm_rf(file.to_s) if File.exist?(file.to_s)
@@ -73,9 +74,18 @@ module Origen
73
74
  end
74
75
 
75
76
  def import(name, options = {})
76
- path = export_path(name)
77
- require path
78
- extend "#{Origen.app.namespace.underscore.camelcase}::#{name.to_s.camelcase}".constantize
77
+ path = export_path(name, options)
78
+ if File.exist?(path)
79
+ require path
80
+ extend "#{Origen.app.namespace.underscore.camelcase}::#{name.to_s.camelcase}".constantize
81
+ true
82
+ else
83
+ if options[:allow_missing]
84
+ false
85
+ else
86
+ fail "The import for #{name} could not be found at #{path}"
87
+ end
88
+ end
79
89
  end
80
90
 
81
91
  private
@@ -115,20 +125,20 @@ module Origen
115
125
  end
116
126
  file.puts '# rubocop:disable all'
117
127
  indent = 0
118
- export_module_names_from_path(file.path).each do |name|
128
+ export_module_names_from_path(file.path, options).each do |name|
119
129
  file.puts((' ' * indent) + "module #{name}")
120
130
  indent += 2
121
131
  end
122
132
  yield indent
123
- export_module_names_from_path(file.path).each do |name|
133
+ export_module_names_from_path(file.path, options).each do |name|
124
134
  indent -= 2
125
135
  file.puts((' ' * indent) + 'end')
126
136
  end
127
137
  file.puts '# rubocop:enable all'
128
138
  end
129
139
 
130
- def export_module_names_from_path(name)
131
- name = name.sub("#{export_dir}/", '').sub('.rb', '')
140
+ def export_module_names_from_path(name, options = {})
141
+ name = name.sub("#{export_dir(options)}/", '').sub('.rb', '')
132
142
  name.split(/[\/\\]/).map do |n|
133
143
  if n == ''
134
144
  nil
@@ -138,12 +148,16 @@ module Origen
138
148
  end.compact
139
149
  end
140
150
 
141
- def export_path(name)
142
- File.join(export_dir, Origen.app.namespace.to_s.underscore, name.to_s.underscore)
151
+ def export_path(name, options = {})
152
+ if options.key?(:namespace) && !options[:namespace]
153
+ File.join(export_dir(options), "#{name.to_s.underscore}.rb")
154
+ else
155
+ File.join(export_dir(options), (options[:namespace] || Origen.app.namespace).to_s.underscore, "#{name.to_s.underscore}.rb")
156
+ end
143
157
  end
144
158
 
145
- def export_dir
146
- File.join(Origen.root, 'vendor', 'lib', 'models')
159
+ def export_dir(options = {})
160
+ options[:dir] || File.join(Origen.root, 'vendor', 'lib', 'models')
147
161
  end
148
162
 
149
163
  def export_pin(id, pin, options = {})
@@ -196,10 +210,19 @@ module Origen
196
210
  indent = ' ' * (options[:indent] || 0)
197
211
  file = File.join(options[:file_path].sub_ext(''), "#{id}.rb")
198
212
  local_file = file.to_s.sub("#{export_dir}/", '')
199
- line = indent + "model.sub_block :#{id}, file: '#{local_file}'"
213
+ line = indent + "model.sub_block :#{id}, file: '#{local_file}', lazy: true"
200
214
  unless block.base_address == 0
201
215
  line << ", base_address: #{block.base_address.to_hex}"
202
216
  end
217
+ block.custom_attrs.each do |key, value|
218
+ if value.is_a?(Symbol)
219
+ line << ", #{key}: :#{value}"
220
+ elsif value.is_a?(String)
221
+ line << ", #{key}: '#{value}'"
222
+ else
223
+ line << ", #{key}: #{value}"
224
+ end
225
+ end
203
226
  block.export(id, options.merge(file_path: options[:file_path].sub_ext('').to_s))
204
227
  line
205
228
  end
@@ -21,6 +21,16 @@ module Origen
21
21
  parent = options.delete(:parent)
22
22
  x.parent = parent if parent
23
23
  end
24
+
25
+ x.class.included_modules.each do |mod|
26
+ mod.send(:origen_model_init, x) if mod.respond_to?(:origen_model_init)
27
+ mod.constants.each do |constant|
28
+ if mod.const_defined?(constant)
29
+ mod.const_get(constant).send(:origen_model_init, x) if mod.const_get(constant).respond_to?(:origen_model_init)
30
+ end
31
+ end
32
+ end
33
+
24
34
  options.each do |k, v|
25
35
  x.send(:instance_variable_set, "@#{k}", v) if x.respond_to?(k)
26
36
  end
@@ -47,10 +57,17 @@ module Origen
47
57
  is_top_level = x.respond_to?(:includes_origen_top_level?)
48
58
  if x.respond_to?(:wrap_in_controller)
49
59
  x = x.wrap_in_controller
50
- # If this object has been instantiated after on_create has already been called,
51
- # then invoke it now
52
- if Origen.application_loaded? && Origen.app.on_create_called?
53
- x.controller.on_create if x.controller.respond_to?(:on_create)
60
+ end
61
+ # If this object has been instantiated after on_create has already been called,
62
+ # then invoke it now
63
+ if Origen.application_loaded? && Origen.app.on_create_called?
64
+ if x.try(:is_a_model_and_controller)
65
+ m = x.model
66
+ c = x.controller
67
+ m.on_create if m.respond_to_directly?(:on_create)
68
+ c.on_create if c.respond_to_directly?(:on_create)
69
+ else
70
+ x.on_create if x.respond_to?(:on_create)
54
71
  end
55
72
  end
56
73
  if is_top_level
@@ -3,10 +3,15 @@ module Origen
3
3
  module Interceptable
4
4
  def self.included(base)
5
5
  base.extend ClassMethods
6
+ end
6
7
 
7
- # Defers this executing until the whole class has loaded
8
- Origen.after_app_loaded do
9
- unless (base.instance_methods - Object.methods).include?(:global_path_to)
8
+ module ClassMethods
9
+ def new(*args, &block)
10
+ o = allocate
11
+ i = OrgFile::Interceptor.new(o)
12
+ o.__interceptor__ = i
13
+ i.send(:initialize, *args, &block)
14
+ unless o.respond_to?(:global_path_to)
10
15
  puts 'When adding the OrgFile::Interceptable module to a class, the class must define an instance method called "global_path_to", like this:'
11
16
  puts
12
17
  puts ' # Must return a string that contains a global path to access the given object,'
@@ -14,17 +19,8 @@ module Origen
14
19
  puts ' def global_path_to'
15
20
  puts ' "dut.pins(:#{id})"'
16
21
  puts ' end'
17
- fail "Incomplete integration of OrgFile::Interceptable in #{base}"
22
+ fail "Incomplete integration of OrgFile::Interceptable in #{o.class}"
18
23
  end
19
- end
20
- end
21
-
22
- module ClassMethods
23
- def new(*args, &block)
24
- o = allocate
25
- i = OrgFile::Interceptor.new(o)
26
- o.__interceptor__ = i
27
- i.send(:initialize, *args, &block)
28
24
  i
29
25
  end
30
26
  end
@@ -91,7 +91,7 @@ module Origen
91
91
  def default_reg_metadata
92
92
  Origen::Registers.reg_metadata[:global] ||= {}
93
93
  if block_given?
94
- collector = Collector.new
94
+ collector = Origen::Utility::Collector.new
95
95
  yield collector
96
96
  Origen::Registers.reg_metadata[:global].merge!(collector.store)
97
97
  end
@@ -107,7 +107,7 @@ module Origen
107
107
  def default_bit_metadata
108
108
  Origen::Registers.bit_metadata[:global] ||= {}
109
109
  if block_given?
110
- collector = Collector.new
110
+ collector = Origen::Utility::Collector.new
111
111
  yield collector
112
112
  Origen::Registers.bit_metadata[:global].merge!(collector.store)
113
113
  end
@@ -275,18 +275,6 @@ module Origen
275
275
  end
276
276
  end
277
277
 
278
- class Collector
279
- attr_reader :store
280
-
281
- def initialize
282
- @store = {}
283
- end
284
-
285
- def method_missing(method, *args, &_block)
286
- @store[method.to_s.sub('=', '').to_sym] = args.first
287
- end
288
- end
289
-
290
278
  # Returns true if the given object is one of the recognized Origen
291
279
  # bit containers (bit collection, reg or container).
292
280
  def contains_bits?(obj)
@@ -434,7 +422,7 @@ module Origen
434
422
  def default_reg_metadata
435
423
  Origen::Registers.reg_metadata[self.class] ||= {}
436
424
  if block_given?
437
- collector = Collector.new
425
+ collector = Origen::Utility::Collector.new
438
426
  yield collector
439
427
  Origen::Registers.reg_metadata[self.class].merge!(collector.store)
440
428
  end
@@ -445,7 +433,7 @@ module Origen
445
433
  def default_bit_metadata
446
434
  Origen::Registers.bit_metadata[self.class] ||= {}
447
435
  if block_given?
448
- collector = Collector.new
436
+ collector = Origen::Utility::Collector.new
449
437
  yield collector
450
438
  Origen::Registers.bit_metadata[self.class].merge!(collector.store)
451
439
  end
@@ -6,10 +6,16 @@ module Origen
6
6
  # @api private
7
7
  def init_sub_blocks(*args)
8
8
  options = args.find { |a| a.is_a?(Hash) }
9
+ @custom_attrs = (options ? options.dup : {}).with_indifferent_access
10
+ # Delete these keys which are either meta data added by Origen or are already covered by
11
+ # dedicated methods
12
+ %w(parent name base_address reg_base_address base).each do |key|
13
+ @custom_attrs.delete(key)
14
+ end
9
15
  if options
10
16
  # Using reg_base_address for storage to avoid class with the original Origen base
11
17
  # address API, but will accept any of these
12
- @reg_base_address = options.delete(:reg_base_address) || options.delete(:reg_base_address) ||
18
+ @reg_base_address = options.delete(:reg_base_address) ||
13
19
  options.delete(:base_address) || options.delete(:base) || 0
14
20
  if options[:_instance]
15
21
  if @reg_base_address.is_a?(Array)
@@ -30,6 +36,20 @@ module Origen
30
36
  end
31
37
  end
32
38
 
39
+ # Returns the default
40
+ def self.lazy?
41
+ @lazy || false
42
+ end
43
+
44
+ def self.lazy=(value)
45
+ @lazy = value
46
+ end
47
+
48
+ # Returns a hash containing all options that were passed to the sub_block definition
49
+ def custom_attrs
50
+ @custom_attrs
51
+ end
52
+
33
53
  module Domains
34
54
  def domain(name, options = {})
35
55
  domains[name] = Origen::Registers::Domain.new(name, options)
@@ -282,7 +302,12 @@ module Origen
282
302
  define_singleton_method name do
283
303
  get_sub_block(name)
284
304
  end
285
- block
305
+ if options.key?(:lazy)
306
+ lazy = options[:lazy]
307
+ else
308
+ lazy = Origen::SubBlocks.lazy?
309
+ end
310
+ lazy ? block : block.materialize
286
311
  end
287
312
  end
288
313
 
@@ -348,12 +373,13 @@ module Origen
348
373
  end
349
374
 
350
375
  def ==(obj)
351
- materialize == obj
352
- end
353
-
354
- def !=(obj)
355
- materialize != obj
376
+ if obj.is_a?(Placeholder)
377
+ materialize == obj.materialize
378
+ else
379
+ materialize == obj
380
+ end
356
381
  end
382
+ alias_method :equal?, :==
357
383
 
358
384
  def freeze
359
385
  materialize.freeze
@@ -7,6 +7,7 @@ module Origen
7
7
  autoload :InputCapture, 'origen/utility/input_capture'
8
8
  autoload :BlockArgs, 'origen/utility/block_args'
9
9
  autoload :FileDiff, 'origen/utility/file_diff.rb'
10
+ autoload :Collector, 'origen/utility/collector.rb'
10
11
 
11
12
  # Creates a hex-like representation of a register read value, where bits within
12
13
  # a nibble have different flags set the nibble will be expanded to bits
@@ -0,0 +1,15 @@
1
+ module Origen
2
+ module Utility
3
+ class Collector
4
+ attr_reader :store
5
+
6
+ def initialize
7
+ @store = {}
8
+ end
9
+
10
+ def method_missing(method, *args, &_block)
11
+ @store[method.to_s.sub('=', '').to_sym] = args.first
12
+ end
13
+ end
14
+ end
15
+ end
@@ -67,7 +67,7 @@ module Origen
67
67
  model.add_ground_pin :gnd3
68
68
  model.add_ground_pin_group :gnd, :gnd1, :gnd2, :gnd3
69
69
 
70
- model.sub_block :block1, file: 'origen/export1/block1.rb'
70
+ model.sub_block :block1, file: 'origen/export1/block1.rb', lazy: true
71
71
 
72
72
  end
73
73
  end
@@ -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', base_address: 0x40000000
7
+ model.sub_block :x, file: 'origen/export1/block1/x.rb', lazy: true, base_address: 0x40000000
8
8
 
9
9
  end
10
10
  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.30.0
4
+ version: 0.31.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-01-24 00:00:00.000000000 Z
11
+ date: 2018-02-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -454,6 +454,8 @@ files:
454
454
  - lib/origen/commands/version.rb
455
455
  - lib/origen/commands/web.rb
456
456
  - lib/origen/commands_global.rb
457
+ - lib/origen/componentable.rb
458
+ - lib/origen/components.rb
457
459
  - lib/origen/controller.rb
458
460
  - lib/origen/core_ext.rb
459
461
  - lib/origen/core_ext/array.rb
@@ -577,6 +579,7 @@ files:
577
579
  - lib/origen/users/user.rb
578
580
  - lib/origen/utility.rb
579
581
  - lib/origen/utility/block_args.rb
582
+ - lib/origen/utility/collector.rb
580
583
  - lib/origen/utility/csv_data.rb
581
584
  - lib/origen/utility/diff.rb
582
585
  - lib/origen/utility/file_diff.rb