class Origen::Generator::Pattern

Public Instance Methods

close(options = {}) click to toggle source
# File lib/origen/generator/pattern.rb, line 58
def close(options = {})
  pattern_close(options)
end
create(options = {}) { || ... } click to toggle source
# File lib/origen/generator/pattern.rb, line 118
def create(options = {})
  if @pattern_sequence
    yield
  else
    @create_options = options
    unless Origen.tester
      puts 'The current target has not instantiated a tester and pattern generation cannot run.'
      puts 'Add something like this to an environment file:'
      puts
      puts '  Origen::Tester::J750.new'
      puts
      puts
      puts 'Then select it by running:  origen e <environment name>'
      exit 1
    end
    Origen.tester.generating = :pattern

    job.output_file_body = options.delete(:name).to_s if options[:name]

    # Order the iterators by the order that their enable keys appear in the options, pad
    # any missing iterators with a dummy function...
    iterators = options.map do |key, _val|
      Origen.app.pattern_iterators.find { |iterator| iterator.key == key }
    end.compact
    iterators << DummyIterator.new while iterators.size < 10

    args = []

    # Couldn't get this to work fully dynamically, so hard-coded for 10 custom
    # iterators for now, should be plenty for any application in the meantime.
    # Should revisit this when time allows and remove this limitation by changing
    # this to a recursive structure.
    iterators[0].invoke(options) do |arg0|
      args[0] = arg0
      iterators[1].invoke(options) do |arg1|
        args[1] = arg1
        iterators[2].invoke(options) do |arg2|
          args[2] = arg2
          iterators[3].invoke(options) do |arg3|
            args[3] = arg3
            iterators[4].invoke(options) do |arg4|
              args[4] = arg4
              iterators[5].invoke(options) do |arg5|
                args[5] = arg5
                iterators[6].invoke(options) do |arg6|
                  args[6] = arg6
                  iterators[7].invoke(options) do |arg7|
                    args[7] = arg7
                    iterators[8].invoke(options) do |arg8|
                      args[8] = arg8
                      iterators[9].invoke(options) do |arg9|
                        args[9] = arg9
                        # Refresh the target to start all settings from scratch each time
                        # This is an easy way to reset all registered values
                        Origen.app.reload_target!(skip_first_time: true)

                        # Final call back to the project to allow it to make any pattern name specific
                        # configuration changes
                        Origen.app.listeners_for(:before_pattern).each do |listener|
                          listener.before_pattern(job.output_pattern_filename)
                        end

                        # Work out the final pattern name based on the current iteration
                        job.reset_output_pattern_filename
                        iterators.each_with_index do |iterator, i|
                          if iterator.enabled?(options)
                            job.output_pattern_filename =
                              iterator.pattern_name.call(job.output_pattern_filename, args[i])
                          end
                        end

                        # Allow custom pattern prefix
                        unless options[:pat_prefix].to_s.empty?
                          if job.output_prefix.empty?
                            job.output_pattern_filename = "#{options[:pat_prefix]}_" + job.output_pattern_filename
                          else
                            job.output_pattern_filename = job.output_pattern_filename.sub(job.output_prefix, job.output_prefix + "#{options[:pat_prefix]}_")
                          end
                        end

                        # Allow custom pattern postfix
                        unless options[:pat_postfix].to_s.empty?
                          job.output_pattern_filename = job.output_pattern_filename.sub(job.output_postfix + job.output_extension, "_#{options[:pat_postfix]}" + job.output_postfix + job.output_extension)
                        end

                        pattern_wrapper(iterators, args, options) do
                          # Call iterator setups, whatever these return are passed to the pattern
                          yield_items = []
                          iterators.each_with_index do |iterator, i|
                            if iterator.enabled?(options)
                              yield_items << iterator.setup.call(args[i])
                            end
                          end

                          yield(*yield_items)
                        end
                      end
                    end
                  end
                end
              end
            end
          end
        end
      end
    end
  end
  @create_options = nil
end
create_options() click to toggle source

