origen_sim 0.5.5 → 0.6.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.
@@ -0,0 +1,36 @@
1
+ require 'optparse'
2
+ require 'origen/commands/helpers'
3
+
4
+ options = {}
5
+
6
+ # App options are options that the application can supply to extend this command
7
+ app_options = @application_options || []
8
+ opt_parser = OptionParser.new do |opts|
9
+ opts.banner = <<-EOT
10
+ Checkin the simulation object for the current or given environment/target.
11
+
12
+ Note that this will commit whatever is in your workspace as the latest version, regardless
13
+ of whether there exists any more recent version in the repository.
14
+
15
+ Usage: origen sim:ci [options]
16
+ EOT
17
+ opts.on('-e', '--environment NAME', String, 'Override the default environment, NAME can be a full path or a fragment of an environment file name') { |e| options[:environment] = e }
18
+ opts.on('-t', '--target NAME', String, 'Override the default target, NAME can be a full path or a fragment of a target file name') { |t| options[:target] = t }
19
+ opts.on('-d', '--debugger', 'Enable the debugger') { options[:debugger] = true }
20
+ # Apply any application option extensions to the OptionParser
21
+ opts.separator ''
22
+ opts.on('-h', '--help', 'Show this message') { puts opts; exit }
23
+ end
24
+
25
+ opt_parser.parse! ARGV
26
+
27
+ Origen.environment.temporary = options[:environment] if options[:environment]
28
+ Origen.target.temporary = options[:target] if options[:target]
29
+ Origen.load_target
30
+
31
+ unless tester.sim?
32
+ Origen.log.error 'To run the sim:ci command your target/environment must instantiate an OrigenSim::Tester'
33
+ exit 1
34
+ end
35
+
36
+ tester.simulator.commit_simulation_objects(options)
@@ -0,0 +1,36 @@
1
+ require 'optparse'
2
+ require 'origen/commands/helpers'
3
+
4
+ options = {}
5
+
6
+ # App options are options that the application can supply to extend this command
7
+ app_options = @application_options || []
8
+ opt_parser = OptionParser.new do |opts|
9
+ opts.banner = <<-EOT
10
+ Checkout the simulation object for the current or given environment/target.
11
+
12
+ Note that this will force a new checkout and will overwrite whatever is in your workspace.
13
+
14
+ Usage: origen sim:co [options]
15
+ EOT
16
+ opts.on('-e', '--environment NAME', String, 'Override the default environment, NAME can be a full path or a fragment of an environment file name') { |e| options[:environment] = e }
17
+ opts.on('-t', '--target NAME', String, 'Override the default target, NAME can be a full path or a fragment of a target file name') { |t| options[:target] = t }
18
+ opts.on('-d', '--debugger', 'Enable the debugger') { options[:debugger] = true }
19
+ # Apply any application option extensions to the OptionParser
20
+ opts.separator ''
21
+ opts.on('-h', '--help', 'Show this message') { puts opts; exit }
22
+ end
23
+
24
+ opt_parser.parse! ARGV
25
+
26
+ Origen.environment.temporary = options[:environment] if options[:environment]
27
+ Origen.target.temporary = options[:target] if options[:target]
28
+ Origen.load_target
29
+
30
+ unless tester.sim?
31
+ Origen.log.error 'To run the sim:co command your target/environment must instantiate an OrigenSim::Tester'
32
+ exit 1
33
+ end
34
+
35
+ options[:force] = true
36
+ tester.simulator.fetch_simulation_objects(options)
@@ -5,24 +5,12 @@ module Origen
5
5
  class Pin
6
6
  # The index number that is used to refer to the pin within the simulation
7
7
  attr_accessor :simulation_index
8
- # When generating a testbench the top-level signal will be tied off to the given
9
- # logic level if this is set to 0 or 1
10
- attr_accessor :tie_off
11
8
 
12
9
  alias_method :_orig_initialize, :initialize
13
10
  def initialize(id, owner, options = {})
14
- @tie_off = options[:tie_off]
15
11
  _orig_initialize(id, owner, options)
16
12
  end
17
13
 
18
- def rtl_name
19
- if primary_group
20
- (@rtl_name || primary_group.id).to_s
21
- else
22
- (@rtl_name || id).to_s
23
- end
24
- end
25
-
26
14
  alias_method :_orig_set_value, :set_value
27
15
  def set_value(val)
28
16
  ret = _orig_set_value(val)
@@ -63,10 +51,16 @@ module Origen
63
51
  @simulator_value = nil
