origen_testers 0.31.0 → 0.40.0

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.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/config/application.rb +3 -1
  3. data/config/shared_commands.rb +4 -0
  4. data/config/version.rb +1 -1
  5. data/lib/origen_testers/api.rb +8 -0
  6. data/lib/origen_testers/atp/flow.rb +30 -9
  7. data/lib/origen_testers/flow.rb +36 -2
  8. data/lib/origen_testers/generator.rb +44 -5
  9. data/lib/origen_testers/interface.rb +22 -2
  10. data/lib/origen_testers/origen_ext/generator.rb +4 -3
  11. data/lib/origen_testers/origen_ext/generator/flow.rb +99 -5
  12. data/lib/origen_testers/program_generators.rb +5 -1
  13. data/lib/origen_testers/smartest_based_tester.rb +1 -0
  14. data/lib/origen_testers/smartest_based_tester/base.rb +177 -114
  15. data/lib/origen_testers/smartest_based_tester/base/flow.rb +329 -127
  16. data/lib/origen_testers/smartest_based_tester/base/generator.rb +25 -7
  17. data/lib/origen_testers/smartest_based_tester/base/limits_file.rb +186 -60
  18. data/lib/origen_testers/smartest_based_tester/base/pattern_compiler.rb +4 -0
  19. data/lib/origen_testers/smartest_based_tester/base/pattern_master.rb +4 -0
  20. data/lib/origen_testers/smartest_based_tester/base/processors/extract_bin_names.rb +5 -1
  21. data/lib/origen_testers/smartest_based_tester/base/processors/extract_flow_vars.rb +108 -0
  22. data/lib/origen_testers/smartest_based_tester/base/test_method.rb +8 -3
  23. data/lib/origen_testers/smartest_based_tester/base/test_suite.rb +9 -108
  24. data/lib/origen_testers/smartest_based_tester/base/test_suites.rb +17 -7
  25. data/lib/origen_testers/smartest_based_tester/base/variables_file.rb +29 -7
  26. data/lib/origen_testers/smartest_based_tester/smt7.rb +59 -0
  27. data/lib/origen_testers/smartest_based_tester/smt8.rb +218 -0
  28. data/lib/origen_testers/smartest_based_tester/v93k/flow.rb +32 -0
  29. data/lib/origen_testers/smartest_based_tester/v93k/templates/vars.tf.erb +2 -2
  30. data/lib/origen_testers/smartest_based_tester/v93k/test_suite.rb +109 -0
  31. data/lib/origen_testers/smartest_based_tester/v93k_smt8.rb +8 -0
  32. data/lib/origen_testers/smartest_based_tester/v93k_smt8/flow.rb +234 -0
  33. data/lib/origen_testers/smartest_based_tester/v93k_smt8/generator.rb +48 -0
  34. data/lib/origen_testers/smartest_based_tester/v93k_smt8/limits_file.rb +14 -0
  35. data/lib/origen_testers/smartest_based_tester/v93k_smt8/limits_workbook.rb +148 -0
  36. data/lib/origen_testers/smartest_based_tester/v93k_smt8/templates/limits.csv.erb +3 -0
  37. data/lib/origen_testers/smartest_based_tester/v93k_smt8/templates/template.flow.erb +41 -0
  38. data/lib/origen_testers/smartest_based_tester/v93k_smt8/test_suite.rb +66 -0
  39. data/lib/origen_testers/stil_based_tester/base.rb +4 -0
  40. data/lib/origen_testers/test/interface.rb +16 -2
  41. data/lib/origen_testers/vector_generator.rb +9 -4
  42. data/lib/origen_testers/vector_pipeline.rb +1 -1
  43. data/pattern/nvm/v93k/v93k_workout.rb +7 -0
  44. data/program/_erase_vfy.rb +2 -1
  45. data/program/components/_deep_nested.rb +3 -0
  46. data/program/components/_prb1_main.rb +1 -1
  47. data/program/components/_prb2_main.rb +6 -2
  48. data/program/flow_control.rb +3 -3
  49. data/program/prb1.rb +4 -0
  50. data/program/prb2.rb +7 -3
  51. data/templates/origen_guides/pattern/v93k.md.erb +24 -0
  52. data/templates/origen_guides/program/v93k.md.erb +6 -148
  53. data/templates/origen_guides/program/v93ksmt7.md.erb +165 -0
  54. data/templates/origen_guides/program/v93ksmt8.md.erb +112 -0
  55. metadata +30 -3
  56. data/lib/origen_testers/smartest_based_tester/base/test_methods/smart_calc_tml.rb +0 -23
@@ -45,7 +45,11 @@ module OrigenTesters
45
45
 
46
46
  def _load_generator
47
47
  if tester.v93k?
48
- class << self; include OrigenTesters::V93K::Generator; end
48
+ if tester.smt8?
49
+ class << self; include OrigenTesters::V93K_SMT8::Generator; end
50
+ else
51
+ class << self; include OrigenTesters::V93K::Generator; end
52
+ end
49
53
  elsif tester.j750_hpt?
50
54
  class << self; include OrigenTesters::J750_HPT::Generator; end
51
55
  elsif tester.j750?
@@ -5,4 +5,5 @@ module OrigenTesters
5
5
  end
6
6
  # Convenience/Legacy names without the SmartestBasedTester namespace
7
7
  autoload :V93K, 'origen_testers/smartest_based_tester/v93k'
8
+ autoload :V93K_SMT8, 'origen_testers/smartest_based_tester/v93k_smt8'
8
9
  end
@@ -19,6 +19,9 @@ module OrigenTesters
19
19
  # flow, the default value is :signature
20
20
  attr_reader :unique_test_names
21
21
 
22
+ # Returns the SMT version, defaults to 7
23
+ attr_reader :smt_version
24
+
22
25
  # permit modification of minimum repeat count
