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,25 +1,25 @@
|
|
1
|
-
require 'origen_link/vector_based'
|
2
|
-
|
3
|
-
module OrigenLink
|
4
|
-
module Test
|
5
|
-
class VectorBased < ::OrigenLink::VectorBased
|
6
|
-
attr_accessor :message, :microcodestr, :test_response
|
7
|
-
|
8
|
-
def send_cmd(cmdstr, argstr)
|
9
|
-
@message = cmdstr + ':' + argstr
|
10
|
-
@test_response
|
11
|
-
end
|
12
|
-
|
13
|
-
def send_batch(vector_batch)
|
14
|
-
@test_response
|
15
|
-
end
|
16
|
-
|
17
|
-
def setup_cmd_response_logger(command, response)
|
18
|
-
end
|
19
|
-
|
20
|
-
def microcode(msg)
|
21
|
-
@microcodestr = @microcodestr + msg
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
1
|
+
require 'origen_link/vector_based'
|
2
|
+
|
3
|
+
module OrigenLink
|
4
|
+
module Test
|
5
|
+
class VectorBased < ::OrigenLink::VectorBased
|
6
|
+
attr_accessor :message, :microcodestr, :test_response
|
7
|
+
|
8
|
+
def send_cmd(cmdstr, argstr)
|
9
|
+
@message = cmdstr + ':' + argstr
|
10
|
+
@test_response
|
11
|
+
end
|
12
|
+
|
13
|
+
def send_batch(vector_batch)
|
14
|
+
@test_response
|
15
|
+
end
|
16
|
+
|
17
|
+
def setup_cmd_response_logger(command, response)
|
18
|
+
end
|
19
|
+
|
20
|
+
def microcode(msg)
|
21
|
+
@microcodestr = @microcodestr + msg
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -1,524 +1,526 @@
|
|
1
|
-
require 'etc'
|
2
|
-
require 'origen_testers'
|
3
|
-
require 'origen_link/server_com'
|
4
|
-
require 'origen_link/capture_support'
|
5
|
-
require 'origen_link/configuration_commands'
|
6
|
-
require 'origen_link/callback_handlers'
|
7
|
-
module OrigenLink
|
8
|
-
# OrigenLink::VectorBased
|
9
|
-
# This class describes the OrigenLink app plug-in. Vector data that Origen
|
10
|
-
# generates is intercepted and sent to a debug device (typically will be a Udoo
|
11
|
-
# Neo - www.udoo.org). The debug device can be any device that is able to serve
|
12
|
-
# a TCP socket, recieve and interpret the command set used by this class and send
|
13
|
-
# the expected responses.
|
14
|
-
#
|
15
|
-
class VectorBased
|
16
|
-
include OrigenTesters::VectorBasedTester
|
17
|
-
include ServerCom
|
18
|
-
include CaptureSupport
|
19
|
-
include ConfigurationCommands
|
20
|
-
|
21
|
-
# The number of cycles that fail
|
22
|
-
attr_accessor :fail_count
|
23
|
-
# The number of vector cycles generated
|
24
|
-
attr_reader :vector_count
|
25
|
-
# The accumulated total time spent communicating with the server
|
26
|
-
attr_reader :total_comm_time
|
27
|
-
# The accumulated total time spent establishing the server connection
|
28
|
-
attr_reader :total_connect_time
|
29
|
-
# The accumulated total time spent transmitting to the server app
|
30
|
-
attr_reader :total_xmit_time
|
31
|
-
# The accumulated total time spent receiving from the server app
|
32
|
-
attr_reader :total_recv_time
|
33
|
-
# The accumulated total number of packets sent to the server
|
34
|
-
attr_reader :total_packets
|
35
|
-
# The accumulated number of times push_vector was called with the present tset and pin info
|
36
|
-
attr_reader :vector_repeatcount
|
37
|
-
# The look up of programmed tsets. Names are converted to a unique number identifier
|
38
|
-
attr_reader :tsets_programmed
|
39
|
-
# Data captured using tester.capture
|
40
|
-
attr_reader :captured_data
|
41
|
-
# Array of vectors waiting to be sent to the sever
|
42
|
-
attr_reader :vector_batch
|
43
|
-
# Used with capture
|
44
|
-
attr_reader :store_pins_batch
|
45
|
-
# Array of comments received through push_comment
|
46
|
-
attr_reader :comment_batch
|
47
|
-
# The name of the user running OrigenLink
|
48
|
-
attr_reader :user_name
|
49
|
-
# Indicates that communication has been initiated with the server
|
50
|
-
attr_reader :initial_comm_sent
|
51
|
-
|
52
|
-
def initialize(address, port, options = {})
|
53
|
-
@address = address
|
54
|
-
@port = port
|
55
|
-
@fail_count = 0
|
56
|
-
@vector_count = 0
|
57
|
-
@previous_vectordata = ''
|
58
|
-
@previous_tset = ''
|
59
|
-
@vector_repeatcount = 0
|
60
|
-
@total_comm_time = 0
|
61
|
-
@total_connect_time = 0
|
62
|
-
@total_xmit_time = 0
|
63
|
-
@total_recv_time = 0
|
64
|
-
@total_packets = 0
|
65
|
-
@max_packet_time = 0
|
66
|
-
@max_receive_time = 0
|
67
|
-
@tsets_programmed = {}
|
68
|
-
@tsets_warned = {}
|
69
|
-
@tset_count = 1
|
70
|
-
@store_pins = []
|
71
|
-
@captured_data = []
|
72
|
-
# A tester seems to be unable to register as a callback handler, so for now instantiating a
|
73
|
-
# dedicated object to implement the handlers related to this tester
|
74
|
-
CallbackHandlers.new
|
75
|
-
@vector_batch = []
|
76
|
-
@store_pins_batch = {}
|
77
|
-
@comment_batch = {}
|
78
|
-
@batch_vectors = true
|
79
|
-
@pattern_link_messages = []
|
80
|
-
@pattern_comments = {}
|
81
|
-
@user_name = Etc.getlogin
|
82
|
-
@initial_comm_sent = false
|
83
|
-
@initial_vector_pushed = false
|
84
|
-
@pinorder = ''
|
85
|
-
@pinmap_hash = {}
|
86
|
-
@batched_setup_cmds = []
|
87
|
-
|
88
|
-
# check the server version against the plug-in version
|
89
|
-
response = send_cmd('version', '')
|
90
|
-
response = 'Error' if response.nil? # prevent run time error in regression tests
|
91
|
-
response.chomp!
|
92
|
-
server_version = response.split(':')[1]
|
93
|
-
server_version = '?.?.? - 0.2.0 or earlier' if response =~ /Error/
|
94
|
-
app_version = OrigenLink::VERSION # Origen.app(:origen_link).version
|
95
|
-
Origen.log.info("Plug-in link version: #{app_version}, Server link version: #{server_version}")
|
96
|
-
unless app_version == server_version
|
97
|
-
Origen.log.warn('Server version and plug-in link versions do not match')
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
# warn but don't fail if an api for another tester is not implmented
|
102
|
-
def method_missing(m, *args, &block)
|
103
|
-
Origen.log.warn "#{m} is not implemented by origen_link and will be ignored"
|
104
|
-
end
|
105
|
-
|
106
|
-
# push_comment
|
107
|
-
# This method intercepts comments so they can be correctly placed in the output file
|
108
|
-
# when vector batching is used
|
109
|
-
def push_comment(msg)
|
110
|
-
if @batch_vectors
|
111
|
-
key = @vector_batch.length
|
112
|
-
if @comment_batch.key?(key)
|
113
|
-
@comment_batch[key] = @comment_batch[key] + "\n" + msg
|
114
|
-
else
|
115
|
-
@comment_batch[key] = msg
|
116
|
-
end
|
117
|
-
pattern_key = @pattern_link_messages.length + key
|
118
|
-
@pattern_comments[pattern_key] = @comment_batch[key]
|
119
|
-
else
|
120
|
-
microcode msg
|
121
|
-
end
|
122
|
-
end
|
123
|
-
|
124
|
-
# ordered_pins(options = {})
|
125
|
-
# expand pin groups to their component pins after the pin ordering is completed
|
126
|
-
# OrigenLink always operates on individual pins. This saves other methods
|
127
|
-
# from each needing to handle pins and/or groups of pins.
|
128
|
-
def ordered_pins(options = {})
|
129
|
-
result = super
|
130
|
-
groups = []
|
131
|
-
result.each { |p| groups << p if p.size > 1 }
|
132
|
-
groups.each do |group|
|
133
|
-
# locate this group in the result array
|
134
|
-
i = result.index(group)
|
135
|
-
result.delete_at(i)
|
136
|
-
dut.pins(group.id).map.each do |sub_pin|
|
137
|
-
result.insert(i, sub_pin)
|
138
|
-
i += 1
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
|
-
if @pinmap.nil?
|
143
|
-
# create the pinmap if pin metadata was provided
|
144
|
-
pinarr = []
|
145
|
-
result.each do |pin|
|
146
|
-
if pin.meta.key?(:link_io)
|
147
|
-
pinarr << pin.name.to_s
|
148
|
-
pinarr << pin.meta[:link_io].to_s
|
149
|
-
end
|
150
|
-
end
|
151
|
-
self.pinmap = pinarr.join(',') unless pinarr.size == 0
|
152
|
-
end
|
153
|
-
|
154
|
-
result
|
155
|
-
end
|
156
|
-
|
157
|
-
# fix_ordered_pins(options)
|
158
|
-
# This method is called the first time push_vector is called.
|
159
|
-
#
|
160
|
-
# This method will create the pinmap from pin meta data if needed.
|
161
|
-
#
|
162
|
-
# This method will remove any pin data that doesn't correspond
|
163
|
-
# to a pin in the link pinmap and remove those pins from the
|
164
|
-
# @ordered_pins_cache to prevent them from being rendered
|
165
|
-
# on the next cycle.
|
166
|
-
# This will prevent unwanted behavior. The link server
|
167
|
-
# expects only pin data for pins in the pinmap.
|
168
|
-
def fix_ordered_pins(options)
|
169
|
-
# remove non-mapped pins from the ordered pins cache - prevents them appearing in future push_vector calls
|
170
|
-
orig_size = @ordered_pins_cache.size
|
171
|
-
@ordered_pins_cache.delete_if { |p| !@pinmap_hash[p.name.to_s] }
|
172
|
-
Origen.log.debug('OrigenLink removed non-mapped pins from the cached pin order array') unless orig_size == @ordered_pins_cache.size
|
173
|
-
# update pin values for the current push_vector call
|
174
|
-
vals = []
|
175
|
-
@ordered_pins_cache.each { |p| vals << p.to_vector }
|
176
|
-
options[:pin_vals] = vals.join('')
|
177
|
-
options
|
178
|
-
end
|
179
|
-
|
180
|
-
# push_vector
|
181
|
-
# This method intercepts vector data from Origen, removes white spaces and compresses repeats
|
182
|
-
def push_vector(options)
|
183
|
-
unless @initial_vector_pushed
|
184
|
-
if @pinmap.nil?
|
185
|
-
Origen.log.error('OrigenLink: pinmap has not been setup, use tester.pinmap= to initialize a pinmap')
|
186
|
-
else
|
187
|
-
Origen.log.debug('OrigenLink: executing pattern with pinmap:' + @pinmap.to_s)
|
188
|
-
end
|
189
|
-
|
190
|
-
# remove pins not in the link pinmap
|
191
|
-
options = fix_ordered_pins(options)
|
192
|
-
|
193
|
-
# now send any configuration commands that were saved prior to pinmap setup (clears all server configs)
|
194
|
-
@batched_setup_cmds.each do |cmd|
|
195
|
-
response = send_cmd(cmd[0], cmd[1])
|
196
|
-
setup_cmd_response_logger(cmd[0], response)
|
197
|
-
end
|
198
|
-
|
199
|
-
@initial_vector_pushed = true
|
200
|
-
end
|
201
|
-
set_pinorder if @pinorder == ''
|
202
|
-
programmed_data = options[:pin_vals].gsub(/\s+/, '')
|
203
|
-
unless options[:timeset]
|
204
|
-
puts 'No timeset defined!'
|
205
|
-
puts 'Add one to your top level startup method or target like this:'
|
206
|
-
puts 'tester.set_timeset("nvmbist", 40) # Where 40 is the period in ns'
|
207
|
-
exit 1
|
208
|
-
end
|
209
|
-
tset = options[:timeset].name
|
210
|
-
local_repeat = options[:repeat].nil? ? 1 : options[:repeat]
|
211
|
-
if @vector_count > 0
|
212
|
-
# compressing repeats as we go
|
213
|
-
if (programmed_data == @previous_vectordata) && (@previous_tset == tset) && @store_pins.empty?
|
214
|
-
@vector_repeatcount += local_repeat
|
215
|
-
else
|
216
|
-
# all repeats of the previous vector have been counted
|
217
|
-
# time to flush. Don't panic though! @previous_vectordata
|
218
|
-
# is what gets flushed. programmed_data is passed as an
|
219
|
-
# arg to be set as the new @previous_vectordata
|
220
|
-
flush_vector(programmed_data, tset, local_repeat)
|
221
|
-
end
|
222
|
-
else
|
223
|
-
# if this is the first vector of the pattern, insure variables are initialized
|
224
|
-
@previous_vectordata = programmed_data
|
225
|
-
@previous_tset = tset
|
226
|
-
@vector_repeatcount = 1
|
227
|
-
end # if vector_count > 0
|
228
|
-
@vector_count += 1
|
229
|
-
end
|
230
|
-
|
231
|
-
# flush_vector
|
232
|
-
# Just as the name suggests, this method "flushes" a vector. This is necessary because
|
233
|
-
# of repeat compression (a vector isn't sent until different vector data is encountered)
|
234
|
-
def flush_vector(programmed_data = '', tset = '', local_repeat = 1)
|
235
|
-
# prevent server crash when vector_flush is used during debug
|
236
|
-
unless @previous_vectordata == ''
|
237
|
-
if @vector_repeatcount > 1
|
238
|
-
repeat_prefix = "repeat#{@vector_repeatcount},"
|
239
|
-
else
|
240
|
-
repeat_prefix = ''
|
241
|
-
end
|
242
|
-
if @tsets_programmed[@previous_tset]
|
243
|
-
tset_prefix = "tset#{@tsets_programmed[@previous_tset]},"
|
244
|
-
else
|
245
|
-
# The hash of programmed tsets does not contain this tset
|
246
|
-
# Check the timing api to see if there is timing info there
|
247
|
-
# and send the timing info to the link server
|
248
|
-
if dut.respond_to?(:timeset)
|
249
|
-
tset_prefix = process_timeset(tset)
|
250
|
-
else
|
251
|
-
tset_warning(tset)
|
252
|
-
tset_prefix = ''
|
253
|
-
end
|
254
|
-
end
|
255
|
-
|
256
|
-
if @batch_vectors
|
257
|
-
@vector_batch << 'pin_cycle:' + tset_prefix + repeat_prefix + @previous_vectordata
|
258
|
-
# store capture pins for batch processing
|
259
|
-
unless @store_pins.empty?
|
260
|
-
@store_pins_batch[@vector_batch.length - 1] = @store_pins
|
261
|
-
end
|
262
|
-
else
|
263
|
-
process_vector_response(send_cmd('pin_cycle', tset_prefix + repeat_prefix + @previous_vectordata))
|
264
|
-
end
|
265
|
-
|
266
|
-
# make sure that only requested vectors are stored when batching is enabled
|
267
|
-
@store_pins = []
|
268
|
-
end
|
269
|
-
|
270
|
-
@vector_repeatcount = local_repeat
|
271
|
-
@previous_vectordata = programmed_data
|
272
|
-
@previous_tset = tset
|
273
|
-
end
|
274
|
-
|
275
|
-
# synchronize
|
276
|
-
# This method will synchronize the DUT state with Origen. All generated
|
277
|
-
# vectors are sent to the DUT for execution and the responses are processed
|
278
|
-
def synchronize(output_file = '')
|
279
|
-
flush_vector
|
280
|
-
if @batch_vectors
|
281
|
-
process_response(send_batch(@vector_batch), output_file)
|
282
|
-
end
|
283
|
-
@vector_batch = []
|
284
|
-
@store_pins_batch.clear
|
285
|
-
@comment_batch.clear
|
286
|
-
end
|
287
|
-
|
288
|
-
# process_response
|
289
|
-
# This method will process a server response. Send log info to the output,
|
290
|
-
# keep track of fail count and captured data
|
291
|
-
def process_response(response, output_file = '')
|
292
|
-
if response.is_a?(Array)
|
293
|
-
# if called from finalize_pattern -> synchronize, open the output_file and store results
|
294
|
-
output_obj = nil
|
295
|
-
output_obj = File.open(output_file, 'a+') unless output_file == ''
|
296
|
-
|
297
|
-
# in case there were only comments and no vectors, place comments (if any)
|
298
|
-
microcode @comment_batch[0] if response.size == 0
|
299
|
-
|
300
|
-
response.each_index do |index|
|
301
|
-
# restore store pins state for processing
|
302
|
-
if @store_pins_batch.key?(index)
|
303
|
-
@store_pins = @store_pins_batch[index]
|
304
|
-
else
|
305
|
-
@store_pins = []
|
306
|
-
end
|
307
|
-
process_vector_response(response[index], output_obj)
|
308
|
-
if @comment_batch.key?(index)
|
309
|
-
if output_file == ''
|
310
|
-
microcode @comment_batch[index]
|
311
|
-
else
|
312
|
-
# get the header placed correctly, the below code doesn't work
|
313
|
-
# if index == response.length - 1
|
314
|
-
# output_obj.puts 'last comment'
|
315
|
-
# output_obj.lineno = 0
|
316
|
-
# end
|
317
|
-
output_obj.puts(@comment_batch[index])
|
318
|
-
end
|
319
|
-
end
|
320
|
-
end
|
321
|
-
output_obj.close unless output_file == ''
|
322
|
-
else
|
323
|
-
process_vector_response(response)
|
324
|
-
end
|
325
|
-
end
|
326
|
-
|
327
|
-
# process_vector_response
|
328
|
-
# This method exists to prevent code duplication when handling an array of
|
329
|
-
# batched responses versus a single response string.
|
330
|
-
def process_vector_response(vector_response, output_obj = nil)
|
331
|
-
msg = ''
|
332
|
-
unless @store_pins.empty?
|
333
|
-
msg = " (Captured #{@store_pins.map(&:name).join(', ')})\n"
|
334
|
-
capture_data(vector_response)
|
335
|
-
vector_response.strip!
|
336
|
-
# vector_response += msg
|
337
|
-
end
|
338
|
-
vector_cycles = vector_response.split(/\s+/)
|
339
|
-
expected_msg = ''
|
340
|
-
expected_msg = ' ' + vector_cycles.pop if vector_cycles[vector_cycles.length - 1] =~ /Expected/
|
341
|
-
pfstatus = vector_cycles[0].chr
|
342
|
-
vector_cycles[0] = vector_cycles[0].byteslice(2, vector_cycles[0].length - 2)
|
343
|
-
|
344
|
-
vector_cycles.each do |cycle|
|
345
|
-
thiscyclefail = false
|
346
|
-
bad_pin_data = false
|
347
|
-
if pfstatus == 'F'
|
348
|
-
# check to see if this cycle failed
|
349
|
-
0.upto(cycle.length - 1) do |index|
|
350
|
-
bad_pin_data = true if (cycle[index] == 'W')
|
351
|
-
thiscyclefail = true if (cycle[index] == 'H') && (expected_msg[expected_msg.length - cycle.length + index] == 'L')
|
352
|
-
thiscyclefail = true if (cycle[index] == 'L') && (expected_msg[expected_msg.length - cycle.length + index] == 'H')
|
353
|
-
end
|
354
|
-
end
|
355
|
-
if thiscyclefail
|
356
|
-
expected_msg_prnt = expected_msg
|
357
|
-
prepend = 'F:'
|
358
|
-
else
|
359
|
-
expected_msg_prnt = ''
|
360
|
-
prepend = 'P:'
|
361
|
-
end
|
362
|
-
if bad_pin_data
|
363
|
-
expected_msg_prnt = ' ' + 'W indicates no operation, check timeset definition'
|
364
|
-
prepend = 'F:'
|
365
|
-
end
|
366
|
-
|
367
|
-
if output_obj.nil?
|
368
|
-
microcode prepend + cycle + expected_msg_prnt + msg
|
369
|
-
else
|
370
|
-
output_obj.puts(prepend + cycle + expected_msg_prnt + msg)
|
371
|
-
end
|
372
|
-
end
|
373
|
-
|
374
|
-
unless vector_response.chr == 'P'
|
375
|
-
# TODO: Put this back with an option to disable, based on a serial or parallel interface being used
|
376
|
-
# microcode 'E:' + @previous_vectordata + ' //expected data for previous vector'
|
377
|
-
@fail_count += 1
|
378
|
-
end
|
379
|
-
end
|
380
|
-
|
381
|
-
# initialize_pattern
|
382
|
-
# This method initializes variables at the start of a pattern.
|
383
|
-
# It is called automatically when pattern generation starts.
|
384
|
-
def initialize_pattern
|
385
|
-
@fail_count = 0
|
386
|
-
@vector_count = 0
|
387
|
-
@vector_batch.delete_if { true }
|
388
|
-
@store_pins_batch.clear
|
389
|
-
@comment_batch.clear
|
390
|
-
@pattern_link_messages.delete_if { true }
|
391
|
-
@pattern_comments.clear
|
392
|
-
|
393
|
-
@total_packets = 0
|
394
|
-
@total_comm_time = 0
|
395
|
-
@total_connect_time = 0
|
396
|
-
@total_xmit_time = 0
|
397
|
-
@total_recv_time = 0
|
398
|
-
|
399
|
-
# moved to push_vector to allow auto-pinmap
|
400
|
-
# if @pinmap.nil?
|
401
|
-
# Origen.log.error('OrigenLink: pinmap has not been setup, use tester.pinmap= to initialize a pinmap')
|
402
|
-
# else
|
403
|
-
# Origen.log.debug('OrigenLink: executing pattern with pinmap:' + @pinmap.to_s)
|
404
|
-
# end
|
405
|
-
end
|
406
|
-
|
407
|
-
# finalize_pattern
|
408
|
-
# This method flushes the final vector. Then, it logs success or failure of the
|
409
|
-
# pattern execution along with execution time information.
|
410
|
-
def finalize_pattern(output_file)
|
411
|
-
Origen.log.debug('Pattern generation completed. Sending all stored vector data')
|
412
|
-
synchronize(output_file)
|
413
|
-
send_cmd('', 'session_end')
|
414
|
-
# for debug, report communication times
|
415
|
-
Origen.log.debug("total communication time: #{@total_comm_time}")
|
416
|
-
Origen.log.debug("total connect time: #{@total_connect_time}")
|
417
|
-
Origen.log.debug("total transmit time: #{@total_xmit_time}")
|
418
|
-
Origen.log.debug("total receive time: #{@total_recv_time}")
|
419
|
-
Origen.log.debug("total packets: #{@total_packets}")
|
420
|
-
Origen.log.debug("total time per packet: #{@total_comm_time / @total_packets}")
|
421
|
-
Origen.log.debug("connect time per packet: #{@total_connect_time / @total_packets}")
|
422
|
-
Origen.log.debug("transmit time per packet: #{@total_xmit_time / @total_packets}")
|
423
|
-
Origen.log.debug("receive time per packet: #{@total_recv_time / @total_packets}")
|
424
|
-
Origen.log.debug("max packet time: #{@max_packet_time}")
|
425
|
-
Origen.log.debug("max duration command - #{@longest_packet}")
|
426
|
-
Origen.log.debug("max receive time: #{@max_receive_time}")
|
427
|
-
if @fail_count == 0
|
428
|
-
# Origen.log.success("PASS - pattern execution passed (#{@vector_count} vectors pass)")
|
429
|
-
Origen.app.stats.report_pass
|
430
|
-
else
|
431
|
-
# Origen.log.error("FAIL - pattern execution failed (#{@fail_count} failures)")
|
432
|
-
Origen.app.stats.report_fail
|
433
|
-
end
|
434
|
-
commands_file = Origen.app.current_job.output_file.split('.')[0] + '_link_cmds.txt'
|
435
|
-
File.open(commands_file, 'w') do |file|
|
436
|
-
file.puts("pin_assign:#{@pinmap}")
|
437
|
-
file.puts("pin_patternorder:#{@pinorder}")
|
438
|
-
@pattern_link_messages.each_index do |index|
|
439
|
-
file.puts(@pattern_link_messages[index])
|
440
|
-
file.puts(@pattern_comments[index]) if @pattern_comments.key?(index)
|
441
|
-
end
|
442
|
-
file.puts(':session_end')
|
443
|
-
end
|
444
|
-
end
|
445
|
-
|
446
|
-
# to_s
|
447
|
-
# returns 'OrigenLink::VectorBased'
|
448
|
-
#
|
449
|
-
# No longer a use for this. Use tester.link?
|
450
|
-
def to_s
|
451
|
-
'OrigenLink::VectorBased'
|
452
|
-
end
|
453
|
-
|
454
|
-
# transaction
|
455
|
-
# returns true/false indicating whether the transaction passed
|
456
|
-
# true = pass
|
457
|
-
# false = fail
|
458
|
-
#
|
459
|
-
# @example
|
460
|
-
# if !tester.transaction {dut.reg blah blah}
|
461
|
-
# puts 'transaction failed'
|
462
|
-
# end
|
463
|
-
def transaction
|
464
|
-
if block_given?
|
465
|
-
synchronize
|
466
|
-
transaction_fail_count = @fail_count
|
467
|
-
yield
|
468
|
-
synchronize
|
469
|
-
transaction_fail_count = @fail_count - transaction_fail_count
|
470
|
-
if transaction_fail_count == 0
|
471
|
-
true
|
472
|
-
else
|
473
|
-
false
|
474
|
-
end
|
475
|
-
else
|
476
|
-
true
|
477
|
-
end
|
478
|
-
end
|
479
|
-
|
480
|
-
# wait a fixed amount of time
|
481
|
-
# This method will run the Sleep method to wait rather than generating cycles.
|
482
|
-
# Cycle execution time is not predictable with OrigenLink.
|
483
|
-
#
|
484
|
-
# If you want cycles to be generated by this method include apply_cycles: true
|
485
|
-
# in the options hash
|
486
|
-
#
|
487
|
-
# This method currently doesn't handle match blocks.
|
488
|
-
def wait(options = {})
|
489
|
-
options = {
|
490
|
-
cycles: 0,
|
491
|
-
time_in_cycles: 0,
|
492
|
-
time_in_us: 0,
|
493
|
-
time_in_ns: 0,
|
494
|
-
time_in_ms: 0,
|
495
|
-
time_in_s: 0,
|
496
|
-
match: false, # Set to true to invoke a match loop where the supplied delay
|
497
|
-
apply_cycles: false
|
498
|
-
# will become the timeout duration
|
499
|
-
}.merge(options)
|
500
|
-
|
501
|
-
fail 'wait(match: true) is not yet implemented on Link' if options[:match]
|
502
|
-
|
503
|
-
time_delay = 0
|
504
|
-
time_delay += (options[:time_in_s])
|
505
|
-
time_delay += (options[:time_in_ms]) * 0.001
|
506
|
-
time_delay += (options[:time_in_us]) * 0.000001
|
507
|
-
time_delay += (options[:time_in_ns]) * 0.000000001
|
508
|
-
time_delay += (options[:cycles] + options[:time_in_cycles]) * current_period_in_ns * 0.000000001
|
509
|
-
|
510
|
-
if options[:apply_cycles]
|
511
|
-
cycles = time_delay / 0.00003 # cycle execution time is unpredictable, but ~= 30us
|
512
|
-
if options[:cycles] > 0 || options[:time_in_cycles] > 0
|
513
|
-
options[:repeat] = options[:cycles] + options[:time_in_cycles]
|
514
|
-
else
|
515
|
-
options[:repeat] = (cycles > 0) ? cycles : 1
|
516
|
-
end
|
517
|
-
cycle(options)
|
518
|
-
else
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
end
|
1
|
+
require 'etc'
|
2
|
+
require 'origen_testers'
|
3
|
+
require 'origen_link/server_com'
|
4
|
+
require 'origen_link/capture_support'
|
5
|
+
require 'origen_link/configuration_commands'
|
6
|
+
require 'origen_link/callback_handlers'
|
7
|
+
module OrigenLink
|
8
|
+
# OrigenLink::VectorBased
|
9
|
+
# This class describes the OrigenLink app plug-in. Vector data that Origen
|
10
|
+
# generates is intercepted and sent to a debug device (typically will be a Udoo
|
11
|
+
# Neo - www.udoo.org). The debug device can be any device that is able to serve
|
12
|
+
# a TCP socket, recieve and interpret the command set used by this class and send
|
13
|
+
# the expected responses.
|
14
|
+
#
|
15
|
+
class VectorBased
|
16
|
+
include OrigenTesters::VectorBasedTester
|
17
|
+
include ServerCom
|
18
|
+
include CaptureSupport
|
19
|
+
include ConfigurationCommands
|
20
|
+
|
21
|
+
# The number of cycles that fail
|
22
|
+
attr_accessor :fail_count
|
23
|
+
# The number of vector cycles generated
|
24
|
+
attr_reader :vector_count
|
25
|
+
# The accumulated total time spent communicating with the server
|
26
|
+
attr_reader :total_comm_time
|
27
|
+
# The accumulated total time spent establishing the server connection
|
28
|
+
attr_reader :total_connect_time
|
29
|
+
# The accumulated total time spent transmitting to the server app
|
30
|
+
attr_reader :total_xmit_time
|
31
|
+
# The accumulated total time spent receiving from the server app
|
32
|
+
attr_reader :total_recv_time
|
33
|
+
# The accumulated total number of packets sent to the server
|
34
|
+
attr_reader :total_packets
|
35
|
+
# The accumulated number of times push_vector was called with the present tset and pin info
|
36
|
+
attr_reader :vector_repeatcount
|
37
|
+
# The look up of programmed tsets. Names are converted to a unique number identifier
|
38
|
+
attr_reader :tsets_programmed
|
39
|
+
# Data captured using tester.capture
|
40
|
+
attr_reader :captured_data
|
41
|
+
# Array of vectors waiting to be sent to the sever
|
42
|
+
attr_reader :vector_batch
|
43
|
+
# Used with capture
|
44
|
+
attr_reader :store_pins_batch
|
45
|
+
# Array of comments received through push_comment
|
46
|
+
attr_reader :comment_batch
|
47
|
+
# The name of the user running OrigenLink
|
48
|
+
attr_reader :user_name
|
49
|
+
# Indicates that communication has been initiated with the server
|
50
|
+
attr_reader :initial_comm_sent
|
51
|
+
|
52
|
+
def initialize(address, port, options = {})
|
53
|
+
@address = address
|
54
|
+
@port = port
|
55
|
+
@fail_count = 0
|
56
|
+
@vector_count = 0
|
57
|
+
@previous_vectordata = ''
|
58
|
+
@previous_tset = ''
|
59
|
+
@vector_repeatcount = 0
|
60
|
+
@total_comm_time = 0
|
61
|
+
@total_connect_time = 0
|
62
|
+
@total_xmit_time = 0
|
63
|
+
@total_recv_time = 0
|
64
|
+
@total_packets = 0
|
65
|
+
@max_packet_time = 0
|
66
|
+
@max_receive_time = 0
|
67
|
+
@tsets_programmed = {}
|
68
|
+
@tsets_warned = {}
|
69
|
+
@tset_count = 1
|
70
|
+
@store_pins = []
|
71
|
+
@captured_data = []
|
72
|
+
# A tester seems to be unable to register as a callback handler, so for now instantiating a
|
73
|
+
# dedicated object to implement the handlers related to this tester
|
74
|
+
CallbackHandlers.new
|
75
|
+
@vector_batch = []
|
76
|
+
@store_pins_batch = {}
|
77
|
+
@comment_batch = {}
|
78
|
+
@batch_vectors = true
|
79
|
+
@pattern_link_messages = []
|
80
|
+
@pattern_comments = {}
|
81
|
+
@user_name = Etc.getlogin
|
82
|
+
@initial_comm_sent = false
|
83
|
+
@initial_vector_pushed = false
|
84
|
+
@pinorder = ''
|
85
|
+
@pinmap_hash = {}
|
86
|
+
@batched_setup_cmds = []
|
87
|
+
|
88
|
+
# check the server version against the plug-in version
|
89
|
+
response = send_cmd('version', '')
|
90
|
+
response = 'Error' if response.nil? # prevent run time error in regression tests
|
91
|
+
response.chomp!
|
92
|
+
server_version = response.split(':')[1]
|
93
|
+
server_version = '?.?.? - 0.2.0 or earlier' if response =~ /Error/
|
94
|
+
app_version = OrigenLink::VERSION # Origen.app(:origen_link).version
|
95
|
+
Origen.log.info("Plug-in link version: #{app_version}, Server link version: #{server_version}")
|
96
|
+
unless app_version == server_version
|
97
|
+
Origen.log.warn('Server version and plug-in link versions do not match')
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# warn but don't fail if an api for another tester is not implmented
|
102
|
+
def method_missing(m, *args, &block)
|
103
|
+
Origen.log.warn "#{m} is not implemented by origen_link and will be ignored"
|
104
|
+
end
|
105
|
+
|
106
|
+
# push_comment
|
107
|
+
# This method intercepts comments so they can be correctly placed in the output file
|
108
|
+
# when vector batching is used
|
109
|
+
def push_comment(msg)
|
110
|
+
if @batch_vectors
|
111
|
+
key = @vector_batch.length
|
112
|
+
if @comment_batch.key?(key)
|
113
|
+
@comment_batch[key] = @comment_batch[key] + "\n" + msg
|
114
|
+
else
|
115
|
+
@comment_batch[key] = msg
|
116
|
+
end
|
117
|
+
pattern_key = @pattern_link_messages.length + key
|
118
|
+
@pattern_comments[pattern_key] = @comment_batch[key]
|
119
|
+
else
|
120
|
+
microcode msg
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# ordered_pins(options = {})
|
125
|
+
# expand pin groups to their component pins after the pin ordering is completed
|
126
|
+
# OrigenLink always operates on individual pins. This saves other methods
|
127
|
+
# from each needing to handle pins and/or groups of pins.
|
128
|
+
def ordered_pins(options = {})
|
129
|
+
result = super
|
130
|
+
groups = []
|
131
|
+
result.each { |p| groups << p if p.size > 1 }
|
132
|
+
groups.each do |group|
|
133
|
+
# locate this group in the result array
|
134
|
+
i = result.index(group)
|
135
|
+
result.delete_at(i)
|
136
|
+
dut.pins(group.id).map.each do |sub_pin|
|
137
|
+
result.insert(i, sub_pin)
|
138
|
+
i += 1
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
if @pinmap.nil?
|
143
|
+
# create the pinmap if pin metadata was provided
|
144
|
+
pinarr = []
|
145
|
+
result.each do |pin|
|
146
|
+
if pin.meta.key?(:link_io)
|
147
|
+
pinarr << pin.name.to_s
|
148
|
+
pinarr << pin.meta[:link_io].to_s
|
149
|
+
end
|
150
|
+
end
|
151
|
+
self.pinmap = pinarr.join(',') unless pinarr.size == 0
|
152
|
+
end
|
153
|
+
|
154
|
+
result
|
155
|
+
end
|
156
|
+
|
157
|
+
# fix_ordered_pins(options)
|
158
|
+
# This method is called the first time push_vector is called.
|
159
|
+
#
|
160
|
+
# This method will create the pinmap from pin meta data if needed.
|
161
|
+
#
|
162
|
+
# This method will remove any pin data that doesn't correspond
|
163
|
+
# to a pin in the link pinmap and remove those pins from the
|
164
|
+
# @ordered_pins_cache to prevent them from being rendered
|
165
|
+
# on the next cycle.
|
166
|
+
# This will prevent unwanted behavior. The link server
|
167
|
+
# expects only pin data for pins in the pinmap.
|
168
|
+
def fix_ordered_pins(options)
|
169
|
+
# remove non-mapped pins from the ordered pins cache - prevents them appearing in future push_vector calls
|
170
|
+
orig_size = @ordered_pins_cache.size
|
171
|
+
@ordered_pins_cache.delete_if { |p| !@pinmap_hash[p.name.to_s] }
|
172
|
+
Origen.log.debug('OrigenLink removed non-mapped pins from the cached pin order array') unless orig_size == @ordered_pins_cache.size
|
173
|
+
# update pin values for the current push_vector call
|
174
|
+
vals = []
|
175
|
+
@ordered_pins_cache.each { |p| vals << p.to_vector }
|
176
|
+
options[:pin_vals] = vals.join('')
|
177
|
+
options
|
178
|
+
end
|
179
|
+
|
180
|
+
# push_vector
|
181
|
+
# This method intercepts vector data from Origen, removes white spaces and compresses repeats
|
182
|
+
def push_vector(options)
|
183
|
+
unless @initial_vector_pushed
|
184
|
+
if @pinmap.nil?
|
185
|
+
Origen.log.error('OrigenLink: pinmap has not been setup, use tester.pinmap= to initialize a pinmap')
|
186
|
+
else
|
187
|
+
Origen.log.debug('OrigenLink: executing pattern with pinmap:' + @pinmap.to_s)
|
188
|
+
end
|
189
|
+
|
190
|
+
# remove pins not in the link pinmap
|
191
|
+
options = fix_ordered_pins(options)
|
192
|
+
|
193
|
+
# now send any configuration commands that were saved prior to pinmap setup (clears all server configs)
|
194
|
+
@batched_setup_cmds.each do |cmd|
|
195
|
+
response = send_cmd(cmd[0], cmd[1])
|
196
|
+
setup_cmd_response_logger(cmd[0], response)
|
197
|
+
end
|
198
|
+
|
199
|
+
@initial_vector_pushed = true
|
200
|
+
end
|
201
|
+
set_pinorder if @pinorder == ''
|
202
|
+
programmed_data = options[:pin_vals].gsub(/\s+/, '')
|
203
|
+
unless options[:timeset]
|
204
|
+
puts 'No timeset defined!'
|
205
|
+
puts 'Add one to your top level startup method or target like this:'
|
206
|
+
puts 'tester.set_timeset("nvmbist", 40) # Where 40 is the period in ns'
|
207
|
+
exit 1
|
208
|
+
end
|
209
|
+
tset = options[:timeset].name
|
210
|
+
local_repeat = options[:repeat].nil? ? 1 : options[:repeat]
|
211
|
+
if @vector_count > 0
|
212
|
+
# compressing repeats as we go
|
213
|
+
if (programmed_data == @previous_vectordata) && (@previous_tset == tset) && @store_pins.empty?
|
214
|
+
@vector_repeatcount += local_repeat
|
215
|
+
else
|
216
|
+
# all repeats of the previous vector have been counted
|
217
|
+
# time to flush. Don't panic though! @previous_vectordata
|
218
|
+
# is what gets flushed. programmed_data is passed as an
|
219
|
+
# arg to be set as the new @previous_vectordata
|
220
|
+
flush_vector(programmed_data, tset, local_repeat)
|
221
|
+
end
|
222
|
+
else
|
223
|
+
# if this is the first vector of the pattern, insure variables are initialized
|
224
|
+
@previous_vectordata = programmed_data
|
225
|
+
@previous_tset = tset
|
226
|
+
@vector_repeatcount = 1
|
227
|
+
end # if vector_count > 0
|
228
|
+
@vector_count += 1
|
229
|
+
end
|
230
|
+
|
231
|
+
# flush_vector
|
232
|
+
# Just as the name suggests, this method "flushes" a vector. This is necessary because
|
233
|
+
# of repeat compression (a vector isn't sent until different vector data is encountered)
|
234
|
+
def flush_vector(programmed_data = '', tset = '', local_repeat = 1)
|
235
|
+
# prevent server crash when vector_flush is used during debug
|
236
|
+
unless @previous_vectordata == ''
|
237
|
+
if @vector_repeatcount > 1
|
238
|
+
repeat_prefix = "repeat#{@vector_repeatcount},"
|
239
|
+
else
|
240
|
+
repeat_prefix = ''
|
241
|
+
end
|
242
|
+
if @tsets_programmed[@previous_tset]
|
243
|
+
tset_prefix = "tset#{@tsets_programmed[@previous_tset]},"
|
244
|
+
else
|
245
|
+
# The hash of programmed tsets does not contain this tset
|
246
|
+
# Check the timing api to see if there is timing info there
|
247
|
+
# and send the timing info to the link server
|
248
|
+
if dut.respond_to?(:timeset)
|
249
|
+
tset_prefix = process_timeset(tset)
|
250
|
+
else
|
251
|
+
tset_warning(tset)
|
252
|
+
tset_prefix = ''
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
if @batch_vectors
|
257
|
+
@vector_batch << 'pin_cycle:' + tset_prefix + repeat_prefix + @previous_vectordata
|
258
|
+
# store capture pins for batch processing
|
259
|
+
unless @store_pins.empty?
|
260
|
+
@store_pins_batch[@vector_batch.length - 1] = @store_pins
|
261
|
+
end
|
262
|
+
else
|
263
|
+
process_vector_response(send_cmd('pin_cycle', tset_prefix + repeat_prefix + @previous_vectordata))
|
264
|
+
end
|
265
|
+
|
266
|
+
# make sure that only requested vectors are stored when batching is enabled
|
267
|
+
@store_pins = []
|
268
|
+
end
|
269
|
+
|
270
|
+
@vector_repeatcount = local_repeat
|
271
|
+
@previous_vectordata = programmed_data
|
272
|
+
@previous_tset = tset
|
273
|
+
end
|
274
|
+
|
275
|
+
# synchronize
|
276
|
+
# This method will synchronize the DUT state with Origen. All generated
|
277
|
+
# vectors are sent to the DUT for execution and the responses are processed
|
278
|
+
def synchronize(output_file = '')
|
279
|
+
flush_vector
|
280
|
+
if @batch_vectors
|
281
|
+
process_response(send_batch(@vector_batch), output_file)
|
282
|
+
end
|
283
|
+
@vector_batch = []
|
284
|
+
@store_pins_batch.clear
|
285
|
+
@comment_batch.clear
|
286
|
+
end
|
287
|
+
|
288
|
+
# process_response
|
289
|
+
# This method will process a server response. Send log info to the output,
|
290
|
+
# keep track of fail count and captured data
|
291
|
+
def process_response(response, output_file = '')
|
292
|
+
if response.is_a?(Array)
|
293
|
+
# if called from finalize_pattern -> synchronize, open the output_file and store results
|
294
|
+
output_obj = nil
|
295
|
+
output_obj = File.open(output_file, 'a+') unless output_file == ''
|
296
|
+
|
297
|
+
# in case there were only comments and no vectors, place comments (if any)
|
298
|
+
microcode @comment_batch[0] if response.size == 0
|
299
|
+
|
300
|
+
response.each_index do |index|
|
301
|
+
# restore store pins state for processing
|
302
|
+
if @store_pins_batch.key?(index)
|
303
|
+
@store_pins = @store_pins_batch[index]
|
304
|
+
else
|
305
|
+
@store_pins = []
|
306
|
+
end
|
307
|
+
process_vector_response(response[index], output_obj)
|
308
|
+
if @comment_batch.key?(index)
|
309
|
+
if output_file == ''
|
310
|
+
microcode @comment_batch[index]
|
311
|
+
else
|
312
|
+
# get the header placed correctly, the below code doesn't work
|
313
|
+
# if index == response.length - 1
|
314
|
+
# output_obj.puts 'last comment'
|
315
|
+
# output_obj.lineno = 0
|
316
|
+
# end
|
317
|
+
output_obj.puts(@comment_batch[index])
|
318
|
+
end
|
319
|
+
end
|
320
|
+
end
|
321
|
+
output_obj.close unless output_file == ''
|
322
|
+
else
|
323
|
+
process_vector_response(response)
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
# process_vector_response
|
328
|
+
# This method exists to prevent code duplication when handling an array of
|
329
|
+
# batched responses versus a single response string.
|
330
|
+
def process_vector_response(vector_response, output_obj = nil)
|
331
|
+
msg = ''
|
332
|
+
unless @store_pins.empty?
|
333
|
+
msg = " (Captured #{@store_pins.map(&:name).join(', ')})\n"
|
334
|
+
capture_data(vector_response)
|
335
|
+
vector_response.strip!
|
336
|
+
# vector_response += msg
|
337
|
+
end
|
338
|
+
vector_cycles = vector_response.split(/\s+/)
|
339
|
+
expected_msg = ''
|
340
|
+
expected_msg = ' ' + vector_cycles.pop if vector_cycles[vector_cycles.length - 1] =~ /Expected/
|
341
|
+
pfstatus = vector_cycles[0].chr
|
342
|
+
vector_cycles[0] = vector_cycles[0].byteslice(2, vector_cycles[0].length - 2)
|
343
|
+
|
344
|
+
vector_cycles.each do |cycle|
|
345
|
+
thiscyclefail = false
|
346
|
+
bad_pin_data = false
|
347
|
+
if pfstatus == 'F'
|
348
|
+
# check to see if this cycle failed
|
349
|
+
0.upto(cycle.length - 1) do |index|
|
350
|
+
bad_pin_data = true if (cycle[index] == 'W')
|
351
|
+
thiscyclefail = true if (cycle[index] == 'H') && (expected_msg[expected_msg.length - cycle.length + index] == 'L')
|
352
|
+
thiscyclefail = true if (cycle[index] == 'L') && (expected_msg[expected_msg.length - cycle.length + index] == 'H')
|
353
|
+
end
|
354
|
+
end
|
355
|
+
if thiscyclefail
|
356
|
+
expected_msg_prnt = expected_msg
|
357
|
+
prepend = 'F:'
|
358
|
+
else
|
359
|
+
expected_msg_prnt = ''
|
360
|
+
prepend = 'P:'
|
361
|
+
end
|
362
|
+
if bad_pin_data
|
363
|
+
expected_msg_prnt = ' ' + 'W indicates no operation, check timeset definition'
|
364
|
+
prepend = 'F:'
|
365
|
+
end
|
366
|
+
|
367
|
+
if output_obj.nil?
|
368
|
+
microcode prepend + cycle + expected_msg_prnt + msg
|
369
|
+
else
|
370
|
+
output_obj.puts(prepend + cycle + expected_msg_prnt + msg)
|
371
|
+
end
|
372
|
+
end
|
373
|
+
|
374
|
+
unless vector_response.chr == 'P'
|
375
|
+
# TODO: Put this back with an option to disable, based on a serial or parallel interface being used
|
376
|
+
# microcode 'E:' + @previous_vectordata + ' //expected data for previous vector'
|
377
|
+
@fail_count += 1
|
378
|
+
end
|
379
|
+
end
|
380
|
+
|
381
|
+
# initialize_pattern
|
382
|
+
# This method initializes variables at the start of a pattern.
|
383
|
+
# It is called automatically when pattern generation starts.
|
384
|
+
def initialize_pattern
|
385
|
+
@fail_count = 0
|
386
|
+
@vector_count = 0
|
387
|
+
@vector_batch.delete_if { true }
|
388
|
+
@store_pins_batch.clear
|
389
|
+
@comment_batch.clear
|
390
|
+
@pattern_link_messages.delete_if { true }
|
391
|
+
@pattern_comments.clear
|
392
|
+
|
393
|
+
@total_packets = 0
|
394
|
+
@total_comm_time = 0
|
395
|
+
@total_connect_time = 0
|
396
|
+
@total_xmit_time = 0
|
397
|
+
@total_recv_time = 0
|
398
|
+
|
399
|
+
# moved to push_vector to allow auto-pinmap
|
400
|
+
# if @pinmap.nil?
|
401
|
+
# Origen.log.error('OrigenLink: pinmap has not been setup, use tester.pinmap= to initialize a pinmap')
|
402
|
+
# else
|
403
|
+
# Origen.log.debug('OrigenLink: executing pattern with pinmap:' + @pinmap.to_s)
|
404
|
+
# end
|
405
|
+
end
|
406
|
+
|
407
|
+
# finalize_pattern
|
408
|
+
# This method flushes the final vector. Then, it logs success or failure of the
|
409
|
+
# pattern execution along with execution time information.
|
410
|
+
def finalize_pattern(output_file)
|
411
|
+
Origen.log.debug('Pattern generation completed. Sending all stored vector data')
|
412
|
+
synchronize(output_file)
|
413
|
+
send_cmd('', 'session_end')
|
414
|
+
# for debug, report communication times
|
415
|
+
Origen.log.debug("total communication time: #{@total_comm_time}")
|
416
|
+
Origen.log.debug("total connect time: #{@total_connect_time}")
|
417
|
+
Origen.log.debug("total transmit time: #{@total_xmit_time}")
|
418
|
+
Origen.log.debug("total receive time: #{@total_recv_time}")
|
419
|
+
Origen.log.debug("total packets: #{@total_packets}")
|
420
|
+
Origen.log.debug("total time per packet: #{@total_comm_time / @total_packets}")
|
421
|
+
Origen.log.debug("connect time per packet: #{@total_connect_time / @total_packets}")
|
422
|
+
Origen.log.debug("transmit time per packet: #{@total_xmit_time / @total_packets}")
|
423
|
+
Origen.log.debug("receive time per packet: #{@total_recv_time / @total_packets}")
|
424
|
+
Origen.log.debug("max packet time: #{@max_packet_time}")
|
425
|
+
Origen.log.debug("max duration command - #{@longest_packet}")
|
426
|
+
Origen.log.debug("max receive time: #{@max_receive_time}")
|
427
|
+
if @fail_count == 0
|
428
|
+
# Origen.log.success("PASS - pattern execution passed (#{@vector_count} vectors pass)")
|
429
|
+
Origen.app.stats.report_pass
|
430
|
+
else
|
431
|
+
# Origen.log.error("FAIL - pattern execution failed (#{@fail_count} failures)")
|
432
|
+
Origen.app.stats.report_fail
|
433
|
+
end
|
434
|
+
commands_file = Origen.app.current_job.output_file.split('.')[0] + '_link_cmds.txt'
|
435
|
+
File.open(commands_file, 'w') do |file|
|
436
|
+
file.puts("pin_assign:#{@pinmap}")
|
437
|
+
file.puts("pin_patternorder:#{@pinorder}")
|
438
|
+
@pattern_link_messages.each_index do |index|
|
439
|
+
file.puts(@pattern_link_messages[index])
|
440
|
+
file.puts(@pattern_comments[index]) if @pattern_comments.key?(index)
|
441
|
+
end
|
442
|
+
file.puts(':session_end')
|
443
|
+
end
|
444
|
+
end
|
445
|
+
|
446
|
+
# to_s
|
447
|
+
# returns 'OrigenLink::VectorBased'
|
448
|
+
#
|
449
|
+
# No longer a use for this. Use tester.link?
|
450
|
+
def to_s
|
451
|
+
'OrigenLink::VectorBased'
|
452
|
+
end
|
453
|
+
|
454
|
+
# transaction
|
455
|
+
# returns true/false indicating whether the transaction passed
|
456
|
+
# true = pass
|
457
|
+
# false = fail
|
458
|
+
#
|
459
|
+
# @example
|
460
|
+
# if !tester.transaction {dut.reg blah blah}
|
461
|
+
# puts 'transaction failed'
|
462
|
+
# end
|
463
|
+
def transaction
|
464
|
+
if block_given?
|
465
|
+
synchronize
|
466
|
+
transaction_fail_count = @fail_count
|
467
|
+
yield
|
468
|
+
synchronize
|
469
|
+
transaction_fail_count = @fail_count - transaction_fail_count
|
470
|
+
if transaction_fail_count == 0
|
471
|
+
true
|
472
|
+
else
|
473
|
+
false
|
474
|
+
end
|
475
|
+
else
|
476
|
+
true
|
477
|
+
end
|
478
|
+
end
|
479
|
+
|
480
|
+
# wait a fixed amount of time
|
481
|
+
# This method will run the Sleep method to wait rather than generating cycles.
|
482
|
+
# Cycle execution time is not predictable with OrigenLink.
|
483
|
+
#
|
484
|
+
# If you want cycles to be generated by this method include apply_cycles: true
|
485
|
+
# in the options hash
|
486
|
+
#
|
487
|
+
# This method currently doesn't handle match blocks.
|
488
|
+
def wait(options = {})
|
489
|
+
options = {
|
490
|
+
cycles: 0,
|
491
|
+
time_in_cycles: 0,
|
492
|
+
time_in_us: 0,
|
493
|
+
time_in_ns: 0,
|
494
|
+
time_in_ms: 0,
|
495
|
+
time_in_s: 0,
|
496
|
+
match: false, # Set to true to invoke a match loop where the supplied delay
|
497
|
+
apply_cycles: false
|
498
|
+
# will become the timeout duration
|
499
|
+
}.merge(options)
|
500
|
+
|
501
|
+
fail 'wait(match: true) is not yet implemented on Link' if options[:match]
|
502
|
+
|
503
|
+
time_delay = 0
|
504
|
+
time_delay += (options[:time_in_s])
|
505
|
+
time_delay += (options[:time_in_ms]) * 0.001
|
506
|
+
time_delay += (options[:time_in_us]) * 0.000001
|
507
|
+
time_delay += (options[:time_in_ns]) * 0.000000001
|
508
|
+
time_delay += (options[:cycles] + options[:time_in_cycles]) * current_period_in_ns * 0.000000001
|
509
|
+
|
510
|
+
if options[:apply_cycles]
|
511
|
+
cycles = time_delay / 0.00003 # cycle execution time is unpredictable, but ~= 30us
|
512
|
+
if options[:cycles] > 0 || options[:time_in_cycles] > 0
|
513
|
+
options[:repeat] = options[:cycles] + options[:time_in_cycles]
|
514
|
+
else
|
515
|
+
options[:repeat] = (cycles > 0) ? cycles : 1
|
516
|
+
end
|
517
|
+
cycle(options)
|
518
|
+
else
|
519
|
+
# ensure at least 1 cycle is generated so that pin changes always get applied before the wait
|
520
|
+
cycle
|
521
|
+
synchronize # ensure all generated cycles are executed before delay is run
|
522
|
+
sleep time_delay
|
523
|
+
end
|
524
|
+
end
|
525
|
+
end
|
526
|
+
end
|