Module: OrigenTesters::Timing

Extended by:
ActiveSupport::Concern
Defined in:
lib/origen_testers/timing.rb

Defined Under Namespace

Classes: Timeset

Instance Method Summary collapse

Instance Method Details

#_if_dutObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



171
172
173
174
175
176
177
178
# File 'lib/origen_testers/timing.rb', line 171

def _if_dut
  if dut
    true
  else
    Origen.log.warning 'It looks like you are calling tester.set_timeset before the DUT is instantiated, you should avoid doing that until the dut object is available'
    false
  end
end

#before_timeset_change(options = {}) ⇒ Object



202
203
# File 'lib/origen_testers/timing.rb', line 202

def before_timeset_change(options = {})
end

#called_timesetsObject



290
291
292
# File 'lib/origen_testers/timing.rb', line 290

def called_timesets
  @called_timesets ||= []
end

#count(options = {}) ⇒ Object

This function can be used to generate a clock or some other repeating function that spans accross a range of vectors. The period of each cycle and the duration of the sequence are supplied via the following options:

  • :period_in_cycles

  • :period_in_ns

  • :period_in_us

  • :period_in_ms

  • :duration_in_cycles

  • :duration_in_ns

  • :duration_in_us

  • :duration_in_ms

If multiple definitions for either option are supplied then they will be added together.

Example

# Supply a clock pulse on :pinA for 100ms
$tester.count(:period_in_cycles => 10, :duration_in_ms => 100) do
    $top.pin(:pinA).drive!(1)
    $top.pin(:pinA).drive!(0)
end


334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
# File 'lib/origen_testers/timing.rb', line 334

def count(options = {})
  options = { period_in_cycles: 0, period_in_ms: 0, period_in_us: 0, period_in_ns: 0,
              duration_in_cycles: 0, duration_in_ms: 0, duration_in_us: 0, duration_in_ns: 0
            }.merge(options)

  period_cycles = options[:period_in_cycles] + ms_to_cycles(options[:period_in_ms]) +
                  us_to_cycles(options[:period_in_us]) + ns_to_cycles(options[:period_in_ns])

  duration_cycles = options[:duration_in_cycles] + ms_to_cycles(options[:duration_in_ms]) +
                    us_to_cycles(options[:duration_in_us]) + ns_to_cycles(options[:duration_in_ns])

  total = 0
  while total < duration_cycles
    wait(time_in_cycles: period_cycles)
    yield								# Return control back to caller
    total += period_cycles
  end
end

#current_period_in_nsObject Also known as: current_period, period



294
295
296
297
298
299
300
# File 'lib/origen_testers/timing.rb', line 294

def current_period_in_ns
  if @timeset
    @timeset.period_in_ns
  else
    fail 'No timeset has been specified yet!'
  end
end

#current_timesetObject Also known as: timeset



304
305
306
# File 'lib/origen_testers/timing.rb', line 304

def current_timeset
  @timeset
end

#cycles_to_time(cycles) ⇒ Object

Convert the supplied number of cycles to a time, based on the SoC defined cycle period



310
311
312
# File 'lib/origen_testers/timing.rb', line 310

def cycles_to_time(cycles) # :nodoc:
  (cycles * current_period_in_ns).to_f / 1_000_000_000
end

#delay(cycles, options = {}) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This should not be called directly, call via tester#wait

See Also:



267
268
269
270
271
272
273
274
275
276
277
278
279
280
# File 'lib/origen_testers/timing.rb', line 267

def delay(cycles, options = {})
  (cycles / max_repeat_loop).times do
    if block_given?
      yield options.merge(repeat: max_repeat_loop)
    else
      cycle(options.merge(repeat: max_repeat_loop))
    end
  end
  if block_given?
    yield options.merge(repeat: (cycles % max_repeat_loop))
  else
    cycle(options.merge(repeat: (cycles % max_repeat_loop)))
  end
end

#max_repeat_loopObject



282
283
284
# File 'lib/origen_testers/timing.rb', line 282

def max_repeat_loop
  @max_repeat_loop || 65_535
end

#min_period_timesetObject

Returns the timeset (a Timeset object) with the shortest period that has been encountered so far in the course of generating the current pattern.

