origen_sim 0.15.0 → 0.16.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -7,6 +7,7 @@ module OrigenSim
7
7
  @socket = socket
8
8
  @continue = true
9
9
  @logged_errors = false
10
+ @last_message_at = Time.now
10
11
  super do
11
12
  begin
12
13
  while @continue
@@ -17,12 +18,13 @@ module OrigenSim
17
18
  !OrigenSim.stderr_string_exceptions.any? { |s| s.is_a?(Regexp) ? s.match?(line) : line =~ /#{s}/i }
18
19
  # We're failing on stderr, so print its results and log as errors if its not an exception.
19
20
  @logged_errors = true
20
- Origen.log.error "(STDERR): #{line}"
21
+ Origen.log.error "(STDERR): #{line}", from_origen_sim: true
21
22
  elsif OrigenSim.verbose?
22
- Origen.log.info line
23
+ Origen.log.info line, from_origen_sim: true
23
24
  else
24
- Origen.log.debug line
25
+ Origen.log.debug line, from_origen_sim: true
25
26
  end
27
+ @last_message_at = Time.now
26
28
  end
27
29
  end
28
30
  rescue IOError => e
@@ -36,5 +38,9 @@ module OrigenSim
36
38
  def stop
37
39
  @continue = false
38
40
  end
41
+
42
+ def time_since_last_message
43
+ Time.now - @last_message_at
44
+ end
39
45
  end
40
46
  end
@@ -3,31 +3,50 @@ module OrigenSim
3
3
  class StdoutReader < Thread
4
4
  attr_reader :socket, :logged_errors
5
5
 
6
- def initialize(socket)
6
+ def initialize(socket, simulator)
7
7
  @socket = socket
8
8
  @continue = true
9
9
  @logged_errors = false
10
+ @last_message_at = Time.now
10
11
  super do
11
12
  begin
12
13
  while @continue
13
14
  line = @socket.gets
14
15
  if line
15
16
  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
17
+ # If line has been sent from Origen for logging
18
+ # https://rubular.com/r/1czQZnlhBq9YtK
19
+ if line =~ /^!(\d)!\[\s*((\d+),)?\s*(\d+)\]\s*(.*)/
20
+ if Regexp.last_match(3)
21
+ time_in_sim_units = (Regexp.last_match(3).to_i << 32) | Regexp.last_match(4).to_i
22
+ time_in_ns = simulator.send(:simtime_units_to_ns, time_in_sim_units)
23
+ else
24
+ # Messages sent from the Origen testbench already have a timestamp in ns
25
+ time_in_ns = Regexp.last_match(4).to_i
26
+ end
27
+ msg = "#{time_in_ns}".rjust(11) + ' ns: ' + Regexp.last_match(5)
28
+
29
+ Origen.log.send(Simulator::LOG_CODES_[Regexp.last_match(1).to_i], msg, from_origen_sim: true)
30
+
31
+ simulator.send(:max_error_abort) if line =~ /!MAX_ERROR_ABORT!/
23
32
  else
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
33
+ if OrigenSim.error_strings.any? { |s| s.is_a?(Regexp) ? s.match?(line) : line =~ /#{s}/i } &&
34
+ !OrigenSim.error_string_exceptions.any? { |s| s.is_a?(Regexp) ? s.match?(line) : line =~ /#{s}/i }
35
+ @logged_errors = true
36
+ Origen.log.error "(STDOUT): #{line}", from_origen_sim: true
37
+ elsif OrigenSim.warning_strings.any? { |s| s.is_a?(Regexp) ? s.match?(line) : line =~ /#{s}/i } &&
38
+ !OrigenSim.warning_string_exceptions.any? { |s| s.is_a?(Regexp) ? s.match?(line) : line =~ /#{s}/i }
39
+ Origen.log.warn line, from_origen_sim: true
27
40
  else
28
- Origen.log.debug line
41
+ if OrigenSim.verbose? ||
42
+ OrigenSim.log_strings.any? { |s| s.is_a?(Regexp) ? s.match?(line) : line =~ /#{s}/i }
43
+ Origen.log.info line, from_origen_sim: true
44
+ else
45
+ Origen.log.debug line, from_origen_sim: true
46
+ end
29
47
  end
30
48
  end
49
+ @last_message_at = Time.now
31
50
  end
32
51
  end
33
52
  rescue IOError => e
@@ -41,5 +60,9 @@ module OrigenSim
41
60
  def stop
42
61
  @continue = false
43
62
  end
63
+
64
+ def time_since_last_message
65
+ Time.now - @last_message_at
66
+ end
44
67
  end
45
68
  end
@@ -19,11 +19,6 @@ module OrigenSim
19
19
  super()
20
20
  end
21
21
 
22
- # Returns the current cycle count
23
- def cycle_count
24
- @cycle_count || 0
25
- end
26
-
27
22
  def simulator
28
23
  OrigenSim.simulator
29
24
  end
@@ -43,9 +38,9 @@ module OrigenSim
43
38
  end
44
39
  @sync_pins.map do |pin|
45
40
  if @sync_cycles.size == 1
46
- simulator.peek("origen.pins.#{pin.id}.sync_memory[0]")
41
+ simulator.peek("#{simulator.testbench_top}.pins.#{pin.id}.sync_memory[0]")
47
42
  else
48
- simulator.peek("origen.pins.#{pin.id}.sync_memory[#{@sync_cycles - 1}:0]")
43
+ simulator.peek("#{simulator.testbench_top}.pins.#{pin.id}.sync_memory[#{@sync_cycles - 1}:0]")
49
44
  end
50
45
  end
51
46
  end
@@ -62,9 +57,9 @@ module OrigenSim
62
57
  end
63
58
 
64
59
  # Flush any buffered simulation output, this should cause live waveviewers to
65
- # reflect the latest state
66
- def flush
67
- simulator.flush
60
+ # reflect the latest state and the console and log files to update
61
+ def flush(*args)
62
+ simulator.flush(*args)
68
63
  end
69
64
 
70
65
  def set_timeset(name, period_in_ns)
@@ -79,24 +74,35 @@ module OrigenSim
79
74
 
80
75
  # This method intercepts vector data from Origen, removes white spaces and compresses repeats
81
76
  def push_vector(options)
82
- unless options[:timeset]
83
- puts 'No timeset defined!'
84
- puts 'Add one to your top level startup method or target like this:'
85
- puts '$tester.set_timeset("nvmbist", 40) # Where 40 is the period in ns'
86
- exit 1
87
- end
88
- flush_comments unless @comment_buffer.empty?
89
- simulator.cycle(options[:repeat] || 1)
90
- @cycle_count ||= 0
91
- @cycle_count += options[:repeat] || 1
92
- if @after_next_vector
93
- @after_next_vector.call(@after_next_vector_args)
94
- @after_next_vector = nil
77
+ if simulator.simulation.max_errors_exceeded
78
+ fail Origen::Generator::AbortError, 'The max error count has been exceeded in the simulation'
79
+ else
80
+ unless options[:timeset]
81
+ puts 'No timeset defined!'
82
+ puts 'Add one to your top level startup method or target like this:'
83
+ puts '$tester.set_timeset("nvmbist", 40) # Where 40 is the period in ns'
84
+ exit 1
85
+ end
86
+ flush_comments unless @comment_buffer.empty?
87
+ simulator.cycle(options[:repeat] || 1)
88
+ if @after_next_vector
89
+ @after_next_vector.call(@after_next_vector_args)
90
+ @after_next_vector = nil
91
+ end
95
92
  end
96
93
  end
97
94
 
98
95
  def c1(msg, options = {})
99
- @comment_buffer << msg if @step_comment_on
96
+ if @step_comment_on
97
+ simulator.log msg
98
+ @comment_buffer << msg
99
+ end
100
+ end
101
+
102
+ def ss(msg = nil)
103
+ simulator.log '=' * 70
104
+ super
105
+ simulator.log '=' * 70
100
106
  end
101
107
 
102
108
  def loop_vectors(name, number_of_loops, options = {})
@@ -132,7 +138,7 @@ module OrigenSim
132
138
  end
133
139
  end
134
140
  end
135
- if simulator.sync_active?
141
+ if simulator.sync_active? && @sync_cycles
136
142
  @sync_cycles += 1
137
143
  pins.each do |pin|
138
144
  @sync_pins << pin unless @sync_pins.include?(pin)
@@ -222,12 +228,186 @@ module OrigenSim
222
228
 
223
229
  def wait(*args)
224
230
  super
225
- flush if Origen.running_interactively? && dut_version > '0.12.1'
231
+ if Origen.running_interactively? ||
232
+ (defined?(Byebug) && Byebug.try(:mode) == :attached)
233
+ flush quiet: true
234
+ end
226
235
  end
227
236
 
228
- # def method_missing(m, *args, &block)
229
- # super
230
- # end
237
+ def log_file_written(path)
238
+ simulator.simulation.log_files << path if simulator.simulation
239
+ end
240
+
241
+ def read_register(reg_or_val, options = {})
242
+ # This could be called multiple times for the same transaction
243
+ if read_reg_open?
244
+ yield
245
+ else
246
+ @read_reg_meta_supplied = false
247
+ @read_reg_open = true
248
+ @read_reg_cycles = {}
249
+ unless @supports_transactions_set
250
+ @supports_transactions = dut_version > '0.15.0'
251
+ @supports_transactions_set = true
252
+ end
253
+
254
+ if reg_or_val.respond_to?(:named_bits)
255
+ bit_names = reg_or_val.named_bits.map { |name, bits| name }.uniq
256
+ expected = bit_names.map do |name|
257
+ bits = reg_or_val.bits(name)
258
+ if bits.is_to_be_read?
259
+ [name, bits.status_str(:read)]
260
+ end
261
+ end.compact
262
+
263
+ # Save which bits are being read for later, the driver performing the read will
264
+ # clear the register flags
265
+ read_flags = reg_or_val.map(&:is_to_be_read?)
266
+ end
267
+
268
+ error_count = simulator.error_count
269
+
270
+ simulator.start_read_reg_transaction if @supports_transactions
271
+
272
+ yield
273
+
274
+ if @supports_transactions
275
+ errors_captured, exceeded_max_errors, errors = *(simulator.stop_read_reg_transaction)
276
+ end
277
+
278
+ @read_reg_open = false
279
+
280
+ if simulator.error_count > error_count
281
+ if @supports_transactions
282
+ actual_data_available = true
283
+ if exceeded_max_errors
284
+ Origen.log.warning 'The number of errors in this transaction exceeded the capture buffer, the actual data reported here may not be accurate'
285
+ end
286
+ out_of_sync = simulator.simulation.cycle_count != simulator.cycle_count
287
+ if out_of_sync
288
+ Origen.log.warning 'Something has gone wrong and Origen and the simulator do not agree on the current cycle number, it is not possible to resolve the actual data'
289
+ actual_data_available = false
290
+ else
291
+ diffs = []
292
+ errors.each do |error|
293
+ if c = read_reg_cycles[error[:cycle]]
294
+ if p = c[simulator.pins_by_rtl_name[error[:pin_name]]]
295
+ if p[:position]
296
+ diffs << [p[:position], error[:recieved], error[:expected]]
297
+ end
298
+ end
299
+ end
300
+ end
301
+ if diffs.empty?
302
+ if @read_reg_meta_supplied
303
+ Origen.log.warning 'It looks like the miscompare(s) occurred on pins/cycles that are not associated with register data'
304
+ else
305
+ Origen.log.warning 'It looks like your current read register driver does not provide the necessary meta-data to map these errors to an actual register value'
306
+ end
307
+ actual_data_available = false
308
+ end
309
+ end
310
+ else
311
+ Origen.log.warning 'Your DUT needs to be compiled with a newer version of OrigenSim to support reporting of the actual read data from this failed transaction'
312
+ actual_data_available = false
313
+ end
314
+
315
+ # If a register object has been supplied...
316
+ if read_flags
317
+ Origen.log.error "Errors occurred reading register #{reg_or_val.path}:"
318
+ if actual_data_available
319
+ actual = nil
320
+ reg_or_val.preserve_flags do
321
+ reg_or_val.each_with_index do |bit, i|
322
+ bit.read if read_flags[i]
323
+ end
324
+
325
+ diffs.each do |position, received, expected|
326
+ reg_or_val[position].data = received
327
+ end
328
+
329
+ actual = bit_names.map do |name|
330
+ bits = reg_or_val.bits(name)
331
+ if bits.is_to_be_read?
332
+ [name, bits.status_str(:read)]
333
+ end
334
+ end.compact
335
+
336
+ # Put the data back so the application behaves as it would if generating
337
+ # for a non-simulation tester target
338
+ diffs.each do |position, received, expected|
339
+ reg_or_val[position].data = expected
340
+ end
341
+ end
342
+ end
343
+
344
+ expected.each do |name, expected|
345
+ msg = "#{reg_or_val.path}.#{name}: expected #{expected}"
346
+ if actual_data_available
347
+ received_ = nil
348
+ actual.each do |name2, received|
349
+ if name == name2
350
+ received_ = received
351
+ msg += " received #{received}"
352
+ end
353
+ end
354
+ if expected == received_
355
+ Origen.log.info msg
356
+ else
357
+ Origen.log.error msg
358
+ end
359
+ else
360
+ msg += " received #{expected}" if @read_reg_meta_supplied
361
+ Origen.log.error msg
362
+ end
363
+ end
364
+ else
365
+ Origen.log.error 'Errors occurred while reading a register:'
366
+ msg = "expected #{reg_or_val.to_s(16).upcase}"
367
+ if actual_data_available
368
+ actual = reg_or_val
369
+ diffs.each do |position, received, expected|
370
+ if received == 1
371
+ actual |= (1 << position)
372
+ else
373
+ lower = actual[(position - 1)..0]
374
+ actual = actual >> (position + 1)
375
+ actual = actual << (position + 1)
376
+ actual |= lower
377
+ end
378
+ end
379
+ msg += " received #{actual.to_s(16).upcase}"
380
+ else
381
+ msg += " received #{reg_or_val.to_s(16).upcase}" if @read_reg_meta_supplied
382
+ end
383
+ Origen.log.error msg
384
+ end
385
+
386
+ Origen.log.error
387
+ caller.each do |line|
388
+ if Pathname.new(line.split(':').first).expand_path.to_s =~ /^#{Origen.root}(?!(\/lbin|\/vendor\/gems)).*$/
389
+ Origen.log.error line
390
+ end
391
+ end
392
+ end
393
+ end
394
+ end
395
+
396
+ def read_reg_open?
397
+ @read_reg_open
398
+ end
399
+
400
+ def read_reg_cycles
401
+ @read_reg_cycles
402
+ end
403
+
404
+ def cycle_count
405
+ simulator.simulation.cycle_count
406
+ end
407
+
408
+ def read_reg_meta_supplied=(val)
409
+ @read_reg_meta_supplied = val
410
+ end
231
411
 
232
412
  private
233
413
 
@@ -121,37 +121,39 @@ module OrigenSimDev
121
121
  end
122
122
 
123
123
  def read_register(reg, options = {})
124
- # Special read for this register to test sync'ing over a parallel
125
- if reg.id == :parallel_read
126
- pins = []
127
- reg.shift_out_with_index do |bit, i|
128
- if bit.is_to_be_stored?
129
- pins << dut.pins(:dout)[i]
124
+ tester.read_register(reg, options) do
125
+ # Special read for this register to test sync'ing over a parallel
126
+ if reg.id == :parallel_read
127
+ pins = []
128
+ reg.shift_out_with_index do |bit, i|
129
+ if bit.is_to_be_stored?
130
+ pins << dut.pins(:dout)[i]
131
+ end
130
132
  end
131
- end
132
- tester.store_next_cycle(*pins.reverse)
133
- 1.cycle
134
- dut.pins(:dout).dont_care
135
- else
136
- if reg.path =~ /ip(\d)/
137
- ir_val = 0b0100 | Regexp.last_match(1).to_i
138
- jtag.write_ir(ir_val, size: 4)
139
- ip = reg.parent
140
- ip.dr.bits(:write).write(0)
141
- ip.dr.bits(:address).write(reg.address)
142
- ip.dr.bits(:data).write(0)
143
- jtag.write_dr(ip.dr)
144
- ip.dr.bits(:data).copy_all(reg)
145
- jtag.read_dr(ip.dr)
133
+ tester.store_next_cycle(*pins.reverse)
134
+ 1.cycle
135
+ dut.pins(:dout).dont_care
146
136
  else
147
- jtag.write_ir(0x8, size: 4)
148
- dr.rg_enable.write(1)
149
- dr.rg_read.write(1)
150
- dr.rg_addr.write(reg.address)
151
- jtag.write_dr(dr)
152
- dr.rg_enable.write(0)
153
- dr.rg_data.copy_all(reg)
154
- jtag.read_dr(dr)
137
+ if reg.path =~ /ip(\d)/
138
+ ir_val = 0b0100 | Regexp.last_match(1).to_i
139
+ jtag.write_ir(ir_val, size: 4)
140
+ ip = reg.parent
141
+ ip.dr.bits(:write).write(0)
142
+ ip.dr.bits(:address).write(reg.address)
143
+ ip.dr.bits(:data).write(0)
144
+ jtag.write_dr(ip.dr)
145
+ ip.dr.bits(:data).copy_all(reg)
146
+ jtag.read_dr(ip.dr)
147
+ else
148
+ jtag.write_ir(0x8, size: 4)
149
+ dr.rg_enable.write(1)
150
+ dr.rg_read.write(1)
151
+ dr.rg_addr.write(reg.address)
152
+ jtag.write_dr(dr)
153
+ dr.rg_enable.write(0)
154
+ dr.rg_data.copy_all(reg)
155
+ jtag.read_dr(dr)
156
+ end
155
157
  end
156
158
  end
157
159
  end