64
52
  end
65
53
 
54
+ def apply_force
55
+ if force
56
+ simulator.put("2^#{simulation_index}^#{force}")
57
+ end
58
+ end
59
+
66
60
  # Applies the current pin state to the simulation, this is triggered everytime
67
61
  # the pin state or value changes
68
62
  def update_simulation
69
- return if tie_off || !simulation_index || !tester.timeset || !simulator_needs_update?
63
+ return if force || !simulation_index || !tester.timeset || !simulator_needs_update?
70
64
  case state
71
65
  when :drive
72
66
  @simulator_state = :drive
@@ -7,5 +7,71 @@ module OrigenTesters
7
7
  is_a?(OrigenSim::Tester)
8
8
  end
9
9
  alias_method :simulator?, :sim?
10
+
11
+ def sim_capture(id, *pins)
12
+ if @sim_capture
13
+ fail 'Nesting of sim_capture blocks is not yet supported!'
14
+ end
15
+ options = pins.last.is_a?(Hash) ? pins.pop : {}
16
+ pins = pins.map { |p| p.is_a?(String) || p.is_a?(Symbol) ? dut.pin(p) : p }
17
+ pins.each(&:save)
18
+ @sim_capture = pins.map { |p| [p, "origen.dut.#{p.rtl_name}"] }
19
+ Origen::OrgFile.open(id) do |org_file|
20
+ @org_file = org_file
21
+ if update_capture?
22
+ @sim_capture.each { |pin, net| pin.record_to_org_file(only: :assert) }
23
+ end
24
+ yield
25
+ end
26
+ pins.each(&:restore)
27
+ @sim_capture = nil
28
+ end
29
+
30
+ alias_method :_origen_testers_cycle, :cycle
31
+ def cycle(options = {})
32
+ if @sim_capture
33
+ # Need to un-roll all repeats to be sure we observe the true data, can't
34
+ # really assume that it will be constant for all cycles covered by the repeat
35
+ cycles = options.delete(:repeat) || 1
36
+ cycles.times do
37
+ if update_capture?
38
+ _origen_testers_cycle(options)
39
+ @sim_capture.each do |pin, net|
40
+ pin.assert(simulator.peek(net))
41
+ # Remove the assertion since it is for the previous cycle in terms of the current simulation,
42
+ # this won't be captured to the org file
43
+ pin.dont_care
44
+ end
45
+ Origen::OrgFile.cycle
46
+ else
47
+ unless @org_file.exist?
48
+ fail "The simulation capture \"#{id}\" has not been made yet, re-run this pattern with a simulation target first!"
49
+ end
50
+ apply_captured_data
51
+ _origen_testers_cycle(options)
52
+ end
53
+ end
54
+ else
55
+ _origen_testers_cycle(options)
56
+ end
57
+ end
58
+
59
+ def apply_captured_data
60
+ if @apply_captured_data_cycles && @apply_captured_data_cycles > 1
61
+ @apply_captured_data_cycles -= 1
62
+ else
63
+ @org_file.read_line do |operations, cycles|
64
+ @apply_captured_data_cycles = cycles
65
+ operations.each do |object, operation, *args|
66
+ object.send(operation, *args)
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ def update_capture?
73
+ return @update_capture if defined? @update_capture
74
+ @update_capture = sim? && (!@org_file.exist? || Origen.app!.update_sim_captures)
75
+ end
10
76
  end
11
77
  end
@@ -22,14 +22,72 @@ module OrigenSim
22
22
  end
23
23
  end
24
24
 
