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,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
@@ -0,0 +1,4 @@
1
+ require 'adept/low_level/device'
2
+ require 'adept/low_level/device_manager'
3
+ require 'adept/low_level/jtag'
4
+ require 'adept/low_level/enhanced_parallel'
@@ -0,0 +1,3 @@
1
+ module Adept
2
+ VERSION = "0.0.1"
3
+ end
data/lib/adept.rb ADDED
@@ -0,0 +1,11 @@
1
+ require "adept/version"
2
+
3
+ require 'adept/low_level'
4
+ require 'adept/error'
5
+ require 'adept/device'
6
+
7
+ require 'adept/data_formats'
8
+ require 'adept/connection_provider'
9
+ require 'adept/jtag'
10
+
11
+ require 'adept/boards'
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