Class: OrigenTesters::SmartestBasedTester::Base

Inherits:
Object
  • Object
show all
Includes:
VectorBasedTester
Defined in:
lib/origen_testers/smartest_based_tester/base.rb,
lib/origen_testers/smartest_based_tester/base/flow.rb,
lib/origen_testers/smartest_based_tester/base/generator.rb,
lib/origen_testers/smartest_based_tester/base/test_suite.rb,
lib/origen_testers/smartest_based_tester/base/test_method.rb,
lib/origen_testers/smartest_based_tester/base/test_suites.rb,
lib/origen_testers/smartest_based_tester/base/test_methods.rb,
lib/origen_testers/smartest_based_tester/base/pattern_master.rb,
lib/origen_testers/smartest_based_tester/base/variables_file.rb,
lib/origen_testers/smartest_based_tester/base/pattern_compiler.rb,
lib/origen_testers/smartest_based_tester/base/test_methods/ac_tml.rb,
lib/origen_testers/smartest_based_tester/base/test_methods/dc_tml.rb,
lib/origen_testers/smartest_based_tester/base/test_methods/limits.rb,
lib/origen_testers/smartest_based_tester/base/test_methods/base_tml.rb,
lib/origen_testers/smartest_based_tester/base/test_methods/custom_tml.rb

Direct Known Subclasses

V93K

Defined Under Namespace

Modules: Generator Classes: Flow, PatternCompiler, PatternMaster, TestMethod, TestMethods, TestSuite, TestSuites, VariablesFile

Instance Method Summary collapse

Methods included from VectorBasedTester

#register_tester

Constructor Details

#initializeBase

Returns a new J750 instance, normally there would only ever be one of these assigned to the global variable such as $tester by your target:

$tester = J750.new


9
10
11
12
13
14
15
16
17
18
19
# File 'lib/origen_testers/smartest_based_tester/base.rb', line 9

def initialize
  @max_repeat_loop = 65_535
  @min_repeat_loop = 17
  @pat_extension = 'avc'
  @compress = true
  # @support_repeat_previous = true
  @match_entries = 10
  @name = 'v93k'
  @comment_char = '#'
  @level_period = true
end

Instance Method Details

#before_timeset_change(options = {}) ⇒ Object



428
429
430
# File 'lib/origen_testers/smartest_based_tester/base.rb', line 428

def before_timeset_change(options = {})
  microcode "SQPG CTIM #{options[:new].name};" unless level_period?
end

#call_subroutine(name, options = {}) ⇒ Object

Call a subroutine.

This calls a subroutine immediately following 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.

Repeated calls to the same subroutine will automatically be compressed unless option :suppress_repeated_calls is supplied and set to false. This means that for the common use case of calling a subroutine to implement an overlay the subroutine can be called for every bit that has the overlay and the pattern will automatically generate correctly.

Examples

$tester.call_subroutine("mysub")
$tester.call_subroutine("my_other_sub", :offset => -1)


141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/origen_testers/smartest_based_tester/base.rb', line 141

def call_subroutine(name, options = {})
  options = {
    offset:                  0,
    suppress_repeated_calls: true
  }.merge(options)
  called_subroutines << name.to_s.chomp unless called_subroutines.include?(name.to_s.chomp) || @inhibit_vectors

  code = "SQPG JSUB #{name};"
  if !options[:suppress_repeated_calls] ||
     last_object != code
    microcode code, offset: (options[:offset] * -1)
  end
end

#called_subroutinesObject

Returns an array of subroutines called while generating the current pattern



369
370
371
# File 'lib/origen_testers/smartest_based_tester/base.rb', line 369

def called_subroutines
  @called_subroutines ||= []
end

#end_subroutine(_cond = false) ⇒ Object

Ends the current subroutine that was started with a previous call to start_subroutine



117
118
119
# File 'lib/origen_testers/smartest_based_tester/base.rb', line 117

def end_subroutine(_cond = false)
  Pattern.close call_shutdown_callbacks: false, subroutine: true
end

#format_vector(vec) ⇒ Object

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



380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
# File 'lib/origen_testers/smartest_based_tester/base.rb', line 380

