• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Origen-SDK / origen_testers / 16837288615

08 Aug 2025 06:04PM UTC coverage: 87.747% (+0.02%) from 87.727%
16837288615

Pull #231

github

web-flow
Merge eb3085ba3 into ba06feffe
Pull Request #231: Update on_whenever/on_loop nodes to create input flow variables for string variables

14 of 14 new or added lines in 1 file covered. (100.0%)

52 existing lines in 3 files now uncovered.

13299 of 15156 relevant lines covered (87.75%)

6221.06 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.24
/lib/origen_testers/test/interface.rb
1
module OrigenTesters
4✔
2
  module Test
4✔
3
    class Interface
4✔
4
      include OrigenTesters::ProgramGenerators
4✔
5
      include OrigenTesters::Charz
4✔
6

7
      attr_accessor :include_additional_prb2_test
4✔
8
      attr_reader :environment
4✔
9

10
      # Options passed to Flow.create and Library.create will be passed in here, use as
11
      # desired to configure your interface
12
      def initialize(options = {})
4✔
13
        @environment = options[:environment]
400✔
14
        add_charz
400✔
15
        add_my_tml if tester.v93k?
400✔
16
      end
17

18
      def add_my_tml
4✔
19
        add_tml :my_hash_tml,
308✔
20
                class_name:   'MyTmlHashNamespace',
21

22
                # Here is a test definition.
23
                # The identifier should be lower-cased and underscored, in-keeping with Ruby naming conventions.
24
                # By default the class name will be the camel-cased version of this identifier, so 'myTest' in
25
                # this case.
26
                my_hash_test: {
27
                  # [OPTIONAL] The C++ test method class name can be overridden from the default like this:
28
                  class_name:             'MyHashExampleClass',
29
                  # [OPTIONAL] If the test method does not require a definition in the testmethodlimits section
30
                  #    of the .tf file, you can suppress like this:
31
                  # render_limits_in_file: false,
32
                  # Parameters can be defined with an underscored symbol as the name, this can be used
33
                  # if the C++ implementation follows the standard V93K convention of calling the attribute
34
                  # the camel cased version, starting with a lower-cased letter, i.e. 'testerState' in this
35
                  # first example.
36
                  # The attribute definition has two required parameters, the type and the default value.
37
                  # The type can be :string, :current, :voltage, :time, :frequency, integer, :double or :boolean
38
                  pin_list:               [:string, ''],
39
                  samples:                [:integer, 1],
40
                  precharge_voltage:      [:voltage, 0],
41
                  settling_time:          [:time, 0],
42
                  # An optional parameter that sets the limits name in the 'testmethodlimits' section
43
                  # of the generated .tf file.  Defaults to 'Functional' if not provided.
44
                  test_name:              [:string, 'HashExample'],
45
                  # An optional 3rd parameter can be supplied to provide an array of allowed values. If supplied,
46
                  # Origen will raise an error upon an attempt to set it to an unlisted value.
47
                  tester_state:           [:string, 'CONNECTED', %w(CONNECTED UNCHANGED DISCONNECTED)],
48
                  force_mode:             [:string, 'VOLT', %w(VOLT CURR)],
49
                  # The name of another parameter can be supplied as the type argument, meaning that the type
50
                  # here will be either :current or :voltage depending on the value of :force_mode
51
                  # force_value: [:force_mode, 3800.mV],
52
                  # In cases where the C++ library has deviated from standard attribute naming conventions
53
                  # (camel-cased with lower cased first character), the absolute attribute name can be given
54
                  # as a string.
55
                  # The Origen accessor for these will be the underscored version, with '.' characters
56
                  # converted to underscores e.g. tm.an_unusual_name
57
                  'hashParameter':        [{ param_name0: [:string, 'NO'], param_name1: [:integer, 0] }],
58
                  'hashParameter2':       [{ param_name0: [:string, 'NO'], param_name1: [:integer, 0] }],
59
                  'nestedHashParameter':  [{
60
                    param_name0:          [:string, ''],
61
                    param_list_strings:   [:list_strings, %w(E1 E2)],
62
                    param_list_classes:   [:list_classes, %w(E1 E2)],
63
                    param_func_call:      [:string, 'setupRef(FQN)'],
64
                    'param_group.param0': [:string, ''],
65
                    param_name1:          [{
66
                      param_name_int:       [:integer, 0],
67
                      param_name_double:    [:double,  0],
68
                      param_list_strings:   [:list_strings, %w(E1 E2)],
69
                      param_list_classes:   [:list_classes, %w(E1 E2)],
70
                      'param_group.param1': [:string, '']
71
                    }]
72
                  }],
73
                  'nestedHashParameter2': [{
74
                    param_name0: [:string, ''],
75
                    param_name1: [{
76
                      param_name_int: [:integer, 0]
77
                    }]
78
                  }]
79
                }
