module Origen module Pins module Timing class Wave attr_reader :events, :timeset, :index # Returns the pattern code value associated with the wave. By default this will return nil # if no code was given at the time the wave was defined, which means it is the wave that will # be applied for the conventional code values of 0, 1, H, L. attr_reader :code VALID_DRIVE_DATA = [0, 1, :data] VALID_COMPARE_DATA = [0, 1, :data] def initialize(timeset, options = {}) @code = options[:code] @code = nil if [0, 1, 'H', 'L', :H, :L].include?(@code) @timeset = timeset @events = [] end # Returns the events array but with any formula based times # evaluated. # Note that this does not raise an error if the period is not currently # set, in that case any events that reference it will have nil for # their time. def evaluated_events if dut.current_timeset_period events.map { |e| [calc.evaluate(e[0], period: dut.current_timeset_period).ceil, e[1]] } else fail 'The current timeset period has not been set' end end # Returns an array containing all dut pin_ids that # are assigned to this wave by the parent timeset def pin_ids @pins_ids ||= timeset.send(:pin_ids_for, self) end # Returns an array containing all dut pin objects that # are assigned to this wave by the parent timeset def pins @pins ||= pin_ids.map { |id| dut.pin(id) } end def drive(data, options) self.type = :drive validate_data(data) do |d| validate_time(options) do |t| events << [t, d] end end end def compare(data, options) self.type = :compare validate_data(data) do |d| validate_time(options) do |t| events << [t, d] end end end alias_method :compare_edge, :compare def dont_care(options) self.type = :drive validate_time(options) do |t| events << [t, :x] end end alias_method :highz, :dont_care def type @type ||= :drive end def drive? @type == :drive end def compare? @type == :compare end private def clear_cache @pin_ids = nil @pins = nil end def index=(val) @index = val end def validate_data(data) valid = drive? ? VALID_DRIVE_DATA : VALID_COMPARE_DATA data = :data if :data == :pattern unless valid.include?(data) fail "Uknown data value #{data}, only these are valid: #{valid.join(', ')}" end yield data end def calc return @calc if @calc require 'dentaku' @calc = Dentaku::Calculator.new end def type=(t) if @type if @type != t fail 'Timing waves cannot both drive and compare within a cycle period!' end else @type = t end end def validate_time(options) unless options[:at] fail 'When defining a wave event you must supply the time via the option :at' end t = options[:at] if t.is_a?(String) d = calc.dependencies(t) - %w(period period_in_ns) unless d.empty? fail "Wave time formulas can only include the variable 'period' (or 'period_in_ns'), this variable is not allowed: #{d}" end t = t.gsub('period_in_ns', 'period') unless calc.evaluate(t, period: 100) fail "There appears to be an error in the formula: #{t}" end yield t return elsif t.is_a?(Numeric) yield t return end fail 'The :at option in a wave event definition must be either a number or a string' end end end end end