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.
- checksums.yaml +5 -5
- data/bin/start_link_server +116 -116
- data/config/application.rb +109 -109
- data/config/commands.rb +74 -74
- data/config/shared_commands.rb +40 -40
- data/config/version.rb +8 -8
- data/lib/origen_link.rb +5 -5
- data/lib/origen_link/callback_handlers.rb +13 -13
- data/lib/origen_link/capture_support.rb +94 -94
- data/lib/origen_link/configuration_commands.rb +236 -236
- data/lib/origen_link/listener.rb +78 -78
- data/lib/origen_link/server/jtag.rb +251 -251
- data/lib/origen_link/server/pin.rb +218 -218
- data/lib/origen_link/server/sequencer.rb +469 -469
- data/lib/origen_link/server_com.rb +154 -154
- data/lib/origen_link/test/top_level.rb +48 -48
- data/lib/origen_link/test/top_level_controller.rb +46 -46
- data/lib/origen_link/test/vector_based.rb +25 -25
- data/lib/origen_link/vector_based.rb +526 -524
- data/lib/tasks/origen_link.rake +6 -6
- data/pattern/example.rb +4 -4
- data/pattern/jtag_capture_id.rb +22 -22
- data/pattern/transaction_test.rb +18 -18
- data/templates/web/index.md.erb +470 -451
- data/templates/web/layouts/_basic.html.erb +13 -13
- data/templates/web/partials/_navbar.html.erb +20 -20
- data/templates/web/release_notes.md.erb +5 -5
- metadata +3 -3
@@ -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
|