origen_link 0.1.0.pre0

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