Returns the options passed to the current create block

# File lib/origen/generator/pattern.rb, line 21
def create_options
  @create_options || {}
end
log() click to toggle source
# File lib/origen/generator/pattern.rb, line 16
def log
  Origen.log
end
open(options = {}) { |*args| ... } click to toggle source

The recommended way to create a pattern is to wrap it within a Pattern.create block, however occasionally the need may arise to manually open and close a pattern, this method can be used in that case in association with the close method.

Pattern iterators are not supported when creating a pattern in this way.

# File lib/origen/generator/pattern.rb, line 31
def open(options = {})
  if block_given?
    create(options) do |*args|
      yield(*args)
    end
  else
    job.output_file_body = options.delete(:name).to_s if options[:name]

    # Refresh the target to start all settings from scratch each time
    # This is an easy way to reset all registered values
    Origen.app.reload_target!(skip_first_time: true)

    # Final call back to the project to allow it to make any pattern name specific
    # configuration changes
    Origen.app.listeners_for(:before_pattern).each do |listener|
      listener.before_pattern(job.output_pattern_filename)
    end

    # Allow custom pattern postfix
    unless options[:pat_postfix].to_s.empty?
      job.output_pattern_filename = job.output_pattern_filename.sub(job.output_postfix + job.output_extension, "_#{options[:pat_postfix]}" + job.output_postfix + job.output_extension)
    end

    pattern_open(options)
  end
end
reset() click to toggle source

This is called before each new pattern source is executed

@api private

# File lib/origen/generator/pattern.rb, line 247
def reset
  $desc = nil # Clear the description
end
sequence(options = {}, &block) click to toggle source
# File lib/origen/generator/pattern.rb, line 62
def sequence(options = {}, &block)
  @create_options = options
  unless Origen.tester
    puts 'The current target has not instantiated a tester and pattern generation cannot run.'
    puts 'Add something like this to an environment file:'
    puts
    puts '  Origen::Tester::J750.new'
    puts
    puts
    puts 'Then select it by running:  origen e <environment name>'
    exit 1
  end
  Origen.tester.generating = :pattern

  job.output_file_body = options.delete(:name).to_s if options[:name]

  # Refresh the target to start all settings from scratch each time
  # This is an easy way to reset all registered values
  Origen.app.reload_target!(skip_first_time: true)

  # Final call back to the project to allow it to make any pattern name specific
  # configuration changes
  Origen.app.listeners_for(:before_pattern).each do |listener|
    listener.before_pattern(job.output_pattern_filename)
  end

  ## Allow custom pattern postfix
  # unless options[:pat_postfix].to_s.empty?
  #  job.output_pattern_filename = job.output_pattern_filename.sub(job.output_postfix + job.output_extension, "_#{options[:pat_postfix]}" + job.output_postfix + job.output_extension)
  # end

  @pattern_sequence = true

  # The startup callbacks need to be skipped for now until the main thread is open for business
  pattern_wrapper([], [], options.merge(call_startup_callbacks: false)) do
    # The startup callbacks, if required, need to be wrapped up in a closure for calling
    # later by the main thread
    if (options.key?(:call_startup_callbacks) && !options[:call_startup_callbacks]) || options[:skip_startup]
      pre_block = nil
    else
      pre_block = proc do
        # Call startup callbacks
        Origen.app.listeners_for(:startup).each do |listener|
          listener.startup(options)
        end
      end
    end
    PatternSequencer.send(:active=, true)
    @pattern_sequence = PatternSequence.new(job.output_pattern_filename, block, pre_block)
    @pattern_sequence.send(:execute)
    PatternSequencer.send(:active=, false)
  end
  @pattern_sequence = false
  @create_options = nil
end
split(options = {}) click to toggle source

Split a running Pattern.create block into multiple patterns.

The output that has been generated up until the point where this is called will be written and a new pattern will be opened to contain the remainder of the pattern content generated by the block.

Each additional pattern section created by calling this method will have ‘_partN’ appended to the original pattern name.

