import os
import importlib
from bag.io import read_yaml
from bag.layout.routing import RoutingGrid
from bag.layout.template import TemplateDB
# TB imports
from bag.data import load_sim_results, save_sim_results, load_sim_file
[docs]class AyarDesignManager:
"""
Class that oversees the creation of layouts, schematics, testbenches, and simulations. Overrides DesignMaster for
more intuitive yaml file organization and handles the RoutingGrid in grid-free layouts
"""
def __init__(self, bprj, spec_file, gds_layermap=''):
self.prj = bprj
self.tdb = None # templateDB instance for layout creation
self.impl_lib = None # Virtuoso library where generated cells are stored
self.cell_name_list = None # list of names for each created cell
self.specs = read_yaml(spec_file)
# Initialize self.tdb with appropriate templateDB instance
self.make_tdb(gds_layermap)
"""
GENERATION METHODS - call these methods to generate and simulate your designs
"""
[docs] def run_flow(self):
"""
Override and call this method to specify your design procedure when subclassing
"""
pass
[docs] def generate_layout(self, layout_params_list=None, cell_name_list=None):
"""
Generates a batch of layouts with the layout package/class in the spec file with parameters set by
layout_params_list and names them according to cell_name_list. Each dict in the layout_params_list creates a
new layout
layout_params_list : :obj:'list' of :obj:'dict'
list of parameter dicts to be applied to the specified layout class
cell_name_list : :obj:'list' of :obj:'str'
list of names to be applied to each implementation of the layout class
"""
# If no list is provided, extract parameters from the provided spec file
if layout_params_list is None:
if 'layout_params' in self.specs:
layout_params_list = [self.specs['layout_params']]
else:
layout_params_list = [self.specs['dsn_params']]
if cell_name_list is None:
cell_name_list = [self.specs['impl_cell']]
# Reformat provided lists
if type(layout_params_list) is not list:
layout_params_list = [layout_params_list]
if type(cell_name_list) is not list:
cell_name_list = [cell_name_list]
print('Generating Layout')
cls_package = self.specs['layout_package']
cls_name = self.specs['layout_class']
lay_module = importlib.import_module(cls_package)
temp_cls = getattr(lay_module, cls_name)
temp_list = []
for lay_params in layout_params_list:
template = self.tdb.new_template(params=lay_params, temp_cls=temp_cls, debug=False)
temp_list.append(template)
self.tdb.batch_layout(self.prj, temp_list, cell_name_list)
[docs] def generate_schematic(self, sch_params_list=None, cell_name_list=None):
"""
Generates a batch of schematics specified by sch_params_list and names them according to cell_name_list.
Each dict in the sch_params_list creates a new schematic
Parameters
----------
sch_params_list : :obj:'list' of :obj:'dict'
parameter dicts to be applied to the specified layout class
cell_name_list : :obj:'list' of :obj:'str'
list of names to be applied to each implementation of the layout class
"""
# If no list is provided, extract parameters from the provided spec file
if sch_params_list is None:
if 'sch_params' in self.specs:
sch_params_list = [self.specs['sch_params']]
else:
sch_params_list = [self.specs['dsn_params']]
if cell_name_list is None:
cell_name_list = [self.specs['impl_cell']]
if type(sch_params_list) is not list:
sch_params_list = [sch_params_list]
if type(cell_name_list) is not list:
cell_name_list = [cell_name_list]
print('Generating Schematic')
sch_temp_lib = self.specs['sch_temp_lib']
sch_temp_cell = self.specs['sch_temp_cell']
impl_lib = self.specs['impl_lib']
inst_list, name_list = [], []
for sch_params, cur_name in zip(sch_params_list, cell_name_list):
dsn = self.prj.create_design_module(sch_temp_lib, sch_temp_cell)
dsn.design(**sch_params)
inst_list.append(dsn)
name_list.append(cur_name)
self.prj.batch_schematic(impl_lib, inst_list, name_list=name_list)
[docs] def generate_tb(self, tb_params_list=None, tb_name_list=None):
"""
Generates a batch of testbenches specified by tb_params_list and names them according to tb_name_list.
Each dict in tb_params_list creates a new set of tb's
Parameters
----------
tb_params_list : :obj:'list' of :obj:'dict'
list of parameter dicts to be applied to the testbench generator class
tb_name_list : :obj:'list' of :obj:'str'
list of names to be applied to each implementation of the tb class
"""
# If no info is provided, extract parameters from the provided spec file
if tb_params_list is None or tb_name_list is None:
tb_name_list = self.specs['tb_params'].keys()
tb_params_list = self.specs['tb_params'].values()
print('Generating Testbench')
impl_lib = self.specs['impl_lib']
impl_cell = self.specs['impl_cell']
for info, name in zip(tb_params_list, tb_name_list):
tb_lib = info['tb_lib']
tb_cell = info['tb_cell']
tb_sch_params = info['tb_sch_params']
# If dut lib/cell is provided, override the impl lib/cell
if 'dut_lib' in info and 'dut_cell' in info:
impl_lib = info['dut_lib']
impl_cell = info['dut_cell']
tb_dsn = self.prj.create_design_module(tb_lib, tb_cell)
tb_dsn.design(dut_lib=impl_lib, dut_cell=impl_cell, **tb_sch_params)
tb_dsn.implement_design(impl_lib, top_cell_name=name)
[docs] def simulate(self):
"""
Runs a batch of simulations on the generated TB's. All parameters for simulation are set within the spec file
"""
print('Running Simulation')
impl_lib = self.specs['impl_lib']
impl_cell = self.specs['impl_cell']
results_dict = {}
for tb_impl_cell, info in self.specs['tb_params'].items():
tb_params = info['tb_sim_params']
view_name = info['view_name']
sim_envs = info['sim_envs']
data_dir = info['data_dir']
# setup testbench ADEXL state
print('setting up %s' % tb_impl_cell)
tb = self.prj.configure_testbench(impl_lib, tb_impl_cell)
# set testbench parameters values
for key, val in tb_params.items():
tb.set_parameter(key, val)
# set config view, i.e. schematic vs extracted
tb.set_simulation_view(impl_lib, impl_cell, view_name)
# set process corners
tb.set_simulation_environments(sim_envs)
# commit changes to ADEXL state back to database
tb.update_testbench()
print('running simulation')
tb.run_simulation()
print('simulation done, load results')
results = load_sim_results(tb.save_dir)
save_sim_results(results, os.path.join(data_dir, '%s.hdf5' % tb_impl_cell))
results_dict[tb_impl_cell] = results
print('all simulation done')
return results_dict
[docs] def run_LVS(self, cell_name_list=None):
"""
Runs LVS on a batch of cells contained within the implementation library
Parameters
----------
cell_name_list : :obj:'list' of :obj:'str'
list of strings containing the names of the cells we should run LVS on
"""
if not cell_name_list:
cell_name_list = [self.specs['impl_cell']]
for cell_name in cell_name_list:
print('Running LVS on {}'.format(cell_name))
lvs_passed, lvs_log = self.prj.run_lvs(self.impl_lib, cell_name)
if lvs_passed:
print('\n' + 'LVS Clean :)')
else:
print('\n' + 'LVS Incorrect :(')
print('LVS log path: {}'.format(lvs_log))
[docs] def run_PEX(self, cell_name_list):
"""
Runs PEX on a batch of cells contained within the implementation library
Parameters
----------
cell_name_list : :obj:'list' of :obj:'str'
list of strings containing the names of the cells we should run PEX on
"""
for cell_name in cell_name_list:
print('Running PEX on {}'.format(cell_name))
pex_passed, pex_log = self.prj.run_rcx(self.impl_lib, cell_name, create_schematic=True)
if pex_passed:
print('\n' + 'PEX Passed :)')
else:
print('\n' + 'PEX Failed :(')
print('PEX log path: {}'.format(pex_log))
[docs] def load_sim_data(self):
"""
Returns simulation data for all TBs in spec file
"""
results_dict = {}
for name, info in self.specs['tb_params'].items():
data_dir = info['data_dir']
fname = os.path.join(data_dir, '%s.hdf5' % name)
print('loading simulation data for %s' % name)
results_dict[name] = load_sim_file(fname)
print('finish loading data')
return results_dict
[docs] def import_schematic_library(self, lib_name):
"""
Imports a Cadence library containing schematic templates for use in BAG, this must be called if
changes to the schematic were made since the last run
Parameters
----------
lib_name : str
string containing name of the library to be imported
"""
self.prj.import_design_library(lib_name)
"""
HELPER METHODS - These should not need to be called by any subclass or external routine
"""
[docs] def make_tdb(self, layermap=''):
"""
Makes a new TemplateDB object. If no routing grid parameters are sent in, dummy parameters are used.
"""
self.impl_lib = self.specs['impl_lib']
if 'routing_grid' in self.specs:
layers = self.specs['routing_grid']['layers']
spaces = self.specs['routing_grid']['spaces']
widths = self.specs['routing_grid']['widths']
bot_dir = self.specs['routing_grid']['bot_dir']
else:
# Use dummy routing grid settings
layers = [1, 2, 3, 4, 5]
spaces = [0.1, 0.1, 0.1, 0.1, 0.2]
widths = [0.1, 0.1, 0.1, 0.1, 0.2]
bot_dir = 'y'
routing_grid = RoutingGrid(self.prj.tech_info, layers, spaces, widths, bot_dir)
self.tdb = TemplateDB('template_libs.def',
routing_grid,
self.impl_lib,
use_cybagoa=True,
name_prefix=self.specs.get('name_prefix', ''),
name_suffix=self.specs.get('name_suffix', ''),
prj=self.prj,
gds_lay_file=layermap)