origen_sim 0.12.0 → 0.13.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +5 -5
  2. data/config/application.rb +17 -2
  3. data/config/shared_commands.rb +7 -0
  4. data/config/version.rb +1 -1
  5. data/ext/bridge.c +13 -3
  6. data/lib/origen_sim.rb +10 -0
  7. data/lib/origen_sim/artifacts.rb +101 -0
  8. data/lib/origen_sim/commands/build.rb +41 -9
  9. data/lib/origen_sim/heartbeat.rb +13 -1
  10. data/lib/origen_sim/origen_testers/api.rb +90 -7
  11. data/lib/origen_sim/simulation.rb +49 -4
  12. data/lib/origen_sim/simulator.rb +171 -19
  13. data/lib/origen_sim/stderr_reader.rb +19 -13
  14. data/lib/origen_sim/stdout_reader.rb +22 -16
  15. data/lib/origen_sim/tester.rb +110 -1
  16. data/lib/origen_sim_dev/dut.rb +5 -0
  17. data/pattern/test.rb +143 -0
  18. data/templates/empty.gtkw +19 -1
  19. data/templates/empty.rc +86 -0
  20. data/templates/empty.svcf +79 -9
  21. data/templates/empty.tcl +37 -9
  22. data/templates/origen_guides/simulation/app.md.erb +131 -0
  23. data/templates/origen_guides/simulation/artifacts.md.erb +115 -0
  24. data/templates/origen_guides/simulation/compiling.md.erb +190 -0
  25. data/templates/origen_guides/simulation/debugging.md.erb +135 -0
  26. data/templates/origen_guides/simulation/environment.md.erb +217 -0
  27. data/templates/origen_guides/simulation/flows.md.erb +69 -0
  28. data/templates/origen_guides/simulation/howitworks.md.erb +64 -0
  29. data/templates/origen_guides/simulation/introduction.md.erb +35 -0
  30. data/templates/origen_guides/simulation/log.md.erb +118 -0
  31. data/templates/origen_guides/simulation/patterns.md.erb +193 -0
  32. data/templates/probe.tcl.erb +3 -0
  33. data/templates/rtl_v/origen.v.erb +19 -3
  34. data/templates/web/layouts/_guides.html.erb +15 -0
  35. metadata +18 -5
@@ -91,15 +91,46 @@ module OrigenSim
91
91
  # heartbeats.
92
92
  def start_heartbeat
93
93
  @heartbeat = @server_heartbeat.accept
94
- @heartbeat_thread = Heartbeat.new(@heartbeat)
94
+ if Heartbeat::THREADSAFE
95
+ @heartbeat_thread = Heartbeat.new(@heartbeat)
96
+ else
97
+ @heartbeat_pid = fork do
98
+ loop do
99
+ begin
100
+ @heartbeat.write("OK\n")
101
+ rescue Errno::EPIPE => e
102
+ if monitor_running?
103
+ Origen.log.error 'Communication with the simulation monitor has been lost (though it seems to still be running)!'
104
+ else
105
+ Origen.log.error 'The simulation monitor has stopped unexpectedly!'
106
+ end
107
+ sleep 2 # To make sure that any log output from the simulator is captured before we pull the plug
108
+ exit 1
109
+ end
110
+ sleep 5
111
+ end
112
+ end
113
+ end
95
114
  end
96
115
 
97
116
  def stop_heartbeat
98
- @heartbeat_thread.stop
117
+ if Heartbeat::THREADSAFE
118
+ @heartbeat_thread.stop
119
+ else
120
+ Process.kill('SIGHUP', @heartbeat_pid)
121
+
122
+ # Ensure that the process has stopped before closing the IO pipes
123
+ begin
124
+ Process.waitpid(@heartbeat_pid)
125
+ rescue Errno::ECHILD
126
+ # Heartbeat process has already stopped, so ignore this.
127
+ end
128
+ end
99
129
  end
100
130
 
101
131
  # Open the communication channels with the simulator