80
        add_tml :my_type_check,
308✔
81
                class_name:         'MyTypeCheck',
82

83
                # Here is a test definition.
84
                # The identifier should be lower-cased and underscored, in-keeping with Ruby naming conventions.
85
                # By default the class name will be the camel-cased version of this identifier, so 'myTest' in
86
                # this case.
87
                my_type_check_test: {
88
                  # [OPTIONAL] The C++ test method class name can be overridden from the default like this:
89
                  class_name:        'MyHashExampleClass',
90
                  int:               [:integer, 1],
91
                  double:            [:double,  1.0],
92
                  int_no_default:    [:integer],
93
                  double_no_default: [:double]
94
                }
95
      end
96

97
      def add_charz
4✔
98
        add_charz_routine :routine1 do |routine|
400✔
99
          routine.name = '_cz__rt1'
400✔
100
        end
101
        add_charz_routine :routine2 do |routine|
400✔
102
          routine.name = '_cz__rt2'
400✔
103
        end
104
        add_charz_routine :routine3 do |routine|
400✔
105
          routine.name = '_cz__rt3'
400✔
106
        end
107
        add_charz_routine :routine4 do |routine|
400✔
108
          routine.name = '_cz__rt4'
400✔
109
        end
110
        add_charz_routine :routine5 do |routine|
400✔
111
          routine.name = '_cz__rt5'
400✔
112
        end
113
        add_charz_routine :routine6 do |routine|
400✔
114
          routine.name = '_cz__rt6'
400✔
115
        end
116
        add_charz_profile :cz do |profile|
400✔
117
          profile.routines = [:routine3]
400✔
118
        end
119
        add_charz_profile :cz_only do |profile|
400✔
120
          profile.charz_only = true
400✔
121
          profile.routines = [:routine1]
400✔
122
        end
123
        add_charz_profile :simple_gates do |profile|
400✔
124
          profile.flags = :my_flag
400✔
125
          profile.enables = :my_enable
400✔
126
          profile.routines = [:routine1]
400✔
127
        end
128
        add_charz_profile :complex_gates do |profile|
400✔
129
          profile.flags = { ['$MyFlag1'] => [:routine1, :routine2], ['$MyFlag2'] => [:routine3], '$MyFlag3' => :routine4 }
400✔
130
          profile.enables = { ['$MyEnable1'] => [:routine1], ['$MyEnable2'] => [:routine2, :routine3], '$MyEnable3' => :routine5 }
400✔
131
          profile.routines = [:routine1, :routine2, :routine3, :routine4, :routine5, :routine6]
400✔
132
        end
133

134
        add_charz_profile :simple_anded_flags do |profile|
400✔
135
          profile.and_flags = true
400✔
136
          profile.routines = [:routine1]
400✔
137
        end
138

139
        add_charz_profile :simple_anded_enables do |profile|
400✔
140
          profile.and_enables = true
400✔
141
          profile.routines = [:routine1]
400✔
142
        end
143

144
        add_charz_profile :complex_anded_flags do |profile|
400✔
145
          profile.and_flags = true
400✔
146
          profile.enables = :my_enable
400✔
147
          profile.routines = [:routine1]
400✔
148
        end
149

150
        add_charz_profile :complex_anded_enables do |profile|
400✔
151
          profile.and_enables = true
400✔
152
          profile.flags = :my_flag
400✔
153
          profile.routines = [:routine1]
400✔
154
        end
155
      end
156

157
      # Test that the block form of flow control methods like this can
158
      # be overridden by an interface
159
      def if_job(*jobs)
4✔
160
        jobs = jobs.flatten
168✔
161
        jobs.delete(:prb9)
168✔
162
        super
168✔
163
      end
164
      alias_method :if_jobs, :if_job
4✔
165

166
      def log(msg)
4✔
167
        if tester.j750? || tester.uflex?
2,844✔
168
          flow.logprint(msg)
1,172✔
169
        else
170
          flow.log(msg)
1,672✔
171
        end
172
      end
173

174
      def func(name, options = {})
4✔
175
        options = {
4,254✔
176
          duration: :static
4,254✔
177
        }.merge(options)
178
        number = options[:number]
8,508✔
179

180
        if tester.j750? || tester.uflex?
8,508✔
181
          block_loop(name, options) do |block, i, group|
3,448✔
182
            options[:number] = number + i if number && i
3,544✔
183
            ins = test_instances.functional(name)
3,544✔
184
            ins.set_wait_flags(:a) if options[:duration] == :dynamic
3,544✔
185
            ins.pin_levels = options.delete(:pin_levels) if options[:pin_levels]
3,544✔
186
            if group
3,544✔
187
              pname = "#{name}_b#{i}_pset"
144✔
188
              patsets.add(pname, [{ pattern: "#{name}_b#{i}.PAT" },
144✔
189
                                  { pattern: 'nvm_global_subs.PAT', start_label: 'subr' }])