# File lib/origen/generator/pattern.rb, line 237
def split(options = {})
  split_name = options.delete(:name) || ''
  pattern_close(options.merge(call_shutdown_callbacks: false))
  job.inc_split_counter(split_name)
  pattern_open(options.merge(call_startup_callbacks: false))
end

Private Instance Methods

header() click to toggle source
# File lib/origen/generator/pattern.rb, line 274
def header
  Origen.tester.pre_header if Origen.tester.doc?
  inject_separator
  if $desc
    c2 'DESCRIPTION:'
    $desc.split(/\n/).each { |line| cc line }
    inject_separator
  end
  c2 'GENERATED:'
  c2 "  Time:    #{Origen.launch_time}"
  c2 "  By:      #{Origen.current_user.name}"
  c2 "  Mode:    #{Origen.mode}"
  l = "  Command: origen g #{job.requested_pattern} -t #{Origen.target.file.basename}"
  if Origen.environment && Origen.environment.file
    l += " -e #{Origen.environment.file.basename}"
  end
  c2(l)
  inject_separator
  c2 'ENVIRONMENT:'
  c2 '  Application'
  if Origen.app.rc
    if Origen.app.rc.git?
      c2 "    Source:    #{Origen.config.rc_url}"
    else
      c2 "    Vault:     #{Origen.config.vault}"
    end
  end
  c2 "    Version:   #{Origen.app.version}"
  unless Origen.app.config.release_externally
    c2 "    Workspace: #{Origen.root}"
  end
  if Origen.app.rc && Origen.app.rc.git?
    begin
      @branch ||= Origen.app.rc.current_branch
      @commit ||= Origen.app.rc.current_commit
      status = "#{@branch}(#{@commit})"
      @pattern_local_mods = !Origen.app.rc.local_modifications.empty? unless @pattern_local_mods_fetched
      @pattern_local_mods_fetched = true
      status += ' (+local edits)' if @pattern_local_mods
      c2 "    Branch:    #{status}"
    rescue
      # No problem, we did our best
    end
  end
  c2 '  Origen'
  c2 '    Source:    https://github.com/Origen-SDK/origen'
  c2 "    Version:   #{Origen.version}"
  unless Origen.app.plugins.empty?
    c2 '  Plugins'
    Origen.app.plugins.sort_by { |p| p.name.to_s }.each do |plugin|
      c2 "    #{plugin.name}:".ljust(30) + plugin.version
    end
  end
  inject_separator

  unless Origen.app.plugins.empty?
    # Plugins can use config.shared_pattern_header to inject plugin-specific comments into the patterns header
    header_printed = false
    Origen.app.plugins.sort_by { |p| p.name.to_s }.each do |plugin|
      unless plugin.config.shared_pattern_header.nil?
        unless header_printed
          c2 'Header Comments From Shared Plugins:'
          header_printed = true
        end
        inject_pattern_header(
          config_loc:      plugin,
          scope:           :shared_pattern_header,
          message:         "Header Comments From Shared Plugin: #{plugin.name}:",
          message_spacing: 2,
          line_spacing:    4,
          no_separator:    true
        )
      end
    end
    inject_separator if header_printed
  end

  if Origen.app.plugins.current && !Origen.app.plugins.current.config.send(:current_plugin_pattern_header).nil?
    # The top level plugin (if one is set) can further inject plugin-specific comment into the header.
    # These will only appear if the plugin is the top-level plugin though.
    inject_pattern_header(
      config_loc: Origen.app.plugins.current,
      scope:      :current_plugin_pattern_header,
      message:    "Header Comments From The Current Plugin: #{Origen.app.plugins.current.name}:"
    )
  end

  unless Origen.app.config.send(:application_pattern_header).nil?
    inject_pattern_header(
      config_loc: Origen.app,
      scope:      :application_pattern_header,
      message:    "Header Comments From Application: #{Origen.app.name}:"
    )
  end

  if Origen.config.pattern_header
    Origen.log.deprecated 'Origen.config.pattern_header is deprecated.'
    Origen.log.deprecated 'Please use config.shared_pattern_header, config.application_pattern_header, or config.current_plugin_pattern_header instead.'
    inject_separator
  end
  Origen.tester.close_text_block if Origen.tester.doc?
