<commit_before>require 'json'
module Origen
  module Registers
    # 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.<br>
    # 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.
    class Reg
      include Origen::SubBlocks::Path
      include Origen::SubBlocks::Domains

      # 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
      REG_LEVEL_ATTRIBUTES = {
        _feature:   {},
        _reset:     { aliases: [:res] },
        _memory:    {},
        _path:      { aliases: [:hdl_path] },
        _abs_path:  { aliases: [:absolute_path] },
        _access:    {},
        _bit_order: {}
      }

      # Returns the object that own the register.
      # ==== Example
      #   $soc.reg(:blah).owner   # Returns the $soc object
      attr_reader :owner
      alias_method :parent, :owner
      # The base address of the register, this will be set dynamically
      # by Origen based on the parent's base address
      attr_accessor :base_address
      attr_writer :address # :nodoc:
      # Returns an integer representing the number of bits in the register
      attr_reader :size
      # The register name
      attr_accessor :name
      # Any feature associated with the register
      attr_accessor :feature

      attr_accessor :grows_backwards # :nodoc:
      attr_accessor :lookup # :nodoc:
      # Returns a full path to the file in which the register was defined
      attr_reader :define_file
      # Returns any application-specific meta-data attatched to the given register
      attr_accessor :meta
      alias_method :meta_data, :meta
      alias_method :metadata, :meta
      # If the given register's reset data is backed by memory, the memory address can
      # be recorded in this attribute
      attr_accessor :memory

      # Normally shouldn't be called directly, instantiate through add_reg
      # Upon initialization bits are stored as follows:
      # @bits -
      # An array of bit objects in position order, @bits[5] corresponds
      # to the bit as position r
      # @lookup -
      # A Hash lookup table for quickly accessing bit objects by name
      # @lookup = { :bit_or_bus_name => {:pos => 3, :bits => 4} }
      def initialize(owner, address, size, name, options = {}) # :nodoc:
        @owner = owner
        @address = address
        @size = size
        @bits = []
        @lookup = {}
        @name = name
        @init_as_writable = options.delete(:init_as_writable)
        @define_file = options.delete(:define_file)
        @from_placeholder = options.delete(:from_placeholder) || false
        REG_LEVEL_ATTRIBUTES.each do |attribute, _meta|
          if @from_placeholder
            instance_variable_set("@#{attribute[1..-1]}", options.delete(attribute))
          else
            # If register creation is coming directly from Reg.new, instead of Placeholder,
            #   it may not have attributes with '_' prefix
            instance_variable_set("@#{attribute[1..-1]}", options.delete(attribute[1..-1].to_sym))
          end
        end
        @description_from_api = {}
        description = options.delete(:description)
        if description
          @description_from_api[:_reg] = description.split(/\r?\n/)
        end
        @meta = default_reg_metadata.merge(options.delete(:meta) || {})

        # Initialize with unwritable bits that read back as zero, can override this
        # to make all writable by default by setting the :init_writable option to true
        @size.times do |n|
          @bits << Bit.new(self, n, writable: @init_as_writable, undefined: true)
        end

        add_bits_from_options(options)
      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
      def bit_order
        @bit_order ||= parent.respond_to?(:bit_order) ? parent.bit_order : :lsb0
      end

      def freeze
        bits.each(&:freeze)
        # Call any methods which cache results to generate the instance variables
        # before they are frozen
        address
        super
      end

      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

      def has_parameter_bound_bits?
        @parameter_bound_bits && !@parameter_bound_bits.empty?
      end

      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

      def updating_bound_bits?
        @updating_bound_bits
      end

      def inspect(options = {})
        # 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 && bit_order == :msb0)
          desc << ('  ' + corner_double_up_left + ((horiz_double_line * bit_width + horiz_double_tee_down) * 8)).chop + corner_double_up_right
        else
          if bit_order == :lsb0
            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
          else
            desc << ('  ' + corner_double_up_left + ((horiz_double_line * bit_width + horiz_double_tee_down) * r)).chop + corner_double_up_right
          end
        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
          if bit_order == :lsb0
            max_bit = (byte_number * 8) - 1
            min_bit = max_bit - 8 + 1
          else
            min_bit = (byte_index * 8)
            max_bit = min_bit + 7
          end

          # BIT INDEX ROW
          line = '  '
          line_complete = false
          8.times do |i|
            if bit_order == :lsb0
              bit_num = (byte_number * 8) - i - 1
            else
              bit_num = (byte_index * 8) + i
            end
            if bit_num > size - 1
              if bit_order == :msb0 && bit_num == size
                line += vert_single_line
                line_complete = true
              else
                line << ' ' + ''.center(bit_width) unless line_complete
              end
            else
              line << vert_single_line + "#{bit_num}".center(bit_width)
            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 bit_order == :lsb0
                if max_bit > (size - 1) && !first_done
                  (max_bit - (size - 1)).times do
                    line << ' ' * (bit_width + 1)
                  end
                end
              end

              if bit.size > 1

                if name
                  if bitcounter.nil?
                    if bit_order == :lsb0
                      bit_name = "#{name}[#{_max_bit_in_range(bit, max_bit, min_bit)}:#{_min_bit_in_range(bit, max_bit, min_bit)}]"
                    else
                      bit_name = "#{name}[#{_min_bit_in_range(bit, max_bit, min_bit)}:#{_max_bit_in_range(bit, max_bit, min_bit)}]"
                    end
                    bit_span = _num_bits_in_range(bit, max_bit, min_bit)

                  else
                    upper = _max_bit_in_range(bit, max_bit, min_bit) + bitcounter - bit.size
                    lower = _min_bit_in_range(bit, max_bit, min_bit) + bitcounter - bit.size
                    if bit_order == :lsb0
                      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 bit_order == :lsb0
                if max_bit > (size - 1) && !first_done
                  (max_bit - (size - 1)).times do
                    line << ' ' * (bit_width + 1)
                  end
                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 && bit_order == :lsb0
              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
            elsif (byte_index == num_bytes - 1) && r != 0 && bit_order == :msb0
              desc << ('  ' + corner_single_down_left + ((horiz_single_line * bit_width + horiz_single_tee_up) * r)).chop + corner_single_down_right
            elsif (byte_index == num_bytes - 2) && r != 0 && bit_order == :msb0
              desc << '  ' + vert_single_tee_right + ((horiz_single_line * bit_width + horiz_single_cross) * r) + ((horiz_single_line * bit_width + horiz_single_tee_up) * (8 - r)).chop + corner_single_down_right
            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
            if bit_order == :lsb0
              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
            else
              desc << ('  ' + corner_single_down_left + ((horiz_single_line * bit_width + horiz_single_tee_up) * size)).chop + corner_single_down_right
            end
          end
        end
        desc.join("\n")
      end

      # Returns a hash containing all register descriptions that have been parsed so far.
      #
      # @api private
      def description_lookup
        @@description_lookup ||= {}
      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.
      def default_reg_metadata
        Origen::Registers.default_reg_metadata.merge(
          Origen::Registers.reg_metadata[owner.class] || {})
      end

      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

      # 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.
      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

      # Escapes brackets and parenthesis. Helper for description method.
      def escape_special_char(str)
        str.gsub('[', '\[').gsub(']', '\]').gsub('(', '\(').gsub(')', '\)') if str
      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)
      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
      alias_method :descriptions, :description

      # @api private
      def parse_descriptions
        desc = []
        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

      def contains_bits?
        true
      end

      # @api private
      def add_bits_from_options(options = {}) # :nodoc:
        # options is now an array for split bit groups or a hash if single bit/range bits
        # Now add the requested bits to the register, removing the unwritable bits as required
        options.each do |bit_id, bit_params|
          puts bit_id
          puts bit_params
          if bit_params.is_a? Hash
            description = bit_params.delete(:description)
            if description
              @description_from_api[bit_id] = description.split(/\r?\n/)
            end
            bind(bit_id, bit_params.delete(:bind)) if bit_params[:bind]
            position = bit_params[:pos] || 0
            num_bits = bit_params[:bits] || 1
            if @_reset
              if @_reset.is_a?(Symbol)
                bit_params[:res] = @_reset
              else
                bit_params[:res] = @_reset[(num_bits + position - 1), position]
              end
            end
            bit_params[:access] = @_access if bit_params[:access].nil?
            bit_params[:res] = bit_params[:data] if bit_params[:data]
            bit_params[:res] = bit_params[:reset] if bit_params[:reset]
            if num_bits == 1
              add_bit(bit_id, position, bit_params)   # and add the new one
            else
              add_bus(bit_id, position, num_bits, bit_params)
            end
          elsif bit_params.is_a? Array<commit_msg>remove debug comments<commit_after>require 'json'