23
26
  attr_accessor :min_repeat_loop
24
27
  alias_method :min_repeat_count, :min_repeat_loop
@@ -55,6 +58,18 @@ module OrigenTesters
55
58
  # delayed: false is supplied when defining the test
56
59
  attr_accessor :delayed_binning
57
60
 
61
+ # Sets the package namespace that all generated test collateral should be placed under,
62
+ # defaults to the application's namespace if not defined (SMT8 only)
63
+ attr_writer :package_namespace
64
+
65
+ # When set to true, the bins and softbins sheets from the limits spreadsheet will
66
+ # be written out to a standalone (spreadsheet) file instead (SMT8 only)
67
+ attr_accessor :separate_bins_file
68
+
69
+ # When set to true (the default), patterns will be generated in ZIP format instead of ASCII
70
+ # format (SMT8 only)
71
+ attr_accessor :zip_patterns
72
+
58
73
  def initialize(options = {})
59
74
  options = {
60
75
  # whether to use multiport bursts or not, if so this indicates the name of the port to use
@@ -62,9 +77,31 @@ module OrigenTesters
62
77
  multiport_prefix: false,
63
78
  multiport_postfix: false
64
79
  }.merge(options)
80
+
81
+ @smt_version = options[:smt_version] || 7
82
+
83
+ @separate_bins_file = options[:separate_bins_file] || false
84
+ if options.key?(:zip_patterns)
85
+ @zip_patterns = options.delete(:zip_patterns)
86
+ else
87
+ @zip_patterns = true
88
+ end
89
+
90
+ if smt8?
91
+ require_relative 'smt8'
92
+ extend SMT8
93
+ else
94
+ require_relative 'smt7'
95
+ extend SMT7
96
+ end
97
+
65
98
  @max_repeat_loop = 65_535
66
99
  @min_repeat_loop = 33
67
- @pat_extension = 'avc'
100
+ if smt8?
101
+ @pat_extension = 'pat'
102
+ else
103
+ @pat_extension = 'avc'
104
+ end
68
105
  @compress = true
69
106
  # @support_repeat_previous = true
70
107
  @match_entries = 10
@@ -85,18 +122,37 @@ module OrigenTesters
85
122
  if options.key?(:unique_test_names)
86
123
  @unique_test_names = options[:unique_test_names]
87
124
  else
88
- @unique_test_names = :signature
125
+ if smt8?
126
+ @unique_test_names = nil
127
+ else
128
+ @unique_test_names = :signature
129
+ end
89
130
  end
90
- if options.key?(:create_limits_file)
91
- @create_limits_file = options[:create_limits_file]
131
+ if smt8?
132
+ @create_limits_file = true
92
133
  else
93
- @create_limits_file = false
134
+ if options.key?(:create_limits_file)
135
+ @create_limits_file = options[:create_limits_file]
136
+ else
137
+ @create_limits_file = false
138
+ end
94
139
  end
140
+ @package_namespace = options.delete(:package_namespace)
95
141
  self.limitfile_test_modes = options[:limitfile_test_modes] || options[:limitsfile_test_modes]
96
142
  self.force_pass_on_continue = options[:force_pass_on_continue]
97
143
  self.delayed_binning = options[:delayed_binning]
98
144
  end
99
145
 
146
+ def disable_pattern_diffs
147
+ smt8? && zip_patterns
148
+ end
149
+
150
+ # Returns the package namespace that all generated test collateral should be placed under,
151
+ # defaults to the application's namespace if not defined
152
+ def package_namespace
153
+ @package_namespace || Origen.app.namespace
154
+ end
155
+
100
156
  # Set the test mode(s) that you want to see in the limits files, supply an array of mode names
101
157
  # to set multiple.
102
158
  def limitfile_test_modes=(val)
@@ -401,81 +457,142 @@ module OrigenTesters
401
457
  pin.dont_care
402
458
  options[:pin2].dont_care if options[:pin2]
403
459
 
404
- # Single condition loops are simple
405
460
  if !options[:pin2]
406
- # Use the counted match loop (rather than timed) which is recommended in the V93K docs for new applications
407
- # No pre-match failure handling is required here because the system will cleanly record failure info
408
- # for this kind of match loop
409
461
  cc "for the #{pin.name.upcase} pin to go #{state.to_s.upcase}"
462
+ match_block(timeout_in_cycles, options) do |match_or_conditions, fail_conditions|
463
+ match_or_conditions.add do
464
+ state == :low ? pin.expect_lo : pin.expect_hi
465
+ cycle
466
+ pin.dont_care
467
+ end
468
+ end
469
+ else
470
+ cc "for the #{pin.name.upcase} pin to go #{state.to_s.upcase}"
471
+ cc "or the #{options[:pin2].name.upcase} pin to go #{options[:state2].to_s.upcase}"
472
+ match_block(timeout_in_cycles, options) do |match_or_conditions, fail_conditions|
473
+ match_or_conditions.add do
474
+ state == :low ? pin.expect_lo : pin.expect_hi
475
+ cycle
476
+ pin.dont_care
477
+ end
478
+ match_or_conditions.add do
479
+ options[:state2] == :low ? options[:pin2].expect_lo : options[:pin2].expect_hi
480
+ cycle
481
+ options[:pin2].dont_care
482
+ end
483
+ fail_conditions.add do
484
+ cc 'To get here something has gone wrong, strobe again to force a pattern failure'
485
+ state == :low ? pin.expect_lo : pin.expect_hi
486
+ options[:state2] == :low ? options[:pin2].expect_lo : options[:pin2].expect_hi
487
+ cycle
488
+ pin.dont_care
489
+ options[:pin2].dont_care
490
+ end
491
+ end
492
+ end
493
+ end
494
+
495
+ def match_block(timeout_in_cycles, options = {}, &block)
496
+ unless block_given?
497
+ fail 'ERROR: block not passed to match_block!'
498
+ end
499
+
500
+ # Create BlockArgs objects in order to receive multiple blocks
501
+ match_conditions = Origen::Utility::BlockArgs.new
502
+ fail_conditions = Origen::Utility::BlockArgs.new
503
+
504
+ if block.arity > 0
505
+ yield match_conditions, fail_conditions
506
+ else
507
+ match_conditions.add(&block)
508
+ end
509
+
510
+ # Generate a conventional match loop when there is only one match condition block
511
+ if match_conditions.instance_variable_get(:@block_args).size == 1
410
512
  # Need to ensure at least 8 cycles with no compares before entering