end
inject_pattern_header(config_loc:, scope:, message:, **options) click to toggle source
# File lib/origen/generator/pattern.rb, line 382
def inject_pattern_header(config_loc:, scope:, message:, **options)
  message_spacing = options[:message_spacing] || 0
  line_spacing = options[:line_spacing] || 2
  no_separator = options[:no_separator] || false

  # Print a warning if any of the pattern header configs have an arity greater than 1.
  # Anything over 1 won't be used and may cause confusion.
  if config_loc.config.send(scope).arity > 1
    Origen.log.warning "Configuration in #{config_loc.name} has attribute ##{scope} whose block has an arity > 1"
    Origen.log.warning 'Calls to this attribute from Origen are only given an options hash parameter. Any other arguments are extraneous'
  end

  # Inject the header based on these guidelines:
  # 1. if pattern_header is nil, ignore all. Don't print he message, don't do anything.
  # 2. if pattern header is a block, print the message, then call the block. This allows the user to format everything themselves.
  #    i.e., use of cc, ss, etc. is allowed here, at the expense of the user being responsible for the formatting.
  # 3. if a string, print the message and format the string as 'cc'
  # 4. if an array, print the message and format the array as a subsequent 'cc' calls.
  injection = config_loc.config.send(scope).call({})
  if injection.nil?
    # Do nothing. It is assumed in this acase that the pattern header has not comments to add at this scope.
    nil
  elsif injection.is_a?(String)
    c2(' ' * message_spacing + message)
    c2(' ' * line_spacing + injection.to_s)
    inject_separator(options) unless no_separator
  elsif injection.is_a?(Array)
    c2(' ' * message_spacing + message)
    injection.each do |line|
      c2(' ' * line_spacing + line.to_s)
    end
    inject_separator(options) unless no_separator
  elsif injection.respond_to?(:call)
    c2(' ' * message_spacing + message)
    injection.call
    inject_separator(options) unless no_separator
  else
    Origen.app.fail!(message: "Unexpected object class returned by config.pattern_header from #{config_loc.name}: #{injection.class}", exception_class: TypeError)
  end
end
inject_separator(options = {}) click to toggle source
# File lib/origen/generator/pattern.rb, line 377
def inject_separator(options = {})
  separator_length = options[:size] || 75
  c2('*' * separator_length)
end
job() click to toggle source
# File lib/origen/generator/pattern.rb, line 253
def job
  # The only time the job is not present should be in a test situation, e.g.
  # when calling Pattern.create within a spec test
  Origen.app.current_job || Origen::Generator::Job.new('anonymous', testing: true)