A tester object is re-instantiated at the start of every pattern which will reset this variable.



185
186
187
# File 'lib/origen_testers/timing.rb', line 185

def min_period_timeset
  @min_period_timeset
end

#min_repeat_loopObject



286
287
288
# File 'lib/origen_testers/timing.rb', line 286

def min_repeat_loop
  @min_repeat_loop
end

#set_timeset(timeset, period_in_ns = nil) ⇒ Object

Set the timeset for the next vectors, this will remain in place until the next time this is called.

$tester.set_timeset("bist_25mhz", 40)

This method also accepts a block in which case the contained vectors will generate with the supplied timeset and subsequent vectors will return to the previous timeset automatically.

$tester.set_timeset("bist_25mhz", 40) do
  $tester.cycle
end

The arguments can also be supplied as a single array, or not at all. In the latter case the existing timeset will simply be preserved. This is useful if you have timesets that can be conditionally set based on the target.

# Target 1
$soc.readout_timeset = ["readout", 120]
# Target 2
$soc.readout_timeset = false

# This code is compatible with both targets, in the first case the timeset will switch
# over, in the second case the existing timeset will be preserved.
$tester.set_timeset($soc.readout_timeset) do
  $tester.cycle
end


130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/origen_testers/timing.rb', line 130

def set_timeset(timeset, period_in_ns = nil)
  if timeset.is_a?(Array)
    timeset, period_in_ns = timeset[0], timeset[1]
  end
  timeset ||= @timeset
  unless timeset.is_a?(Timeset)
    fail 'You must supply a period_in_ns argument to set_timeset' unless period_in_ns
    timeset = Timeset.new(name: timeset.to_s.chomp, period_in_ns: period_in_ns)
  end
  called_timesets << timeset unless called_timesets.map(&:name).include?(timeset.name)
  if @min_period_timeset
    @min_period_timeset = timeset if timeset.shorter_period_than?(@min_period_timeset)
  else
    @min_period_timeset = timeset
  end
  if block_given?
    original = @timeset
    timeset_changed(timeset)
    @timeset = timeset
    _if_dut do
      dut.timeset = timeset.name if dut.timesets[timeset.name]
      dut.current_timeset_period = timeset.period_in_ns
    end
    yield
    timeset_changed(original)
    @timeset = original
    _if_dut do
      dut.timeset = original.name if dut.timesets[original.name]
      dut.current_timeset_period = original.period_in_ns
    end
  else
    timeset_changed(timeset)
    @timeset = timeset
    _if_dut do
      dut.timeset = timeset.name if dut.timesets[timeset.name]
      dut.current_timeset_period = timeset.period_in_ns
    end
  end
end

#timeset_changed(timeset) ⇒ Object



189
190
191
192
193
194
195
196
197
198
199
200
# File 'lib/origen_testers/timing.rb', line 189

def timeset_changed(timeset)
  if last_vector && last_vector.timeset != timeset
    change = { old: last_vector.timeset, new: timeset }
    # Suppress any duplicate calls
    if !@_last_timeset_change ||
       (@_last_timeset_change[:new] != change[:new] &&
         @_last_timeset_change[:old] != change[:old])
      before_timeset_change(change)
    end
    @_last_timeset_change = change
  end
end

#timing_toggled_pinsObject

When period levelling is enabled, vectors will be expanded like this:

$tester.set_timeset("fast", 40)
2.cycles                        #    fast   1 0 0 1 0
                                #    fast   1 0 0 1 0
# Without levelling enabled
$tester.set_timeset("slow", 80)
2.cycles                        #    slow   1 0 0 1 0
                                #    slow   1 0 0 1 0
# With levelling enabled
$tester.set_timeset("slow", 80)
2.cycles                        #    fast   1 0 0 1 0
                                #    fast   1 0 0 1 0
                                #    fast   1 0 0 1 0
                                #    fast   1 0 0 1 0

The overall time of the levelled/expanded vectors matches that of the unlevelled case. i.e. 4 cycles at fast speed (4 * 40ns = 160ns) is equivalent to 2 cycles at slow speed (2 * 80ns = 160ns).

