Module: OrigenTesters::Timing
- Extended by:
- ActiveSupport::Concern
- Defined in:
- lib/origen_testers/timing.rb
Defined Under Namespace
Classes: Timeset
Instance Method Summary collapse
- #_if_dut ⇒ Object private
- #before_timeset_change(options = {}) ⇒ Object
- #called_timesets ⇒ Object
-
#count(options = {}) ⇒ Object
This function can be used to generate a clock or some other repeating function that spans accross a range of vectors.
- #current_period_in_ns ⇒ Object (also: #current_period, #period)
- #current_timeset ⇒ Object (also: #timeset)
-
#cycles_to_time(cycles) ⇒ Object
Convert the supplied number of cycles to a time, based on the SoC defined cycle period.
-
#delay(cycles, options = {}) ⇒ Object
private
This should not be called directly, call via tester#wait.
- #max_repeat_loop ⇒ Object
-
#min_period_timeset ⇒ Object
Returns the timeset (a Timeset object) with the shortest period that has been encountered so far in the course of generating the current pattern.
- #min_repeat_loop ⇒ Object
-
#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.
- #timeset_changed(timeset) ⇒ Object
-
#timing_toggled_pins ⇒ Object
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.
-
#wait(options = {}) ⇒ Object
Cause the pattern to wait.
Instance Method Details
#_if_dut ⇒ 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.
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( = {}) end |
#called_timesets ⇒ Object
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( = {}) = { 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() period_cycles = [:period_in_cycles] + ms_to_cycles([:period_in_ms]) + us_to_cycles([:period_in_us]) + ns_to_cycles([:period_in_ns]) duration_cycles = [:duration_in_cycles] + ms_to_cycles([:duration_in_ms]) + us_to_cycles([:duration_in_us]) + ns_to_cycles([: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_ns ⇒ Object 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_timeset ⇒ Object 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
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, = {}) (cycles / max_repeat_loop).times do if block_given? yield .merge(repeat: max_repeat_loop) else cycle(.merge(repeat: max_repeat_loop)) end end if block_given? yield .merge(repeat: (cycles % max_repeat_loop)) else cycle(.merge(repeat: (cycles % max_repeat_loop))) end end |
#max_repeat_loop ⇒ Object
282 283 284 |
# File 'lib/origen_testers/timing.rb', line 282 def max_repeat_loop @max_repeat_loop || 65_535 end |
#min_period_timeset ⇒ Object
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_loop ⇒ Object
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_pins ⇒ Object
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
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( = {}) = { 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() cycles = 0 cycles += [:cycles] + [:time_in_cycles] cycles += s_to_cycles([:time_in_s]) cycles += ms_to_cycles([:time_in_ms]) cycles += us_to_cycles([:time_in_us]) cycles += ns_to_cycles([: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 [:match]}#{time}ns" when time < 1_000_000 # When less than 1ms cc "Wait for #{'a maximum of ' if [: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 [:match]}#{(time.to_f / 1_000_000).round(1)}ms" else cc "Wait for #{'a maximum of ' if [: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 [:match] if block_given? match_block(cycles, ) { yield } else match([:pin], [:state], cycles, ) end else delay(cycles) end end end |