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.
Files changed (55) hide show
  1. data/.gitignore +17 -0
  2. data/.travis.yml +6 -0
  3. data/Gemfile +10 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +29 -0
  6. data/Rakefile +16 -0
  7. data/adept.gemspec +33 -0
  8. data/autotest/discover.rb +2 -0
  9. data/bin/bprog +110 -0
  10. data/firmware/.gitignore +73 -0
  11. data/firmware/epp_stream/Basys2_100_250General.ucf +21 -0
  12. data/firmware/epp_stream/epp_controller.vhd +210 -0
  13. data/firmware/epp_stream/epp_stream.xise +355 -0
  14. data/firmware/epp_stream/fifo.vhd +178 -0
  15. data/firmware/epp_stream/tests/fifo_testbench.vhdl +164 -0
  16. data/lib/adept/boards/basys2.rb +84 -0
  17. data/lib/adept/boards.rb +2 -0
  18. data/lib/adept/connection_provider.rb +30 -0
  19. data/lib/adept/data_formats/bitstream.rb +116 -0
  20. data/lib/adept/data_formats/data_factories.rb +33 -0
  21. data/lib/adept/data_formats.rb +2 -0
  22. data/lib/adept/device.rb +127 -0
  23. data/lib/adept/error.rb +4 -0
  24. data/lib/adept/jtag/connection.rb +404 -0
  25. data/lib/adept/jtag/device.rb +178 -0
  26. data/lib/adept/jtag/devices/fpga.rb +162 -0
  27. data/lib/adept/jtag/devices/null.rb +0 -0
  28. data/lib/adept/jtag/devices/platform_flash.rb +23 -0
  29. data/lib/adept/jtag/devices.rb +2 -0
  30. data/lib/adept/jtag/error.rb +8 -0
  31. data/lib/adept/jtag/tap_state.rb +67 -0
  32. data/lib/adept/jtag/tap_states.rb +52 -0
  33. data/lib/adept/jtag.rb +11 -0
  34. data/lib/adept/low_level/connection.rb +59 -0
  35. data/lib/adept/low_level/device.rb +43 -0
  36. data/lib/adept/low_level/device_error.rb +22 -0
  37. data/lib/adept/low_level/device_manager.rb +142 -0
  38. data/lib/adept/low_level/enhanced_parallel.rb +151 -0
  39. data/lib/adept/low_level/error_handler.rb +60 -0
  40. data/lib/adept/low_level/jtag.rb +379 -0
  41. data/lib/adept/low_level/library.rb +173 -0
  42. data/lib/adept/low_level.rb +4 -0
  43. data/lib/adept/version.rb +3 -0
  44. data/lib/adept.rb +11 -0
  45. data/spec/firmware/epp_loopback.bit +0 -0
  46. data/spec/lib/adept/data_formats/bitstream_spec.rb +95 -0
  47. data/spec/lib/adept/data_formats/data_factories_spec.rb +42 -0
  48. data/spec/lib/adept/device_spec.rb +88 -0
  49. data/spec/lib/adept/jtag/connection_spec.rb +433 -0
  50. data/spec/lib/adept/jtag/device_spec.rb +107 -0
  51. data/spec/lib/adept/jtag/devices/fpga_spec.rb +71 -0
  52. data/spec/lib/adept/low_level/enhanced_parallel_spec.rb +72 -0
  53. data/spec/lib/adept/low_level/jtag_spec.rb +204 -0
  54. data/spec/spec_helpers.rb +25 -0
  55. metadata +240 -0
