ruby-adept 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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
+