102
- def open(timeout)
132
+ def open(monitor_pid, timeout)
133
+ @monitor_pid = monitor_pid
103
134
  timeout_connection(timeout) do
104
135
  start_heartbeat
105
136
  @stdout = @server_stdout.accept
@@ -169,10 +200,12 @@ module OrigenSim
169
200
  @socket.close
170
201
  @stderr.close
171
202
  @stdout.close
203
+ @status.close
172
204
  File.unlink(socket_id(:heartbeat)) if File.exist?(socket_id(:heartbeat))
173
205
  File.unlink(socket_id) if File.exist?(socket_id)
174
206
  File.unlink(socket_id(:stderr)) if File.exist?(socket_id(:stderr))
175
207
  File.unlink(socket_id(:stdout)) if File.exist?(socket_id(:stdout))
208
+ File.unlink(socket_id(:status)) if File.exist?(socket_id(:status))
176
209
  end
177
210
 
178
211
  # Returns true if the simulation is running
@@ -186,8 +219,20 @@ module OrigenSim
186
219
  end
187
220
  end
188
221
 
222
+ # Returns true if the simulation monitor process (the one that receives the heartbeat
223
+ # and kills the simulation if it stops) is running
224
+ def monitor_running?
225
+ return false unless @monitor_pid
226
+ begin
227
+ Process.getpgid(@monitor_pid)
228
+ true
229
+ rescue Errno::ESRCH
230
+ false
231
+ end
232
+ end
233
+
189
234
  def socket_id(type = nil)
190
- @socket_ids[type] ||= "/tmp/#{socket_number}#{type}.sock"
235
+ @socket_ids[type] ||= "#{OrigenSim.socket_dir || '/tmp'}/#{socket_number}#{type}.sock"
191
236
  end
192
237
 
193
238
  private
@@ -1,10 +1,13 @@
1
1
  require 'origen_sim/simulation'
2
+ require 'origen_sim/artifacts'
3
+
2
4
  module OrigenSim
3
5
  # Responsible for managing and communicating with the simulator
4
6
  # process, a single instance of this class is instantiated as
5
7
  # OrigenSim.simulator
6
8
  class Simulator
7
9
  include Origen::PersistentCallbacks
10
+ include Artifacts
8
11
 
9
12
  VENDORS = [:icarus, :cadence, :synopsys, :generic]
10
13
 
@@ -116,6 +119,78 @@ module OrigenSim
116
119
  end
117
120
  @configuration = options
118
121
  @tmp_dir = nil
122
+
123
+ # Temporary workaround for bug in componentable, which is making the container a class object, instead of an
124
+ # instance object.
125
+ clear_artifacts
126
+
127
+ # Add any artifacts in the given artifact path
128
+ if Dir.exist?(default_artifact_dir)
129
+ default_artifact_dir.children.each { |a| artifact(a.basename.to_s, target: a) }
130
+ end
131
+
132
+ # Add any artifacts from the target-specific path (simulation/<target>/artifacts). Files of the same name
133
+ # will override artifacts residing in the default directory.
134
+ if Dir.exist?(target_artifact_dir)
135
+ target_artifact_dir.children.each do |a|
136
+ remove_artifact(a.basename.to_s) if has_artifact?(a.basename.to_s)
137
+ add_artifact(a.basename.to_s, target: a)
138
+ end
139
+ end
140
+
141
+ # If a user artifact path was given, add those artifacts as well, overriding any of the default and target artifacts
142
+ if user_artifact_dirs?
143
+ user_artifact_dirs.each do |d|
144
+ if Dir.exist?(d)
145
+ # Add any artifacts from any user-given paths. Files of the same name will override artifacts residing in the default directory.
146
+ d.children.each do |a|
147
+ remove_artifact(a.basename.to_s) if has_artifact?(a.basename.to_s)
148
+ add_artifact(a.basename.to_s, target: a)
149
+ end
150
+ else
151
+ Origen.app.fail! message: "Simulator configuration specified a user artifact dir at #{d} but this directory could not be found!"
152
+ end
153
+ end
154
+ end
155
+
156
+ self
157
+ end
158
+
159
+ def default_artifact_dir
160
+ # Removed this from a constant at the top of the file since it gave boot errors when the file was
161
+ # being required while Origen.app was still loading
162
+ Pathname("#{Origen.app.root}/simulation/application/artifacts")
163
+ end
164
+
165
+ def user_artifact_dirs?
166
+ @configuration.key?(:user_artifact_dirs)
167
+ end
168
+
169
+ def user_artifact_dirs
170
+ @configuration.key?(:user_artifact_dirs) ? @configuration[:user_artifact_dirs].map { |d| Pathname(d) } : nil
171
+ end
172
+
173
+ def target_artifact_dir
174
+ Pathname(@configuration[:target_artifact_dir] || "#{Origen.app.root}/simulation/#{Origen.target.name}/artifacts")
175
+ end
176
+
177
+ def artifact_run_dir
178
+ p = Pathname(@configuration[:artifact_run_dir] || './application/artifacts')
179
+ if p.absolute?
180
+ p
181
+ else
182
+ Pathname(run_dir).join(p)
183
+ end
184
+ end
185
+
186
+ def artifact_populate_method
187
+ @configuration[:artifact_populate_method] || begin
188
+ if Origen.running_on_windows?
189
+ :copy
190
+ else
191
+ :symlink
192
+ end
193
+ end
119
194
  end