190
              ins.pattern = pname
144✔
191
              flow.test(group, options) if i == 0
144✔
192
            else
193
              pname = "#{name}_pset"
3,400✔
194
              patsets.add(pname, [{ pattern: "#{name}.PAT" },
3,400✔
195
                                  { pattern: 'nvm_global_subs.PAT', start_label: 'subr' }])
196
              ins.pattern = pname
3,400✔
197
              if options[:cz_setup]
3,400✔
198
                flow.cz(ins, options[:cz_setup], options)
60✔
199
              else
200
                flow.test(ins, options)
3,340✔
201
              end
202
            end
203
          end
204

205
        elsif tester.v93k?
5,060✔
206
          block_loop(name, options) do |block, i|
5,060✔
207
            options[:number] = number + i if number && i
5,284✔
208
            tm = test_methods.ac_tml.ac_test.functional_test
5,284✔
209
            ts = test_suites.run(name, options)
5,284✔
210
            ts.test_method = tm
5,284✔
211
            if tester.smt8?
5,284✔
212
              ts.spec = options.delete(:pin_levels) if options[:pin_levels]
2,360✔
213
              ts.spec ||= 'specs.Nominal'
2,360✔
214
            else
215
              ts.levels = options.delete(:pin_levels) if options[:pin_levels]
2,924✔
216
            end
217
            if block
5,284✔
218
              ts.pattern = "#{name}_b#{i}"
336✔
219
            else
220
              ts.pattern = name.to_s
4,948✔
221
              #    if options[:cz_setup]
222
              #      flow.cz(ins, options[:cz_setup], options)
223
              #    else
224
              #    end
225
            end
226
            flow.test ts, options
5,284✔
227
          end
228
        end
229
      end
230

231
      def func_with_charz(name, options = {})
4✔
232
        options = {
94✔
233
          duration: :static
94✔
234
        }.merge(options)
235

236
        if tester.v93k?
188✔
237
          if tester.smt7?
188✔
238
            tm = test_methods.ac_tml.ac_test.functional_test
188✔
239
            ts = test_suites.run(name, options)
188✔
240
            ts.test_method = tm
188✔
241
            ts.pattern = 'charz_example'
188✔
242

243
            test_level_charz = false
188✔
244
            if options[:charz]
188✔
245
              charz_on(*options[:charz])
8✔
246
              test_level_charz = true
8✔
247
            end
248

249
            unless charz_only? && !options[:charz_test]
188✔
250
              options[:parent_test_name] = name
184✔
251
              set_conditional_charz_id(options)
184✔
252
              flow.test ts, options
184✔
253
            end
254

255
            unless options[:charz_test]
188✔
256
              insert_charz_tests(options.merge(parent_test_name: name, charz_test: true)) do |options|
64✔
257
                charz_name = :"#{name}_#{charz_routines[options[:current_routine]].name}"
124✔
258
                func_with_charz(charz_name, options)
124✔
259
              end
260
            end
261

262
            charz_off if test_level_charz
188✔
263
          else
UNCOV
264
            fail 'Only SMT7 is Implemented for Charz'
×
265
          end
266
        else
UNCOV
267
          fail "Tester #{tester.name} Not Yet Implemented for Charz"
×
268
        end
269
      end
270

271
      def func_with_comment(name, options = {})
4✔
272
        if tester.v93k?
24✔
273
          options = {
12✔
274
            duration: :static
12✔
275
          }.merge(options)
276
          number = options[:number]
24✔
277

278
          block_loop(name, options) do |block, i|
24✔
279
            options[:number] = number + i if number && i
24✔
280
            tm = test_methods.ac_tml.ac_test.functional_test
24✔
281
            ts = test_suites.run(name, options)
24✔
282
            ts.test_method = tm
24✔
283
            ts.levels = options.delete(:pin_levels) if options[:pin_levels]
24✔
284
            ts.comment = options.delete(:comment) || flow.active_description
24✔
285
            if block
24✔
UNCOV
286
              ts.pattern = "#{name}_b#{i}"
×
287
            else
288
              ts.pattern = name.to_s
24✔
289
              #    if options[:cz_setup]
290
              #      flow.cz(ins, options[:cz_setup], options)
291
              #    else
292
              #    end
293
            end
294
            flow.test ts, options
24✔
295
          end
296
        else
UNCOV
297
          func(name, options)
×
298
        end
299
      end
300

301
      def double_int_type_check(name, options = {})
4✔
302
        number = options[:number]
40✔
303
        if tester.v93k?
40✔
304
          block_loop(name, options) do |block, i|
28✔
305
            options[:number] = number + i if number && i
28✔
306
            tm = test_methods.my_type_check.my_type_check_test
28✔
307
            tm.int    = '1'
28✔
308
            tm.double = '1.0'