However, what if pin 1 in the example above was a clk pin where the 1 -> 0 transition was handled by the timing setup for that pin. In that case the levelled code is no longer functionally correct since it contains 4 clock pulses while the unlevelled code only has 2.

Such pins can be specified via this attribute and the levelling logic will then automatically adjust the drive state to keep the number of pulses correct. It would automatically adjust to the alternative logic state where 0 means ‘on’ and 1 means ‘off’ if applicable.

$tester.timing_toggled_pins << $dut.pin(:tclk)  # This is pin 1

$tester.set_timeset("fast", 40)
2.cycles                        #    fast   1 0 0 1 0
                                #    fast   1 0 0 1 0
# Without levelling enabled
$tester.set_timeset("slow", 80)
2.cycles                        #    slow   1 0 0 1 0
                                #    slow   1 0 0 1 0
# With levelling enabled
$tester.set_timeset("slow", 80)
2.cycles                        #    fast   1 0 0 1 0
                                #    fast   0 0 0 1 0
                                #    fast   1 0 0 1 0
                                #    fast   0 0 0 1 0

Multiple pins an be specified like this:

$tester.timing_toggled_pins = [$dut.pin(:tclk), $dut.pin(:clk)]   # Overrides any pins added elsewhere
$tester.timing_toggled_pins << [$dut.pin(:tclk), $dut.pin(:clk)]  # In addition to any pins added elsewhere

See Also:

  • Timing#level_period


97
98
99
100
101
# File 'lib/origen_testers/timing.rb', line 97

def timing_toggled_pins
  @timing_toggled_pins ||= []
  @timing_toggled_pins.flatten!
  @timing_toggled_pins
end

#wait(options = {}) ⇒ Object

Cause the pattern to wait. The following options are available to help you specify the time to wait:

  • :cycles - delays specified in raw cycles, the test model is responsible for translating this into a sequence of valid repeat statements

  • :time_in_ns - time specified in nano-seconds

  • :time_in_us - time specified in micro-seconds

  • :time_in_ms - time specified in milli-seconds

  • :time_in_s - time specified in seconds

If more than one option is supplied they will get added together to give a final delay time expressed in cycles.

Examples

$tester.wait(cycles: 100, time_in_ns: 200)   # Wait for 100 cycles + 200ns

This method can also be used to trigger a match loop in which case the supplied time becomes the time out for the match. See the J750#match method for full details of the available options.

$tester.wait(match: true, state: :high, pin: $dut.pin(:done), time_in_ms: 500)


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
# File 'lib/origen_testers/timing.rb', line 220

def wait(options = {})
  options = {
    cycles:         0,
    time_in_cycles: 0,
    time_in_us:     0,
    time_in_ns:     0,
    time_in_ms:     0,
    time_in_s:      0,
    match:          false,   # Set to true to invoke a match loop where the supplied delay
    # will become the timeout duration
  }.merge(options)

  cycles = 0
  cycles += options[:cycles] + options[:time_in_cycles]
  cycles += s_to_cycles(options[:time_in_s])
  cycles += ms_to_cycles(options[:time_in_ms])
  cycles += us_to_cycles(options[:time_in_us])
  cycles += ns_to_cycles(options[:time_in_ns])

  time = cycles * current_period_in_ns   # Total delay in ns
  case
    when time < 1000                      # When less than 1us
      cc "Wait for #{'a maximum of ' if options[:match]}#{time}ns"
    when time < 1_000_000                   # When less than 1ms
      cc "Wait for #{'a maximum of ' if options[:match]}#{(time.to_f / 1000).round(1)}us"        # Display delay in us
    when time < 1_000_000_000                # When less than 1s
      cc "Wait for #{'a maximum of ' if options[:match]}#{(time.to_f / 1_000_000).round(1)}ms"
    else
      cc "Wait for #{'a maximum of ' if options[:match]}%.2fs" % (time.to_f / 1_000_000_000)
  end

  if cycles > 0   # Allow this function to be called with 0 in which case it will just return
    if options[:match]
      if block_given?
        match_block(cycles, options) { yield }
      else
        match(options[:pin], options[:state], cycles, options)
      end
    else
      delay(cycles)
    end
  end
end