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,173 @@
|
|
1
|
+
require 'ffi'
|
2
|
+
require 'adept/low_level'
|
3
|
+
require 'adept/low_level/error_handler'
|
4
|
+
|
5
|
+
module Adept
|
6
|
+
module LowLevel
|
7
|
+
|
8
|
+
#Maximum possible length of a library version string.
|
9
|
+
VersionMaxLength = 256
|
10
|
+
|
11
|
+
#
|
12
|
+
# "Base" module, which provides functionality for interfacing with
|
13
|
+
# Digilent Adept low-level libraries.
|
14
|
+
#
|
15
|
+
module Library
|
16
|
+
include FFI::Library
|
17
|
+
|
18
|
+
#
|
19
|
+
# Attaches the appropriate dynamically-linked library to the calling module.
|
20
|
+
#
|
21
|
+
def wrap_adept_library(name)
|
22
|
+
|
23
|
+
#Store the prefix for the library name, which
|
24
|
+
#
|
25
|
+
@prefix = name.capitalize
|
26
|
+
#Make the adept_library function local, so it can only be used in class defintions.
|
27
|
+
#private :wrap_adept_library
|
28
|
+
|
29
|
+
#And attach the relevant dynamic library (DLL / SO).
|
30
|
+
ffi_lib "lib#{name}"
|
31
|
+
|
32
|
+
#Attach the function which queries the device runtime's version.
|
33
|
+
attach_adept_function :GetVersion, [:pointer]
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
#
|
38
|
+
# Attaches an adept function, handling error checking automatically.
|
39
|
+
#
|
40
|
+
def attach_adept_function(name, arguments)
|
41
|
+
|
42
|
+
#Compute the name for the Digilent Adept library.
|
43
|
+
base_name = @prefix + name.to_s
|
44
|
+
|
45
|
+
#Attach the library's version of the function...
|
46
|
+
base_function = attach_function base_name, arguments, :bool
|
47
|
+
|
48
|
+
#Make the base function protected, as not to clutter the (public) namespace.
|
49
|
+
private_class_method(base_name)
|
50
|
+
|
51
|
+
#And create our enhanced version of the function,
|
52
|
+
#which automatically raises an exception if an error occurs.
|
53
|
+
define_singleton_method(name) do |*args|
|
54
|
+
|
55
|
+
#Call the base function, and throw an exception if the call fails.
|
56
|
+
unless base_function.call(*args)
|
57
|
+
|
58
|
+
#Get the most recent error information as a raisable exception.
|
59
|
+
error = ErrorHandler.last_error
|
60
|
+
|
61
|
+
#Override the exception's backtrace so it doesn't include this anonymous singleton.
|
62
|
+
error.set_backtrace(caller(2))
|
63
|
+
|
64
|
+
#And raise the exception itself.
|
65
|
+
raise error
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
#Make the wrap_adept_library/attach_adept_function functions local, so they can only be used in class defintions.
|
73
|
+
private :wrap_adept_library
|
74
|
+
private :attach_adept_function
|
75
|
+
|
76
|
+
#
|
77
|
+
# Returns the version of the wrapped runtime, as a string.
|
78
|
+
#
|
79
|
+
def runtime_version
|
80
|
+
|
81
|
+
#create a new buffer which will hold the runtime's version
|
82
|
+
version_buffer = FFI::MemoryPointer.new(VersionMaxLength)
|
83
|
+
|
84
|
+
#get the system's version
|
85
|
+
GetVersion(version_buffer)
|
86
|
+
|
87
|
+
#and return the retrieved version
|
88
|
+
version_buffer.read_string
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
|
96
|
+
#
|
97
|
+
# Creates a C-style byte buffer containing the given item using FFI.
|
98
|
+
#
|
99
|
+
# The buffer exists in managed memory (and thus will be garbage
|
100
|
+
# collected when appropriate), but is contiguous, and thus prime
|
101
|
+
# for passing to a C interop.
|
102
|
+
#
|
103
|
+
def to_buffer(item)
|
104
|
+
|
105
|
+
#Try to convert the item to an array, and then to a string,
|
106
|
+
#if it supports it. This allows us to easily get a byte string
|
107
|
+
#from most Ruby types.
|
108
|
+
#
|
109
|
+
#Strings _shouldn't_ support either of these methods, and thus will
|
110
|
+
#pass unaltered.
|
111
|
+
#
|
112
|
+
item = item.to_a if item.respond_to?(:to_a)
|
113
|
+
item = item.pack("C*") if item.respond_to?(:pack)
|
114
|
+
|
115
|
+
#Create a new buffer, and fill it with our byte string.
|
116
|
+
buffer = FFI::MemoryPointer.new(item.byte_size)
|
117
|
+
buffer.put_bytes(0, item)
|
118
|
+
|
119
|
+
#And return the filled buffer.
|
120
|
+
return buffer
|
121
|
+
|
122
|
+
end
|
123
|
+
|
124
|
+
|
125
|
+
#
|
126
|
+
# Recieves a byte string via a C-style byte buffer.
|
127
|
+
#
|
128
|
+
# types: A list of types to be received, in the same format as accepted by
|
129
|
+
# FFI::MemoryPointer. :int, 8, and :long are all acceptable types.
|
130
|
+
#
|
131
|
+
# Must be called with a block, which should fill the byte buffer.
|
132
|
+
# Yields a c-style pointer to each of the created buffers, in the order
|
133
|
+
# they were specified as arguments.
|
134
|
+
#
|
135
|
+
def receive_out_arguments(*types)
|
136
|
+
|
137
|
+
#Create a pointer to each of the requested types.
|
138
|
+
pointers = types.map { |type| FFI::MemoryPointer.new(type) }
|
139
|
+
|
140
|
+
#Yield each of the pointers to the given block.
|
141
|
+
yield(*pointers)
|
142
|
+
|
143
|
+
#Read each of the byte-buffers given.
|
144
|
+
results = types.zip(pointers).map do |type, pointer|
|
145
|
+
|
146
|
+
#If we've been passed a buffer type as a symbol, use the
|
147
|
+
#symbol name to figure out the appropriate reading method.
|
148
|
+
if type.kind_of?(Symbol)
|
149
|
+
|
150
|
+
#Compute the method name by adding "get_" to the device type,
|
151
|
+
#as the the FFI convention.
|
152
|
+
method_name = 'read_' + type.to_s
|
153
|
+
|
154
|
+
#And return the contents of the byte buffer.
|
155
|
+
next pointer.send(method_name)
|
156
|
+
|
157
|
+
#Otherwise, return the data in raw binary.
|
158
|
+
else
|
159
|
+
next pointer.get_string(0, type)
|
160
|
+
end
|
161
|
+
|
162
|
+
end
|
163
|
+
|
164
|
+
#If we have a single-element array, return the element directly;
|
165
|
+
#otherwise, return the array. This format works well with multiple
|
166
|
+
#assignment.
|
167
|
+
(results.count == 1) ? results.first : results
|
168
|
+
|
169
|
+
end
|
170
|
+
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
data/lib/adept.rb
ADDED
Binary file
|
@@ -0,0 +1,95 @@
|
|
1
|
+
|
2
|
+
require 'date'
|
3
|
+
|
4
|
+
require 'adept/data_formats/bitstream'
|
5
|
+
|
6
|
+
#
|
7
|
+
# Tests for the Bitstream file reader.
|
8
|
+
#
|
9
|
+
describe Adept::DataFormats::Bitstream do
|
10
|
+
|
11
|
+
#A very simple string which should _technically_ be a valid implementation of the bitstream protocol.
|
12
|
+
ValidBitFile = "\x00\x09012345678\x00\x01a\x00\x22design_name.ncd;UserID=0x0123ABCD\x00b\x00\x0C3s250ecp132\x00c\x00\x0B2012/12/29\x00d\x00\x0922:41:50\x00e\x00\x00\x00\x100123456789ABCDEF\x00"
|
13
|
+
InvalidBitFile = "\x00\x09012345678\x00\x01b\x00\x0BDesignName\x00c\x00\x09PartName\x00d\x00\x05Date\x00e\x00\x05Time\x00f\x00\x00\x00\x10012345689ABCDEF\x00"
|
14
|
+
|
15
|
+
let(:bitstream_array) { "0123456789ABCDEF".bytes.collect { |b| Adept::DataFormats::Bitstream::send(:reverse_byte, b) } }
|
16
|
+
|
17
|
+
context "when provided with a valid bitstream" do
|
18
|
+
subject { Adept::DataFormats::Bitstream.from_string(ValidBitFile) }
|
19
|
+
|
20
|
+
it "should read the header from the start of the subject" do
|
21
|
+
subject.header.should == "012345678"
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should read the design information from the second field in the file" do
|
25
|
+
subject.info.should == "design_name.ncd;UserID=0x0123ABCD"
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should read the part number from the third field in the file" do
|
29
|
+
subject.part.should == "3s250ecp132"
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should read the date from the fourth field of the file" do
|
33
|
+
subject.raw_date.should == "2012/12/29"
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should read the time from the fifth field of the file" do
|
37
|
+
subject.raw_time.should == "22:41:50"
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should read the bitsream itself from the remainder of the file" do
|
41
|
+
subject.raw_bitstream.should == "0123456789ABCDEF".unpack("C*")
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "#filename" do
|
45
|
+
it "should extract the filename from the design information" do
|
46
|
+
subject.filename.should == "design_name.ncd"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe "#usercode" do
|
51
|
+
it "should extract the usercode from the design information" do
|
52
|
+
subject.usercode.should == "0123ABCD"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe "#time_created" do
|
57
|
+
it "should extract the time and date that the bitstream was created as a ruby DateTime" do
|
58
|
+
subject.time_created.should == DateTime.new(2012, 12, 29, 22, 41, 50)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe "#to_a" do
|
63
|
+
it "should create a valid array equal to the bitstream's contents with each byte reversed." do
|
64
|
+
subject.to_a.should == bitstream_array
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe "#to_s" do
|
69
|
+
it "should return the binary data from the bitstream with the header removed" do
|
70
|
+
subject.to_s.should == bitstream_array.pack("C*")
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
context "when provided with an invalid bitstream" do
|
77
|
+
|
78
|
+
it "should raise an exception" do
|
79
|
+
expect { Adept::DataFormats::Bitstream.from_string(InvalidBitFile) }.to raise_error(BinData::ValidityError)
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
describe "#reverse_byte" do
|
85
|
+
subject { Adept::DataFormats::Bitstream }
|
86
|
+
|
87
|
+
it "should correct reverse the bits in a single byte" do
|
88
|
+
subject.send(:reverse_byte, 0xF0).should == 0x0F
|
89
|
+
subject.send(:reverse_byte, 0xAA).should == 0x55
|
90
|
+
subject.send(:reverse_byte, 0xDE).should == 0x7B
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
|
2
|
+
require 'adept'
|
3
|
+
require 'adept/data_formats'
|
4
|
+
|
5
|
+
#
|
6
|
+
#Use FakeFS, so none of the calls below actually touch the filesystem.
|
7
|
+
#
|
8
|
+
require 'rspec/mocks'
|
9
|
+
require 'rspec/expectations'
|
10
|
+
require 'fakefs/spec_helpers'
|
11
|
+
|
12
|
+
include Adept
|
13
|
+
|
14
|
+
#
|
15
|
+
# Tests for the DataFactories module.
|
16
|
+
#
|
17
|
+
describe Adept::DataFormats::DataFactories do
|
18
|
+
subject { Object.new.extend(Adept::DataFormats::DataFactories) }
|
19
|
+
|
20
|
+
describe "#from_file" do
|
21
|
+
include FakeFS::SpecHelpers
|
22
|
+
|
23
|
+
before :each do
|
24
|
+
File::open('test', 'w') { |x| x.write('ABCDE') }
|
25
|
+
end
|
26
|
+
|
27
|
+
context "when given a filename" do
|
28
|
+
it "should call from_string with the file's contents" do
|
29
|
+
subject.should_receive(:from_string).with('ABCDE')
|
30
|
+
subject.from_file('test')
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context "when given a file object" do
|
35
|
+
it "should call from_string with the file's contents" do
|
36
|
+
subject.should_receive(:from_string).with('ABCDE')
|
37
|
+
File::open('test', 'r') { |file| subject.from_file(file) }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
#
|
2
|
+
# These tests assume _one_ single connected Basys2 board!
|
3
|
+
#
|
4
|
+
|
5
|
+
require 'adept'
|
6
|
+
|
7
|
+
#Pull the relevant modules into the main namespace, for convenience.
|
8
|
+
include Adept
|
9
|
+
|
10
|
+
# Specification for the Adept Device interface.
|
11
|
+
# These tests assume _only_ one connected Basys2 board!
|
12
|
+
#
|
13
|
+
describe Device do
|
14
|
+
|
15
|
+
describe ".connected_devices" do
|
16
|
+
|
17
|
+
it "should be able to enumerate the connected devices", :online => true do
|
18
|
+
|
19
|
+
devices = Adept::Device.connected_devices
|
20
|
+
|
21
|
+
#We should detect only a single device; which should be our Basys2 board.
|
22
|
+
devices.count.should == 1
|
23
|
+
devices.first[:name].should == 'Basys2'
|
24
|
+
devices.first[:path].should include('usb')
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
describe ".open" do
|
31
|
+
|
32
|
+
it "should be able to connect to a board by path", :online => true do
|
33
|
+
|
34
|
+
#Get the path of a connected device.
|
35
|
+
path = Adept::Device.connected_devices.first[:path]
|
36
|
+
|
37
|
+
#And open the device.
|
38
|
+
device = Adept::Device.open(path)
|
39
|
+
|
40
|
+
#The connected handle should be non-zero.
|
41
|
+
device.handle.should_not == 0
|
42
|
+
|
43
|
+
#Close the device afterwards
|
44
|
+
device.close
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe ".by_name" do
|
50
|
+
|
51
|
+
it "should be able to connect to a device by name", :online => true do
|
52
|
+
|
53
|
+
#Try to connect to a Basys board...
|
54
|
+
device = Adept::Device.by_name('Basys2')
|
55
|
+
|
56
|
+
#Ensure we got a valid handle.
|
57
|
+
device.handle.should_not == 0
|
58
|
+
|
59
|
+
#Close the device afterwards
|
60
|
+
device.close
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
describe "post-connection tasks", :online => true do
|
67
|
+
|
68
|
+
before :each do
|
69
|
+
@device = Adept::Device.by_name('Basys2')
|
70
|
+
end
|
71
|
+
|
72
|
+
after :each do
|
73
|
+
@device.close
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
it "should be able to determine the supported connections for a Basys2 board" do
|
78
|
+
|
79
|
+
#We should support JTAG
|
80
|
+
@device.supported_connections.should include(JTAG::Connection)
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
#TODO: ensure that the device-list is free'd afterwards
|
87
|
+
|
88
|
+
end
|