28✔
309
            tm.int_no_default = ''
28✔
310
            tm.double_no_default = ''
28✔
311
            ts = test_suites.run(name, options)
28✔
312
            ts.test_method = tm
28✔
313
            flow.test ts, options
28✔
314
          end
315
        end
316
      end
317

318
      def my_hash_test(name, options = {})
4✔
319
        number = options[:number]
8✔
320

321
        if tester.v93k? && tester.smt8?
8✔
322
          block_loop(name, options) do |block, i|
8✔
323
            options[:number] = number + i if number && i
8✔
324
            tm = test_methods.my_hash_tml.my_hash_test
8✔
325
            tm.hashParameter = {
8✔
326
              param1: {}
327
            }
328
            tm.nestedHashParameter = {
8✔
329
              my_param_name0: {
330
                param_name0:        'hello',
331
                param_group_param0: 'test_group',
332
                param_name1:        {
333
                  my_param_name1: {
334
                    param_name_int:     '1',
335
                    param_name_double:  '1.0',
336
                    param_group_param1: 'test_nested_group'
337
                  },
338
                  my_param_name2: {
339
                    param_name_int:    2,
340
                    param_name_double: 2.0
341
                  },
342
                  my_param_name3: {
343
                    param_name_int: 3
344
                  }
345
                }
346
              }
347
            }
348
            tm.nestedHashParameter2 = {
8✔
349
              my_param_name4: {
350
                param_name0: 'goodbye'
351
              },
352
              my_param_name5: {
353
                param_name0: 'goodbye forever'
354
              }
355
            }
356
            tm.samples = '2'
8✔
357
            ts = test_suites.run(name, options)
8✔
358
            ts.test_method = tm
8✔
359
            ts.spec = options.delete(:pin_levels) if options[:pin_levels]
8✔
360
            ts.spec ||= 'specs.Nominal'
8✔
361
            flow.test ts, options
8✔
362
          end
363
        end
364
      end
365

366
      def my_override_spec_test(name, options = {})
4✔
367
        number = options[:number]
8✔
368

369
        if tester.v93k? && tester.smt8?
8✔
370
          tm = test_methods.ac_tml.ac_test.functional_test
8✔
371
          ts = test_suites.run(name, options)
8✔
372
          ts.test_method = tm
8✔
373
          ts.spec = options.delete(:pin_levels) if options[:pin_levels]
8✔
374
          ts.spec ||= 'specs.Nominal'
8✔
375
          ts.pattern = 'pat1'
8✔
376
          ts.burst = 'sequence1'
8✔
377
          ts.spec_path = 'myCustomSpecPath'
8✔
378
          ts.seq_path  = 'myCustomSeqPath'
8✔
379
          ts.spec_namespace = 'myCustomSpecNamespace'
8✔
380
          ts.seq_namespace  = 'myCustomSeqNamespace'
8✔
381
          flow.test ts, options
8✔
382
        end
383
      end
384

385
      def block_loop(name, options)
4✔
386
        if options[:by_block]
8,576✔
387
          if tester.j750? || tester.uflex?
160✔
388
            test_instances.group do |group|
48✔
389
              group.name = name
48✔
390
              $dut.blocks.each_with_index do |block, i|
48✔
391
                yield block, i, group
144✔
392
              end
393
            end
394
          elsif tester.v93k?
112✔
395
            flow.group name, options do
112✔
396
              $dut.blocks.each_with_index do |block, i|
112✔
397
                yield block, i
336✔
398
              end
399
            end
400
          end
401
        else
402
          yield
8,416✔
403
        end
404
      end
405

406
      def shmoo(name, targets, options = {})
4✔
407
        if tester.v93k? && tester.smt8?
40✔
408
          targets = [targets] unless targets.is_a?(Array)
40✔
409
          st = shmoo_tests.run(name, { targets: targets }.merge(options))
40✔
410
          flow.test st, options
40✔
411
        end
412
      end
413

414
      def por(options = {})
4✔
415
        options = {
20✔
416
          instance_not_available: true
20✔
417
        }.merge(options)
418
        if tester.j750? || tester.uflex?
40✔
419
          flow.test('por_ins', options)
12✔
420
        else
421
          func('por_ins', options)
28✔
422
        end
423
      end
424

425
      def para(name, options = {})
4✔
426
        options = {
12✔
427
          high_voltage: false
12✔
428
        }.merge(options)
429

430
        if tester.j750?
24✔
431
          if options.delete(:high_voltage)
8✔
432
            ins = test_instances.bpmu(name)
8✔
433
          else
UNCOV
434
            ins = test_instances.ppmu(name)
×
435
          end
436
          ins.dc_category = 'NVM_PARA'
8✔
437
          flow.test(ins, options)
8✔
438
          patsets.add("#{name}_pset", pattern: "#{name}.PAT")
8✔
439
        end
440
      end
