origen_link 0.4.2 → 0.4.3

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.
@@ -1,218 +1,218 @@
1
- # OrigenLink::Server::Pin class manipulate input/output pins of the Udoo
2
- # using exported file objects. If the pin is not exported, it
3
- # will be exported when a pin is initialized
4
- #
5
-
6
- module OrigenLink
7
- module Server
8
- # The server pin class is used to perform IO using the UDOO pins
9
- class Pin
10
- @@pin_setup = {
11
- in: 'in',
12
- out: 'out'
13
- }
14
-
15
- # True if this pin exists in /sys/class/gpio after export. False otherwise.
16
- attr_reader :gpio_valid
17
- # This pin's vector data for the pattern cycle being executed
18
- attr_accessor :pattern_data
19
- # This pin's index in the vector data received from the plug-in app
20
- attr_accessor :pattern_index
21
- # This pin's response for the executing vector
22
- attr_accessor :response
23
-
24
- # Export the pin io files through the system and take control of the IO
25
- #
26
- # This method will execute system command
27
- # "echo #{ionumber} > /sys/class/gpio/export"
28
- # to create the IO file interface. It will
29
- # set the direction, initial pin state and initialize
30
- # instance variables
31
- #
32
- # Receives
33
- # ionumber - required, value indicating the pin number (BCM IO number,
34
- # not the header pin number)
35
- # direction - optional, specifies the pin direction. A pin is
36
- # initialized as an input if a direction isn't specified.
37
- #
38
- def initialize(ionumber, direction = :in)
39
- @ionumber = Integer(ionumber)
40
- @pin_dir_name = "#{Server.gpio_dir}/gpio#{@ionumber}/direction"
41
- @pin_val_name = "#{Server.gpio_dir}/gpio#{@ionumber}/value"
42
- if !File.exist?(@pin_dir_name)
43
- system("echo #{@ionumber} > #{Server.gpio_dir}/export")
44
- sleep 0.05
45
- if $CHILD_STATUS == 0
46
- @gpio_valid = true
47
- else
48
- @gpio_valid = false
49
- end
50
- else
51
- @gpio_valid = true
52
- end
53
- if @gpio_valid
54
- if File.writable?(@pin_dir_name)
55
- @pin_dir_obj = File.open(@pin_dir_name, 'w')
56
- update_direction(direction)
57
- else
58
- @gpio_valid = false
59
- puts "#{@pin_dir_name} is not writable. Fix permissions or run as super user."
60
- end
61
- @pin_val_obj = File.open(@pin_val_name, 'r+') if @gpio_valid
62
- end
63
- @pattern_data = ''
64
- @pattern_index = -1
65
- @response = 'W'
66
- @cycle_failure = false
67
- @data_is_drive = false
68
- end
69
-
70
- # data_is_drive?
71
- # returns whether the current pattern data is drive
72
- def data_is_drive?
73
- @data_is_drive
74
- end
75
-
76
- # data_is_compare?
77
- # returns whether the current pattern data is compare
78
- def data_is_compare?
79
- !@data_is_drive
80
- end
81
-
82
- # cycle_failure
83
- # returns a boolean indicating pass/fail status of this pin
84
- def cycle_failure
85
- if @response == 'W'
86
- true # force failure if no operation performed
87
- else
88
- @cycle_failure
89
- end
90
- end
91
-
92
- # load_pattern_data
93
- # Grab this pin's data character from the pattern data
94
- def load_pattern_data(cycle)
95
- if @pattern_index > -1
96
- @pattern_data = cycle[@pattern_index]
97
- @response = 'W'
98
- @cycle_failure = false
99
- if @pattern_data == '1' || @pattern_data == '0'
100
- @data_is_drive = true
101
- else
102
- @data_is_drive = false
103
- end
104
- else
105
- @gpio_valid = false
106
- end
107
- end
108
-
109
- # process_event(event)
110
- # perform the requested pin operation and update the response (if required)
111
- def process_event(operation, requested_action)
112
- if operation == :drive
113
- if data_is_drive?
114
- if requested_action == 'data'
115
- out(@pattern_data)
116
- @response = @pattern_data
117
- else
118
- out(requested_action)
119
- end # requested_action == 'data'
120
- end # data_is_drive?
121
- end # operation == :drive
122
- if operation == :compare
123
- if data_is_compare?
124
- if requested_action == 'data'
125
- case self.in
126
- when '0'
127
- @cycle_failure = true if @pattern_data == 'H'
128
- if @pattern_data == 'X'
129
- @response = '.'
130
- else
131
- @response = 'L'
132
- end
133
- # end of when '0'
134
- when '1'
135
- @cycle_failure = true if @pattern_data == 'L'
136
- if @pattern_data == 'X'
137
- @response = '`'
138
- else
139
- @response = 'H'
140
- end
141
- # end of when '1'
142
- else
143
- @response = 'W'
144
- end # case
145
- end # requested_action == 'data'
146
- end # data_is_compare?
147
- end # operation == :compare
148
- end
149
-
150
- # Close the file IO objects associated with this pin
151
- def destroy
152
- if @gpio_valid
153
- @pin_dir_obj.close
154
- @pin_val_obj.close
155
- # system("echo #{@ionumber} > /sys/class/gpio/unexport")
156
- # puts "pin #{@ionumber} is no longer exported"
157
- end
158
- end
159
-
160
- # Sets the output state of the pin.
161
- #
162
- # If the pin is setup as an input,
163
- # the direction will first be changed to output.
164
- #
165
- def out(value)
166
- if @gpio_valid
167
- if @direction == :in
168
- update_direction(:out)
169
- end
170
- @pin_val_obj.write(value)
171
- @pin_val_obj.flush
172
- end
173
- end
174
-
175
- # Reads and returns state of the pin.
176
- #
177
- # If the pin is setup as an output, the direction will first
178
- # be changed to input.
179
- #
180
- def in
181
- if @gpio_valid
182
- if @direction == :out
183
- update_direction(:in)
184
- end
185
- # below is original read - slow to reopen every time
186
- # File.open(@pin_val_name, 'r') do |file|
187
- # file.read#.chomp
188
- # end
189
- # end original read
190
- @pin_val_obj.pos = 0
191
- @pin_val_obj.getc
192
- end
193
- end
194
-
195
- # Sets the pin direction
196
- #
197
- # Receives:
198
- # direction - specifies the pin direction. Input is default.
199
- #
200
- # Valid direction values:
201
- # :in - input
202
- # :out - output
203
- def update_direction(direction)
204
- if @gpio_valid
205
- @pin_dir_obj.pos = 0
206
- @pin_dir_obj.write(@@pin_setup[direction])
207
- @pin_dir_obj.flush
208
- @direction = direction
209
- end
210
- end
211
-
212
- # Returns 'OrigenLinPin' + io number
213
- def to_s
214
- 'OrigenLinkPin' + @ionumber.to_s
215
- end
216
- end
217
- end
218
- end
1
+ # OrigenLink::Server::Pin class manipulate input/output pins of the Udoo
2
+ # using exported file objects. If the pin is not exported, it
3
+ # will be exported when a pin is initialized
4
+ #
5
+
6
+ module OrigenLink
7
+ module Server
8
+ # The server pin class is used to perform IO using the UDOO pins
9
+ class Pin
10
+ @@pin_setup = {
11
+ in: 'in',
12
+ out: 'out'
13
+ }
14
+
15
+ # True if this pin exists in /sys/class/gpio after export. False otherwise.
16
+ attr_reader :gpio_valid
17
+ # This pin's vector data for the pattern cycle being executed
18
+ attr_accessor :pattern_data
19
+ # This pin's index in the vector data received from the plug-in app
20
+ attr_accessor :pattern_index
21
+ # This pin's response for the executing vector
22
+ attr_accessor :response
23
+
24
+ # Export the pin io files through the system and take control of the IO
25
+ #
26
+ # This method will execute system command
27
+ # "echo #{ionumber} > /sys/class/gpio/export"
28
+ # to create the IO file interface. It will
29
+ # set the direction, initial pin state and initialize
30
+ # instance variables
31
+ #
32
+ # Receives
33
+ # ionumber - required, value indicating the pin number (BCM IO number,
34
+ # not the header pin number)
35
+ # direction - optional, specifies the pin direction. A pin is
36
+ # initialized as an input if a direction isn't specified.
37
+ #
38
+ def initialize(ionumber, direction = :in)
39
+ @ionumber = Integer(ionumber)
40
+ @pin_dir_name = "#{Server.gpio_dir}/gpio#{@ionumber}/direction"
41
+ @pin_val_name = "#{Server.gpio_dir}/gpio#{@ionumber}/value"
42
+ if !File.exist?(@pin_dir_name)
43
+ system("echo #{@ionumber} > #{Server.gpio_dir}/export")
44
+ sleep 0.05
45
+ if $CHILD_STATUS == 0
46
+ @gpio_valid = true
47
+ else
48
+ @gpio_valid = false
49
+ end
50
+ else
51
+ @gpio_valid = true
52
+ end
53
+ if @gpio_valid
54
+ if File.writable?(@pin_dir_name)
55
+ @pin_dir_obj = File.open(@pin_dir_name, 'w')
56
+ update_direction(direction)
57
+ else
58
+ @gpio_valid = false
59
+ puts "#{@pin_dir_name} is not writable. Fix permissions or run as super user."
60
+ end
61
+ @pin_val_obj = File.open(@pin_val_name, 'r+') if @gpio_valid
62
+ end
63
+ @pattern_data = ''
64
+ @pattern_index = -1
65
+ @response = 'W'
66
+ @cycle_failure = false
67
+ @data_is_drive = false
68
+ end
69
+
70
+ # data_is_drive?
71
+ # returns whether the current pattern data is drive
72
+ def data_is_drive?
73
+ @data_is_drive
74
+ end
75
+
76
+ # data_is_compare?
77
+ # returns whether the current pattern data is compare
78
+ def data_is_compare?
79
+ !@data_is_drive
80
+ end
81
+
82
+ # cycle_failure
83
+ # returns a boolean indicating pass/fail status of this pin
84
+ def cycle_failure
85
+ if @response == 'W'
86
+ true # force failure if no operation performed
87
+ else
88
+ @cycle_failure
89
+ end
90
+ end
91
+
92
+ # load_pattern_data
93
+ # Grab this pin's data character from the pattern data
94
+ def load_pattern_data(cycle)
95
+ if @pattern_index > -1
96
+ @pattern_data = cycle[@pattern_index]
97
+ @response = 'W'
98
+ @cycle_failure = false
99
+ if @pattern_data == '1' || @pattern_data == '0'
100
+ @data_is_drive = true
101
+ else
102
+ @data_is_drive = false
103
+ end
104
+ else
105
+ @gpio_valid = false
106
+ end
107
+ end
108
+
109
+ # process_event(event)
110
+ # perform the requested pin operation and update the response (if required)
111
+ def process_event(operation, requested_action)
112
+ if operation == :drive
113
+ if data_is_drive?
114
+ if requested_action == 'data'
115
+ out(@pattern_data)
116
+ @response = @pattern_data
117
+ else
118
+ out(requested_action)
119
+ end # requested_action == 'data'
120
+ end # data_is_drive?
121
+ end # operation == :drive
122
+ if operation == :compare
123
+ if data_is_compare?
124
+ if requested_action == 'data'
125
+ case self.in
126
+ when '0'
127
+ @cycle_failure = true if @pattern_data == 'H'
128
+ if @pattern_data == 'X'
129
+ @response = '.'
130
+ else
131
+ @response = 'L'
132
+ end
133
+ # end of when '0'
134
+ when '1'
135
+ @cycle_failure = true if @pattern_data == 'L'
136
+ if @pattern_data == 'X'
137
+ @response = '`'
138
+ else
139
+ @response = 'H'
140
+ end
141
+ # end of when '1'
142
+ else
143
+ @response = 'W'
144
+ end # case
145
+ end # requested_action == 'data'
146
+ end # data_is_compare?
147
+ end # operation == :compare
148
+ end
149
+
150
+ # Close the file IO objects associated with this pin
151
+ def destroy
152
+ if @gpio_valid
153
+ @pin_dir_obj.close
154
+ @pin_val_obj.close
155
+ # system("echo #{@ionumber} > /sys/class/gpio/unexport")
156
+ # puts "pin #{@ionumber} is no longer exported"
157
+ end
158
+ end
159
+
160
+ # Sets the output state of the pin.
161
+ #
162
+ # If the pin is setup as an input,
163
+ # the direction will first be changed to output.
164
+ #
165
+ def out(value)
166
+ if @gpio_valid
167
+ if @direction == :in
168
+ update_direction(:out)
169
+ end
170
+ @pin_val_obj.write(value)
171
+ @pin_val_obj.flush
172
+ end
173
+ end
174
+
175
+ # Reads and returns state of the pin.
176
+ #
177
+ # If the pin is setup as an output, the direction will first
178
+ # be changed to input.
179
+ #
180
+ def in
181
+ if @gpio_valid
182
+ if @direction == :out
183
+ update_direction(:in)
184
+ end
185
+ # below is original read - slow to reopen every time
186
+ # File.open(@pin_val_name, 'r') do |file|
187
+ # file.read#.chomp
188
+ # end
189
+ # end original read
190
+ @pin_val_obj.pos = 0
191
+ @pin_val_obj.getc
192
+ end
193
+ end
194
+
195
+ # Sets the pin direction
196
+ #
197
+ # Receives:
198
+ # direction - specifies the pin direction. Input is default.
199
+ #
200
+ # Valid direction values:
201
+ # :in - input
202
+ # :out - output
203
+ def update_direction(direction)
204
+ if @gpio_valid
205
+ @pin_dir_obj.pos = 0
206
+ @pin_dir_obj.write(@@pin_setup[direction])
207
+ @pin_dir_obj.flush
208
+ @direction = direction
209
+ end
210
+ end
211
+
212
+ # Returns 'OrigenLinPin' + io number
213
+ def to_s
214
+ 'OrigenLinkPin' + @ionumber.to_s
215
+ end
216
+ end
217
+ end
218
+ end
@@ -1,469 +1,469 @@
1
- require 'origen_link/server/pin'
2
-
3
- ##################################################
4
- # OrigenLink::Server::Sequencer Class
5
- # Instance variables:
6
- # pinmap: hash with ["pin name"] = pin object
7
- # patternpinindex: hash with ["pin name"] =
8
- # integer index into vector data
9
- # patternpinorder: Array with pin names in
10
- # the vector order
11
- #
12
- # This class processes messages targeted for
13
- # pin sequencer interface (vector pattern
14
- # execution).
15
- #
16
- # Supported messages:
17
- # pin_assign (create pin mapping)
18
- # ex: "pin_assign:tck,3,extal,23,tdo,5"
19
- #
20
- # pin_patternorder (define vector pin order)
21
- # ex: "pin_patternorder:tdo,extal,tck"
22
- #
23
- # pin_cycle (execute vector data)
24
- # ex: "pin_cycle:H11"
25
- #
26
- # pin_clear (clear all setup information)
27
- # ex: "pin_clear:"
28
- #
29
- # pin_format (setup a pin with return format)
30
- # first argument is the timeset
31
- # ex: "pin_format:1,tck,rl"
32
- #
33
- # pin_timing (define when pin events happen)
34
- # timing is stored in a timeset hash
35
- # first argument is the timeset key
36
- # ex: "pin_timing:1,tdi,0,tdo,1,tms,0
37
- #
38
- # version (check version of app server is
39
- # running)
40
- # ex: "version:"
41
- # response ex: "P:0.2.0.pre0"
42
- ##################################################
43
- module OrigenLink
44
- module Server
45
- def self.gpio_dir=(path)
46
- @gpio_dir = path
47
- end
48
-
49
- def self.gpio_dir
50
- @gpio_dir || '/sys/class/gpio'
51
- end
52
-
53
- ##################################################
54
- # OrigenLink::Server::Sequencer Class
55
- #
56
- # This class processes messages targeted for
57
- # pin sequencer interface (vector pattern
58
- # execution).
59
- #
60
- # Supported messages:
61
- # pin_assign (create pin mapping)
62
- # ex: "pin_assign:tck,3,extal,23,tdo,5"
63
- #
64
- # pin_patternorder (define vector pin order)
65
- # ex: "pin_patternorder:tdo,extal,tck"
66
- #
67
- # pin_cycle (execute vector data)
68
- # ex: "pin_cycle:H11"
69
- #
70
- # pin_clear (clear all setup information)
71
- # ex: "pin_clear:"
72
- #
73
- # pin_format (setup a pin with return format)
74
- # first argument is the timeset
75
- # ex: "pin_format:1,tck,rl"
76
- #
77
- # pin_timing (define when pin events happen)
78
- # timing is stored in a timeset hash
79
- # first argument is the timeset key
80
- # ex: "pin_timing:1,tdi,0,tdo,1,tms,0
81
- #
82
- # version (check version of app server is
83
- # running)
84
- # ex: "version:"
85
- # response ex: "P:0.2.0.pre0"
86
- ##################################################
87
- class Sequencer
88
- # code version of the server (populated by start_link_server)
89
- attr_accessor :version
90
- # hash holding the pinmap ('name' => pin object)
91
- attr_reader :pinmap
92
- # array of pin names in pattern order
93
- attr_reader :patternorder
94
- # hash holding programmed time set information
95
- attr_reader :cycletiming
96
- # hash holding pin pattern order ('name' => index)
97
- attr_reader :patternpinindex
98
-
99
- ##################################################
100
- # initialize method
101
- # Create empty pinmap, pattern pin index
102
- # and pattern order instance variables
103
- ##################################################
104
- def initialize
105
- @pinmap = Hash.new(-1)
106
- @patternpinindex = Hash.new(-1)
107
- @patternorder = []
108
- @cycletiming = Hash.new(-1)
109
- @version = ''
110
- end
111
-
112
- ##################################################
113
- # processmessage method
114
- # arguments: message
115
- # message format is <group>_<command>:<args>
116
- # returns: message response
117
- #
118
- # This method splits a message into it's
119
- # command and arguments and passes this
120
- # information to the method that performs
121
- # the requested command
122
- ##################################################
123
- def processmessage(message)
124
- command = message.split(':')
125
-
126
- case command[0]
127
- when 'pin_assign'
128
- pin_assign(command[1])
129
- when 'pin_patternorder'
130
- pin_patternorder(command[1])
131
- when 'pin_cycle'
132
- pin_cycle(command[1])
133
- when 'pin_clear'
134
- pin_clear
135
- when 'pin_format'
136
- pin_format(command[1])
137
- when 'pin_timing'
138
- pin_timing(command[1])
139
- when 'pin_timingv2'
140
- pin_timingv2(command[1])
141
- when 'version'
142
- "P:#{@version}"
143
- else
144
- 'Error Invalid command: ' + command[0].to_s
145
- end
146
- end
147
-
148
- ##################################################
149
- # pin_assign method
150
- # arguments: <args> from the message request
151
- # see "processmessage" method
152
- # returns: "P:" or error message
153
- #
154
- # This method creates a pin instance for each
155
- # pin in the pin map and builds the pinmap
156
- # hash. Before the pinmap is created, any
157
- # information from a previous pattern run is
158
- # cleared.
159
- ##################################################
160
- def pin_assign(args)
161
- pin_clear
162
- success = true
163
- fail_message = ''
164
- argarr = args.split(',')
165
- 0.step(argarr.length - 2, 2) do |index|
166
- @pinmap[argarr[index]] = Pin.new(argarr[index + 1])
167
- unless @pinmap[argarr[index]].gpio_valid
168
- success = false
169
- fail_message = fail_message + 'pin ' + argarr[index] + ' gpio' + argarr[index + 1] + ' is invalid'
170
- end
171
- end
172
- if success
173
- 'P:'
174
- else
175
- 'F:' + fail_message
176
- end
177
- end
178
-
179
- ########################################################
180
- # pin_timingv2 method
181
- # arguments: <args> from the message request
182
- # Should be '1,drive,5,pin,10,pin2,|compare,0,pin3'
183
- # First integer is timeset number
184
- #
185
- # returns "P:" or error message
186
- #
187
- # This method sets up a time set. Any arbitrary
188
- # waveform can be generated
189
- #
190
- ########################################################
191
- def pin_timingv2(args)
192
- # get the tset number
193
- argarr = args.split(',')
194
- tset_key = argarr.delete_at(0).to_i
195
- new_timeset(tset_key)
196
- args = argarr.join(',')
197
-
198
- invalid_pins = []
199
-
200
- # process and load the timeset
201
- waves = args.split('|')
202
- waves.each do |w|
203
- argarr = w.split(',')
204
- wave_type = argarr.delete_at(0)
205
- event_data_key = wave_type + '_event_data'
206
- event_pins_key = wave_type + '_event_pins'
207
- w = argarr.join(',')
208
- events = w.split(';')
209
- events.each do |e|
210
- argarr = e.split(',')
211
- event_key = argarr.delete_at(0).to_f
212
- @cycletiming[tset_key]['events'] << event_key
213
- @cycletiming[tset_key][event_data_key][event_key] = [] unless @cycletiming[tset_key][event_data_key].key?(event_key)
214
- @cycletiming[tset_key][event_data_key][event_key] << argarr.delete_at(0)
215
- # now load the pins for this event
216
- @cycletiming[tset_key][event_pins_key][event_key] = [] unless @cycletiming[tset_key][event_pins_key].key?(event_key)
217
- @cycletiming[tset_key][event_pins_key][event_key] << []
218
- argarr.each do |pin|
219
- if @pinmap.key?(pin)
220
- @cycletiming[tset_key][event_pins_key][event_key].last << @pinmap[pin]
221
- else
222
- invalid_pins << pin
223
- end
224
- end # of argarr.each
225
- end # of events.each
226
- end # of waves.each
227
- @cycletiming[tset_key]['events'].uniq!
228
- @cycletiming[tset_key]['events'].sort!
229
-
230
- # return result
231
- if invalid_pins.size > 0
232
- 'F:Invalid pins (not in pinmap): ' + invalid_pins.join(', ')
233
- else
234
- 'P:'
235
- end
236
- end # of def pin_timingv2
237
-
238
- ##################################################
239
- # new_timeset(tset)
240
- # creates a new empty timeset hash
241
- #
242
- # timing format:
243
- # ['events'] = [0, 5, 10, 35]
244
- # ['drive_event_data'] = {
245
- # 0: ['data']
246
- # 10: ['data','0']
247
- # 35: ['0']
248
- # }
249
- # ['drive_event_pins'] = {
250
- # 0: [[pin_obj1, pin_obj2]]
251
- # 10: [[pin1,pin2], [pin3]]
252
- # etc.
253
- # }
254
- ##################################################
255
- def new_timeset(tset)
256
- @cycletiming[tset] = {}
257
- @cycletiming[tset]['events'] = []
258
- @cycletiming[tset]['drive_event_data'] = {}
259
- @cycletiming[tset]['drive_event_pins'] = {}
260
- @cycletiming[tset]['compare_event_data'] = {}
261
- @cycletiming[tset]['compare_event_pins'] = {}
262
- end
263
-
264
- ##################################################
265
- # pin_format method
266
- # arguments: <args> from the message request
267
- # Should be <timeset>,<pin>,rl or rh
268
- # multi-clock not currently supported
269
- #
270
- ##################################################
271
- def pin_format(args)
272
- argarr = args.split(',')
273
- tset_key = argarr.delete_at(0).to_i
274
- new_timeset(tset_key) unless @cycletiming.key?(tset_key)
275
- @cycletiming[tset_key]['events'] += [1, 3]
276
- @cycletiming[tset_key]['events'].sort!
277
- [1, 3].each do |event|
278
- @cycletiming[tset_key]['drive_event_pins'][event] = [[]]
279
- end
280
- @cycletiming[tset_key]['compare_event_data'][1] = ['data']
281
- 0.step(argarr.length - 2, 2) do |index|
282
- drive_type = argarr[index + 1]
283
- pin_name = argarr[index]
284
- @cycletiming[tset_key]['drive_event_data'][1] = ['data']
285
- @cycletiming[tset_key]['drive_event_pins'][1][0] << @pinmap[pin_name]
286
- @cycletiming[tset_key]['drive_event_pins'][3][0] << @pinmap[pin_name]
287
- @cycletiming[tset_key]['compare_event_pins'][1] = [[@pinmap[pin_name]]]
288
- if drive_type == 'rl'
289
- @cycletiming[tset_key]['drive_event_data'][3] = ['0']
290
- else
291
- @cycletiming[tset_key]['drive_event_data'][3] = ['1']
292
- end
293
- end
294
- 'P:'
295
- end
296
-
297
- ##################################################
298
- # pin_timing method
299
- # arguments: <args> from the message request
300
- # Should be '1,pin,-1,pin2,0,pin3,1'
301
- # First integer is timeset number
302
- # If argument is '', default timing is created
303
- # Default timeset number is 0, this is used
304
- # if no timeset is explicitly defined
305
- #
306
- # cycle arg: 0 1 2
307
- # waveform : ___/***\___
308
- #
309
- # returns "P:" or error message
310
- #
311
- # This method sets up a time set. All retrun
312
- # format pins are driven between 0 and 1 and
313
- # return between 1 and 2. Non-return pins are
314
- # acted upon during the 0, 1 or 2 time period.
315
- #
316
- ##################################################
317
- def pin_timing(args)
318
- argarr = args.split(',')
319
- tset_key = argarr.delete_at(0).to_i
320
- new_timeset(tset_key) unless @cycletiming.key?(tset_key)
321
- [0, 2, 4].each do |event|
322
- @cycletiming[tset_key]['drive_event_pins'][event] = [[]]
323
- @cycletiming[tset_key]['compare_event_pins'][event] = [[]]
324
- end
325
-
326
- # process the information received
327
- 0.step(argarr.length - 2, 2) do |index|
328
- event = argarr[index + 1].to_i
329
- # reorder event number to allow rising/falling edges
330
- event *= 2
331
- pin_name = argarr[index]
332
- @cycletiming[tset_key]['events'] << event
333
- @cycletiming[tset_key]['drive_event_data'][event] = ['data']
334
- @cycletiming[tset_key]['drive_event_pins'][event][0] << @pinmap[pin_name]
335
- @cycletiming[tset_key]['compare_event_data'][event] = ['data']
336
- @cycletiming[tset_key]['compare_event_pins'][event][0] << @pinmap[pin_name]
337
- end
338
-
339
- # remove events with no associated pins
340
- @cycletiming[tset_key]['events'].uniq!
341
- @cycletiming[tset_key]['events'].sort!
342
- [0, 2, 4].each do |event|
343
- if @cycletiming[tset_key]['drive_event_pins'][event][0].size == 0
344
- @cycletiming[tset_key]['events'] -= [event]
345
- @cycletiming[tset_key]['drive_event_data'].delete(event)
346
- @cycletiming[tset_key]['drive_event_pins'].delete(event)
347
- @cycletiming[tset_key]['compare_event_data'].delete(event)
348
- @cycletiming[tset_key]['compare_event_pins'].delete(event)
349
- end
350
- end
351
- 'P:'
352
- end
353
-
354
- ##################################################
355
- # pin_patternorder method
356
- # arguments: <args> from the message request
357
- # returns: "P:" or error message
358
- #
359
- # This method is used to define the order
360
- # for pin vector data.
361
- ##################################################
362
- def pin_patternorder(args)
363
- argarr = args.split(',')
364
- index = 0
365
- new_timeset(0)
366
- argarr.each do |pin|
367
- @patternorder << pin
368
- @pinmap[pin].pattern_index = index # pattern index stored in pin object now
369
- @patternpinindex[pin] = index # to be removed
370
-
371
- # define default timing
372
- @cycletiming[0]['events'] << index
373
- @cycletiming[0]['drive_event_data'][index] = ['data']
374
- @cycletiming[0]['drive_event_pins'][index] = [[@pinmap[pin]]]
375
- @cycletiming[0]['compare_event_data'][index] = ['data']
376
- @cycletiming[0]['compare_event_pins'][index] = [[@pinmap[pin]]]
377
- index += 1
378
- end
379
- 'P:'
380
- end
381
-
382
- ##################################################
383
- # pin_cycle method
384
- # arguments: <args> from the message request
385
- # returns: "P:" or "F:" followed by results
386
- #
387
- # This method executes one cycle of pin vector
388
- # data. The vector data is decomposed and
389
- # sequenced. Each pin object and pin data
390
- # is passed to the "process_pindata" method
391
- # for decoding and execution
392
- #
393
- ##################################################
394
- def pin_cycle(args)
395
- # set default repeats and timeset
396
- repeat_count = 1
397
- tset = 0
398
- if args =~ /,/
399
- parsedargs = args.split(',')
400
- args = parsedargs.pop
401
- parsedargs.each do |arg|
402
- if arg =~ /repeat/
403
- repeat_count = arg.sub(/repeat/, '').to_i
404
- elsif arg =~ /tset/
405
- tset = arg.sub(/tset/, '').to_i
406
- end
407
- end
408
- end
409
-
410
- message = ''
411
- # load pattern cycle data into pin objects
412
- @pinmap.each_value { |p| p.load_pattern_data(args) }
413
- cycle_failure = false
414
-
415
- 0.upto(repeat_count - 1) do |count|
416
- # process timing events
417
- @cycletiming[tset]['events'].each do |event|
418
- # process drive events
419
- if @cycletiming[tset]['drive_event_pins'].key?(event)
420
- @cycletiming[tset]['drive_event_pins'][event].each_index do |i|
421
- @cycletiming[tset]['drive_event_pins'][event][i].each do |p|
422
- p.process_event(:drive, @cycletiming[tset]['drive_event_data'][event][i])
423
- end
424
- end
425
- end
426
-
427
- # process compare events
428
- if @cycletiming[tset]['compare_event_pins'].key?(event)
429
- @cycletiming[tset]['compare_event_pins'][event].each_index do |i|
430
- @cycletiming[tset]['compare_event_pins'][event][i].each do |p|
431
- p.process_event(:compare, @cycletiming[tset]['compare_event_data'][event][i])
432
- end
433
- end
434
- end
435
- end # event
436
-
437
- # build response message
438
- message += ' ' unless count == 0
439
-
440
- @patternorder.each do |p|
441
- message += @pinmap[p].response
442
- cycle_failure = true if @pinmap[p].cycle_failure
443
- end
444
- end # count
445
- if cycle_failure
446
- rtnmsg = 'F:' + message + ' Expected:' + args
447
- else
448
- rtnmsg = 'P:' + message
449
- end
450
- rtnmsg
451
- end
452
-
453
- ##################################################
454
- # pin_clear method
455
- #
456
- # This method clears all storage objects. It
457
- # is called by the "pin_assign" method
458
- ##################################################
459
- def pin_clear
460
- @pinmap.each { |pin_name, pin| pin.destroy }
461
- @pinmap.clear
462
- @patternpinindex.clear
463
- @patternorder.delete_if { true }
464
- @cycletiming.clear
465
- 'P:'
466
- end
467
- end
468
- end
469
- end
1
+ require 'origen_link/server/pin'
2
+
3
+ ##################################################
4
+ # OrigenLink::Server::Sequencer Class
5
+ # Instance variables:
6
+ # pinmap: hash with ["pin name"] = pin object
7
+ # patternpinindex: hash with ["pin name"] =
8
+ # integer index into vector data
9
+ # patternpinorder: Array with pin names in
10
+ # the vector order
11
+ #
12
+ # This class processes messages targeted for
13
+ # pin sequencer interface (vector pattern
14
+ # execution).
15
+ #
16
+ # Supported messages:
17
+ # pin_assign (create pin mapping)
18
+ # ex: "pin_assign:tck,3,extal,23,tdo,5"
19
+ #
20
+ # pin_patternorder (define vector pin order)
21
+ # ex: "pin_patternorder:tdo,extal,tck"
22
+ #
23
+ # pin_cycle (execute vector data)
24
+ # ex: "pin_cycle:H11"
25
+ #
26
+ # pin_clear (clear all setup information)
27
+ # ex: "pin_clear:"
28
+ #
29
+ # pin_format (setup a pin with return format)
30
+ # first argument is the timeset
31
+ # ex: "pin_format:1,tck,rl"
32
+ #
33
+ # pin_timing (define when pin events happen)
34
+ # timing is stored in a timeset hash
35
+ # first argument is the timeset key
36
+ # ex: "pin_timing:1,tdi,0,tdo,1,tms,0
37
+ #
38
+ # version (check version of app server is
39
+ # running)
40
+ # ex: "version:"
41
+ # response ex: "P:0.2.0.pre0"
42
+ ##################################################
43
+ module OrigenLink
44
+ module Server
45
+ def self.gpio_dir=(path)
46
+ @gpio_dir = path
47
+ end
48
+
49
+ def self.gpio_dir
50
+ @gpio_dir || '/sys/class/gpio'
51
+ end
52
+
53
+ ##################################################
54
+ # OrigenLink::Server::Sequencer Class
55
+ #
56
+ # This class processes messages targeted for
57
+ # pin sequencer interface (vector pattern
58
+ # execution).
59
+ #
60
+ # Supported messages:
61
+ # pin_assign (create pin mapping)
62
+ # ex: "pin_assign:tck,3,extal,23,tdo,5"
63
+ #
64
+ # pin_patternorder (define vector pin order)
65
+ # ex: "pin_patternorder:tdo,extal,tck"
66
+ #
67
+ # pin_cycle (execute vector data)
68
+ # ex: "pin_cycle:H11"
69
+ #
70
+ # pin_clear (clear all setup information)
71
+ # ex: "pin_clear:"
72
+ #
73
+ # pin_format (setup a pin with return format)
74
+ # first argument is the timeset
75
+ # ex: "pin_format:1,tck,rl"
76
+ #
77
+ # pin_timing (define when pin events happen)
78
+ # timing is stored in a timeset hash
79
+ # first argument is the timeset key
80
+ # ex: "pin_timing:1,tdi,0,tdo,1,tms,0
81
+ #
82
+ # version (check version of app server is
83
+ # running)
84
+ # ex: "version:"
85
+ # response ex: "P:0.2.0.pre0"
86
+ ##################################################
87
+ class Sequencer
88
+ # code version of the server (populated by start_link_server)
89
+ attr_accessor :version
90
+ # hash holding the pinmap ('name' => pin object)
91
+ attr_reader :pinmap
92
+ # array of pin names in pattern order
93
+ attr_reader :patternorder
94
+ # hash holding programmed time set information
95
+ attr_reader :cycletiming
96
+ # hash holding pin pattern order ('name' => index)
97
+ attr_reader :patternpinindex
98
+
99
+ ##################################################
100
+ # initialize method
101
+ # Create empty pinmap, pattern pin index
102
+ # and pattern order instance variables
103
+ ##################################################
104
+ def initialize
105
+ @pinmap = Hash.new(-1)
106
+ @patternpinindex = Hash.new(-1)
107
+ @patternorder = []
108
+ @cycletiming = Hash.new(-1)
109
+ @version = ''
110
+ end
111
+
112
+ ##################################################
113
+ # processmessage method
114
+ # arguments: message
115
+ # message format is <group>_<command>:<args>
116
+ # returns: message response
117
+ #
118
+ # This method splits a message into it's
119
+ # command and arguments and passes this
120
+ # information to the method that performs
121
+ # the requested command
122
+ ##################################################
123
+ def processmessage(message)
124
+ command = message.split(':')
125
+
126
+ case command[0]
127
+ when 'pin_assign'
128
+ pin_assign(command[1])
129
+ when 'pin_patternorder'
130
+ pin_patternorder(command[1])
131
+ when 'pin_cycle'
132
+ pin_cycle(command[1])
133
+ when 'pin_clear'
134
+ pin_clear
135
+ when 'pin_format'
136
+ pin_format(command[1])
137
+ when 'pin_timing'
138
+ pin_timing(command[1])
139
+ when 'pin_timingv2'
140
+ pin_timingv2(command[1])
141
+ when 'version'
142
+ "P:#{@version}"
143
+ else
144
+ 'Error Invalid command: ' + command[0].to_s
145
+ end
146
+ end
147
+
148
+ ##################################################
149
+ # pin_assign method
150
+ # arguments: <args> from the message request
151
+ # see "processmessage" method
152
+ # returns: "P:" or error message
153
+ #
154
+ # This method creates a pin instance for each
155
+ # pin in the pin map and builds the pinmap
156
+ # hash. Before the pinmap is created, any
157
+ # information from a previous pattern run is
158
+ # cleared.
159
+ ##################################################
160
+ def pin_assign(args)
161
+ pin_clear
162
+ success = true
163
+ fail_message = ''
164
+ argarr = args.split(',')
165
+ 0.step(argarr.length - 2, 2) do |index|
166
+ @pinmap[argarr[index]] = Pin.new(argarr[index + 1])
167
+ unless @pinmap[argarr[index]].gpio_valid
168
+ success = false
169
+ fail_message = fail_message + 'pin ' + argarr[index] + ' gpio' + argarr[index + 1] + ' is invalid'
170
+ end
171
+ end
172
+ if success
173
+ 'P:'
174
+ else
175
+ 'F:' + fail_message
176
+ end
177
+ end
178
+
179
+ ########################################################
180
+ # pin_timingv2 method
181
+ # arguments: <args> from the message request
182
+ # Should be '1,drive,5,pin,10,pin2,|compare,0,pin3'
183
+ # First integer is timeset number
184
+ #
185
+ # returns "P:" or error message
186
+ #
187
+ # This method sets up a time set. Any arbitrary
188
+ # waveform can be generated
189
+ #
190
+ ########################################################
191
+ def pin_timingv2(args)
192
+ # get the tset number
193
+ argarr = args.split(',')
194
+ tset_key = argarr.delete_at(0).to_i
195
+ new_timeset(tset_key)
196
+ args = argarr.join(',')
197
+
198
+ invalid_pins = []
199
+
200
+ # process and load the timeset
201
+ waves = args.split('|')
202
+ waves.each do |w|
203
+ argarr = w.split(',')
204
+ wave_type = argarr.delete_at(0)
205
+ event_data_key = wave_type + '_event_data'
206
+ event_pins_key = wave_type + '_event_pins'
207
+ w = argarr.join(',')
208
+ events = w.split(';')
209
+ events.each do |e|
210
+ argarr = e.split(',')
211
+ event_key = argarr.delete_at(0).to_f
212
+ @cycletiming[tset_key]['events'] << event_key
213
+ @cycletiming[tset_key][event_data_key][event_key] = [] unless @cycletiming[tset_key][event_data_key].key?(event_key)
214
+ @cycletiming[tset_key][event_data_key][event_key] << argarr.delete_at(0)
215
+ # now load the pins for this event
216
+ @cycletiming[tset_key][event_pins_key][event_key] = [] unless @cycletiming[tset_key][event_pins_key].key?(event_key)
217
+ @cycletiming[tset_key][event_pins_key][event_key] << []
218
+ argarr.each do |pin|
219
+ if @pinmap.key?(pin)
220
+ @cycletiming[tset_key][event_pins_key][event_key].last << @pinmap[pin]
221
+ else
222
+ invalid_pins << pin
223
+ end
224
+ end # of argarr.each
225
+ end # of events.each
226
+ end # of waves.each
227
+ @cycletiming[tset_key]['events'].uniq!
228
+ @cycletiming[tset_key]['events'].sort!
229
+
230
+ # return result
231
+ if invalid_pins.size > 0
232
+ 'F:Invalid pins (not in pinmap): ' + invalid_pins.join(', ')
233
+ else
234
+ 'P:'
235
+ end
236
+ end # of def pin_timingv2
237
+
238
+ ##################################################
239
+ # new_timeset(tset)
240
+ # creates a new empty timeset hash
241
+ #
242
+ # timing format:
243
+ # ['events'] = [0, 5, 10, 35]
244
+ # ['drive_event_data'] = {
245
+ # 0: ['data']
246
+ # 10: ['data','0']
247
+ # 35: ['0']
248
+ # }
249
+ # ['drive_event_pins'] = {
250
+ # 0: [[pin_obj1, pin_obj2]]
251
+ # 10: [[pin1,pin2], [pin3]]
252
+ # etc.
253
+ # }
254
+ ##################################################
255
+ def new_timeset(tset)
256
+ @cycletiming[tset] = {}
257
+ @cycletiming[tset]['events'] = []
258
+ @cycletiming[tset]['drive_event_data'] = {}
259
+ @cycletiming[tset]['drive_event_pins'] = {}
260
+ @cycletiming[tset]['compare_event_data'] = {}
261
+ @cycletiming[tset]['compare_event_pins'] = {}
262
+ end
263
+
264
+ ##################################################
265
+ # pin_format method
266
+ # arguments: <args> from the message request
267
+ # Should be <timeset>,<pin>,rl or rh
268
+ # multi-clock not currently supported
269
+ #
270
+ ##################################################
271
+ def pin_format(args)
272
+ argarr = args.split(',')
273
+ tset_key = argarr.delete_at(0).to_i
274
+ new_timeset(tset_key) unless @cycletiming.key?(tset_key)
275
+ @cycletiming[tset_key]['events'] += [1, 3]
276
+ @cycletiming[tset_key]['events'].sort!
277
+ [1, 3].each do |event|
278
+ @cycletiming[tset_key]['drive_event_pins'][event] = [[]]
279
+ end
280
+ @cycletiming[tset_key]['compare_event_data'][1] = ['data']
281
+ 0.step(argarr.length - 2, 2) do |index|
282
+ drive_type = argarr[index + 1]
283
+ pin_name = argarr[index]
284
+ @cycletiming[tset_key]['drive_event_data'][1] = ['data']
285
+ @cycletiming[tset_key]['drive_event_pins'][1][0] << @pinmap[pin_name]
286
+ @cycletiming[tset_key]['drive_event_pins'][3][0] << @pinmap[pin_name]
287
+ @cycletiming[tset_key]['compare_event_pins'][1] = [[@pinmap[pin_name]]]
288
+ if drive_type == 'rl'
289
+ @cycletiming[tset_key]['drive_event_data'][3] = ['0']
290
+ else
291
+ @cycletiming[tset_key]['drive_event_data'][3] = ['1']
292
+ end
293
+ end
294
+ 'P:'
295
+ end
296
+
297
+ ##################################################
298
+ # pin_timing method
299
+ # arguments: <args> from the message request
300
+ # Should be '1,pin,-1,pin2,0,pin3,1'
301
+ # First integer is timeset number
302
+ # If argument is '', default timing is created
303
+ # Default timeset number is 0, this is used
304
+ # if no timeset is explicitly defined
305
+ #
306
+ # cycle arg: 0 1 2
307
+ # waveform : ___/***\___
308
+ #
309
+ # returns "P:" or error message
310
+ #
311
+ # This method sets up a time set. All retrun
312
+ # format pins are driven between 0 and 1 and
313
+ # return between 1 and 2. Non-return pins are
314
+ # acted upon during the 0, 1 or 2 time period.
315
+ #
316
+ ##################################################
317
+ def pin_timing(args)
318
+ argarr = args.split(',')
319
+ tset_key = argarr.delete_at(0).to_i
320
+ new_timeset(tset_key) unless @cycletiming.key?(tset_key)
321
+ [0, 2, 4].each do |event|
322
+ @cycletiming[tset_key]['drive_event_pins'][event] = [[]]
323
+ @cycletiming[tset_key]['compare_event_pins'][event] = [[]]
324
+ end
325
+
326
+ # process the information received
327
+ 0.step(argarr.length - 2, 2) do |index|
328
+ event = argarr[index + 1].to_i
329
+ # reorder event number to allow rising/falling edges
330
+ event *= 2
331
+ pin_name = argarr[index]
332
+ @cycletiming[tset_key]['events'] << event
333
+ @cycletiming[tset_key]['drive_event_data'][event] = ['data']
334
+ @cycletiming[tset_key]['drive_event_pins'][event][0] << @pinmap[pin_name]
335
+ @cycletiming[tset_key]['compare_event_data'][event] = ['data']
336
+ @cycletiming[tset_key]['compare_event_pins'][event][0] << @pinmap[pin_name]
337
+ end
338
+
339
+ # remove events with no associated pins
340
+ @cycletiming[tset_key]['events'].uniq!
341
+ @cycletiming[tset_key]['events'].sort!
342
+ [0, 2, 4].each do |event|
343
+ if @cycletiming[tset_key]['drive_event_pins'][event][0].size == 0
344
+ @cycletiming[tset_key]['events'] -= [event]
345
+ @cycletiming[tset_key]['drive_event_data'].delete(event)
346
+ @cycletiming[tset_key]['drive_event_pins'].delete(event)
347
+ @cycletiming[tset_key]['compare_event_data'].delete(event)
348
+ @cycletiming[tset_key]['compare_event_pins'].delete(event)
349
+ end
350
+ end
351
+ 'P:'
352
+ end
353
+
354
+ ##################################################
355
+ # pin_patternorder method
356
+ # arguments: <args> from the message request
357
+ # returns: "P:" or error message
358
+ #
359
+ # This method is used to define the order
360
+ # for pin vector data.
361
+ ##################################################
362
+ def pin_patternorder(args)
363
+ argarr = args.split(',')
364
+ index = 0
365
+ new_timeset(0)
366
+ argarr.each do |pin|
367
+ @patternorder << pin
368
+ @pinmap[pin].pattern_index = index # pattern index stored in pin object now
369
+ @patternpinindex[pin] = index # to be removed
370
+
371
+ # define default timing
372
+ @cycletiming[0]['events'] << index
373
+ @cycletiming[0]['drive_event_data'][index] = ['data']
374
+ @cycletiming[0]['drive_event_pins'][index] = [[@pinmap[pin]]]
375
+ @cycletiming[0]['compare_event_data'][index] = ['data']
376
+ @cycletiming[0]['compare_event_pins'][index] = [[@pinmap[pin]]]
377
+ index += 1
378
+ end
379
+ 'P:'
380
+ end
381
+
382
+ ##################################################
383
+ # pin_cycle method
384
+ # arguments: <args> from the message request
385
+ # returns: "P:" or "F:" followed by results
386
+ #
387
+ # This method executes one cycle of pin vector
388
+ # data. The vector data is decomposed and
389
+ # sequenced. Each pin object and pin data
390
+ # is passed to the "process_pindata" method
391
+ # for decoding and execution
392
+ #
393
+ ##################################################
394
+ def pin_cycle(args)
395
+ # set default repeats and timeset
396
+ repeat_count = 1
397
+ tset = 0
398
+ if args =~ /,/
399
+ parsedargs = args.split(',')
400
+ args = parsedargs.pop
401
+ parsedargs.each do |arg|
402
+ if arg =~ /repeat/
403
+ repeat_count = arg.sub(/repeat/, '').to_i
404
+ elsif arg =~ /tset/
405
+ tset = arg.sub(/tset/, '').to_i
406
+ end
407
+ end
408
+ end
409
+
410
+ message = ''
411
+ # load pattern cycle data into pin objects
412
+ @pinmap.each_value { |p| p.load_pattern_data(args) }
413
+ cycle_failure = false
414
+
415
+ 0.upto(repeat_count - 1) do |count|
416
+ # process timing events
417
+ @cycletiming[tset]['events'].each do |event|
418
+ # process drive events
419
+ if @cycletiming[tset]['drive_event_pins'].key?(event)
420
+ @cycletiming[tset]['drive_event_pins'][event].each_index do |i|
421
+ @cycletiming[tset]['drive_event_pins'][event][i].each do |p|
422
+ p.process_event(:drive, @cycletiming[tset]['drive_event_data'][event][i])
423
+ end
424
+ end
425
+ end
426
+
427
+ # process compare events
428
+ if @cycletiming[tset]['compare_event_pins'].key?(event)
429
+ @cycletiming[tset]['compare_event_pins'][event].each_index do |i|
430
+ @cycletiming[tset]['compare_event_pins'][event][i].each do |p|
431
+ p.process_event(:compare, @cycletiming[tset]['compare_event_data'][event][i])
432
+ end
433
+ end
434
+ end
435
+ end # event
436
+
437
+ # build response message
438
+ message += ' ' unless count == 0
439
+
440
+ @patternorder.each do |p|
441
+ message += @pinmap[p].response
442
+ cycle_failure = true if @pinmap[p].cycle_failure
443
+ end
444
+ end # count
445
+ if cycle_failure
446
+ rtnmsg = 'F:' + message + ' Expected:' + args
447
+ else
448
+ rtnmsg = 'P:' + message
449
+ end
450
+ rtnmsg
451
+ end
452
+
453
+ ##################################################
454
+ # pin_clear method
455
+ #
456
+ # This method clears all storage objects. It
457
+ # is called by the "pin_assign" method
458
+ ##################################################
459
+ def pin_clear
460
+ @pinmap.each { |pin_name, pin| pin.destroy }
461
+ @pinmap.clear
462
+ @patternpinindex.clear
463
+ @patternorder.delete_if { true }
464
+ @cycletiming.clear
465
+ 'P:'
466
+ end
467
+ end
468
+ end
469
+ end