require 'origen_testers/api' module OrigenTesters module API # Returns true if the tester is an instance of OrigenSim::Tester, # otherwise returns false def sim? is_a?(OrigenSim::Tester) end alias_method :simulator?, :sim? def read_register(reg_or_val, options = {}) yield end # Set a marker in the OrigenSim testbench def marker=(val) simulator.marker = val if sim? end def sim_delay(id, options = {}, &block) if sim? && dut_version <= '0.12.0' OrigenSim.error "Use of sim_delay requires a DUT model compiled with OrigenSim version > 0.12.0, the current dut was compiled with #{dut_version}" end orig_id = id id = "delay_#{id}".to_sym # Just to make sure it is unique from the sim_capture IDs if @sim_capture || @sim_delay fail 'Nesting of sim_capture and/or sim_delay blocks is not yet supported!' end Origen::OrgFile.open(id, path: OrigenSim.capture_dir) do |org_file| @org_file = org_file if update_capture? @sim_delay = true # This enables errors to be captured in a separate variable so that they don't affect # the overall simulation result start_cycle = cycle_count delay = 0 simulator.match_loop do if options[:resolution] if options[:resolution].is_a?(Hash) resolution_in_cycles = time_to_cycles(options[:resolution]) else resolution_in_cycles = options[:resolution] end end timeout_in_cycles = time_to_cycles(options) e = -1 until (e == simulator.match_errors) || (timeout_in_cycles > 0 ? delay > timeout_in_cycles : false) delay = cycle_count - start_cycle e = simulator.match_errors pre_block_cycle = cycle_count block.call if resolution_in_cycles remaining = resolution_in_cycles - (cycle_count - pre_block_cycle) remaining.cycles if remaining > 0 else # Make sure time is advancing, the block does not necessarily have to advance time 1.cycle if pre_block_cycle == cycle_count end end end Origen.log.debug "sim_delay #{orig_id} resolved after #{delay} cycles" # We now know how long it took before the block could pass, now record that information # to the org file for next time org_file.record('tester', 'cycle') org_file.file # Need to call this since we are bypassing the regular capture API here Origen::OrgFile.cycle(delay) else unless org_file.exist? fail "The simulation delay \"#{orig_id}\" has not been simulated yet, re-run this pattern with a simulation target first!" end org_file.read_line do |operations, cycles| cycles.cycles end end end # Finally execute the block after waiting if options[:padding] time_to_cycles(options[:padding]).cycles end block.call @sim_delay = nil end def sim_capture(id, *pins) if @sim_capture || @sim_delay fail 'Nesting of sim_capture and/or sim_delay blocks is not yet supported!' end @sim_capture_id = id options = pins.last.is_a?(Hash) ? pins.pop : {} pins = pins.map { |p| p.is_a?(String) || p.is_a?(Symbol) ? dut.pin(p) : p } pins.each(&:save) @sim_capture = pins.map { |p| [p, "origen.dut.#{p.rtl_name}"] } Origen::OrgFile.open(id, path: OrigenSim.capture_dir) do |org_file| @org_file = org_file @update_capture = update_capture? if @update_capture @sim_capture.each { |pin, net| pin.record_to_org_file(only: :assert) } end yield end pins.each(&:restore) @sim_capture = nil end alias_method :_origen_testers_cycle, :cycle def cycle(options = {}) if @sim_capture # Need to un-roll all repeats to be sure we observe the true data, can't # really assume that it will be constant for all cycles covered by the repeat cycles = options.delete(:repeat) || 1 cycles.times do if @update_capture _origen_testers_cycle(options) @sim_capture.each do |pin, net| pin.assert(simulator.peek(net)) # Remove the assertion since it is for the previous cycle in terms of the current simulation, # this won't be captured to the org file pin.dont_care end Origen::OrgFile.cycle else unless @org_file.exist? fail "The simulation capture \"#{@sim_capture_id}\" has not been simulated yet, re-run this pattern with a simulation target first!" end apply_captured_data _origen_testers_cycle(options) end end else _origen_testers_cycle(options) end end def apply_captured_data if @apply_captured_data_cycles && @apply_captured_data_cycles > 1 @apply_captured_data_cycles -= 1 else @org_file.read_line do |operations, cycles| @apply_captured_data_cycles = cycles operations.each do |object, operation, *args| object.send(operation, *args) end end end end def update_capture? sim? && (!@org_file.exist? || Origen.app!.update_sim_captures) end def time_to_cycles(options) options = { cycles: 0, time_in_cycles: 0, time_in_us: 0, time_in_ns: 0, time_in_ms: 0, time_in_s: 0 }.merge(options) cycles = 0 cycles += options[:cycles] + options[:time_in_cycles] cycles += s_to_cycles(options[:time_in_s]) cycles += ms_to_cycles(options[:time_in_ms]) cycles += us_to_cycles(options[:time_in_us]) cycles += ns_to_cycles(options[:time_in_ns]) cycles end end end