441

442
      # OR 2 IDS together into 1 flag
443
      def or_ids(options = {})
4✔
UNCOV
444
        flow.or_flags(options[:id1], options[:id2], options)
×
445
      end
446

447
      def nop(options = {})
4✔
UNCOV
448
        flow.nop options
×
449
      end
450

451
      def mto_memory(name, options = {})
4✔
452
        options = {
20✔
453
          duration: :static
20✔
454
        }.merge(options)
455

456
        if tester.j750?
40✔
457
          block_loop(name, options) do |block, i, group|
8✔
458
            ins = test_instances.mto_memory(name)
8✔
459
            ins.set_wait_flags(:a) if options[:duration] == :dynamic
8✔
460
            ins.pin_levels = options.delete(:pin_levels) if options[:pin_levels]
8✔
461
            if group
8✔
462
              pname = "#{name}_b#{i}_pset"
×
UNCOV
463
              patsets.add(pname, [{ pattern: "#{name}_b#{i}.PAT" },
×
464
                                  { pattern: 'nvm_global_subs.PAT', start_label: 'subr' }])
465
              ins.pattern = pname
×
UNCOV
466
              flow.test(group, options) if i == 0
×
467
            else
468
              pname = "#{name}_pset"
8✔
469
              patsets.add(pname, [{ pattern: "#{name}.PAT" },
8✔
470
                                  { pattern: 'nvm_global_subs.PAT', start_label: 'subr' }])
471
              ins.pattern = pname
8✔
472
              if options[:cz_setup]
8✔
UNCOV
473
                flow.cz(ins, options[:cz_setup], options)
×
474
              else
475
                flow.test(ins, options)
8✔
476
              end
477
            end
478
          end
479
        end
480
      end
481

482
      def meas_multi_limits(name, options = {})
4✔
483
        options = {
8✔
484
          duration: :static
8✔
485
        }.merge(options)
486

487
        name = "measmulti_#{name}" unless name.to_s =~ /measmulti/
16✔
488

489
        if tester.uflex?
16✔
490
          ins = test_instances.functional(name)
16✔
491
          ins.set_wait_flags(:a) if options[:duration] == :dynamic
16✔
492
          ins.pin_levels = options.delete(:pin_levels) if options[:pin_levels]
16✔
493
          ins.defer_limits = options[:defer_limits]
16✔
494

495
          # some made up sub test limits
496
          options[:sub_tests] = [sub_test('limit1', lo: 0, hi: 7), sub_test('limit2', lo: 3, hi: 8)]
16✔
497

498
          pname = "#{name}_pset"
16✔
499
          patsets.add(pname, [{ pattern: "#{name}.PAT" },
16✔
500
                              { pattern: 'nvm_global_subs.PAT', start_label: 'subr' }])
501
          ins.pattern = pname
16✔
502

503
          flow.test(ins, options)
16✔
504
        end
505
      end
506

507
      def meas(name, options = {})
4✔
508
        options = {
332✔
509
          duration: :static
332✔
510
        }.merge(options)
511

512
        name = "meas_#{name}" unless name.to_s =~ /meas/
664✔
513

514
        if tester.j750? || tester.uflex?
664✔
515
          if tester.uflex?
284✔
516
            if options[:pins] == :dcvi
100✔
517
              ins = test_instances.dcvi_powersupply(name)
4✔
518
              ins.set_wait_flags(:a) # set wait flag for tester handshake with patterns
4✔
519
              ins.relay_mode = 1 # tlPowered - keep power on
4✔
520
            else
521
              ins = test_instances.functional(name)
96✔
522
              ins.set_wait_flags(:a) if options[:duration] == :dynamic
96✔
523
              ins.scale = options[:scale]
96✔
524
              ins.units = options[:units]
96✔
525
            end
526
            ins.pin_levels = options.delete(:pin_levels) if options[:pin_levels]
100✔
527
            ins.lo_limit = options[:lo_limit]
100✔
528
            ins.hi_limit = options[:hi_limit]
100✔
529
            ins.defer_limits = options[:defer_limits]
100✔
530
          else
531
            if options[:pins] == :hi_v
184✔
532
              ins = test_instances.board_pmu(name)
8✔
533
            elsif options[:pins] == :power
176✔
534
              ins = test_instances.powersupply(name)
8✔
535
            else
536
              ins = test_instances.pin_pmu(name)
168✔
537
            end
538
            ins.set_wait_flags(:a) if options[:duration] == :dynamic
184✔
539
            ins.pin_levels = options.delete(:pin_levels) if options[:pin_levels]
184✔
540
            ins.lo_limit = options[:lo_limit]
184✔
541
            ins.hi_limit = options[:hi_limit]
184✔
542
          end
543

544
          pname = "#{name}_pset"
