module OrigenTesters module IGXLBasedTester # This is the base class of all IGXL-based testers class Base include VectorBasedTester attr_accessor :software_version attr_accessor :pattern_compiler_pinmap attr_accessor :memory_test_en attr_accessor :testerconfig attr_accessor :channelmap attr_accessor :default_channelmap attr_accessor :default_testerconfig attr_accessor :max_site # NOTE: DO NOT USE THIS CLASS DIRECTLY ONLY USED AS PARENT FOR # DESIRED TESTER CLASS # Returns a new IGXLBasedTester instance, normally there would only ever be one of these # assigned to the global variable such as $tester by your target. def initialize @unique_counter = 0 @counter_lsb_bits = 0 @counter_msb_bits = 0 @max_repeat_loop = 65_535 # 16 bits @min_repeat_loop = 2 @pat_extension = 'atp' @active_loads = true @pipeline_depth = 34 @software_version = '' @compress = true @support_repeat_previous = true @match_entries = 10 @name = '' @program_comment_char = ['logprint', "'"] @opcode_mode = :extended @flags = %w(cpuA cpuB cpuC cpuD) @microcode = {} @microcode[:enable] = 'enable' @microcode[:set_flag] = 'set_cpu' @microcode[:mask_vector] = 'ign ifc icc' @mask_vector = false # sticky option to mask all subsequent vectors @min_pattern_vectors = 0 # no minimum @memory_test_en = false # memory test enabled (for all patterns?) @testerconfig ||= {} @channelmap ||= {} @pushed_instrument_configs = {} @overlay_style = :subroutine # default to use subroutine for overlay @capture_style = :hram # default to use hram for capture @overlay_history = {} # used to track labels, subroutines, digsrc pins used etc @overlay_subr = nil @capture_history = {} end def igxl_based? true end def import_tester_config(testconfigname, fullconfigpath) # This function reads in CurrentConfig.txt file generated by IG-XL. # testconfigname example ==> "FT", "WT", "Production" # fullconfigpath example ==> "/product_folder/CurrentConfig.txt" puts "importing Testerconfig #{testconfigname}..." slotnum = Struct.new(:slot, :instrument, :idprom) @testerconfig[testconfigname] ||= {} current_config_file = Pathname.new(fullconfigpath) File.open(current_config_file, 'r').each_line do |line| if line =~ /^\d*.0/ (slot, blank1, instrument, blank2, idprom) = line.split(/\t/) if (!slot.nil?) && (!instrument.nil?) && (!idprom.nil?) @testerconfig[testconfigname][slot.split('.').first.to_i] = slotnum.new(slot.split('.').first.to_i, instrument, idprom.chomp) end end end @default_testerconfig ||= testconfigname # Default TesterConfig gets set if it's not nil end def get_tester_instrument(testconfigname, slot) @testerconfig[testconfigname].each_with_index do |element, index| if slot.to_s == element[0].to_s return element[1][:instrument] end end nil # if no corresponding slot end def get_instrument_slots(testconfigname, instrument) # testconfigname example "WT", "FT", "Production" @slots = [] @testerconfig[testconfigname].each_with_index do |element, index| if instrument.to_s == element[1][:instrument].to_s @slots << element[0].to_i end end @slots # if no corresponding slot end def import_chanmap(chanmapname, fullchanmappath) # This function reads IG-XL ChannelMap file # chanmapname example ==> "FT", "WT", "FTX2" # fullchanmappath example ==> "/product_folder/Chans_FT.txt" puts "importing ChannelMap #{fullchanmappath}..." chanassignment = Struct.new(:pinname, :site, :channel, :type, :packagepin) chanmap_file = Pathname.new(fullchanmappath) @channelmap[chanmapname] ||= {} File.open(chanmap_file, 'r').each_line.with_index do |line, index| if index == 0 unless line =~ /DTChanMap/ puts "#{fullchanmappath} is not a valid IG-XL ChannelMap!" break end end if index == 5 siteloc = line.strip.split(/\t/).size - 1 # strip all white spaces and grab second to last @max_site_s = line.split(/\t/)[siteloc].strip.split(/\s/)[1] @max_site = @max_site_s.to_i (0..@max_site).each do |sitenum| @channelmap[chanmapname][sitenum] ||= {} end end if index > 5 (blank1, pinname, packagepin, type) = line.split(/\t/) (0..@max_site).each do |sitenum| channel = line.split(/\t/)[4 + sitenum].to_s @channelmap[chanmapname][sitenum][pinname.downcase.intern] = chanassignment.new(pinname.downcase.intern, sitenum, channel.chomp, type.chomp, packagepin) end end end @default_channelmap ||= chanmapname # Default Channelmap gets set if it's not nil end def get_tester_channel(chanmapname, pinname, sitenum) if sitenum <= @max_site @testerchannel = @channelmap[chanmapname][sitenum][pinname].channel return @testerchannel else return nil end end # Check if a specific pin for a given channelmap for a given site number is using merged channel. # If yes, return x2 for Merged2, x4 for Merged4, etc. If no, return nil. def merged_channels(chanmapname, pinname, sitenum) if sitenum <= @max_site if @channelmap[chanmapname][sitenum][pinname].type.include?('Merged') @merged_channels = @channelmap[chanmapname][sitenum][pinname].type.split('Merged')[1] return 'x' + @merged_channels end else return nil end end # Check if a specific HexVS supply is HexVS+ variety, which has +/-2mV accuracy as opposed to # +/-7mV accuracy. # If the specific HexVS is HexVS+ variety, returns a "+" string, otherwise nil. def is_hexvs_plus(testconfigname, slot) if @testerconfig[testconfigname][slot][:instrument].to_s == 'HexVS' @productnum = @testerconfig[testconfigname][slot][:idprom].split(' ')[0] if (@productnum.include?('974-294-')) && (@productnum.split('-')[2].to_i >= 20) return '+' end end nil # if nothing matched end # Check if a specific VHDVS (UVS256) supply is High-Accuracy (HA) variety, which has +/-5mV+0.1%*SUPPLY accuracy as opposed to # +/-10mV+0.1%*SUPPLY accuracy. # If the specific VHDVS is of High-Accuracy variety, returns a "+" string, otherwise nil. def is_vhdvs_plus(testconfigname, slot) if @testerconfig[testconfigname][slot][:instrument].to_s == 'VHDVS' @productnum = @testerconfig[testconfigname][slot][:idprom].split(' ')[0] # binding.pry if (@productnum.include?('805-052-')) && (@productnum.split('-')[2].to_i >= 05) return '+' end end nil # if nothing matched end # Check if a specific VHDVS (UVS256) channel assignment is _HC variety (high-current) # If the specific VHDVS channel is _HC variety, returns a "_HC" string, otherwise nil. def is_vhdvs_hc(chanmapname, pinname, sitenum) if sitenum <= @max_site if @channelmap[chanmapname][sitenum][pinname].channel.downcase.include?('hc') return '_HC' end end nil # if nothing matched end def assign_dc_instr_pins(dc_pins) if !dc_pins.is_a?(Array) @dc_pins = [] << dc_pins else @dc_pins = dc_pins end end def assign_digsrc_pins(digsrc_pins) if !digsrc_pins.is_a?(Array) @digsrc_pins = [] << digsrc_pins else @digsrc_pins = digsrc_pins end end def assign_digcap_pins(digcap_pins) if !digcap_pins.is_a?(Array) @digcap_pins = [] << digcap_pins else @digcap_pins = digcap_pins end end def get_dc_instr_pins @dc_pins end def get_digsrc_pins @digsrc_pins end def get_digcap_pins @digcap_pins end def flows parser.flows end # Main accessor to all content parsed from existing test program sheets found in the # supplied directory or in Origen.config.test_program_output_directory def parser(prog_dir = Origen.config.test_program_output_directory) unless prog_dir fail 'You must supply the directory containing the test program sheets, or define it via Origen.config.test_program_output_directory' end @parser ||= IGXLBasedTester::Parser.new @parsed_dir ||= false if @parsed_dir != prog_dir @parser.parse(prog_dir) @parsed_dir = prog_dir end @parser end # Capture a vector to the tester HRAM. # # This method applies a store vector (stv) opcode to the previous vector, note that is does # not actually generate a new vector. # # Sometimes when generating vectors within a loop you may want to apply a stv opcode # retrospectively to a previous vector, passing in an offset option will allow you # to do this. # # On J750 the pins argument is ignored since the tester only supports whole vector capture. # # @example # $tester.cycle # This is the vector you want to capture # $tester.store # This applies the STV opcode # # $tester.cycle # This one gets stored # $tester.cycle # $tester.cycle # $tester.store(:offset => -2) # Just realized I need to capture that earlier vector def store(*pins) options = pins.last.is_a?(Hash) ? pins.pop : {} options = { offset: 0 }.merge(options) update_vector microcode: 'stv', offset: options[:offset] last_vector(options[:offset]).contains_capture = true end alias_method :to_hram, :store alias_method :capture, :store # Capture the next vector generated to HRAM # # This method applies a store vector (stv) opcode to the next vector to be generated, # note that is does not actually generate a new vector. # # On J750 the pins argument is ignored since the tester only supports whole vector capture. # # @example # $tester.store_next_cycle # $tester.cycle # This is the vector that will be captured def store_next_cycle(*pins) options = pins.last.is_a?(Hash) ? pins.pop : {} options = { }.merge(options) preset_next_vector microcode: 'stv' do |vector| vector.contains_capture = true end end alias_method :store!, :store_next_cycle # @api private def remove_store_from_vector(vector) super vector.microcode = nil end # Call a subroutine. # # This method applies a call subroutine opcode to the previous vector, it does not # generate a new vector. # # Subroutines should always be called through this method as it ensures a running # log of called subroutines is maintained and which then gets output in the pattern # header to import the right dependencies. # # An offset option is available to make the call on earlier vectors. # # ==== Examples # $tester.call_subroutine("mysub") # $tester.call_subroutine("my_other_sub", :offset => -1) def call_subroutine(name, options = {}) options = { offset: 0 }.merge(options) called_subroutines << name.to_s.chomp unless called_subroutines.include?(name.to_s.chomp) || @inhibit_vectors update_vector microcode: "call #{name}", offset: options[:offset] end # Start a subroutine. # # Generates a global subroutine label. Global is used to adhere to the best practice of # containing all subroutines in dedicated patterns, e.g. global_subs.atp # # ==== Examples # $tester.start_subroutine("wait_for_done") # < generate your subroutine vectors here > # $tester.end_subroutine def start_subroutine(name, options = {}) local_subroutines << name.to_s.chomp unless local_subroutines.include?(name.to_s.chomp) || @inhibit_vectors if $tester.ultraflex? && (name =~ /keep_?alive/ || options[:keep_alive]) microcode "keepalive subr #{name}:" else microcode "global subr #{name}:" end end # End a subroutine. # # Generates a return opcode on the last vector. # # ==== Examples # $tester.start_subroutine("wait_for_done") # < generate your subroutine vectors here > # $tester.end_subroutine # cond: whether return is conditional on a flag (to permit to mix subrs together) def end_subroutine(cond = false, options = {}) (cond, options) = false, cond if cond.is_a?(Hash) if cond update_vector microcode: 'if (flag) return' else update_vector microcode: 'return' end end # Do a frequency measure. # # Write the necessary micro code to do a frequency measure on the given pin, # optionally supply a read code to pass information to the tester. # # ==== Examples # $tester.freq_count($top.pin(:d_out)) # Freq measure on pin "d_out" # $tester.freq_count($top.pin(:d_out):readcode => 10) def freq_count(pin, options = {}) options = { readcode: false }.merge(options) set_code(options[:readcode]) if options[:readcode] cycle(microcode: "#{@microcode[:set_flag]} (cpuA)") cycle(microcode: "#{@microcode[:set_flag]} (cpuA)") cycle(microcode: "#{@microcode[:set_flag]} (cpuB)") cycle(microcode: "#{@microcode[:set_flag]} (cpuC)") cycle(microcode: 'freq_loop_1:') cycle(microcode: 'if (cpuA) jump freq_loop_1') pin.drive_lo delay(2000) pin.dont_care cycle(microcode: "freq_loop_2: #{@microcode[:enable]} (#{@flags[1]})") cycle(microcode: 'if (flag) jump freq_loop_2') cycle(microcode: "#{@microcode[:enable]} (#{@flags[2]})") cycle(microcode: 'if (flag) jump freq_loop_1') end # * J750 Specific * # # Generates a single MTO opcode line for J750 # # Codes implemented: xa load_preset, xa inc, ya load_preset, ya inc, stv_m0, stv_m1, stv_c
def memory_test(options = {}) options = { gen_vector: true, # Default generate vector not just MTO opcode init_counter_x: false, # initialize counter X inc_counter_x: false, # increment counter X init_counter_y: false, # initialize counter X inc_counter_y: false, # increment counter X capture_vector: false, # capture vector to memory using all mem types capture_vector_mem0: false, # capture vector to memory type 0, here for J750 will be stv_m0 capture_vector_mem1: false, # capture vector to memory type 1, here for J750 will be stv_m1 capture_vector_mem2: false, # capture vector to memory type 2, here for J750 will be stv_c pin: false, # pin on which to drive or expect data, pass pin object here! pin_data: false, # pin data (:none, :drive, :expect) }.merge(options) mto_opcode = '' if options[:init_counter_x] mto_opcode += ' xa load_preset' end if options[:inc_counter_x] mto_opcode += ' xa inc' end if options[:init_counter_y] mto_opcode += ' ya load_preset' end if options[:inc_counter_y] mto_opcode += ' ya inc' end if options[:capture_vector] mto_opcode += ' stv_m0 stv_m1 stv_c' end if options[:capture_vector_mem0] mto_opcode += ' stv_m0' end if options[:capture_vector_mem1] mto_opcode += ' stv_m1' end if options[:capture_vector_mem2] mto_opcode += ' stv_c' end unless mto_opcode.eql?('') mto_opcode = '(mto:' + mto_opcode + ')' end if options[:gen_vector] if options[:pin] case options[:pin_data] when :drive # store current pin state cur_pin_state = options[:pin].state.to_sym options[:pin].drive_mem when :expect # store current pin state cur_pin_state = options[:pin].state.to_sym options[:pin].expect_mem end end cycle(microcode: "#{mto_opcode}") if options[:pin] # restore previous pin state case options[:pin_data] when :drive options[:pin].state = cur_pin_state when :expect options[:pin].state = cur_pin_state end end else microcode "#{mto_opcode}" end end # Generates a match loop on up to two pins. # # This method is not really intended to be called directly, rather you should call # via Tester#wait e.g. $tester.wait(:match => true). # # The timeout should be provided in cycles, however when called via the wait method the # time-based helpers (time_in_us, etc) will be converted to cycles for you. # The following options are available to tailor the match loop behavior, defaults in # parenthesis: # * :pin - The pin object to match on (*required*) # * :state - The pin state to match on, :low or :high (*required*) # * :pin2 (nil) - Optionally supply a second pin to match on # * :state2 (nil) - State for the second pin (required if :pin2 is supplied) # * :check_for_fails (false) - Flushes the pipeline and handshakes with the tester (passing readcode 100) prior to the match (to allow binout of fails encountered before the match) # * :force_fail_on_timeout (true) - Force a vector mis-compare if the match loop times out # * :on_timeout_goto ("") - Optionally supply a label to branch to on timeout, by default will continue from the end of the match loop # * :on_pin_match_goto ("") - Optionally supply a label to branch to when pin 1 matches, by default will continue from the end of the match loop # * :on_pin2_match_goto ("") - Optionally supply a label to branch to when pin 2 matches, by default will continue from the end of the match loop # * :multiple_entries (false) - Supply an integer to generate multiple entries into the match (each with a unique readcode), this can be useful when debugging patterns with multiple matches # * :force_fail_on_timeout (true) - force pattern to fail if timeout occurs # * :global_loops (false) - whether match loop loops should use global labels # * :manual_stop (false) - whether to use extra cpuB flag to resolve IG-XL v.3.50.xx bug where VBT clears cpuA immediately # at start of PatFlagFunc instead of at end. Use will have to manually clear cpuB to resume this pattern. # ==== Examples # $tester.wait(:match => true, :time_in_us => 5000, :pin => $top.pin(:done), :state => :high) def match(pin, state, timeout, options = {}) options = { check_for_fails: false, on_timeout_goto: false, pin2: false, state2: false, on_pin_match_goto: false, multiple_entries: false, force_fail_on_timeout: true, global_loops: false, manual_stop: false, clr_fail_post_match: false }.merge(options) options[:on_block_match_goto] ||= options.delete(:on_pin_match_goto) match_block(timeout, options) do |match_conditions, fail_conditions| # Define match conditions match_conditions.add do state == :low ? pin.expect_lo : pin.expect_hi cc "Check if #{pin.name} is #{state == :low ? 'low' : 'high'}" cycle pin.dont_care end if options[:pin2] match_conditions.add do state == :low ? pin.expect_hi : pin.expect_lo options[:state2] == :low ? options[:pin2].expect_lo : options[:pin2].expect_hi cc "Check if #{options[:pin2].name} is #{options[:state2] == :low ? 'low' : 'high'}" cycle options[:pin2].dont_care pin.dont_care end end # Define fail conditions fail_conditions.add do state == :low ? pin.expect_lo : pin.expect_hi cc "Check if #{pin.name} is #{state == :low ? 'low' : 'high'}" if options[:pin2] options[:state2] == :low ? options[:pin2].expect_lo : options[:pin2].expect_hi cc "Check if #{options[:pin2].name} is #{options[:state2] == :low ? 'low' : 'high'}" end cycle pin.dont_care options[:pin2].dont_care if options[:pin2] end end end # Call a match loop. # # Normally you would put your match loop in a global subs pattern, then you can # call it via this method. This method automatically syncs match loop naming with # the match generation flow, no arguments required. # # This is an IGXLBasedTester specific API. # # ==== Examples # $tester.cycle # $tester.call_match # Calls the match loop, or the first entry point if you have multiple # $tester.cycle # $tester.call_match # Calls the match loop, or the second entry point if you have multiple def call_match @match_counter = @match_counter || 0 call_subroutine("match_done_#{@match_counter}") @match_counter += 1 unless @match_counter == (@match_entries || 1) - 1 end # Apply a label to the pattern. # # No additional vector is generated. # Arguments: # name : label name # global : (optional) whether to apply global label, default=false # # ==== Examples # $tester.label("something_significant") # $tester.label("something_significant",true) # apply global label def label(name, global = false) global_opt = (global) ? 'global ' : '' microcode global_opt + name + ':' end # * J750 Specific * # # Set a readcode. # # Use the set an explicit readcode for communicating with the tester. This method # will generate an additional vector. # # ==== Examples # $tester.set_code(55) def set_code(code) cycle(microcode: "set_code #{code}") end # Branch execution to the given point. # # This generates a new vector with a jump instruction to a given label. This method # will generate an additional vector. # # ==== Examples # $tester.branch_to("something_significant") def branch_to(label) cycle(microcode: "jump #{label}") end alias_method :branch, :branch_to # Add loop to the pattern. # # Pass in a name for the loop and the number of times to execute it, all vectors # generated by the given block will be captured in the loop. # # Optional arguments: global - whether to apply global label (default=false) # label_first - whether to apply loop label before loop vector or not # # ==== Examples # $tester.loop_vectors("pulse_loop", 3) do # Do this 3 times... # $tester.cycle # some_other_method_to_generate_vectors # end def loop_vectors(name, number_of_loops, global = false, label_first = false) if number_of_loops > 1 @loop_counters ||= {} if @loop_counters[name] @loop_counters[name] += 1 else @loop_counters[name] = 0 end loop_name = @loop_counters[name] == 0 ? name : "#{name}_#{@loop_counters[name]}" if label_first global_opt = (global) ? 'global ' : '' microcode "#{global_opt}#{loop_name}: " end cycle(microcode: "loopA #{number_of_loops}") unless label_first global_opt = (global) ? 'global ' : '' cycle(microcode: "#{global_opt}#{loop_name}: ") end yield cycle(microcode: "end_loopA #{loop_name}") else yield end end alias_method :loop_vector, :loop_vectors # An internal method called by Origen Pattern Create to create the pattern header def pattern_header(options = {}) options = { instruments: {}, # Provide instruments here if desired as a hash (e.g. "mto" => "dgen_2bit") subroutine_pat: false, svm_only: true, # Whether 'svm_only' can be specified group: false, # If true the end pattern is intended to run within a pattern group high_voltage: false, # Supply a pin name here to declare it as an HV instrument (not yet defined) freq_counter: false, # Supply a pin name here to declare it as a frequency counter memory_test: false, # If true, define 2-bit MTO DGEN as instrument }.merge(options) if level_period? microcode "import tset #{min_period_timeset.name};" else called_timesets.each do |timeset| microcode "import tset #{timeset.name};" end end unless options[:group] # Withhold imports for pattern groups, is this correct? called_subroutines.each do |sub_name| # Don't import any called subroutines that are declared in the current pattern microcode "import svm_subr #{sub_name};" unless local_subroutines.include?(sub_name) end end # If memory test, then add to instruments hash if options[:memory_test] options[:instruments].merge!('mto' => 'dgen_2bit') end if options[:svm_only] microcode "svm_only_file = #{options[:subroutine_pat] ? 'yes' : 'no'};" end microcode "opcode_mode = #{@opcode_mode};" microcode "digital_inst = #{options[:digital_inst]};" if options[:digital_inst] microcode 'compressed = yes;' # if $dut.gzip_patterns if tester.j750? && options[:freq_counter] # pin setup type => freq_counter is only for the J750. # UltraFLEX has frequency counter capability behind every pin. # need to make sure the pin_setup freq_count is defined after opcode_mode extended since freq count only work in extended mode if @opcode_mode == :extended microcode "pin_setup = {#{options[:freq_counter]} freq_count;}" else fail 'pin_setup freq_count can only work in extended mode' end end # Take care of any instruments options[:instruments] = options[:instruments].merge(@pushed_instrument_configs) if options[:instruments].length > 0 microcode 'instruments = {' options[:instruments].each do |pins, instrument| if "#{pins}" == 'nil' microcode " #{instrument};" elsif instrument == 'digsrc' microcode " #{pins}:#{instrument} #{options[:digsrc_width]}:#{options[:digsrc_bit_order]}:#{options[:digsrc_mode]}:format=#{options[:digsrc_format]}:#{options[:digsrc_site_uniqueness]}:#{options[:digsrc_auto_cond]};" elsif instrument == 'digcap' microcode " #{pins}:#{instrument} #{options[:digcap_width]}:#{options[:digcap_bit_order]}:#{options[:digcap_mode]}:format=#{options[:digcap_format]}:data_type=#{options[:digcap_data_type]}:#{options[:digcap_auto_cond]}:#{options[:digcap_auto_trig_enable]}:#{options[:digcap_store_stv]}:#{options[:digcap_receive_data]};" else microcode " #{pins}:#{instrument};" end end microcode '}' end options[:high_voltage] = @use_hv_pin microcode "pin_setup = {#{options[:high_voltage]} high_voltage;}" if options[:high_voltage] microcode '' pin_list = ordered_pins.map(&:name).join(', ') # here indicate pattern header specific stuff yield pin_list if ordered_pins.size > 0 max_pin_name_length = ordered_pins.map(&:name).max { |a, b| a.length <=> b.length }.length pin_widths = ordered_pins.map { |p| p.size - 1 } max_pin_name_length.times do |i| cc((' ' * 93) + ordered_pins.map.with_index { |p, x| ((p.name[i] || ' ') + ' ' * pin_widths[x]).gsub('_', '-') }.join(' ')) end end end # An internal method called by Origen to generate the pattern footer def pattern_footer(options = {}) options = { subroutine_pat: false, end_in_ka: false, end_with_halt: false, end_module: true }.merge(options) $tester.align_to_last # cycle(:microcode => "#{$dut.end_of_pattern_label}:") if $dut.end_of_pattern_label if options[:end_in_ka] keep_alive(options) else if options[:end_with_halt] $tester.cycle microcode: 'halt' else if options[:end_module] $tester.cycle microcode: 'end_module' unless options[:subroutine_pat] else $tester.cycle end end end microcode '}' end # Returns an array of subroutines called while generating the current pattern def called_subroutines @called_subroutines ||= [] end # Returns an array of subroutines created by the current pattern def local_subroutines # :nodoc: @local_subroutines ||= [] end # This is an internal method use by Origen which returns a fully formatted vector # You can override this if you wish to change the output formatting at vector level def format_vector(vec) timeset = vec.timeset ? "> #{vec.timeset.name}" : '' pin_vals = vec.pin_vals ? "#{vec.pin_vals} ;" : '' if vec.repeat > 1 microcode = "repeat #{vec.repeat}" else microcode = vec.microcode ? vec.microcode : '' end if vec.pin_vals && ($_testers_enable_vector_comments || vector_comments) comment = " // #{vec.number}:#{vec.cycle} #{vec.inline_comment}" else comment = vec.inline_comment.empty? ? '' : " // #{vec.inline_comment}" end "#{microcode.ljust(65)}#{timeset.ljust(31)}#{pin_vals}#{comment}" end # Override this to force the formatting to match the v1 J750 model (easier diffs) def push_microcode(code) # :nodoc: stage.store(code.ljust(65) + ''.ljust(31)) end alias_method :microcode, :push_microcode # All vectors generated with the supplied block will have all pins set # to the repeat previous state. Any pins that are changed state within # the block will still update to the supplied value. # ==== Example # # All pins except invoke will be assigned the repeat previous code # # in the generated vector. On completion of the block they will # # return to their previous state, except for invoke which will # # retain the value assigned within the block. # $tester.repeat_previous do # $top.pin(:invoke).drive(1) # $tester.cycle # end def repeat_previous pinmap = Origen.pin_bank.pins pinmap.each { |id, pin| pin.repeat_previous = true } yield pinmap.each { |id, pin| pin.repeat_previous = false } end def ignore_fails(*pins) pins.each(&:suspend) yield pins.each(&:resume) end def enable_flag(options = {}) options = { flagnum: 4, # default flag to use }.merge(options) if options[:flagnum] > @flags.length abort "ERROR! Invalid flag value passed to 'enable_flag' method!\n" end flagname = @flags[options[:flagnum] - 1] update_vector(microcode: "#{@microcode[:enable]}(#{flagname})") end def set_flag(options = {}) options = { flagnum: 4, # default flag to use }.merge(options) if options[:flagnum] > @flags.length abort "ERROR! Invalid flag value passed to 'set_flag' method!\n" end flagname = @flags[options[:flagnum] - 1] update_vector(microcode: "#{@microcode[:set_flag]}(#{flagname})") end def cycle(options = {}) # handle overlay if requested ovly_style = nil if options.key?(:overlay) ovly_style = options[:overlay][:overlay_style].nil? ? @overlay_style : options[:overlay][:overlay_style] overlay_str = options[:overlay][:overlay_str] # route the overlay request to the appropriate method case ovly_style when :subroutine, :default subroutine_overlay(overlay_str, options) ovly_style = :subroutine when :label options[:dont_compress] = true unless @overlay_history.key?(overlay_str) label "#{overlay_str}", true @overlay_history[overlay_str] = { is_label: true } end when :digsrc if ultraflex? cur_pin_state = options[:overlay][:pins].state.to_sym options[:overlay][:pins].drive_mem options = digsrc_overlay(options) else ovly_style = overlay_style_warn(options[:overlay][:overlay_str], options) end else ovly_style = overlay_style_warn(options[:overlay][:overlay_str], options) end # case ovly_style else @overlay_subr = nil end # of handle overlay options_overlay = options.delete(:overlay) if options.key?(:overlay) unless ovly_style == :subroutine if @mask_vector # tack on masking opcodes super(options.merge(microcode: "#{options[:microcode]} #{@microcode[:mask_vector]}")) else super(options) end end # unless ovly_style unless options_overlay.nil? options_overlay[:pins].state = cur_pin_state if ovly_style == :digsrc # stage = :body if ovly_style == :subroutine # always set stage back to body in case subr overlay was selected end end # Warn user of unsupported overlay style def overlay_style_warn(overlay_str, options) Origen.log.warn("Unrecognized overlay style :#{@overlay_style}, defaulting to subroutine") Origen.log.warn('Available overlay styles :label, :subroutine') if j750? || j750_hpt? Origen.log.warn('Available overlay styles :digsrc, :digsrc_subroutine, :label, :subroutine') if ultraflex? subroutine_overlay(overlay_str, options) @overlay_style = :subroutine # Just give 1 warning end # Call this method at the start of any digsrc overlay operations, this method # takes care of all the microcodes and delays that's needed for digsrc overlay # Required arguments: # pins # Optionsal arguments: # options[:dssc_mode] = :single or :dual, anything else wil be # treated as if it's operating in :quad mode def digsrc_start(pins, options = {}) options = { dssc_mode: :single # defaults dssc_mode to single mode }.merge(options) if pins.size > 1 microcode "((#{format_multiple_instrument_pins(pins)}):DigSrc = Start)" else microcode "((#{pin}):DigSrc = Start)" end if options[:dssc_mode] == :single $tester.cycle(repeat: 145) # minimum of 144 cycles, adding 1 for safey measures elsif options[:dssc_mode] == :dual $tester.cycle(repeat: 289) # minimum of 288 cycles, adding 1 for safety measures else $tester.cycle(repeat: 577) # minimum of 577 cycles, adding 1 for safety measures end end # Call this method at the end of each digscr overlay operation to clear the pattern # memory pipeline, so that the pattern is ready to do the next digsrc overlay operation. # Required arguments: # pins def digsrc_stop(pins, options = {}) if pins.size > 1 microcode "((#{format_multiple_instrument_pins(pins)}):DigSrc = Stop)" else microcode "((#{pins}):DigSrc = Stop)" end end # Call this method before each tester cycle to insert the digsrc overlay microcode def digsrc_send(pins) microcode "((#{format_multiple_instrument_pins(pins)}):DigSrc = SEND)" end # Call this method before each tester cycle to inser the digcap overlay microcode def digcap_store(pins) microcode "((#{format_multiple_instrument_pins(pins)}):DigCap = STORE)" end def apply_digsrc_settings(options = {}) options.merge!(digsrc_width: 1) if options[:digsrc_width].nil? # default to digsrc 1 options.merge!(digsrc_bit_order: :lsb) if options[:digsrc_bit_order].nil? # default to lsb options.merge!(digsrc_mode: :serial) if options[:digsrc_mode].nil? # default to serial mode options.merge!(digsrc_format: :binary) if options[:digsrc_format].nil? # default to binary options.merge!(digsrc_site_uniqueness: :unique_sites) if options[:digsrc_site_uniqueness].nil? # default to unique sites options.merge!(digsrc_data_type: :default) if options[:digsrc_data_type].nil? # default to default options.merge!(digsrc_auto_cond: :auto_cond_disable) if options[:digsrc_auto_cond].nil? # default to disable @digsrc_settings = options end def apply_digcap_settings(options = {}) options.merge!(digcap_width: 8) if options[:digcap_width].nil? # default to digcap 8 options.merge!(digcap_bit_order: :lsb) if options[:digcap_bit_order].nil? # default to lsb options.merge!(digcap_mode: :serial) if options[:digcap_mode].nil? # default to serial mode options.merge!(digcap_site_uniqueness: :unique_sites) if options[:digcap_site_uniqueness].nil? # default to unique sites options.merge!(digcap_format: :binary) if options[:digcap_format].nil? # default to binary options.merge!(digcap_data_type: :default) if options[:digcap_data_type].nil? # default to default options.merge!(digcap_auto_cond: :auto_cond_disable) if options[:digcap_auto_cond].nil? # default to disable options.merge!(digcap_auto_trig_enable: :auto_trig_disable) if options[:digcap_auto_trig_enable].nil? # default to disable options.merge!(digcap_store_stv: :store_stv_disable) if options[:digcap_store_stv].nil? # default to disable options.merge!(digcap_receive_data: :store_stv_disable) if options[:digcap_receive_data].nil? # default to logic @digcap_settings = options end # Call this method if there are more than one pin/pin groups used with an instrument, # this method will format an array of pins into the correct format required by igxl # pattern microcodes. def format_multiple_instrument_pins(pins, options = {}) return_value = '' pins.each do |pin| return_value += "#{pin}" return_value += ',' unless pins.last == pin end return_value end def mask_fails(setclr) @mask_vector = setclr end # Similar to push_microcode, but for the instrument statement in the pattern header # # @example # tester.push_instrument 'SAR_IN_1', 'UltraSource' # # results in the below line added # # instruments = { # # SAR_IN_1:UltraSource; # # } def push_instrument(pin_spec, instrument_def) @pushed_instrument_configs[pin_spec] = instrument_def end # Implement subroutine overlay, called by tester.cycle def subroutine_overlay(sub_name, options = {}) if @overlay_subr != sub_name # unless last staged vector already has the subr call do the following i = -1 i -= 1 until stage.bank[i].is_a?(OrigenTesters::Vector) if stage.bank[i].microcode !~ /call #{sub_name}/ # check for repeat on new last vector, unroll 1 if needed if stage.bank[i].repeat > 1 v = OrigenTesters::Vector.new v.pin_vals = stage.bank[i].pin_vals v.timeset = stage.bank[i].timeset stage.bank[i].repeat -= 1 stage.store(v) i = -1 end # mark last vector as dont_compress stage.bank[i].dont_compress = true # insert subroutine call call_subroutine sub_name end # if microcode not placed @overlay_subr = sub_name end # stage = sub_name end # subroutine_overlay # Perform digsrc overlay (called by tester.cycle) def digsrc_overlay(options = {}) options[:overlay] = { change_data: true }.merge(options[:overlay]) pin_name = dut.pin(options[:overlay][:pins]).name repeat_count = options[:repeat].nil? ? 1 : options[:repeat] if options[:overlay][:change_data] # add the send microcode microcode "((#{pin_name}):DigSrc = SEND)" # keep track of amount of digsrc used for header comment if @overlay_history[pin_name].nil? @overlay_history[pin_name] = { count: repeat_count, is_digsrc: true } else @overlay_history[pin_name][:count] += repeat_count end # ensure no unwanted repeats on the send vector options[:dont_compress] = true # ensure start microcode is placed at the beginning of the pattern if @overlay_history[pin_name][:start_applied].nil? # insert start microcode at the beginning of the pattern stage.insert_from_start "((#{pin_name}):DigSrc = Start)", 0 @overlay_history[pin_name][:start_applied] = true # get the first vector of the pattern i = 0 i += 1 until stage.bank[i].is_a?(OrigenTesters::Vector) first_vector = stage.bank[i] # insert a copy of the first vector with no repeats unless first_vector.inline_comment == 'added for digsrc start opcode' v = OrigenTesters::Vector.new v.pin_vals = stage.bank[i].pin_vals v.timeset = stage.bank[i].timeset v.inline_comment = 'added for digsrc start opcode' v.dont_compress = true stage.insert_from_start v, i # decrement repeat count of previous first vector if > 1 first_vector.repeat -= 1 if first_vector.repeat > 1 end # get cycle count up to this point, add repeat to beginning if needed cycle_count = -1 stage.bank.each { |v| cycle_count += v.repeat if v.is_a?(OrigenTesters::Vector) } if cycle_count < @dssc_send_delay d = OrigenTesters::Vector.new d.pin_vals = first_vector.pin_vals d.timeset = first_vector.timeset d.inline_comment = 'added for dssc start to send delay' d.repeat = @dssc_send_delay - cycle_count # get the first vector of the pattern i = 0 i += 1 until stage.bank[i].is_a?(OrigenTesters::Vector) # insert new vector after the first vector stage.insert_from_start d, i + 1 end end # of place start microcode end # if options[:change_data] options end # digsrc overlay end end end