513
+ dut.pins.each do |name, pin|
514
+ pin.save
515
+ pin.dont_care if pin.comparing?
516
+ end
411
517
  8.cycles
412
- number_of_loops = (timeout_in_cycles.to_f / 72).ceil
413
- # This seems to be a limit on the max MACT value, so account for longer times by expanding
518
+ dut.pins.each { |name, pin| pin.restore }
519
+
520
+ # Placeholder, real number of loops required to implement the required timeout will be
521
+ # concatenated onto the end later once the length of the match loop is known
522
+ microcode 'SQPG MACT'
523
+ match_microcode = stage.current_bank.last
524
+
525
+ prematch_cycle_count = cycle_count
526
+ match_conditions.each(&:call)
527
+
528
+ match_loop_cycle_count = cycle_count - prematch_cycle_count
529
+
530
+ # Pad the compare vectors out to a multiple of 8 per the ADV documentation
531
+ until match_loop_cycle_count % 8 == 0
532
+ cycle
533
+ match_loop_cycle_count += 1
534
+ end
535
+
536
+ # Use 8 wait vectors by default to keep the overall number of cycles as a multiple of 8
537
+ mrpt = 8
538
+
539
+ number_of_loops = (timeout_in_cycles.to_f / (match_loop_cycle_count + mrpt)).ceil
540
+
541
+ # There seems to be a limit on the max MACT value, so account for longer times by expanding
414
542
  # the wait loop
415
- if number_of_loops > 262_144
416
- mrpt = ((timeout_in_cycles.to_f / 262_144) - 8).ceil
417
- mrpt = Math.sqrt(mrpt).ceil
418
- mrpt += (8 - (mrpt % 8)) # Keep to a multiple of 8, but round up to be safe
419
- number_of_loops = 262_144
420
- else
421
- mrpt = 8
543
+ while number_of_loops > 262_144
544
+ mrpt = mrpt * 2 # Keep this as a multiple of 8
545
+ number_of_loops = (timeout_in_cycles.to_f / (match_loop_cycle_count + mrpt)).ceil
422
546
  end
423
- microcode "SQPG MACT #{number_of_loops};"
424
- # Strobe the pin for the required state
425
- state == :low ? pin.expect_lo : pin.expect_hi
426
- # Always do 8 vectors here as this allows reconstruction of test results if multiple loops
427
- # are called in a pattern
428
- 8.cycles
429
- pin.dont_care
547
+
548
+ match_microcode.concat(" #{number_of_loops};")
549
+
430
550
  # Now do the wait loop, mrpt should always be a multiple of 8
431
551
  microcode "SQPG MRPT #{mrpt};"
432
- mrpt.times do
433
- cycle(dont_compress: true)
552
+
553
+ # Should be no compares in the wait cycles
554
+ dut.pins.each do |name, pin|
555
+ pin.save
556
+ pin.dont_care if pin.comparing?
434
557
  end
558
+ mrpt.cycles
559
+ dut.pins.each { |name, pin| pin.restore }
560
+
561
+ # This is just used as a marker by the vector translator to indicate the end of the MRPT
562
+ # vectors, it does not end up in the final pattern binary.
563
+ # It is also used in a similar manner by Origen when generating SMT8 patterns.
435
564
  microcode 'SQPG PADDING;'
436
- 8.cycles
437
565
 
566
+ # For multiple match conditions do something more like the J750 approach where branching based on
567
+ # miscompares is used to keep the loop going
438
568
  else
439
-
440
- # For two pins do something more like the J750 approach where branching based on miscompares is used
441
- # to keep the loop going
442
- cc "for the #{pin.name.upcase} pin to go #{state.to_s.upcase}"
443
- cc "or the #{options[:pin2].name.upcase} pin to go #{options[:state2].to_s.upcase}"
444
-
445
569
  if options[:check_for_fails]
446
570
  cc 'Return preserving existing errors if the pattern has already failed before arriving here'
447
571
  cycle(repeat: propagation_delay)
448
572
  microcode 'SQPG RETC 1 1;'
449
573
  end
450
- number_of_loops = (timeout_in_cycles.to_f / ((propagation_delay * 2) + 2)).ceil
451
-
452
- loop_vectors number_of_loops do
453
- # Check pin 1
454
- cc "Check if #{pin.name.upcase} is #{state.to_s.upcase} yet"
455
- state == :low ? pin.expect_lo! : pin.expect_hi!
456
- pin.dont_care
457
- cc 'Wait for failure to propagate'
458
- cycle(repeat: propagation_delay)
459
- cc 'Exit match loop if pin has matched (no error), otherwise clear error and remain in loop'
460
- microcode 'SQPG RETC 0 0;'
461
-
462
- # Check pin 2
463
- cc "Check if #{options[:pin2].name.upcase} is #{options[:state2].to_s.upcase} yet"
464
- options[:state2] == :low ? options[:pin2].expect_lo! : options[:pin2].expect_hi!
465
- options[:pin2].dont_care
466
- cc 'Wait for failure to propagate'
467
- cycle(repeat: propagation_delay)
468
- cc 'Exit match loop if pin has matched (no error), otherwise clear error and remain in loop'
469
- microcode 'SQPG RETC 0 0;'
574
+
575
+ loop_microcode = ''
576
+ loop_cycles = 0
577
+ loop_vectors 2 do
578
+ loop_microcode = stage.current_bank.last
579
+ preloop_cycle_count = cycle_count
580
+ match_conditions.each do |condition|
581
+ condition.call
582
+ cc 'Wait for failure to propagate'
583
+ cycle(repeat: propagation_delay)
584
+ cc 'Exit match loop if pin has matched (no error), otherwise clear error and remain in loop'
585
+ microcode 'SQPG RETC 0 0;'
586
+ end
587
+ loop_cycles = cycle_count - preloop_cycle_count
470
588
  end
