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):

:tclk
:tdi
:tdo
:tms

Constant Summary collapse

REQUIRED_PINS =
[:tclk, :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



37
38
39
40
41
42
43
44
45
46
47
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
# File 'lib/origen_jtag/driver.rb', line 37

def initialize(owner, options = {})
  if owner.is_a?(Hash)
    @owner = parent
    options = owner
  else
    @owner = owner
  end
  validate_pins(options)

  # The parent can configure JTAG settings by defining this constant
  if defined?(owner.class::JTAG_CONFIG)
    options = owner.class::JTAG_CONFIG.merge(options)
  end

  # Fallback defaults
  options = {
    verbose:         false,
    tclk_format:     :rh,                # format of JTAG clock used:  ReturnHigh (:rh), ReturnLo (:rl)
    tclk_multiple:   1,                  # number of cycles for one clock pulse, assumes 50% duty cycle. Uses tester non-return format to spread TCK across multiple cycles.
    #    e.g. @tclk_multiple = 2, @tclk_format = :rh, means one cycle with Tck low (non-return), one with Tck high (NR)
    #         @tclk_multiple = 4, @tclk_format = :rl, means 2 cycles with Tck high (NR), 2 with Tck low (NR)
    tdo_strobe:      :tclk_high,            # when using multiple cycles for TCK, when to strobe for TDO, options include:
    #     :tclk_high   - strobe TDO only when TCK is high
    #     :tclk_low    - strobe TDO only when TCK is low
    #     :tclk_all    - strobe TDO throughout TCK cycle
    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
  @tclk_format = options[:tclk_format]
  @tclk_multiple = options[:tclk_multiple]
  @tdo_strobe = options[:tdo_strobe]
  @tdo_store_cycle = options[:tdo_store_cycle]
  @state = options[:init_state]
  @log_state_changes = options[:log_state_changes] || false
  if options[:tclk_vals]
    @tclk_vals = options[:tclk_vals]
    unless @tclk_vals.is_a?(Hash) && @tclk_vals.key?(:on) && @tclk_vals.key?(:off)
      fail "When specifying TCLK values, you must supply a hash with both :on and :off keys, e.g. tclk_vals: { on: 'P', off: 0 }"
    end
  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



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

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

#tclk_formatObject

Returns the value of attribute tclk_format.



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

def tclk_format
  @tclk_format
end

#tclk_multipleObject (readonly)

Returns the tclk multiple



27
28
29
# File 'lib/origen_jtag/driver.rb', line 27

def tclk_multiple
  @tclk_multiple
end

#verboseObject Also known as: verbose?

Set true to print out debug comments about all state transitions



31
32
33
# File 'lib/origen_jtag/driver.rb', line 31

def verbose
  @verbose
end

Instance Method Details

#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.

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.



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

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(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.

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.



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

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.

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.



119
120
121
122
123
124
125
126
127
128
129
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
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
# File 'lib/origen_jtag/driver.rb', line 119

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_tclk = false

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

    # TDI
    @tdi_pin.drive(tdi_reg[i])

    # TDO
    @tdo_pin.dont_care                               # default setting
    if tdo_reg[i]
      if tdo_reg[i].is_to_be_stored?                        # store
        store_tdo_this_tclk = true
        @tdo_pin.dont_care if Origen.tester.j750?
      elsif tdo_reg[i].is_to_be_read?                       # compare/assert
        @tdo_pin.assert(tdo_reg[i], meta: { position: i })
      end
    end

    # TMS
    @tms_pin.drive(0)

    # let tester handle overlay if implemented
    overlay_options = {}
    if tester.respond_to?(:source_memory)
      if ovl_reg[i] && ovl_reg[i].has_overlay? && !Origen.mode.simulation?
        overlay_options[:pins] = @tdi_pin
        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
        @tdi_pin.drive(0) if tester_subr_overlay
        @tdo_pin.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
          @tdo_pin.assert(tdo_reg[i], meta: { position: i }) if options[:read]
        else
          @tdi_pin.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 tclk_cycle to
    #   execute a single TCLK period.  Special handling of subroutines,
    #   case of last bit in shift, and store vector (within a multi-cycle
    #   tclk 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
            @tms_pin.drive(1)
            @last_data_vector_shifted = true
          end
        end
        tclk_cycle do
          if store_tdo_this_tclk && @next_data_vector_to_be_stored
            Origen.tester.store_next_cycle(@tdo_pin)
          end
          if overlay_options[:pins].nil?
            Origen.tester.cycle
          else
            Origen.tester.cycle overlay: overlay_options
            overlay_options[:change_data] = false			# data change only on first cycle if overlay
          end
        end
        @tdo_pin.dont_care
      else
        @deferred_compare = true
        @deferred_store = true if store_tdo_this_tclk
      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

#tclk_cycleObject

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



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
298
299
300
301
302
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
# File 'lib/origen_jtag/driver.rb', line 271

def tclk_cycle
  case @tclk_format
    when :rh
      tclk_val = 0
    when :rl
      tclk_val = 1
    else
      fail 'ERROR: Invalid Tclk timing format!'
  end

  # determine whether to mask TDO on first half cycle
  mask_tdo_half0 =  ((@tclk_format == :rl) && (@tdo_strobe == :tclk_low) && (@tclk_multiple > 1)) ||
                    ((@tclk_format == :rh) && (@tdo_strobe == :tclk_high) && (@tclk_multiple > 1))

  # determine whether to mask TDO on second half cycle
  mask_tdo_half1 =  ((@tclk_format == :rl) && (@tdo_strobe == :tclk_high) && (@tclk_multiple > 1)) ||
                    ((@tclk_format == :rh) && (@tdo_strobe == :tclk_low) && (@tclk_multiple > 1))

  # determine whether TDO is set to capture for this TCLK cycle
  tdo_to_be_captured = @tdo_pin.to_be_captured?

  # 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 = @tdo_pin.suspended? && !@tdo_suspended_by_driver

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

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

    if i < (@tclk_multiple + 1) / 2
      # first half of cycle
      @tck_pin.drive(@tclk_vals ? @tclk_vals[:on] : tclk_val)
      unless tdo_already_suspended
        unless tdo_to_be_captured
          if mask_tdo_half0
            @tdo_suspended_by_driver = true
            @tdo_pin.suspend
          else
            @tdo_suspended_by_driver = false
            @tdo_pin.resume
          end
        end
      end
    else
      # second half of cycle
      @tck_pin.drive(@tclk_vals ? @tclk_vals[:off] : (1 - tclk_val))
      unless tdo_already_suspended
        unless tdo_to_be_captured
          if mask_tdo_half1
            @tdo_suspended_by_driver = true
            @tdo_pin.suspend
          else
            @tdo_suspended_by_driver = false
            @tdo_pin.resume
          end
        end
      end
    end
    yield
  end
  if @tdo_suspended_by_driver
    @tdo_pin.resume
    @tdo_suspended_by_driver = false
  end
end

#tms!(val) ⇒ Object

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



342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
# File 'lib/origen_jtag/driver.rb', line 342

def tms!(val)
  if @deferred_compare
    @deferred_compare = nil
  else
    @tdo_pin.dont_care
  end

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

  tclk_cycle do
    if store_tdo_this_tclk && @next_data_vector_to_be_stored
      Origen.tester.store_next_cycle(@tdo_pin)
    end
    @tms_pin.drive!(val)
  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.

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.



379
380
381
382
383
384
385
386
387
388
389
390
391
# File 'lib/origen_jtag/driver.rb', line 379

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(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.

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.



446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
# File 'lib/origen_jtag/driver.rb', line 446

def write_ir(reg_or_val, options = {})
  if Origen.tester.respond_to?(:write_ir)
    Origen.tester.write_ir(reg_or_val, options)
  else
    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
      shift_ir(write: val.to_hex) do
        shift(reg_or_val, options)
      end
      @ir_value = val
    end
  end
end