25
+ def fetch_simulation_objects(options = {})
26
+ sid = options[:id] || id
27
+ ldir = "#{Origen.root}/simulation/#{sid}"
28
+ tmp_dir = "#{Origen.root}/tmp/origen_sim/tmp"
29
+ if config[:rc_dir_url]
30
+ unless config[:rc_version]
31
+ puts "You must supply an :rc_version option when using :rc_dir_url (you can set this to something like 'Trunk' or 'master' if you want)"
32
+ exit 1
33
+ end
34
+ if !File.exist?(compiled_dir) ||
35
+ (File.exist?(compiled_dir) && Dir.entries(compiled_dir).size <= 2) ||
36
+ (Origen.app.session.origen_sim[sid] != config[:rc_version]) ||
37
+ options[:force]
38
+ Origen.log.info "Fetching the simulation object for #{sid}..."
39
+ Origen.app.session.origen_sim[sid] = nil # Clear this up front, if the checkout fails we won't know what we have
40
+ FileUtils.rm_rf(tmp_dir) if File.exist?(tmp_dir)
41
+ FileUtils.mkdir_p(tmp_dir)
42
+ FileUtils.mkdir_p("#{Origen.root}/simulation")
43
+ rc = Origen::RevisionControl.new remote: config[:rc_dir_url], local: tmp_dir
44
+ rc.checkout "#{sid}.tar.gz", force: true, version: config[:rc_version]
45
+ FileUtils.mv "#{tmp_dir}/#{sid}.tar.gz", "#{Origen.root}/simulation"
46
+ FileUtils.rm_rf(ldir) if File.exist?(ldir)
47
+ Dir.chdir "#{Origen.root}/simulation/" do
48
+ system "tar -xvf #{sid}.tar.gz"
49
+ end
50
+ Origen.app.session.origen_sim[sid] = config[:rc_version]
51
+ end
52
+ else
53
+ if !File.exist?(compiled_dir) ||
54
+ (File.exist?(compiled_dir) && Dir.entries(compiled_dir).size <= 2)
55
+ puts "There is no previously compiled simulation object in: #{compiled_dir}"
56
+ exit 1
57
+ end
58
+ end
59
+ ensure
60
+ FileUtils.rm_f "#{ldir}.tar.gz" if File.exist?("#{ldir}.tar.gz")
61
+ FileUtils.rm_rf tmp_dir if File.exist?(tmp_dir)
62
+ end
63
+
64
+ def commit_simulation_objects(options = {})
65
+ sid = options[:id] || id
66
+ ldir = "#{Origen.root}/simulation/#{sid}"
67
+ tmp_dir = "#{Origen.root}/tmp/origen_sim/tmp"
68
+ unless File.exist?(ldir)
69
+ fail "The simulation directory to check in does not exist: #{ldir}"
70
+ end
71
+ Dir.chdir "#{Origen.root}/simulation/" do
72
+ system "tar -cvzf #{sid}.tar.gz #{sid}"
73
+ end
74
+
75
+ FileUtils.rm_rf(tmp_dir) if File.exist?(tmp_dir)
76
+ FileUtils.mkdir_p(tmp_dir)
77
+ FileUtils.cp "#{ldir}.tar.gz", tmp_dir
78
+
79
+ rc = Origen::RevisionControl.new remote: config[:rc_dir_url], local: tmp_dir
80
+ rc.checkin "#{sid}.tar.gz", unmanaged: true, force: true, comment: 'Checked in via sim:rc command'
81
+ ensure
82
+ FileUtils.rm_f "#{ldir}.tar.gz" if File.exist?("#{ldir}.tar.gz")
83
+ FileUtils.rm_rf tmp_dir if File.exist?(tmp_dir)
84
+ end
85
+
25
86
  def configure(options)
26
87
  fail 'A vendor must be supplied, e.g. OrigenSim::Tester.new(vendor: :icarus)' unless options[:vendor]
27
88
  unless VENDORS.include?(options[:vendor])
28
89
  fail "Unknown vendor #{options[:vendor]}, valid values are: #{VENDORS.map { |v| ':' + v.to_s }.join(', ')}"
29
90
  end
30
- unless options[:rtl_top]
31
- fail "The name of the file containing the DUT top-level must be supplied, e.g. OrigenSim::Tester.new(rtl_top: 'my_ip')" unless options[:rtl_top]
32
- end
33
91
  @configuration = options
34
92
  @tmp_dir = nil
35
93
  end
@@ -49,14 +107,6 @@ module OrigenSim
49
107
  end
50
108
  end
51
109
 
52
- def artifacts_dir
53
- @artifacts_dir ||= begin
54
- d = "#{Origen.root}/simulation/#{id}/artifacts"
55
- FileUtils.mkdir_p(d)
56
- d
57
- end
58
- end
59
-
60
110
  # Returns the directory where the compiled simulation object lives, this should
61
111
  # be checked into your Origen app's repository
62
112
  def compiled_dir
@@ -93,20 +143,20 @@ module OrigenSim
93
143
 
94
144
  def wave_config_file
95
145
  @wave_config_file ||= begin
96
- f = "#{wave_config_dir}/#{User.current.id}.svcf"
146
+ f = "#{wave_config_dir}/#{User.current.id}.#{wave_config_ext}"
97
147
  unless File.exist?(f)
98
148
  # Take a default wave if one has been set up