471
589
 
590
+ number_of_loops = (timeout_in_cycles.to_f / loop_cycles).ceil
591
+
592
+ loop_microcode.sub!('2', number_of_loops.to_s)
593
+
472
594
  if options[:force_fail_on_timeout]
473
- cc 'To get here something has gone wrong, strobe again to force a pattern failure'
474
- state == :low ? pin.expect_lo : pin.expect_hi
475
- options[:state2] == :low ? options[:pin2].expect_lo : options[:pin2].expect_hi if options[:pin2]
476
- cycle
477
- pin.dont_care
478
- options[:pin2].dont_care if options[:pin2]
595
+ fail_conditions.each(&:call)
479
596
  end
480
597
  end
481
598
  end
@@ -578,60 +695,6 @@ module OrigenTesters
578
695
  @local_subroutines ||= []
579
696
  end
580
697
 
581
- # This is an internal method use by Origen which returns a fully formatted vector
582
- # You can override this if you wish to change the output formatting at vector level
583
- def format_vector(vec)
584
- timeset = vec.timeset ? "#{vec.timeset.name}" : ''
585
- pin_vals = vec.pin_vals ? "#{vec.pin_vals} " : ''
586
- if vec.repeat # > 1
587
- microcode = "R#{vec.repeat}"
588
- else
589
- microcode = vec.microcode ? vec.microcode : ''
590
- end
591
-
592
- if Origen.mode.simulation? || !inline_comments || $_testers_no_inline_comments
593
- comment = ''
594
- else
595
-
596
- header_comments = []
597
- repeat_comment = ''
598
- vec.comments.each_with_index do |comment, i|
599
- if comment =~ /^#/
600
- if comment =~ /^#(R\d+)$/
601
- repeat_comment = Regexp.last_match(1) + ' '
602
- # Throw away the ############# headers and footers
603
- elsif comment !~ /^# ####################/
604
- comment = comment.strip.sub(/^# (## )?/, '')
605
- if comment == ''
606
- # Throw away empty lines at the start/end, but preserve them in the middle
607
- unless header_comments.empty? || i == vec.comments.size - 1
608
- header_comments << comment
609
- end
610
- else
611
- header_comments << comment
612
- end
613
- end
614
- end
615
- end
616
-
617
- if vec.pin_vals && ($_testers_enable_vector_comments || vector_comments)
618
- comment = "#{vec.number}:#{vec.cycle}"
619
- comment += ': ' if !header_comments.empty? || !vec.inline_comment.empty?
620
- else
621
- comment = ''
622
- end
623
- comment += header_comments.join("\cm") unless header_comments.empty?
624
- unless vec.inline_comment.empty?
625
- comment += "\cm" unless header_comments.empty?
626
- comment += "(#{vec.inline_comment})"
627
- end
628
- comment = "#{repeat_comment}#{comment}"
629
- end
630
-
631
- # Max comment length 250 at the end
632
- "#{microcode.ljust(25)}#{timeset.ljust(27)}#{pin_vals}# #{comment[0, 247]};"
633
- end
634
-
635
698
  # All vectors generated with the supplied block will have all pins set
636
699
  # to the repeat previous state. Any pins that are changed state within
637
700
  # the block will still update to the supplied value.
@@ -1,3 +1,4 @@
1
+ require 'origen_testers/smartest_based_tester/base/processors/extract_flow_vars'
1
2
  module OrigenTesters
2
3
  module SmartestBasedTester
3
4
  class Base
@@ -10,20 +11,67 @@ module OrigenTesters
10
11
 
11
12
  attr_accessor :add_flow_enable, :flow_name, :flow_description
12
13
 
14
+ def self.generate_flag_name(flag)
15
+ case flag[0]
16
+ when '$'
17
+ flag[1..-1]
18
+ else
19
+ flag.upcase
20
+ end
21
+ end
22
+
23
+ def smt8?
24
+ tester.smt8?
25
+ end
26
+
13
27
  def var_filename
14
28
  @var_filename || 'global'
15
29
  end
16
30
 
17
31
  def subdirectory
18
- 'testflow/mfh.testflow.group'
32
+ @subdirectory ||= begin
33
+ if smt8?
34
+ parents = []
35
+ f = parent
36
+ while f
37
+ parents.unshift(File.basename(f.filename, '.*').to_s.downcase)
38
+ f = f.parent
39
+ end
40
+ File.join tester.package_namespace, 'flows', *parents
41
+ else
42
+ 'testflow/mfh.testflow.group'
43
+ end
44
+ end
19
45
  end
20
46
 
21
47
  def filename
22
- super.gsub('_flow', '')
48
+ base = super.gsub('_flow', '')
49
+ if smt8?
50
+ flow_name(base) + '.flow'
51
+ else
52
+ base
53
+ end
54
+ end
55
+
56
+ def flow_enable_var_name
57
+ var = filename.sub(/\..*/, '').upcase
58
+ if smt8?
59
+ 'ENABLE'
60
+ else
61
+ generate_flag_name("#{var}_ENABLE")
62
+ end
23
63
  end
24
64
 
25
- def flow_name
26
- @flow_name || filename.sub(/\..*/, '').upcase
65
+ def flow_name(filename = nil)
66
+ @flow_name_ = @flow_name unless smt8?
67
+ @flow_name_ ||= begin
68
+ flow_name = (filename || self.filename).sub(/\..*/, '').upcase
69
+ if smt8?
70
+ flow_name.gsub(' ', '_')
71
+ else
72
+ flow_name
73
+ end
74
+ end
27
75
  end
28
76
 
29
77
  def flow_description
@@ -34,95 +82,174 @@ module OrigenTesters
34
82
  @hardware_bin_descriptions ||= {}
35
83
  end
36
84
 
37
- def flow_control_variables
38
- Origen.interface.variables_file(self).flow_control_variables
39
- end
40
-
41
- def runtime_control_variables
42
- Origen.interface.variables_file(self).runtime_control_variables
85
+ def flow_variables
86
+ @flow_variables ||= begin
87
+ vars = Processors::ExtractFlowVars.new.run(ast)
88
+ if !smt8? || (smt8? && top_level?)
89
+ if add_flow_enable
90
+ if add_flow_enable == :enabled
91
+ vars[:all][:referenced_enables] << [flow_enable_var_name, 1]
92
+ vars[:this_flow][:referenced_enables] << [flow_enable_var_name, 1]
93
+ else
94
+ vars[:all][:referenced_enables] << [flow_enable_var_name, 0]
95
+ vars[:this_flow][:referenced_enables] << [flow_enable_var_name, 0]
96
+ end
97
+ vars[:empty?] = false
98
+ end
99
+ end
100
+ vars
101
+ end
43
102
  end
44
103
 
45
104
  def at_flow_start
46
105
  model # Call to ensure the signature gets populated
47
106
  end
48
107
 
108
+ def on_top_level_set
109
+ if top_level?
110
+ if smt8?
111
+ @limits_file = platform::LimitsFile.new(self, manually_register: true, filename: filename.sub(/\..*/, ''), test_modes: @test_modes)
112
+ else
113
+ @limits_file = platform::LimitsFile.new(self, manually_register: true, filename: "#{name}_limits", test_modes: @test_modes)
114
+ end
115
+ else
116
+ @limits_file = top_level.limits_file
117
+ end
118
+ end
119
+
120
+ def limits_file
121
+ @limits_file
122
+ end
123
+
49
124
  def at_flow_end
50
125
  # Take whatever the test modes are set to at the end of the flow as what we go with
51
126
  @test_modes = tester.limitfile_test_modes
52
127
  end
53
128
 
54
- def flow_header
55
- h = [' {']
56
- if add_flow_enable
57
- var = filename.sub(/\..*/, '').upcase
58
- var = generate_flag_name("#{var}_ENABLE")
59
- if add_flow_enable == :enabled
60
- flow_control_variables << [var, 1]
61
- else
62
- flow_control_variables << [var, 0]
63
- end
64
- h << " if @#{var} == 1 then"
65
- h << ' {'
66
- i = ' '
67
- else
68
- i = ''
129
+ def ast
130
+ @ast = nil unless @finalized
131
+ @ast ||= begin
132
+ unique_id = smt8? ? nil : sig
133
+ atp.ast(unique_id: unique_id, optimization: :smt,
134
+ implement_continue: !tester.force_pass_on_continue,
135
+ optimize_flags_when_continue: !tester.force_pass_on_continue
136
+ )
69
137
  end
70
- if set_runtime_variables.size > 0
71
- h << i + ' {'
72
- set_runtime_variables.each do |var|
73
- h << i + " @#{generate_flag_name(var.to_s)} = -1;"
74
- end
75
- h << i + ' }, open,"Init Flow Control Vars", ""'
138
+ end
139
+
140
+ # Returns an array containing all sub-flow objects, not just the immediate children
141
+ def all_sub_flows
142
+ @all_sub_flows ||= begin
143
+ sub_flows = []
144
+ extract_sub_flows(self, sub_flows)
145
+ sub_flows
76
146
  end
77
- h
78
147
  end
79
148
 
80
- def flow_footer
81
- f = []
82
- if add_flow_enable
83
- f << ' }'
84
- f << ' else'
85
- f << ' {'
86
- f << ' }'
149
+ # @api private
150
+ def extract_sub_flows(flow, sub_flows)
151
+ flow.children.each do |id, sub_flow|
152
+ sub_flows << sub_flow
153
+ extract_sub_flows(sub_flow, sub_flows)
87
154
  end
88
- f << ''
89
- f << " }, open,\"#{flow_name}\",\"#{flow_description}\""
90
- f
155
+ sub_flows
156
+ end
157
+
158
+ # Returns the sub_flow object corresponding to the given sub_flow AST
159
+ def sub_flow_from(sub_flow_ast)
160
+ path = sub_flow_ast.find(:path).value
161
+ sub_flow = all_sub_flows.find { |f| File.join(f.subdirectory, f.filename) == path }
91
162
  end
92
163
 
164
+ # This is called by Origen on each flow after they have all been executed but before they
165
+ # are finally written/rendered
93
166
  def finalize(options = {})
94
- super
95
- @indent = add_flow_enable ? 2 : 1
167
+ if smt8?
168
+ return unless top_level? || options[:called_by_top_level]
169
+ super
170
+ @finalized = true
171
+ # All flows have now been executed and the top-level contains the final AST.
172
+ # The AST contained in each child flow may not be complete since it has not been subject to the
173
+ # full-flow processing, e.g. to set flags in the event of a reference to a test being made from
174
+ # outside of a sub-flow.
175
+ # So here we substitute the AST in all sub-flows with the corresponding sub-flow node from the
176
+ # top-level AST, then we finalize the sub-flows with the final AST in place and then later final
177
+ # writing/rendering will be called as normal.
178
+ if top_level?
179
+ ast.find_all(:sub_flow, recursive: true).each do |sub_flow_ast|
180
+ sub_flow = sub_flow_from(sub_flow_ast)
181
+ unless sub_flow
182
+ fail "Something went wrong, couldn't find the sub-flow object for path #{path}"
183
+ end
184
+ # on_fail and on_pass nodes are removed because they will be rendered by the sub-flow's parent
185
+ sub_flow.instance_variable_set(:@ast, sub_flow_ast.remove(:on_fail, :on_pass).updated(:flow))
186
+ sub_flow.instance_variable_set(:@finalized, true) # To stop the AST being regenerated
187
+ end
188
+ options[:called_by_top_level] = true
189
+ all_sub_flows.each { |f| f.finalize(options) }
190
+ options.delete(:called_by_top_level)
191
+ end
192
+ else
193
+ super
194
+ @finalized = true
195
+ end
196
+ if smt8?
197
+ @indent = (add_flow_enable && top_level?) ? 3 : 2
198
+ else
199
+ @indent = add_flow_enable ? 2 : 1
200
+ end
96
201
  @lines = []
202
+ @lines_buffer = []
97
203
  @open_test_methods = []
204
+ @open_test_names = []
205
+ @post_test_lines = []
98
206
  @stack = { on_fail: [], on_pass: [] }
99
- ast = atp.ast(unique_id: sig, optimization: :smt,
100
- implement_continue: !tester.force_pass_on_continue,
101
- optimize_flags_when_continue: !tester.force_pass_on_continue
102
- )
103
- @set_runtime_variables = ast.set_flags
207
+ @set_runtime_variables = ast.excluding_sub_flows.set_flags
104
208
  process(ast)
209
+ unless smt8?
210
+ unless flow_variables[:empty?]
211
+ Origen.interface.variables_file(self).add_variables(flow_variables)
212
+ end
213
+ end
105
214
  test_suites.finalize
106
215
  test_methods.finalize
107
- render_limits_file(ast) if tester.create_limits_file
216
+ if tester.create_limits_file && top_level?
217
+ render_limits_file
218
+ end
108
219
  end
109
220
 
110
- def render_limits_file(ast)
111
- m = platform::LimitsFile.new(self, ast, manually_register: true, filename: "#{name}_limits", test_modes: @test_modes)
112
- m.write_to_file unless m.empty?
221
+ def render_limits_file
222
+ if limits_file
223
+ limits_file.test_modes = @test_modes
224
+ limits_file.generate(ast)
225
+ limits_file.write_to_file
226
+ end
113
227
  end
114
228
 
115
- def line(str)
116
- @lines << ' ' + (' ' * (@indent - 1)) + str
229
+ def line(str, options = {})
230
+ if options[:already_indented]
231
+ line = str
232
+ else
233
+ if smt8?
234
+ line = (' ' * @indent) + str
235
+ else
236
+ line = ' ' + (' ' * (@indent - 1)) + str
237
+ end
238
+ end
239
+ if @lines_buffer.last
240
+ @lines_buffer.last << line
241
+ else
242
+ @lines << line
243
+ end
117
244
  end
118
245
 
119
- # def on_flow(node)
120
- # line '{'
121
- # @indent += 1
122
- # process_all(node.children)
123
- # @indent -= 1
124
- # line "}, open,\"#{unique_group_name(node.find(:name).value)}\", \"\""
125
- # end
246
+ # Any calls to line made within the given block will be returned in an array, rather than
247
+ # immediately being put into the @lines array
248
+ def capture_lines
249
+ @lines_buffer << []
250
+ yield
251
+ @lines_buffer.pop
252
+ end
126
253
 
127
254
  def on_test(node)
128
255
  test_suite = node.find(:object).to_a[0]
@@ -139,7 +266,11 @@ module OrigenTesters
139
266
 
140
267
  if node.children.any? { |n| t = n.try(:type); t == :on_fail || t == :on_pass } ||
141
268
  !stack[:on_pass].empty? || !stack[:on_fail].empty?
142
- line "run_and_branch(#{name})"
269
+ if smt8?
270
+ line "#{name}.execute();"
271
+ else
272
+ line "run_and_branch(#{name})"
273
+ end
143
274
  process_all(node.to_a.reject { |n| t = n.try(:type); t == :on_fail || t == :on_pass })
144
275
  on_pass = node.find(:on_pass)
145
276
  on_fail = node.find(:on_fail)
@@ -160,17 +291,25 @@ module OrigenTesters
160
291
  @open_test_methods << nil
161
292
  end
162
293
 
163
- line 'then'
164
- line '{'
294
+ if smt8?
295
+ line "if (#{name}.pass) {"
296
+ else
297
+ line 'then'
298
+ line '{'
299
+ end
165
300
  @indent += 1
166
301
  pass_branch do
167
302
  process_all(on_pass) if on_pass
168
303
  stack[:on_pass].each { |n| process_all(n) }
169
304
  end
170
305
  @indent -= 1
171
- line '}'
172
- line 'else'
173
- line '{'
306
+ if smt8?
307
+ line '} else {'
308
+ else
309
+ line '}'
310
+ line 'else'
311
+ line '{'
312
+ end
174
313
  @indent += 1
175
314
  fail_branch do
176
315
  process_all(on_fail) if on_fail
@@ -181,7 +320,11 @@ module OrigenTesters
181
320
 
182
321
  @open_test_methods.pop
183
322
  else
184
- line "run(#{name});"
323
+ if smt8?
324
+ line "#{name}.execute();"
325
+ else
326
+ line "run(#{name});"
327
+ end
185
328
  end
186
329
  end
187
330
 
@@ -195,16 +338,28 @@ module OrigenTesters
195
338
  jobs, *nodes = *node
196
339
  jobs = clean_job(jobs)
197
340
  state = node.type == :if_job
198
- runtime_control_variables << ['JOB', '']
199
- condition = jobs.join(' or ')
200
- line "if #{condition} then"
201
- line '{'
341
+ if smt8?
342
+ if jobs.size == 1
343
+ condition = jobs.first
344
+ else
345
+ condition = jobs.map { |j| "(#{j})" }.join(' || ')
346
+ end
347
+ line "if (#{condition}) {"
348
+ else
349
+ condition = jobs.join(' or ')
350
+ line "if #{condition} then"
351
+ line '{'
352
+ end
202
353
  @indent += 1
203
354
  process_all(node) if state
204
355
  @indent -= 1
205
- line '}'
206
- line 'else'
207
- line '{'
356
+ if smt8?
357
+ line '} else {'
358
+ else
359
+ line '}'
360
+ line 'else'
361
+ line '{'
362
+ end
208
363
  @indent += 1
209
364
  process_all(node) unless state
210
365
  @indent -= 1
@@ -215,13 +370,22 @@ module OrigenTesters
215
370
  def on_condition_flag(node, state)
216
371
  flag, *nodes = *node
217
372
  else_node = node.find(:else)
218
- if flag.is_a?(Array)
219
- condition = flag.map { |f| "@#{generate_flag_name(f)} == 1" }.join(' or ')
373
+ if smt8?
374
+ if flag.is_a?(Array)
375
+ condition = flag.map { |f| "(#{generate_flag_name(f)} == 1)" }.join(' || ')
376
+ else
377
+ condition = "#{generate_flag_name(flag)} == 1"
378
+ end
379
+ line "if (#{condition}) {"
220
380
  else
221
- condition = "@#{generate_flag_name(flag)} == 1"
381
+ if flag.is_a?(Array)
382
+ condition = flag.map { |f| "@#{generate_flag_name(f)} == 1" }.join(' or ')
383
+ else
384
+ condition = "@#{generate_flag_name(flag)} == 1"
385
+ end
386
+ line "if #{condition} then"
387
+ line '{'
222
388
  end
223
- line "if #{condition} then"
224
- line '{'
225
389
  @indent += 1
226
390
  if state
227
391
  process_all(node.children - [else_node])
@@ -229,9 +393,13 @@ module OrigenTesters
229
393
  process(else_node) if else_node
230
394
  end
231
395
  @indent -= 1
232
- line '}'
233
- line 'else'
234
- line '{'
396
+ if smt8?
397
+ line '} else {'
398
+ else
399
+ line '}'
400
+ line 'else'
401
+ line '{'
402
+ end
235
403
  @indent += 1
236
404
  if state
237
405
  process(else_node) if else_node
@@ -245,9 +413,6 @@ module OrigenTesters
245
413
  def on_if_enabled(node)
246
414
  flag, *nodes = *node
247
415
  state = node.type == :if_enabled
248
- [flag].flatten.each do |f|
249
- flow_control_variables << generate_flag_name(f)
250
- end
251
416
  on_condition_flag(node, state)
252
417
  end
253
418
  alias_method :on_unless_enabled, :on_if_enabled
@@ -255,67 +420,90 @@ module OrigenTesters
255
420
  def on_if_flag(node)
256
421
  flag, *nodes = *node
257
422
  state = node.type == :if_flag
258
- [flag].flatten.each do |f|
259
- runtime_control_variables << generate_flag_name(f)
260
- end
261
423
  on_condition_flag(node, state)
262
424
  end
263
425
  alias_method :on_unless_flag, :on_if_flag
264
426
 
265
427
  def on_enable(node)
266
428
  flag = node.value.upcase
267
- flow_control_variables << flag
268
- line "@#{flag} = 1;"
429
+ if smt8?
430
+ line "#{flag} = 1;"
431
+ else
432
+ line "@#{flag} = 1;"
433
+ end
269
434
  end
270
435
 
271
436
  def on_disable(node)
272
437
  flag = node.value.upcase
273
- flow_control_variables << flag
274
- line "@#{flag} = 0;"
438
+ if smt8?
439
+ line "#{flag} = 0;"
440
+ else
441
+ line "@#{flag} = 0;"
442
+ end
275
443
  end
276
444
 
277
445
  def on_set_flag(node)
278
446
  flag = generate_flag_name(node.value)
279
- runtime_control_variables << flag
447
+ # This means if we are currently generating an on_test node and tester.force_pass_on_continue has been set
280
448
  if @open_test_methods.last
281
449
  if pass_branch?
282
- if @open_test_methods.last.respond_to?(:on_pass_flag)
283
- if @open_test_methods.last.on_pass_flag == ''
284
- @open_test_methods.last.on_pass_flag = flag
450
+ if smt8?
451
+ @post_test_lines.last << "#{flag} = #{@open_test_names.last}.setOnPassFlags;"
452
+ else
453
+ if @open_test_methods.last.respond_to?(:on_pass_flag)
454
+ if @open_test_methods.last.on_pass_flag == ''
455
+ @open_test_methods.last.on_pass_flag = flag
456
+ else
457
+ Origen.log.error "The test method cannot set #{flag} on passing, because it already sets: #{@open_test_methods.last.on_pass_flag}"
458
+ Origen.log.error " #{node.source}"
459
+ exit 1
460
+ end
285
461
  else
286
- Origen.log.error "The test method cannot set #{flag} on passing, because it already sets: #{@open_test_methods.last.on_pass_flag}"
462
+ Origen.log.error 'Force pass on continue has been requested, but the test method does not have an :on_pass_flag attribute:'
287
463
  Origen.log.error " #{node.source}"
288
464
  exit 1
289
465
  end
290
- else
291
- Origen.log.error 'Force pass on continue has been requested, but the test method does not have an :on_pass_flag attribute:'
292
- Origen.log.error " #{node.source}"
293
- exit 1
294
466
  end
295
467
  else
296
- if @open_test_methods.last.respond_to?(:on_fail_flag)
297
- if @open_test_methods.last.on_fail_flag == ''
298
- @open_test_methods.last.on_fail_flag = flag
468
+ if smt8?
469
+ @post_test_lines.last << "#{flag} = #{@open_test_names.last}.setOnFailFlags;"
470
+ else
471
+ if @open_test_methods.last.respond_to?(:on_fail_flag)
472
+ if @open_test_methods.last.on_fail_flag == ''
473
+ @open_test_methods.last.on_fail_flag = flag
474
+ else
475
+ Origen.log.error "The test method cannot set #{flag} on failing, because it already sets: #{@open_test_methods.last.on_fail_flag}"
476
+ Origen.log.error " #{node.source}"
477
+ exit 1
478
+ end
299
479
  else
300
- Origen.log.error "The test method cannot set #{flag} on failing, because it already sets: #{@open_test_methods.last.on_fail_flag}"
480
+ Origen.log.error 'Force pass on continue has been requested, but the test method does not have an :on_fail_flag attribute:'
301
481
  Origen.log.error " #{node.source}"
302
482
  exit 1
303
483
  end
304
- else
305
- Origen.log.error 'Force pass on continue has been requested, but the test method does not have an :on_fail_flag attribute:'
306
- Origen.log.error " #{node.source}"
307
- exit 1
308
484
  end
309
485
  end
310
486
  else
311
- line "@#{flag} = 1;"
487
+ if smt8?
488
+ line "#{flag} = 1;"
489
+ else
490
+ line "@#{flag} = 1;"
491
+ end
312
492
  end
313
493
  end
314
494
 
495
+ # Note that for smt8?, this should never be hit anymore since groups are now generated as sub-flows
315
496
  def on_group(node)
316
497
  on_fail = node.children.find { |n| n.try(:type) == :on_fail }
317
498
  on_pass = node.children.find { |n| n.try(:type) == :on_pass }
318
- line '{'
499
+ group_name = unique_group_name(node.find(:name).value)
500
+ if smt8?
501
+ line '// *******************************************************'
502
+ line "// GROUP - #{group_name}"
503
+ line '// *******************************************************'
504
+ else
505
+ line '{'
506
+ end
319
507
  @indent += 1
320
508
  stack[:on_fail] << on_fail if on_fail
321
509
  stack[:on_pass] << on_pass if on_pass
@@ -323,7 +511,13 @@ module OrigenTesters
323
511
  stack[:on_fail].pop if on_fail
324
512
  stack[:on_pass].pop if on_pass
325
513
  @indent -= 1
326
- line "}, open,\"#{unique_group_name(node.find(:name).value)}\", \"\""
514
+ if smt8?
515
+ line '// *******************************************************'
516
+ line "// /GROUP - #{group_name}"
517
+ line '// *******************************************************'
518
+ else
519
+ line "}, open,\"#{group_name}\", \"\""
520
+ end
327
521
  end
328
522
 
329
523
  def on_set_result(node)
@@ -336,19 +530,31 @@ module OrigenTesters
336
530
  hardware_bin_descriptions[bin] ||= desc
337
531
  end
338
532
 
339
- if node.to_a[0] == 'pass'
340
- line "stop_bin \"#{sbin}\", \"\", , good, noreprobe, green, #{bin}, #{overon};"
533
+ if smt8?
534
+ # Currently only rendering pass bins or those not associated with a test (should come from the bin
535
+ # table if its associated with a test)
536
+ if node.to_a[0] == 'pass' || @open_test_methods.empty?
537
+ line "addBin(#{sbin || bin});"
538
+ end
341
539
  else
342
- if tester.create_limits_file
343
- line 'multi_bin;'
540
+ if node.to_a[0] == 'pass'
541
+ line "stop_bin \"#{sbin}\", \"\", , good, noreprobe, green, #{bin}, over_on;"
344
542
  else
345
- line "stop_bin \"#{sbin}\", \"#{sdesc}\", , bad, noreprobe, red, #{bin}, #{overon};"
543
+ if tester.create_limits_file
544
+ line 'multi_bin;'
545
+ else
546
+ line "stop_bin \"#{sbin}\", \"#{sdesc}\", , bad, noreprobe, red, #{bin}, #{overon};"
547
+ end
346
548
  end
347
549
  end
348
550
  end
349
551
 
350
552
  def on_log(node)
351
- line "print_dl(\"#{node.to_a[0]}\");"
553
+ if smt8?
554
+ line "println(\"#{node.to_a[0]}\");"
555
+ else
556
+ line "print_dl(\"#{node.to_a[0]}\");"
557
+ end
352
558
  end
353
559
 
354
560
  def unique_group_name(name)
@@ -363,7 +569,8 @@ module OrigenTesters
363
569
  end
364
570
 
365
571
  def clean_job(job)
366
- [job].flatten.map { |j| "@JOB == \"#{j.to_s.upcase}\"" }
572
+ var = smt8? ? 'JOB' : '@JOB'
573
+ [job].flatten.map { |j| "#{var} == \"#{j.to_s.upcase}\"" }
367
574
  end
368
575
 
369
576
  private
@@ -393,12 +600,7 @@ module OrigenTesters
393
600
  end
394
601
 
395
602
  def generate_flag_name(flag)
396
- case flag[0]
397
- when '$'
398
- flag[1..-1]
399
- else
400
- flag.upcase
401
- end
603
+ self.class.generate_flag_name(flag)
402
604
  end
403
605
  end
404
606
  end