120
195
 
121
196
  # The ID assigned to the current simulation target, falls back to to the
@@ -160,7 +235,7 @@ module OrigenSim
160
235
  end
161
236
 
162
237
  def wave_config_file
163
- @wave_config_file ||= begin
238
+ @wave_config_file ||= configuration[:wave_config_file] || begin
164
239
  f = "#{wave_config_dir}/#{User.current.id}.#{wave_config_ext}"
165
240
  unless File.exist?(f)
166
241
  # Take a default wave if one has been set up
@@ -190,7 +265,11 @@ module OrigenSim
190
265
  when :cadence
191
266
  'svcf'
192
267
  when :synopsys
193
- 'tcl'
268
+ if configuration[:verdi]
269
+ 'rc'
270
+ else
271
+ 'tcl'
272
+ end
194
273
  end
195
274
  end
196
275
 
@@ -231,7 +310,11 @@ module OrigenSim
231
310
  cmd += " -nclibdirpath #{compiled_dir}"
232
311
 
233
312
  when :synopsys
234
- cmd = "#{compiled_dir}/simv +socket+#{socket_id} -vpd_file #{wave_file_basename}.vpd"
313
+ if configuration[:verdi]
314
+ cmd = "#{compiled_dir}/simv +socket+#{socket_id} +FSDB_ON +fsdbfile+#{Origen.root}/waves/#{Origen.target.name}/#{wave_file_basename}.fsdb +memcbk +vcsd"
315
+ else
316
+ cmd = "#{compiled_dir}/simv +socket+#{socket_id} -vpd_file #{wave_file_basename}.vpd"
317
+ end
235
318
 
236
319
  when :generic
237
320
  # Generic tester requires that a generic_run_command option/block be provided.
@@ -269,6 +352,10 @@ module OrigenSim
269
352
  cmd = post_process_run_cmd.call(cmd, self) if post_process_run_cmd
270
353
  fail "OrigenSim: :post_process_run_cmd returned object of class #{cmd.class}. Must return a String." unless cmd.is_a?(String)
271
354
 
355
+ # Print the command if debug is enabled
356
+ Origen.log.debug 'OrigenSim Run Command:'
357
+ Origen.log.debug cmd
358
+
272
359
  cmd
273
360
  end
274
361
 
@@ -279,7 +366,11 @@ module OrigenSim
279
366
  if Origen.app.current_job
280
367
  @last_wafe_file_basename = Pathname.new(Origen.app.current_job.output_file).basename('.*').to_s
281
368
  else
282
- @last_wafe_file_basename
369
+ if Origen.interactive?
370
+ 'interactive'
371
+ else
372
+ @last_wafe_file_basename
373
+ end
283
374
  end
