Class: OrigenJTAG::Driver

Inherits:
Object
  • Object
show all
Includes:
Origen::Model, TAPController
Defined in:
lib/origen_jtag/driver.rb

Overview

This driver provides methods to read and write from a JTAG instruction and data registers.

Low level methods are also provided for fine control of the TAP Controller state machine via the TAPController module.

To use this driver the parent model must define the following pins (an alias is fine):

:tck
:tdi
:tdo
:tms

Constant Summary collapse

REQUIRED_PINS =
[:tck, :tdi, :tdo, :tms]

Constants included from TAPController

TAPController::STATES

Instance Attribute Summary collapse

Attributes included from TAPController

#state

Instance Method Summary collapse

Methods included from TAPController

#idle, #pause_dr, #pause_ir, #reset, #shift_dr, #shift_ir, #state_str, #update_state

Constructor Details

#initialize(owner, options = {}) ⇒ Driver

Returns a new instance of Driver.



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/origen_jtag/driver.rb', line 48

def initialize(owner, options = {})
  if owner.is_a?(Hash)
    @owner = parent
    options = owner
  else
    @owner = owner
  end
  # The parent can configure JTAG settings by defining this constant
  if defined?(owner.class::JTAG_CONFIG)
    options = owner.class::JTAG_CONFIG.merge(options)
  end

  @cycle_callback = options[:cycle_callback]
  @given_options = options.dup  # Save these for later use in the pins method

  # Fallback defaults
  options = {
    verbose:         false,
    tdo_store_cycle: 0,                # store vector cycle within TCK (i.e. when to indicate to tester to store vector within TCK cycle.  0 is first vector, 1 is second, etc.)
    # NOTE: only when user indicates to store TDO, which will mean we don't care the 1 or 0 value on TDO (overriding effectively :tdo_strobe option above)
    init_state:      :unknown
  }.merge(options)

  init_tap_controller(options)

  @verbose = options[:verbose]
  @ir_value = :unknown
  @tck_format = options[:tck_format] || options[:tclk_format] || :rh
  @tck_multiple = options[:tck_multiple] || options[:tclk_multiple] || 1
  self.tdo_strobe = options[:tdo_strobe] || :tck_high
  @tdo_store_cycle = options[:tdo_store_cycle]
  @state = options[:init_state]
  @log_state_changes = options[:log_state_changes] || false
  if options[:tck_vals] || options[:tclk_vals]
    @tck_vals = options[:tck_vals] || options[:tclk_vals]
    unless @tck_vals.is_a?(Hash) && @tck_vals.key?(:on) && @tck_vals.key?(:off)
      fail "When specifying TCK values, you must supply a hash with both :on and :off keys, e.g. tck_vals: { on: 'P', off: 0 }"
    end
  end
  if @cycle_callback && @tck_multiple != 1
    fail 'A cycle_callback can only be used with a tck_multiple setting of 1'
  end
end

Instance Attribute Details

#ir_valueObject (readonly)

Returns the current value in the instruction register



24
25
26
# File 'lib/origen_jtag/driver.rb', line 24

def ir_value
  @ir_value
end

#log_state_changesObject

Log all state changes in pattern comments, false by default



46
47
48
# File 'lib/origen_jtag/driver.rb', line 46

def log_state_changes
  @log_state_changes
end

#ownerObject (readonly)

Returns the object that instantiated the JTAG



21
22
23
# File 'lib/origen_jtag/driver.rb', line 21

def owner
  @owner
end

#tck_formatObject Also known as: tclk_format

Wave/timing format of the JTAG clock: :rh (ReturnHigh) or :rl (ReturnLo), :rh is the default



34
35
36
# File 'lib/origen_jtag/driver.rb', line 34

def tck_format
  @tck_format
end

#tck_multipleObject Also known as: tclk_multiple

The number of cycles for one clock pulse, assumes 50% duty cycle. Uses tester non-return format to spread TCK across multiple cycles.

e.g. @tck_multiple = 2, @tck_format = :rh, means one cycle with Tck low (non-return), one with Tck high (NR)
     @tck_multiple = 4, @tck_format = :rl, means 2 cycles with Tck high (NR), 2 with Tck low (NR)


29
30
31
# File 'lib/origen_jtag/driver.rb', line 29

def tck_multiple
  @tck_multiple
end

#tdo_store_cycleObject

Returns the value of attribute tdo_store_cycle.



39
40
41
# File 'lib/origen_jtag/driver.rb', line 39

def tdo_store_cycle
  @tdo_store_cycle