284✔
545
          patsets.add(pname, [{ pattern: "#{name}.PAT" },
284✔
546
                              { pattern: 'nvm_global_subs.PAT', start_label: 'subr' }])
547
          ins.pattern = pname
284✔
548
          if options[:cz_setup]
284✔
UNCOV
549
            flow.cz(ins, options[:cz_setup], options)
×
550
          else
551
            flow.test(ins, options)
284✔
552
          end
553

554
        elsif tester.v93k?
380✔
555
          tm = test_methods.dc_tml.dc_test.general_pmu
380✔
556
          ts = test_suites.run(name, options)
380✔
557
          ts.test_method = tm
380✔
558
          if tester.smt8?
380✔
559
            ts.spec = options.delete(:pin_levels) if options[:pin_levels]
112✔
560
            ts.spec ||= 'specs.Nominal'
112✔
561
          else
562
            ts.levels = options.delete(:pin_levels) if options[:pin_levels]
268✔
563
          end
564
          ts.lo_limit = options[:lo_limit] if options[:lo_limit]
380✔
565
          ts.hi_limit = options[:hi_limit] if options[:hi_limit]
380✔
566
          ts.pattern = name.to_s
380✔
567
          # if options[:cz_setup]
568
          #  flow.cz(ins, options[:cz_setup], options)
569
          # else
570
          #  use_limit_params = [:lo_limit, :hi_limit, :scale, :units] # define options to strip for flow.test
571
          #  options_use_limit = options.dup                           # duplicate, as modifying options directly, even an assigned copy modifies original
572
          #  flow.test(ins, options.reject! { |k, _| use_limit_params.include? k })    # set up test skipping use-limit options
573
          #  flow.use_limit(name, options_use_limit) if options_use_limit[:hi_limit] || options_use_limit[:lo_limit]  # Only use use-limit if limits present in flow
574
          # end
575
          flow.test ts, options
380✔
576
        end
577

578
        def group(name, options = {})
664✔
579
          flow.group name, options do |group|
352✔
580
            yield group
352✔
581
          end
582
        end
583

584
        ####################################################
585
        #######  UltraFLEX Pinmap Stuff ####################
586
        ####################################################
587

588
        # Assign relevant pins for pinmap sheet generation
589
        def pinmap(name, options = {})
664✔
590
          pinmap = pinmaps("#{name}")
4✔
591
          Origen.top_level.add_pin_group :JTAG, :tdi, :tdo, :tms
4✔
592
          Origen.top_level.power_pin_groups.keys.each do |grp_key|
4✔
593
            pinmap.add_power_pin(grp_key, type: 'Power', comment: "# #{grp_key}")
8✔
594
          end
595
          Origen.top_level.virtual_pins.keys.each do |util_pin|
4✔
596
            upin = Origen.top_level.virtual_pins(util_pin)
8✔
597
            case upin.type
8✔
598
            when :virtual_pin
599
              pinmap.add_utility_pin(upin.name, type: 'Utility', comment: "# #{util_pin}")
4✔
600
            when :ate_ch
601
              pinmap.add_utility_pin(upin.name, type: 'I/O', comment: "# #{util_pin}")
4✔
602
            end
603
          end
604
          Origen.top_level.pin.keys.each do |pkey|
4✔
605
            pinmap.add_pin(Origen.top_level.pin(pkey).name, type: 'I/O', comment: "# #{pkey}")
16✔
606
          end
607
          Origen.top_level.pin_groups.keys.sort.each do |gkey|
4✔
608
            # Do not include pins that are aliased to themselves
609
            Origen.top_level.pin(gkey).each do |pin|
4✔
610
              pinmap.add_group_pin(gkey, Origen.top_level.pin(pin.id).name, type: 'I/O', comment: "# #{gkey}")
12✔
611
            end
612
          end
613
        end
614

615
        # Assign relevant edges in preparation for edgeset/timeset sheet generation
616
        def edge(category, pin, options = {})
664✔
617
          options = {
618
            d_src:   'PAT',     # source of the channel drive data (e.g. pattern, drive_hi, drive_lo, etc.)
16✔
619
            d_fmt:   'NR',      # drive data format (NR, RL, RH, etc.)
620
            d0_edge: '',        # time at which the input drive is turned on
621
            d1_edge: '',        # time of the initial data drive edge
622
            d2_edge: '',        # time of the return format data drive edge
623
            d3_edge: '',        # time at which the input drive is turned off
624
            c_mode:  'Edge',    # output compare mode
625
            c1_edge: '',        # time of the initial output compare edge
626
            c2_edge: '',        # time of the final output compare edge (window compare)
627
            t_res:   'Machine', # timing resolution (possibly ATE-specific)
628
            clk_per: ''         # clock period equation - for use with MCG
629
          }.merge(options)
630

631
          @edge_collection = edges
16✔
632
          @edge_collection.add(category, pin, options)
16✔
633
        end
634

635
        def edge_collection
