2019-04-26 21:21:50 +02:00
|
|
|
# See LICENSE for licensing information.
|
|
|
|
|
#
|
2022-11-30 23:50:43 +01:00
|
|
|
# Copyright (c) 2016-2022 Regents of the University of California and The Board
|
2019-06-14 17:43:41 +02:00
|
|
|
# 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
|
|
|
#
|
2022-11-27 22:01:20 +01:00
|
|
|
from openram import debug
|
|
|
|
|
from openram.tech import GDS, layer
|
|
|
|
|
from openram.tech import preferred_directions
|
|
|
|
|
from openram.tech import cell_properties as props
|
|
|
|
|
from openram import OPTS
|
2022-07-13 19:57:56 +02:00
|
|
|
from . import utils
|
|
|
|
|
from .hierarchy_design import hierarchy_design
|
2019-10-03 01:26:02 +02:00
|
|
|
|
2019-12-18 00:07:01 +01:00
|
|
|
|
2018-07-10 23:17:09 +02:00
|
|
|
class design(hierarchy_design):
|
2018-07-10 00:43:26 +02:00
|
|
|
"""
|
|
|
|
|
This is the same as the hierarchy_design class except it contains
|
2019-12-13 23:13:41 +01:00
|
|
|
some DRC/layer constants and analytical models for other modules to reuse.
|
2018-07-10 00:43:26 +02:00
|
|
|
"""
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2020-11-14 17:08:42 +01:00
|
|
|
def __init__(self, name, cell_name=None, prop=None):
|
2020-11-03 16:06:01 +01:00
|
|
|
# This allows us to use different GDS/spice circuits for hard cells instead of the default ones
|
|
|
|
|
# Except bitcell names are generated automatically by the globals.py setup_bitcells routines
|
|
|
|
|
# depending on the number of ports.
|
2020-11-18 19:47:05 +01:00
|
|
|
|
2020-11-03 16:06:01 +01:00
|
|
|
if name in props.names:
|
2020-11-19 01:27:28 +01:00
|
|
|
if type(props.names[name]) is list:
|
|
|
|
|
num_ports = OPTS.num_rw_ports + OPTS.num_r_ports + OPTS.num_w_ports - 1
|
2020-11-18 19:47:05 +01:00
|
|
|
cell_name = props.names[name][num_ports]
|
|
|
|
|
else:
|
2020-11-19 01:27:28 +01:00
|
|
|
cell_name = props.names[name]
|
2020-11-18 19:47:05 +01:00
|
|
|
|
2020-11-03 16:06:01 +01:00
|
|
|
elif not cell_name:
|
2020-11-03 01:00:16 +01:00
|
|
|
cell_name = name
|
|
|
|
|
super().__init__(name, cell_name)
|
2018-07-10 00:43:26 +02:00
|
|
|
|
2020-11-14 17:08:42 +01:00
|
|
|
# This means it is a custom cell.
|
|
|
|
|
# It could have properties and not be a hard cell too (e.g. dff_buf)
|
|
|
|
|
if prop and prop.hard_cell:
|
2020-11-16 20:04:03 +01:00
|
|
|
# The pins get added from the spice file, so just check
|
|
|
|
|
# that they matched here
|
2020-11-14 17:08:42 +01:00
|
|
|
debug.check(prop.port_names == self.pins,
|
|
|
|
|
"Custom cell pin names do not match spice file:\n{0} vs {1}".format(prop.port_names, self.pins))
|
2020-11-17 20:12:59 +01:00
|
|
|
self.add_pin_indices(prop.port_indices)
|
2020-11-16 20:04:03 +01:00
|
|
|
self.add_pin_names(prop.port_map)
|
2020-11-14 17:08:42 +01:00
|
|
|
self.add_pin_types(prop.port_types)
|
2021-02-10 05:51:50 +01:00
|
|
|
|
|
|
|
|
|
2020-11-14 17:08:42 +01:00
|
|
|
(width, height) = utils.get_libcell_size(self.cell_name,
|
|
|
|
|
GDS["unit"],
|
|
|
|
|
layer[prop.boundary_layer])
|
|
|
|
|
self.pin_map = utils.get_libcell_pins(self.pins,
|
|
|
|
|
self.cell_name,
|
|
|
|
|
GDS["unit"])
|
2020-11-14 00:55:55 +01:00
|
|
|
|
2021-01-22 00:22:54 +01:00
|
|
|
# Convert names back to the original names
|
|
|
|
|
# so that copying will use the new names
|
|
|
|
|
for pin_name in self.pin_map:
|
|
|
|
|
for index1, pin in enumerate(self.pin_map[pin_name]):
|
|
|
|
|
self.pin_map[pin_name][index1].name = self.get_original_pin_name(pin.name)
|
|
|
|
|
|
2020-11-14 17:08:42 +01:00
|
|
|
self.width = width
|
|
|
|
|
self.height = height
|
2020-11-14 00:55:55 +01:00
|
|
|
|
2020-10-27 17:28:21 +01:00
|
|
|
self.setup_multiport_constants()
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2022-02-19 00:02:45 +01:00
|
|
|
try:
|
2022-11-27 22:01:20 +01:00
|
|
|
from openram.tech import power_grid
|
2022-02-19 00:02:45 +01:00
|
|
|
self.supply_stack = power_grid
|
|
|
|
|
except ImportError:
|
|
|
|
|
# if no power_grid is specified by tech we use sensible defaults
|
|
|
|
|
# Route a M3/M4 grid
|
|
|
|
|
self.supply_stack = self.m3_stack
|
|
|
|
|
|
2020-09-08 22:31:50 +02:00
|
|
|
def check_pins(self):
|
|
|
|
|
for pin_name in self.pins:
|
|
|
|
|
pins = self.get_pins(pin_name)
|
|
|
|
|
for pin in pins:
|
2022-07-26 21:20:15 +02:00
|
|
|
debug.info(0, "{0} {1}".format(pin_name, pin))
|
2020-10-27 17:23:11 +01:00
|
|
|
|
2020-10-27 17:28:21 +01:00
|
|
|
def setup_multiport_constants(self):
|
2020-11-03 15:29:17 +01:00
|
|
|
"""
|
2018-11-08 21:19:40 +01:00
|
|
|
These are contants and lists that aid multiport design.
|
|
|
|
|
Ports are always in the order RW, W, R.
|
|
|
|
|
Port indices start from 0 and increment.
|
|
|
|
|
A first RW port will have clk0, csb0, web0, addr0, data0
|
|
|
|
|
A first W port (with no RW ports) will be: clk0, csb0, addr0, data0
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
total_ports = OPTS.num_rw_ports + OPTS.num_w_ports + OPTS.num_r_ports
|
|
|
|
|
|
|
|
|
|
# These are the read/write port indices.
|
2020-10-27 17:28:21 +01:00
|
|
|
self.readwrite_ports = []
|
2018-11-08 21:19:40 +01:00
|
|
|
# These are the read/write and write-only port indices
|
2020-10-27 17:28:21 +01:00
|
|
|
self.write_ports = []
|
2018-11-17 00:03:12 +01:00
|
|
|
# These are the write-only port indices.
|
2020-10-27 17:28:21 +01:00
|
|
|
self.writeonly_ports = []
|
2020-08-17 23:35:39 +02:00
|
|
|
# These are the read/write and read-only port indices
|
2020-10-27 17:28:21 +01:00
|
|
|
self.read_ports = []
|
2018-11-17 00:03:12 +01:00
|
|
|
# These are the read-only port indices.
|
2020-10-27 17:28:21 +01:00
|
|
|
self.readonly_ports = []
|
2018-11-08 21:19:40 +01:00
|
|
|
# These are all the ports
|
2020-10-27 17:28:21 +01:00
|
|
|
self.all_ports = list(range(total_ports))
|
2020-08-17 23:35:39 +02:00
|
|
|
|
|
|
|
|
# The order is always fixed as RW, W, R
|
2018-09-28 09:11:39 +02:00
|
|
|
port_number = 0
|
|
|
|
|
for port in range(OPTS.num_rw_ports):
|
2020-10-27 17:28:21 +01:00
|
|
|
self.readwrite_ports.append(port_number)
|
|
|
|
|
self.write_ports.append(port_number)
|
|
|
|
|
self.read_ports.append(port_number)
|
2018-09-28 09:11:39 +02:00
|
|
|
port_number += 1
|
|
|
|
|
for port in range(OPTS.num_w_ports):
|
2020-10-27 17:28:21 +01:00
|
|
|
self.write_ports.append(port_number)
|
|
|
|
|
self.writeonly_ports.append(port_number)
|
2018-09-28 09:11:39 +02:00
|
|
|
port_number += 1
|
|
|
|
|
for port in range(OPTS.num_r_ports):
|
2020-10-27 17:28:21 +01:00
|
|
|
self.read_ports.append(port_number)
|
|
|
|
|
self.readonly_ports.append(port_number)
|
2019-10-03 01:26:02 +02:00
|
|
|
port_number += 1
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2019-03-05 04:27:53 +01:00
|
|
|
def analytical_power(self, corner, load):
|
2018-07-10 00:43:26 +02:00
|
|
|
""" Get total power of a module """
|
|
|
|
|
total_module_power = self.return_power()
|
|
|
|
|
for inst in self.insts:
|
2019-03-05 04:27:53 +01:00
|
|
|
total_module_power += inst.mod.analytical_power(corner, load)
|
2018-07-10 00:43:26 +02:00
|
|
|
return total_module_power
|