From 2f74b8a508d56753b7e806e32f09c61f1bb8b002 Mon Sep 17 00:00:00 2001 From: Keith Rothman <537074+litghost@users.noreply.github.com> Date: Mon, 6 May 2019 12:27:41 -0700 Subject: [PATCH 1/7] Add initial object and docstrings for timing model. Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com> --- prjxray/timing.py | 104 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 prjxray/timing.py diff --git a/prjxray/timing.py b/prjxray/timing.py new file mode 100644 index 00000000..75141443 --- /dev/null +++ b/prjxray/timing.py @@ -0,0 +1,104 @@ +""" Route timing delay definitions. + +Routing delay is formed from two parts in this model: + +- Intristic delay of the element +- Capactive loading delay of the net + +Intristic delay is a time value (e.g. nanoseconds), does not vary based on +routing fanout. It does vary based on the PVT (process, voltage, temperature) +corner. PvtCorner and IntristicDelay objects are used to model intristic +delay of elements. + +Capactive loading is the elmore delay from the RC tree formed by interconnect. +The RC tree is made up of 5 types of RC nodes: + +1. Out-pins +2. Buffer switches +3. Pass-through switches +4. Wires +5. In-pins + +Outpins have intristic delays and an driver resistence. + +Buffer switches have instrinsic delays, internal capacitance, and output driver +resistence. + +Pass-through switches have instrinsic delays and a resistence and capacitance. + +Wires have a resistence and capacitance. + +Inpins have instrinsic delays and a sink capacitance. + +The elmore delay is the RC tree formed by these 5 components. Out pins and +buffer switches are the roots of the elmore tree. Buffer switches +and inpins are leafs of the elmore tree. Wires and pass-through switches are +nodes in the tree, and share their capacitance upstream and downstream using a +pi-model. + +""" +import enum +from collections import namedtuple + +class PvtCorner(enum.Enum): + """ Process/voltage/temperature corner definitions. """ + + # Corner where device operates with fastest intristic delays. + FAST = "FAST" + + # Corner where device operates with slowest intristic delays. + SLOW = "SLOW" + +class IntristicDelay(namedtuple('IntristicDelay', 'min max')): + """ An intristic delay instance. + + Represents is the intristic delay through an element (e.g. a site pin or + interconnect pip). + + The intristic delay of an element is generally modelled at a particular + "corner" of a design. The "corner" generally speaking is modelled over + process, voltage and temperature PVT. The IntristicDelay object + reperesents the minimum or maximum delay through all instances of the + element at 1 corner. + + Attributes + ---------- + + min : float + Minimum instrinsic delay (nsec) + + max : str + Maximum instrinsic delay (nsec) + """ + +class RcElement(namedtuple('RcElement', 'resistence capacitance')): + """ One part of an RcNode, embedded within an RcTree. + + """ + pass + + +def fast_slow_tuple_to_corners(arr): + """ Convert delay 4-tuple into two IntristicDelay objects. + + Returns + ------- + + corners : dict of PvtCorner to IntristicDelay + Dictionary keys of FAST and SLOW, mapping to the instrinsic delay + for each corner. + + """ + + fast_min, fast_max, slow_min, slow_max = map(float, arr) + + return { + PvtCorner.FAST: IntristicDelay( + min=fast_min, + max=fast_max, + ), + PvtCorner.SLOW: IntristicDelay( + min=slow_min, + max=slow_max, + ), + } From 64798087b192d474d2c513b0b764552eb309fede Mon Sep 17 00:00:00 2001 From: Keith Rothman <537074+litghost@users.noreply.github.com> Date: Tue, 7 May 2019 10:57:35 -0700 Subject: [PATCH 2/7] Complete documentation for new timing model. Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com> --- prjxray/tile.py | 272 +++++++++++++++++++++++--- prjxray/timing.py | 478 ++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 703 insertions(+), 47 deletions(-) diff --git a/prjxray/tile.py b/prjxray/tile.py index d6f1ae6d..35c49aa1 100644 --- a/prjxray/tile.py +++ b/prjxray/tile.py @@ -1,38 +1,238 @@ +""" Database files available for a tile type. """ from collections import namedtuple import json from prjxray import lib -""" Database files available for a tile """ +from prjxray.timing import fast_slow_tuple_to_corners, RcElement + TileDbs = namedtuple( 'TileDbs', 'segbits block_ram_segbits ppips mask tile_type') -Pip = namedtuple( - 'Pip', 'name net_to net_from can_invert is_directional is_pseudo') -""" Site - Represents an instance of a site within a tile. +class OutpinTiming(namedtuple('OutpinTiming', 'delays drive_resistance')): + """ Timing for site output pins. -name - Name of site within tile, instance specific. -prefix - Prefix of site naming in Xilinx parlance. -type - What type of slice this instance presents. -pins - Instaces of site pins within this site and tile. This is an tuple of - SitePin tuples, and is specific to this instance of the site within - the tile. + Attributes + ---------- + delays : dicts of PvtCorner to IntristicDelay + Intristic delay of output pin. + drive_resistance : float + Resistance of drive output pin (milliOhms). -""" -Site = namedtuple('Site', 'name prefix x y type site_pins') -""" SitePin - Tuple representing a site pin within a tile. + """ + pass -Sites are generic based on type, however sites are instanced -within a tile 1 or more times. The SitePin contains both site type generic -information and tile type specific information. +class InpinTiming(namedtuple('InpinTiming', 'delays capacitance')): + """ Timing for site input pins. -name - Site type specific name. This name is expected to be the same for all - sites of the same type. -wire - Wire name within the tile. This name is site instance specific. + Attributes + ---------- + delays : dicts of PvtCorner to IntristicDelay + Intristic delay of input pin. + capacitance : float + Capacitance of input pints (microFarads). -""" -SitePin = namedtuple('SitePin', 'name wire') + """ + pass + +class PipTiming(namedtuple('PipTiming', 'delays drive_resistance internal_capacitance')): + """ Timing for pips. + + Attributes + ---------- + delays : dicts of PvtCorner to IntristicDelay + Intristic delay of pip. + internal_capacitance : float + Capacitance (microFarads) of pip (which is only seen if pip is used). + drive_resistance : float + Resistance of drive output pin (milliOhms). + + """ + pass + +class Pip(namedtuple( + 'Pip', ('name', 'net_to', 'net_from', 'can_invert', 'is_directional', + 'is_pseudo', 'is_pass_transitor', 'timing', 'backward_timing'))): + """ Pip information. + + Attributes + ---------- + + name : str + Name of pip + net_to : str + Name of output tile wire when pip is unidirectional. + net_from: str + Name of input tile wire when pip is unidirectional. + can_invert : bool + Can this pip invert the signal. + is_directional : bool + True if this pip is unidirectional, False if this pip is + unidirectional. + is_pseudo : bool + True if this pip is mark as a pseudo-pip. + is_pass_transitor : bool + True if this pip is non-isolating. + timing : PipTiming + Timing used when connecting net_from to net_to. This is the only + timing used when a pip is unidirectional. + + May be None if timing information is not present in the database. + backward_timing : PipTiming + Timing used when connecting net_to to net_from. This is only used + if the pip is bidirectional. + + May be None if timing information is not present in the database. + + """ + pass + +class Site(namedtuple('Site', 'name prefix x y type site_pins')): + """ Represents an instance of a site within a tile. + + Attributes + ---------- + name : str + Name of site within tile, instance specific. + prefix : str + Prefix of site naming in Xilinx parlance. + type : str + What type of slice this instance presents. + site_pins : list of SitePin + Instaces of site pins within this site and tile. This is an tuple of + SitePin tuples, and is specific to this instance of the site within + the tile. + + """ + +class SitePin(namedtuple('SitePin', 'name wire timing')): + """ Tuple representing a site pin within a tile. + + Sites are generic based on type, however sites are instanced + within a tile 1 or more times. The SitePin contains both site type generic + information and tile type specific information. + + Attributes + ---------- + name : str + Site type specific name. This name is expected to be the same for + all sites of the same type. + wire : str + Wire name within the tile. This name is site instance specific. + timing : Either InpinTiming or OutpinTiming + Timing of site pin. May be None if database lacks timing information. + + """ WireInfo = namedtuple('WireInfo', 'pips sites') +# Conversion factor from database to internal units. +RESISTANCE_FACTOR = 1 +CAPACITANCE_FACTOR = 1e3 + +def get_pip_timing(pip_timing_json): + """ Convert pip_timing_json JSON into PipTiming object. + + Returns + ------- + If timing information is not present for this pip, returns None. + If timing information is present, returns PipTiming. Some fields may be + None if the pip type lacks that field. + + """ + + if pip_timing_json is None: + return None + + delays = None + + if pip_timing_json.get('delay') is not None: + delays = fast_slow_tuple_to_corners(pip_timing_json.get('delay')) + + in_cap = pip_timing_json.get('in_cap') + if in_cap is not None: + in_cap = float(in_cap)/CAPACITANCE_FACTOR + else: + in_cap = 0 + + res = pip_timing_json.get('res') + if res is not None: + res = float(res)/RESISTANCE_FACTOR + else: + res = 0 + + return PipTiming( + delays=delays, drive_resistance=res, internal_capacitance=in_cap, + ) + + +def get_site_pin_timing(site_pin_info): + """ Convert site_pin_info JSON into InpinTiming or OutpinTiming object. + + Returns + ------- + If timing information is not present for this site pin, returns None. + If this is an output pin, returns OutpinTiming. + If this is an input pin, returns InpinTiming. + + """ + if isinstance(site_pin_info, str): + return site_pin_info, None + + wire = site_pin_info['wire'] + + if 'delay' not in site_pin_info: + return None + + delays = fast_slow_tuple_to_corners(site_pin_info['delay']) + + if 'cap' in site_pin_info: + assert 'res' not in site_pin_info + return wire, InpinTiming( + delays=delays, + capacitance=float(site_pin_info['cap'])/CAPACITANCE_FACTOR, + ) + else: + assert 'res' in site_pin_info + return wire, OutpinTiming( + delays=delays, + drive_resistance=float(site_pin_info['res'])/RESISTANCE_FACTOR, + ) + + +def get_wires(wires): + """ Converts database input to dictionary of tile wires to wire timing. + + Returns dictionary of tile wire name to RcElement or None. """ + + if isinstance(wires, list): + # Handle old database gracefully. + return {wire: None for wire in wires} + + output = {} + + for wire, rc_json in wires.items(): + if rc_json is None: + output[wire] = RcElement( + resistance=0, + capacitance=0, + ) + else: + output[wire] = RcElement( + resistance=float(rc_json['res'])/RESISTANCE_FACTOR, + capacitance=float(rc_json['cap'])/CAPACITANCE_FACTOR, + ) + + return output + +def is_pass_transitor(pip_json): + """ Returns boolean if pip JSON indicates pip is a pass transistor. + + Always returns False if database lacks this information. + """ + if 'is_pass_transitor' in pip_json: + return bool(int(pip_json['is_pass_transitor'])) + else: + return False + class Tile(object): """ Provides abstration of a tile in the database. """ @@ -49,17 +249,32 @@ class Tile(object): def yield_sites(sites): for site in sites: + site_pins = [] + for name, site_pin_info in site['site_pins'].items(): + if site_pin_info is not None: + wire, timing = get_site_pin_timing(site_pin_info) + site_pins.append( + SitePin( + name=name, + wire=wire, + timing=timing, + )) + else: + site_pins.append( + SitePin( + name=name, + wire=None, + timing=None, + )) + yield Site( name=site['name'], prefix=site['prefix'], type=site['type'], x=site['x_coord'], y=site['y_coord'], - site_pins=tuple( - SitePin( - name=name, - wire=wire, - ) for name, wire in site['site_pins'].items())) + site_pins=site_pins, + ) def yield_pips(pips): for name, pip in pips.items(): @@ -70,12 +285,15 @@ class Tile(object): can_invert=bool(int(pip['can_invert'])), is_directional=bool(int(pip['is_directional'])), is_pseudo=bool(int(pip['is_pseudo'])), + is_pass_transitor=is_pass_transitor(pip), + timing=get_pip_timing(pip.get('src_to_dst')), + backward_timing=get_pip_timing(pip.get('dst_to_src')), ) with open(self.tile_dbs.tile_type) as f: tile_type = json.load(f) assert self.tilename_upper == tile_type['tile_type'] - self.wires = tile_type['wires'] + self.wires = get_wires(tile_type['wires']) self.sites = tuple(yield_sites(tile_type['sites'])) self.pips = tuple(yield_pips(tile_type['pips'])) diff --git a/prjxray/timing.py b/prjxray/timing.py index 75141443..73a52c7d 100644 --- a/prjxray/timing.py +++ b/prjxray/timing.py @@ -13,28 +13,20 @@ delay of elements. Capactive loading is the elmore delay from the RC tree formed by interconnect. The RC tree is made up of 5 types of RC nodes: -1. Out-pins -2. Buffer switches -3. Pass-through switches -4. Wires -5. In-pins -Outpins have intristic delays and an driver resistence. - -Buffer switches have instrinsic delays, internal capacitance, and output driver -resistence. - -Pass-through switches have instrinsic delays and a resistence and capacitance. - -Wires have a resistence and capacitance. - -Inpins have instrinsic delays and a sink capacitance. +|Element type |Object |Intrinsic delays?|Output resistance?|Capacitance type | +|----------------------|--------------|-----------------|------------------|--------------------| +|Site output pin |Outpin |Yes |Yes |N/A | +|Buffered switch |Buffer |Yes |Yes |Internal capacitance| +|Pass-transistor switch|PassTransistor|Yes |Yes |N/A | +|Wire |Wire |No |Yes |Pi model | +|Site input pin |Inpin |Yes |No |Input capacitance | The elmore delay is the RC tree formed by these 5 components. Out pins and buffer switches are the roots of the elmore tree. Buffer switches -and inpins are leafs of the elmore tree. Wires and pass-through switches are -nodes in the tree, and share their capacitance upstream and downstream using a -pi-model. +and inpins are leafs of the elmore tree. Wires and pass-transistor switches are +nodes in the tree. Wires share their capacitance upstream and downstream using +a pi-model. """ import enum @@ -67,13 +59,22 @@ class IntristicDelay(namedtuple('IntristicDelay', 'min max')): min : float Minimum instrinsic delay (nsec) - max : str + max : float Maximum instrinsic delay (nsec) """ -class RcElement(namedtuple('RcElement', 'resistence capacitance')): +class RcElement(namedtuple('RcElement', 'resistance capacitance')): """ One part of an RcNode, embedded within an RcTree. + Attributes + ---------- + + resistance : float + Resistance of element (milliOhms) + + capacitance : float + Capacitance of element (microFarads) + """ pass @@ -102,3 +103,440 @@ def fast_slow_tuple_to_corners(arr): max=slow_max, ), } + + +# Math models are used to represent abstract operations for the timing models. +# This is useful for creating excel workbooks that can update dynamically, or +# generating a model with symbolic constants to be backsolved. + +class ExcelMathModel(object): + """ Math model used for outputting to an dyanmic Excel sheet. """ + def __init__(self): + pass + + def sum(self, elems): + sum_val = '(' + ' + '.join(elems) + ')' + if sum_val == '()': + return '0' + else: + return sum_val + + def product(self, elems): + sum_val = '(' + ' * '.join(elems) + ')' + if sum_val == '()': + return '1' + else: + return sum_val + + def plus(self, a, b): + return self.sum((a, b)) + + def divide(self, a, b): + return '({}/{})'.format(a, b) + + def multiply(self, a, b): + return '({}*{})'.format(a, b) + + def eval(self, elem): + return '=' + elem + + +def PythonMathModel(object): + """ Math model used for outputting values equalated immediately. """ + def __init__(self): + pass + + def sum(self, elems): + return sum(elems) + + def product(self, elems): + v = 1.0 + for elem in elems: + v *= elem + return v + + def divide(self, a, b): + return a/b + + def plus(self, a, b): + return a+b + + def multiply(self, a, b): + return a*b + + def eval(self, elem): + return elem + +class TimingNode(object): + """ Base class for timing node models. + """ + + def get_intrinsic_delays(self): + """ Returns Intristic delays (if any) timing node. + + Returns + ------- + + Dictionary of PvtCorner to Intristic. Is None if node has no intristic + delay. + """ + + pass + + def get_rc_delay(self): + """ Return portion of net delay due to elmore (RC) delay at this node. + + Must be called after propigate_delays has been called on the Outpin + object of this tree. + + """ + pass + + def get_downstream_cap(self): + """ Returns downstream capacitance at this node. + + Must be called after propigate_delays has been called on the Outpin + object of this tree. + + """ + pass + + def propigate_downstream_capacitance(self, math): + """ Returns capacitance visible to parent of this node. + + Must call propigate_downstream_capacitance on all children of this node. + Should save downstream capacitance visible to this node's output + (if any) to be returned in the get_downstream_cap method. + """ + pass + + +class DownstreamNode(TimingNode): + """ All non-root TimingNode's are DownstreamNode's. + + """ + def propigate_delays(self, elements, math): + """ Propigates upstream delay elements to children of the tree. + + Must call propigated_delays on all children of this node, and add this + node to elements. + + Arguments + --------- + elements : list of TimingNode's + List of delay nodes between root of this tree and this node. + math : MathModel + Math model to use to compute delays + """ + + +class Outpin(TimingNode): + """ Represents a site output pin. + + Outpin object is the root of the timing tree. Once tree is built with + set_sink_wire and Wire.add_child methods, propigate_delays should be + invoked to estabilish model. + + Arguments + --------- + + resistance + Drive resistance in elmore delay model + delays + Intristic delays on output pin. + + """ + + def __init__(self, resistance, delays): + self.resistance = resistance + self.delays = delays + self.sink_wire = None + + self.downstream_cap = None + self.rc_delay = None + + def set_sink_wire(self, wire): + """ Sets sink wire for this output pin. + + An output pin always sinks to exactly 1 wire. + + This method must be called prior to calling propigate_delays method + on this object. + + Arguments + --------- + wire : Wire object + Sink wire for this output pin. + + """ + self.sink_wire = wire + + def propigate_downstream_capacitance(self, math): + assert self.sink_wire is not None + self.downstream_cap = self.sink_wire.propigate_downstream_capacitance(math) + self.rc_delay = math.multiply(self.downstream_cap, self.resistance) + + def propigate_delays(self, math): + """ Propigate delays throughout tree using specified math model. + + Must be called after elmore tree is estabilished. + + Arguments + --------- + math : MathModel object + Math model used when doing timing computations. + + """ + self.propigate_downstream_capacitance(math) + self.sink_wire.propigate_delays([self], math) + self.rc_delay = math.multiply(self.resistance, self.downstream_cap) + + def get_intrinsic_delays(self): + return self.delays + + def get_rc_delay(self): + assert self.rc_delay is not None + return self.rc_delay + + def get_downstream_cap(self): + assert self.downstream_cap is not None + return self.downstream_cap + + +class Inpin(DownstreamNode): + """ Represents a site input pin. + + Represents leaf of timing model. Once model is connected and delays + are propigate (by calling Outpin,propigated_delays), get_delays will + correctly return the list of delay elements from the root to this leaf. + + Arguments + --------- + + capacitance + Pin capacitance for input pin. + delays + Intristic delays on input pin. + + """ + def __init__(self, capacitance, delays, name=None): + self.capacitance = capacitance + self.delays = delays + self.propigated_delays = None + self.name = name + + def get_intrinsic_delays(self): + return self.delays + + def get_rc_delay(self): + return None + + def get_downstream_cap(self): + return None + + def propigate_downstream_capacitance(self, math): + return self.capacitance + + def propigate_delays(self, elements, math): + self.propigated_delays = list(elements) + + def get_delays(self): + """ Return list of delay models that make up the delay for this pin. + + The sum of all delay elements (both intristic and RC) is the net + delay from the output pin to this input pin. + + """ + return self.propigated_delays + [self] + + +class Wire(DownstreamNode): + """ Represents a wire in the timing model. + + Wires must be connected to an upstream driver model (Outpin, Buffer, + PassTransistor objects) with set_sink_wire, and add_child must be called + attaching output nodes (Buffer, PassTransistor, Inpin objects). + + Arguments + --------- + rc_elements : List of RcElement + Resistance and capacitance of this wire. + math : MathModel + Math model used to compute lumped resistance and capacitance. + + """ + def __init__(self, rc_elements, math): + self.resistance = math.sum(elem.resistance for elem in rc_elements) + self.capacitance = math.sum(elem.capacitance for elem in rc_elements) + self.children = [] + + self.downstream_cap = None + self.propigated_delays = None + self.rc_delay = None + + def add_child(self, child): + """ Add a child node to this wire. + + Call this method as needed prior to calling propigate_delays on the + root Outpin object. + + Arguments + --------- + child : Buffer or PassTransistor or Inpin + Adds child load to this wire. + + """ + self.children.append(child) + + def propigate_downstream_capacitance(self, math): + downstream_cap = math.sum(child.propigate_downstream_capacitance(math) for child in self.children) + + # Pi-model is definied such that wire resistance only sees half of the + # wire capacitance. + self.downstream_cap = math.plus(math.divide(self.capacitance, 2), downstream_cap) + + # Upstream seems all of the wires capacitance + return math.plus(downstream_cap, self.capacitance) + + def propigate_delays(self, elements, math): + self.propigated_delays = list(elements) + + for child in self.children: + child.propigate_delays(self.propigated_delays + [self], math) + + self.rc_delay = math.multiply(self.resistance, self.downstream_cap) + + def get_intrinsic_delays(self): + return None + + def get_rc_delay(self): + assert self.rc_delay is not None + return self.rc_delay + + def get_downstream_cap(self): + assert self.downstream_cap is not None + return self.downstream_cap + +class Buffer(DownstreamNode): + """ Represents an isolating switch. + + The internal_capacitance model is such that the upstream node only sees + the capacitance of this node when the switch is enabled. Therefore, only + active buffers should be included in the model. + + Arguments + --------- + internal_capacitance + Capacitance seen by upstream node when this buffer is enabled. + drive_resistance + Driver resistance used for computing elmore delay. + delays : Dictionary of PvtCorner to IntristicDelay + Delay through switch + + """ + def __init__(self, internal_capacitance, drive_resistance, delays): + self.internal_capacitance = internal_capacitance + self.drive_resistance = drive_resistance + self.delays = delays + + self.downstream_cap = None + self.rc_delay = None + + def set_sink_wire(self, wire): + """ Sets sink wire for this output pin. + + An output pin always sinks to exactly 1 wire. + + This method must be called prior to calling propigate_delays method + on the root Outpin object of this tree. + + Arguments + --------- + wire : Wire object + Sink wire for this output pin. + + """ + self.sink_wire = wire + + def propigate_downstream_capacitance(self, math): + assert self.sink_wire is not None + self.downstream_cap = self.sink_wire.propigate_downstream_capacitance(math) + return self.internal_capacitance + + def propigate_delays(self, elements, math): + self.propigated_delays = list(elements) + + assert self.sink_wire is not None + self.sink_wire.propigate_delays(self.propigated_delays + [self], math) + self.rc_delay = math.multiply(self.downstream_cap, self.drive_resistance) + + def get_intrinsic_delays(self): + return self.delays + + def get_rc_delay(self): + assert self.rc_delay is not None + return self.rc_delay + + def get_downstream_cap(self): + assert self.downstream_cap is not None + return self.downstream_cap + + +class PassTransistor(DownstreamNode): + """ Represents a non-isolating switch. + + Arguments + --------- + drive_resistance + Driver resistance used for computing elmore delay. + delays : Dictionary of PvtCorner to IntristicDelay + Delay through switch. + + """ + def __init__(self, drive_resistance, delays): + self.drive_resistance = drive_resistance + self.delays = delays + self.sink_wire = None + + self.downstream_cap = None + self.rc_delay = None + + def set_sink_wire(self, wire): + """ Sets sink wire for this output pin. + + An output pin always sinks to exactly 1 wire. + + This method must be called prior to calling propigate_delays method + on the root Outpin object of this tree. + + Arguments + --------- + wire : Wire object + Sink wire for this output pin. + + """ + self.sink_wire = wire + + def propigate_downstream_capacitance(self, math): + assert self.sink_wire is not None + self.downstream_cap = self.sink_wire.propigate_downstream_capacitance(math) + + return self.downstream_cap + + def propigate_delays(self, elements, math): + self.propigated_delays = list(elements) + + assert self.sink_wire is not None + self.sink_wire.propigate_delays(self.propigated_delays + [self], math) + self.rc_delay = math.multiply(self.downstream_cap, self.drive_resistance) + + def get_intrinsic_delays(self): + return self.delays + + def get_rc_delay(self): + assert self.rc_delay is not None + return self.rc_delay + + def get_downstream_cap(self): + assert self.downstream_cap is not None + return self.downstream_cap From b14952e1da3a6b1599f1cd53b23dcf60a167889b Mon Sep 17 00:00:00 2001 From: Keith Rothman <537074+litghost@users.noreply.github.com> Date: Tue, 7 May 2019 12:12:36 -0700 Subject: [PATCH 3/7] Add example timing tree. Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com> --- prjxray/timing.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/prjxray/timing.py b/prjxray/timing.py index 73a52c7d..57d3ccd4 100644 --- a/prjxray/timing.py +++ b/prjxray/timing.py @@ -28,6 +28,34 @@ and inpins are leafs of the elmore tree. Wires and pass-transistor switches are nodes in the tree. Wires share their capacitance upstream and downstream using a pi-model. +Example timing tree: + ++------+ +|Outpin| ++--+---+ + | + | + v ++--+--+ +|Wire | ++--+--+ + | + +-----------------+ + | | ++--+---+ +-------+------+ +|Buffer| |PassTransistor| ++--+---+ +------+-------+ + | | + v v ++--+-+ +--+-+ +|Wire| |Wire| ++--+-+ +--+-+ + | | + v v ++--+--+ +--+--+ +|Inpin| |Inpin| ++-----+ +-----+ + """ import enum from collections import namedtuple From af8c1884d44a40d10b8aee643a26f83458ea1fcc Mon Sep 17 00:00:00 2001 From: Keith Rothman <537074+litghost@users.noreply.github.com> Date: Tue, 7 May 2019 12:37:14 -0700 Subject: [PATCH 4/7] Run make format. Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com> --- prjxray/tile.py | 73 ++++++++++++++++++++++++++++------------------- prjxray/timing.py | 59 ++++++++++++++++++++++++++------------ 2 files changed, 83 insertions(+), 49 deletions(-) diff --git a/prjxray/tile.py b/prjxray/tile.py index 35c49aa1..f6339794 100644 --- a/prjxray/tile.py +++ b/prjxray/tile.py @@ -7,6 +7,7 @@ from prjxray.timing import fast_slow_tuple_to_corners, RcElement TileDbs = namedtuple( 'TileDbs', 'segbits block_ram_segbits ppips mask tile_type') + class OutpinTiming(namedtuple('OutpinTiming', 'delays drive_resistance')): """ Timing for site output pins. @@ -20,6 +21,7 @@ class OutpinTiming(namedtuple('OutpinTiming', 'delays drive_resistance')): """ pass + class InpinTiming(namedtuple('InpinTiming', 'delays capacitance')): """ Timing for site input pins. @@ -33,7 +35,9 @@ class InpinTiming(namedtuple('InpinTiming', 'delays capacitance')): """ pass -class PipTiming(namedtuple('PipTiming', 'delays drive_resistance internal_capacitance')): + +class PipTiming(namedtuple('PipTiming', + 'delays drive_resistance internal_capacitance')): """ Timing for pips. Attributes @@ -48,9 +52,11 @@ class PipTiming(namedtuple('PipTiming', 'delays drive_resistance internal_capaci """ pass + class Pip(namedtuple( - 'Pip', ('name', 'net_to', 'net_from', 'can_invert', 'is_directional', - 'is_pseudo', 'is_pass_transitor', 'timing', 'backward_timing'))): + 'Pip', + ('name', 'net_to', 'net_from', 'can_invert', 'is_directional', 'is_pseudo', + 'is_pass_transitor', 'timing', 'backward_timing'))): """ Pip information. Attributes @@ -85,6 +91,7 @@ class Pip(namedtuple( """ pass + class Site(namedtuple('Site', 'name prefix x y type site_pins')): """ Represents an instance of a site within a tile. @@ -103,6 +110,7 @@ class Site(namedtuple('Site', 'name prefix x y type site_pins')): """ + class SitePin(namedtuple('SitePin', 'name wire timing')): """ Tuple representing a site pin within a tile. @@ -122,12 +130,14 @@ class SitePin(namedtuple('SitePin', 'name wire timing')): """ + WireInfo = namedtuple('WireInfo', 'pips sites') # Conversion factor from database to internal units. RESISTANCE_FACTOR = 1 CAPACITANCE_FACTOR = 1e3 + def get_pip_timing(pip_timing_json): """ Convert pip_timing_json JSON into PipTiming object. @@ -149,19 +159,21 @@ def get_pip_timing(pip_timing_json): in_cap = pip_timing_json.get('in_cap') if in_cap is not None: - in_cap = float(in_cap)/CAPACITANCE_FACTOR + in_cap = float(in_cap) / CAPACITANCE_FACTOR else: in_cap = 0 res = pip_timing_json.get('res') if res is not None: - res = float(res)/RESISTANCE_FACTOR + res = float(res) / RESISTANCE_FACTOR else: res = 0 return PipTiming( - delays=delays, drive_resistance=res, internal_capacitance=in_cap, - ) + delays=delays, + drive_resistance=res, + internal_capacitance=in_cap, + ) def get_site_pin_timing(site_pin_info): @@ -187,15 +199,15 @@ def get_site_pin_timing(site_pin_info): if 'cap' in site_pin_info: assert 'res' not in site_pin_info return wire, InpinTiming( - delays=delays, - capacitance=float(site_pin_info['cap'])/CAPACITANCE_FACTOR, - ) + delays=delays, + capacitance=float(site_pin_info['cap']) / CAPACITANCE_FACTOR, + ) else: assert 'res' in site_pin_info return wire, OutpinTiming( - delays=delays, - drive_resistance=float(site_pin_info['res'])/RESISTANCE_FACTOR, - ) + delays=delays, + drive_resistance=float(site_pin_info['res']) / RESISTANCE_FACTOR, + ) def get_wires(wires): @@ -212,17 +224,18 @@ def get_wires(wires): for wire, rc_json in wires.items(): if rc_json is None: output[wire] = RcElement( - resistance=0, - capacitance=0, - ) + resistance=0, + capacitance=0, + ) else: output[wire] = RcElement( - resistance=float(rc_json['res'])/RESISTANCE_FACTOR, - capacitance=float(rc_json['cap'])/CAPACITANCE_FACTOR, - ) + resistance=float(rc_json['res']) / RESISTANCE_FACTOR, + capacitance=float(rc_json['cap']) / CAPACITANCE_FACTOR, + ) return output + def is_pass_transitor(pip_json): """ Returns boolean if pip JSON indicates pip is a pass transistor. @@ -254,18 +267,18 @@ class Tile(object): if site_pin_info is not None: wire, timing = get_site_pin_timing(site_pin_info) site_pins.append( - SitePin( - name=name, - wire=wire, - timing=timing, - )) + SitePin( + name=name, + wire=wire, + timing=timing, + )) else: site_pins.append( - SitePin( - name=name, - wire=None, - timing=None, - )) + SitePin( + name=name, + wire=None, + timing=None, + )) yield Site( name=site['name'], @@ -274,7 +287,7 @@ class Tile(object): x=site['x_coord'], y=site['y_coord'], site_pins=site_pins, - ) + ) def yield_pips(pips): for name, pip in pips.items(): diff --git a/prjxray/timing.py b/prjxray/timing.py index 57d3ccd4..f28b8795 100644 --- a/prjxray/timing.py +++ b/prjxray/timing.py @@ -60,6 +60,7 @@ Example timing tree: import enum from collections import namedtuple + class PvtCorner(enum.Enum): """ Process/voltage/temperature corner definitions. """ @@ -69,6 +70,7 @@ class PvtCorner(enum.Enum): # Corner where device operates with slowest intristic delays. SLOW = "SLOW" + class IntristicDelay(namedtuple('IntristicDelay', 'min max')): """ An intristic delay instance. @@ -91,6 +93,7 @@ class IntristicDelay(namedtuple('IntristicDelay', 'min max')): Maximum instrinsic delay (nsec) """ + class RcElement(namedtuple('RcElement', 'resistance capacitance')): """ One part of an RcNode, embedded within an RcTree. @@ -122,23 +125,25 @@ def fast_slow_tuple_to_corners(arr): fast_min, fast_max, slow_min, slow_max = map(float, arr) return { - PvtCorner.FAST: IntristicDelay( - min=fast_min, - max=fast_max, - ), - PvtCorner.SLOW: IntristicDelay( - min=slow_min, - max=slow_max, - ), - } + PvtCorner.FAST: IntristicDelay( + min=fast_min, + max=fast_max, + ), + PvtCorner.SLOW: IntristicDelay( + min=slow_min, + max=slow_max, + ), + } # Math models are used to represent abstract operations for the timing models. # This is useful for creating excel workbooks that can update dynamically, or # generating a model with symbolic constants to be backsolved. + class ExcelMathModel(object): """ Math model used for outputting to an dyanmic Excel sheet. """ + def __init__(self): pass @@ -171,6 +176,7 @@ class ExcelMathModel(object): def PythonMathModel(object): """ Math model used for outputting values equalated immediately. """ + def __init__(self): pass @@ -184,17 +190,18 @@ def PythonMathModel(object): return v def divide(self, a, b): - return a/b + return a / b def plus(self, a, b): - return a+b + return a + b def multiply(self, a, b): - return a*b + return a * b def eval(self, elem): return elem + class TimingNode(object): """ Base class for timing node models. """ @@ -243,6 +250,7 @@ class DownstreamNode(TimingNode): """ All non-root TimingNode's are DownstreamNode's. """ + def propigate_delays(self, elements, math): """ Propigates upstream delay elements to children of the tree. @@ -301,7 +309,8 @@ class Outpin(TimingNode): def propigate_downstream_capacitance(self, math): assert self.sink_wire is not None - self.downstream_cap = self.sink_wire.propigate_downstream_capacitance(math) + self.downstream_cap = self.sink_wire.propigate_downstream_capacitance( + math) self.rc_delay = math.multiply(self.downstream_cap, self.resistance) def propigate_delays(self, math): @@ -347,6 +356,7 @@ class Inpin(DownstreamNode): Intristic delays on input pin. """ + def __init__(self, capacitance, delays, name=None): self.capacitance = capacitance self.delays = delays @@ -393,6 +403,7 @@ class Wire(DownstreamNode): Math model used to compute lumped resistance and capacitance. """ + def __init__(self, rc_elements, math): self.resistance = math.sum(elem.resistance for elem in rc_elements) self.capacitance = math.sum(elem.capacitance for elem in rc_elements) @@ -417,11 +428,14 @@ class Wire(DownstreamNode): self.children.append(child) def propigate_downstream_capacitance(self, math): - downstream_cap = math.sum(child.propigate_downstream_capacitance(math) for child in self.children) + downstream_cap = math.sum( + child.propigate_downstream_capacitance(math) + for child in self.children) # Pi-model is definied such that wire resistance only sees half of the # wire capacitance. - self.downstream_cap = math.plus(math.divide(self.capacitance, 2), downstream_cap) + self.downstream_cap = math.plus( + math.divide(self.capacitance, 2), downstream_cap) # Upstream seems all of the wires capacitance return math.plus(downstream_cap, self.capacitance) @@ -445,6 +459,7 @@ class Wire(DownstreamNode): assert self.downstream_cap is not None return self.downstream_cap + class Buffer(DownstreamNode): """ Represents an isolating switch. @@ -462,6 +477,7 @@ class Buffer(DownstreamNode): Delay through switch """ + def __init__(self, internal_capacitance, drive_resistance, delays): self.internal_capacitance = internal_capacitance self.drive_resistance = drive_resistance @@ -488,7 +504,8 @@ class Buffer(DownstreamNode): def propigate_downstream_capacitance(self, math): assert self.sink_wire is not None - self.downstream_cap = self.sink_wire.propigate_downstream_capacitance(math) + self.downstream_cap = self.sink_wire.propigate_downstream_capacitance( + math) return self.internal_capacitance def propigate_delays(self, elements, math): @@ -496,7 +513,8 @@ class Buffer(DownstreamNode): assert self.sink_wire is not None self.sink_wire.propigate_delays(self.propigated_delays + [self], math) - self.rc_delay = math.multiply(self.downstream_cap, self.drive_resistance) + self.rc_delay = math.multiply( + self.downstream_cap, self.drive_resistance) def get_intrinsic_delays(self): return self.delays @@ -521,6 +539,7 @@ class PassTransistor(DownstreamNode): Delay through switch. """ + def __init__(self, drive_resistance, delays): self.drive_resistance = drive_resistance self.delays = delays @@ -547,7 +566,8 @@ class PassTransistor(DownstreamNode): def propigate_downstream_capacitance(self, math): assert self.sink_wire is not None - self.downstream_cap = self.sink_wire.propigate_downstream_capacitance(math) + self.downstream_cap = self.sink_wire.propigate_downstream_capacitance( + math) return self.downstream_cap @@ -556,7 +576,8 @@ class PassTransistor(DownstreamNode): assert self.sink_wire is not None self.sink_wire.propigate_delays(self.propigated_delays + [self], math) - self.rc_delay = math.multiply(self.downstream_cap, self.drive_resistance) + self.rc_delay = math.multiply( + self.downstream_cap, self.drive_resistance) def get_intrinsic_delays(self): return self.delays From b5b9efe29ab487af309c967bec86da252d171b84 Mon Sep 17 00:00:00 2001 From: Keith Rothman <537074+litghost@users.noreply.github.com> Date: Tue, 7 May 2019 14:45:08 -0700 Subject: [PATCH 5/7] Update units to reflect actually constraint. Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com> --- prjxray/timing.py | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/prjxray/timing.py b/prjxray/timing.py index f28b8795..2098d4d1 100644 --- a/prjxray/timing.py +++ b/prjxray/timing.py @@ -56,6 +56,30 @@ Example timing tree: |Inpin| |Inpin| +-----+ +-----+ +Note on units: + +The timing model operates on the following types of units: + - Time + - Resistance + - Capacitance + +For a consistent unit set, the following equation must be satisfied: + +1 Resistance unit * 1 Capacitance unit = 1 Time unit + +The SI unit set would be: + - Time = seconds + - Resistance = Ohms + - Capacitance = Farads + +However as long as the scale factors are consistent, the model will work +with other unit combinations. For example: + - Time = nanoseconds (1e-9 seconds) + - Resistance = milliOhms (1e-3 Ohms) + - Capacitance = microFarads (1e-6 Farads) + +(1e-3 * 1e-6) (Ohms * Farads) does equal (1e-9) seconds. + """ import enum from collections import namedtuple @@ -101,10 +125,10 @@ class RcElement(namedtuple('RcElement', 'resistance capacitance')): ---------- resistance : float - Resistance of element (milliOhms) + Resistance of element capacitance : float - Capacitance of element (microFarads) + Capacitance of element """ pass From 3838a643cfece995bcc995fae18cd5b0c382f2e5 Mon Sep 17 00:00:00 2001 From: Keith Rothman <537074+litghost@users.noreply.github.com> Date: Tue, 7 May 2019 15:55:27 -0700 Subject: [PATCH 6/7] Fix spelling. Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com> --- prjxray/tile.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/prjxray/tile.py b/prjxray/tile.py index f6339794..e6e01660 100644 --- a/prjxray/tile.py +++ b/prjxray/tile.py @@ -56,7 +56,7 @@ class PipTiming(namedtuple('PipTiming', class Pip(namedtuple( 'Pip', ('name', 'net_to', 'net_from', 'can_invert', 'is_directional', 'is_pseudo', - 'is_pass_transitor', 'timing', 'backward_timing'))): + 'is_pass_transistor', 'timing', 'backward_timing'))): """ Pip information. Attributes @@ -75,7 +75,7 @@ class Pip(namedtuple( unidirectional. is_pseudo : bool True if this pip is mark as a pseudo-pip. - is_pass_transitor : bool + is_pass_transistor : bool True if this pip is non-isolating. timing : PipTiming Timing used when connecting net_from to net_to. This is the only @@ -236,13 +236,13 @@ def get_wires(wires): return output -def is_pass_transitor(pip_json): +def is_pass_transistor(pip_json): """ Returns boolean if pip JSON indicates pip is a pass transistor. Always returns False if database lacks this information. """ - if 'is_pass_transitor' in pip_json: - return bool(int(pip_json['is_pass_transitor'])) + if 'is_pass_transistor' in pip_json: + return bool(int(pip_json['is_pass_transistor'])) else: return False @@ -298,7 +298,7 @@ class Tile(object): can_invert=bool(int(pip['can_invert'])), is_directional=bool(int(pip['is_directional'])), is_pseudo=bool(int(pip['is_pseudo'])), - is_pass_transitor=is_pass_transitor(pip), + is_pass_transistor=is_pass_transistor(pip), timing=get_pip_timing(pip.get('src_to_dst')), backward_timing=get_pip_timing(pip.get('dst_to_src')), ) From cf84dfe2fd6098f7c38085ab91ccee450d3603c2 Mon Sep 17 00:00:00 2001 From: Keith Rothman <537074+litghost@users.noreply.github.com> Date: Thu, 9 May 2019 14:18:09 -0700 Subject: [PATCH 7/7] Fix some comments. Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com> --- prjxray/math_models.py | 65 +++++++++++++++++++++++ prjxray/tile.py | 16 +++--- prjxray/timing.py | 117 +++++++++-------------------------------- 3 files changed, 99 insertions(+), 99 deletions(-) create mode 100644 prjxray/math_models.py diff --git a/prjxray/math_models.py b/prjxray/math_models.py new file mode 100644 index 00000000..a649302c --- /dev/null +++ b/prjxray/math_models.py @@ -0,0 +1,65 @@ +""" Math models are used to represent abstract operations for the timing models. +This is useful for creating excel workbooks that can update dynamically, or +generating a model with symbolic constants to be backsolved. +""" + + +class ExcelMathModel(object): + """ Math model used for outputting to an dyanmic Excel sheet. """ + + def __init__(self): + pass + + def sum(self, elems): + sum_val = '(' + ' + '.join(elems) + ')' + if sum_val == '()': + return '0' + else: + return sum_val + + def product(self, elems): + sum_val = '(' + ' * '.join(elems) + ')' + if sum_val == '()': + return '1' + else: + return sum_val + + def plus(self, a, b): + return self.sum((a, b)) + + def divide(self, a, b): + return '({}/{})'.format(a, b) + + def multiply(self, a, b): + return '({}*{})'.format(a, b) + + def eval(self, elem): + return '=' + elem + + +def PythonMathModel(object): + """ Math model used for outputting values equalated immediately. """ + + def __init__(self): + pass + + def sum(self, elems): + return sum(elems) + + def product(self, elems): + v = 1.0 + for elem in elems: + v *= elem + return v + + def divide(self, a, b): + return a / b + + def plus(self, a, b): + return a + b + + def multiply(self, a, b): + return a * b + + def eval(self, elem): + return elem diff --git a/prjxray/tile.py b/prjxray/tile.py index e6e01660..4ca5a702 100644 --- a/prjxray/tile.py +++ b/prjxray/tile.py @@ -8,7 +8,7 @@ TileDbs = namedtuple( 'TileDbs', 'segbits block_ram_segbits ppips mask tile_type') -class OutpinTiming(namedtuple('OutpinTiming', 'delays drive_resistance')): +class OutPinTiming(namedtuple('OutPinTiming', 'delays drive_resistance')): """ Timing for site output pins. Attributes @@ -22,7 +22,7 @@ class OutpinTiming(namedtuple('OutpinTiming', 'delays drive_resistance')): pass -class InpinTiming(namedtuple('InpinTiming', 'delays capacitance')): +class InPinTiming(namedtuple('InPinTiming', 'delays capacitance')): """ Timing for site input pins. Attributes @@ -125,7 +125,7 @@ class SitePin(namedtuple('SitePin', 'name wire timing')): all sites of the same type. wire : str Wire name within the tile. This name is site instance specific. - timing : Either InpinTiming or OutpinTiming + timing : Either InPinTiming or OutPinTiming Timing of site pin. May be None if database lacks timing information. """ @@ -177,13 +177,13 @@ def get_pip_timing(pip_timing_json): def get_site_pin_timing(site_pin_info): - """ Convert site_pin_info JSON into InpinTiming or OutpinTiming object. + """ Convert site_pin_info JSON into InPinTiming or OutPinTiming object. Returns ------- If timing information is not present for this site pin, returns None. - If this is an output pin, returns OutpinTiming. - If this is an input pin, returns InpinTiming. + If this is an output pin, returns OutPinTiming. + If this is an input pin, returns InPinTiming. """ if isinstance(site_pin_info, str): @@ -198,13 +198,13 @@ def get_site_pin_timing(site_pin_info): if 'cap' in site_pin_info: assert 'res' not in site_pin_info - return wire, InpinTiming( + return wire, InPinTiming( delays=delays, capacitance=float(site_pin_info['cap']) / CAPACITANCE_FACTOR, ) else: assert 'res' in site_pin_info - return wire, OutpinTiming( + return wire, OutPinTiming( delays=delays, drive_resistance=float(site_pin_info['res']) / RESISTANCE_FACTOR, ) diff --git a/prjxray/timing.py b/prjxray/timing.py index 2098d4d1..29a3dd78 100644 --- a/prjxray/timing.py +++ b/prjxray/timing.py @@ -30,31 +30,31 @@ a pi-model. Example timing tree: -+------+ -|Outpin| -+--+---+ - | - | - v -+--+--+ -|Wire | -+--+--+ - | - +-----------------+ - | | -+--+---+ +-------+------+ -|Buffer| |PassTransistor| -+--+---+ +------+-------+ - | | - v v -+--+-+ +--+-+ -|Wire| |Wire| -+--+-+ +--+-+ - | | - v v -+--+--+ +--+--+ -|Inpin| |Inpin| -+-----+ +-----+ + +------+ + |Outpin| + +--+---+ + | + | + v + +--+--+ + |Wire | + +--+--+ + | + +-----------------+ + | | + +--+---+ +-------+------+ + |Buffer| |PassTransistor| + +--+---+ +------+-------+ + | | + v v + +--+-+ +--+-+ + |Wire| |Wire| + +--+-+ +--+-+ + | | + v v + +--+--+ +--+--+ + |Inpin| |Inpin| + +-----+ +-----+ Note on units: @@ -160,72 +160,6 @@ def fast_slow_tuple_to_corners(arr): } -# Math models are used to represent abstract operations for the timing models. -# This is useful for creating excel workbooks that can update dynamically, or -# generating a model with symbolic constants to be backsolved. - - -class ExcelMathModel(object): - """ Math model used for outputting to an dyanmic Excel sheet. """ - - def __init__(self): - pass - - def sum(self, elems): - sum_val = '(' + ' + '.join(elems) + ')' - if sum_val == '()': - return '0' - else: - return sum_val - - def product(self, elems): - sum_val = '(' + ' * '.join(elems) + ')' - if sum_val == '()': - return '1' - else: - return sum_val - - def plus(self, a, b): - return self.sum((a, b)) - - def divide(self, a, b): - return '({}/{})'.format(a, b) - - def multiply(self, a, b): - return '({}*{})'.format(a, b) - - def eval(self, elem): - return '=' + elem - - -def PythonMathModel(object): - """ Math model used for outputting values equalated immediately. """ - - def __init__(self): - pass - - def sum(self, elems): - return sum(elems) - - def product(self, elems): - v = 1.0 - for elem in elems: - v *= elem - return v - - def divide(self, a, b): - return a / b - - def plus(self, a, b): - return a + b - - def multiply(self, a, b): - return a * b - - def eval(self, elem): - return elem - - class TimingNode(object): """ Base class for timing node models. """ @@ -288,6 +222,7 @@ class DownstreamNode(TimingNode): math : MathModel Math model to use to compute delays """ + pass class Outpin(TimingNode):