284
375
  end
285
376
  end
@@ -308,12 +399,26 @@ module OrigenSim
308
399
  when :synopsys
309
400
  edir = Pathname.new(wave_config_dir).relative_path_from(Pathname.pwd)
310
401
  cmd = "cd #{edir} && "
311
- cmd += configuration[:dve] || 'dve'
312
- dir = Pathname.new(wave_dir).relative_path_from(edir.expand_path)
313
- cmd += " -vpd #{dir}/#{wave_file_basename}.vpd"
314
- f = Pathname.new(wave_config_file).relative_path_from(edir.expand_path)
315
- cmd += " -session #{f}"
316
- cmd += ' &'
402
+ if configuration[:verdi]
403
+ unless ENV['VCS_HOME'] && ENV['LD_LIBRARY_PATH']
404
+ puts 'Please make sure the VCS_HOME and LD_LIBRARY PATH are setup correctly before using Verdi'
405
+ end
406
+ edir = Pathname.new(wave_config_dir).relative_path_from(Pathname.pwd)
407
+ cmd = "cd #{edir} && "
408
+ cmd += configuration[:verdi] || 'verdi'
409
+ dir = Pathname.new(wave_dir).relative_path_from(edir.expand_path)
410
+ cmd += " -ssz -dbdir #{Origen.root}/simulation/#{Origen.target.name}/synopsys/simv.daidir/ -ssf #{dir}/#{wave_file_basename}.fsdb"
411
+ f = Pathname.new(wave_config_file).relative_path_from(edir.expand_path)
412
+ cmd += " -sswr #{f}"
413
+ cmd += ' &'
414
+ else
415
+ cmd += configuration[:dve] || 'dve'
416
+ dir = Pathname.new(wave_dir).relative_path_from(edir.expand_path)
417
+ cmd += " -vpd #{dir}/#{wave_file_basename}.vpd"
418
+ f = Pathname.new(wave_config_file).relative_path_from(edir.expand_path)
419
+ cmd += " -session #{f}"
420
+ cmd += ' &'
421
+ end
317
422
 
318
423
  when :generic
319
424
  # Since this could be anything, the simulator will need to set this up. But, once it is, we can print it here.
@@ -359,6 +464,9 @@ module OrigenSim
359
464
 
360
465
  fetch_simulation_objects
361
466
 
467
+ artifact.clean
468
+ artifact.populate
469
+
362
470
  cmd = run_cmd + ' & echo \$!'
363
471
 