99
- d = "#{wave_config_dir}/default.svcf"
149
+ d = "#{wave_config_dir}/default.#{wave_config_ext}"
100
150
  if File.exist?(d)
101
151
  FileUtils.cp(d, f)
102
152
  else
103
153
  # Otherwise seed it with the latest existing setup by someone else
104
- d = Dir.glob("#{wave_config_dir}/*.svcf").max { |a, b| File.ctime(a) <=> File.ctime(b) }
154
+ d = Dir.glob("#{wave_config_dir}/*.#{wave_config_ext}").max { |a, b| File.ctime(a) <=> File.ctime(b) }
105
155
  if d
106
156
  FileUtils.cp(d, f)
107
157
  else
108
158
  # We tried our best, start from scratch
109
- d = "#{Origen.root!}/templates/empty.svcf"
159
+ d = "#{Origen.root!}/templates/empty.#{wave_config_ext}"
110
160
  FileUtils.cp(d, f)
111
161
  end
112
162
  end
@@ -115,15 +165,24 @@ module OrigenSim
115
165
  end
116
166
  end
117
167
 
168
+ def wave_config_ext
169
+ case config[:vendor]
170
+ when :icarus
171
+ 'gtkw'
172
+ when :cadence
173
+ 'svcf'
174
+ end
175
+ end
176
+
118
177
  def run_cmd
119
178
  case config[:vendor]
120
179
  when :icarus
121
180
  cmd = configuration[:vvp] || 'vvp'
122
- cmd += " -M#{compiled_dir} -morigen #{compiled_dir}/dut.vvp +socket+#{socket_id}"
181
+ cmd += " -M#{compiled_dir} -morigen #{compiled_dir}/origen.vvp +socket+#{socket_id}"
123
182
 
124
183
  when :cadence
125
184
  input_file = "#{tmp_dir}/#{id}.tcl"
126
- unless File.exist?(input_file)
185
+ if !File.exist?(input_file) || config_changed?
127
186
  Origen.app.runner.launch action: :compile,
128
187
  files: "#{Origen.root!}/templates/probe.tcl.erb",
129
188
  output: tmp_dir,
@@ -133,7 +192,7 @@ module OrigenSim
133
192
  output_file_name: "#{id}.tcl"
134
193
  end
135
194
  input_file_fast = "#{tmp_dir}/#{id}_fast.tcl"
136
- if $use_fast_probe_depth
195
+ if !File.exist?(input_file_fast) || config_changed?
137
196
  fast_probe_depth = config[:fast_probe_depth] || 1
138
197
  Origen.app.runner.launch action: :compile,
139
198
  files: "#{Origen.root!}/templates/probe.tcl.erb",
@@ -143,8 +202,8 @@ module OrigenSim
143
202
  options: { dir: wave_dir, force: config[:force], setup: config[:setup], depth: fast_probe_depth },
144
203
  output_file_name: "#{id}_fast.tcl"
145
204
  end
146
- wave_dir # Ensure this exists since it won't be referenced above if the
147
- # input file is already generated
205
+ save_config_signature
206
+ wave_dir # Ensure this exists since it won't be referenced above if the input file is already generated
148
207
 
149
208
  cmd = configuration[:irun] || 'irun'
150
209
  cmd += " -r origen -snapshot origen +socket+#{socket_id}"
@@ -157,6 +216,15 @@ module OrigenSim
157
216
  def view_wave_command
158
217
  cmd = nil
159
218
  case config[:vendor]
219
+ when :icarus
220
+ edir = Pathname.new(wave_config_dir).relative_path_from(Pathname.pwd)
221
+ cmd = "cd #{edir} && "
222
+ cmd += configuration[:gtkwave] || 'gtkwave'
223
+ dir = Pathname.new(wave_dir).relative_path_from(edir.expand_path)
224
+ cmd += " #{dir}/origen.vcd "
225
+ f = Pathname.new(wave_config_file).relative_path_from(edir.expand_path)
226
+ cmd += " --save #{f} &"
227
+
160
228
  when :cadence
161
229
  edir = Pathname.new(wave_config_dir).relative_path_from(Pathname.pwd)
162
230
  cmd = "cd #{edir} && "
@@ -165,6 +233,7 @@ module OrigenSim
165
233
  cmd += " #{dir}/#{id}.dsn #{dir}/#{id}.trn"
166
234
  f = Pathname.new(wave_config_file).relative_path_from(edir.expand_path)
167
235
  cmd += " -input #{f} &"
236
+
168
237
  end
169
238
  cmd