module Origen
  module Registers
    # 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.<br>
    # 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.
    class Reg
      include Origen::SubBlocks::Path
      include Origen::SubBlocks::Domains

      # 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
      REG_LEVEL_ATTRIBUTES = {
        _feature:   {},
        _reset:     { aliases: [:res] },
        _memory:    {},
        _path:      { aliases: [:hdl_path] },
        _abs_path:  { aliases: [:absolute_path] },
        _access:    {},
        _bit_order: {}
      }

      # Returns the object that own the register.
      # ==== Example
      #   $soc.reg(:blah).owner   # Returns the $soc object
      attr_reader :owner
      alias_method :parent, :owner
      # The base address of the register, this will be set dynamically
      # by Origen based on the parent's base address
      attr_accessor :base_address
      attr_writer :address # :nodoc:
      # Returns an integer representing the number of bits in the register
      attr_reader :size
      # The register name
      attr_accessor :name
      # Any feature associated with the register
      attr_accessor :feature

      attr_accessor :grows_backwards # :nodoc:
      attr_accessor :lookup # :nodoc:
      # Returns a full path to the file in which the register was defined
      attr_reader :define_file
      # Returns any application-specific meta-data attatched to the given register
      attr_accessor :meta
      alias_method :meta_data, :meta
      alias_method :metadata, :meta
      # If the given register's reset data is backed by memory, the memory address can
      # be recorded in this attribute
      attr_accessor :memory

      # Normally shouldn't be called directly, instantiate through add_reg
      # Upon initialization bits are stored as follows:
      # @bits -
      # An array of bit objects in position order, @bits[5] corresponds
      # to the bit as position r
      # @lookup -
      # A Hash lookup table for quickly accessing bit objects by name
      # @lookup = { :bit_or_bus_name => {:pos => 3, :bits => 4} }
      def initialize(owner, address, size, name, options = {}) # :nodoc:
        @owner = owner
        @address = address
        @size = size
        @bits = []
        @lookup = {}
        @name = name
        @init_as_writable = options.delete(:init_as_writable)
        @define_file = options.delete(:define_file)
        @from_placeholder = options.delete(:from_placeholder) || false
        REG_LEVEL_ATTRIBUTES.each do |attribute, _meta|
          if @from_placeholder
            instance_variable_set("@#{attribute[1..-1]}", options.delete(attribute))
          else
            # If register creation is coming directly from Reg.new, instead of Placeholder,
            #   it may not have attributes with '_' prefix
            instance_variable_set("@#{attribute[1..-1]}", options.delete(attribute[1..-1].to_sym))
          end
        end
        @description_from_api = {}
        description = options.delete(:description)
        if description
          @description_from_api[:_reg] = description.split(/\r?\n/)
        end
        @meta = default_reg_metadata.merge(options.delete(:meta) || {})

        # Initialize with unwritable bits that read back as zero, can override this
        # to make all writable by default by setting the :init_writable option to true
        @size.times do |n|
          @bits << Bit.new(self, n, writable: @init_as_writable, undefined: true)
        end

        add_bits_from_options(options)
      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
      def bit_order
        @bit_order ||= parent.respond_to?(:bit_order) ? parent.bit_order : :lsb0
      end

      def freeze
        bits.each(&:freeze)
        # Call any methods which cache results to generate the instance variables
        # before they are frozen
        address
        super
      end

      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

      def has_parameter_bound_bits?
        @parameter_bound_bits && !@parameter_bound_bits.empty?
      end

      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

      def updating_bound_bits?
        @updating_bound_bits
      end

      def inspect(options = {})
        # 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 && bit_order == :msb0)
          desc << ('  ' + corner_double_up_left + ((horiz_double_line * bit_width + horiz_double_tee_down) * 8)).chop + corner_double_up_right
        else
          if bit_order == :lsb0
            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
          else
            desc << ('  ' + corner_double_up_left + ((horiz_double_line * bit_width + horiz_double_tee_down) * r)).chop + corner_double_up_right
          end
        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
          if bit_order == :lsb0
            max_bit = (byte_number * 8) - 1
            min_bit = max_bit - 8 + 1
          else
            min_bit = (byte_index * 8)
            max_bit = min_bit + 7
          end

          # BIT INDEX ROW
          line = '  '
          line_complete = false
          8.times do |i|
            if bit_order == :lsb0
              bit_num = (byte_number * 8) - i - 1
            else
              bit_num = (byte_index * 8) + i
            end
            if bit_num > size - 1
              if bit_order == :msb0 && bit_num == size
                line += vert_single_line
                line_complete = true
              else
                line << ' ' + ''.center(bit_width) unless line_complete
              end
            else
              line << vert_single_line + "#{bit_num}".center(bit_width)
            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 bit_order == :lsb0
                if max_bit > (size - 1) && !first_done
                  (max_bit - (size - 1)).times do
                    line << ' ' * (bit_width + 1)
                  end
                end
              end

              if bit.size > 1

                if name
                  if bitcounter.nil?
                    if bit_order == :lsb0
                      bit_name = "#{name}[#{_max_bit_in_range(bit, max_bit, min_bit)}:#{_min_bit_in_range(bit, max_bit, min_bit)}]"
                    else
                      bit_name = "#{name}[#{_min_bit_in_range(bit, max_bit, min_bit)}:#{_max_bit_in_range(bit, max_bit, min_bit)}]"
                    end
                    bit_span = _num_bits_in_range(bit, max_bit, min_bit)

                  else
                    upper = _max_bit_in_range(bit, max_bit, min_bit) + bitcounter - bit.size
                    lower = _min_bit_in_range(bit, max_bit, min_bit) + bitcounter - bit.size
                    if bit_order == :lsb0
                      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 bit_order == :lsb0
                if max_bit > (size - 1) && !first_done
                  (max_bit - (size - 1)).times do
                    line << ' ' * (bit_width + 1)
                  end
                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 && bit_order == :lsb0
              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
            elsif (byte_index == num_bytes - 1) && r != 0 && bit_order == :msb0
              desc << ('  ' + corner_single_down_left + ((horiz_single_line * bit_width + horiz_single_tee_up) * r)).chop + corner_single_down_right
            elsif (byte_index == num_bytes - 2) && r != 0 && bit_order == :msb0
              desc << '  ' + vert_single_tee_right + ((horiz_single_line * bit_width + horiz_single_cross) * r) + ((horiz_single_line * bit_width + horiz_single_tee_up) * (8 - r)).chop + corner_single_down_right
            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
            if bit_order == :lsb0
              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
            else
              desc << ('  ' + corner_single_down_left + ((horiz_single_line * bit_width + horiz_single_tee_up) * size)).chop + corner_single_down_right
            end
          end
        end
        desc.join("\n")
      end

      # Returns a hash containing all register descriptions that have been parsed so far.
      #
      # @api private
      def description_lookup
        @@description_lookup ||= {}
      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.
      def default_reg_metadata
        Origen::Registers.default_reg_metadata.merge(
          Origen::Registers.reg_metadata[owner.class] || {})
      end

      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

      # 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.
      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

      # Escapes brackets and parenthesis. Helper for description method.
      def escape_special_char(str)
        str.gsub('[', '\[').gsub(']', '\]').gsub('(', '\(').gsub(')', '\)') if str
      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)
      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
      alias_method :descriptions, :description

      # @api private
      def parse_descriptions
        desc = []
        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

      def contains_bits?
        true
      end

      # @api private
      def add_bits_from_options(options = {}) # :nodoc:
        # options is now an array for split bit groups or a hash if single bit/range bits
        # Now add the requested bits to the register, removing the unwritable bits as required
        options.each do |bit_id, bit_params|
          if bit_params.is_a? Hash
            description = bit_params.delete(:description)
            if description
              @description_from_api[bit_id] = description.split(/\r?\n/)
            end
            bind(bit_id, bit_params.delete(:bind)) if bit_params[:bind]
            position = bit_params[:pos] || 0
            num_bits = bit_params[:bits] || 1
            if @_reset
              if @_reset.is_a?(Symbol)
                bit_params[:res] = @_reset
              else
                bit_params[:res] = @_reset[(num_bits + position - 1), position]
              end
            end
            bit_params[:access] = @_access if bit_params[:access].nil?
            bit_params[:res] = bit_params[:data] if bit_params[:data]
            bit_params[:res] = bit_params[:reset] if bit_params[:reset]
            if num_bits == 1
              add_bit(bit_id, position, bit_params)   # and add the new one
            else
              add_bus(bit_id, position, num_bits, bit_params)
            end
          elsif bit_params.is_a? Array
{'fraction_non_alphanumeric': 0.09724299065420561, 'fraction_numerical': 0.004953271028037384, 'mean_word_length': 1.6615882096884522, 'pattern_counts': {'":': 0, '<': 117, '<?xml version=': 0, '>': 33, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1332446', 'n_tokens_mistral': 12585, 'n_tokens_neox': 12154, 'n_words': 3177}