def merge(self, track_set): # type: (TrackSet) -> None """Merge the given TrackSet to this one.""" for hidx, new_intv_set in track_set._tracks.items(): if hidx not in self._tracks: intv_set = IntervalSet() self._tracks[hidx] = intv_set else: intv_set = self._tracks[hidx] for intv, val in new_intv_set.items(): intv_set.add(intv, val, merge=True)
class LaygoIntvSet(object): def __init__(self): super(LaygoIntvSet, self).__init__() self._intv = IntervalSet() self._end_flags = {} def add(self, intv, endl, endr): ans = self._intv.add(intv) if ans: start, stop = intv if start in self._end_flags: del self._end_flags[start] else: self._end_flags[start] = endl if stop in self._end_flags: del self._end_flags[stop] else: self._end_flags[stop] = endr return True else: return False def get_complement(self, total_intv): compl_intv = self._intv.get_complement(total_intv) intv_list = [] end_list = [] for intv in compl_intv: intv_list.append(intv) end_list.append((self._end_flags.get(intv[0], False), self._end_flags.get(intv[1], False))) return intv_list, end_list def get_end_flags(self, num_col): if 0 not in self._end_flags: start_flag = False else: start_flag = self._end_flags[0] if num_col not in self._end_flags: end_flag = False else: end_flag = self._end_flags[num_col] return start_flag, end_flag def get_end(self): if not self._intv: return 0 return self._intv.get_end()
def add_std_space(self, loc, num_col, update_used_blks=True): # type: (Tuple[int, int], int, bool) -> None """Add standard cell spaces at the given location. Parameters ---------- loc : Tuple[int, int] the lower-left corner of the space block. num_col : int the space block width in number of columns. update_used_blks : bool True to register space blocks. This flag is for internal use only. """ if update_used_blks: # update self._used_blocks while len(self._used_blocks) < loc[1] + 1: self._used_blocks.append(IntervalSet()) success = self._used_blocks[loc[1]].add((loc[0], loc[0] + num_col)) if not success: raise ValueError('Cannot add space at std loc (%d, %d)' % (loc[0], loc[1])) col_pitch = self.std_col_width xcur = loc[0] * col_pitch if loc[1] % 2 == 0: orient = 'R0' ycur = loc[1] * self.std_row_height else: orient = 'MX' ycur = (loc[1] + 1) * self.std_row_height if self._draw_boundaries: dx = self._bound_params['lr_width'] * self.std_col_width dy = self._bound_params['tb_height'] * self.std_row_height else: dx = dy = 0 for blk_params in self.get_space_blocks(): lib_name = blk_params['lib_name'] cell_name = blk_params['cell_name'] blk_col = blk_params['num_col'] num_blk, num_col = divmod(num_col, blk_col) blk_width = blk_col * col_pitch if num_blk > 0: self.add_instance_primitive(lib_name, cell_name, (xcur + dx, ycur + dy), orient=orient, nx=num_blk, spx=blk_width) xcur += num_blk * blk_width if num_col > 0: raise ValueError('has %d columns remaining' % num_col)
def add_track(self, hidx, intv, width, value=None): # type: (int, Tuple[int, int], int, Any) -> None """Add tracks to this data structure. Parameters ---------- hidx : int the half track index. intv : Tuple[int, int] the track interval. width : int the track width. value : Any value associated with this track. """ if intv[1] - intv[0] >= self._min_len: if hidx not in self._tracks: intv_set = IntervalSet() self._tracks[hidx] = intv_set else: intv_set = self._tracks[hidx] # TODO: add more robust checking? intv_set.add(intv, val=[width, value], merge=True)
def add_std_instance(self, master, inst_name=None, loc=(0, 0), nx=1, ny=1, spx=0, spy=0, flip_lr=False): # type: (StdCellBase, Optional[str], Tuple[int, int], int, int, int, int, bool) -> Instance """Add a new standard cell instance. Parameters ---------- master : StdCellBase the standard cell template master to add. inst_name : Optional[str] the instance name. loc : Tuple[int, int] lower-left corner of the instance in number of standard cell columns/rows. nx : int horizontal array count. ny : int vertical array count. spx : int horizontal pitch in number of standard cell columns. spy : int vertical pitch in number of standard cell rows. Must be even. flip_lr : bool True to flip the standard cell over Y axis. Returns ------- inst : Instance the standard cell instance. """ if spy % 2 != 0: raise ValueError('row pitch must be even') # update self._used_blocks master_std_size = master.std_size if master_std_size is None: raise ValueError("master.std_size is unset. Try calling master.set_std_size()?") inst_ncol, inst_nrow = master_std_size cur_nrow = loc[1] + inst_nrow + (ny - 1) * spy while len(self._used_blocks) < cur_nrow: self._used_blocks.append(IntervalSet()) for col_off in range(nx): xoff = col_off * spx + loc[0] for row_off in range(ny): yoff = row_off * spy + loc[1] for std_row_idx in range(yoff, yoff + inst_nrow): success = self._used_blocks[std_row_idx].add((xoff, xoff + inst_ncol)) if not success: raise ValueError('Cannot add instance at std loc (%d, %d)' % (xoff, yoff)) col_pitch = self.std_col_width row_pitch = self.std_row_height if loc[1] % 2 == 0: orient = 'R0' dy = loc[1] * row_pitch else: orient = 'MX' dy = (loc[1] + 1) * row_pitch dx = loc[0] * col_pitch if flip_lr: dx += inst_ncol * col_pitch if orient == 'R0': orient = 'MY' else: orient = 'R180' spx_new = spx * col_pitch spy_new = spy * row_pitch if self._draw_boundaries: dx += self._bound_params['lr_width'] * self.std_col_width dy += self._bound_params['tb_height'] * self.std_row_height return self.add_instance(master, inst_name=inst_name, loc=(dx, dy), orient=orient, nx=nx, ny=ny, spx=spx_new, spy=spy_new)
def draw_bias_shields( cls, # type: BiasShield template, # type: TemplateBase layer, # type: int bias_config, # type: Dict[int, Tuple[int, ...]] nwire, # type: int offset, # type: int lower, # type: int upper, # type: int width=1, # type: int space_sig=0, # type: int sup_warrs=None, # type: Optional[Union[WireArray, List[WireArray]]] check_blockage=True, # type: bool tb_mode=3, # type: int ): if lower >= upper: return [], None grid = template.grid res = grid.resolution params = dict( layer=layer, nwire=nwire, bias_config=bias_config, top=False, width=width, space_sig=space_sig, ) bot_master = template.new_template(params=params, temp_cls=BiasShield) sh_box = bot_master.bound_box params['top'] = True top_master = template.new_template(params=params, temp_cls=BiasShield) blk_w = sh_box.width_unit blk_h = sh_box.height_unit route_tids = bot_master.route_tids tr_dir = grid.get_direction(layer) is_horiz = tr_dir == 'x' qdim = blk_w if is_horiz else blk_h tr0 = grid.coord_to_track(layer, offset, unit_mode=True) + 0.5 bot_intvs = IntervalSet() top_intvs = IntervalSet() nstart, tr_offset = divmod(lower, qdim) nstop, tr_off_test = divmod(upper, qdim) if tr_offset != tr_off_test: raise ValueError('upper - lower not divisible by %d' % qdim) # draw shields tr0_ref = route_tids[0][0] sh0 = tr0_ref + tr0 shp = route_tids[nwire + 1][0] - tr0_ref shields = template.add_wires(layer, sh0, lower, upper, num=2, pitch=shp, unit_mode=True) # connect supply wires if necessary sup_layer = layer + 1 if sup_warrs is not None: new_sup_warrs = [] _, wires = template.connect_to_tracks(sup_warrs, shields.track_id, return_wires=True) for warr in wires: if warr.layer_id == sup_layer: new_sup_warrs.append(warr) sup_warrs = new_sup_warrs else: sup_warrs = [] # get blockages if check_blockage: if is_horiz: test_box = BBox(lower, offset, upper, offset + blk_h, res, unit_mode=True) else: test_box = BBox(offset, lower, offset + blk_w, upper, res, unit_mode=True) for lay_id, intv in ((layer - 1, bot_intvs), (layer + 1, top_intvs)): w_ntr = bias_config[lay_id][1] if is_horiz: spx = grid.get_space(lay_id, w_ntr, unit_mode=True) spy = grid.get_line_end_space(lay_id, w_ntr, unit_mode=True) else: spy = grid.get_space(lay_id, w_ntr, unit_mode=True) spx = grid.get_line_end_space(lay_id, w_ntr, unit_mode=True) for box in template.blockage_iter(lay_id, test_box, spx=spx, spy=spy): blkl, blku = box.get_interval(tr_dir, unit_mode=True) blkl = max(blkl, lower) blku = min(blku, upper) if blku > blkl: intv.add((blkl, blku), merge=True, abut=True) master_intv_list = [] if tb_mode & 1 != 0: master_intv_list.append((bot_master, bot_intvs)) if tb_mode & 2 != 0: master_intv_list.append((top_master, top_intvs)) # draw blocks for master, intvs in master_intv_list: sl, su = master.sup_intv ncur = nstart for nend, nnext in cls._get_blk_idx_iter(intvs, sl + tr_offset, su + tr_offset, qdim, nstart, nstop): nblk = nend - ncur if nblk > 0: if is_horiz: loc = (ncur * qdim + tr_offset, offset) nx = nblk ny = 1 else: loc = (offset, ncur * qdim + tr_offset) nx = 1 ny = nblk inst = template.add_instance(master, loc=loc, nx=nx, ny=ny, spx=qdim, spy=qdim, unit_mode=True) sup_warrs.extend( inst.port_pins_iter('sup', layer=sup_layer)) ncur = nnext return sup_warrs, shields
def join_bias_vroutes(template, vm_layer, vdd_dx, vss_dx, xr, num_vdd_tot, num_vss_tot, hm_bias_info_list, bias_config, vdd_pins, vss_pins, xl=0, yt=None, vss_warrs=None, vdd_warrs=None): grid = template.grid vss_intvs = IntervalSet() vdd_intvs = IntervalSet() if vss_warrs is not None: for w in WireArray.single_warr_iter(vss_warrs): vss_intvs.add(w.track_id.get_bounds(grid, unit_mode=True), val=w) if vdd_warrs is not None: for w in WireArray.single_warr_iter(vdd_warrs): vdd_intvs.add(w.track_id.get_bounds(grid, unit_mode=True), val=w) vdd_xl, vdd_xr = vdd_dx vss_xl, vss_xr = vss_dx vdd_xl += xl vdd_xr += xl vss_xl += xl vss_xr += xl vss_params = dict( nwire=num_vss_tot, width=1, space_sig=0, ) vdd_params = dict( nwire=num_vdd_tot, width=1, space_sig=0, ) hm_layer = vm_layer + 1 vss_hm_list = [] vdd_hm_list = [] inst_info_list = [] vss_y_prev = vdd_y_prev = None params = dict( bot_layer=vm_layer, bias_config=bias_config, ) for idx, (code, num, y0) in enumerate(hm_bias_info_list): if code == 0: params['bot_params'] = vss_params if vss_y_prev is not None and y0 > vss_y_prev: sup_warrs = list(vss_intvs.overlap_values((vss_y_prev, y0))) tmp = BiasShield.draw_bias_shields(template, vm_layer, bias_config, num_vss_tot, vss_xl, vss_y_prev, y0, sup_warrs=sup_warrs) vss_hm_list.extend(tmp[0]) else: params['bot_params'] = vdd_params if vdd_y_prev is not None and y0 > vdd_y_prev: sup_warrs = list(vdd_intvs.overlap_values((vdd_y_prev, y0))) tmp = BiasShield.draw_bias_shields(template, vm_layer, bias_config, num_vdd_tot, vdd_xl, vdd_y_prev, y0, sup_warrs=sup_warrs) vdd_hm_list.extend(tmp[0]) if vss_y_prev is not None and y0 > vss_y_prev: sup_warrs = list(vss_intvs.overlap_values((vss_y_prev, y0))) tmp = BiasShield.draw_bias_shields(template, vm_layer, bias_config, num_vss_tot, vss_xl, vss_y_prev, y0, sup_warrs=sup_warrs) vss_hm_list.extend(tmp[0]) params['top_params'] = dict( nwire=num, width=1, space_sig=0, ) if idx == 0: master = template.new_template(params=params, temp_cls=BiasShieldJoin) if code == 1: inst_info_list.append((1, master, vdd_xl, y0)) BiasShield.draw_bias_shields(template, hm_layer, bias_config, num, y0, vdd_xr, xr) vdd_y_prev = y0 + master.array_box.top_unit else: inst_info_list.append((0, master, vss_xl, y0)) BiasShield.draw_bias_shields(template, hm_layer, bias_config, num, y0, vss_xr, xr) vss_y_prev = y0 + master.array_box.top_unit elif code == 1: params['bot_open'] = True master = template.new_template(params=params, temp_cls=BiasShieldJoin) inst_info_list.append((1, master, vdd_xl, y0)) BiasShield.draw_bias_shields(template, hm_layer, bias_config, num, y0, vdd_xr, vss_xl, tb_mode=1) vdd_y_prev = y0 + master.array_box.top_unit params['draw_top'] = False params['bot_params'] = vss_params master = template.new_template(params=params, temp_cls=BiasShieldCrossing) inst_info_list.append((0, master, vss_xl, y0)) BiasShield.draw_bias_shields(template, hm_layer, bias_config, num, y0, vss_xr, xr) vss_y_prev = y0 + master.array_box.top_unit else: params['bot_open'] = True master = template.new_template(params=params, temp_cls=BiasShieldJoin) inst_info_list.append((0, master, vss_xl, y0)) BiasShield.draw_bias_shields(template, hm_layer, bias_config, num, y0, vss_xr, xr) vss_y_prev = y0 + master.array_box.top_unit if yt is None: yt = max(vss_y_prev, vdd_y_prev) if vss_y_prev < yt: sup_warrs = list(vss_intvs.overlap_values((vss_y_prev, yt))) tmp = BiasShield.draw_bias_shields(template, vm_layer, bias_config, num_vss_tot, vss_xl, vss_y_prev, yt, sup_warrs=sup_warrs) vss_hm_list.extend(tmp[0]) if vdd_y_prev < yt: sup_warrs = list(vdd_intvs.overlap_values((vdd_y_prev, yt))) tmp = BiasShield.draw_bias_shields(template, vm_layer, bias_config, num_vdd_tot, vdd_xl, vdd_y_prev, yt, sup_warrs=sup_warrs) vdd_hm_list.extend(tmp[0]) sup_layer = hm_layer + 1 vss_list = [] vdd_list = [] for code, master, x0, y0 in inst_info_list: inst = _add_inst_r180(template, master, x0, y0) if code == 1: vdd_list.extend(inst.port_pins_iter('sup', layer=sup_layer)) else: vss_list.extend(inst.port_pins_iter('sup', layer=sup_layer)) vdd_list = template.connect_wires(vdd_list, upper=yt, unit_mode=True) vss_list = template.connect_wires(vss_list, upper=yt, unit_mode=True) template.draw_vias_on_intersections(vdd_hm_list, vdd_list) template.draw_vias_on_intersections(vss_hm_list, vss_list) # connect pins vss_tidx_list = BiasShield.get_route_tids(grid, vm_layer, vss_xl, bias_config, num_vss_tot) vdd_tidx_list = BiasShield.get_route_tids(grid, vm_layer, vdd_xl, bias_config, num_vdd_tot) pin_map = {} vss_tr_warrs = [] vdd_tr_warrs = [] for pins, tidx_list, result_list in ((vss_pins, vss_tidx_list, vss_tr_warrs), (vdd_pins, vdd_tidx_list, vdd_tr_warrs)): next_idx = 0 for name, warr in pins: if name in pin_map: cur_idx = pin_map[name] add_pin = False else: cur_idx = pin_map[name] = next_idx next_idx += 1 add_pin = True tidx, tr_w = tidx_list[cur_idx + 1] tid = TrackID(vm_layer, tidx, width=tr_w) warr = template.connect_to_tracks(warr, tid, track_upper=yt, unit_mode=True) if add_pin: result_list.append((name, warr)) pin_map.clear() return vdd_tr_warrs, vss_tr_warrs, vdd_list, vss_list
def __init__(self): super(LaygoIntvSet, self).__init__() self._intv = IntervalSet() self._end_flags = {}