170
239
  end
@@ -178,22 +247,12 @@ module OrigenSim
178
247
  end
179
248
  end
180
249
 
181
- def link_artifacts
182
- done = "#{run_dir}/.artifacts_linked"
183
- unless File.exist?(done)
184
- Dir.foreach(artifacts_dir) do |item|
185
- next if item == '.' || item == '..'
186
- FileUtils.ln_s("#{artifacts_dir}/#{item}", "#{run_dir}/#{item}") unless File.exist?("#{run_dir}/#{item}")
187
- end
188
- FileUtils.touch(done)
189
- end
190
- end
191
-
192
250
  # Starts up the simulator process
193
251
  def start
252
+ fetch_simulation_objects
253
+
194
254
  server = UNIXServer.new(socket_id)
195
255
  verbose = Origen.debugger_enabled?
196
- link_artifacts
197
256
  cmd = run_cmd + ' & echo \$!'
198
257
 
199
258
  launch_simulator = %(
@@ -224,7 +283,7 @@ module OrigenSim
224
283
  simulator_parent_process = spawn("ruby -e \"#{launch_simulator}\"")
225
284
  Process.detach(simulator_parent_process)
226
285
 
227
- timeout_connection(config[:startup_timeout] || 15) do
286
+ timeout_connection(config[:startup_timeout] || 60) do
228
287
  @socket = server.accept
229
288
  @connection_established = true
230
289
  if @connection_timed_out
@@ -350,10 +409,11 @@ module OrigenSim
350
409
  # set up internal handles to efficiently access them
351
410
  def define_pins
352
411
  dut.rtl_pins.each_with_index do |(name, pin), i|
353
- unless pin.tie_off
354
- pin.simulation_index = i
355
- put("0^#{pin.id}^#{i}^#{pin.drive_wave.index}^#{pin.compare_wave.index}")
356
- end
412
+ pin.simulation_index = i
413
+ put("0^#{pin.rtl_name}^#{i}^#{pin.drive_wave.index}^#{pin.compare_wave.index}")
414
+ end
415
+ dut.rtl_pins.each do |name, pin|
416
+ pin.apply_force
357
417
  end
358
418
  end
359
419
 
@@ -407,11 +467,13 @@ module OrigenSim
407
467
 
408
468
  # Returns the current simulation error count
409
469
  def error_count
410
- peek('origen.debug.errors')
470
+ peek('origen.debug.errors').to_i
411
471
  end
412
472
 
413
473
  # Returns the current value of the given net, or nil if the given path does not
414
474
  # resolve to a valid node
475
+ #
476
+ # The value is returned as an instance of Origen::Value
415
477
  def peek(net)
416
478
  # The Verilog spec does not specify that underlying VPI put method should
417
479
  # handle a part select, so some simulators do not handle it. Therefore we
@@ -427,21 +489,20 @@ module OrigenSim
427
489
 
428
490
  sync_up
429
491
  put("9^#{clean(net)}")
430
-
431
492
  m = get.strip
493
+
432
494
  if m == 'FAIL'
433
495
  return nil
434
496
  else
435
- m = m.to_i
436
497
  if msb
437
498
  # Setting a range of bits
438
499
  if lsb
439
- m[msb..lsb]
500
+ Origen::Value.new('b' + m[(m.size - 1 - msb)..(m.size - 1 - lsb)])
440
501
  else
441
- m[msb]
502
+ Origen::Value.new('b' + m[m.size - 1 - msb])
442
503
  end
443
504
  else
444
- m
505
+ Origen::Value.new('b' + m)
445
506
  end
446
507
  end
447
508
  end
@@ -587,6 +648,25 @@ module OrigenSim
587
648
  @sync_active
588
649
  end
589
650
 
651
+ # Returns true if the config has been changed since the last time we called save_config_signature
652
+ def config_changed?
653
+ Origen.app.session.origen_sim["#{id}_config"] != config
654
+ end
655
+
656
+ # Locally saves a signature for the current config, this will cause config_changed? to return false
657
+ # until its contents change
658
+ def save_config_signature
659
+ Origen.app.session.origen_sim["#{id}_config"] = config
660
+ end
661
+
662
+ # Returns the version of Origen Sim that the current DUT object was compiled with
663
+ def dut_version
664
+ @dut_version ||= begin
665
+ put('i^')
666
+ Origen::VersionString.new(get.strip)
667
+ end
668
+ end
669
+
590
670
  private
591
671
 
592
672
  def clean(net)