def format_vector(vec)
  timeset = vec.timeset ? "#{vec.timeset.name}" : ''
  pin_vals = vec.pin_vals ? "#{vec.pin_vals} " : ''
  if vec.repeat # > 1
    microcode = "R#{vec.repeat}"
  else
    microcode = vec.microcode ? vec.microcode : ''
  end
  header_comments = ''
  vec.comments.each do |comment|
    if comment.include? '#'
      comment = comment.gsub(/# /, '')
      comment = comment.gsub(/#/, '')
      if comment != ''
        header_comments << comment + "\cm"
      end
    end
  end
  if vec.pin_vals && ($_testers_enable_vector_comments || vector_comments)
    comment = "#{header_comments}#{vec.number}:#{vec.cycle} #{vec.inline_comment}"
  else
    inline = vec.inline_comment.empty? ? '' : " # #{vec.inline_comment}"
    comment = " #{header_comments}#{inline}"
  end
  if Origen.mode.simulation? || $_testers_no_inline_comments
    comment = ''
  end
  "#{microcode.ljust(25)}#{timeset.ljust(27)}#{pin_vals}#{comment};"
end

#freq_count(_pin, options = {}) ⇒ Object

Do a frequency measure.

Examples

$tester.freq_count($top.pin(:d_out))                 # Freq measure on pin "d_out"


169
170
171
172
173
# File 'lib/origen_testers/smartest_based_tester/base.rb', line 169

def freq_count(_pin, options = {})
  options = {
  }.merge(options)
  Pattern.split(options)
end

#handshake(options = {}) ⇒ Object

Handshake with the tester.

Examples

$tester.handshake                   # Pass control to the tester for a measurement


159
160
161
162
163
# File 'lib/origen_testers/smartest_based_tester/base.rb', line 159

def handshake(options = {})
  options = {
  }.merge(options)
  Pattern.split(options)
end

#local_subroutinesObject

Returns an array of subroutines created by the current pattern



374
375
376
# File 'lib/origen_testers/smartest_based_tester/base.rb', line 374

def local_subroutines # :nodoc:
  @local_subroutines ||= []
end

#loop_vectors(name = nil, number_of_loops = 1, _global = false) ⇒ Object Also known as: loop_vector

Add a loop to the pattern.

Pass in the number of times to execute it, all vectors generated by the given block will be captured in the loop.

Examples

$tester.loop_vectors 3 do   # Do this 3 times...
    $tester.cycle
    some_other_method_to_generate_vectors
end

For compatibility with the J750 you can supply a name as the first argument and that will simply be ignored when generated for the V93K tester…

$tester.loop_vectors "my_loop", 3 do   # Do this 3 times...
    $tester.cycle
    some_other_method_to_generate_vectors
end


317
318
319
320
321
322
323
324
325
326
327
328
329
# File 'lib/origen_testers/smartest_based_tester/base.rb', line 317

def loop_vectors(name = nil, number_of_loops = 1, _global = false)
  # The name argument is present to maych J750 API, sort out the
  unless name.is_a?(String)
    name, number_of_loops, global = nil, name, number_of_loops
  end
  if number_of_loops > 1
    microcode "SQPG LBGN #{number_of_loops};"
    yield
    microcode 'SQPG LEND;'
  else
    yield
  end
end

#match(pin, state, timeout_in_cycles, options = {}) ⇒ Object

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)

  • :check_for_fails (false) - Flushes the pipeline and checks for fails prior to the match (to allow binout of fails encountered before the match)

  • :pin2 (nil) - Optionally supply a second pin to match on

  • :state2 (nil) - State for the second pin (required if :pin2 is supplied)

  • :force_fail_on_timeout (true) - Force a vector mis-compare if the match loop times out

Examples

$tester.wait(:match => true, :time_in_us => 5000, :pin => $top.pin(:done), :state => :high)


194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
# File 'lib/origen_testers/smartest_based_tester/base.rb', line 194

def match(pin, state, timeout_in_cycles, options = {})
  options = {
    check_for_fails:       false,
    pin2:                  false,
    state2:                false,
    global_loops:          false,
    generate_subroutine:   false,
    force_fail_on_timeout: true
  }.merge(options)

  # Ensure the match pins are don't care by default
  pin.dont_care
  options[:pin2].dont_care if options[:pin2]

  # Single condition loops are simple
  if !options[:pin2]
    # Use the counted match loop (rather than timed) which is recommended in the V93K docs for new applications
    # No pre-match failure handling is required here because the system will cleanly record failure info
    # for this kind of match loop
    cc "for the #{pin.name.upcase} pin to go #{state.to_s.upcase}"
    # Need to ensure at least 8 cycles with no compares before entering
    8.cycles
    number_of_loops = (timeout_in_cycles.to_f / 72).ceil
    # This seems to be a limit on the max MACT value, so account for longer times by expanding
    # the wait loop
    if number_of_loops > 262_144
      mrpt = ((timeout_in_cycles.to_f / 262_144) - 8).ceil
      mrpt = Math.sqrt(mrpt).ceil
      mrpt += (8 - (mrpt % 8)) # Keep to a multiple of 8, but round up to be safe
      number_of_loops = 262_144
    else
      mrpt = 8
    end
    microcode "SQPG MACT #{number_of_loops};"
    # Strobe the pin for the required state
    state == :low ? pin.expect_lo : pin.expect_hi
    # Always do 8 vectors here as this allows reconstruction of test results if multiple loops
    # are called in a pattern
    8.cycles
    pin.dont_care
    # Now do the wait loop, mrpt should always be a multiple of 8
    microcode "SQPG MRPT #{mrpt};"
    mrpt.times do
      cycle(dont_compress: true)
    end
    microcode 'SQPG PADDING;'
    8.cycles

  else

    # For two pins do something more like the J750 approach where branching based on miscompares is used
    # to keep the loop going
    cc "for the #{pin.name.upcase} pin to go #{state.to_s.upcase}"
    cc "or the #{options[:pin2].name.upcase} pin to go #{options[:state2].to_s.upcase}"

    if options[:check_for_fails]
      cc 'Return preserving existing errors if the pattern has already failed before arriving here'
      cycle(repeat: propagation_delay)
      microcode 'SQPG RETC 1 1;'
    end
    number_of_loops = (timeout_in_cycles.to_f / ((propagation_delay * 2) + 2)).ceil

    loop_vectors number_of_loops do
      # Check pin 1
      cc "Check if #{pin.name.upcase} is #{state.to_s.upcase} yet"
      state == :low ? pin.expect_lo! : pin.expect_hi!
      pin.dont_care
      cc 'Wait for failure to propagate'
      cycle(repeat: propagation_delay)
      cc 'Exit match loop if pin has matched (no error), otherwise clear error and remain in loop'
      microcode 'SQPG RETC 0 0;'

      # Check pin 2
      cc "Check if #{options[:pin2].name.upcase} is #{options[:state2].to_s.upcase} yet"
      options[:state2] == :low ? options[:pin2].expect_lo! : options[:pin2].expect_hi!
      options[:pin2].dont_care
      cc 'Wait for failure to propagate'
      cycle(repeat: propagation_delay)
      cc 'Exit match loop if pin has matched (no error), otherwise clear error and remain in loop'
      microcode 'SQPG RETC 0 0;'
    end

    if options[:force_fail_on_timeout]
      cc 'To get here something has gone wrong, strobe again to force a pattern failure'
      state == :low ? pin.expect_lo : pin.expect_hi
      options[:state2] == :low ? options[:pin2].expect_lo : options[:pin2].expect_hi if options[:pin2]
      cycle
      pin.dont_care
      options[:pin2].dont_care if options[:pin2]
    end
  end
end

An internal method called by Origen to generate the pattern footer



364
365
366
# File 'lib/origen_testers/smartest_based_tester/base.rb', line 364

def pattern_footer(options = {})
  microcode 'SQPG STOP;' unless options[:subroutine]
end

#pattern_header(options = {}) ⇒ Object

An internal method called by Origen to create the pattern header



333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
# File 'lib/origen_testers/smartest_based_tester/base.rb', line 333

def pattern_header(options = {})
  options = {
  }.merge(options)
  pin_list = ordered_pins.map do |p|
    if Origen.app.pin_pattern_order.include?(p.id)
      # specified name overrides pin name
      if (p.is_a?(Origen::Pins::PinCollection)) || p.id != p.name
        p.id.to_s # groups or aliases can be lower case
      else
        p.id.to_s.upcase # pins must be uppercase
      end
    else
      if (p.is_a?(Origen::Pins::PinCollection)) || p.id != p.name
        p.name.to_s # groups or aliases can be lower case
      else
        p.name.to_s.upcase # pins must be uppercase
      end
    end
  end.join(' ')
  microcode "FORMAT #{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((' ' * 50) + ordered_pins.map.with_index { |p, x| ((p.name[i] || ' ') + ' ' * pin_widths[x]).gsub('_', '-') }.join(' '))
    end
  end
end

#propagation_delayObject

Returns the number of cycles to wait for any fails to propagate through the pipeline based on the current timeset



289
290
291
292
293
294
295
296
297
# File 'lib/origen_testers/smartest_based_tester/base.rb', line 289

def propagation_delay
  # From 'Calculating the buffer cycles for JMPE and RETC (and match loops)' in SmarTest docs
  data_queue_buffer = (([105, 64 + ((125 + current_period_in_ns - 1) / current_period_in_ns).ceil].min + 3) * 8) + 72
  # Don't know how to calculate at runtime, hardcoding these to some default values for now
  number_of_sites = 128
  sclk_period = 40
  prop_delay_buffer = 195 + ((2 * number_of_sites + 3) * (sclk_period / 2))
  data_queue_buffer + prop_delay_buffer
end

#repeat_previousObject

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


422
423
424
425
426
# File 'lib/origen_testers/smartest_based_tester/base.rb', line 422

def repeat_previous
  Origen.app.pin_map.each { |_id, pin| pin.repeat_previous = true }
  yield
  Origen.app.pin_map.each { |_id, pin| pin.repeat_previous = false }
end

#start_subroutine(name) ⇒ Object

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


110
111
112
113
114
# File 'lib/origen_testers/smartest_based_tester/base.rb', line 110

def start_subroutine(name)
  local_subroutines << name.to_s.chomp unless local_subroutines.include?(name.to_s.chomp) || @inhibit_vectors
  # name += "_subr" unless name =~ /sub/
  Pattern.open name: name, call_startup_callbacks: false, subroutine: true
end

#store(*pins) ⇒ Object Also known as: capture

Capture the pin data from a vector to the tester.

This method uses the Digital Capture feature (Selective mode) of the V93000 to capture the data from the given pins on the previous vector. Note that is does not actually generate a new vector.

Note also that any drive cycles on the target pins can also be captured, to avoid this the wavetable should be set up like this to infer a ‘D’ (Don’t Capture) on vectors where the target pin is being used to drive data:

PINS nvm_fail
0  d1:0  r1:D  0
1  d1:1  r1:D  1
2  r1:C  Capt
3  r1:D  NoCapt

Sometimes when generating vectors within a loop you may want to apply a capture retrospectively to a previous vector, passing in an offset option will allow you to do this.

Examples

$tester.cycle                     # This is the vector you want to capture
$tester.store :pin => pin(:fail)  # This applys the required opcode to the given pins

$tester.cycle                     # This one gets captured
$tester.cycle
$tester.cycle
$tester.store(:pin => pin(:fail), :offset => -2) # Just realized I need to capture that earlier vector

# Capturing multiple pins:
$tester.cycle
$tester.store :pins => [pin(:fail), pin(:done)]

Since the V93K store operates on a pin level (rather than vector level as on the J750) equivalent functionality can also be achieved by setting the store attribute of the pin itself prior to calling $tester.cycle. However it is recommended to use the tester API to do the store if cross-compatiblity with other platforms, such as the J750, is required.



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/origen_testers/smartest_based_tester/base.rb', line 59

def store(*pins)
  options = pins.last.is_a?(Hash) ? pins.pop : {}
  options = { offset: 0
            }.merge(options)
  pins = pins.flatten.compact
  if pins.empty?
    fail 'For the V93K you must supply the pins to store/capture'
  end
  pins.each do |pin|
    pin.restore_state do
      pin.capture
      update_vector_pin_val pin, offset: options[:offset]
      last_vector(options[:offset]).dont_compress = true
      last_vector(options[:offset]).contains_capture = true
    end
  end
end

#store_next_cycle(*pins) ⇒ Object

Same as the store method, except that the capture will be applied to the next vector to be generated.

Examples:

$tester.store_next_cycle
$tester.cycle                # This is the vector that will be captured


84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/origen_testers/smartest_based_tester/base.rb', line 84

def store_next_cycle(*pins)
  options = pins.last.is_a?(Hash) ? pins.pop : {}
  options = {
  }.merge(options)
  pins = pins.flatten.compact
  if pins.empty?
    fail 'For the V93K you must supply the pins to store/capture'
  end
  pins.each { |pin| pin.save; pin.capture }
  # Register this clean up function to be run after the next vector
  # is generated, cool or what!
  preset_next_vector do |vector|
    vector.contains_capture = true
    pins.each(&:restore)
  end
end