364
472
  launch_simulator = %(
@@ -448,10 +556,10 @@ module OrigenSim
448
556
 
449
557
  Origen.log.debug 'Starting the simulation monitor...'
450
558
 
451
- simulator_parent_process = spawn("ruby -e \"#{launch_simulator}\"")
452
- Process.detach(simulator_parent_process)
559
+ monitor_pid = spawn("ruby -e \"#{launch_simulator}\"")
560
+ Process.detach(monitor_pid)
453
561
 
454
- simulation.open(config[:startup_timeout] || 60) # This will block until the simulation process has started
562
+ simulation.open(monitor_pid, config[:startup_timeout] || 60) # This will block until the simulation process has started
455
563
 
456
564
  # The VPI extension will send 'READY!' when it starts, make sure we get it before proceeding
457
565
  data = get
@@ -460,6 +568,8 @@ module OrigenSim
460
568
  simulation.log_results
461
569
  exit # Assume it is not worth trying another pattern in this case, some kind of environment/config issue
462
570
  end
571
+ Origen.log.info "OrigenSim version #{Origen.app!.version}"
572
+ Origen.log.info "OrigenSim DUT version #{dut_version}"
463
573
  # Tick the simulation on, this seems to be required since any VPI puts operations before
464
574
  # the simulation has started are not applied.
465
575
  # Note that this is not setting a tester timeset, so the application will still have to
@@ -472,6 +582,14 @@ module OrigenSim
472
582
  # Send the given message string to the simulator
473
583
  def put(msg)
474
584
  simulation.socket.write(msg + "\n")
585
+ rescue Errno::EPIPE => e
586
+ if simulation.running?
587
+ Origen.log.error 'Communication with the simulator has been lost (though it seems to still be running)!'
588
+ else
589
+ Origen.log.error 'The simulator has stopped unexpectedly!'
590
+ end
591
+ sleep 2 # To make sure that any log output from the simulator is captured before we pull the plug
592
+ exit 1
475
593
  end
476
594
 
477
595
  # Get a message from the simulator, will block until one
@@ -536,11 +654,16 @@ module OrigenSim
536
654
  end
537
655
  end
538
656
 
539
- def write_comment(comment)
657
+ def write_comment(line, comment)
658
+ return if line >= OrigenSim::NUMBER_OF_COMMENT_LINES
540
659
  # Not sure what the limiting factor here is, the comment memory in the test bench should
541
660
  # be able to handle 1024 / 8 length strings, but any bigger than this hangs the simulation
542
- comment = comment[0..96]
543
- put("c^#{comment}")
661
+ comment = comment ? comment[0..96] : ''
662
+ if dut_version > '0.12.1'
663
+ put("c^#{line}^#{comment} ") # Space at the end is important so that an empty comment is communicated properly
664
+ else
665
+ put("c^#{comment} ")
666
+ end
544
667
  end
545
668
 
546
669
  # Applies the current state of all pins to the simulation
@@ -624,6 +747,17 @@ module OrigenSim
624
747
  end
625
748
  end
626
749
 
750
+ # Flush any buffered simulation output, this should cause live wave viewers to
751
+ # reflect the latest state
752
+ def flush
753
+ if dut_version > '0.12.0'
754
+ put('j^')
755
+ sync_up
756
+ else
757
+ OrigenSim.error "Use of flush requires a DUT model compiled with OrigenSim version > 0.12.0, the current dut was compiled with #{dut_version}"
758
+ end
759
+ end
760
+
627
761
  def error(message)
628
762
  simulation.logged_errors = true
629
763
  Origen.log.error message
@@ -816,11 +950,29 @@ module OrigenSim
816
950
  # Returns the version of Origen Sim that the current DUT object was compiled with
817
951
  def dut_version
818
952
  @dut_version ||= begin
819
- put('i^')
820
- Origen::VersionString.new(get.strip)
953
+ # Allow configs to force a dut version, this is to allow backwards compatibility with very early
954
+ # compiled duts which do not support the command to get it from the compiled object
955
+ config[:dut_version] || begin
956
+ put('i^')
957
+ Origen::VersionString.new(get.strip)
958
+ end
821
959
  end
822
960
  end
823
961
 
962
+ # Any vectors executed within the given block will increment the match_errors counter
963
+ # rather than the errors counter.
964
+ # The match_errors counter will be returned to 0 at the end.
965
+ def match_loop
966
+ poke("#{testbench_top}.pins.match_loop", 1)
967
+ yield
968
+ poke("#{testbench_top}.pins.match_loop", 0)
969
+ poke("#{testbench_top}.pins.match_errors", 0)
970
+ end
971
+
972
+ def match_errors
973
+ peek("#{testbench_top}.pins.match_errors").to_i
974
+ end
975
+
824
976
  private
825
977
 
826
978
  # Pre 0.8.0 the simulator represented the time in ns instead of ps
@@ -830,7 +982,7 @@ module OrigenSim
830
982
 
831
983
  def clean(net)
832
984
  if net =~ /^dut\./
833
- "origen.#{net}"
985
+ "#{testbench_top}.#{net}"
834
986
  else
835
987
  net
836
988
  end
@@ -1,5 +1,4 @@
1
1
  require 'thread'
2
- require 'io/wait'
3
2
  module OrigenSim
4
3
  class StderrReader < Thread
5
4
  attr_reader :socket, :logged_errors
@@ -9,20 +8,27 @@ module OrigenSim
9
8
  @continue = true
10
9
  @logged_errors = false
11
10
  super do
12
- while @continue
13
- while @socket.ready?
14
- line = @socket.gets.chomp
15
- if OrigenSim.fail_on_stderr && !line.empty? &&
16
- !OrigenSim.stderr_string_exceptions.any? { |s| line =~ /#{s}/ }
17
- # We're failing on stderr, so print its results and log as errors if its not an exception.
18
- @logged_errors = true
19
- Origen.log.error "(STDERR): #{line}"
20
- elsif OrigenSim.verbose?
21
- Origen.log.info line
22
- else
23
- Origen.log.debug line
11
+ begin
12
+ while @continue
13
+ line = @socket.gets
14
+ if line
15
+ line = line.chomp
16
+ if OrigenSim.fail_on_stderr && !line.empty? &&
17
+ !OrigenSim.stderr_string_exceptions.any? { |s| s.is_a?(Regexp) ? s.match?(line) : line =~ /#{s}/i }
18
+ # We're failing on stderr, so print its results and log as errors if its not an exception.
19
+ @logged_errors = true
20
+ Origen.log.error "(STDERR): #{line}"
21
+ elsif OrigenSim.verbose?
22
+ Origen.log.info line
23
+ else
24
+ Origen.log.debug line
25
+ end
24
26
  end
25
27
  end
28
+ rescue IOError => e
29
+ unless e.message =~ /stream closed/
30
+ raise e
31
+ end
26
32
  end
27
33
  end
28
34
  end
@@ -1,5 +1,4 @@
1
1
  require 'thread'
2
- require 'io/wait'
3
2
  module OrigenSim
4
3
  class StdoutReader < Thread
5
4
  attr_reader :socket, :logged_errors
@@ -9,25 +8,32 @@ module OrigenSim
9
8
  @continue = true
10
9
  @logged_errors = false
11
10
  super do
12
- while @continue
13
- while @socket.ready?
14
- line = @socket.gets.chomp
15
- if OrigenSim.error_strings.any? { |s| line =~ /#{s}/ } &&
16
- !OrigenSim.error_string_exceptions.any? { |s| line =~ /#{s}/ }
17
- @logged_errors = true
18
- Origen.log.error "(STDOUT): #{line}"
19
- elsif OrigenSim.warning_strings.any? { |s| line =~ /#{s}/ } &&
20
- !OrigenSim.warning_string_exceptions.any? { |s| line =~ /#{s}/ }
21
- Origen.log.warn line
22
- else
23
- if OrigenSim.verbose? ||
24
- OrigenSim.log_strings.any? { |s| line =~ /#{s}/ }
25
- Origen.log.info line
11
+ begin
12
+ while @continue
13
+ line = @socket.gets
14
+ if line
15
+ line = line.chomp
16
+ if OrigenSim.error_strings.any? { |s| s.is_a?(Regexp) ? s.match?(line) : line =~ /#{s}/i } &&
17
+ !OrigenSim.error_string_exceptions.any? { |s| s.is_a?(Regexp) ? s.match?(line) : line =~ /#{s}/i }
18
+ @logged_errors = true
19
+ Origen.log.error "(STDOUT): #{line}"
20
+ elsif OrigenSim.warning_strings.any? { |s| s.is_a?(Regexp) ? s.match?(line) : line =~ /#{s}/i } &&
21
+ !OrigenSim.warning_string_exceptions.any? { |s| s.is_a?(Regexp) ? s.match?(line) : line =~ /#{s}/i }
22
+ Origen.log.warn line
26
23
  else
27
- Origen.log.debug line
24
+ if OrigenSim.verbose? ||
25
+ OrigenSim.log_strings.any? { |s| s.is_a?(Regexp) ? s.match?(line) : line =~ /#{s}/i }
26
+ Origen.log.info line
27
+ else
28
+ Origen.log.debug line
29
+ end
28
30
  end
29
31
  end
30
32
  end
33
+ rescue IOError => e
34
+ unless e.message =~ /stream closed/
35
+ raise e
36
+ end
31
37
  end
32
38
  end
33
39
  end