@@ -0,0 +1,151 @@
1
+
2
+ require 'ffi'
3
+ require 'adept/low_level/library'
4
+
5
+ module Adept
6
+ module LowLevel
7
+
8
+ #
9
+ # Low-Level Enhanced Parallel Port (EPP) Connection
10
+ #
11
+ module EnhancedParallel
12
+ extend LowLevel::Library
13
+
14
+ #Wrap the Digilent Enhanced Parallel Port library, DEPP
15
+ wrap_adept_library 'depp'
16
+
17
+ #And mix-in the low-level connection module.
18
+ extend LowLevel::Connection
19
+
20
+ #
21
+ # Simple register read/write.
22
+ #
23
+
24
+ #Set the value of a given register.
25
+ attach_adept_function :PutReg, [:ulong, :uint8, :uint8, :bool]
26
+
27
+ #Get the value of a given register.
28
+ attach_adept_function :GetReg, [:ulong, :uint8, :pointer, :bool]
29
+
30
+
31
+ #
32
+ # Sets the value of a given EPP register.
33
+ # This function exists for symettry with get_register_value.
34
+ #
35
+ # handle: The handle to the target device.
36
+ # address:
37
+ # The address of the register to be set, may by from [0..255], though
38
+ # not all EPP devices will provide all 256 registers.
39
+ # value:
40
+ # The value to be placed into the register. Should be within the range
41
+ # [0..255].
42
+ #
43
+ # overlap: True to make the operation non-blocking.
44
+ #
45
+ #
46
+ def self.set_register_value(handle, address, value, overlap=false)
47
+ PutReg(handle, address, value, false)
48
+ end
49
+
50
+ #
51
+ # Returns the value of a single EPP register.
52
+ #
53
+ def self.get_register_value(handle, address, overlap=false)
54
+ receive_out_arguments(:uint8) { |receive_buffer| GetReg(handle, address, receive_buffer, overlap) }
55
+ end
56
+
57
+ #
58
+ # Multiple register ("set") read/write.
59
+ #
60
+
61
+ #Sets the value of a collection of registers.
62
+ attach_adept_function :PutRegSet, [:ulong, :pointer, :ulong, :bool]
63
+
64
+ #Gets the value of a collection of registers.
65
+ attach_adept_function :GetRegSet, [:ulong, :pointer, :pointer, :ulong, :bool]
66
+
67
+
68
+ #
69
+ # Sets the value of multiple registers at once.
70
+ #
71
+ # handle: The handle of the affected adept device.
72
+ # mapping: A hash mapping addresses to values.
73
+ # For example { 3 => 4, 9 => 5} would place 4 in register 3, and 5 in register 9.
74
+ #
75
+ # overlap: True to make the operation non-blocking.
76
+ #
77
+ def self.set_register_values(handle, mapping, overlap=false)
78
+
79
+ #Create a buffer, which contains each of the register => value pairs.
80
+ value_buffer = to_buffer(mapping.flatten)
81
+
82
+ #And set each of the register values.
83
+ PutRegSet(handle, value_buffer, mapping.size, overlap)
84
+
85
+ end
86
+
87
+
88
+ #
89
+ # Gets the value of mulitple registers at once.
90
+ #
91
+ # handle: The handle of the affected adept device.
92
+ # addresses: A list of register values to get. Must support to_a.
93
+ # overlap: True to make the operation non-blocking.
94
+ #
95
+ def self.get_register_values(handle, addresses, overlap=false)
96
+
97
+ #Create a buffer containing each of the addresses to query.
98
+ address_buffer = to_buffer(addresses)
99
+
100
+ #And perform the query itself, returning
101
+ out_args = receive_out_arguments(addresses.count) do
102
+ |data_buffer| GetRegSet(handle, address_buffer, data_buffer, addresses.count, overlap)
103
+ end
104
+
105
+ #Pair each of the addresses with the corresponding data value received.
106
+ pairs = addresses.zip(out_args.unpack("C*"))
107
+
108
+ #Convert that response to a hash, and return it.
109
+ Hash[pairs]
110
+
111
+ end
112
+
113
+ #
114
+ # Repeated ("serial") read/write operations.
115
+ #
116
+
117
+ #Write each of a colleciton of bytes to a given register, in order.
118
+ attach_adept_function :PutRegRepeat, [:ulong, :uint8, :pointer, :ulong, :bool]
119
+
120
+ #Read a colleciton of bytes from a given register, in order.
121
+ attach_adept_function :GetRegRepeat, [:ulong, :uint8, :pointer, :ulong, :bool]
122
+
123
+
124
+ #
125
+ # Sends a "stream" of data to a single register, by repeatedly writing to
126
+ # that register. Some hardware targets may be able to interpret these repeated
127
+ # writes as a data-stream.
128
+ #
129
+ # handle: The handle of the affected adept device.
130
+ # address: The address of the register to target; should be within [0..255].
131
+ # data: An array (or array-like object) containing the data to be sent. Index
132
+ # 0 is sent first, followed by 1, and etc.
133
+ #
134
+ # overlap: True to make the operation non-blocking.
135
+ #
136
+ def send_to_register(handle, address, data, overlap=false)
137
+
138
+ #Create a buffer containing the data to be sent.
139
+ data_buffer = to_buffer(data)
140
+
141
+ #And send the relevant data.
142
+ PutRegRepeat(handle, address, data_buffer, data.size, overlap)
143
+
144
+ end
145
+
146
+
147
+
148
+ end
149
+
150
+ end
151
+ end
@@ -0,0 +1,60 @@
1
+ require 'ffi'
2
+ require 'adept/low_level/device_error'
3
+
4
+ module Adept
5
+ module LowLevel
6
+
7
+ #Maximum length of an error messsage's shortname, with null terminator.
8
+ ErrorNameMaxLength = 16
9
+
10
+ #Maximum length of an error message's description, with null terminator.
11
+ ErrorMessageMaxLength = 128
12
+
13
+ #
14
+ # Basic low-level error handler.
15
+ #
16
+ # This class implements the basic error reporting functionality from the Digilent Device Manager API.
17
+ # It is intentionally separate from the low-level DeviceManager wrapper, so error checking works even if
18
+ # the AdeptLibrary class fails during development.
19
+ #
20
+ module ErrorHandler
21
+ extend FFI::Library
22
+
23
+ ffi_lib 'libdmgr'
24
+
25
+ #
26
+ # Error handling functions.
27
+ #
28
+
29
+ #Get the most recent error code.
30
+ attach_function :DmgrGetLastError, [], :int
31
+ attach_function :DmgrSzFromErc, [:int, :pointer, :pointer], :void
32
+
33
+ #
34
+ # Returns a DeviceError which encapsulates the most recent error,
35
+ # in a format which can be easily raised.
36
+ #
37
+ def self.last_error
38
+
39
+ #get the error code most recently seen by the device manager API.
40
+ code = DmgrGetLastError()
41
+
42
+ #if no error has occurred, return nil.
43
+ return nil if code.zero?
44
+
45
+ #Create space for the error name and message...
46
+ error_name = FFI::MemoryPointer.new(ErrorNameMaxLength)
47
+ error_message = FFI::MemoryPointer.new(ErrorMessageMaxLength)
48
+
49
+ #... and populate those spaces with the relevant error information.
50
+ DmgrSzFromErc(code, error_name, error_message)
51
+
52
+ #Convert the error information into a DeviceError.
53
+ DeviceError.new(error_message.read_string, error_name.read_string, code)
54
+
55
+ end
56
+
57
+ end
58
+ end
59
+ end
60
+
@@ -0,0 +1,379 @@
1
+ require 'ffi'
2
+ require 'adept/low_level/library'
3
+ require 'adept/low_level/connection'
4
+
5
+ module Adept
6
+ module LowLevel
7
+
8
+ #
9
+ # Diglient JTAG (DJTG)
10
+ # Wrapper for the low-level JTAG manipulation functions.
11
+ #
12
+ module JTAG
13
+ extend LowLevel::Library
14
+
15
+ #Wrap the JTAG library, libDJTG
16
+ wrap_adept_library 'djtg'
17
+
18
+ #And mix-in the low-level connection module.
19
+ extend LowLevel::Connection
20
+
21
+ #
22
+ # JTAG Support Query Functions
23
+ #
24
+
25
+ #Determines which interfaces the given JTAG port provides.
26
+ attach_adept_function :GetPortProperties, [:ulong, :int32, :pointer]
27
+
28
+ #Bit numbers for the call support functions.
29
+ SUPPORTS_SET_SPEED = 0
30
+ SUPPORTS_SET_PIN_STATE = 1
31
+
32
+
33
+ #
34
+ # Returns a hash which indicates the calls that the given port supports.
35
+ # Keys include:
36
+ # -set_speed, which sets the bit-rate of the JTAG connection.
37
+ # -set_pins, which sets the values of the JTAG pins directly.
38
+ #
39
+ def self.supported_calls(device, port_number)
40
+
41
+ #Create a pointer to a new DWORD...
42
+ properties_pointer = FFI::MemoryPointer.new(:ulong)
43
+
44
+ #... and fill it with a bit-vector indicates supports for various system calls.
45
+ GetPortProperties(device, port_number, properties_pointer)
46
+
47
+ #Extract the property bit-vector from the
48
+ properties = properties_pointer.get_ulong(0)
49
+
50
+ #Return a hash which indicates which calls are supported.
51
+ {
52
+ :set_speed => properties[SUPPORTS_SET_SPEED].nonzero?,
53
+ :set_pins => properties[SUPPORTS_SET_PIN_STATE].nonzero?
54
+ }
55
+
56
+ end
57
+
58
+ #
59
+ # JTAG speed manipulation calls
60
+ #
61
+
62
+ attach_adept_function :GetSpeed, [:ulong, :pointer]
63
+ attach_adept_function :SetSpeed, [:ulong, :ulong, :pointer]
64
+
65
+ #
66
+ # Attempts to set the device's speed, in Hz.
67
+ # Returns the actual speed set.
68
+ #
69
+ def self.get_speed(handle)
70
+ get_speed_out_argument { |speed_out| GetSpeed(handle, speed_out) }
71
+ end
72
+
73
+ #
74
+ # Attempts to set the device's speed, in Hz.
75
+ # Returns the actual speed set.
76
+ #
77
+ def self.set_speed(handle, speed)
78
+ get_speed_out_argument { |speed_out| SetSpeed(handle, speed, speed_out) }
79
+ end
80
+
81
+
82
+ #
83
+ # JTAG Transmit/Receive Calls
84
+ #
85
+
86
+ attach_adept_function :PutTdiBits, [:ulong, :bool, :pointer, :pointer, :ulong, :bool]
87
+ attach_adept_function :PutTmsBits, [:ulong, :bool, :pointer, :pointer, :ulong, :bool]
88
+ attach_adept_function :PutTmsTdiBits, [:ulong, :pointer, :pointer, :ulong, :bool]
89
+ attach_adept_function :GetTdoBits, [:ulong, :bool, :bool, :pointer, :ulong, :bool]
90
+ attach_adept_function :ClockTck, [:ulong, :bool, :bool, :ulong, :bool]
91
+
92
+
93
+ #
94
+ # Sends (and recieves) raw data via the JTAG lines.
95
+ #
96
+ def self.transmit(handle, tms, tdi, bit_count, overlap=false)
97
+
98
+ return if bit_count.zero?
99
+
100
+ #If TMS and TDI were both provided as byte arrays, send them both.
101
+ if tms.respond_to?(:size) and tdi.respond_to?(:size)
102
+
103
+ #Convert the raw TMS/TDI values into an interleave bytes.
104
+ interleave = interleave_tms_tdi_bytes(tms, tdi)
105
+
106
+ #And perform an interleave transmission
107
+ transmit_interleave(handle, interleave, bit_count, overlap)
108
+
109
+ #If only TMS was provided as a byte array, use the specialized version of that function.
110
+ elsif tms.respond_to?(:size)
111
+ transmit_mode_select(handle, tms, tdi, bit_count, overlap)
112
+
113
+ #If only TDI was provided as a byte array, use the specified version of that function.
114
+ elsif tdi.respond_to?(:size)
115
+ transmit_data(handle, tms, tdi, bit_count, overlap)
116
+
117
+ #Otherwise, transmit only constant#Otherwise, transmit only constants.
118
+ else
119
+ transmit_constants(handle, tms, tdi, bit_count, overlap)
120
+ end
121
+
122
+ end
123
+
124
+ #
125
+ # Tick the Test Clock (TCK) without recieving data.
126
+ #
127
+ # device: The device with which to transmit.
128
+ # tms_value: The static, /boolean/ value (true or false) to be held on TMS while the clock is ticked.
129
+ # tdi_value: The static, /boolean/ value (true or false) to be held on TDI while the clock is ticked.
130
+ # tick_count: The amount of times TCK should be ticked.
131
+ #
132
+ #
133
+ def self.tick(handle, tms, tdi, tick_count, overlap=false)
134
+ ClockTck(handle, tms, tdi, tick_count, overlap)
135
+ end
136
+
137
+ #
138
+ # Transmits a stream of bits on the TMS (Test Mode Set) line.
139
+ #
140
+ # device: The device with which to transmit.
141
+ # tms: A string (or array) of bytes, which will be transmitted over TMS.
142
+ # tdi_value: The static, /boolean/ value (true or false) to be held on TDI while the TMS values are transmitted.
143
+ # bit_count: The total number of bits to be transmitted.
144
+ #
145
+ # Returns the values recieved on TDO during the transmission.
146
+ #
147
+ def self.transmit_mode_select(handle, tms, tdi_value, bit_count, overlap=false)
148
+ specialized_transmit(:PutTmsBits, handle, tdi_value, tms, bit_count, overlap)
149
+ end
150
+
151
+ #
152
+ # Transmits a stream of bits on the TDI (test data in).
153
+ #
154
+ # device: The device with which to transmit.
155
+ # tms_value: The static, /boolean/ value (true or false) to be held on TMS while the TDI values are transmitted.
156
+ # tdi: A string (or array) of bytes, which will be transmitted over TDI.
157
+ # bit_count: The total number of bits to be transmitted.
158
+ #
159
+ # Returns the values recieved on TDO during the transmission.
160
+ #
161
+ def self.transmit_data(handle, tms_value, tdi, bit_count, overlap=false)
162
+ specialized_transmit(:PutTdiBits, handle, tms_value, tdi, bit_count, overlap)
163
+ end
164
+
165
+ #
166
+ # Transmits a constant pair of TMS/TDI values, and recieves the TDO values that appear.
167
+ #
168
+ # device: The device with which to transmit.
169
+ # tms_value: The static, /boolean/ value (true or false) to be held on TMS while the TD0 values are receieved.
170
+ # tdi_value: The static, /boolean/ value (true or false) to be held on TDI while the TD0 values are receieved.
171
+ # bit_count: The total number of bits to be received.
172
+ #
173
+ def self.transmit_constants(handle, tms_value, tdi_value, bit_count, overlap=false)
174
+
175
+ return if bit_count.zero?
176
+
177
+ #Determine the number of bytes to be transmitted...
178
+ receive_bytes = (bit_count / 8.0).ceil
179
+
180
+ #Transmit the given tms values...
181
+ received = transmit_with(nil, receive_bytes) do |send_buffer, receive_buffer|
182
+ GetTdoBits(handle, tms_value, tdi_value, receive_buffer, bit_count, overlap)
183
+ end
184
+
185
+ #... and return the values recieved on TDO.
186
+ return received
187
+
188
+ end
189
+
190
+ #
191
+ # When using JTAG, receiving is the same as transmitting a
192
+ # long string of constant values.
193
+ #
194
+ class << self
195
+ alias_method :receive, :transmit_constants
196
+ end
197
+
198
+
199
+ #
200
+ # Sends (and recieves) raw data via the JTAG lines.
201
+ # Accepts input as an array of _interleaved_ bytes, in the format specified by the DJTG
202
+ # reference manual.
203
+ #
204
+ # device: A reference to a Digilent Adept device.
205
+ #
206
+ # interleaved:
207
+ # An array or binary string of single-byte values, in the format specified in the
208
+ # DJTG reference manual. Each byte in the interleaved array should contain a _nibble_
209
+ # of TMS, and a nibble of TDI, in the following order:
210
+ #
211
+ # TMS[3], TDI[3], TMS[2], TDI[2], TMS[1], TDI[1], TMS[0], TMS[0]
212
+ #
213
+ # bit_count: The total amount of bits to send.
214
+ #
215
+ def self.transmit_interleave(handle, interleave, bit_count, overlap = false)
216
+
217
+ #Transmit the given interleave using out transmisison helper function.
218
+ #
219
+ #Note that we're expecting to recieve about half as many bits as are contained in the
220
+ #interleave, as half of them are transmitted on TMS, and the other half on TDI.
221
+ #
222
+ receive_data = transmit_with(interleave, interleave.size / 2) do |send_buffer, receive_buffer|
223
+ PutTmsTdiBits(handle, send_buffer, receive_buffer, bit_count, overlap)
224
+ end
225
+
226
+ #Return the recieved data.
227
+ return receive_data
228
+
229
+ end
230
+
231
+ private
232
+
233
+ #
234
+ # Helper function which creates a buffer for a frequency out-argument.
235
+ # Used for calling the get/set speed low-level functions.
236
+ #
237
+ def self.get_speed_out_argument
238
+
239
+ #Reserve space in memory for the actual speed returned.
240
+ speed_pointer = FFI::MemoryPointer.new(:ulong)
241
+
242
+ #Attempt to set the JTAG connection's speed...
243
+ yield speed_pointer
244
+
245
+ #... and return the actual speed set.
246
+ speed_pointer.get_ulong(0)
247
+
248
+ end
249
+
250
+ #
251
+ # Helper function which calls the specialized Adept transmit functions.
252
+ #
253
+ # handle: The device with which to transmit.
254
+ # tms: The tmi value to be provided to the
255
+ # tdi: A string (or array) of bytes, which will be transmitted over TDI.
256
+ # bit_count: The total number of bits to be transmitted.
257
+ #
258
+ # Returns the values recieved on TDO during the transmission.
259
+ #
260
+
261
+ def self.specialized_transmit(base_function_name, handle, static_value, dynamic_value, bit_count, overlap=false)
262
+
263
+ byte_count = (bit_count / 8.0).ceil
264
+
265
+ #Transmit the given values.
266
+ received = transmit_with(dynamic_value, byte_count) do |send_buffer, receive_buffer|
267
+ send(base_function_name, handle, static_value, send_buffer, receive_buffer, bit_count, overlap)
268
+ end
269
+
270
+ #... and return the values recieved on TDO.
271
+ return received
272
+
273
+ end
274
+
275
+
276
+ #
277
+ # Helper function which automatically handles the creation of the send/receive buffers
278
+ # necessary for JTAG transactions.
279
+ #
280
+ # Accepts two arguments:
281
+ # transmit_data: The data to be transmitted; will be converted to a C byte array; or nil, if the send_buffer won't be used.
282
+ # receive_size: The amount of data to be received, in bytes. If not provided, the size of transmit_data will be used.
283
+ #
284
+ # Requires a block, which should accept two pointers:
285
+ # transmit_buffer: A FFI pointer to a block of memory which contains the transmit data.
286
+ # receive_buffer: A FFI pointer to a block of memory where the recieved data should be placed.
287
+ #
288
+ # Returns the contents of the recieve buffer after the block is called, as a ruby string.
289
+ #
290
+ def self.transmit_with(transmit_data, receive_size=nil)
291
+
292
+ #If the transmit data was provided as a byte array, convert it to a string of bytes.
293
+ if transmit_data.respond_to?(:pack)
294
+ transmit_data = transmit_data.pack('C*').force_encoding('UTF-8')
295
+ end
296
+
297
+ #Create the recieve buffer.
298
+ #If no receive size was provided, assume the same size as the data to be transmitted.
299
+ receive_size ||= transmit_data.bytesize
300
+ receive_buffer = FFI::MemoryPointer.new(receive_size)
301
+
302
+ #If transmit data was provided, place it in contiguous memory and get a pointer to it.
303
+ unless transmit_data.nil?
304
+ send_buffer = FFI::MemoryPointer.new(transmit_data.bytesize)
305
+ send_buffer.put_bytes(0, transmit_data)
306
+ end
307
+
308
+ #Yield the newly-created send and recieve buffer to the passed-in block.
309
+ yield send_buffer, receive_buffer
310
+
311
+ #And return the contents of the recieve buffer.
312
+ return receive_buffer.get_bytes(0, receive_size)
313
+
314
+ end
315
+
316
+ #
317
+ # Interleaves two sequences of TMS and TDI values into the format used by the Digilent
318
+ # API.
319
+ #
320
+ # tms: A string (or array of bytes) to be used as TMS values in the interleave.
321
+ # tdi: A string (or array of bytes) to be used as TDI values in the interleave.
322
+ #
323
+ # Returns a byte-string in the format used by transmit_interleave.
324
+ #
325
+ def self.interleave_tms_tdi_bytes(tms, tdi)
326
+
327
+ #Ensure we have two byte arrays of the same length.
328
+ raise ArgumentError, "You must specify the same amount of bytes for TDI and TMS!" if tdi.size != tms.size
329
+
330
+ #If we were given a string-like object, handle it byte by byte.
331
+ tms = tms.bytes if tms.respond_to?(:bytes)
332
+ tdi = tdi.bytes if tdi.respond_to?(:bytes)
333
+
334
+ #Merge the two arrays into a single array of byte-pairs.
335
+ byte_pairs = tms.zip(tdi)
336
+
337
+ #Convert each of the byte pairs into pairs of interleave bytes.
338
+ interleave = byte_pairs.map { |tms_byte, tdi_byte| interleave_tms_tdi_byte_pair(tms_byte, tdi_byte) }
339
+
340
+ #And flatten the pairs into a long array of interleave bytes.
341
+ interleave.flatten.pack('C*').force_encoding('UTF-8')
342
+
343
+ end
344
+
345
+ #
346
+ # Interleaves a single byte of TDI with a single byte of TMS, creating two bytes
347
+ # of interleave data.
348
+ #
349
+ def self.interleave_tms_tdi_byte_pair(tms, tdi)
350
+
351
+ #Ensure that the two values we have are represented as numbers.
352
+ tms = tms.ord
353
+ tdi = tdi.ord
354
+
355
+ #Interleave the lower and upper nibbles of each of the two values.
356
+ lower = interleave_tms_tdi_nibble_pair(tms, tdi)
357
+ upper = interleave_tms_tdi_nibble_pair(tms >> 4, tdi >> 4)
358
+
359
+ #Return the interleave bytes in little endian order.
360
+ return lower, upper
361
+
362
+ end
363
+
364
+ #
365
+ # Interleaves a single nibble of TMS and TDI data, creating a new byte.
366
+ # See transmit_interleave (or the Digilent DJTG API) for the interleave format.
367
+ #
368
+ def self.interleave_tms_tdi_nibble_pair(tms, tdi)
369
+ #Interleave the TMS and TDI values into a new byte.
370
+ new_byte = [tms[3], tdi[3], tms[2], tdi[2], tms[1], tdi[1], tms[0], tdi[0]]
371
+
372
+ #And convert the new byte into a ruby fixnum.
373
+ new_byte.join.to_i(2)
374
+ end
375
+
376
+ end
377
+ end
378
+ end
379
+