require 'origen/specs'
module Origen
module PowerDomains
class PowerDomain
include Origen::Specs
attr_accessor :id, :description, :min, :max
attr_writer :maximum_voltage_rating, :nominal_voltage, :unit_voltage_range
# Generic Power Domain Name
# This is the power supply that can be blocked off to multiple power supplies
# For example, Power Domain for DDR blocks could be GVDD, then the actual
# power supplies can be different for each DDR block.
# DDR1 --> G1VDD
# DDR2 --> G2VDD
attr_accessor :generic_name
# Actual Power Domain Name
attr_accessor :actual_name
# Allowed Voltage Points
# Some power supplies can be at different levels, e.g. 1.8V or 3.3V
# Could be a signal voltage or an array of voltages
attr_accessor :voltages
# Display Names
# Hash of display names
# display_name = {
# input: Input voltage name, e.g. VIN
# output: Output voltage name, e.g. VOUT
# default: Regular Voltage name, e.g. VDD
attr_accessor :display_name
def initialize(id, options = {}, &block)
@id = id
@description = ''
@display_name = {}
@id = @id.symbolize unless @id.is_a? Symbol
options.each { |k, v| instance_variable_set("@#{k}", v) }
(block.arity < 1 ? (instance_eval(&block)) : block.call(self)) if block_given?
@unit_voltage_range = :fixed if @unit_voltage_range.nil?
fail unless attrs_ok?
create_dut_spec unless @min.nil? || @max.nil?
end
def name
@id
end
# Create DUT specs for the power supply
def create_dut_spec
if Origen.top_level.specs.nil?
set_specs
elsif Origen.top_level.specs.include? name
Origen.log.error("Cannot create power domain spec '#{name}', it already exists!")
fail
else
set_specs
end
end
# Maximum Voltage Rating
def maximum_voltage_rating
@maximum_voltage_rating
end
alias_method :mvr, :maximum_voltage_rating
# Sets setpoint equal to nominal_voltage
def setpoint_to_nominal
@setpoint = @nominal_voltage
end
# Returns an Array of all pins that reference the power domain
def pins
signal_pins + ground_pins + power_pins
end
# Returns an Array of signal pin IDs that match the power domain ID
def signal_pins
Origen.top_level.pins.select { |_pin_id, p| p.supply == id }.keys
end
# Returns an Array of ground pin IDs that match the power domain ID
def ground_pins
Origen.top_level.ground_pins.select { |_pin_id, p| p.supply == id }.keys
end
# Returns an Array of ground pin IDs that match the power domain ID
def power_pins
Origen.top_level.power_pins.select { |_pin_id, p| p.supply == id }.keys
end
# Checks for the existence of a signal pin that references the power domain
def has_signal_pin?(pin)
signal_pins.include?(pin) ? true : false
end
# Checks for the existence of a signal pin that references the power domain
def has_ground_pin?(pin)
ground_pins.include?(pin) ? true : false
end
# Checks for the existence of a signal pin that references the power domain
def has_power_pin?(pin)
power_pins.include?(pin) ? true : false
end
# Checks if a pin references the power domain, regardless of type
def has_pin?(pin)
pins.include? pin
end
# Checks for a pin type, returns nil if it is not found
def pin_type(pin)
if has_pin?(pin) == false
nil
else
[:signal, :ground, :power].each do |pintype|
return pintype if send("has_#{pintype}_pin?", pin)
end
end
end
# Nominal voltage
def nominal_voltage
@nominal_voltage
end
alias_method :nominal, :nominal_voltage
alias_method :nom, :nominal_voltage
# Current setpoint, defaults top nil on init
def setpoint
@setpoint
end
alias_method :curr_value, :setpoint
alias_method :value, :setpoint
# Power domain can allow either a variable
# or fixed unit voltage range (Range or :fixed)
def unit_voltage_range
@unit_voltage_range
end
alias_method :unit_range, :unit_voltage_range
# Setter for setpoint
def setpoint=(val)
unless setpoint_ok?(val)
Origen.log.warn("Setpoint (#{setpoint_string(val)}) for power domain '#{name}' is not within the voltage range (#{unit_voltage_range_string})!")
end
@setpoint = val
end
# Checks if the setpoint is valid
# This will need rework once the class has spec limits added
def setpoint_ok?(val = nil)
return true if maximum_voltage_rating.nil?
compare_val = val.nil? ? setpoint : val
if compare_val.nil?
false
elsif compare_val <= maximum_voltage_rating
true
else
false
end
end
alias_method :value_ok?, :setpoint_ok?
alias_method :val_ok?, :setpoint_ok?
def display_names(default_name)
@display_name[:default] = default_name
@display_name[:input] = change_subscript('IN')
@display_name[:output] = change_subscript('OUT')
end
def method_missing(m, *args, &block)
ivar = "@#{m.to_s.gsub('=', '')}"
ivar_sym = ":#{ivar}"
if m.to_s =~ /=$/
define_singleton_method(m) do |val|
instance_variable_set(ivar, val)
end
elsif instance_variables.include? ivar_sym
instance_variable_get(ivar)
else
define_singleton_method(m) do
instance_variable_get(ivar)
end
end
send(m, *args, &block)
end
private
def set_specs
Origen.top_level.spec name, :dc do |s|
s.description = "#{name.to_s.upcase} Power Domain"
s.min = min
s.typ = nominal_voltage
s.max = max
s.unit = 'V'
end
end
def attrs_ok?
return_value = true
unless description.is_a? String
Origen.log.error("Power domain attribute 'description' must be a String!")
return_value = false
end
return_value = false unless voltages_ok?
return_value
end
def setpoint_string(val = nil)
if val.nil?
setpoint.as_units('V')
else
val.as_units('V')
end
end
def voltages_ok?
if nominal_voltage.nil?
false
elsif unit_voltage_range == :fixed
true
elsif unit_voltage_range.nil?
Origen.log.error("PPEKit: Missing unit voltage range for power domain '#{name}'!")
false
elsif unit_voltage_range.is_a? Range
unless unit_voltage_range.include?(nominal_voltage)
Origen.log.error("PPEKit: Nominal voltage #{nominal_voltage} is not inbetween the unit voltage range #{unit_voltage_range} for power domain '#{name}'!")
false
end
unless maximum_voltage_rating.nil?
unless unit_voltage_range.last <= maximum_voltage_rating
Origen.log.error('PPEKit: Unit voltage range exceeds the maximum voltage range!')
fail
end
end
true
else
Origen.log.error("Power domain attribute 'unit_voltage_range' must be a Range or set to the value of :fixed!")
return_value = false
end
end
def change_subscript(new_subscript)
tmp = @display_name[:default].dup
sub_input = tmp.at_css 'sub'
sub_input.content = new_subscript unless sub_input.nil?
tmp
end
end
end
end