Class: OrigenSim::Tester
- Inherits:
-
Object
- Object
- OrigenSim::Tester
- Includes:
- OrigenTesters::VectorBasedTester
- Defined in:
- lib/origen_sim/tester.rb
Overview
Responsible for interfacing the simulator with Origen
Constant Summary collapse
Instance Attribute Summary collapse
-
#out_of_bounds_handler ⇒ Object
Returns the value of attribute out_of_bounds_handler.
Instance Method Summary collapse
- #c1(msg, options = {}) ⇒ Object
- #capture ⇒ Object
- #cycle_count ⇒ Object
- #dut_version ⇒ Object
-
#flush(*args) ⇒ Object
Flush any buffered simulation output, this should cause live waveviewers to reflect the latest state and the console and log files to update.
-
#force(*args) ⇒ Object
Shorthand for simulator.force.
- #handshake(options = {}) ⇒ Object
-
#initialize(options = {}, &block) ⇒ Tester
constructor
A new instance of Tester.
- #log_file_written(path) ⇒ Object
- #loop_vectors(name, number_of_loops, options = {}) ⇒ Object (also: #loop_vector)
- #match(pin, state, timeout_in_cycles, options = {}) ⇒ Object
- #match_block(timeout_in_cycles, options = {}, &block) ⇒ Object
- #match_loop_resolution(timeout_in_cycles, options) ⇒ Object private
-
#peek(*args) ⇒ Object
Shorthand for simulator.peek.
-
#peek_real(*args) ⇒ Object
Shorthand for simulator.peek_real.
-
#poke(*args) ⇒ Object
Shorthand for simulator.poke.
-
#push_vector(options) ⇒ Object
This method intercepts vector data from Origen, removes white spaces and compresses repeats.
- #read_reg_cycles ⇒ Object
- #read_reg_meta_supplied=(val) ⇒ Object
- #read_reg_open? ⇒ Boolean
- #read_register(reg_or_val, options = {}) ⇒ Object
-
#release(*args) ⇒ Object
Shorthand for simulator.release.
- #set_timeset(name, period_in_ns) ⇒ Object
- #simulator ⇒ Object
- #ss(msg = nil) ⇒ Object
-
#start ⇒ Object
Start the simulator.
-
#store_next_cycle(*pins) ⇒ Object
Capture the next vector generated.
-
#sync_up ⇒ Object
Blocks the Origen process until the simulator indicates that it has processed all operations up to this point.
- #wait(*args) ⇒ Object
Constructor Details
#initialize(options = {}, &block) ⇒ Tester
Returns a new instance of Tester.
10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
# File 'lib/origen_sim/tester.rb', line 10 def initialize( = {}, &block) # Use Origen's collector to allow options to be set either from the options hash, or from the block if block_given? opts = Origen::Utility.collector(hash: , merge_method: :keep_hash, &block).to_hash else opts = end simulator.configure(opts, &block) @comment_buffer = [] @last_comment_size = 0 @execution_time_in_ns = 0 super() end |
Instance Attribute Details
#out_of_bounds_handler ⇒ Object
Returns the value of attribute out_of_bounds_handler.
8 9 10 |
# File 'lib/origen_sim/tester.rb', line 8 def out_of_bounds_handler @out_of_bounds_handler end |
Instance Method Details
#c1(msg, options = {}) ⇒ Object
116 117 118 119 120 121 122 |
# File 'lib/origen_sim/tester.rb', line 116 def c1(msg, = {}) if @step_comment_on PatSeq.add_thread(msg) unless [:no_thread_id] simulator.log msg @comment_buffer << msg end end |
#capture ⇒ Object
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
# File 'lib/origen_sim/tester.rb', line 36 def capture simulator.sync do @sync_pins = [] @sync_cycles = 0 yield end @sync_pins.map do |pin| if @sync_cycles.size == 1 b = simulator.peek("#{simulator.testbench_top}.pins.#{pin.id}.sync_memory")[0] if b.is_a?(Integer) b else Origen.log.warning "The data captured on pin #{pin.id} was undefined (X or Z), the captured value is not correct!" 0 end else val = 0 mem = simulator.peek("#{simulator.testbench_top}.pins.#{pin.id}.sync_memory") @sync_cycles.times do |i| b = mem[i] if b.is_a?(Integer) val |= b << i else Origen.log.warning "The data captured on cycle #{i} of pin #{pin.id} was undefined (X or Z), the captured value is not correct!" end end val end end end |
#cycle_count ⇒ Object
465 466 467 |
# File 'lib/origen_sim/tester.rb', line 465 def cycle_count simulator.simulation.cycle_count end |
#dut_version ⇒ Object
29 30 31 |
# File 'lib/origen_sim/tester.rb', line 29 def dut_version simulator.dut_version end |
#flush(*args) ⇒ Object
Flush any buffered simulation output, this should cause live waveviewers to reflect the latest state and the console and log files to update
80 81 82 |
# File 'lib/origen_sim/tester.rb', line 80 def flush(*args) simulator.flush(*args) end |
#force(*args) ⇒ Object
Shorthand for simulator.force
489 490 491 |
# File 'lib/origen_sim/tester.rb', line 489 def force(*args) simulator.force(*args) end |
#handshake(options = {}) ⇒ Object
33 34 |
# File 'lib/origen_sim/tester.rb', line 33 def handshake( = {}) end |
#log_file_written(path) ⇒ Object
275 276 277 |
# File 'lib/origen_sim/tester.rb', line 275 def log_file_written(path) simulator.simulation.log_files << path if simulator.simulation end |
#loop_vectors(name, number_of_loops, options = {}) ⇒ Object Also known as: loop_vector
130 131 132 133 134 |
# File 'lib/origen_sim/tester.rb', line 130 def loop_vectors(name, number_of_loops, = {}) number_of_loops.times do yield end end |
#match(pin, state, timeout_in_cycles, options = {}) ⇒ Object
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 |
# File 'lib/origen_sim/tester.rb', line 177 def match(pin, state, timeout_in_cycles, = {}) if dut_version <= '0.12.0' OrigenSim.error "Use of match loops requires a DUT model compiled with OrigenSim version > 0.12.0, the current dut was compiled with #{dut_version}" end expected_val = state == :high ? 1 : 0 if [:pin2] expected_val2 = [:state2] == :high ? 1 : 0 end matched = false start_cycle = cycle_count resolution = match_loop_resolution(timeout_in_cycles, ) until matched || cycle_count > start_cycle + timeout_in_cycles resolution.cycles current_val = simulator.peek("dut.#{pin.rtl_name}").to_i if [:pin2] current_val2 = simulator.peek("dut.#{[:pin2].rtl_name}").to_i matched = current_val == expected_val || current_val2 == expected_val2 else matched = current_val == expected_val end end # Final assertion to make the pattern fail if the loop timed out unless matched pin.restore_state do pin.assert!(expected_val) end if [:pin2] [:pin2].restore_state do [:pin2].assert!(expected_val2) end end end end |
#match_block(timeout_in_cycles, options = {}, &block) ⇒ Object
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 |
# File 'lib/origen_sim/tester.rb', line 211 def match_block(timeout_in_cycles, = {}, &block) if dut_version <= '0.12.0' OrigenSim.error "Use of match loops requires a DUT model compiled with OrigenSim version > 0.12.0, the current dut was compiled with #{dut_version}" end match_conditions = Origen::Utility::BlockArgs.new fail_conditions = Origen::Utility::BlockArgs.new if block.arity > 0 block.call(match_conditions, fail_conditions) else match_conditions.add(&block) end matched = false start_cycle = cycle_count resolution = match_loop_resolution(timeout_in_cycles, ) simulator.match_loop do until matched || cycle_count > start_cycle + timeout_in_cycles resolution.cycles # Consider the match resolved if any condition can execute without generating errors matched = match_conditions.any? do |condition| e = simulator.match_errors condition.call e == simulator.match_errors end end end # Final execution to make the pattern fail if the loop timed out unless matched if fail_conditions.instance_variable_get(:@block_args).empty? match_conditions.each(&:call) else fail_conditions.each(&:call) end end end |
#match_loop_resolution(timeout_in_cycles, options) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 |
# File 'lib/origen_sim/tester.rb', line 247 def match_loop_resolution(timeout_in_cycles, ) if [:resolution] if [:resolution].is_a?(Hash) return time_to_cycles([:resolution]) else return [:resolution] end else # Used to use the supplied timeout / 10, thinking that the supplied number would be # roughly how long it would take. However, found that when users didn't know the timeout # they would just put in really large numbers, like 1sec, which would mean we would not # check until 100ms for an operation that might be done after 100us. # So now if the old default comes out less than the new one, then use it, otherwise use # the newer more fine-grained default. old_default = timeout_in_cycles / 10 new_default = time_to_cycles(time_in_us: 100) return old_default < new_default ? old_default : new_default end end |
#peek(*args) ⇒ Object
Shorthand for simulator.peek
479 480 481 |
# File 'lib/origen_sim/tester.rb', line 479 def peek(*args) simulator.peek(*args) end |
#peek_real(*args) ⇒ Object
Shorthand for simulator.peek_real
484 485 486 |
# File 'lib/origen_sim/tester.rb', line 484 def peek_real(*args) simulator.peek_real(*args) end |
#poke(*args) ⇒ Object
Shorthand for simulator.poke
474 475 476 |
# File 'lib/origen_sim/tester.rb', line 474 def poke(*args) simulator.poke(*args) end |
#push_vector(options) ⇒ Object
This method intercepts vector data from Origen, removes white spaces and compresses repeats
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
# File 'lib/origen_sim/tester.rb', line 95 def push_vector() if simulator.simulation.max_errors_exceeded fail Origen::Generator::AbortError, 'The max error count has been exceeded in the simulation' else unless [:timeset] puts 'No timeset defined!' puts 'Add one to your top level startup method or target like this:' puts 'tester.set_timeset("nvmbist", 40) # Where 40 is the period in ns' exit 1 end flush_comments unless @comment_buffer.empty? repeat = [:repeat] || 1 simulator.cycle(repeat) @execution_time_in_ns += repeat * tester.timeset.period_in_ns if @after_next_vector @after_next_vector.call(@after_next_vector_args) @after_next_vector = nil end end end |
#read_reg_cycles ⇒ Object
461 462 463 |
# File 'lib/origen_sim/tester.rb', line 461 def read_reg_cycles @read_reg_cycles end |
#read_reg_meta_supplied=(val) ⇒ Object
469 470 471 |
# File 'lib/origen_sim/tester.rb', line 469 def (val) @read_reg_meta_supplied = val end |
#read_reg_open? ⇒ Boolean
457 458 459 |
# File 'lib/origen_sim/tester.rb', line 457 def read_reg_open? @read_reg_open end |
#read_register(reg_or_val, options = {}) ⇒ Object
279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 |
# File 'lib/origen_sim/tester.rb', line 279 def read_register(reg_or_val, = {}) # This could be called multiple times for the same transaction if read_reg_open? yield else @read_reg_meta_supplied = false @read_reg_open = true @read_reg_cycles = {} unless @supports_transactions_set @supports_transactions = dut_version > '0.15.0' @supports_transactions_set = true end if reg_or_val.respond_to?(:named_bits) bit_names = reg_or_val.named_bits.map { |name, bits| name }.uniq expected = bit_names.map do |name| bits = reg_or_val.bits(name) if bits.is_to_be_read? [name, bits.status_str(:read)] end end.compact # Save which bits are being read for later, the driver performing the read will # clear the register flags read_flags = reg_or_val.map(&:is_to_be_read?) end error_count = simulator.error_count simulator.start_read_reg_transaction if @supports_transactions yield if @supports_transactions errors_captured, exceeded_max_errors, errors = *(simulator.stop_read_reg_transaction) end @read_reg_open = false if simulator.error_count > error_count if @supports_transactions actual_data_available = true if exceeded_max_errors Origen.log.warning 'The number of errors in this transaction exceeded the capture buffer, the actual data reported here may not be accurate' end out_of_sync = simulator.simulation.cycle_count != simulator.cycle_count if out_of_sync 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' actual_data_available = false else diffs = [] errors.each do |error| if c = read_reg_cycles[error[:cycle]] if p = c[simulator.pins_by_rtl_name[error[:pin_name]]] if p[:position] diffs << [p[:position], error[:expected], error[:received]] end end end end if diffs.empty? if @read_reg_meta_supplied Origen.log.warning 'It looks like the miscompare(s) occurred on pins/cycles that are not associated with register data' non_data_miscompare = true else 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' end actual_data_available = false end end else 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' actual_data_available = false end # If a register object has been supplied... if read_flags Origen.log.error "Errors occurred reading register #{reg_or_val.path}:" if actual_data_available actual = nil reg_or_val.preserve_flags do reg_or_val.each_with_index do |bit, i| bit.read if read_flags[i] end diffs.each do |position, expected, received| if position < reg_or_val.size if received == -1 || received == -2 reg_or_val[position].unknown = true else reg_or_val[position].write(received, force: true) end else # This bit position is beyond the bounds of the register if @out_of_bounds_handler @out_of_bounds_handler.call(position, received, expected, reg_or_val) else Origen.log.error "bit[#{position}] of read operation on #{reg_or_val.path}.#{reg_or_val.name}: expected #{expected} received #{received}" end end end actual = bit_names.map do |name| bits = reg_or_val.bits(name) if bits.is_to_be_read? [name, bits.status_str(:read)] end end.compact # Put the data back so the application behaves as it would if generating # for a non-simulation tester target diffs.each do |position, expected, received| reg_or_val[position].write(expected, force: true) if position < reg_or_val.size end end end expected.each do |name, expected| msg = "#{reg_or_val.path}.#{name}: expected #{expected}" if actual_data_available received_ = nil actual.each do |name2, received| if name == name2 received_ = received msg += " received #{received}" end end if expected == received_ Origen.log.info msg else Origen.log.error msg end else # This means that the correct data was read, but errors occurred on other pins/cycles during the transaction msg += " received #{expected}" if non_data_miscompare Origen.log.error msg end end else Origen.log.error 'Errors occurred while reading a register:' msg = "expected #{reg_or_val.to_s(16).upcase}" if actual_data_available actual = reg_or_val diffs.each do |position, expected, received| if received == -1 || received == -2 actual = '?' * reg_or_val.to_s(16).size break elsif received == 1 actual |= (1 << position) else lower = actual[(position - 1)..0] actual = actual >> (position + 1) actual = actual << (position + 1) actual |= lower end end if actual.is_a?(String) msg += " received #{actual}" else msg += " received #{actual.to_s(16).upcase}" end else # This means that the correct data was read, but errors occurred on other pins/cycles during the transaction msg += " received #{reg_or_val.to_s(16).upcase}" if non_data_miscompare end Origen.log.error msg end Origen.log.error caller.each do |line| if Pathname.new(line.split(':').first)..to_s =~ /^#{Origen.root}(?!(\/lbin|\/vendor\/gems)).*$/ Origen.log.error line end end end end end |
#release(*args) ⇒ Object
Shorthand for simulator.release
494 495 496 |
# File 'lib/origen_sim/tester.rb', line 494 def release(*args) simulator.release(*args) end |
#set_timeset(name, period_in_ns) ⇒ Object
84 85 86 87 88 89 90 91 92 |
# File 'lib/origen_sim/tester.rb', line 84 def set_timeset(name, period_in_ns) super # Need to remove this once OrigenTesters does it dut.timeset = name dut.current_timeset_period = period_in_ns # Now update the simulator with the new waves simulator.on_timeset_changed end |
#simulator ⇒ Object
25 26 27 |
# File 'lib/origen_sim/tester.rb', line 25 def simulator OrigenSim.simulator end |
#ss(msg = nil) ⇒ Object
124 125 126 127 128 |
# File 'lib/origen_sim/tester.rb', line 124 def ss(msg = nil) simulator.log '=' * 70 super simulator.log '=' * 70 end |
#start ⇒ Object
Start the simulator
68 69 70 |
# File 'lib/origen_sim/tester.rb', line 68 def start simulator.start end |
#store_next_cycle(*pins) ⇒ Object
Capture the next vector generated
This method applies a store request to the next vector to be generated, note that is does not actually generate a new vector.
The captured data is added to the captured_data array.
This method is intended to be used by pin drivers, see the #capture method for the application level API.
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 |
# File 'lib/origen_sim/tester.rb', line 150 def store_next_cycle(*pins) = pins.last.is_a?(Hash) ? pins.pop : {} if pins.empty? pins = dut.rtl_pins.values else pins_orig = pins.dup pins_orig.each do |p| if p.is_a? Origen::Pins::PinCollection pins.concat(p.map(&:id).map { |p| dut.pin(p) }) pins.delete(p) end end end if simulator.sync_active? && @sync_cycles @sync_cycles += 1 pins.each do |pin| @sync_pins << pin unless @sync_pins.include?(pin) end end pins.each(&:capture) # A store request is only valid for one cycle, this tells the simulator # to stop after the next vector is generated after_next_vector do pins.each { |pin| simulator.put("h^#{pin.simulation_index}") } end end |
#sync_up ⇒ Object
Blocks the Origen process until the simulator indicates that it has processed all operations up to this point
74 75 76 |
# File 'lib/origen_sim/tester.rb', line 74 def sync_up simulator.sync_up end |
#wait(*args) ⇒ Object
267 268 269 270 271 272 273 |
# File 'lib/origen_sim/tester.rb', line 267 def wait(*args) super if Origen.running_interactively? || (defined?(Byebug) && Byebug.try(:mode) == :attached) flush quiet: true end end |