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