origen_sim 0.5.5 → 0.6.0

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