lib/origen/sub_blocks.rb in origen-0.41.2 vs lib/origen/sub_blocks.rb in origen-0.42.0

- old
+ new

@@ -15,11 +15,11 @@ if options # Using reg_base_address for storage to avoid class with the original Origen base # address API, but will accept any of these @reg_base_address = options.delete(:reg_base_address) || options.delete(:base_address) || options.delete(:base) || 0 - if options[:_instance] + if options[:_instance] # to be deprecated as part of multi-instance removal below if @reg_base_address.is_a?(Array) @reg_base_address = @reg_base_address[options[:_instance]] elsif options[:base_address_step] @reg_base_address = @reg_base_address + (options[:_instance] * options[:base_address_step]) end @@ -271,46 +271,99 @@ tests.empty? ? false : true end def sub_block(name, options = {}) if i = options.delete(:instances) - a = [] - options[:_instance] = i - i.times do |j| - o = options.dup - o[:_instance] = j - a << sub_block("#{name}#{j}", o) + # permit creating multiple instances of a particular sub_block class + # can pass array for base_address, which will be processed above + Origen.deprecate 'instances: option to sub_block is deprecated, use sub_block_groups instead' + group_name = name =~ /s$/ ? name : "#{name}s" # take care if name already with 's' is passed + unless respond_to?(group_name) + sub_block_groups group_name do + i.times do |j| + o = options.dup + o[:_instance] = j + sub_block("#{name}#{j}", o) + end + end end - define_singleton_method "#{name}s" do - a - end - a else block = Placeholder.new(self, name, options) - if sub_blocks[name] - # Allow additional attributes to be added to an existing sub-block if it hasn't - # been instantiated yet. This is not supported yet for instantiated sub-blocks since - # there are probably a lot more corner-cases to consider, and hopefully no one will - # really need this anyway. - if sub_blocks[name].is_a?(Placeholder) - sub_blocks[name].add_attributes(options) - else - fail "You have already defined a sub-block named #{name} within class #{self.class}" + # Allow additional attributes to be added to an existing sub-block if it hasn't + # been instantiated yet. This is not supported yet for instantiated sub-blocks since + # there are probably a lot more corner-cases to consider, and hopefully no one will + # really need this anyway. + if sub_blocks[name] && !sub_blocks[name].is_a?(Placeholder) + fail "You have already defined a sub-block named #{name} within class #{self.class}" + end + unless respond_to?(name) + define_singleton_method name do + get_sub_block(name) end + end + if sub_blocks[name] && sub_blocks[name].is_a?(Placeholder) + sub_blocks[name].add_attributes(options) else sub_blocks[name] = block end - define_singleton_method name do - get_sub_block(name) + unless @current_group.nil? # a group is currently open, store sub_block id only + @current_group << name end if options.key?(:lazy) lazy = options[:lazy] else lazy = Origen::SubBlocks.lazy? end lazy ? block : block.materialize end end + + # Create a group of associated sub_blocks under a group name + # permits each sub_block to be of a different class + # e.g. + # sub_block_group :my_ip_group do + # sub_block :ip0, class_name: 'IP0', base_address: 0x000000 + # sub_block :ip1, class_name: 'IP1', base_address: 0x000200 + # sub_block :ip2, class_name: 'IP2', base_address: 0x000400 + # sub_block :ip3, class_name: 'IP3', base_address: 0x000600 + # end + # + # creates an array referenced by method called 'my_ip_group' + # which contains the sub_blocks 'ip0', 'ip1', 'ip2', 'ip3'. + # + # Can also indicate a custom class container to hold these. + # This custom class container MUST support a '<<' method in + # order to add new sub_blocks to the container instance. + # + # e.g. + # sub_block_group :my_ip_group, class_name: 'MYGRP' do + # sub_block :ip0, class_name: 'IP0', base_address: 0x000000 + # sub_block :ip1, class_name: 'IP1', base_address: 0x000200 + # sub_block :ip2, class_name: 'IP2', base_address: 0x000400 + # sub_block :ip3, class_name: 'IP3', base_address: 0x000600 + # end + # + # + def sub_block_group(id, options = {}) + @current_group = [] # open group + yield # any sub_block calls within this block will have their ID added to @current_group + my_group = @current_group.dup + define_singleton_method "#{id}" do + if options[:class_name] + b = Object.const_get(options[:class_name]).new + else + b = [] + end + my_group.each do |group_id| + b << send(group_id) + end + b # return array inside new singleton method + end + @current_group = nil # close group + end + alias_method :sub_block_groups, :sub_block_group + alias_method :sub_blocks_groups, :sub_block_group + alias_method :sub_blocks_group, :sub_block_group def namespace self.class.to_s.sub(/::[^:]*$/, '') end