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.
- checksums.yaml +4 -4
- data/config/application.rb +1 -1
- data/config/shared_commands.rb +13 -4
- data/config/version.rb +1 -1
- data/ext/bridge.c +521 -272
- data/ext/bridge.h +2 -0
- data/ext/defines.h.erb +8 -0
- data/ext/origen.c +2 -0
- data/lib/origen_sim.rb +21 -1
- data/lib/origen_sim/origen/pins/pin.rb +7 -2
- data/lib/origen_sim/origen_testers/api.rb +11 -2
- data/lib/origen_sim/simulation.rb +34 -4
- data/lib/origen_sim/simulator.rb +227 -19
- data/lib/origen_sim/stderr_reader.rb +9 -3
- data/lib/origen_sim/stdout_reader.rb +35 -12
- data/lib/origen_sim/tester.rb +209 -29
- data/lib/origen_sim_dev/dut.rb +31 -29
- data/pattern/test.rb +2 -0
- data/templates/origen_guides/simulation/debugging.md.erb +123 -6
- data/templates/origen_guides/simulation/environment.md.erb +3 -6
- data/templates/origen_guides/simulation/log.md.erb +1 -1
- data/templates/origen_guides/simulation/patterns.md.erb +35 -0
- data/templates/rtl_v/origen.v.erb +14 -49
- metadata +5 -5
@@ -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
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
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.
|
25
|
-
OrigenSim.
|
26
|
-
|
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
|
-
|
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
|
data/lib/origen_sim/tester.rb
CHANGED
@@ -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("
|
41
|
+
simulator.peek("#{simulator.testbench_top}.pins.#{pin.id}.sync_memory[0]")
|
47
42
|
else
|
48
|
-
simulator.peek("
|
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
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
@after_next_vector
|
94
|
-
|
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
|
-
|
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
|
-
|
231
|
+
if Origen.running_interactively? ||
|
232
|
+
(defined?(Byebug) && Byebug.try(:mode) == :attached)
|
233
|
+
flush quiet: true
|
234
|
+
end
|
226
235
|
end
|
227
236
|
|
228
|
-
|
229
|
-
|
230
|
-
|
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
|
|
data/lib/origen_sim_dev/dut.rb
CHANGED
@@ -121,37 +121,39 @@ module OrigenSimDev
|
|
121
121
|
end
|
122
122
|
|
123
123
|
def read_register(reg, options = {})
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
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
|
-
|
132
|
-
|
133
|
-
|
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
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
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
|