end

#tdo_strobeObject

Returns the value of attribute tdo_strobe.



38
39
40
# File 'lib/origen_jtag/driver.rb', line 38

def tdo_strobe
  @tdo_strobe
end

#verboseObject Also known as: verbose?

Set true to print out debug comments about all state transitions



42
43
44
# File 'lib/origen_jtag/driver.rb', line 42

def verbose
  @verbose
end

Instance Method Details

#apply_action(pin, actions) ⇒ Object



528
529
530
531
532
533
# File 'lib/origen_jtag/driver.rb', line 528

def apply_action(pin, actions)
  actions.each do |operation|
    method = operation.shift
    pin.send(method, *operation) if method
  end
end

#cycle_callback?Boolean

When true it means that the application is dealing with how to handle the 4 JTAG signals for each JTAG cycle. In that case this driver calculates what state the 4 pins should be in each signal and then calls back to the application with that information and it is up to the application to decide what to do with that information and when/if to generate tester cycles.

Returns:

  • (Boolean)


113
114
115
# File 'lib/origen_jtag/driver.rb', line 113

def cycle_callback?
  !!@cycle_callback
end

#read_dr(reg_or_val, options = {}) ⇒ Object

Read the given value, register or bit collection from the data register. This is a self contained method that will take care of the TAP controller state transitions, exiting with the TAP controller in Run-Test/Idle.

Parameters:

  • reg_or_val (Integer, Origen::Register::Reg, Origen::Register::BitCollection, Origen::Register::Bit)

    Value to be read. If a reg/bit collection is supplied this can be pre-marked for read in which case only the marked bits will be read and the vectors corresponding to the data from the non-read bits will be set to don't care. Similarly the bits can be pre-marked for store (capture) or overlay.

  • options (Hash) (defaults to: {})

    Options to customize the operation

Options Hash (options):

  • :size (Integer)

    The number of bits to read. This is optional when supplying a register or bit collection in which case the size will be derived from the number of bits supplied. If the size is supplied then it will override the size derived from the bits. If the size is greater than the number of bits provided then the additional space will be padded by don't care cycles.

  • :msg (String)

    By default will not make any comments directly here. Can pass a msg to be written out prior to shifting data.



442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
# File 'lib/origen_jtag/driver.rb', line 442

def read_dr(reg_or_val, options = {})
  if Origen.tester.respond_to?(:read_dr)
    Origen.tester.read_dr(reg_or_val, options)
  else
    options = {
      read: true
    }.merge(options)
    if options[:msg]
      cc "#{options[:msg]}\n"
    end
    shift_dr(options.merge(read: Origen::Utility.read_hex(reg_or_val))) do
      shift(reg_or_val, options)
    end
  end
end

#read_ir(reg_or_val, options = {}) ⇒ Object

Read the given value, register or bit collection from the instruction register. This is a self contained method that will take care of the TAP controller state transitions, exiting with the TAP controller in Run-Test/Idle.

Parameters:

  • reg_or_val (Integer, Origen::Register::Reg, Origen::Register::BitCollection, Origen::Register::Bit)

    Value to be read. If a reg/bit collection is supplied this can be pre-marked for read in which case only the marked bits will be read and the vectors corresponding to the data from the non-read bits will be set to don't care. Similarly the bits can be pre-marked for store (capture) or overlay.

  • options (Hash) (defaults to: {})

    Options to customize the operation

Options Hash (options):

  • :size (Integer)

    The number of bits to read. This is optional when supplying a register or bit collection in which case the size will be derived from the number of bits supplied. If the size is supplied then it will override the size derived from the bits. If the size is greater than the number of bits provided then the additional space will be padded by don't care cycles.

  • :msg (String)

    By default will not make any comments directly here. Can pass a msg to be written out prior to shifting data.



512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
# File 'lib/origen_jtag/driver.rb', line 512

def read_ir(reg_or_val, options = {})
  if Origen.tester.respond_to?(:read_ir)
    Origen.tester.read_ir(reg_or_val, options)
  else
    options = {
      read: true
    }.merge(options)
    if options[:msg]
      cc "#{options[:msg]}\n"
    end
    shift_ir(read: Origen::Utility.read_hex(reg_or_val)) do
      shift(reg_or_val, options)
    end
  end
end

#shift(reg_or_val, options = {}) ⇒ Object

Shift data into the TDI pin or out of the TDO pin.

There is no TAP controller state checking or handling here, it just shifts some data directly into the pattern, so it is assumed that some higher level logic is co-ordinating the TAP Controller.

