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,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
|
+
|