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

#before_timeset_change(options = {}) ⇒ Object



180
181
# File 'lib/origen_testers/timing.rb', line 180

def before_timeset_change(options = {})
end

#called_timesetsObject



268
269
270
# File 'lib/origen_testers/timing.rb', line 268

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


312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
# File 'lib/origen_testers/timing.rb', line 312

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



272
273
274
275
276
277
278
# File 'lib/origen_testers/timing.rb', line 272

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



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

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



288
289
290
# File 'lib/origen_testers/timing.rb', line 288

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:



245
246
247
248
249
250
251
252
253
254
255
256
257
258
# File 'lib/origen_testers/timing.rb', line 245

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



260
261
262
# File 'lib/origen_testers/timing.rb', line 260

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.



163
164
165
# File 'lib/origen_testers/timing.rb', line 163

def min_period_timeset
  @min_period_timeset
end

#min_repeat_loopObject



264
265
266
# File 'lib/origen_testers/timing.rb', line 264

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
# 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
    yield
    timeset_changed(original)
    @timeset = original
  else
    timeset_changed(timeset)
    @timeset = timeset
  end
end

#timeset_changed(timeset) ⇒ Object



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

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)


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

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