ruby-adept 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +17 -0
- data/.travis.yml +6 -0
- data/Gemfile +10 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +16 -0
- data/adept.gemspec +33 -0
- data/autotest/discover.rb +2 -0
- data/bin/bprog +110 -0
- data/firmware/.gitignore +73 -0
- data/firmware/epp_stream/Basys2_100_250General.ucf +21 -0
- data/firmware/epp_stream/epp_controller.vhd +210 -0
- data/firmware/epp_stream/epp_stream.xise +355 -0
- data/firmware/epp_stream/fifo.vhd +178 -0
- data/firmware/epp_stream/tests/fifo_testbench.vhdl +164 -0
- data/lib/adept/boards/basys2.rb +84 -0
- data/lib/adept/boards.rb +2 -0
- data/lib/adept/connection_provider.rb +30 -0
- data/lib/adept/data_formats/bitstream.rb +116 -0
- data/lib/adept/data_formats/data_factories.rb +33 -0
- data/lib/adept/data_formats.rb +2 -0
- data/lib/adept/device.rb +127 -0
- data/lib/adept/error.rb +4 -0
- data/lib/adept/jtag/connection.rb +404 -0
- data/lib/adept/jtag/device.rb +178 -0
- data/lib/adept/jtag/devices/fpga.rb +162 -0
- data/lib/adept/jtag/devices/null.rb +0 -0
- data/lib/adept/jtag/devices/platform_flash.rb +23 -0
- data/lib/adept/jtag/devices.rb +2 -0
- data/lib/adept/jtag/error.rb +8 -0
- data/lib/adept/jtag/tap_state.rb +67 -0
- data/lib/adept/jtag/tap_states.rb +52 -0
- data/lib/adept/jtag.rb +11 -0
- data/lib/adept/low_level/connection.rb +59 -0
- data/lib/adept/low_level/device.rb +43 -0
- data/lib/adept/low_level/device_error.rb +22 -0
- data/lib/adept/low_level/device_manager.rb +142 -0
- data/lib/adept/low_level/enhanced_parallel.rb +151 -0
- data/lib/adept/low_level/error_handler.rb +60 -0
- data/lib/adept/low_level/jtag.rb +379 -0
- data/lib/adept/low_level/library.rb +173 -0
- data/lib/adept/low_level.rb +4 -0
- data/lib/adept/version.rb +3 -0
- data/lib/adept.rb +11 -0
- data/spec/firmware/epp_loopback.bit +0 -0
- data/spec/lib/adept/data_formats/bitstream_spec.rb +95 -0
- data/spec/lib/adept/data_formats/data_factories_spec.rb +42 -0
- data/spec/lib/adept/device_spec.rb +88 -0
- data/spec/lib/adept/jtag/connection_spec.rb +433 -0
- data/spec/lib/adept/jtag/device_spec.rb +107 -0
- data/spec/lib/adept/jtag/devices/fpga_spec.rb +71 -0
- data/spec/lib/adept/low_level/enhanced_parallel_spec.rb +72 -0
- data/spec/lib/adept/low_level/jtag_spec.rb +204 -0
- data/spec/spec_helpers.rb +25 -0
- metadata +240 -0
@@ -0,0 +1,404 @@
|
|
1
|
+
|
2
|
+
require 'adept'
|
3
|
+
require 'adept/low_level'
|
4
|
+
require 'adept/jtag'
|
5
|
+
|
6
|
+
module Adept
|
7
|
+
module JTAG
|
8
|
+
|
9
|
+
#
|
10
|
+
# Represents a connection to a JTAG device.
|
11
|
+
#
|
12
|
+
class Connection
|
13
|
+
extend ConnectionProvider
|
14
|
+
|
15
|
+
attr_reader :tap_state
|
16
|
+
|
17
|
+
#
|
18
|
+
# Sets up a new JTAG connection.
|
19
|
+
#
|
20
|
+
def initialize(device, port_number=0)
|
21
|
+
|
22
|
+
#Store the information regarding the owning device...
|
23
|
+
@device = device
|
24
|
+
|
25
|
+
#Initialize the chain to zero until enumeration occurs.
|
26
|
+
@chain_length = 0
|
27
|
+
@devices_in_chain = 0
|
28
|
+
|
29
|
+
#... open a JTAG connection.
|
30
|
+
LowLevel::JTAG::EnableEx(@device.handle, port_number)
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
#
|
35
|
+
# Closes the given JTAG connection.
|
36
|
+
#
|
37
|
+
def close
|
38
|
+
LowLevel::JTAG::Disable(@device.handle)
|
39
|
+
end
|
40
|
+
|
41
|
+
#
|
42
|
+
# Returns a list of the JTAG IDCodes for all connected devices.
|
43
|
+
#
|
44
|
+
def connected_devices
|
45
|
+
|
46
|
+
#Reset all targets' TAPs; this will automatically load the IDCODE instruction into the
|
47
|
+
#instruction register
|
48
|
+
reset_target
|
49
|
+
|
50
|
+
devices = []
|
51
|
+
chain_length = 0
|
52
|
+
devices_in_chain = 0
|
53
|
+
|
54
|
+
#Loop until we've enumerated all devices in the JTAG chain.
|
55
|
+
loop do
|
56
|
+
|
57
|
+
#Recieve a single 32-bit JTAG ID code, LSB first.
|
58
|
+
idcode = receive_data(32, true)
|
59
|
+
|
60
|
+
#If we've recieved the special "null" IDcode, we've finished enumerating.
|
61
|
+
#(In this case, we'll choose to accept the technically-valid all-ones IDcode as null,
|
62
|
+
#as it is returned by most system boards when their power is turned off, and isn't
|
63
|
+
#otherwise supported.)
|
64
|
+
break if idcode == "\x00\x00\x00\x00" or idcode == "\xFF\xFF\xFF\xFF"
|
65
|
+
|
66
|
+
#Otherwise, add this idcode to the list...
|
67
|
+
devices << JTAG::Device.from_idcode(idcode.reverse, self, devices_in_chain, chain_length)
|
68
|
+
|
69
|
+
#... add its width the the known scan-chain length
|
70
|
+
chain_length += devices.last.instruction_width
|
71
|
+
devices_in_chain += 1
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
#Update the internal chain-length.
|
76
|
+
@chain_length = chain_length
|
77
|
+
@devices_in_chain = devices_in_chain
|
78
|
+
|
79
|
+
#Return the list of IDCodes.
|
80
|
+
devices.reverse
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
#
|
85
|
+
# Sets the state of the target's Test Access Port.
|
86
|
+
#
|
87
|
+
def tap_state=(new_state)
|
88
|
+
|
89
|
+
#If we're trying to enter the reset state, force a reset of the test hardware.
|
90
|
+
#(This ensure that we can reset the test hardware even if a communications (or target) error
|
91
|
+
# causes improper behavior.)
|
92
|
+
reset_target if new_state == JTAG::TAPStates::Reset
|
93
|
+
|
94
|
+
#If we're already in the desired state, abort.
|
95
|
+
return if new_state == @tap_state
|
96
|
+
|
97
|
+
#Find the correct sequence of TMS values to reach the desired state...
|
98
|
+
path = path_to_state(new_state).reverse
|
99
|
+
tms_values = [path.to_i(2)]
|
100
|
+
|
101
|
+
#... and apply them.
|
102
|
+
LowLevel::JTAG::transmit(@device.handle, tms_values, false, path.length, false)
|
103
|
+
|
104
|
+
#Update the internal record of the TAP state.
|
105
|
+
@tap_state = new_state
|
106
|
+
|
107
|
+
end
|
108
|
+
|
109
|
+
#
|
110
|
+
# Transmit an instruction over the JTAG test access lines, to be placed into
|
111
|
+
# the JTAG instruction register.
|
112
|
+
#
|
113
|
+
# bytes: A byte-string which contains the instruction to be transmitted.
|
114
|
+
# bit_count: The total amount of bits to be transmitted from the byte string.
|
115
|
+
#
|
116
|
+
# pad_to_chain_length:
|
117
|
+
# If set, the transmitted data will be suffixed with logic '1's until the chain length has been met.
|
118
|
+
# This allows the transmitter to easily put devices to the "left" of afttarget device into bypass.
|
119
|
+
#
|
120
|
+
# prefix_with_ones:
|
121
|
+
# Prefixes the transmitted data with the specified amount of logic '1's. Prefixing is skipped if this parameter
|
122
|
+
# is not provided, or is set to zero. This allows the transmitter to easily put devices to the "right" of a
|
123
|
+
# target device into bypass.
|
124
|
+
#
|
125
|
+
# do_not_finish:
|
126
|
+
# If set, the device will be left in the ShiftIR state, so additional instructions data be transmitted.
|
127
|
+
#
|
128
|
+
def transmit_instruction(bytes, bit_count, pad_to_chain_length=false, prefix_with_ones=0, do_not_finish=false)
|
129
|
+
|
130
|
+
#If the pad-to-chain length option is selected, compute the total amount of padding required.
|
131
|
+
#Otherwise, set the required padding to zero.
|
132
|
+
padding_after = pad_to_chain_length ? [@chain_length - prefix_with_ones - bit_count, 0].max : 0
|
133
|
+
|
134
|
+
#Move to the Exit1IR state after transmission, allowing the recieved data to be processed,
|
135
|
+
#unless the do_not_finish value is set.
|
136
|
+
state_after = do_not_finish ? nil : TAPStates::Exit1IR
|
137
|
+
|
138
|
+
#Transmit the actual instruction.
|
139
|
+
transmit_in_state(TAPStates::ShiftIR, bytes, bit_count, state_after, true, prefix_with_ones, padding_after)
|
140
|
+
|
141
|
+
end
|
142
|
+
|
143
|
+
#
|
144
|
+
# Transmit data over the JTAG test access lines, to be placed into
|
145
|
+
# the JTAG data register.
|
146
|
+
#
|
147
|
+
# bytes: A byte-string which contains the instruction to be transmitted.
|
148
|
+
# bit_count: The total amount of bits to be transmitted from the byte string.
|
149
|
+
#
|
150
|
+
# pad_to_chain_length:
|
151
|
+
# If set, the transmitted data will be suffixed with logic '1's until the chain length has been met,
|
152
|
+
# *assuming that all devices other than the single target device are in bypass*.
|
153
|
+
# This allows the transmitter to easily fill the bypass registers of all additional devices with zeroes.
|
154
|
+
#
|
155
|
+
# prefix_with_zeroes:
|
156
|
+
# Prefixes the transmitted data with the specified amount of logic '0's. Prefixing is skipped if this parameter
|
157
|
+
# is not provided, or is set to zero.
|
158
|
+
#
|
159
|
+
# do_not_finish:
|
160
|
+
# If set, the device will be left in the ShiftIR state, so additional instructions data be transmitted.
|
161
|
+
#
|
162
|
+
def transmit_data(bytes, bit_count, pad_to_chain_length=false, prefix_with_zeroes=0, do_not_finish=false)
|
163
|
+
|
164
|
+
#If the pad-to-chain length option is selected, compute the total amount of padding required.
|
165
|
+
#Otherwise, set the required padding to zero.
|
166
|
+
padding_after = pad_to_chain_length ? [@devices_in_chain - prefix_with_zeroes - 1, 0].max : 0
|
167
|
+
|
168
|
+
#Move to the Exit1IR state after transmission, allowing the recieved data to be processed,
|
169
|
+
#unless the do_not_finish value is set.
|
170
|
+
state_after = do_not_finish ? nil : TAPStates::Exit1DR
|
171
|
+
|
172
|
+
#Transmit the actual instruction.
|
173
|
+
transmit_in_state(TAPStates::ShiftDR, bytes, bit_count, state_after, false, prefix_with_zeroes, padding_after)
|
174
|
+
|
175
|
+
end
|
176
|
+
|
177
|
+
|
178
|
+
#
|
179
|
+
# Recieve data from the JTAG data register.
|
180
|
+
#
|
181
|
+
# bit_count: The amount of bits to receive.
|
182
|
+
# do_not_finish: If set, the transmission will be "left open" so additional data can be received.
|
183
|
+
#
|
184
|
+
def receive_data(bit_count, do_not_finish=false, overlap=false)
|
185
|
+
|
186
|
+
#Put the device into the desired state.
|
187
|
+
self.tap_state = JTAG::TAPStates::ShiftDR
|
188
|
+
|
189
|
+
#Transmit the data, and recieve the accompanying response.
|
190
|
+
response = LowLevel::JTAG::receive(@device.handle, false, false, bit_count, overlap)
|
191
|
+
|
192
|
+
#If a state_after was provided, place the device into that state.
|
193
|
+
self.tap_state = JTAG::TAPStates::Exit1DR unless do_not_finish
|
194
|
+
|
195
|
+
#Return the received response.
|
196
|
+
response
|
197
|
+
|
198
|
+
end
|
199
|
+
|
200
|
+
#
|
201
|
+
# Switches to run/test mode, and holds that state for the desired amount of clock ticks.
|
202
|
+
#
|
203
|
+
def run_test(clock_ticks)
|
204
|
+
|
205
|
+
#Put the target into the Run-Test-Idle state.
|
206
|
+
self.tap_state = JTAG::TAPStates::Idle
|
207
|
+
|
208
|
+
#And "tick" the test clock for the desired amount of cycles.
|
209
|
+
LowLevel::JTAG::tick(@device.handle, false, false, clock_ticks, false)
|
210
|
+
|
211
|
+
end
|
212
|
+
|
213
|
+
#
|
214
|
+
# Force-resets the target device.
|
215
|
+
#
|
216
|
+
def reset_target
|
217
|
+
|
218
|
+
#Reset the target device's JTAG controller by sending five bits of TMS='1'.
|
219
|
+
LowLevel::JTAG::tick(@device.handle, true, false, 5)
|
220
|
+
|
221
|
+
#Set the internal TAP state to reset.
|
222
|
+
@tap_state = JTAG::TAPStates::Reset
|
223
|
+
|
224
|
+
end
|
225
|
+
|
226
|
+
#
|
227
|
+
# Registers a device type to be handled by JTAG connections, allowing JTAGDevice
|
228
|
+
# instances to be automatically created upon device enumeration.
|
229
|
+
#
|
230
|
+
# Device types typically are classes which include the JTAGDevice mixin,
|
231
|
+
#
|
232
|
+
#
|
233
|
+
def self.register_device_type(type)
|
234
|
+
@device_types << type
|
235
|
+
end
|
236
|
+
|
237
|
+
#
|
238
|
+
# Determines if the given device can serve as the host for a JTAG connection.
|
239
|
+
#
|
240
|
+
def self.supported_by?(device)
|
241
|
+
LowLevel::JTAG::supported?(device)
|
242
|
+
end
|
243
|
+
|
244
|
+
private
|
245
|
+
|
246
|
+
#
|
247
|
+
# Transmits a sequence of data while in a given state.
|
248
|
+
#
|
249
|
+
# bytes: A byte-string which contains the instruction to be transmitted;
|
250
|
+
# or a boolean value to send a single bit repeatedly.
|
251
|
+
# bit_count: The total amount of bits to be transmitted.
|
252
|
+
#
|
253
|
+
#
|
254
|
+
def transmit_in_state(state_before, value, bit_count, state_after=nil, pad_with=false, pad_before=0, pad_after=0)
|
255
|
+
|
256
|
+
#Put the device into the desired state.
|
257
|
+
self.tap_state = state_before
|
258
|
+
|
259
|
+
#If we've been instructed to pad before the transmission, do so.
|
260
|
+
LowLevel::JTAG::transmit(@device.handle, false, pad_with, pad_before) unless pad_before.zero?
|
261
|
+
|
262
|
+
#Transmit the data, and receive the accompanying response.
|
263
|
+
response = transmit_and_advance(false, value, bit_count, pad_after.zero?() ? state_after : nil)
|
264
|
+
|
265
|
+
#If we've been instructed to pad after the transmission, do so.
|
266
|
+
transmit_and_advance(false, pad_with, pad_after, state_after) unless pad_after.zero?
|
267
|
+
|
268
|
+
#If a state_after was provided, place the device into that state.
|
269
|
+
self.tap_state = state_after unless state_after.nil?
|
270
|
+
|
271
|
+
#Return the received response.
|
272
|
+
response
|
273
|
+
|
274
|
+
end
|
275
|
+
|
276
|
+
#
|
277
|
+
# Performs a data transmission, and advances TMS.
|
278
|
+
#
|
279
|
+
# The final TMS value is overlapped with the last bit, so the final shift and
|
280
|
+
# state change occur on the same clock edge.
|
281
|
+
#
|
282
|
+
# tms: The TMS value to use for all but the last bit of the transmission.
|
283
|
+
# tdi: The TDI values to transmit, in the same format accepted by the other transmit functions.
|
284
|
+
# bit_count: The total amount of bits to transmit.
|
285
|
+
# advance_towards:
|
286
|
+
# The state to advance towards after the transmission is complete. Used to
|
287
|
+
# determine the last value of TMS. If advance_towards is nil, the TAP state
|
288
|
+
# will not be advanced- this is useful for conditionally advancing the state.
|
289
|
+
#
|
290
|
+
def transmit_and_advance(tms, tdi, bit_count, advance_towards)
|
291
|
+
|
292
|
+
#If we don't have a state to advance towards, perform a normal transmit, and return.
|
293
|
+
unless advance_towards
|
294
|
+
return LowLevel::JTAG::transmit(@device.handle, tms, tdi, bit_count, false)
|
295
|
+
end
|
296
|
+
|
297
|
+
#If we were passed a byte array, pack it into a string
|
298
|
+
tdi = tdi.pack("C*") if tdi.respond_to?(:pack)
|
299
|
+
|
300
|
+
#Transmit all but the last bit of the TDI data.
|
301
|
+
main_response = LowLevel::JTAG::transmit(@device.handle, tms, tdi, bit_count - 1, false)
|
302
|
+
|
303
|
+
#Figure out what TMS should be during the last transmission by checking the path
|
304
|
+
#to the state we want to advance towards.
|
305
|
+
last_tms, next_state = @tap_state.next_hop_towards(advance_towards)
|
306
|
+
|
307
|
+
#Transmit the last bit of the TDI data, with the final TMS value.
|
308
|
+
last_tdi = bit_of_message(tdi, bit_count - 1)
|
309
|
+
last_response = LowLevel::JTAG::transmit(@device.handle, last_tms == 1, last_tdi, 1, false)
|
310
|
+
|
311
|
+
#Update the internal TAP fsm.
|
312
|
+
@tap_state = next_state
|
313
|
+
|
314
|
+
#Compose a single byte-string response by merging the response from the
|
315
|
+
#first and second transmissions.
|
316
|
+
return add_bit_to_message(main_response, bit_count - 1, last_response)
|
317
|
+
|
318
|
+
end
|
319
|
+
|
320
|
+
#
|
321
|
+
# Returns the appropriate bit of a given message.
|
322
|
+
#
|
323
|
+
def bit_of_message(string, bit_number)
|
324
|
+
|
325
|
+
#If were passed a non-unpackable element, return it directly.
|
326
|
+
return string unless string.respond_to?(:unpack)
|
327
|
+
|
328
|
+
#Break the message down into an array of bytes
|
329
|
+
bytes = string.unpack("B*").first
|
330
|
+
|
331
|
+
#... and return the requested bit, as a boolean.
|
332
|
+
bytes[-bit_number - 1] == "1"
|
333
|
+
|
334
|
+
end
|
335
|
+
|
336
|
+
#
|
337
|
+
# Appends a given bit to a message.
|
338
|
+
#
|
339
|
+
# message: The message to which the bit is to be appended, as a byte-string.
|
340
|
+
# message_length: The message's length, in bits.
|
341
|
+
# bit: The bit to be appended, as a boolean.
|
342
|
+
#
|
343
|
+
def add_bit_to_message(message, message_length, bit)
|
344
|
+
|
345
|
+
#If we don't have a message, return the bit as a string.
|
346
|
+
return bit ? "\x01" : "\x00" if message_length.zero?
|
347
|
+
|
348
|
+
#If we have something other than an integer, consider its truthiness
|
349
|
+
#in the same way that C would.
|
350
|
+
bit = bit.unpack("C*").first.nonzero? if bit.respond_to?(:unpack)
|
351
|
+
|
352
|
+
#Convert the message into a sequence of binary bits.
|
353
|
+
message = message.unpack("B*").first
|
354
|
+
|
355
|
+
#Ensure that the message is zero-padded to be at least the message length.
|
356
|
+
message = message.rjust(message_length, '0')
|
357
|
+
|
358
|
+
#Extract all of the bits up to the bit count, and add the new bit.
|
359
|
+
message = (bit ? '1' : '0') + message[-message_length..-1]
|
360
|
+
|
361
|
+
#Convert the message back into a packed string, and return it.
|
362
|
+
[message].pack("B*")
|
363
|
+
|
364
|
+
end
|
365
|
+
|
366
|
+
#
|
367
|
+
# Find the shortest "path" (sequence of most-select values) which will
|
368
|
+
# put the JTAG TAP FSM into the desired state.
|
369
|
+
#
|
370
|
+
# Note that the next-hop-towards algorithms do not consider "impossible"
|
371
|
+
# combinations, such as a jump from EXIT1DR to EXIT2DR; these may cause
|
372
|
+
# an infinite loop.
|
373
|
+
#
|
374
|
+
def path_to_state(destination, start=nil)
|
375
|
+
|
376
|
+
#Create a "state pointer", which will be used to trace the FSM in order to
|
377
|
+
#find a path to the destination state. If no start was provided, use the
|
378
|
+
#current TAP state.
|
379
|
+
state = start || @tap_state
|
380
|
+
|
381
|
+
path = ""
|
382
|
+
|
383
|
+
#Traverse the FSM until we reach our destination.
|
384
|
+
until state == destination
|
385
|
+
|
386
|
+
#Find the next hop on the path to the destination...
|
387
|
+
next_hop, _ = state.next_hop_towards(destination)
|
388
|
+
|
389
|
+
#Move the "state pointer" to the next state, simulating a traversal
|
390
|
+
#of the Finite State Machine.
|
391
|
+
state = state.next_state(next_hop)
|
392
|
+
|
393
|
+
#And add the hop to the path
|
394
|
+
path << next_hop.to_s
|
395
|
+
|
396
|
+
end
|
397
|
+
|
398
|
+
#And return the computed path.
|
399
|
+
path
|
400
|
+
|
401
|
+
end
|
402
|
+
end
|
403
|
+
end
|
404
|
+
end
|
@@ -0,0 +1,178 @@
|
|
1
|
+
|
2
|
+
module Adept
|
3
|
+
module JTAG
|
4
|
+
|
5
|
+
#
|
6
|
+
# Generic JTAG Device.
|
7
|
+
#
|
8
|
+
# This class primarily exists to serve as a base for custom JTAG devices,
|
9
|
+
# but also can be used to represent an unknown JTAG device.
|
10
|
+
#
|
11
|
+
class Device
|
12
|
+
|
13
|
+
attr_accessor :idcode
|
14
|
+
|
15
|
+
#Assume an instruction width of 4; the minimum possible insruction width.
|
16
|
+
#This should be re-defined in each inheriting class.
|
17
|
+
InstructionWidth = 4
|
18
|
+
|
19
|
+
# An internal list of device types which can be recognized
|
20
|
+
# on a JTAG bus.
|
21
|
+
@device_types = []
|
22
|
+
|
23
|
+
#
|
24
|
+
# Hook which is executed when a class inherits from the JTAG Device
|
25
|
+
# class. Registers the class as a Device Type provider, and sets up
|
26
|
+
# the device's basic metaprogramming abilities.
|
27
|
+
#
|
28
|
+
def self.inherited(klass)
|
29
|
+
|
30
|
+
#Register the class as device-type provider...
|
31
|
+
@device_types << klass
|
32
|
+
|
33
|
+
#And set up the "supports_idcode" metaprogramming facility.
|
34
|
+
klass.instance_variable_set(:@supported_idcodes, [])
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
#
|
39
|
+
# Factory method which creates a new Device whose type is determined
|
40
|
+
# by the provided IDCode.
|
41
|
+
#
|
42
|
+
# idcode: The IDCode of the new JTAG device.
|
43
|
+
# position_in_chain:
|
44
|
+
# The device's position in the chain. The first device to recieve data will have
|
45
|
+
# the highest device number. In a two device chain, Device 1 will recieve data before
|
46
|
+
# Device 0.
|
47
|
+
# chain_offset:
|
48
|
+
# The amount of bits which must be transmitted to other devices before an instruction
|
49
|
+
# can be transmitted to this device- equal to the amount of bits to the _right_ of the
|
50
|
+
# active device on the scan chain.
|
51
|
+
#
|
52
|
+
def self.from_idcode(idcode, connection, position_in_chain, chain_offset)
|
53
|
+
|
54
|
+
#Find the first device type which supports the IDCode.
|
55
|
+
device_type = @device_types.find { |type| type.supports?(idcode) }
|
56
|
+
|
57
|
+
#If we weren't able to find a device, use this class as a generic wrapper.
|
58
|
+
device_type ||= self
|
59
|
+
|
60
|
+
#Otherwise, instantiate tha new device from the device type.
|
61
|
+
device_type.new(idcode, connection, position_in_chain, chain_offset)
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
#
|
66
|
+
# Default implementation for detection of IDCode support.
|
67
|
+
# Checks to see if any of the IDcode matches any of this class's masks.
|
68
|
+
#
|
69
|
+
def self.supports?(idcode)
|
70
|
+
@supported_idcodes.any? { |mask| idcode_matches_mask(mask, idcode) }
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
#
|
75
|
+
# Returns the expected instruction width of the JTAG device.
|
76
|
+
#
|
77
|
+
# In this case, we don't know what the instruction width will be,
|
78
|
+
# so we'll assume the minimum possible width of four bits.
|
79
|
+
#
|
80
|
+
def instruction_width
|
81
|
+
return self.class::InstructionWidth
|
82
|
+
end
|
83
|
+
|
84
|
+
#
|
85
|
+
# idcode: The IDCode of the new JTAG device.
|
86
|
+
# scan_offset:
|
87
|
+
# The amount of bits which must be transmitted to other devices before an instruction
|
88
|
+
# can be transmitted to this device- equal to the amount of bits to the _right_ of the
|
89
|
+
# active device on the scan chain.
|
90
|
+
#
|
91
|
+
#
|
92
|
+
def initialize(idcode, connection, position_in_chain, chain_offset)
|
93
|
+
@idcode = idcode
|
94
|
+
@connection = connection
|
95
|
+
@position_in_chain = position_in_chain
|
96
|
+
@chain_offset = chain_offset
|
97
|
+
end
|
98
|
+
|
99
|
+
#
|
100
|
+
# Activate the device, and set its current operating instruction.
|
101
|
+
# All other devices in the scan chain are placed into BYPASS.
|
102
|
+
#
|
103
|
+
def instruction=(instruction)
|
104
|
+
|
105
|
+
#If we were provided an instruction name, look up the corresponding instruction.
|
106
|
+
instruction = self.class::Instructions[instruction] if instruction.kind_of?(Symbol)
|
107
|
+
|
108
|
+
#If we have a packable number, pack it into a byte-string.
|
109
|
+
instruction = [instruction].pack("C*") if instruction.kind_of?(Numeric)
|
110
|
+
|
111
|
+
#Transmit the instruction itself.
|
112
|
+
@connection.transmit_instruction(instruction, instruction_width, true, @chain_offset)
|
113
|
+
end
|
114
|
+
|
115
|
+
#
|
116
|
+
# TODO: Handle instruction readback, by rotating instructions through the device.?
|
117
|
+
#
|
118
|
+
|
119
|
+
#
|
120
|
+
# Send data directly to (and receive data directly from) the given device.
|
121
|
+
# Assumes the current device is active, and all other devices are in bypass.
|
122
|
+
#
|
123
|
+
def transmit_data(data, bit_count=data.length * 8)
|
124
|
+
@connection.transmit_data(data, bit_count, true, @position_in_chain)
|
125
|
+
end
|
126
|
+
|
127
|
+
#
|
128
|
+
# Recieves data directly from the given device by sending the device an
|
129
|
+
# appropriately-sized string of zeroes.
|
130
|
+
# Assumes the current device is active, and all other devices are in bypass.
|
131
|
+
#
|
132
|
+
#
|
133
|
+
def receive_data(bit_count)
|
134
|
+
@connection.transmit_data(false, bit_count, true, @position_in_chain, true)
|
135
|
+
end
|
136
|
+
|
137
|
+
#
|
138
|
+
# Allows the device to run its test operation for a certain amount of TCK cycles.
|
139
|
+
# (Delegates the run_test operation to the JTAG connection object, which is in charge
|
140
|
+
# of the TAP state.)
|
141
|
+
#
|
142
|
+
def run_test(cycles)
|
143
|
+
@connection.run_test(cycles)
|
144
|
+
end
|
145
|
+
|
146
|
+
private
|
147
|
+
|
148
|
+
#
|
149
|
+
# Metaprogramming routine which indicates that the class being defined
|
150
|
+
# supports an IDcode mask.
|
151
|
+
#
|
152
|
+
def self.supports_idcode(*idcodes)
|
153
|
+
|
154
|
+
#And merge them with the known supported IDcodes.
|
155
|
+
@supported_idcodes |= idcodes
|
156
|
+
|
157
|
+
end
|
158
|
+
|
159
|
+
#
|
160
|
+
# Determines if a given IDCode matches a hex mask.
|
161
|
+
#
|
162
|
+
def self.idcode_matches_mask(mask, idcode)
|
163
|
+
|
164
|
+
#Convert the IDcode into a string, for comparison.
|
165
|
+
idcode = idcode.unpack("H*").first.downcase
|
166
|
+
|
167
|
+
#Get a set of pairs containing the characters with the same position in each string.
|
168
|
+
character_pairs = mask.downcase.chars.zip(idcode.chars)
|
169
|
+
|
170
|
+
#And verify that each character is either a match, or a Don't Care.
|
171
|
+
character_pairs.all? { |m, i| m == 'x' || m == i }
|
172
|
+
|
173
|
+
end
|
174
|
+
|
175
|
+
end
|
176
|
+
|
177
|
+
end
|
178
|
+
end
|