# Interface to VPI handles. #-- # Copyright 2006 Suraj N. Kurapati # See the file named LICENSE for details. require 'ruby-vpi/util' module VPI Handle = SWIG::TYPE_p_unsigned_int # A handle is an object inside a Verilog simulation (see # *vpiHandle* in IEEE Std. 1364-2005 for details). # # Nearly all methods of this class, such as put_value() # and get_value(), you allow you to specify VPI types # and properties (which are listed in ext/vpi_user.h) by # their names (strings or symbols) or integer constants. # # For example, the vpiIntVal property can be specified as a string # ("vpiIntVal"), a symbol (:vpiIntVal), or as # an integer (VpiIntVal or VPI::vpiIntVal). # class Handle include VPI #--------------------------------------------------------------------------- # testing & setting common logic values #--------------------------------------------------------------------------- # Tests if the integer value of this handle is 1. def vpi1? get_value(VpiScalarVal) == Vpi1 end # Sets the integer value of this handle to 1. def vpi1! put_value(Vpi1, VpiScalarVal) end alias t? vpi1? alias t! vpi1! # Tests if the integer value of this handle is 0. def vpi0? get_value(VpiScalarVal) == Vpi0 end # Sets the integer value of this handle to 0. def vpi0! put_value(Vpi0, VpiScalarVal) end alias f? vpi0? alias f! vpi0! # Tests if the logic value of this handle is unknown (x). def vpiX? get_value(VpiScalarVal) == VpiX end # Sets the logic value of this handle to unknown (x). def vpiX! put_value(VpiX, VpiScalarVal) end alias x? vpiX? alias x! vpiX! # Tests if the logic value of this handle is high impedance (z). def vpiZ? get_value(VpiScalarVal) == VpiZ end # Sets the logic value of this handle to high impedance (z). def vpiZ! put_value(VpiZ, VpiScalarVal) end alias z? vpiZ? alias z! vpiZ! # Tests if the strength value of this handle is high. def vpiH? get_value(VpiScalarVal) == VpiH end # Sets the strength value of this handle to high. def vpiH! put_value(VpiH, VpiScalarVal) end alias h? vpiH? alias h! vpiH! # Tests if the strength value of this handle is low. def vpiL? get_value(VpiScalarVal) == VpiL end # Sets the strength value of this handle to low. def vpiL! put_value(VpiL, VpiScalarVal) end alias l? vpiL? alias l! vpiL! # Inspects the given VPI property names, in # addition to those common to all handles. def inspect *aPropNames aPropNames.unshift :name, :fullName, :size, :file, :lineNo, :hexStrVal aPropNames.map! do |name| "#{name}=#{__send__(name).inspect}" end "#" end alias to_s inspect #--------------------------------------------------------------------------- # reading & writing values #--------------------------------------------------------------------------- # Reads the value using the given format (name or # integer constant) and returns a +S_vpi_value+ object. def get_value_wrapper aFormat fmt = resolve_prop_type(aFormat) val = S_vpi_value.new(:format => fmt) vpi_get_value(self, val) val end # Reads the value using the given format (name or integer constant) and # returns it. If a format is not given, then it is assumed to be VpiIntVal. def get_value aFormat = VpiIntVal fmt = resolve_prop_type(aFormat) @size ||= vpi_get(VpiSize, self) if fmt == VpiIntVal and @size > INTEGER_BITS fmt = VpiBinStrVal val = get_value_wrapper(fmt) val.read(fmt).gsub(/[^01]/, '0').to_i(2) else val = get_value_wrapper(fmt) val.read(fmt) end end # Writes the given value using the given format (name or integer # constant), time, and delay, and then returns the written value. # # * If you do not specify the format, then the Verilog # simulator will attempt to determine the correct format. # def put_value aValue, aFormat = nil, aTime = nil, aDelay = VpiNoDelay if vpi_get(VpiType, self) == VpiNet aDelay = VpiForceFlag if driver = self.to_a(VpiDriver).find {|d| vpi_get(VpiType, d) != VpiForce} warn "forcing value #{aValue.inspect} onto wire #{self} that is already driven by #{driver.inspect}" end end aFormat = if aFormat resolve_prop_type(aFormat) else S_vpi_value.detect_format(aValue) || get_value_wrapper(VpiObjTypeVal).format # let the simulator detect end if aFormat == VpiIntVal @size ||= vpi_get(VpiSize, self) unless @size < INTEGER_BITS aFormat = VpiHexStrVal aValue = aValue.to_i.to_s(16) end end aTime ||= S_vpi_time.new(:type => VpiSimTime, :integer => 0) wrapper = S_vpi_value.new(:format => aFormat) result = wrapper.write(aValue, aFormat) vpi_put_value(self, wrapper, aTime, aDelay) result end # Forces the given value (see arguments for #put_value) onto this handle. def force_value *args args[3] = VpiForceFlag put_value(*args) end # Releases all forced values on this handle (if any). def release_value # this doesn't really change the value, it only removes the force flag put_value(0, VpiIntVal, nil, VpiReleaseFlag) end # Tests if there is currently a value forced onto this handle. def force? self.to_a(VpiDriver).any? {|d| vpi_get(VpiType, d) == VpiForce} end #--------------------------------------------------------------------------- # accessing related handles / traversing the hierarchy #--------------------------------------------------------------------------- # Returns the child handle at the given relative VPI path. def / aRelativePath access_child(aRelativePath) end # Returns an array of child handles which have # the given types (names or integer constants). def to_a *aChildTypes handles = [] aChildTypes.each do |arg| t = resolve_prop_type(arg) if itr = vpi_iterate(t, self) while h = vpi_scan(itr) handles << h end end end handles end # inherit Enumerable methods, such as #each, #map, #select, etc. list = Enumerable.instance_methods list.delete 'to_a' list.push 'each' list.each do |meth| # using a string because define_method # does not accept a block until Ruby 1.9 class_eval %{ def #{meth}(*args, &block) if ary = self.to_a(*args) ary.#{meth}(&block) end end }, __FILE__, __LINE__ end # Sort by absolute VPI path. def <=> other get_value(VpiFullName) <=> other.get_value(VpiFullName) end #--------------------------------------------------------------------------- # accessing VPI properties #--------------------------------------------------------------------------- # Returns the value of the given VPI property # (name or integer constant) of this handle. def [] aProp access_prop(aProp) end @@propCache = Hash.new {|h,k| h[k] = Property.new(k)} undef id # deprecated in Ruby 1.8 undef type # used to access VpiType # Provides access to this handle's (1) child handles and (2) VPI # properties through method calls. In the case that a child handle # has the same name as a VPI property, the child handle will be # accessed instead of the VPI property. However, you can still # access the VPI property using the square brackets #[] method. def method_missing aMeth, *aArgs, &aBlock # cache the result for future accesses, in order # to cut down number of calls to method_missing() eigen_class = (class << self; self; end) if child = vpi_handle_by_name(aMeth.to_s, self) eigen_class.class_eval do define_method aMeth do child end end child else # XXX: using a string because define_method() does # not support a block argument until Ruby 1.9 eigen_class.class_eval %{ def #{aMeth}(*a, &b) access_prop(#{aMeth.inspect}, *a, &b) end }, __FILE__, __LINE__ __send__(aMeth, *aArgs, &aBlock) end end private def access_child aPath vpi_handle_by_name(aPath.to_s, self) end def access_prop aProp, *aArgs, &aBlock @@propCache[aProp.to_sym].execute(self, *aArgs, &aBlock) end class Property # :nodoc: attr_reader :name, :type, :accessor, :operation def initialize aMethName @methName = aMethName.to_s # parse property information from the given method name tokens = @methName.split('_') tokens.last.sub!(/[\?!=]$/, '') addendum = $& @isAssign = $& == '=' isQuery = $& == '?' tokens.last =~ /^[a-z]$/ && tokens.pop @accessor = $& @name = tokens.pop @operation = unless tokens.empty? tokens.join('_') << (addendum || '') end # determine the VPI integer type for the property @name = @name.to_ruby_const_name @name.insert 0, 'Vpi' unless @name =~ /^[Vv]pi/ begin @type = VPI.const_get(@name) rescue NameError raise ArgumentError, "#{@name.inspect} is not a valid VPI property" end @accessor = if @accessor @accessor.to_sym else # infer accessor from VPI property @name if isQuery :b else case @name when /Time$/ :d when /Val$/ :l when /Type$/, /Direction$/, /Index$/, /Size$/, /Strength\d?$/, /Polarity$/, /Edge$/, /Offset$/, /Mode$/, /LineNo$/ :i when /Is[A-Z]/, /ed$/ :b when /Name$/, /File$/, /Decompile$/ :s when /Parent$/, /Inst$/, /Range$/, /Driver$/, /Net$/, /Load$/, /Conn$/, /Bit$/, /Word$/, /[LR]hs$/, /(In|Out)$/, /Term$/, /Argument$/, /Condition$/, /Use$/, /Operand$/, /Stmt$/, /Expr$/, /Scope$/, /Memory$/, /Delay$/ :h end end end end def execute aHandle, *aArgs, &aBlockArg if @operation aHandle.__send__(@operation, @type, *aArgs, &aBlockArg) else case @accessor when :d # delay values raise NotImplementedError, 'processing of delay values is not yet implemented.' # TODO: vpi_put_delays # TODO: vpi_get_delays when :l # logic values if @isAssign value = aArgs.shift aHandle.put_value(value, @type, *aArgs) else aHandle.get_value(@type) end when :i # integer values if @isAssign raise NotImplementedError else vpi_get(@type, aHandle) end when :b # boolean values if @isAssign raise NotImplementedError else value = vpi_get(@type, aHandle) value && (value != 0) # zero is false in C end when :s # string values if @isAssign raise NotImplementedError else vpi_get_str(@type, aHandle) end when :h # handle values if @isAssign raise NotImplementedError else vpi_handle(@type, aHandle) end when :a # array of child handles if @isAssign raise NotImplementedError else aHandle.to_a(@type) end else raise NoMethodError, "cannot access VPI property #{@name.inspect} for handle #{aHandle.inspect} through method #{@methName.inspect} with arguments #{aArgs.inspect}" end end end end # resolve type names into type constants def resolve_prop_type aNameOrType if aNameOrType.respond_to? :to_int and not aNameOrType.is_a? Symbol aNameOrType.to_int else @@propCache[aNameOrType.to_sym].type end end end end