2019-04-26 21:21:50 +02:00
|
|
|
# See LICENSE for licensing information.
|
|
|
|
|
#
|
2019-06-14 17:43:41 +02:00
|
|
|
# Copyright (c) 2016-2019 Regents of the University of California and The Board
|
|
|
|
|
# of Regents for the Oklahoma Agricultural and Mechanical College
|
|
|
|
|
# (acting for and on behalf of Oklahoma State University)
|
|
|
|
|
# All rights reserved.
|
2019-04-26 21:21:50 +02:00
|
|
|
#
|
2018-07-10 00:42:46 +02:00
|
|
|
import hierarchy_design
|
2016-11-08 18:57:35 +01:00
|
|
|
import debug
|
2020-06-11 20:53:59 +02:00
|
|
|
from tech import drc, layer
|
2019-12-18 00:07:01 +01:00
|
|
|
import tech
|
2016-11-08 18:57:35 +01:00
|
|
|
from vector import vector
|
2019-12-18 00:07:01 +01:00
|
|
|
from sram_factory import factory
|
2019-12-18 00:45:07 +01:00
|
|
|
import sys
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2017-12-12 23:53:19 +01:00
|
|
|
|
2018-07-10 00:42:46 +02:00
|
|
|
class contact(hierarchy_design.hierarchy_design):
|
2016-11-08 18:57:35 +01:00
|
|
|
"""
|
2019-10-03 01:26:02 +02:00
|
|
|
Object for a contact shape with its conductor enclosures. Creates
|
|
|
|
|
a contact array minimum active or poly enclosure and metal1
|
|
|
|
|
enclosure. This class has enclosure on two or four sides of the
|
|
|
|
|
contact. The direction specifies whether the first and second
|
|
|
|
|
layer have asymmetric extension in the H or V direction.
|
|
|
|
|
|
|
|
|
|
The well/implant_type is an option to add a select/implant layer
|
|
|
|
|
enclosing the contact. This is necessary to import layouts into
|
|
|
|
|
Magic which requires the select to be in the same GDS hierarchy as
|
|
|
|
|
the contact.
|
|
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
"""
|
2018-09-05 01:35:40 +02:00
|
|
|
|
2019-12-18 00:45:07 +01:00
|
|
|
def __init__(self, layer_stack, dimensions=(1, 1), directions=None,
|
2019-10-03 01:26:02 +02:00
|
|
|
implant_type=None, well_type=None, name=""):
|
|
|
|
|
# This will ignore the name parameter since
|
|
|
|
|
# we can guarantee a unique name here
|
2019-01-17 01:56:06 +01:00
|
|
|
|
2018-07-10 00:42:46 +02:00
|
|
|
hierarchy_design.hierarchy_design.__init__(self, name)
|
2017-09-30 01:22:13 +02:00
|
|
|
debug.info(4, "create contact object {0}".format(name))
|
2020-01-30 02:45:33 +01:00
|
|
|
|
2019-04-01 23:23:47 +02:00
|
|
|
self.add_comment("layers: {0}".format(layer_stack))
|
|
|
|
|
self.add_comment("dimensions: {0}".format(dimensions))
|
|
|
|
|
if implant_type or well_type:
|
2019-10-03 01:26:02 +02:00
|
|
|
self.add_comment("implant type: {}\n".format(implant_type))
|
|
|
|
|
self.add_comment("well_type: {}\n".format(well_type))
|
2020-02-05 19:21:01 +01:00
|
|
|
|
|
|
|
|
self.is_well_contact = implant_type == well_type
|
2020-06-11 20:53:59 +02:00
|
|
|
|
|
|
|
|
# If we have a special tap layer, use it
|
2016-11-08 18:57:35 +01:00
|
|
|
self.layer_stack = layer_stack
|
|
|
|
|
self.dimensions = dimensions
|
2020-05-07 21:35:21 +02:00
|
|
|
|
|
|
|
|
# Non-preferred directions
|
|
|
|
|
if directions == "nonpref":
|
|
|
|
|
first_dir = "H" if self.get_preferred_direction(layer_stack[0])=="V" else "V"
|
|
|
|
|
second_dir = "H" if self.get_preferred_direction(layer_stack[2])=="V" else "V"
|
|
|
|
|
self.directions = (first_dir, second_dir)
|
2020-05-14 20:20:37 +02:00
|
|
|
# Preferred directions
|
|
|
|
|
elif directions == "pref":
|
|
|
|
|
self.directions = (tech.preferred_directions[layer_stack[0]],
|
|
|
|
|
tech.preferred_directions[layer_stack[2]])
|
2020-05-07 21:35:21 +02:00
|
|
|
# User directions
|
|
|
|
|
elif directions:
|
2019-12-18 00:45:07 +01:00
|
|
|
self.directions = directions
|
2020-05-07 21:35:21 +02:00
|
|
|
# Preferred directions
|
2019-12-18 00:45:07 +01:00
|
|
|
else:
|
|
|
|
|
self.directions = (tech.preferred_directions[layer_stack[0]],
|
|
|
|
|
tech.preferred_directions[layer_stack[2]])
|
2019-10-03 01:26:02 +02:00
|
|
|
self.offset = vector(0, 0)
|
2018-01-11 19:24:44 +01:00
|
|
|
self.implant_type = implant_type
|
2019-10-03 01:26:02 +02:00
|
|
|
self.well_type = well_type
|
2019-04-01 23:23:47 +02:00
|
|
|
# Module does not have pins, but has empty pin list.
|
|
|
|
|
self.pins = []
|
2016-11-08 18:57:35 +01:00
|
|
|
self.create_layout()
|
2020-01-16 20:26:43 +01:00
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
def create_layout(self):
|
2019-04-01 23:23:47 +02:00
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
self.setup_layers()
|
|
|
|
|
self.setup_layout_constants()
|
|
|
|
|
self.create_contact_array()
|
|
|
|
|
self.create_first_layer_enclosure()
|
|
|
|
|
self.create_second_layer_enclosure()
|
2019-12-11 18:09:59 +01:00
|
|
|
self.create_nitride_cut_enclosure()
|
2017-08-24 00:02:15 +02:00
|
|
|
|
2020-01-16 20:26:43 +01:00
|
|
|
self.height = max(self.first_layer_position.y + self.first_layer_height,
|
|
|
|
|
self.second_layer_position.y + self.second_layer_height)
|
|
|
|
|
self.width = max(self.first_layer_position.x + self.first_layer_width,
|
|
|
|
|
self.second_layer_position.x + self.second_layer_width)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2018-01-11 19:24:44 +01:00
|
|
|
# Do not include the select layer in the height/width
|
|
|
|
|
if self.implant_type and self.well_type:
|
|
|
|
|
self.create_implant_well_enclosures()
|
|
|
|
|
elif self.implant_type or self.well_type:
|
2019-12-18 00:07:01 +01:00
|
|
|
debug.error(-1,
|
|
|
|
|
"Must define both implant and well type or none.")
|
2017-08-24 00:02:15 +02:00
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
def setup_layers(self):
|
2019-04-01 23:23:47 +02:00
|
|
|
""" Locally assign the layer names. """
|
|
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
(first_layer, via_layer, second_layer) = self.layer_stack
|
|
|
|
|
self.first_layer_name = first_layer
|
2019-12-06 00:38:25 +01:00
|
|
|
self.second_layer_name = second_layer
|
2019-12-11 18:09:59 +01:00
|
|
|
|
2019-12-05 01:12:53 +01:00
|
|
|
# Contacts will have unique per first layer
|
2019-12-23 17:42:52 +01:00
|
|
|
if via_layer in tech.layer:
|
2019-12-12 02:56:55 +01:00
|
|
|
self.via_layer_name = via_layer
|
|
|
|
|
elif via_layer == "contact":
|
2019-12-06 00:38:25 +01:00
|
|
|
if first_layer in ("active", "poly"):
|
|
|
|
|
self.via_layer_name = first_layer + "_" + via_layer
|
2019-12-12 02:56:55 +01:00
|
|
|
elif second_layer in ("active", "poly"):
|
|
|
|
|
self.via_layer_name = second_layer + "_" + via_layer
|
2019-12-06 00:38:25 +01:00
|
|
|
else:
|
2019-12-12 02:56:55 +01:00
|
|
|
debug.error("Invalid via layer {}".format(via_layer), -1)
|
2018-01-11 19:24:44 +01:00
|
|
|
else:
|
2019-12-12 02:56:55 +01:00
|
|
|
debug.error("Invalid via layer {}".format(via_layer), -1)
|
2019-12-11 18:09:59 +01:00
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
def setup_layout_constants(self):
|
2019-04-01 23:23:47 +02:00
|
|
|
""" Determine the design rules for the enclosure layers """
|
|
|
|
|
|
2018-10-12 23:37:51 +02:00
|
|
|
self.contact_width = drc("minwidth_{0}". format(self.via_layer_name))
|
|
|
|
|
contact_to_contact = drc("{0}_to_{0}".format(self.via_layer_name))
|
2018-01-11 19:24:44 +01:00
|
|
|
self.contact_pitch = self.contact_width + contact_to_contact
|
2019-04-01 23:23:47 +02:00
|
|
|
|
2017-08-24 00:02:15 +02:00
|
|
|
self.contact_array_width = self.contact_width + (self.dimensions[0] - 1) * self.contact_pitch
|
|
|
|
|
self.contact_array_height = self.contact_width + (self.dimensions[1] - 1) * self.contact_pitch
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2018-01-11 19:24:44 +01:00
|
|
|
# DRC rules
|
2019-04-01 23:23:47 +02:00
|
|
|
# The extend rule applies to asymmetric enclosures in one direction.
|
|
|
|
|
# The enclosure rule applies to symmetric enclosure component.
|
|
|
|
|
|
2019-12-18 18:30:00 +01:00
|
|
|
self.first_layer_minwidth = drc("minwidth_{0}".format(self.first_layer_name))
|
|
|
|
|
self.first_layer_enclosure = drc("{0}_enclose_{1}".format(self.first_layer_name, self.via_layer_name))
|
2020-02-05 19:21:01 +01:00
|
|
|
# If there's a different rule for active
|
|
|
|
|
# FIXME: Make this more elegant
|
|
|
|
|
if self.is_well_contact and self.first_layer_name == "active" and "tap_extend_contact" in drc.keys():
|
|
|
|
|
self.first_layer_extend = drc("tap_extend_contact")
|
|
|
|
|
else:
|
|
|
|
|
self.first_layer_extend = drc("{0}_extend_{1}".format(self.first_layer_name, self.via_layer_name))
|
2019-04-01 23:23:47 +02:00
|
|
|
|
2019-12-18 18:30:00 +01:00
|
|
|
self.second_layer_minwidth = drc("minwidth_{0}".format(self.second_layer_name))
|
|
|
|
|
self.second_layer_enclosure = drc("{0}_enclose_{1}".format(self.second_layer_name, self.via_layer_name))
|
|
|
|
|
self.second_layer_extend = drc("{0}_extend_{1}".format(self.second_layer_name, self.via_layer_name))
|
2018-01-11 19:24:44 +01:00
|
|
|
|
2019-10-03 01:26:02 +02:00
|
|
|
# In some technologies, the minimum width may be larger
|
|
|
|
|
# than the overlap requirement around the via, so
|
2019-04-01 23:23:47 +02:00
|
|
|
# check this for each dimension.
|
|
|
|
|
if self.directions[0] == "V":
|
2019-12-18 18:30:00 +01:00
|
|
|
self.first_layer_horizontal_enclosure = max(self.first_layer_enclosure,
|
|
|
|
|
(self.first_layer_minwidth - self.contact_array_width) / 2)
|
|
|
|
|
self.first_layer_vertical_enclosure = max(self.first_layer_extend,
|
|
|
|
|
(self.first_layer_minwidth - self.contact_array_height) / 2)
|
2019-04-01 23:23:47 +02:00
|
|
|
elif self.directions[0] == "H":
|
2019-12-18 18:30:00 +01:00
|
|
|
self.first_layer_horizontal_enclosure = max(self.first_layer_extend,
|
|
|
|
|
(self.first_layer_minwidth - self.contact_array_width) / 2)
|
|
|
|
|
self.first_layer_vertical_enclosure = max(self.first_layer_enclosure,
|
|
|
|
|
(self.first_layer_minwidth - self.contact_array_height) / 2)
|
2019-04-01 23:23:47 +02:00
|
|
|
else:
|
2020-05-14 20:20:37 +02:00
|
|
|
debug.error("Invalid first layer direction: ".format(self.directions[0]), -1)
|
2019-04-01 23:23:47 +02:00
|
|
|
|
2019-12-18 18:30:00 +01:00
|
|
|
# In some technologies, the minimum width may be larger
|
|
|
|
|
# than the overlap requirement around the via, so
|
2019-04-01 23:23:47 +02:00
|
|
|
# check this for each dimension.
|
|
|
|
|
if self.directions[1] == "V":
|
2019-12-18 18:30:00 +01:00
|
|
|
self.second_layer_horizontal_enclosure = max(self.second_layer_enclosure,
|
|
|
|
|
(self.second_layer_minwidth - self.contact_array_width) / 2)
|
|
|
|
|
self.second_layer_vertical_enclosure = max(self.second_layer_extend,
|
|
|
|
|
(self.second_layer_minwidth - self.contact_array_height) / 2)
|
2019-04-01 23:23:47 +02:00
|
|
|
elif self.directions[1] == "H":
|
2019-12-18 18:30:00 +01:00
|
|
|
self.second_layer_horizontal_enclosure = max(self.second_layer_extend,
|
|
|
|
|
(self.second_layer_minwidth - self.contact_array_height) / 2)
|
|
|
|
|
self.second_layer_vertical_enclosure = max(self.second_layer_enclosure,
|
|
|
|
|
(self.second_layer_minwidth - self.contact_array_width) / 2)
|
2019-04-01 23:23:47 +02:00
|
|
|
else:
|
2020-05-14 20:20:37 +02:00
|
|
|
debug.error("Invalid secon layer direction: ".format(self.directions[1]), -1)
|
2019-04-01 23:23:47 +02:00
|
|
|
|
2018-01-11 19:24:44 +01:00
|
|
|
def create_contact_array(self):
|
|
|
|
|
""" Create the contact array at the origin"""
|
2017-08-24 00:02:15 +02:00
|
|
|
# offset for the via array
|
2019-10-03 01:26:02 +02:00
|
|
|
self.via_layer_position = vector(
|
2019-12-18 00:07:01 +01:00
|
|
|
max(self.first_layer_horizontal_enclosure,
|
|
|
|
|
self.second_layer_horizontal_enclosure),
|
|
|
|
|
max(self.first_layer_vertical_enclosure,
|
|
|
|
|
self.second_layer_vertical_enclosure))
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
for i in range(self.dimensions[1]):
|
2019-12-18 00:07:01 +01:00
|
|
|
offset = self.via_layer_position + vector(0,
|
|
|
|
|
self.contact_pitch * i)
|
2016-11-08 18:57:35 +01:00
|
|
|
for j in range(self.dimensions[0]):
|
2019-12-05 01:12:53 +01:00
|
|
|
self.add_rect(layer=self.via_layer_name,
|
2016-11-08 18:57:35 +01:00
|
|
|
offset=offset,
|
|
|
|
|
width=self.contact_width,
|
|
|
|
|
height=self.contact_width)
|
2019-10-03 01:26:02 +02:00
|
|
|
offset = offset + vector(self.contact_pitch, 0)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2019-12-11 18:09:59 +01:00
|
|
|
def create_nitride_cut_enclosure(self):
|
|
|
|
|
""" Special layer that encloses poly contacts in some processes """
|
|
|
|
|
# Check if there is a special poly nitride cut layer
|
2019-12-23 17:42:52 +01:00
|
|
|
if "npc" not in tech.layer:
|
2019-12-11 18:09:59 +01:00
|
|
|
return
|
|
|
|
|
|
2020-06-11 20:53:59 +02:00
|
|
|
npc_enclose_poly = drc("npc_enclose_poly")
|
|
|
|
|
npc_enclose_offset = vector(npc_enclose_poly, npc_enclose_poly)
|
2019-12-11 18:09:59 +01:00
|
|
|
# Only add for poly layers
|
|
|
|
|
if self.first_layer_name == "poly":
|
|
|
|
|
self.add_rect(layer="npc",
|
2020-06-11 20:53:59 +02:00
|
|
|
offset=self.first_layer_position - npc_enclose_offset,
|
|
|
|
|
width=self.first_layer_width + 2 * npc_enclose_poly,
|
|
|
|
|
height=self.first_layer_height + 2 * npc_enclose_poly)
|
2019-12-11 18:09:59 +01:00
|
|
|
elif self.second_layer_name == "poly":
|
|
|
|
|
self.add_rect(layer="npc",
|
2020-06-11 20:53:59 +02:00
|
|
|
offset=self.second_layer_position - npc_enclose_offset,
|
|
|
|
|
width=self.second_layer_width + 2 * npc_enclose_poly,
|
|
|
|
|
height=self.second_layer_height + 2 * npc_enclose_poly)
|
2019-12-11 18:09:59 +01:00
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
def create_first_layer_enclosure(self):
|
2018-01-11 19:24:44 +01:00
|
|
|
# this is if the first and second layers are different
|
2019-10-03 01:26:02 +02:00
|
|
|
self.first_layer_position = vector(
|
|
|
|
|
max(self.second_layer_horizontal_enclosure - self.first_layer_horizontal_enclosure, 0),
|
|
|
|
|
max(self.second_layer_vertical_enclosure - self.first_layer_vertical_enclosure, 0))
|
2018-01-11 19:24:44 +01:00
|
|
|
|
2019-12-18 18:30:00 +01:00
|
|
|
self.first_layer_width = max(self.contact_array_width + 2 * self.first_layer_horizontal_enclosure,
|
|
|
|
|
self.first_layer_minwidth)
|
|
|
|
|
self.first_layer_height = max(self.contact_array_height + 2 * self.first_layer_vertical_enclosure,
|
|
|
|
|
self.first_layer_minwidth)
|
2020-06-11 20:53:59 +02:00
|
|
|
if self.is_well_contact and self.first_layer_name == "active" and "tap" in layer:
|
|
|
|
|
first_layer_name = "tap"
|
|
|
|
|
else:
|
|
|
|
|
first_layer_name = self.first_layer_name
|
|
|
|
|
self.add_rect(layer=first_layer_name,
|
2017-08-24 00:02:15 +02:00
|
|
|
offset=self.first_layer_position,
|
2018-01-11 19:24:44 +01:00
|
|
|
width=self.first_layer_width,
|
|
|
|
|
height=self.first_layer_height)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
def create_second_layer_enclosure(self):
|
2018-01-11 19:24:44 +01:00
|
|
|
# this is if the first and second layers are different
|
2019-10-03 01:26:02 +02:00
|
|
|
self.second_layer_position = vector(
|
|
|
|
|
max(self.first_layer_horizontal_enclosure - self.second_layer_horizontal_enclosure, 0),
|
|
|
|
|
max(self.first_layer_vertical_enclosure - self.second_layer_vertical_enclosure, 0))
|
2018-01-11 19:24:44 +01:00
|
|
|
|
2019-12-18 18:30:00 +01:00
|
|
|
self.second_layer_width = max(self.contact_array_width + 2 * self.second_layer_horizontal_enclosure,
|
|
|
|
|
self.second_layer_minwidth)
|
|
|
|
|
self.second_layer_height = max(self.contact_array_height + 2 * self.second_layer_vertical_enclosure,
|
|
|
|
|
self.second_layer_minwidth)
|
2016-11-08 18:57:35 +01:00
|
|
|
self.add_rect(layer=self.second_layer_name,
|
2017-08-24 00:02:15 +02:00
|
|
|
offset=self.second_layer_position,
|
2018-01-11 19:24:44 +01:00
|
|
|
width=self.second_layer_width,
|
|
|
|
|
height=self.second_layer_height)
|
|
|
|
|
|
|
|
|
|
def create_implant_well_enclosures(self):
|
2019-12-17 20:03:36 +01:00
|
|
|
implant_position = self.first_layer_position - [drc("implant_enclose_active")] * 2
|
|
|
|
|
implant_width = self.first_layer_width + 2 * drc("implant_enclose_active")
|
|
|
|
|
implant_height = self.first_layer_height + 2 * drc("implant_enclose_active")
|
2018-01-11 19:24:44 +01:00
|
|
|
self.add_rect(layer="{}implant".format(self.implant_type),
|
|
|
|
|
offset=implant_position,
|
|
|
|
|
width=implant_width,
|
|
|
|
|
height=implant_height)
|
2019-12-23 17:42:52 +01:00
|
|
|
|
|
|
|
|
# Optionally implant well if layer exists
|
|
|
|
|
well_layer = "{}well".format(self.well_type)
|
|
|
|
|
if well_layer in tech.layer:
|
2020-02-04 18:37:58 +01:00
|
|
|
well_width_rule = drc("minwidth_" + well_layer)
|
2020-02-05 19:21:01 +01:00
|
|
|
self.well_enclose_active = drc(well_layer + "_enclose_active")
|
|
|
|
|
self.well_width = max(self.first_layer_width + 2 * self.well_enclose_active,
|
|
|
|
|
well_width_rule)
|
|
|
|
|
self.well_height = max(self.first_layer_height + 2 * self.well_enclose_active,
|
|
|
|
|
well_width_rule)
|
2020-02-04 18:37:58 +01:00
|
|
|
center_pos = vector(0.5*self.width, 0.5*self.height)
|
2020-02-05 19:21:01 +01:00
|
|
|
well_position = center_pos - vector(0.5*self.well_width, 0.5*self.well_height)
|
2019-12-23 17:42:52 +01:00
|
|
|
self.add_rect(layer=well_layer,
|
|
|
|
|
offset=well_position,
|
2020-02-05 19:21:01 +01:00
|
|
|
width=self.well_width,
|
|
|
|
|
height=self.well_height)
|
2018-01-11 19:24:44 +01:00
|
|
|
|
2019-03-05 04:27:53 +01:00
|
|
|
def analytical_power(self, corner, load):
|
2018-07-10 19:17:23 +02:00
|
|
|
""" Get total power of a module """
|
|
|
|
|
return self.return_power()
|
2017-12-12 23:53:19 +01:00
|
|
|
|
2020-01-30 02:45:33 +01:00
|
|
|
|
2019-12-18 00:45:07 +01:00
|
|
|
# Set up a static for each layer to be used for measurements
|
|
|
|
|
for layer_stack in tech.layer_stacks:
|
|
|
|
|
(layer1, via, layer2) = layer_stack
|
|
|
|
|
cont = factory.create(module_type="contact",
|
|
|
|
|
layer_stack=layer_stack)
|
|
|
|
|
module = sys.modules[__name__]
|
2020-01-30 02:45:33 +01:00
|
|
|
# Also create a contact that is just the first layer
|
|
|
|
|
if layer1 == "poly" or layer1 == "active":
|
|
|
|
|
setattr(module, layer1 + "_contact", cont)
|
|
|
|
|
else:
|
|
|
|
|
setattr(module, layer1 + "_via", cont)
|
|
|
|
|
|
2020-02-05 19:21:01 +01:00
|
|
|
# Set up a static for each well contact for measurements
|
|
|
|
|
if "nwell" in tech.layer:
|
|
|
|
|
cont = factory.create(module_type="contact",
|
|
|
|
|
layer_stack=tech.active_stack,
|
|
|
|
|
implant_type="n",
|
|
|
|
|
well_type="n")
|
|
|
|
|
module = sys.modules[__name__]
|
|
|
|
|
setattr(module, "nwell_contact", cont)
|
|
|
|
|
|
|
|
|
|
if "pwell" in tech.layer:
|
|
|
|
|
cont = factory.create(module_type="contact",
|
|
|
|
|
layer_stack=tech.active_stack,
|
|
|
|
|
implant_type="p",
|
|
|
|
|
well_type="p")
|
|
|
|
|
module = sys.modules[__name__]
|
|
|
|
|
setattr(module, "pwell_contact", cont)
|
2019-12-18 18:30:00 +01:00
|
|
|
|
2017-12-12 23:53:19 +01:00
|
|
|
|