664✔
UNCOV
636
          @edge_collection
×
637
        end
638

639
        def edgeset(sheet_name, options = {})
664✔
640
          options = {
641
            edgeset: :es_default,
64✔
642
            period:  'cycle',        # tester cycle period
643
            t_mode:  'Machine'       # edgeset timing mode (possibly ATE-specific)
644
          }.merge(options)
645
          edgeset = options.delete(:edgeset)
64✔
646
          pin = options.delete(:pin)
64✔
647
          edge = options.delete(:edge)
64✔
648

649
          @edgeset = edgesets(sheet_name, options)
64✔
650
          @edgeset.add(edgeset, pin, edge, options)
64✔
651
          collect_ac_specs(@edgeset.es[edgeset].spec_sheet, edge)
64✔
652
        end
653

654
        def timeset(sheet_name, options = {})
664✔
655
          options = {
32✔
656
            timeset:   :default,
32✔
657
            master_ts: :default
658
          }.merge(options)
659
          timeset = options.delete(:timeset)
64✔
660
          pin = options.delete(:pin)
64✔
661
          eset = options.delete(:eset)
64✔
662

663
          @timeset = timesets(sheet_name, options)
64✔
664
          @timeset.add(timeset, pin, eset, options)
64✔
665
        end
666

667
        def ac_specset(sheet_name, expression, options = {})
664✔
668
          options = {
669
            specset: :default,
16✔
670
            nom:     { typ:  nil }
671
          }.merge(options)
672

673
          ss = ac_specsets(sheet_name)
16✔
674
          add_ac_specs(ss, expression, options)
16✔
675
        end
676

677
        # Collects AC Spec object(s) from the given expression and adds them to the given Specset
678
        def collect_ac_specs(ssname, edge, options = {})
664✔
679
          options = {
680
            nom: { typ:  nil }
64✔
681
          }.merge(options)
682

683
          # Create a Specsets object from the UFlex program generator API
684
          ss = ac_specsets(ssname.to_sym)
64✔
685
          add_ac_specs(ss, edge.clk_per, options)
64✔
686
          add_ac_specs(ss, edge.d0_edge, options)
64✔
687
          add_ac_specs(ss, edge.d1_edge, options)
64✔
688
          add_ac_specs(ss, edge.d2_edge, options)
64✔
689
          add_ac_specs(ss, edge.d3_edge, options)
64✔
690
          add_ac_specs(ss, edge.c1_edge, options)
64✔
691
          add_ac_specs(ss, edge.c2_edge, options)
64✔
692
        end
693

694
        # Adds new AC Spec object(s) to the given Specset
695
        def add_ac_specs(ss, expression, options = {})
664✔
696
          options = {
232✔
697
            specset: :default
232✔
698
          }.merge(options)
699

700
          return unless expression.is_a? String
464✔
701
          # collect all variable names within the expression
702
          vars = expression.scan(/[a-zA-Z][\w]+/).map(&:to_sym)
464✔
703
          vars.each do |var|
464✔
704
            next if var =~ /^(d0_edge|d1_edge|d2_edge|d3_edge|c1_edge|c2_edge)$/
296✔
705
            # The substitutions below are used for backward compatibility
706
            next if var =~ /^(d_on|d_data|d_ret|d_off|c_open|c_close)$/
232✔
707
            next if var =~ /^(nS|uS|mS|S)$/i
232✔
708
            next if ss.ac_specsets.key?(options[:specset]) && ss.ac_specsets[options[:specset]].include?(var)
232✔
709

710
            ss.add(var, options)
36✔
711
          end
712
        end
713

714
        # Assign relevant power supply levels in preparation for levelset sheet generation
715
        def pwr_level(category, options = {})
664✔
716
          options = {
2✔
717
            vmain: 1.8,              # Main supply voltage
2✔
718
            valt:  1.8,              # Alternate supply voltage
719
            ifold: 1,                # Supply clamp current
720
            delay: 0                 # Supply power-up delay
721
          }.merge(options)
722

723
          @level_collection = levels
4✔
724
          @level_collection.add_power_level(category, options)
4✔
725
        end
726

727
        # Assign relevant single-ended pin levels in preparation for levelset sheet generation
728
        def pin_level_se(category, options = {})
664✔
729
          options = {
730
            vil:       0,            # Input drive low
8✔
731
            vih:       1.8,            # Input drive high
732
            vol:       1.0,            # Output compare low
733
            voh:       0.8,            # Output compare high
734
            vcl:       -1,            # Voltage clamp low
735
            vch:       2.5,            # Voltage clamp high
736
            vt:        0.9,            # Termination voltage
737
            voutlotyp: 0,            #
738
            vouthityp: 0,            #
739
            dmode:     'Largeswing-VT' # Driver mode
740
          }.merge(options)
741

742
          @level_collection = levels
