origen_sim 0.15.0 → 0.16.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.
@@ -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