class Origen::Registers::Reg
The register class can be used to represent not only h/ware resgisters, but really any entity which has an address and data component, such as a specific RAM location.
Any registers instantiated through Origen::Registers#add_reg
are instances of this class.
All methods in BitCollection
can also be called on a Reg
object.
Constants
- REG_LEVEL_ATTRIBUTES
These attributes can be defined on a register at definition time and will get applied to all of its contained bits unless a specific bit has its own definition of the same attribute
Attributes
The base address of the register, this will be set dynamically by Origen
based on the parent’s base address
Returns a full path to the file in which the register was defined
Any feature associated with the register
If the given register’s reset data is backed by memory, the memory address can be recorded in this attribute
Returns any application-specific meta-data attatched to the given register
Returns any application-specific meta-data attatched to the given register
Returns any application-specific meta-data attatched to the given register
The register name
Returns an integer representing the number of bits in the register
Public Class Methods
Returns a dummy register object that can be used on the fly, this can sometimes be useful to configure an intricate read operation.
Example¶ ↑
# Read bit 5 of RAM address 0xFFFF1280 dummy = Reg.dummy # Create a dummy reg to configure the read operation dummy.address = 0xFFFF1280 # Set the address dummy.bit(5).read!(1) # Read bit 5 expecting a 1
# File lib/origen/registers/reg.rb, line 569 def self.dummy(size = 16) Reg.new(self, 0, size, :dummy, init_as_writable: true) end
Public Instance Methods
Returns the BITWISE AND of reg with another reg or a number, the state of both registers remains unchanged
Example¶ ↑
reg(:data).write(0x5555) reg(:data2).write(0xFFFF) reg(:data) & 0xFF00 # => 0x5500 reg(:data) & reg(:data2) # => 0x5555
# File lib/origen/registers/reg.rb, line 1373 def &(val) data & Reg.clean_value(val) end
Returns the PRODUCT of reg with another reg or a number, the state of both registers remains unchanged
# File lib/origen/registers/reg.rb, line 1403 def *(val) data * Reg.clean_value(val) end
Returns the SUM of reg with another reg or a number, the state of both registers remains unchanged
# File lib/origen/registers/reg.rb, line 1385 def +(val) data + Reg.clean_value(val) end
Returns the SUBTRACTION of reg with another reg or a number, the state of both registers remains unchanged
# File lib/origen/registers/reg.rb, line 1391 def -(val) data - Reg.clean_value(val) end
Returns the DIVISION of reg with another reg or a number, the state of both registers remains unchanged
# File lib/origen/registers/reg.rb, line 1397 def /(val) data / Reg.clean_value(val) end
# File lib/origen/registers/reg.rb, line 1021 def add_bus_scramble(id, array_of_hashes = []) array_of_hashes.each do |options| bind(id, options.delete(:bind)) if options[:bind] position = options[:pos] || 0 num_bits = options[:bits] || 1 size = options[:bits] options[:data] = options[:data] if options[:data] options[:res] = options[:reset] if options[:reset] default_data = 0 size.times do |n| default_data |= @bits[position + n].data << n end options = { data: default_data, # If undefined preserve any data/reset value that has res: default_data # already been applied at reg level }.merge(options) @lookup[id] = [] if @lookup[id].nil? @lookup[id] = @lookup[id].push(pos: position, bits: size) size.times do |n| bit_options = options.dup bit_options[:data] = options[:data][n] bit_options[:res] = options[:res][n] @bits.delete_at(position + n) @bits.insert(position + n, Bit.new(self, position + n, bit_options)) end self # rubocop:disable Lint/Void end end
Returns the register address added to its current base_address
value (if any).
@param [Hash] options @option options [Boolean] :relative (false) Return the address without adding the base address (if present)
# File lib/origen/registers/reg.rb, line 859 def address(options = {}) options = { relative: false }.merge(options) address = @address domain_option = options[:domains] || options[:domain] @domain_option ||= domain_option unless frozen? # Blow the cache when the domain option changes @base_address_applied = nil unless @domain_option == domain_option unless @base_address_applied # Give highest priority to the original API which allowed the object # doing register read/write to define a base_address method if (writer && writer.methods.include?(:base_address) && writer.method(:base_address).arity != 0) || (reader && reader.methods.include?(:base_address) && reader.method(:base_address).arity != 0) # This currently assumes that the base address is always the same # for reading and writing if writer && writer.respond_to?(:base_address) && writer.method(:base_address).arity != 0 self.base_address = writer.base_address(self) elsif reader && reader.respond_to?(:base_address) && reader.method(:base_address).arity != 0 self.base_address = reader.base_address(self) end else o = owner.is_a?(Container) ? owner.owner : owner d = domain_option || domains if o && o.reg_base_address(domain: d) self.base_address = o.reg_base_address(domain: d) end end @base_address_applied = true end unless options[:relative] address += base_address if base_address end if options[:address_type] Origen.deprecate 'Specifying the address_type of a register address will be removed from Origen 3' case options[:address_type] when :byte address = address * 2 when :word address = address when :longword address = address / 2 else fail 'Unknown address type requested!' end end address end
# File lib/origen/registers/reg.rb, line 126 def bind(bitname, live_parameter) unless live_parameter.respond_to?(:is_a_live_parameter?) && live_parameter.is_a_live_parameter? fail 'Only live updating parameters should be bound, make sure you have not missed .live in the path to the parameter!' end @parameter_bound_bits ||= {} @parameter_bound_bits[bitname] = live_parameter end
Returns the bit object(s) responding to the given name, wrapped in a BitCollection
. This method also accepts multiple name possibilities, if neither bit exists in the register it will raise an error, otherwise it will return the first match. If no args passed in, it will return a BitCollection
containing all bits. If a number is passed in then the bits from those positions are returned.
Example¶ ↑
add_reg :control, 0x55, :status => {:pos => 1, :bits => 2}, :fail => {:pos => 0} reg(:control).bit(:fail) # => Returns a BitCollection containing the fail bit reg(:control).bits(:status) # => Returns a BifCollection containing the status bits reg(:control).bit(:bist_fail, :fail) # => Returns a BitCollection containing the fail bit reg(:control).bit(0) # => Returns a BitCollection containing the fail bit reg(:control).bit(1) # => Returns a BitCollection containing status bit reg(:control).bit(1,2) # => Returns a BitCollection containing both status bits
# File lib/origen/registers/reg.rb, line 1091 def bit(*args) # allow msb0 bit numbering if requested wbo = :lsb0 if args.last.is_a?(Hash) wbo = args.last.delete(:with_bit_order) || :lsb0 args.pop if args.last.size == 0 end multi_bit_names = false # return get_bits_with_constraint(nil,:default) if args.size == 0 constraint = extract_feature_params(args) if constraint.nil? constraint = :default end collection = BitCollection.new(self, :unknown, [], with_bit_order: wbo) if args.size == 0 collection.add_name(name) @bits.each do |bit| collection << get_bits_with_constraint(bit.position, constraint) end else args.flatten! args.sort! args.reverse! if wbo == :msb0 args.each do |arg_item| if arg_item.is_a?(Integer) b = get_bits_with_constraint(arg_item, constraint, with_bit_order: wbo) collection << b if b elsif arg_item.is_a?(Range) expand_range(arg_item, wbo) do |bit_number| collection << get_bits_with_constraint(bit_number, constraint, with_bit_order: wbo) end else multi_bit_names = args.size > 1 # Reaches here if bit name is specified if @lookup.include?(arg_item) split_bits = false @lookup.each { |_k, v| split_bits = true if v.is_a? Array } coll = get_lookup_feature_bits(arg_item, constraint, split_bits) if coll coll.each do |b| collection.add_name(arg_item) collection << b end end end end end end if collection.size == 0 # Originally Origen returned nil when asking for a bit via an index which does not # exist, e.g. reg[1000] => nil # The args numeric clause here is to maintain that behavior if Origen.config.strict_errors && !args.all? { |arg| arg.is_a?(Numeric) } puts "Register #{@name} does not have a bits(s) named :#{args.join(', :')} or it might not be enabled." puts 'This could also be a typo, these are the valid bit names:' puts @lookup.keys fail 'Missing bits error!' end nil else if multi_bit_names collection.sort_by!(&:position) wbo == :msb0 ? collection.with_msb0 : collection.with_lsb0 end wbo == :msb0 ? collection.with_msb0 : collection.with_lsb0 end end
Returns the bit order attribute of the register (either :msb0 or :lsb0). If not explicitly defined on this register it will be inherited from the parent and will default to :lsb0 at the top-level
# File lib/origen/registers/reg.rb, line 114 def bit_order @bit_order ||= parent.respond_to?(:bit_order) ? parent.bit_order : :lsb0 end
# File lib/origen/registers/reg.rb, line 389 def bit_value_descriptions(bitname, options = {}) options = { format: :binary }.merge(options) base = case options[:format] when :bin, :binary 2 when :hex, :hexadecimal 16 when :dec, :decimal 10 else fail "Unknown integer format: #{options[:format]}" end desc = {} description(bitname).each do |line| if line =~ /^\s*(\d+)\s+\|\s+(.+)/ desc[Regexp.last_match[1].to_i(base)] = Regexp.last_match[2] end end desc end
Shows the bits that have changed from the reset value of the register/bits. Currently logs it to the console window using Origen
logger.
# File lib/origen/registers/reg.rb, line 582 def changed_bits(options = {}) temp = named_bits.select { |name, bits| bits.data != bits.reset_val } temp.each do |r| Origen.log.info "\t\t#{self.name}.#{r[0]} : #{r[1].value.to_hex}".bold.blue end Origen.log.info ' ' end
# File lib/origen/registers/reg.rb, line 505 def contains_bits? true end
Copies data and overlays from one reg object to another, it does not copy read or store flags
# File lib/origen/registers/reg.rb, line 1357 def copy(reg) size.times do |i| source_bit = reg.bit[i] @bits[i].overlay(source_bit.overlay_str) if source_bit.has_overlay? @bits[i].write(source_bit.data) end self end
Copies data from one reg object to another
Example¶ ↑
reg(:data_copy).data # => 0 reg(:data).write(0x1234) reg(:data_copy).copy_data_from(reg(:data)) reg(:data_copy).data # => 0x1234
# File lib/origen/registers/reg.rb, line 1348 def copy_data_from(reg) size.times do |i| @bits[i].write(reg.bit[i].data) end self end
Copy overlays from one reg object to another
Example¶ ↑
reg(:data_copy).has_overlay? # => false reg(:data).overlay("data_val") reg(:data_copy).copy_overlays_from(reg(:data)) reg(:data_copy).has_overlay? # => true
# File lib/origen/registers/reg.rb, line 1328 def copy_overlays_from(reg, options = {}) size.times do |i| source_bit = reg.bit[i] if source_bit.has_overlay? ov = source_bit.overlay_str # If an id has been supplied make sure any trailing ID in the source is # changed to supplied identifier ov.gsub!(/_\d$/, "_#{options[:update_id]}") if options[:update_id] @bits[i].overlay(ov) end end self end
Returns any application specific metadata that has been inherited by the given register. This does not account for any overridding that may have been applied to this register specifically however, use the meta method to get that.
# File lib/origen/registers/reg.rb, line 383 def default_reg_metadata Origen::Registers.default_reg_metadata.merge( Origen::Registers.reg_metadata[owner.class] || {} ) end
Delete the bits in the collection from the register
# File lib/origen/registers/reg.rb, line 1052 def delete_bit(collection) [collection.name].flatten.each do |name| @lookup.delete(name) end collection.each do |bit| @bits.delete_at(bit.position) # Remove the bit @bits.insert(bit.position, Bit.new(self, bit.position, writable: @init_as_writable)) end self end
Returns the description of this register if any, if none then an empty array will be returned
Note Adding a description field will override any comment-driven documentation of a register (ie markdown style comments)
# File lib/origen/registers/reg.rb, line 442 def description(bitname = :_reg, options = {}) bitname, options = :_reg, bitname if bitname.is_a?(Hash) options = { include_name: true, include_bit_values: true }.merge(options) if @description_from_api[bitname] desc = @description_from_api[bitname] else parse_descriptions unless description_lookup[define_file] begin desc = description_lookup[define_file][name][bitname] || [] rescue desc = [] end end desc = desc.reject do |line| if bitname != :_reg unless options[:include_bit_values] !!(line =~ /^\s*(\d+)\s+\|\s+(.+)/) end else false end end if desc.first unless options[:include_name] desc[0] = desc.first.sub(/\s*\*\*\s*#{escape_special_char(full_name(bitname))}\s*\*\*\s*-?\s*/, '') end end desc.shift while desc.first && desc.first.strip.empty? desc.pop while desc.last && desc.last.strip.empty? desc end
Returns a hash containing all register descriptions that have been parsed so far.
@api private
# File lib/origen/registers/reg.rb, line 375 def description_lookup @@description_lookup ||= {} end
Returns an array of unoccupied bit positions
Example¶ ↑
reg :fstat, @base + 0x0000, :size => 8 do bit 7, :ccif bit 6, :rdcolerr bit 5, :accerr bit 4, :pviol bit 0, :mgstat0 end regs(:fstat).empty_bits => [1, 2, 3]
Example¶ ↑
reg :aguahb2, @base + 0x2A, :size => 8 do bit 5..2, :m0b_hbstrb, :reset => 0x0 bit 1..0, :m0b_htrans, :reset => 0x2 end regs(:aguahb2).empty_bits => [6, 7]
# File lib/origen/registers/reg.rb, line 795 def empty_bits(_options = {}) array_span = (0..(size - 1)).to_a empty_bits = array_span - used_bits empty_bits end
Returns true if any named_bits
exist, false if used_bits
is an empty array
# File lib/origen/registers/reg.rb, line 802 def empty_bits?(_options = {}) empty_bits.size > 0 end
Query the owner heirarchy to see if this register is enabled
# File lib/origen/registers/reg.rb, line 1457 def enabled? if feature value = false current_owner = self if feature.class == Array feature.each do |f| current_owner = self loop do if current_owner.respond_to?(:owner) current_owner = current_owner.owner if current_owner.respond_to?(:has_feature?) if current_owner.has_feature?(f) value = true break end end else # if current owner does not have a owner value = false break end end # loop end unless value if Origen.top_level && \ Origen.top_level.respond_to?(:has_feature?) && \ Origen.top_level.has_feature?(f) value = true unless value break end end end unless value break # break if feature not found and return false end end # iterated through all features in array value else # if feature.class != Array loop do if current_owner.respond_to?(:owner) current_owner = current_owner.owner if current_owner.respond_to?(:has_feature?) if current_owner.has_feature?(feature) value = true break end end else # if current owner does not have a owner value = false break end end # loop end unless value if Origen.top_level && \ Origen.top_level.respond_to?(:has_feature?) && \ Origen.top_level.has_feature?(feature) value = true end end value end else true end end
Returns true if the register is constrained by the given/any feature
# File lib/origen/registers/reg.rb, line 1438 def enabled_by_feature?(name = nil) if !name !!feature else if feature.class == Array feature.each do |f| if f == name return true end end false else feature == name end end end
Escapes brackets and parenthesis. Helper for description method.
# File lib/origen/registers/reg.rb, line 433 def escape_special_char(str) str.gsub('[', '\[').gsub(']', '\]').gsub('(', '\(').gsub(')', '\)') if str end
@api private
# File lib/origen/registers/reg.rb, line 1065 def expand_range(range, wbo = :lsb0) if range.first > range.last range = Range.new(range.last, range.first) end range = range.to_a range.reverse! if wbo == :msb0 range.each do |i| yield i end end
# File lib/origen/registers/reg.rb, line 1276 def extract_feature_params(args) index = args.find_index { |arg| arg.class == Hash } if index params = args.delete_at(index) else params = nil end if params params[:enabled_features] || params[:enabled_feature] end end
# File lib/origen/registers/reg.rb, line 1428 def extract_meta_data(method, *args) method = method.to_s.sub('?', '') if method =~ /=/ instance_variable_set("@#{method.sub('=', '')}", args.first) else instance_variable_get("@#{method}") || meta[method.to_sym] end end
# File lib/origen/registers/reg.rb, line 118 def freeze bits.each(&:freeze) # Call any methods which cache results to generate the instance variables # before they are frozen address super end
Returns the full name of the register when this has been specified in the register description like this:
# ** This is the Register Full Name ** # This register blah blah
This method will also be called by bit collections to look up the name when defined in a similar manner in the bit description.
If no name has been specified this will return nil.
# File lib/origen/registers/reg.rb, line 422 def full_name(bitname = :_reg, _options = {}) bitname, options = :_reg, bitname if bitname.is_a?(Hash) desc = description(bitname).first # Capture something like this: # ** This is the full name ** - This bit blah blah if desc && desc =~ /\s*\*\*\s*([^\*.]*)\s*\*\*/ Regexp.last_match[1].strip end end
# File lib/origen/registers/reg.rb, line 1164 def get_bits_with_constraint(number, params, options = {}) options = { with_bit_order: :lsb0 }.merge(options) # remap to lsb0 number to grab correct bit if options[:with_bit_order] == :msb0 number = size - number - 1 end return nil unless @bits[number] if (params == :default || !params) && @bits[number].enabled? @bits[number] elsif params == :none && !@bits[number].has_feature_constraint? @bits[number] elsif params == :all @bits[number] elsif params.class == Array params.each do |param| unless @bits[number].enabled_by_feature?(param) return nil end @bits[number] end elsif @bits[number].enabled_by_feature?(params) @bits[number] else Bit.new(self, number, writable: false) end end
# File lib/origen/registers/reg.rb, line 1194 def get_lookup_feature_bits(bit_name, params, split_group_reg) ## if split_group_reg == false # if this register has single bits and continuous ranges if @lookup.include?(bit_name) collection = BitCollection.new(self, bit_name) (@lookup[bit_name][:bits]).times do |i| collection << @bits[@lookup[bit_name][:pos] + i] end if !params || params == :default if collection.enabled? return collection end elsif params == :none unless collection.has_feature_constraint? return collection end elsif params == :all return collection elsif params.class == Array if params.all? { |param| collection.enabled_by_feature?(param) } return collection end else if collection.enabled_by_feature?(params) return collection end end BitCollection.dummy(self, bit_name, size: collection.size, pos: @lookup[bit_name][:pos]) else [] end elsif split_group_reg == true # if this registers has split bits in its range if @lookup.is_a?(Hash) # && @lookup.include?(bit_name) collection = false @lookup.each do |k, v| # k is the bitname, v is the hash of bit data if k == bit_name collection ||= BitCollection.new(self, k) if v.is_a?(Array) v.reverse_each do |pb| # loop each piece of bit group data (pb[:bits]).times do |i| collection << @bits[pb[:pos] + i] end end else (v[:bits]).times do |i| collection << @bits[v[:pos] + i] end end end end if !params || params == :default if collection.enabled? return collection end elsif params == :none unless collection.has_feature_constraint? return collection end elsif params == :all return collection elsif params.class == Array if params.all? { |param| collection.enabled_by_feature?(param) } return collection end else if collection.enabled_by_feature?(params) return collection end end if @lookup.is_a?(Hash) && @lookup[bit_name].is_a?(Array) BitCollection.dummy(self, bit_name, size: collection.size, pos: @lookup[bit_name][0][:pos]) else BitCollection.dummy(self, bit_name, size: collection.size, pos: @lookup[bit_name[:pos]]) end else [] end end end
Returns true if the register contains a bit(s) matching the given name
Example¶ ↑
add_reg :control, 0x55, :status => {:pos => 1} reg(:control).has_bit?(:result) # => false reg(:control).has_bit?(:status) # => true
# File lib/origen/registers/reg.rb, line 974 def has_bit?(name) @lookup.include?(name) end
Returns true if any of the bits within this register has feature associated with it.
# File lib/origen/registers/reg.rb, line 1524 def has_bits_enabled_by_feature?(name = nil) if !name bits.any?(&:has_feature_constraint?) else bits.any? { |bit| bit.enabled_by_feature?(name) } end end
# File lib/origen/registers/reg.rb, line 135 def has_parameter_bound_bits? @parameter_bound_bits && !@parameter_bound_bits.empty? end
# File lib/origen/registers/reg.rb, line 151 def inspect(options = {}) wbo = options[:with_bit_order] || :lsb0 domsb0 = wbo == :msb0 dolsb0 = !domsb0 if wbo != bit_order Origen.log.warn "Register displayed with #{wbo} numbering, but defined with #{bit_order} numbering" Origen.log.warn 'Access (and display) this register with explicit numbering like this:' Origen.log.warn '' Origen.log.warn " reg(:#{name}).with_msb0 # bit numbering scheme is msb0" Origen.log.warn " reg(:#{name}).with_lsb0 # bit numbering scheme is lsb0 (default)" Origen.log.warn " reg(:#{name}) # bit numbering scheme is lsb0 (default)" end # This fancy_output option is passed in via option hash # Even better, the output could auto-detect 7-bit vs 8-bit terminal output and adjust the parameter, but that's for another day fancy_output = options[:fancy_output].nil? ? true : options[:fancy_output] if fancy_output horiz_double_line = '═' horiz_double_tee_down = '╤' horiz_double_tee_up = '╧' corner_double_up_left = '╒' corner_double_up_right = '╕' horiz_single_line = '─' horiz_single_tee_down = '┬' horiz_single_tee_up = '┴' horiz_single_cross = '┼' horiz_double_cross = '╪' corner_single_down_left = '└' corner_single_down_right = '┘' vert_single_line = '│' vert_single_tee_left = '┤' vert_single_tee_right = '├' else horiz_double_line = '=' horiz_double_tee_down = '=' horiz_double_tee_up = '=' corner_double_up_left = '.' corner_double_up_right = '.' horiz_single_line = '-' horiz_single_tee_down = '-' horiz_single_tee_up = '-' horiz_single_cross = '+' horiz_double_cross = '=' corner_single_down_left = '`' corner_single_down_right = '\'' vert_single_line = '|' vert_single_tee_left = '<' vert_single_tee_right = '>' end bit_width = 13 desc = ["\n0x%X - :#{name}" % address] r = size % 8 if r == 0 # || (size > 8 && domsb0) desc << (' ' + corner_double_up_left + ((horiz_double_line * bit_width + horiz_double_tee_down) * 8)).chop + corner_double_up_right else desc << (' ' + (' ' * (bit_width + 1) * (8 - r)) + corner_double_up_left + ((horiz_double_line * bit_width + horiz_double_tee_down) * r)).chop + corner_double_up_right end # "<#{self.class}: #{self.name}>" num_bytes = (size / 8.0).ceil num_bytes.times do |byte_index| # Need to add support for little endian regs here? byte_number = num_bytes - byte_index max_bit = (byte_number * 8) - 1 min_bit = max_bit - 8 + 1 # BIT INDEX ROW line = ' ' line_complete = false 8.times do |i| bit_num = (byte_number * 8) - i - 1 if bit_num > size - 1 line << ' ' + ''.center(bit_width) unless line_complete else if dolsb0 line << vert_single_line + "#{bit_num}".center(bit_width) else line << vert_single_line + "#{size - bit_num - 1}".center(bit_width) end end end line += vert_single_line unless line_complete desc << line # BIT NAME ROW line = ' ' first_done = false line_complete = false named_bits include_spacers: true do |name, bit, bitcounter| if _bit_in_range?(bit, max_bit, min_bit) if max_bit > (size - 1) && !first_done (max_bit - (size - 1)).times do line << ' ' * (bit_width + 1) end end if bit.size > 1 if name if bitcounter.nil? bit_name = "#{name}[#{_max_bit_in_range(bit, max_bit, min_bit, options)}:#{_min_bit_in_range(bit, max_bit, min_bit, options)}]" bit_span = _num_bits_in_range(bit, max_bit, min_bit) else upper = _max_bit_in_range(bit, max_bit, min_bit, options) + bitcounter - bit.size lower = _min_bit_in_range(bit, max_bit, min_bit, options) + bitcounter - bit.size if dolsb0 bit_name = "#{name}[#{upper}:#{lower}]" else bit_name = "#{name}[#{upper}:#{lower}]" end bit_span = upper - lower + 1 end width = (bit_width * bit_span) + bit_span - 1 if bit_name.length > width line << vert_single_line + "#{bit_name[0..width - 2]}*" else line << vert_single_line + bit_name.center(width) end else bit.shift_out_left do |bit| if _index_in_range?(bit.position, max_bit, min_bit) line << vert_single_line + ''.center(bit_width) end end end else if name bit_name = "#{name}" if bit_name.length > bit_width txt = "#{bit_name[0..bit_width - 2]}*" else txt = bit_name end else txt = '' end line << vert_single_line + txt.center(bit_width) end end first_done = true end line += vert_single_line desc << line # BIT STATE ROW line = ' ' first_done = false named_bits include_spacers: true do |name, bit, _bitcounter| if _bit_in_range?(bit, max_bit, min_bit) if max_bit > (size - 1) && !first_done (max_bit - (size - 1)).times do line << ' ' * (bit_width + 1) end end if bit.size > 1 if name if bit.has_known_value? value = '0x%X' % bit.val[_max_bit_in_range(bit, max_bit, min_bit).._min_bit_in_range(bit, max_bit, min_bit)] else if bit.reset_val == :undefined value = 'X' else value = 'M' end end value += _state_desc(bit) bit_span = _num_bits_in_range(bit, max_bit, min_bit) width = bit_width * bit_span line << vert_single_line + value.center(width + bit_span - 1) else bit.shift_out_left do |bit| if _index_in_range?(bit.position, max_bit, min_bit) line << vert_single_line + ''.center(bit_width) end end end else if name if bit.has_known_value? val = bit.val else if bit.reset_val == :undefined val = 'X' else val = 'M' end end value = "#{val}" + _state_desc(bit) line << vert_single_line + value.center(bit_width) else line << vert_single_line + ''.center(bit_width) end end end first_done = true end line += vert_single_line desc << line if size >= 8 r = size % 8 if byte_index == 0 && r != 0 desc << (' ' + corner_double_up_left + ((horiz_double_line * bit_width + horiz_double_tee_down) * (8 - r)).chop + horiz_double_cross + (horiz_single_line * (bit_width + 1) * r)).chop + vert_single_tee_left else if byte_index == num_bytes - 1 desc << (' ' + corner_single_down_left + ((horiz_single_line * bit_width + horiz_single_tee_up) * 8)).chop + corner_single_down_right else desc << (' ' + vert_single_tee_right + ((horiz_single_line * bit_width + horiz_single_cross) * 8)).chop + vert_single_tee_left end end else desc << (' ' + (' ' * (bit_width + 1) * (8 - size)) + corner_single_down_left + ((horiz_single_line * bit_width + horiz_single_tee_up) * size)).chop + corner_single_down_right end end desc.join("\n") end
@api private
# File lib/origen/registers/reg.rb, line 836 def lookup_operation_handler(operation) # Could have made the controller be the owner when assigned above, but may have run # into problems with the reg meta data stuff reg_owner = owner.respond_to?(:controller) && owner.controller ? owner.controller : owner if reg_owner.respond_to?(operation) reg_owner elsif reg_owner.respond_to?(:owner) && reg_owner.owner.respond_to?(operation) reg_owner.owner elsif Origen.top_level && Origen.top_level.respond_to?(operation) Origen.top_level end end
@api private
# File lib/origen/registers/reg.rb, line 1415 def meta_data_method?(method) attr_name = method.to_s.gsub(/\??=?/, '').to_sym if default_reg_metadata.key?(attr_name) if method.to_s =~ /\?/ [true, false].include?(default_reg_metadata[attr_name]) else true end else false end end
Returns each named bit collection contained in the register,
# File lib/origen/registers/reg.rb, line 574 def named_bits(options = {}) options = { include_spacers: false }.merge(options) result = [] # Shows the bits that have changed from the reset value of the register/bits. # Currently logs it to the console window using Origen logger. def changed_bits(options = {}) temp = named_bits.select { |name, bits| bits.data != bits.reset_val } temp.each do |r| Origen.log.info "\t\t#{self.name}.#{r[0]} : #{r[1].value.to_hex}".bold.blue end Origen.log.info ' ' end # test if @lookup has any values stored as an array # if so it means there is a split group of bits # process that differently to a single bit or continuous range of bits # which are typically stored in a hash split_bits = false @lookup.each { |_k, v| split_bits = true if v.is_a? Array } if split_bits == false current_pos = size # Sort by position @lookup.sort_by { |_name, details| -details[:pos] }.each do |name, details| pos = details[:bits] + details[:pos] if options[:include_spacers] && (pos != current_pos) collection = BitCollection.dummy(self, nil, size: current_pos - pos, pos: pos) unless collection.size == 0 if block_given? yield nil, collection else result << [nil, collection] end end end collection = BitCollection.new(self, name) details[:bits].times do |i| collection << @bits[details[:pos] + i] end unless collection.size == 0 if block_given? yield name, collection else result << [name, collection] end end current_pos = details[:pos] end if options[:include_spacers] && (current_pos != 0) collection = BitCollection.dummy(self, nil, size: current_pos, pos: 0) unless collection.size == 0 if block_given? yield nil, collection else result << [nil, collection] end end end elsif split_bits == true # if there are split bits, need to convert all register bit values to array elements to allow sorting # if the register has bits split up across it, then store the bits in order of decreasing reg position # but first, stuff all the bits in a simple array, as single bits, or ranges of bits @lookup_splits = [] @lookup.each do |k, v| tempbit = {} bitcounter = {} if v.is_a? Hash # then this is already a single bit or a continuous range so just stuff it into the array tempbit[k] = v @lookup_splits << tempbit.clone elsif v.is_a? Array # if the bitgroup is split, then decompose into single bits and continuous ranges v.each_with_index do |bitdetail, _i| if bitcounter.key?(k) bitcounter[k] = bitcounter[k] + bitdetail[:bits] else bitcounter[k] = bitdetail[:bits] end tempbit[k] = bitdetail @lookup_splits << tempbit.clone end end if v.is_a? Array @lookup_splits.each_with_index do |_e, q| groupname = @lookup_splits[q].to_a[0][0] if groupname == k @lookup_splits[q][groupname][:bitgrouppos] = bitcounter[groupname] if groupname == k bitcounter[groupname] = bitcounter[groupname] - @lookup_splits[q][groupname][:bits] end end end end # Now sort the array in descending order # Does adding the bitgrouppos need to happen after the sort ? @lookup_splits = @lookup_splits.sort do |a, b| b.to_a[0][1][:pos] <=> a.to_a[0][1][:pos] end current_pos = size countbits = {} # if countbits.method == nil @master = {} bitgroup = {} bitinfo = {} info = {} @lookup_splits.each_with_index do |hash, _i| name = hash.to_a[0][0] details = hash.to_a[0][1] bitcounter = hash.to_a[0][1][:bitgrouppos] pos = details[:bits] + details[:pos] if options[:include_spacers] && (pos != current_pos) collection = BitCollection.dummy(self, nil, size: current_pos - pos, pos: pos) unless collection.size == 0 if block_given? yield nil, collection, bitcounter else result << [nil, collection, bitcounter] end end end collection = BitCollection.new(self, name) details[:bits].times do |i| collection << @bits[details[:pos] + i] end unless collection.size == 0 if block_given? yield name, collection, bitcounter else result << [name, collection, bitcounter] end end current_pos = details[:pos] end if options[:include_spacers] && current_pos != 0 collection = BitCollection.dummy(self, nil, size: current_pos, pos: 0) unless collection.size == 0 if block_given? yield nil, collection, bitcounter else result << [nil, collection, bitcounter] end end end end unless block_given? result end end
Returns the relative address of the given register, equivalent to calling reg.address(:relative => true)
# File lib/origen/registers/reg.rb, line 851 def offset address(relative: true) end
Returns true if the register owner matches the given name. A match will be detected if the class names of the register’s owner contains the given name.
Alternatively if the register owner implements a method called reg_owner_alias then the value that this returns instead will also be considered when checking if the given name matches. This method can return an array of names if multiple aliases are required.
Aliases can be useful for de-coupling the commonly used name, e.g. “NVM” from the actual class name.
@example
class C90NVM include Origen::Registers def initialize add_reg :clkdiv, 0x3, 16, :div => {:bits => 8} end end reg = C90NVM.new.reg(:clkdiv) reg.owned_by?(:ram) # => false reg.owned_by?(:nvm) # => true reg.owned_by?(:c90nvm) # => true reg.owned_by?(:c40nvm) # => false reg.owned_by?(:flash) # => false
@example Using an alias
class C90NVM include Origen::Registers def initialize add_reg :clkdiv, 0x3, 16, :div => {:bits => 8} end def reg_owner_alias "flash" end end reg = C90NVM.new.reg(:clkdiv) reg.owned_by?(:ram) # => false reg.owned_by?(:nvm) # => true reg.owned_by?(:c90nvm) # => true reg.owned_by?(:c40nvm) # => false reg.owned_by?(:flash) # => true
# File lib/origen/registers/reg.rb, line 956 def owned_by?(name) !!(owner.class.to_s =~ /#{name}/i) || begin if owner.respond_to?(:reg_owner_alias) [owner.reg_owner_alias].flatten.any? do |al| al.to_s =~ /#{name}/i end else false end end end
@api private
# File lib/origen/registers/reg.rb, line 479 def parse_descriptions desc = [] unless File.exist?(define_file) return desc end File.readlines(define_file).each do |line| if line =~ /^\s*#(.*)/ desc << Regexp.last_match[1].strip # http://rubular.com/r/D8lg2P5kK1 http://rubular.com/r/XP4ydPV8Fd elsif line =~ /^\s*reg\(?\s*[:"'](\w+)["']?\s*,.*\sdo/ || line =~ /^\s*.*add_reg\(?\s*[:"'](\w+)["']?\s*,.*/ @current_reg_name = Regexp.last_match[1].to_sym description_lookup[define_file] ||= {} description_lookup[define_file][@current_reg_name] ||= {} description_lookup[define_file][@current_reg_name][:_reg] = desc.dup desc = [] # http://www.rubular.com/r/7FidbC1JRA elsif @current_reg_name && line =~ /^\s*(add_bit|bit|reg\.bit)s?\(?\s*\d+\.?\.?\d*\s*,\s*:(\w+)/ description_lookup[define_file][@current_reg_name][Regexp.last_match[2].to_sym] = desc.dup desc = [] else desc = [] end end end
Returns the object that will be responsible for reading the given register
# File lib/origen/registers/reg.rb, line 831 def reader @reader ||= lookup_operation_handler(:read_register) end
Returns each named bit collection contained in self
# File lib/origen/registers/reg.rb, line 731 def reverse_named_bits(_options = {}) bits = [] named_bits { |name, bit| bits << [name, bit] } bits.each do |bit| yield bit[0], bit[1] end end
# File lib/origen/registers/reg.rb, line 1311 def to_bit_collection(options = {}) BitCollection.new(self, name, @bits, options) end
# File lib/origen/registers/reg.rb, line 1532 def to_json(*args) JSON.pretty_generate({ name: name, full_name: full_name, address: address, offset: offset, size: size, path: path, reset_value: reset_value, description: description(include_name: false, include_bit_values: false), bits: named_bits.map do |name, bit| { name: name, full_name: bit.full_name, position: bit.position, size: bit.size, reset_value: bit.reset_value, access: bit.access, description: bit.description(include_name: false, include_bit_values: false), bit_values: bit.bit_value_descriptions.map do |val, desc| { value: val, description: desc } end } end }, *args) end
# File lib/origen/registers/reg.rb, line 139 def update_bound_bits @updating_bound_bits = true @parameter_bound_bits.each do |name, val| bits(name).write(val) end @updating_bound_bits = false end
# File lib/origen/registers/reg.rb, line 147 def updating_bound_bits? @updating_bound_bits end
Returns an array of occupied bit positions
Example¶ ↑
reg :fstat, @base + 0x0000, :size => 8 do bit 7, :ccif bit 6, :rdcolerr bit 5, :accerr bit 4, :pviol bit 0, :mgstat0 end regs(:fstat).used_bits => [0, 4, 5, 6, 7]
Example¶ ↑
reg :aguahb2, @base + 0x2A, :size => 8 do bit 5..2, :m0b_hbstrb, :reset => 0x0 bit 1..0, :m0b_htrans, :reset => 0x2 end regs(:aguahb2).used_bits => [0, 1, 2, 3, 4, 5]
# File lib/origen/registers/reg.rb, line 758 def used_bits(_options = {}) used_bits = [] named_bits do |_name, bit| used_bits << bit.position if bit.size == 1 if bit.size > 1 used_bits << ((bit.position)..(bit.position + bit.size - 1)).to_a end end used_bits.flatten! used_bits.sort! used_bits end
Returns true if any named_bits
exist, false if used_bits
is an empty array
# File lib/origen/registers/reg.rb, line 772 def used_bits?(_options = {}) used_bits.size > 0 end
# File lib/origen/registers/reg.rb, line 107 def with_lsb0 self end
# File lib/origen/registers/reg.rb, line 103 def with_msb0 @msb0_delegator end
Returns the object that will be responsible for writing the given register
# File lib/origen/registers/reg.rb, line 826 def writer @writer ||= lookup_operation_handler(:write_register) end
Returns the BITWISE OR of reg with another reg or a number, the state of both registers remains unchanged
# File lib/origen/registers/reg.rb, line 1379 def |(val) data | Reg.clean_value(val) end
Private Instance Methods
Returns true if some portion of the given bits falls within the given range
# File lib/origen/registers/reg.rb, line 1603 def _bit_in_range?(bits, max, min) upper = bits.position + bits.size - 1 lower = bits.position !((lower > max) || (upper < min)) end
# File lib/origen/registers/reg.rb, line 1623 def _bit_rw(bits) if bits.readable? && bits.writable? 'RW' elsif bits.readable? 'RO' elsif bits.writable? 'WO' else 'X' end end
Returns true if the given number is is the given range
# File lib/origen/registers/reg.rb, line 1619 def _index_in_range?(i, max, min) !((i > max) || (i < min)) end
# File lib/origen/registers/reg.rb, line 1583 def _max_bit_in_range(bits, max, _min, options = { with_bit_order: false }) upper = bits.position + bits.size - 1 if options[:with_bit_order] == :msb0 bits.size - ([upper, max].min - bits.position) - 1 else [upper, max].min - bits.position end end
# File lib/origen/registers/reg.rb, line 1592 def _min_bit_in_range(bits, _max, min, options = { with_bit_order: false }) lower = bits.position if options[:with_bit_order] == :msb0 bits.size - ([lower, min].max - lower) - 1 else [lower, min].max - bits.position end end
Returns the number of bits from the given bits that fall within the given range
# File lib/origen/registers/reg.rb, line 1611 def _num_bits_in_range(bits, max, min) upper = bits.position + bits.size - 1 lower = bits.position [upper, max].min - [lower, min].max + 1 end
# File lib/origen/registers/reg.rb, line 1564 def _state_desc(bits) state = [] unless bits.readable? && bits.writable? if bits.readable? state << 'RO' else state << 'WO' end end state << 'Rd' if bits.is_to_be_read? state << 'Str' if bits.is_to_be_stored? state << 'Ov' if bits.has_overlay? if state.empty? '' else "(#{state.join('|')})" end end