origen_link 0.1.0.pre0
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 +7 -0
- data/config/application.rb +105 -0
- data/config/boot.rb +7 -0
- data/config/commands.rb +74 -0
- data/config/version.rb +8 -0
- data/lib/origen_link/includes_vector_based.rb +76 -0
- data/lib/origen_link/test/regression_tests.rb +68 -0
- data/lib/origen_link/test/test_dut.rb +46 -0
- data/lib/origen_link/test/test_dut_controller.rb +42 -0
- data/lib/origen_link/test/vector_based_redefs.rb +17 -0
- data/lib/origen_link/vector_based.rb +254 -0
- data/lib/origen_link.rb +18 -0
- data/lib/origen_link_server/LinkSequencer.rb +333 -0
- data/lib/origen_link_server/LinkTCPServer.rb +45 -0
- data/lib/origen_link_server/jtag_interface.rb +177 -0
- data/lib/origen_link_server/pin_interface.rb +134 -0
- data/lib/origen_link_server/test/test_Sequencer.rb +51 -0
- data/lib/tasks/origen_link.rake +6 -0
- data/pattern/example.rb +4 -0
- data/pattern/jtag_100_operations.rb +14 -0
- data/pattern/jtag_comm_fail_test.rb +13 -0
- data/pattern/jtag_comm_test.rb +11 -0
- data/templates/web/index.md.erb +19 -0
- data/templates/web/layouts/_basic.html.erb +13 -0
- data/templates/web/partials/_navbar.html.erb +20 -0
- data/templates/web/release_notes.md.erb +5 -0
- metadata +125 -0
@@ -0,0 +1,254 @@
|
|
1
|
+
module OrigenLink
|
2
|
+
# OrigenLink::VectorBased
|
3
|
+
# This class is meant to be used for live silicon debug. Vector data that Origen
|
4
|
+
# generates is intercepted and sent to a debug device (typically will be a Udoo
|
5
|
+
# Neo - www.udoo.org). The debug device can be any device that is able to serve
|
6
|
+
# a TCP socket, recieve and interpret the command set used by this class and send
|
7
|
+
# the expected responses.
|
8
|
+
#
|
9
|
+
# Integration instructions
|
10
|
+
# Set the pin map (must be done first) and pin order
|
11
|
+
# if tester.link?
|
12
|
+
# tester.pinmap = 'tclk,26,tms,19,tdi,16,tdo,23'
|
13
|
+
# tester.pinorder = 'tclk,tms,tdi,tdo'
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# Set Origen to only generate vectors for pins in the pinmap (order should match)
|
17
|
+
# pin_pattern_order :tclk, :tms, :tdi, :tdo, only: true if tester.link?
|
18
|
+
#
|
19
|
+
# At the beginning of the Startup method add this line
|
20
|
+
# tester.initialize_pattern if tester.link?
|
21
|
+
#
|
22
|
+
# At the end of the Shutdown method add this line
|
23
|
+
# tester.finalize_pattern if tester.link?
|
24
|
+
#
|
25
|
+
# Create a link environment with the IP address and socket number of a link server
|
26
|
+
# $tester = OrigenLink::VectorBased.new('192.168.0.2', 12777)
|
27
|
+
class VectorBased
|
28
|
+
# include OrigenTesters::VectorBasedTester
|
29
|
+
|
30
|
+
# these attributes are exposed for testing purposes, a user would not need to read them
|
31
|
+
attr_reader :fail_count, :vector_count, :total_comm_time, :total_connect_time, :total_xmit_time, :total_recv_time, :total_packets, :vector_repeatcount, :tsets_programmed
|
32
|
+
|
33
|
+
def initialize(address, port)
|
34
|
+
@address = address
|
35
|
+
@port = port
|
36
|
+
@fail_count = 0
|
37
|
+
@vector_count = 0
|
38
|
+
@previous_vectordata = ''
|
39
|
+
@previous_tset = ''
|
40
|
+
@vector_repeatcount = 0
|
41
|
+
@total_comm_time = 0
|
42
|
+
@total_connect_time = 0
|
43
|
+
@total_xmit_time = 0
|
44
|
+
@total_recv_time = 0
|
45
|
+
@total_packets = 0
|
46
|
+
@max_packet_time = 0
|
47
|
+
@max_receive_time = 0
|
48
|
+
@tsets_programmed = {}
|
49
|
+
@tset_count = 1
|
50
|
+
end
|
51
|
+
|
52
|
+
# push_vector
|
53
|
+
# This method intercepts vector data from Origen, removes white spaces and compresses repeats
|
54
|
+
def push_vector(options)
|
55
|
+
programmed_data = options[:pin_vals].gsub(/\s+/, '')
|
56
|
+
tset = options[:timeset].name
|
57
|
+
if @vector_count > 0
|
58
|
+
# compressing repeats as we go
|
59
|
+
if (programmed_data == @previous_vectordata) && (@previous_tset == tset)
|
60
|
+
@vector_repeatcount += 1
|
61
|
+
else
|
62
|
+
# all repeats of the previous vector have been counted
|
63
|
+
# time to flush. Don't panic though! @previous_vectordata
|
64
|
+
# is what gets flushed. programmed_data is passed as an
|
65
|
+
# arg to be set as the new @previous_vectordata
|
66
|
+
flush_vector(programmed_data, tset)
|
67
|
+
end
|
68
|
+
else
|
69
|
+
# if this is the first vector of the pattern, insure variables are initialized
|
70
|
+
@previous_vectordata = programmed_data
|
71
|
+
@previous_tset = tset
|
72
|
+
@vector_repeatcount = 1
|
73
|
+
end # if vector_count > 0
|
74
|
+
@vector_count += 1
|
75
|
+
end
|
76
|
+
|
77
|
+
# flush_vector
|
78
|
+
# Just as the name suggests, this method "flushes" a vector. This is necessary because
|
79
|
+
# of repeat compression (a vector isn't sent until different vector data is encountered)
|
80
|
+
#
|
81
|
+
# Don't forget to flush when you're in debug mode. Otherwise, the last vector of a
|
82
|
+
# write command won't be sent to the server.
|
83
|
+
def flush_vector(programmed_data = '', tset = '')
|
84
|
+
if @vector_repeatcount > 1
|
85
|
+
repeat_prefix = "repeat#{@vector_repeatcount},"
|
86
|
+
else
|
87
|
+
repeat_prefix = ''
|
88
|
+
end
|
89
|
+
if @tsets_programmed[@previous_tset]
|
90
|
+
tset_prefix = "tset#{@tsets_programmed[@previous_tset]},"
|
91
|
+
else
|
92
|
+
tset_prefix = ''
|
93
|
+
end
|
94
|
+
response = send_cmd('pin_cycle', tset_prefix + repeat_prefix + @previous_vectordata)
|
95
|
+
microcode response
|
96
|
+
unless response.chr == 'P'
|
97
|
+
microcode 'E:' + @previous_vectordata + ' //expected data for previous vector'
|
98
|
+
@fail_count += 1
|
99
|
+
end
|
100
|
+
@vector_repeatcount = 1
|
101
|
+
@previous_vectordata = programmed_data
|
102
|
+
@previous_tset = tset
|
103
|
+
end
|
104
|
+
|
105
|
+
# initialize_pattern
|
106
|
+
# At some point in the future this intialization should be done behind the
|
107
|
+
# scenes without requiring the user to add code to the controller.
|
108
|
+
#
|
109
|
+
# This method sets initializes variables at the start of a pattern.
|
110
|
+
# it should be called from the Startup method of the dut controller.
|
111
|
+
def initialize_pattern
|
112
|
+
@fail_count = 0
|
113
|
+
@vector_count = 0
|
114
|
+
|
115
|
+
@total_packets = 0
|
116
|
+
@total_comm_time = 0
|
117
|
+
@total_connect_time = 0
|
118
|
+
@total_xmit_time = 0
|
119
|
+
@total_recv_time = 0
|
120
|
+
|
121
|
+
if @pinmap.nil?
|
122
|
+
Origen.log.error('pinmap has not been setup, use tester.pinmap= to initialize a pinmap')
|
123
|
+
else
|
124
|
+
Origen.log.info('executing pattern with pinmap:' + @pinmap.to_s)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
# finalize_pattern
|
129
|
+
# At some point in the future this finalization should be done behind the scenes.
|
130
|
+
#
|
131
|
+
# This method flushes the final vector. Then, it logs success or failure of the
|
132
|
+
# pattern execution along with execution time information.
|
133
|
+
def finalize_pattern(programmed_data = '')
|
134
|
+
flush_vector(programmed_data)
|
135
|
+
if @fail_count == 0
|
136
|
+
Origen.log.success("PASS - pattern execution passed (#{@vector_count} vectors pass)")
|
137
|
+
else
|
138
|
+
Origen.log.error("FAIL - pattern execution failed (#{@fail_count} failures)")
|
139
|
+
end
|
140
|
+
# for debug, report communication times
|
141
|
+
Origen.log.info("total communication time: #{@total_comm_time}")
|
142
|
+
Origen.log.info("total connect time: #{@total_connect_time}")
|
143
|
+
Origen.log.info("total transmit time: #{@total_xmit_time}")
|
144
|
+
Origen.log.info("total receive time: #{@total_recv_time}")
|
145
|
+
Origen.log.info("total packets: #{@total_packets}")
|
146
|
+
Origen.log.info("total time per packet: #{@total_comm_time / @total_packets}")
|
147
|
+
Origen.log.info("connect time per packet: #{@total_connect_time / @total_packets}")
|
148
|
+
Origen.log.info("transmit time per packet: #{@total_xmit_time / @total_packets}")
|
149
|
+
Origen.log.info("receive time per packet: #{@total_recv_time / @total_packets}")
|
150
|
+
Origen.log.info("max packet time: #{@max_packet_time}")
|
151
|
+
Origen.log.info('max duration command - ' + @longest_packet)
|
152
|
+
Origen.log.info("max receive time: #{@max_receive_time}")
|
153
|
+
end
|
154
|
+
|
155
|
+
# to_s
|
156
|
+
# returns 'Origen::VectorBased'
|
157
|
+
#
|
158
|
+
# This method at the moment is used for implementing code that runs only if the
|
159
|
+
# environment is set to link vector based. tester.link? will be used once the testers
|
160
|
+
# plug in supports the method link?.
|
161
|
+
def to_s
|
162
|
+
'OrigenLink::VectorBased'
|
163
|
+
end
|
164
|
+
|
165
|
+
# link?
|
166
|
+
# returns true.
|
167
|
+
#
|
168
|
+
# This method indicates to user code that link is the tester environment.
|
169
|
+
def link?
|
170
|
+
true
|
171
|
+
end
|
172
|
+
|
173
|
+
# pinmap=
|
174
|
+
# This method is used to setup the pin map on the debugger device.
|
175
|
+
# The argument should be a string with <pin name>, <gpio #>, <pin name>
|
176
|
+
# <gpio #>, etc
|
177
|
+
#
|
178
|
+
# example:
|
179
|
+
# tester.pinmap = 'tclk,26,tms,19,tdi,16,tdo,23'
|
180
|
+
def pinmap=(pinmap)
|
181
|
+
@pinmap = pinmap.gsub(/\s+/, '')
|
182
|
+
response = send_cmd('pin_assign', @pinmap)
|
183
|
+
setup_cmd_response_logger('pin_assign', response)
|
184
|
+
end
|
185
|
+
|
186
|
+
# pinorder=
|
187
|
+
# This method is used to setup the pin order on the debugger device.
|
188
|
+
# The pin order will indicate the order that pin data appears in vector
|
189
|
+
# data.
|
190
|
+
#
|
191
|
+
# This is a duplicate of pattern_pin_order and can be handled behind the
|
192
|
+
# scenes in the future.
|
193
|
+
#
|
194
|
+
# example:
|
195
|
+
# tester.pinorder = 'tclk,tms,tdi,tdo'
|
196
|
+
def pinorder=(pinorder)
|
197
|
+
@pinorder = pinorder.gsub(/\s+/, '')
|
198
|
+
response = send_cmd('pin_patternorder', @pinorder)
|
199
|
+
setup_cmd_response_logger('pin_patternorder', response)
|
200
|
+
end
|
201
|
+
|
202
|
+
# pinformat=
|
203
|
+
# This method is used to setup the pin clock format on the debugger device.
|
204
|
+
# The supported formats are rl and rh
|
205
|
+
#
|
206
|
+
# example:
|
207
|
+
# tester.pinformat = 'func_25mhz,tclk,rl'
|
208
|
+
def pinformat=(pinformat)
|
209
|
+
@pinformat = replace_tset_name_w_number(pinformat.gsub(/\s+/, ''))
|
210
|
+
response = send_cmd('pin_format', @pinformat)
|
211
|
+
setup_cmd_response_logger('pin_format', response)
|
212
|
+
end
|
213
|
+
|
214
|
+
# pintiming=
|
215
|
+
# This method is used to setup the pin timing on the debugger device.
|
216
|
+
# Timing is relative to the rise and fall of a clock
|
217
|
+
#
|
218
|
+
# timing value: 0 1 2
|
219
|
+
# clock waveform: ___/***\___
|
220
|
+
#
|
221
|
+
# example:
|
222
|
+
# tester.pintiming = 'func_25mhz,tms,0,tdi,0,tdo,1'
|
223
|
+
def pintiming=(pintiming)
|
224
|
+
@pintiming = replace_tset_name_w_number(pintiming.gsub(/\s+/, ''))
|
225
|
+
response = send_cmd('pin_timing', @pintiming)
|
226
|
+
setup_cmd_response_logger('pin_timing', response)
|
227
|
+
end
|
228
|
+
|
229
|
+
# replace_tset_name_w_number(csl)
|
230
|
+
# This method is used by pinformat= and pintiming=
|
231
|
+
# This method receives a comma separated list of arguments
|
232
|
+
# the first of which is a timeset name. A comma
|
233
|
+
# separated list is returned with the timeset name replaced
|
234
|
+
# by it's lookup number. If it is a new timset, a lookup
|
235
|
+
# number is associated with the name.
|
236
|
+
def replace_tset_name_w_number(csl)
|
237
|
+
args = csl.split(',')
|
238
|
+
args[0] = get_tset_number(args[0])
|
239
|
+
args.join(',')
|
240
|
+
end
|
241
|
+
|
242
|
+
# get_tset_number(name)
|
243
|
+
# This method returns the test number associated with the
|
244
|
+
# passed in tset name. If the name is unknown a new lookup
|
245
|
+
# number is returned.
|
246
|
+
def get_tset_number(name)
|
247
|
+
unless @tsets_programmed.key?(name)
|
248
|
+
@tsets_programmed[name] = @tset_count
|
249
|
+
@tset_count += 1
|
250
|
+
end
|
251
|
+
@tsets_programmed[name]
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
data/lib/origen_link.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'origen'
|
2
|
+
require 'origen_testers'
|
3
|
+
require 'socket'
|
4
|
+
|
5
|
+
module OrigenLink
|
6
|
+
# Load all files in the lib directory via a wildcard, if your project becomes
|
7
|
+
# large or load order dependencies start to creep in then you may need to
|
8
|
+
# start taking control of this manually as described above.
|
9
|
+
# Note that there is no problem from requiring a file twice (Ruby will ignore
|
10
|
+
# the second require), so if you have a file that must be required up front
|
11
|
+
# you can do that one manually and the let the wildcard take care of the rest.
|
12
|
+
# Dir.glob("#{File.dirname(__FILE__)}/**/*.rb").sort.each do |file|
|
13
|
+
# require file
|
14
|
+
# end
|
15
|
+
Dir.glob("#{File.dirname(__FILE__)}/origen_link/*.rb").sort.each do |file|
|
16
|
+
require file
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,333 @@
|
|
1
|
+
# rubocop:disable Style/FileName: Use snake_case for source file names
|
2
|
+
require_relative 'pin_interface'
|
3
|
+
|
4
|
+
##################################################
|
5
|
+
# OrigenLinkSequencer Class
|
6
|
+
# Instance variables:
|
7
|
+
# pinmap: hash with ["pin name"] = pin object
|
8
|
+
# patternpinindex: hash with ["pin name"] =
|
9
|
+
# integer index into vector data
|
10
|
+
# patternpinorder: Array with pin names in
|
11
|
+
# the vector order
|
12
|
+
#
|
13
|
+
# This class processes messages targeted for
|
14
|
+
# pin sequencer interface (vector pattern
|
15
|
+
# execution).
|
16
|
+
#
|
17
|
+
# Supported messages:
|
18
|
+
# pin_assign (create pin mapping)
|
19
|
+
# ex: "pin_assign:tck,3,extal,23,tdo,5"
|
20
|
+
#
|
21
|
+
# pin_patternorder (define vector pin order)
|
22
|
+
# ex: "pin_patternorder:tdo,extal,tck"
|
23
|
+
#
|
24
|
+
# pin_cycle (execute vector data)
|
25
|
+
# ex: "pin_cycle:H11"
|
26
|
+
#
|
27
|
+
# pin_clear (clear all setup information)
|
28
|
+
# ex: "pin_clear:"
|
29
|
+
#
|
30
|
+
# pin_format (setup a pin with return format)
|
31
|
+
# first argument is the timeset
|
32
|
+
# ex: "pin_format:1,tck,rl"
|
33
|
+
#
|
34
|
+
# pin_timing (define when pin events happen)
|
35
|
+
# timing is stored in a timeset hash
|
36
|
+
# first argument is the timeset key
|
37
|
+
# ex: "pin_timing:1,tdi,0,tdo,1,tms,0
|
38
|
+
##################################################
|
39
|
+
class OrigenLinkSequencer
|
40
|
+
attr_reader :pinmap
|
41
|
+
attr_reader :patternorder
|
42
|
+
attr_reader :cycletiming
|
43
|
+
attr_reader :patternpinindex
|
44
|
+
|
45
|
+
##################################################
|
46
|
+
# initialize method
|
47
|
+
# Create empty pinmap, pattern pin index
|
48
|
+
# and pattern order instance variables
|
49
|
+
##################################################
|
50
|
+
def initialize
|
51
|
+
@pinmap = Hash.new(-1)
|
52
|
+
@patternpinindex = Hash.new(-1)
|
53
|
+
@patternorder = []
|
54
|
+
@cycletiming = Hash.new(-1)
|
55
|
+
end
|
56
|
+
|
57
|
+
##################################################
|
58
|
+
# processmessage method
|
59
|
+
# arguments: message
|
60
|
+
# message format is <group>_<command>:<args>
|
61
|
+
# returns: message response
|
62
|
+
#
|
63
|
+
# This method splits a message into it's
|
64
|
+
# command and arguments and passes this
|
65
|
+
# information to the method that performs
|
66
|
+
# the requested command
|
67
|
+
##################################################
|
68
|
+
def processmessage(message)
|
69
|
+
command = message.split(':')
|
70
|
+
|
71
|
+
case command[0]
|
72
|
+
when 'pin_assign'
|
73
|
+
pin_assign(command[1])
|
74
|
+
when 'pin_patternorder'
|
75
|
+
pin_patternorder(command[1])
|
76
|
+
when 'pin_cycle'
|
77
|
+
pin_cycle(command[1])
|
78
|
+
when 'pin_clear'
|
79
|
+
pin_clear
|
80
|
+
when 'pin_format'
|
81
|
+
pin_format(command[1])
|
82
|
+
when 'pin_timing'
|
83
|
+
pin_timing(command[1])
|
84
|
+
else
|
85
|
+
'Error Invalid command: ' + command[0].to_s
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
##################################################
|
90
|
+
# pin_assign method
|
91
|
+
# arguments: <args> from the message request
|
92
|
+
# see "processmessage" method
|
93
|
+
# returns: "P:" or error message
|
94
|
+
#
|
95
|
+
# This method creates a pin instance for each
|
96
|
+
# pin in the pin map and builds the pinmap
|
97
|
+
# hash. Before the pinmap is created, any
|
98
|
+
# information from a previous pattern run is
|
99
|
+
# cleared.
|
100
|
+
##################################################
|
101
|
+
def pin_assign(args)
|
102
|
+
pin_clear
|
103
|
+
success = true
|
104
|
+
fail_message = ''
|
105
|
+
argarr = args.split(',')
|
106
|
+
0.step(argarr.length - 2, 2) do |index|
|
107
|
+
@pinmap[argarr[index]] = OrigenLinkPin.new(argarr[index + 1])
|
108
|
+
unless @pinmap[argarr[index]].gpio_valid
|
109
|
+
success = false
|
110
|
+
fail_message = fail_message + 'pin ' + argarr[index] + ' gpio' + argarr[index + 1] + ' is invalid'
|
111
|
+
end
|
112
|
+
end
|
113
|
+
if success
|
114
|
+
'P:'
|
115
|
+
else
|
116
|
+
'F:' + fail_message
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
##################################################
|
121
|
+
# new_timeset(tset)
|
122
|
+
# creates a new empty timeset hash
|
123
|
+
##################################################
|
124
|
+
def new_timeset(tset)
|
125
|
+
@cycletiming[tset] = {}
|
126
|
+
@cycletiming[tset]['timing'] = [[], [], []]
|
127
|
+
end
|
128
|
+
|
129
|
+
##################################################
|
130
|
+
# pin_format method
|
131
|
+
# arguments: <args> from the message request
|
132
|
+
# Should be <timeset>,<pin>,rl or rh
|
133
|
+
# multi-clock not currently supported
|
134
|
+
#
|
135
|
+
##################################################
|
136
|
+
def pin_format(args)
|
137
|
+
argarr = args.split(',')
|
138
|
+
tset_key = argarr.delete_at(0).to_i
|
139
|
+
new_timeset(tset_key) unless @cycletiming.key?(tset_key)
|
140
|
+
@cycletiming[tset_key].delete('rl')
|
141
|
+
@cycletiming[tset_key].delete('rh')
|
142
|
+
0.step(argarr.length - 2, 2) do |index|
|
143
|
+
@cycletiming[tset_key][argarr[index + 1]] = [] unless @cycletiming[tset_key].key?(argarr[index + 1])
|
144
|
+
@cycletiming[tset_key][argarr[index + 1]] << argarr[index]
|
145
|
+
end
|
146
|
+
'P:'
|
147
|
+
end
|
148
|
+
|
149
|
+
##################################################
|
150
|
+
# pin_timing method
|
151
|
+
# arguments: <args> from the message request
|
152
|
+
# Should be '1,pin,-1,pin2,0,pin3,1'
|
153
|
+
# First integer is timeset number
|
154
|
+
# If argument is '', default timing is created
|
155
|
+
# Default timeset number is 0, this is used
|
156
|
+
# if no timeset is explicitly defined
|
157
|
+
#
|
158
|
+
# cycle arg: 0 1 2
|
159
|
+
# waveform : ___/***\___
|
160
|
+
#
|
161
|
+
# returns "P:" or error message
|
162
|
+
#
|
163
|
+
# This method sets up a time set. All retrun
|
164
|
+
# format pins are driven between 0 and 1 and
|
165
|
+
# return between 1 and 2. Non-return pins are
|
166
|
+
# acted upon during the 0, 1 or 2 time period.
|
167
|
+
##################################################
|
168
|
+
def pin_timing(args)
|
169
|
+
argarr = args.split(',')
|
170
|
+
tset_key = argarr.delete_at(0).to_i
|
171
|
+
new_timeset(tset_key) unless @cycletiming.key?(tset_key)
|
172
|
+
@cycletiming[tset_key]['timing'].each do |index|
|
173
|
+
index.delete_if { true }
|
174
|
+
end
|
175
|
+
0.step(argarr.length - 2, 2) do |index|
|
176
|
+
@cycletiming[tset_key]['timing'][argarr[index + 1].to_i] << argarr[index]
|
177
|
+
end
|
178
|
+
'P:'
|
179
|
+
end
|
180
|
+
|
181
|
+
##################################################
|
182
|
+
# pin_patternorder method
|
183
|
+
# arguments: <args> from the message request
|
184
|
+
# returns: "P:" or error message
|
185
|
+
#
|
186
|
+
# This method is used to define the order
|
187
|
+
# for pin vector data.
|
188
|
+
##################################################
|
189
|
+
def pin_patternorder(args)
|
190
|
+
argarr = args.split(',')
|
191
|
+
index = 0
|
192
|
+
if @cycletiming.key?(0)
|
193
|
+
@cycletiming[0]['timing'][0].delete_if { true }
|
194
|
+
else
|
195
|
+
new_timeset(0)
|
196
|
+
end
|
197
|
+
argarr.each do |pin|
|
198
|
+
@patternorder << pin
|
199
|
+
@patternpinindex[pin] = index
|
200
|
+
@cycletiming[0]['timing'][0] << pin
|
201
|
+
index += 1
|
202
|
+
end
|
203
|
+
'P:'
|
204
|
+
end
|
205
|
+
|
206
|
+
##################################################
|
207
|
+
# pin_cycle method
|
208
|
+
# arguments: <args> from the message request
|
209
|
+
# returns: "P:" or "F:" followed by results
|
210
|
+
#
|
211
|
+
# This method executes one cycle of pin vector
|
212
|
+
# data. The vector data is decomposed and
|
213
|
+
# sequenced. Each pin object and pin data
|
214
|
+
# is passed to the "process_pindata" method
|
215
|
+
# for decoding and execution
|
216
|
+
##################################################
|
217
|
+
def pin_cycle(args)
|
218
|
+
# set default repeats and timeset
|
219
|
+
repeat_count = 1
|
220
|
+
tset = 0
|
221
|
+
if args =~ /,/
|
222
|
+
parsedargs = args.split(',')
|
223
|
+
args = parsedargs.pop
|
224
|
+
parsedargs.each do |arg|
|
225
|
+
if arg =~ /repeat/
|
226
|
+
repeat_count = arg.sub(/repeat/, '').to_i
|
227
|
+
elsif arg =~ /tset/
|
228
|
+
tset = arg.sub(/tset/, '').to_i
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
message = ''
|
234
|
+
pindata = args.split('')
|
235
|
+
@cycle_failure = false
|
236
|
+
0.upto(repeat_count - 1) do |count|
|
237
|
+
response = {}
|
238
|
+
# process time 0 events
|
239
|
+
response = process_events(@cycletiming[tset]['timing'][0], pindata)
|
240
|
+
# send drive data for return format pins
|
241
|
+
response = (process_events(@cycletiming[tset]['rl'], pindata)).merge(response)
|
242
|
+
response = (process_events(@cycletiming[tset]['rh'], pindata)).merge(response)
|
243
|
+
# process time 1 events
|
244
|
+
response = process_events(@cycletiming[tset]['timing'][1], pindata).merge(response)
|
245
|
+
# send return data
|
246
|
+
unless @cycletiming[tset]['rl'].nil?
|
247
|
+
@cycletiming[tset]['rl'].each do |pin|
|
248
|
+
process_pindata(@pinmap[pin], '0')
|
249
|
+
end
|
250
|
+
end
|
251
|
+
unless @cycletiming[tset]['rh'].nil?
|
252
|
+
@cycletiming[tset]['rh'].each do |pin|
|
253
|
+
process_pindata(@pinmap[pin], '1')
|
254
|
+
end
|
255
|
+
end
|
256
|
+
# process time 2 events
|
257
|
+
response = process_events(@cycletiming[tset]['timing'][2], pindata).merge(response)
|
258
|
+
if (count == 0) || (@cycle_failure)
|
259
|
+
message = ''
|
260
|
+
@patternorder.each do |pin|
|
261
|
+
message += response[pin]
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end # end cycle through repeats
|
265
|
+
if @cycle_failure
|
266
|
+
rtnmsg = 'F:' + message + ' Expected:' + args
|
267
|
+
else
|
268
|
+
rtnmsg = 'P:' + message
|
269
|
+
end
|
270
|
+
rtnmsg += ' Repeat ' + repeat_count.to_s if repeat_count > 1
|
271
|
+
rtnmsg
|
272
|
+
end
|
273
|
+
|
274
|
+
##################################################
|
275
|
+
# process_events
|
276
|
+
# used by pin_cycle to avoid duplicating code
|
277
|
+
##################################################
|
278
|
+
def process_events(events, pindata)
|
279
|
+
response = {}
|
280
|
+
unless events.nil?
|
281
|
+
events.each do |pin|
|
282
|
+
response[pin] = process_pindata(@pinmap[pin], pindata[@patternpinindex[pin]])
|
283
|
+
end
|
284
|
+
end
|
285
|
+
response
|
286
|
+
end
|
287
|
+
|
288
|
+
##################################################
|
289
|
+
# process_pindata method
|
290
|
+
# arguments:
|
291
|
+
# pin: the pin object to be operated on
|
292
|
+
# data: the pin data to be executed
|
293
|
+
# returns: the drive data or read data
|
294
|
+
#
|
295
|
+
# This method translates pin data into one
|
296
|
+
# of three possible events. Drive 0, drive 1
|
297
|
+
# or read. Supported character decode:
|
298
|
+
# drive 0: '0'
|
299
|
+
# drive 1: '1'
|
300
|
+
# read: anything else
|
301
|
+
##################################################
|
302
|
+
def process_pindata(pin, data)
|
303
|
+
if data == '0' || data == '1'
|
304
|
+
pin.out(data)
|
305
|
+
data
|
306
|
+
else
|
307
|
+
case pin.in
|
308
|
+
when '0'
|
309
|
+
@cycle_failure = true if data == 'H'
|
310
|
+
'L'
|
311
|
+
when '1'
|
312
|
+
@cycle_failure = true if data == 'L'
|
313
|
+
'H'
|
314
|
+
else
|
315
|
+
'W'
|
316
|
+
end
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
320
|
+
##################################################
|
321
|
+
# pin_clear method
|
322
|
+
#
|
323
|
+
# This method clears all storage objects. It
|
324
|
+
# is called by the "pin_assign" method
|
325
|
+
##################################################
|
326
|
+
def pin_clear
|
327
|
+
@pinmap.clear
|
328
|
+
@patternpinindex.clear
|
329
|
+
@patternorder.delete_if { true }
|
330
|
+
@cycletiming.clear
|
331
|
+
'P:'
|
332
|
+
end
|
333
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# rubocop:disable Style/FileName: Use snake_case for source file names.
|
2
|
+
require 'socket'
|
3
|
+
require_relative 'LinkSequencer'
|
4
|
+
|
5
|
+
server = TCPServer.open('192.168.0.2', 12_777)
|
6
|
+
puts 'server started'
|
7
|
+
pinsequencer = OrigenLinkSequencer.new
|
8
|
+
|
9
|
+
# time measurements for debug only
|
10
|
+
# total_receive_time=0
|
11
|
+
# total_process_time=0
|
12
|
+
# total_xmit_time=0
|
13
|
+
# total_close_time=0
|
14
|
+
# total_packets=0
|
15
|
+
loop do
|
16
|
+
client = server.accept
|
17
|
+
# time measurements for debug only
|
18
|
+
# t1 = Time.now
|
19
|
+
message = client.gets
|
20
|
+
# t2 = Time.now
|
21
|
+
# process the message
|
22
|
+
# for now only pin_ messages are accepted
|
23
|
+
response = pinsequencer.processmessage(message.chomp)
|
24
|
+
# t3 = Time.now
|
25
|
+
client.puts(response)
|
26
|
+
# t4 = Time.now
|
27
|
+
client.close
|
28
|
+
# t5 = Time.now
|
29
|
+
|
30
|
+
# puts "packet process time: #{t3-t2}"
|
31
|
+
# total_receive_time += (t2-t1)
|
32
|
+
# total_process_time += (t3-t2)
|
33
|
+
# total_xmit_time += (t4-t3)
|
34
|
+
# total_close_time += (t5-t4)
|
35
|
+
# total_packets += 1
|
36
|
+
# puts "total receive time: #{total_receive_time}"
|
37
|
+
# puts "total process time: #{total_process_time}"
|
38
|
+
# puts "total xmit time: #{total_xmit_time}"
|
39
|
+
# puts "total close time: #{total_close_time}"
|
40
|
+
# puts ''
|
41
|
+
# puts "total receive time: #{total_receive_time/total_packets}"
|
42
|
+
# puts "total process time: #{total_process_time/total_packets}"
|
43
|
+
# puts "total xmit time: #{total_xmit_time/total_packets}"
|
44
|
+
# puts "total close time: #{total_close_time/total_packets}"
|
45
|
+
end
|