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,162 @@
1
+ #
2
+ # FPGA JTAG Target
3
+ #
4
+
5
+ require 'adept/jtag'
6
+
7
+ module Adept
8
+ module JTAG
9
+ module Devices
10
+
11
+ #
12
+ # Base module for JTAG devices.
13
+ #
14
+ class FPGA < Device
15
+
16
+ #Basic device definitions.
17
+ InstructionWidth = 6
18
+ supports_idcode "X1c1a093", "X1c10093"
19
+
20
+ #Supported boundary-scan instructions.
21
+ Instructions = \
22
+ {
23
+ :extest => 0b001111,
24
+ :sample => 0b000001,
25
+ :preload => 0b000001, # Same as :sample
26
+ :user1 => 0b000010, # Not available until after configuration
27
+ :user2 => 0b000011, # Not available until after configuration
28
+ :cfg_out => 0b000100, # Not available during configuration with another mode.
29
+ :cfg_in => 0b000101, # Not available during configuration with another mode.
30
+ :intest => 0b000111,
31
+ :usercode => 0b001000,
32
+ :idcode => 0b001001,
33
+ :highz => 0b001010,
34
+ :jprogram => 0b001011, # Not available during configuration with another mode.
35
+ :jstart => 0b001100, # Not available during configuration with another mode.
36
+ :jshutdown => 0b001101, # Not available during configuration with another mode.
37
+ :bypass => 0b111111,
38
+ :isc_enable => 0b010000,
39
+ :isc_program => 0b010001,
40
+ :isc_noop => 0b010101,
41
+ :isc_disable => 0b010110
42
+ }
43
+
44
+ #Database which maps IDCodes to bit-file part numbers.
45
+ #Used to validate
46
+ PartIdcodes = \
47
+ {
48
+ '3s100ecp132' => 'X1c10093',
49
+ '3s250ecp132' => 'X1c1a093'
50
+ }
51
+
52
+ ConfigurationStartup = 14_000
53
+ FPGAStartup = 100
54
+
55
+ #
56
+ # Verifies the device's IDcode using the explicit IDCode instruction.
57
+ #
58
+ def verify_idcode
59
+
60
+ #Put the device into IDCode retrival mode.
61
+ self.instruction = :idcode
62
+
63
+ #And attempt to retrieve the 32-bit IDcode.
64
+ id_code = receive_data(32).reverse
65
+
66
+ #If the two IDcodes don't match, raise an error.
67
+ raise JTAG::Error, "IDCode verification failed! Expected: #{@idcode.unpack("H*")}, receieved #{id_code.unpack("H*")}. " unless id_code == @idcode
68
+
69
+ end
70
+
71
+ #
72
+ # Returns the "user code", an ID number which identifies the configuration of the FPGA.
73
+ #
74
+ def usercode
75
+
76
+ #Put the device into IDCode retrival mode.
77
+ self.instruction = :usercode
78
+
79
+ #And attempt to retrieve the 32-bit IDcode.
80
+ usercode_packed = receive_data(32).reverse
81
+
82
+ #Return the usercode as a hex string.
83
+ usercode_packed.unpack("H*").first.upcase
84
+
85
+ end
86
+
87
+ #
88
+ # Configures (programs) the given FPGA.
89
+ #
90
+ def configure(bitstream)
91
+
92
+ validate_bitstream(bitstream)
93
+
94
+ #Send the bitstream to the FPGA.
95
+ initialize_configuration
96
+ transmit_data(bitstream.to_s)
97
+ finalize_configuration
98
+
99
+ #And verify that the programming succeeded.
100
+ unless bitstream.usercode == usercode || bitstrea.usercode.nil?
101
+ raise ProgrammingError, "Programming failed; expected a usercode of #{bitstream.usercode}, recieved #{usercode}."
102
+ end
103
+
104
+ end
105
+
106
+ def part_name
107
+ connected_part, _ = PartIdcodes.find { |part, mask| self.class.idcode_matches_mask(mask, @idcode) }
108
+ connected_part
109
+ end
110
+
111
+ #
112
+ # Returns true iff the provided bitstream is intended for this FPGA.
113
+ #
114
+ def supports_bitstream?(bitstream)
115
+ self.class.idcode_matches_mask(PartIdcodes[bitstream.part], @idcode)
116
+ end
117
+
118
+
119
+ private
120
+
121
+ #
122
+ # Check to ensure that the provided bit-stream is intended for this part.
123
+ #
124
+ def validate_bitstream(bitstream)
125
+ unless supports_bitstream?(bitstream)
126
+ raise ProgrammingError, "The provided bitstream was intended for an '#{bitstream.part}', but a '#{part_name}' is connected."
127
+ end
128
+ end
129
+
130
+ #
131
+ # Performs the initial steps which ready the FPGA for configuration.
132
+ #
133
+ def initialize_configuration
134
+
135
+ #Pulse the Program pin via JTAG.
136
+ self.instruction = :jprogram
137
+
138
+ #Put the device into configuration mode, and give it 14,000 cycles to start up.
139
+ self.instruction = :cfg_in
140
+ run_test(ConfigurationStartup)
141
+
142
+ end
143
+
144
+ #
145
+ # Performs the final steps which start up a configured program.
146
+ #
147
+ def finalize_configuration
148
+
149
+ #Put the FPGA into startup mode...
150
+ self.instruction = :jstart
151
+
152
+ #And then allow the FPGA to run normally.
153
+ run_test(FPGAStartup)
154
+
155
+ end
156
+
157
+
158
+
159
+ end
160
+ end
161
+ end
162
+ end
File without changes
@@ -0,0 +1,23 @@
1
+ #
2
+ # FPGA Configuration Platform Flash
3
+ #
4
+
5
+ require 'adept/jtag'
6
+
7
+ module Adept
8
+ module JTAG
9
+ module Devices
10
+
11
+ #
12
+ # Base module for JTAG devices.
13
+ #
14
+ class PlatformFlash < Device
15
+
16
+ InstructionWidth = 8
17
+ supports_idcode "X5045093"
18
+
19
+ end
20
+
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,2 @@
1
+ require 'require_all'
2
+ require_rel 'devices'
@@ -0,0 +1,8 @@
1
+ require 'adept/error'
2
+
3
+ module Adept
4
+ module JTAG
5
+ class Error < Adept::Error; end
6
+ class ProgrammingError < Error; end
7
+ end
8
+ end
@@ -0,0 +1,67 @@
1
+
2
+ module Adept
3
+ module JTAG
4
+ module TAPStates
5
+
6
+ #
7
+ # Base for all Test Access Port states.
8
+ #
9
+ module TAPState
10
+
11
+ #
12
+ # Returns the successor to the current state, given an input value.
13
+ #
14
+ def next_state(value)
15
+
16
+ #Get the name of the module which describes the successor state...
17
+ state = self::NextState[value]
18
+
19
+ #... and get a reference to the module itself.
20
+ TAPStates.const_get(state)
21
+
22
+ end
23
+
24
+ #
25
+ # Determines the next value which should be present on the mode-set line
26
+ # to get to the given state.
27
+ #
28
+ def next_hop_towards(state)
29
+
30
+ #Get a refrence to the state module indicated by the TowardsZeroIfStateIs metaconstant.
31
+ towards_zero = TAPStates.const_get(self::NextState[0])
32
+ towards_one = TAPStates.const_get(self::NextState[1])
33
+
34
+ #Determine if a next-hop of one would cause us to get stuck in a loop.
35
+ towards_one_would_cause_loop = (towards_one == self || self::NextState[1] == :Reset)
36
+
37
+ #If the next state would be achievable by providing a hop of zero,
38
+ #or a hop of one would cause a loop, then the next hop should be zero.
39
+ #
40
+ #Otherwise, the next hop should be '1'.
41
+ ((state == towards_zero) || towards_one_would_cause_loop) ? [0, towards_zero] : [1, towards_one]
42
+ end
43
+
44
+ end
45
+
46
+ #
47
+ # Dynamically create a new TAPState module.
48
+ #
49
+ def self.tap_state(name, next_state)
50
+
51
+ #Create a new Module for the new TAP state...
52
+ mod = Module.new
53
+
54
+ #... and assign it the given name.
55
+ self.const_set(name, mod)
56
+
57
+ #Add the TAPState methods to the new module...
58
+ mod.extend(TAPState)
59
+
60
+ #And set the module's NextState constant, as given.
61
+ mod.const_set(:NextState, next_state)
62
+
63
+ end
64
+
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,52 @@
1
+
2
+ require 'adept/jtag/tap_state'
3
+
4
+ module Adept
5
+ module JTAG
6
+ module TAPStates
7
+
8
+ #
9
+ # JTAG Test Access Port Finite State Machine
10
+ #
11
+
12
+ tap_state :Reset, 0 => :Idle, 1 => :Reset
13
+ tap_state :Idle, 0 => :Idle, 1 => :SelectDR
14
+
15
+ #Data Register Access States
16
+ tap_state :SelectDR, 0 => :CaptureDR, 1 => :SelectIR
17
+ tap_state :CaptureDR, 0 => :ShiftDR, 1 => :Exit1DR
18
+ tap_state :ShiftDR, 0 => :ShiftDR, 1 => :Exit1DR
19
+ tap_state :Exit1DR, 0 => :PauseDR, 1 => :UpdateDR
20
+ tap_state :PauseDR, 0 => :PauseDR, 1 => :Exit2DR
21
+ tap_state :Exit2DR, 0 => :ShiftDR, 1 => :UpdateDR
22
+ tap_state :UpdateDR, 0 => :Idle, 1 => :SelectDR
23
+
24
+ #Instruction Register Access States
25
+ tap_state :SelectIR, 0 => :CaptureIR, 1 => :Reset
26
+ tap_state :CaptureIR, 0 => :ShiftIR, 1 => :Exit1IR
27
+ tap_state :ShiftIR, 0 => :ShiftIR, 1 => :Exit1IR
28
+ tap_state :Exit1IR, 0 => :PauseIR, 1 => :UpdateIR
29
+ tap_state :PauseIR, 0 => :PauseIR, 1 => :Exit2IR
30
+ tap_state :Exit2IR, 0 => :ShiftIR, 1 => :UpdateIR
31
+ tap_state :UpdateIR, 0 => :Idle, 1 => :SelectDR
32
+
33
+
34
+ #
35
+ # Override the FSM's base behavior in the SelectDR state, as that state
36
+ # is the "branch point" between the instruction register and the data-register columns.
37
+ #
38
+ module SelectDR
39
+
40
+ #
41
+ # Override the heuristic for path-finding within the TAP finite state machine;
42
+ # move to the appropriate column depending on if we're looking for an Data Register
43
+ # or Instruction Register access.
44
+ #
45
+ def self.next_hop_towards(state)
46
+ #If we're looking for an instruction state, continue to the next column.
47
+ (state.name =~ /IR$/) ? 1 : 0
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
data/lib/adept/jtag.rb ADDED
@@ -0,0 +1,11 @@
1
+
2
+ require 'adept/jtag/error'
3
+
4
+ #Basic components.
5
+ require 'adept/jtag/tap_states'
6
+ require 'adept/jtag/connection'
7
+ require 'adept/jtag/device'
8
+
9
+ #Load each of the defined JTAG devices
10
+ require 'adept/jtag/devices'
11
+
@@ -0,0 +1,59 @@
1
+
2
+ require 'ffi'
3
+
4
+ module Adept
5
+ module LowLevel
6
+
7
+ #
8
+ # Module mix-in which adds the basic functionality for
9
+ #
10
+ module Connection
11
+
12
+ #
13
+ # Hook which runs whenever a module extends AdeptConnection.
14
+ #
15
+ def self.extended(base)
16
+
17
+ #Attach each of the relevant functions to the extending class.
18
+ base.module_eval do
19
+
20
+ #Returns the amount of connections the given port provides.
21
+ attach_adept_function :GetPortCount, [:ulong, :pointer]
22
+
23
+ #Enable the JTAG port with the given number. Only one JTAG device can be active at a time!
24
+ attach_adept_function :EnableEx, [:ulong, :int32]
25
+
26
+ #Disable the currently active JTAG port.
27
+ attach_adept_function :Disable, [:ulong]
28
+
29
+ end
30
+ end
31
+
32
+ #
33
+ # Returns the number of JTAG ports the given device offers.
34
+ #
35
+ def port_count(device)
36
+
37
+ #Create a pointer to a new Int32 in memory...
38
+ count_pointer = FFI::MemoryPointer.new(:int32)
39
+
40
+ #... and fill it with the number of available JTAG ports.
41
+ GetPortCount(device, count_pointer)
42
+
43
+ #Return the acquired count as a ruby integer.
44
+ count_pointer.get_int32(0)
45
+
46
+ end
47
+
48
+ #
49
+ # Returns a true-like value if the given device supports JTAG,
50
+ # or nil if it does not.
51
+ #
52
+ def supported?(device)
53
+ port_count(device).nonzero?
54
+ end
55
+
56
+ end
57
+
58
+ end
59
+ end
@@ -0,0 +1,43 @@
1
+ require 'ffi'
2
+ require 'adept/low_level/device_manager'
3
+
4
+ module Adept
5
+ module LowLevel
6
+
7
+ #Maximum length for an Adept device's name, including a null terminator.
8
+ NameMaxLength = 64
9
+
10
+ #Maximum length for an Adept device path, including a null terminator.
11
+ PathMaxLength = 260 + 1
12
+
13
+ #
14
+ # Structure used by the Adept SDK to represent an adept device.
15
+ #
16
+ class Device < FFI::Struct
17
+
18
+ #Set up the structure's layout.
19
+ layout :name, [:char, NameMaxLength], #char[NameMaxLength]
20
+ :path, [:char, PathMaxLength], #char[PathMaxLength]
21
+ :transport, :ulong
22
+
23
+ #
24
+ #Convert the given device record into a ruby hash.
25
+ #
26
+ def to_h
27
+ result = Hash[members.zip(values)]
28
+
29
+ #Convert the internal character arrays to ruby strings.
30
+ result[:name] = result[:name].to_s
31
+ result[:path] = result[:path].to_s
32
+
33
+ #And convert the device's transport to a string.
34
+ result[:transport] = DeviceManager::get_transport_name(result[:transport])
35
+
36
+ #return the resultant hash
37
+ result
38
+ end
39
+
40
+ end
41
+
42
+ end
43
+ end
@@ -0,0 +1,22 @@
1
+
2
+ module Adept
3
+ module LowLevel
4
+
5
+ #
6
+ # Wrapper for Digilent Device Manager errors, which allows
7
+ # errors raised from the low-level SDK to be handled like ruby exceptions.
8
+ #
9
+ class DeviceError < StandardError
10
+
11
+ attr_accessor :short_name
12
+ attr_accessor :code
13
+
14
+ def initialize(message, short_name, code=nil)
15
+ @short_name = short_name
16
+ @code = code
17
+ super(message)
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,142 @@
1
+ require 'ffi'
2
+ require 'adept/low_level/device'
3
+ require 'adept/low_level/library'
4
+
5
+ module Adept
6
+ module LowLevel
7
+
8
+ #Stores the maximum length of a transport string, will terminating null.
9
+ TransportMaxLength = 16
10
+
11
+ #
12
+ # DeviceManager (DMGR)
13
+ # Wrapper for the low-level Adept device management functionality.
14
+ #
15
+ module DeviceManager
16
+ extend LowLevel::Library
17
+
18
+ #Wrap the device manager library, libDMGR
19
+ wrap_adept_library 'dmgr'
20
+
21
+
22
+ #
23
+ # Enumeration functions.
24
+ #
25
+
26
+ #Get the list of all detected Adept devices; should be followed by the accompanying FreeDvcEnum call.
27
+ attach_adept_function :EnumDevices, [:pointer]
28
+
29
+ #Free the internal list of connected devices.
30
+ attach_adept_function :FreeDvcEnum, []
31
+
32
+
33
+ #
34
+ # Populate the internal list of connected devices.
35
+ # Returns the total amount of devices enumerated.
36
+ #
37
+ def self.populate_device_list
38
+
39
+ #Create a pointer to a new C integer.
40
+ count_pointer = FFI::MemoryPointer.new(:int)
41
+
42
+ #Enumerate all of the connected devices, and retrieve the amount of enumerated devices.
43
+ EnumDevices(count_pointer)
44
+
45
+ #Dereference the count pointer, extracting the number of devices present.
46
+ count_pointer.get_int(0)
47
+
48
+ end
49
+
50
+ #
51
+ # Free the internal list of connected devices.
52
+ #
53
+ def self.free_device_list
54
+ FreeDvcEnum()
55
+ end
56
+
57
+
58
+ #
59
+ # Device Information-Query Functions
60
+ #
61
+
62
+ #Get all enumeration information regarding the current device.
63
+ attach_adept_function :GetDvc, [:int, :pointer]
64
+
65
+ #Get a string describing the transport
66
+ attach_adept_function :GetDtpString, [:ulong, :pointer]
67
+
68
+ #
69
+ # Returns a single record from the internal device enumeration list.
70
+ # This record contains low-level information about how to connect to the device.
71
+ #
72
+ def self.get_device_info(device_number)
73
+
74
+ #Create a new, empty low-level device structure.
75
+ device = Device.new
76
+
77
+ #Get the device's information from the internal enumeration table.
78
+ GetDvc(device_number, device.pointer)
79
+
80
+ #And return the newly-fetched device object as a ruby hash.
81
+ device.to_h
82
+
83
+ end
84
+
85
+ #
86
+ # Returns the transport name for a given transport ID ("DTP").
87
+ #
88
+ def self.get_transport_name(transport_id)
89
+
90
+ #Create a string buffer for the transport's name...
91
+ string_pointer = FFI::MemoryPointer.new(TransportMaxLength)
92
+
93
+ #... and fill it with the relevant transport's description.
94
+ GetDtpString(transport_id, string_pointer)
95
+
96
+ #Return the retrieved string.
97
+ string_pointer.read_string
98
+
99
+ end
100
+
101
+ #
102
+ # Device Open/Close Functions
103
+ #
104
+
105
+ #Open the device at the specified path.
106
+ attach_adept_function :Open, [:pointer, :string]
107
+
108
+ #Close the device with the specified handle.
109
+ attach_adept_function :Close, [:ulong]
110
+
111
+ #
112
+ # Opens the device at the given path, and returns an interface handle.
113
+ #
114
+ def self.open_device(path)
115
+
116
+ #Create a pointer to a new C long.
117
+ handle_pointer = FFI::MemoryPointer.new(:ulong)
118
+
119
+ #Open the device at the given path, retrieving the newly-created device handle.
120
+ Open(handle_pointer, path)
121
+
122
+ #Dereference the handle pointer, retrieving the handle itself.
123
+ handle = handle_pointer.get_ulong(0)
124
+
125
+ #If we recieved a handle of zero (C's NULL), convert that to nil;
126
+ #otherwise, return the handle directly.
127
+ handle.zero?() ? nil : handle
128
+
129
+ end
130
+
131
+ #
132
+ # Closes the device which is referenced by the given interface handle.
133
+ #
134
+ def self.close_device(handle)
135
+ Close(handle)
136
+ end
137
+
138
+
139
+ end
140
+
141
+ end
142
+ end