Most applications should not call this method directly and should instead use the pre-packaged read/write_dr/ir methods. However it is provided as a public API for the corner cases like generating an overlay subroutine pattern where it would be necessary to generate some JTAG vectors outwith the normal state controller wrapper.

Parameters:

  • reg_or_val (Integer, Origen::Register::Reg, Origen::Register::BitCollection, Origen::Register::Bit)

    Value to be shifted. If a reg/bit collection is supplied this can be pre-marked for read, store or overlay and which will result in the requested action being applied to the cycles corresponding to those bits only (don't care cycles will be generated for the others).

  • options (Hash) (defaults to: {})

    Options to customize the operation

Options Hash (options):

  • :size (Integer)

    The number of bits to shift. This is optional when supplying a register or bit collection in which case the size will be derived from the number of bits supplied. If this option is supplied then it will override the size derived from the bits. If the size is greater than the number of bits provided then the additional space will be padded by 0s or don't cares as appropriate.

  • :read (Boolean) — default: false

    When true the given value will be compared on the TDO pin instead of being shifted into the TDI pin. In the case of a register object being provided only those bits that are actually marked for read will be compared.

  • :cycle_last (Boolean) — default: false

    Normally the last data bit is applied to the pins but not cycled, this is to integrate with the TAPController which usually requires that the TMS value is also changed on the last data bit. To override this default behavior and force a cycle for the last data bit set this to true.

  • :includes_last_bit (Boolean) — default: true

    When true the TMS pin will be driven to 1 on the last cycle of the shift if :cycle_last has been specified. To override this and keep TMS low on the last cycle set this to false. One reason for doing this would be if generating some subroutine vectors which only represented a partial section of a shift operation.



151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
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
286
287
288
289
290
291
292
293
294
295
296
297
# File 'lib/origen_jtag/driver.rb', line 151

def shift(reg_or_val, options = {})
  options = {
    read:              false,
    cycle_last:        false,
    includes_last_bit: true,
    no_subr:           false      # do not use subroutine for any overlay
  }.merge(options)

  # save compression state for restoring afterwards
  compression_on = !Origen.tester.dont_compress

  # clean incoming data
  size = extract_size(reg_or_val, options)
  tdi_reg = extract_shift_in_data(reg_or_val, size, options)
  tdo_reg = extract_shift_out_data(reg_or_val, size, options)
  global_ovl, ovl_reg = extract_overlay_data(reg_or_val, size, options)

  # let the tester handle overlay if possible
  unless tester.respond_to?(:source_memory)
    # tester does not support direct labels, so can't do
    if options[:no_subr] && !$tester.respond_to?('label')
      cc 'This tester does not support use of labels, cannot do no_subr option as requested'
      cc '  going with subroutine overlay instead'
      options[:no_subr] = false
    end

    # insert global label if specified
    if global_ovl
      if $tester.respond_to?('label')
        $tester.label(global_ovl, true)
      else
        cc "Unsupported global label: #{global_ovl}"
      end
    end
  end # of let tester handle overlay if possible

  # loop through each data bit
  last_overlay_label = ''
  size.times do |i|
    store_tdo_this_tck = false

    # Set up pin actions for bit transaction (tck cycle)

    # TDI
    action :tdi, :drive, tdi_reg[i]

    # TDO
    action :tdo, :dont_care                                 # default setting
    if tdo_reg[i]
      if tdo_reg[i].is_to_be_stored?                        # store
        store_tdo_this_tck = true
        action :tdo, :dont_care if Origen.tester.j750?
      elsif tdo_reg[i].is_to_be_read?                       # compare/assert
        action :tdo, :assert, tdo_reg[i], meta: { position: i }
      end
    end

    # TMS
    action :tms, :drive, 0

    # let tester handle overlay if implemented
    overlay_options = {}
    if tester.respond_to?(:source_memory) && !cycle_callback?
      if ovl_reg[i] && ovl_reg[i].has_overlay? && !Origen.mode.simulation?
        overlay_options[:pins] = pins[:tdi]
        if global_ovl
          overlay_options[:overlay_str] = global_ovl
        else
          overlay_options[:overlay_str] = ovl_reg[i].overlay_str
        end
        if options[:no_subr] || global_ovl
          if global_ovl
            overlay_options[:overlay_style] = :global_label
          else
            overlay_options[:overlay_style] = :label
          end
        end
        tester_subr_overlay = !(options[:no_subr] || global_ovl) && tester.overlay_style == :subroutine
        action :tdi, :drive, 0 if tester_subr_overlay
        action :tdo, :assert, tdo_reg[i], meta: { position: i } if options[:read] unless tester_subr_overlay
        # Force the last bit to be shifted from this method if overlay requested on the last bit
        options[:cycle_last] = true if i == size - 1
      end
    else
      # Overlay - reconfigure pin action for overlay if necessary
      if ovl_reg[i] && ovl_reg[i].has_overlay? && !Origen.mode.simulation?
        if options[:no_subr]
          Origen.tester.dont_compress = true
          if ovl_reg[i].overlay_str != last_overlay_label
            $tester.label(ovl_reg[i].overlay_str)
            last_overlay_label = ovl_reg[i].overlay_str
          end
          action :tdo, :assert, tdo_reg[i], meta: { position: i } if options[:read]
        else
          action :tdi, :drive, 0
          call_subroutine = ovl_reg[i].overlay_str
        end
      end
    end # of let tester handle overlay

    # With JTAG pin actions queued up, use block call to tck_cycle to
    #   execute a single TCK period.  Special handling of subroutines,
    #   case of last bit in shift, and store vector (within a multi-cycle
    #   tck config).
    if call_subroutine || tester_subr_overlay
      @last_data_vector_shifted = true
    else
      @last_data_vector_shifted = false
    end

    if call_subroutine
      Origen.tester.call_subroutine(call_subroutine)
    else
      @next_data_vector_to_be_stored = false
      # Don't latch the last bit, that will be done when leaving the state.
      if i != size - 1 || options[:cycle_last]
        if i == size - 1 && options[:includes_last_bit]
          unless tester_subr_overlay
            action :tms, :drive, 1
            @last_data_vector_shifted = true
          end
        end
        tck_cycle do
          if store_tdo_this_tck && @next_data_vector_to_be_stored
            action :store
          end
          if overlay_options[:pins].nil? || cycle_callback?
            cycle
          else
            cycle overlay: overlay_options
            overlay_options[:change_data] = false			# data change only on first cycle if overlay
          end
        end
        pins[:tdo].dont_care unless cycle_callback?
      else
        @deferred_compare = true
        @deferred_store = true if store_tdo_this_tck
      end
    end
  end

  # Clear read and similar flags to reflect that the request has just been fulfilled
  reg_or_val.clear_flags if reg_or_val.respond_to?(:clear_flags)

  # put back compression if turned on above
  Origen.tester.dont_compress = false if compression_on
end

#tck_cycleObject Also known as: tclk_cycle

Cycles the tester through one TCK cycle Adjusts for the TCK format and cycle span Assumes caller will drive pattern to tester via .drive or similar



303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
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
362
363
364
365
366
# File 'lib/origen_jtag/driver.rb', line 303

def tck_cycle
  if cycle_callback?
    @next_data_vector_to_be_stored = @tdo_store_cycle
    yield
  else
    case @tck_format
      when :rh
        tck_val = 0
      when :rl
        tck_val = 1
      else
        fail 'ERROR: Invalid Tclk timing format!'
    end

    # determine whether to mask TDO on first half cycle
    mask_tdo_half0 =  ((@tck_format == :rl) && (@tdo_strobe == :tck_low) && (@tck_multiple > 1)) ||
                      ((@tck_format == :rh) && (@tdo_strobe == :tck_high) && (@tck_multiple > 1))

    # determine whether to mask TDO on second half cycle
    mask_tdo_half1 =  ((@tck_format == :rl) && (@tdo_strobe == :tck_high) && (@tck_multiple > 1)) ||
                      ((@tck_format == :rh) && (@tdo_strobe == :tck_low) && (@tck_multiple > 1))

    # If TDO is already suspended (by an application) then don't do the
    # suspends below since the resume will clear the application's suspend
    tdo_already_suspended = !cycle_callback? && pins[:tdo].suspended? && !@tdo_suspended_by_driver

    @tck_multiple.times do |i|
      # 50% duty cycle if @tck_multiple is even, otherwise slightly off

      @next_data_vector_to_be_stored = @tdo_store_cycle == i ? true : false

      if i < (@tck_multiple + 1) / 2
        # first half of cycle
        pins[:tck].drive(@tck_vals ? @tck_vals[:on] : tck_val)
        unless tdo_already_suspended
          if mask_tdo_half0
            @tdo_suspended_by_driver = true
            pins[:tdo].suspend
          else
            @tdo_suspended_by_driver = false
            pins[:tdo].resume
          end
        end
      else
        # second half of cycle
        pins[:tck].drive(@tck_vals ? @tck_vals[:off] : (1 - tck_val))
        unless tdo_already_suspended
          if mask_tdo_half1
            @tdo_suspended_by_driver = true
            pins[:tdo].suspend
          else
            @tdo_suspended_by_driver = false
            pins[:tdo].resume
          end
        end
      end
      yield
    end
    if @tdo_suspended_by_driver
      @tdo_suspended_by_driver = false
      pins[:tdo].resume
    end
  end
end

#tms!(val) ⇒ Object

Applies the given value to the TMS pin and then cycles the tester for one TCK

Parameters:

  • val (Integer)

    Value to drive on the TMS pin, 0 or 1



373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
# File 'lib/origen_jtag/driver.rb', line 373

def tms!(val)
  if @deferred_compare
    @deferred_compare = nil
  else
    action :tdo, :dont_care
  end

  if @deferred_store
    @deferred_store = nil
    store_tdo_this_tck = true
  else
    store_tdo_this_tck = false
  end
  @next_data_vector_to_be_stored = false

  tck_cycle do
    if store_tdo_this_tck && @next_data_vector_to_be_stored
      action :store
    end
    action :tms, :drive, val
    cycle
  end
end

#write_dr(reg_or_val, options = {}) ⇒ Object

Write the given value, register or bit collection to the data register. This is a self contained method that will take care of the TAP controller state transitions, exiting with the TAP controller in Run-Test/Idle.

Parameters:

  • reg_or_val (Integer, Origen::Register::Reg, Origen::Register::BitCollection, Origen::Register::Bit)

    Value to be written. If a reg/bit collection is supplied this can be pre-marked for overlay.

  • options (Hash) (defaults to: {})

    Options to customize the operation

Options Hash (options):

  • :size (Integer)

    The number of bits to write. This is optional when supplying a register or bit collection in which case the size will be derived from the number of bits supplied. If this option is supplied then it will override the size derived from the bits. If the size is greater than the number of bits provided then the additional space will be padded by 0s.

  • :msg (String)

    By default will not make any comments directly here. Can pass a msg to be written out prior to shifting data.



411
412
413
414
415
416
417
418
419
420
421
422
423
# File 'lib/origen_jtag/driver.rb', line 411

def write_dr(reg_or_val, options = {})
  if Origen.tester.respond_to?(:write_dr)
    Origen.tester.write_dr(reg_or_val, options)
  else
    if options[:msg]
      cc "#{options[:msg]}\n"
    end
    val = reg_or_val.respond_to?(:data) ? reg_or_val.data : reg_or_val
    shift_dr(options.merge(write: val.to_hex)) do
      shift(reg_or_val, options)
    end
  end
end

#write_ir(reg_or_val, options = {}) ⇒ Object

Write the given value, register or bit collection to the instruction register. This is a self contained method that will take care of the TAP controller state transitions, exiting with the TAP controller in Run-Test/Idle.

Parameters:

  • reg_or_val (Integer, Origen::Register::Reg, Origen::Register::BitCollection, Origen::Register::Bit)

    Value to be written. If a reg/bit collection is supplied this can be pre-marked for overlay.

  • options (Hash) (defaults to: {})

    Options to customize the operation

Options Hash (options):

  • :size (Integer)

    The number of bits to write. This is optional when supplying a register or bit collection in which case the size will be derived from the number of bits supplied. If this option is supplied then it will override the size derived from the bits. If the size is greater than the number of bits provided then the additional space will be padded by 0s.

  • :force (Boolean)

    By default multiple calls to this method will not generate multiple writes. This is to allow wrapper algorithms to remain efficient yet not have to manually track the IR state (and in many cases this may be impossible due to multiple protocols using the same JTAG). To force a write regardless of what the driver thinks the IR contains set this to true.

  • :msg (String)

    By default will not make any comments directly here. Can pass a msg to be written out prior to shifting in IR data. Will not write comment only if write occurs.



478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
# File 'lib/origen_jtag/driver.rb', line 478

def write_ir(reg_or_val, options = {})
  val = reg_or_val.respond_to?(:data) ? reg_or_val.data : reg_or_val
  if val != ir_value || options[:force]
    if options[:msg]
      cc "#{options[:msg]}\n"
    end
    if Origen.tester.respond_to?(:write_ir)
      Origen.tester.write_ir(reg_or_val, options)
    else
      shift_ir(options.merge(write: val.to_hex)) do
        shift(reg_or_val, options)
      end
    end
    @ir_value = val
  end
end