8✔
743
          @level_collection.add_se_pin_level(category, options)
8✔
744
        end
745

746
        def level_collection
664✔
UNCOV
747
          @level_collection
×
748
        end
749

750
        def levelset(sheet_name, options = {})
664✔
751
          pin = options.delete(:pin)
24✔
752
          level = options.delete(:level)
24✔
753

754
          @levelset = levelsets(sheet_name)
24✔
755
          @levelset.add(sheet_name, pin, level, options)
24✔
756
          collect_dc_specs(@levelset.spec_sheet, level)
24✔
757
        end
758

759
        def dc_specset(sheet_name, expression, options = {})
664✔
760
          options = {
761
            min: { min:  nil },
16✔
762
            nom: { typ:  nil },
763
            max: { max:  nil }
764
          }.merge(options)
765

766
          ss = dc_specsets(sheet_name.to_sym)
16✔
767
          add_dc_specs(ss, expression, options)
16✔
768
        end
769

770
        # Collects DC Spec object(s) from the given expression and adds them to the given Specset
771
        def collect_dc_specs(ssname, level, options = {})
664✔
772
          options = {
773
            nom: { typ:  nil },
24✔
774
            min: { min:  nil },
775
            max: { max:  nil }
776
          }.merge(options)
777

778
          # Create a Specsets object from the UFlex program generator API
779
          ss = dc_specsets(ssname.to_sym)
24✔
780
          if level.respond_to?(:vmain)
24✔
781
            add_dc_specs(ss, level.vmain, options)
8✔
782
            add_dc_specs(ss, level.valt, options)
8✔
783
            add_dc_specs(ss, level.ifold, options)
8✔
784
          elsif level.respond_to?(:vil)
16✔
785
            add_dc_specs(ss, level.vil, options)
16✔
786
            add_dc_specs(ss, level.vih, options)
16✔
787
            add_dc_specs(ss, level.vol, options)
16✔
788
            add_dc_specs(ss, level.voh, options)
16✔
789
            add_dc_specs(ss, level.vcl, options)
16✔
790
            add_dc_specs(ss, level.vch, options)
16✔
791
            add_dc_specs(ss, level.vt, options)
16✔
792
            add_dc_specs(ss, level.voutlotyp, options)
16✔
793
            add_dc_specs(ss, level.vouthityp, options)
16✔
794
          end
795
        end
796

797
        # Adds new DC Spec object(s) to the given Specset
798
        def add_dc_specs(ss, expression, options = {})
664✔
799
          options = {
92✔
800
            specset: :default
92✔
801
          }.merge(options)
802

803
          return unless expression.is_a? String
184✔
804
          vars = expression.scan(/[a-zA-Z][\w]+/).map(&:to_sym)
184✔
805
          vars.each do |var|
184✔
806
            next if var =~ /^(nA|uA|mA|A|nV|uV|mV|V)$/i
152✔
807

808
            ss.add(var, options)
152✔
809
          end
810
        end
811

812
        def global_spec(var, options = {})
664✔
813
          options = {
8✔
814
            job:     nil,
8✔
815
            value:   nil,
816
            comment: nil
817
          }.merge(options)
818

819
          global_specs('SpecsGlobal').add(var, job: options[:job], value: options[:value], comment: options[:comment])
16✔
820
        end
821

822
        def job_def(jname, options = {})
664✔
823
          options = {
824
            pinmap:         pinmap_sheets.map { |k, v| v.filename.gsub(/\.txt$/, '') },
16✔
825
            instances:      test_instance_sheets.map { |k, v| v.filename.gsub(/\.txt$/, '') },
32✔
826
            flows:          flow_sheets.map { |k, v| v.filename.gsub(/\.txt$/, '') },
64✔
827
            ac_specs:       ac_specset_sheets.map { |k, v| v.filename.gsub(/\.txt$/, '') },
8✔
828
            dc_specs:       dc_specset_sheets.map { |k, v| v.filename.gsub(/\.txt$/, '') },
8✔
829
            patsets:        patset_sheets.map { |k, v| v.filename.gsub(/\.txt$/, '') },
32✔
UNCOV
830
            patgroups:      patgroup_sheets.map { |k, v| v.filename.gsub(/\.txt$/, '') },
×
831
            bintables:      [],
832
            cz:             [],
833
            test_procs:     [],
834
            mix_sig_timing: [],
835
            wave_defs:      [],
836
            psets:          [],
837
            sigs_port_map:  [],
838
            fract_bus:      [],
839
            comment:        nil
840
          }.merge(options)
841

842
          program_jobs('Jobs').add(jname, options)
8✔
843
        end
844

845
        def reference(reference, options = {})
664✔
846
          options = {
4✔
847
            comment:        nil
4✔
848
          }.merge(options)
849

850
          references('Refs').add(reference, options)
8✔
851
        end
852
      end
853
    end
854
  end
855
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc