origen_testers 0.31.0 → 0.40.0

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