origen_link 0.4.2 → 0.4.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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