end
pattern_close(options = {}) click to toggle source
# File lib/origen/generator/pattern.rb, line 472
def pattern_close(options = {})
  options = {
    call_shutdown_callbacks: true
  }.merge(options)

  bank = options[:inhibit] ? :null : :body
  stage.with_bank bank do
    if options.delete(:call_shutdown_callbacks) && !options[:skip_shutdown]
      # Call shutdown callbacks
      Origen.app.listeners_for(:shutdown, top_level: :last).each do |listener|
        listener.shutdown(options)
      end
    end
    ss 'Pattern complete'

    # Now the pattern has run call the render method if the tester uses a template
    Origen.tester.render_template
    Origen.tester.render_body
  end

  bank = options[:inhibit] ? :null : :footer
  stage.with_bank bank do
    Origen.tester.pattern_footer(options)
    Origen.tester.render_footer
  end

  # Generate the pattern header, do this at the very end so that it can
  # dynamically pick up what import subs are required and things like that
  if Origen.tester.generate?
    bank = options[:inhibit] ? :null : :header
    stage.with_bank bank do
      header
      options[:pattern] = job.output_pattern_filename.sub(job.output_extension, '') # remove output extension
      Origen.tester.pattern_header(options)
      Origen.tester.render_header
    end
  end

  unless options[:inhibit] || !Origen.tester.generate? || job.test?
    stats.collect_for_pattern(job.output_pattern) do
      # If the tester is going to deal with writing out the final pattern. The use case for this is when
      # the pattern is comprised of multiple files instead of the more conventional case here which each
      # pattern is one file.
      if tester.respond_to?(:open_and_write_pattern)
        tester.open_and_write_pattern(job.output_pattern) do
          [:header, :body, :footer].each do |section|
            Origen.tester.format(stage.bank(section), section)
          end
        end
      else
        File.open(job.output_pattern, 'w') do |f|
          [:header, :body, :footer].each do |section|
            Origen.tester.format(stage.bank(section), section) do |line|
              f.puts line
            end
          end
        end
      end
    end

    unless tester.try(:sim?)
      log.info ' '
      log.info "Pattern vectors: #{stats.number_of_vectors_for(job.output_pattern).to_s.ljust(10)}"
      log.info 'Execution time'.ljust(15) + ': %.6f' % stats.execution_time_for(job.output_pattern)
      log.info '----------------------------------------------------------------------'
      check_for_changes(job.output_pattern, job.reference_pattern) unless tester.try(:disable_pattern_diffs)
    end
    if @pattern_sequence
      @pattern_sequence.send(:log_execution_profile)
    end
    stats.record_pattern_completion(job.output_pattern)
  end

  if Origen.tester.generate?
    Origen.app.listeners_for(:pattern_generated).each do |listener|
      if listener.class.instance_method(:pattern_generated).arity == 1
        listener.pattern_generated(Pathname.new(job.output_pattern))
      else
        listener.pattern_generated(Pathname.new(job.output_pattern), job.options)
      end
    end
  end
end
pattern_open(options = {}) click to toggle source
# File lib/origen/generator/pattern.rb, line 423
def pattern_open(options = {})
  options = {
    call_startup_callbacks: true
  }.merge(options)

  iterators = options.delete(:iterators) || []

  args = options.delete(:args)

  if Origen.tester.generate?
    # Now the pattern name is established delete any existing versions of this pattern
    # to make it clearer that something has gone wrong if there is an error generating the new one
    unless job.test?
      File.delete(job.output_pattern) if File.exist?(job.output_pattern)

      unless tester.try(:sim?)
        log.info "Generating...  #{job.output_pattern_directory}/#{job.output_pattern_filename}".ljust(50)
      end
    end
  end

  Origen.tester.inhibit_comments = job.no_comments? if Origen.tester.respond_to?(:inhibit_comments=)

  stage.reset!

  stage.bank = options[:inhibit] ? :null : :body

  if options.delete(:call_startup_callbacks)

    skip_startup = false

    # Call the iterator startup methods, if any of them return false/nil the standard
    # startup callbacks will be skipped
    iterators.each_with_index do |iterator, i|
      if iterator.enabled?(options)
        skip_startup = !iterator.startup.call(options, args[i]) || skip_startup
      end
    end

    unless skip_startup || options[:skip_startup]
      # Call startup callbacks
      Origen.app.listeners_for(:startup).each do |listener|
        listener.startup(options)
      end
    end

  end
end
pattern_wrapper(iterators, args, options = {}) { || ... } click to toggle source

Creates a header and footer for the pattern based on the current tester and any supplied options

# File lib/origen/generator/pattern.rb, line 268
def pattern_wrapper(iterators, args, options = {})
  pattern_open(options.merge(iterators: iterators, args: args))
  yield # Pass control back to the pattern source
  pattern_close(options)
end
stage() click to toggle source
# File lib/origen/generator/pattern.rb, line 259
def stage
  Origen.generator.stage
end
stats() click to toggle source
# File lib/origen/generator/pattern.rb, line 263
def stats
  Origen.app.stats
end