def make_hcs_3d_scaled (a, b, c): """build a 3D homogeneus coordiate system from three points, and derive scale from distance between points""" # create orthonormal basis u = normalised(b-a) v = normalised(c-a) nu = vector.norm(u) nv = vector.norm(v) if tol_eq(nu,0) and tol_eq(nv,0): # all points equal, no rotation u = vector.vector([1.0,0.0,0.0]) v = vector.vector([0.0,1.0,0.0]) elif tol_eq(nu, 0): # determine u perpendicular from v u,dummy = perp_3d(v)[0] elif tol_eq(nv, 0): # determine v perpendicular from u dummy,v = perp_3d(u)[0] # make the basis vectors orthogonal w = vector.cross(u,v) v = vector.cross(w,u) # scale again if not tol_eq(vector.norm(b-a),0.0): u = u / vector.norm(b-a) if not tol_eq(vector.norm(c-a),0.0): v = v / vector.norm(c-a) # note: w is not scaled # create matix with basis vectors + translation as columns hcs = Mat([ [u[0],v[0], w[0], a[0]], [u[1],v[1], w[1], a[1]], [u[2],v[2], w[2], a[2]], [0.0, 0.0, 0.0, 1.0] ]) return hcs
def make_hcs_3d (a, b, c, righthanded=True): """build a 3D homogeneous coordiate system from three points. The origin is point a. The x-axis is b-a, the y axis is c-a, or as close as possible after orthogonormalisation.""" # create orthonormal basis u = normalised(b-a) v = normalised(c-a) nu = vector.norm(u) nv = vector.norm(v) if tol_eq(nu,0.0) and tol_eq(nv,0.0): # all points equal, no rotation u = vector.vector([1.0,0.0,0.0]) v = vector.vector([0.0,1.0,0.0]) elif tol_eq(nu, 0.0): # determine u perpendicular from v u,dummy = perp_3d(v) elif tol_eq(nv, 0.0): # determine v perpendicular from u dummy,v = perp_3d(u) # ensure that u and v are different if tol_eq(vector.norm(u-v),0.0): dummy,v = perp_3d(u) # make the basis vectors orthogonal w = vector.cross(u,v) v = vector.cross(w,u) # flip basis if lefthanded desired if righthanded==False: w = -w # create matix with basis vectors + translation as columns hcs = Mat([ [u[0],v[0], w[0], a[0]], [u[1],v[1], w[1], a[1]], [u[2],v[2], w[2], a[2]], [0.0, 0.0, 0.0, 1.0] ]) return hcs
def cc_int(p1, r1, p2, r2): """ Intersect circle (p1,r1) circle (p2,r2) where p1 and p2 are 2-vectors and r1 and r2 are scalars Returns a list of zero, one or two solution points. """ d = vector.norm(p2-p1) if not tol_gt(d, 0): return [] u = ((r1*r1 - r2*r2)/d + d)/2 if tol_lt(r1*r1, u*u): return [] elif r1*r1 < u*u: v = 0.0 else: v = math.sqrt(r1*r1 - u*u) s = (p2-p1) * u / d if tol_eq(vector.norm(s),0): p3a = p1+vector.vector([p2[1]-p1[1],p1[0]-p2[0]])*r1/d if tol_eq(r1/d,0): return [p3a] else: p3b = p1+vector.vector([p1[1]-p2[1],p2[0]-p1[0]])*r1/d return [p3a,p3b] else: p3a = p1 + s + vector.vector([s[1], -s[0]]) * v / vector.norm(s) if tol_eq(v / vector.norm(s),0): return [p3a] else: p3b = p1 + s + vector.vector([-s[1], s[0]]) * v / vector.norm(s) return [p3a,p3b]
def __getitem__(self, i): """This operation returns either a single value or a subview. Examples: >>> m = matrix(array=[[1.,2.,3.],[4.,5.,6.],[7.,8.,9.]]) >>> m[1] # row 1 vector(dtype=float64, shape=(3), data=[4.,5.,6.]) >>> m[:,1] # column 1 vector(dtype=float64, shape=(3), data=[2.,5.,8.]) >>> m[0,0] # element (0,0) 1.0 >>> m[0:2,0:3:2] # a submatrix matrix(dtype=float64, shape=(2,2) [[1.0, 3.0], [4.0, 6.0]]) """ if type(i) != tuple: # must be a row accessor if isinstance(i, slice): return matrix(block=_block.subblock(self.block, (i,slice(0,None)))) else: return vector(block=self.block.row(i)) assert len(i) == 2 if isinstance(i[0], slice) and isinstance(i[1], slice): return matrix(block=_block.subblock(self.block, i)) elif isinstance(i[0], slice): return vector(block=_block.subblock(self.block.col(i[1]), (i[0],))) elif isinstance(i[1], slice): return vector(block=_block.subblock(self.block.row(i[0]), (i[1],))) else: return self.block.get(i[0], i[1])
def think(self, others): ''' When the leader is user controlled the leader uses the same seek target logic as without user control. The target to be sought to is set in the direction desired by user. When the leader is not user controlled the seek target moves using self.seek_offset_speed. The position of seek target and self.seek_offset_speed is randomized at regular interval or when the leader gets too close to the seek target. ''' if self.user_controlled: direction = vector(0, 0) if self.user_up: direction.y-= 100 if self.user_down: direction.y+= 100 if self.user_left: direction.x-= 100 if self.user_right: direction.x+= 100 self.current_seek_target = self.pos + direction else: distance = self.current_seek_target - self.pos if distance.lenght < 10.0 or random.randint(0, 300) == 0: self.current_seek_target = self.pos + vector(random.uniform(-50, 50), random.uniform(-50, 50)) self.randomizeSeekOffsetSpeed() else: self.seek_offset_speed+= vector(random.uniform(-0.3, 0.3), random.uniform(-0.3, 0.3)) self.seek_offset_speed.clamp(0, 2.5) self.current_seek_target+= self.seek_offset_speed #go, seek! self.seekTraget(self.current_seek_target, 1.0)
def __init__(self, obj, **kwargs): super(Physics, self).__init__(obj, **kwargs) self.velocity = vector(0,0) self.rect = pygame.Rect(0,0,30,30) self.corners = (vector(0,0), vector(0,30), vector(30,30), vector(30,0)) #self.corners = (vector(16,16),) self.attached = None self.gravity = 1.0
def test_sss_degen(): p1 = vector.vector([0.0, 0.0, 0.0]) p2 = vector.vector([2.0, 0.0, 0.0]) p3 = vector.vector([1.0, 0.0, 0.0]) r1 = 2.0 r2 = 2.0 r3 = math.sqrt(3) print sss_int(p1,r1,p2,r2,p3,r3)
def make_hcs_2d_scaled (a, b): """build a 2D homogeneus coordiate system from two points, but scale with distance between input point""" u = b-a if tol_eq(vector.norm(u), 0.0): u = vector.vector([1.0,0.0]) #else: # u = u / vector.norm(u) v = vector.vector([-u[1], u[0]]) hcs = Mat([ [u[0],v[0],a[0]] , [u[1],v[1],a[1]] , [0.0, 0.0, 1.0] ] ) return hcs
def make_hcs_2d (a, b): """build a 2D homogeneus coordiate system from two points""" u = b-a if tol_eq(vector.norm(u), 0.0): u = vector.vector([0.0,0.0]) else: u = u / vector.norm(u) v = vector.vector([-u[1], u[0]]) hcs = Mat([ [u[0],v[0],a[0]] , [u[1],v[1],a[1]] , [0.0, 0.0, 1.0] ] ) return hcs
def bounce(self, direction): if self['Physics'].velocity.dot(direction) < 0: #reflect u = direction.normal() v = direction t = vector(self['Physics'].velocity.dot(u), -self['Physics'].velocity.dot(v)) self['Physics'].velocity = vector(t.x * u.x + t.y * v.x, t.x * u.y + t.y * v.y) self['Physics'].velocity += direction * self.movement.speed_bonus else: self['Physics'].velocity += direction * self.movement.speed_bonus
def trilaterate(u, v, r1, r2): P1 = vector(u) P2 = vector(v) ex = (P2 - P1) / abs(P2 - P1) d = abs(P2 - P1) x = (r1 ** 2 - r2 ** 2 + d ** 2)/(2 * d) y1 = math.sqrt(r1 ** 2 - x ** 2) y2 = -y1 p1 = P1 + x * ex + y1 p2 = P1 + x * ex + y2 return [p1, p2]
def problem102(): count = 0 zero = vector(0,0) with open('Problem102.txt','r') as data: for l in data.readlines(): x0,x1,y0,y1,z0,z1 = l.split(',') X, Y, Z = vector(x0,x1),vector(y0,y1),vector(z0,z1) # Computes the area of each of the possible inner trianle and compares it to the area of the centre if area(X,Y,Z) == area(X,Y,zero) + area(X,zero,Z) + area(zero,Y,Z): count += 1 #return all(orthogonalIntersection(v,w, vector(0,0)) for v,w in combinations([X,Y,Z],2)) return count
def sss_int(p1, r1, p2, r2, p3, r3): """Intersect three spheres, centered in p1, p2, p3 with radius r1,r2,r3 respectively. Returns a list of zero, one or two solution points. """ solutions = [] # intersect circles in plane cp1 = vector.vector([0.0,0.0]) cp2 = vector.vector([vector.norm(p2-p1), 0.0]) cpxs = cc_int(cp1, r1, cp2, r2) if len(cpxs) == 0: return [] # determine normal of plane though p1, p2, p3 n = vector.cross(p2-p1, p3-p1) if not tol_eq(vector.norm(n),0.0): n = n / vector.norm(n) else: # traingle p1, p2, p3 is degenerate # check for 2d solutions if len(cpxs) == 0: return [] # project cpxs back to 3d and check radius r3 cp4 = cpxs[0] u = normalised(p2-p1) v,w = perp_3d(u) p4 = p1 + cp4[0] * u + cp4[1] * v if tol_eq(vector.norm(p4-p3), r3): return [p4] else: return [] # px, rx, nx is circle px = p1 + (p2-p1) * cpxs[0][0] / vector.norm(p2-p1) rx = abs(cpxs[0][1]) nx = p2-p1 nx = nx / vector.norm(nx) # py is projection of p3 on px,nx dy3 = vector.dot(p3-px, nx) py = p3 - (nx * dy3) if tol_gt(dy3, r3): return [] # ry is radius of circle in py if tol_eq(r3,0.0): ry = 0.0 else: ry = math.sin(math.acos(min(1.0,abs(dy3/r3))))*r3 # determine intersection of circle px, rx and circle py, ry, projected relative to line py-px cpx = vector.vector([0.0,0.0]) cpy = vector.vector([vector.norm(py-px), 0.0]) cp4s = cc_int(cpx, rx, cpy, ry) for cp4 in cp4s: p4 = px + (py-px) * cp4[0] / vector.norm(py-px) + n * cp4[1] solutions.append(p4) return solutions
def eye(n, d=None): """ Creates an identity TT-matrix""" c = _matrix.matrix() c.tt = _vector.vector() if d is None: n0 = _np.asanyarray(n, dtype=_np.int32) c.tt.d = n0.size else: n0 = _np.asanyarray([n] * d, dtype=_np.int32) c.tt.d = d c.n = n0.copy() c.m = n0.copy() c.tt.n = (c.n) * (c.m) c.tt.r = _np.ones((c.tt.d + 1,), dtype=_np.int32) c.tt.get_ps() c.tt.alloc_core() for i in xrange(c.tt.d): c.tt.core[ c.tt.ps[i] - 1:c.tt.ps[ i + 1] - 1] = _np.eye( c.n[i]).flatten() return c
def mkron(a, *args): """Kronecker product of all the arguments""" if not isinstance(a, list): a = [a] a = list(a) # copy list for i in args: if isinstance(i, list): a.extend(i) else: a.append(i) c = _vector.vector() c.d = 0 c.n = _np.array([], dtype=_np.int32) c.r = _np.array([], dtype=_np.int32) c.core = [] for t in a: thetensor = t.tt if isinstance(t, _matrix.matrix) else t c.d += thetensor.d c.n = _np.concatenate((c.n, thetensor.n)) c.r = _np.concatenate((c.r[:-1], thetensor.r)) c.core = _np.concatenate((c.core, thetensor.core)) c.get_ps() return c
def random_problem_2D(numpoints, radius=10.0, roundoff=0.0, angleratio=0.5): """Generate a random problem with given number of points, a roundoff value for the prototype points, a radius for the cloud of prototype points and a ratio of angle constraints over distance constraints""" group = {} problem = GeometricProblem(dimension=2) i = 0 while i < numpoints: aname = 'p'+str(i) apoint = vector([ _round(random.uniform(-radius,radius),roundoff), _round(random.uniform(-radius,radius),roundoff) ]) unique = True for v in group: p = group[v] if tol_eq(apoint[0],p[0]) and tol_eq(apoint[1],p[1]): unique = False break if unique: problem.add_point(aname, apoint) group[aname] = apoint i = i + 1 #next _constraint_group(problem, group, None, angleratio) return problem
def __matmul__(self, other): """ Multiplication of two TT-matrices """ diff = len(self.n) - len(other.m) L = self if diff >= 0 else _tools.kron(self, matrix(_tools.ones(1, abs(diff)))) R = other if diff <= 0 else _tools.kron(other, matrix(_tools.ones(1, abs(diff)))) c = matrix() c.n = L.n.copy() c.m = R.m.copy() res = _vector.vector() res.d = L.tt.d res.n = c.n * c.m if L.is_complex or R.is_complex: res.r = _core_f90.core.zmat_mat( L.n, L.m, R.m, _np.array( L.tt.core, dtype=_np.complex), _np.array( R.tt.core, dtype=_np.complex), L.tt.r, R.tt.r) res.core = _core_f90.core.zresult_core.copy() else: res.r = _core_f90.core.dmat_mat( L.n, L.m, R.m, _np.real( L.tt.core), _np.real( R.tt.core), L.tt.r, R.tt.r) res.core = _core_f90.core.result_core.copy() _core_f90.core.dealloc() res.get_ps() c.tt = res return c
def intersectionOfTwoLines(p1,p2,p3,p4): ''' The intersetion of the two lines spanned by p1,p2 and by p3,p4''' x1,y1,x2,y2,x3,y3,x4,y4 = p1.x,p1.y,p2.x,p2.y,p3.x,p3.y,p4.x,p4.y denominator = (x1 - x2)*(y3-y4) - (y1-y2)*(x3-x4) numeratorx = (x1*y2 - y1*x2)*(x3-x4) - (x1 - x2)*(x3*y4 - y3*x4) numeratory = (x1*y2 - y1*x2)*(y3-y4) - (y1 - y2)*(x3*y4 - y3*x4) return vector(numeratorx,numeratory)/denominator
def __init__(self, x, y, max_speed = -1, max_force = -1, size = -1, random_position = True): ''' x and y should be the window dimensions when random_position is True. ''' if random_position: self.pos = vector(random.uniform(0, x), random.uniform(0, y)) else: self.pos = vector(x, y) self.speed = vector(0, 0) if max_speed > 0: self.max_speed = max_speed else: self.randomizeMaxSpeed() self.current_force = vector(0, 0) if max_force > 0: self.max_force = max_force else: self.randomizeMaxForce() if size > 0: self.size = size else: self.randomizeSize() #This is used for drawing self.last_seek_target = vector(0, 0)
def improvisedTrilateration(x1, y1, d1, x2,y2,d2, x3,y3,d3): p1 = vector.vector() p1.append(x1) p1.append(y1) p2 = vector.vector() p2.append(x2) p2.append(y2) p3 = vector.vector() p3.append(x3) p3.append(y3) a = cc_int(p1, d1, p2, d2) b = cc_int(p2, d2, p3, d3) c = cc_int(p3, d3, p1, d1) return centerFinder(a,b,c)
def transform_point(point, transform): """transform a point""" hpoint = Vec(point) hpoint.append(1.0) hres = transform.mmul(hpoint) res = vector.vector(hres[0:-1]) / hres[-1] return res
def __init__(self, obj, **kwargs): super(Player, self).__init__(obj) self.movement = movement.Momentum() self.size = vector(32, 32) self.image = pygame.transform.scale(resource.get('characters')[0][0], self.size) self.keys = {} self.new_keys = set() print 'instantiated player'
def quarilaterate(u, v, w, r1, r2, r3): P1 = vector(u) P2 = vector(v) P3 = vector(w) ex = (P2 - P1) / abs(P2 - P1) i = ex * (P3 - P1) ey = (P3 - P1 - i * ex) / abs(P3 - P1 - i * ex) d = abs(P2 - P1) j = ey * (P3 - P1) x = (r1 ** 2 - r2 ** 2 + d ** 2)/(2 * d) y = (r1 ** 2 - r3 ** 2 - x ** 2 + (i + x) ** 2 + j ** 2) / 2 * j ez = ex ^ ey z1 = math.sqrt(r1 ** 2 - x ** 2 - y ** 2) z2 = -z1 p1 = P1 + x * ex + y * ey + z1 * ez p2 = P1 + x * ex + y * ey + z2 * ez return [p1, p2]
def plocket_pla(d,limit): w = vector(5) updated = True n = len(d.input_vectors) w_pocket = vector(5) #for i in range(limit): count = 0 while count < limit: index = random.randint(0,n-1) if(not verify(d,w,index)): w = update(d,w,index) count = count + 1 if(error_count(d,w) < error_count(d,w_pocket)): w_pocket = w if(error_count(d,w) ==0 ): break return w_pocket
def is_counterclockwise(p1,p2,p3): """ returns True iff triangle p1,p2,p3 is counterclockwise oriented""" assert len(p1)==2 assert len(p1)==len(p2) assert len(p2)==len(p3) u = p2 - p1 v = p3 - p2; perp_u = vector.vector([-u[1], u[0]]) return tol_gt(vector.dot(perp_u,v), 0)
def concatenate(*args): """Concatenates given TT-vectors. For two tensors :math:`X(i_1,\\ldots,i_d),Y(i_1,\\ldots,i_d)` returns :math:`(d+1)`-dimensional tensor :math:`Z(i_0,i_1,\\ldots,i_d)`, :math:`i_0=\\overline{0,1}`, such that .. math:: Z(0, i_1, \\ldots, i_d) = X(i_1, \\ldots, i_d), Z(1, i_1, \\ldots, i_d) = Y(i_1, \\ldots, i_d). """ tmp = _np.array([[1] + [0] * (len(args) - 1)]) result = kron(_vector.vector(tmp), args[0]) for i in range(1, len(args)): result += kron(_vector.vector(_np.array([[0] * i + [1] + [0] * (len(args) - i - 1)])), args[i]) return result
def __init__(self, **kwargs): dict.__init__(self) # Set the default attributes self['radius'] = 1.0 self['axis'] = vector(1,0,0, record=False) # Don't log the creation of these vectors self['color'] = color.white self['opacity'] = 1.0 self['pos'] = vector(0,0,0, record=False) self['up'] = vector(0,1,0,record=False) self['x'] = 0.0 self['y'] = 0.0 self['z'] = 0.0 for key in kwargs: setattr(self, key, kwargs[key]) log.debug("create", extra={'class':'sphere', 'object':self, 'keywords':kwargs})
def move(self): ''' This function actually moves this thinker to a new position using self.current_force that should be the desired force. ''' self.current_force.clamp(0, self.max_force) self.speed+= self.current_force self.speed.clamp(0, self.max_speed) self.pos+= self.speed self.current_force = vector(0, 0)
def __next_step(self, matr_m, matr, vect, cur_approx, t): """In this func: from M*(x(k+1)-x(k))/t(k+1) + Ax(k) = b to x(k+1) = t(k+1)b - t(k + 1)Ax(k) + x(k)""" next_approx = vector(vect) next_approx.mult_by_number(t) next_approx.add(cur_approx) matr_a = matr.multiply(cur_approx) matr_a.mult_by_number(t) next_approx.add(matr_a) return next_approx
def __init__(self, **kwargs): dict.__init__(self) # Set default attributes self['fixedwidth'] = False self['headlength'] = 0.3 self['headwidth'] = 0.2 self['length'] = 1.0 self['shaftwidth'] = 0.1 self['axis'] = vector(1,0,0, record=False) self['color'] = color.white self['opacity'] = 1.0 self['pos'] = vector(0,0,0, record=False) self['up'] = vector(0,1,0, record=False) for key in kwargs: setattr(self, key, kwargs[key]) log.debug("create", extra={'class':'arrow', 'object':self, 'keywords':kwargs})
def add_modules(self): """ Add all of the module instances in the logical netlist """ # This is the threshold detect inverter on the output of the RBL self.rbl_inv_inst = self.add_inst(name="rbl_inv", mod=self.inv, offset=self.rbl_inv_offset + vector(0, self.inv.width), rotate=270, mirror="MX") self.connect_inst(["bl[0]", "out", "vdd", "gnd"]) self.tx_inst = self.add_inst(name="rbl_access_tx", mod=self.access_tx, offset=self.access_tx_offset, rotate=90) # D, G, S, B self.connect_inst(["vdd", "delayed_en", "bl[0]", "vdd"]) # add the well and poly contact self.dc_inst = self.add_inst(name="delay_chain", mod=self.delay_chain, offset=self.delay_chain_offset, rotate=90) self.connect_inst(["en", "delayed_en", "vdd", "gnd"]) self.rbc_inst = self.add_inst(name="bitcell", mod=self.replica_bitcell, offset=self.bitcell_offset, mirror="MX") self.connect_inst(["bl[0]", "br[0]", "delayed_en", "vdd", "gnd"]) self.rbl_inst = self.add_inst(name="load", mod=self.rbl, offset=self.rbl_offset) self.connect_inst(["bl[0]", "br[0]"] + ["gnd"] * self.rows + ["vdd", "gnd"])
def add_decoder_inv_array(self): """Add a column of INV gates for the decoder above the predecoders and to the right of the NAND decoders.""" z_pin = self.inv.get_pin("Z") if (self.num_inputs == 4 or self.num_inputs == 5): x_off = self.routing_width + self.nand2.width else: x_off = self.routing_width + self.nand3.width self.inv_inst = [] for row in range(self.rows): name = "DEC_INV_[{0}]".format(row) if (row % 2 == 0): inv_row_height = self.inv.height * row mirror = "R0" y_dir = 1 else: inv_row_height = self.inv.height * (row + 1) mirror = "MX" y_dir = -1 y_off = self.predecoder_height + inv_row_height offset = vector(x_off, y_off) self.inv_inst.append( self.add_inst(name=name, mod=self.inv, offset=offset, mirror=mirror)) # This will not check that the inst connections match. self.connect_inst(args=[ "Z[{0}]".format(row), "decode[{0}]".format(row), "vdd", "gnd" ], check=False)
def route_input_gate_B(self): """ routing for input B """ xoffset = self.pmos2.poly_positions[0].x \ + self.pmos_position2.x - drc["minwidth_poly"] yoffset = self.nmos_position1.y + self.nmos1.height \ - drc["well_enclosure_active"] + (self.nmos1.active_contact.height \ - self.nmos1.active_height) / 2 \ + drc["metal1_to_metal1"] self.add_contact(layers=("poly", "contact", "metal1"), offset=[xoffset,yoffset]) self.add_via(layers=("metal1", "via1", "metal2"), offset=[xoffset,yoffset]) xoffset = self.pmos2.poly_positions[0].x + self.pmos_position2.x \ - drc["minwidth_poly"] + self.m1m2_via.width length = -xoffset + self.m1m2_via.width self.add_rect(layer="metal2", offset=[xoffset, yoffset], width=length, height=-drc["minwidth_metal2"]) self.B_position = vector(0, yoffset - drc["minwidth_metal1"]) self.add_label(text="B", layer="metal1", offset=self.B_position) xoffset = self.pmos_position1.x + self.pmos1.active_position.x \ - drc["metal1_to_metal1"] + (self.pmos1.active_contact.width \ - self.m1m2_via.second_layer_width) / 2 self.add_via(layers=("metal1", "via1", "metal2"), offset=[xoffset,yoffset - drc["minwidth_metal2"]], rotate=90) self.add_rect(layer="metal1", offset=[xoffset, yoffset], width=-xoffset, height=-drc["minwidth_metal1"])
def multiply(self, multiplier): error_msg = ('Number of columns in first matrix must be equal ' 'number of rows in second multiplier(matrix/vector)') result = [] if type(multiplier) == matrix: if self.cnt_col() != multiplier.cnt_row(): raise Exception(error_msg) for row in range(self.cnt_row()): result.append([]) for col in range(multiplier.cnt_col()): result[row].append(0) for step in range(self.cnt_col()): result[row][col] += self.get( row, step) * multiplier.get(step, col) return matrix(result) elif type(multiplier) == vector: if self.cnt_col() != multiplier.size(): raise Exception(error_msg) for row in range(self.cnt_row()): result.append(0) for col in range(self.cnt_col()): result[row] += self.get(row, col) * multiplier.get_elem(col) return vector(result)
def setup_layout_constants(self): """ Pre-compute some handy layout parameters. """ poly_contact = contact.contact(("poly", "contact", "metal1")) m1m2_via = contact.contact(("metal1", "via1", "metal2")) m2m3_via = contact.contact(("metal2", "via2", "metal3")) # metal spacing to allow contacts on any layer self.input_spacing = max( self.poly_space + poly_contact.first_layer_width, self.m1_space + m1m2_via.first_layer_width, self.m2_space + m2m3_via.first_layer_width, self.m3_space + m2m3_via.second_layer_width) # Compute the other pmos2 location, but determining offset to overlap the # source and drain pins self.overlap_offset = self.pmos.get_pin("D").ll() - self.pmos.get_pin( "S").ll() # Two PMOS devices and a well contact. Separation between each. # Enclosure space on the sides. self.well_width = 2*self.pmos.active_width + self.pmos.active_contact.width \ + 2*drc["active_to_body_active"] + 2*drc["well_enclosure_active"] self.width = self.well_width # Height is an input parameter, so it is not recomputed. # This will help with the wells self.well_pos = vector(0, 0.4 * self.height) # This is the extra space needed to ensure DRC rules to the active contacts extra_contact_space = max(-self.nmos.get_pin("D").by(), 0) # This is a poly-to-poly of a flipped cell self.top_bottom_space = max( 0.5 * self.m1_width + self.m1_space + extra_contact_space, drc["poly_extend_active"], self.poly_space)
def add_layout_pins(self): # input is A pin of first inverter a_pin = self.driver_inst_list[0].get_pin("A") self.add_via_stack_center(from_layer=a_pin.layer, to_layer="m2", offset=a_pin.center()) self.add_layout_pin(text="in", layer="m2", offset=a_pin.ll().scale(1, 0), height=a_pin.cy()) # output is A pin of last load inverter last_driver_inst = self.driver_inst_list[-1] a_pin = self.load_inst_map[last_driver_inst][-1].get_pin("A") self.add_via_stack_center(from_layer=a_pin.layer, to_layer="m2", offset=a_pin.center()) mid_point = vector(a_pin.cx() + 3 * self.m2_width, a_pin.cy()) self.add_path("m2", [a_pin.center(), mid_point, mid_point.scale(1, 0)]) self.add_layout_pin_segment_center(text="out", layer="m2", start=mid_point, end=mid_point.scale(1, 0))
def __init__(self, layer_stack, dimensions=[1, 1], implant_type=None, well_type=None): if implant_type or well_type: name = "{0}_{1}_{2}_{3}x{4}_{5}{6}".format( layer_stack[0], layer_stack[1], layer_stack[2], dimensions[0], dimensions[1], implant_type, well_type) else: name = "{0}_{1}_{2}_{3}x{4}".format(layer_stack[0], layer_stack[1], layer_stack[2], dimensions[0], dimensions[1]) design.design.__init__(self, name) debug.info(4, "create contact object {0}".format(name)) self.layer_stack = layer_stack self.dimensions = dimensions self.offset = vector(0, 0) self.implant_type = implant_type self.well_type = well_type self.pins = [] # used for matching parm lengths self.create_layout()
def add_horizontal_trunk_route(self, pins, trunk_offset, layer_stack, pitch): """ Create a trunk route for all pins with the trunk located at the given y offset. """ max_x = max([pin.center().x for pin in pins]) min_x = min([pin.center().x for pin in pins]) # if we are less than a pitch, just create a non-preferred layer jog if max_x - min_x <= pitch: half_layer_width = 0.5 * drc["minwidth_{0}".format( self.vertical_layer)] # Add the horizontal trunk on the vertical layer! self.add_path(self.vertical_layer, [ vector(min_x - half_layer_width, trunk_offset.y), vector(max_x + half_layer_width, trunk_offset.y) ]) # Route each pin to the trunk for pin in pins: # No bend needed here mid = vector(pin.center().x, trunk_offset.y) self.add_path(self.vertical_layer, [pin.center(), mid]) else: # Add the horizontal trunk self.add_path( self.horizontal_layer, [vector(min_x, trunk_offset.y), vector(max_x, trunk_offset.y)]) trunk_mid = vector(0.5 * (max_x + min_x), trunk_offset.y) # Route each pin to the trunk for pin in pins: mid = vector(pin.center().x, trunk_offset.y) self.add_path(self.vertical_layer, [pin.center(), mid]) self.add_via_center(layers=layer_stack, offset=mid)
def route_ctrl_en_gate(self): """ Routing pins to modules input and output""" #route the "ack" input to ctrl_latch gate pos1 = (self.ack_xoff + self.via_shift("co"), self.ctrl_en_inst.get_pin("Gn0").lc().y) pos2 = self.ctrl_en_inst.get_pin("Gn0").lc() self.add_path("poly", [pos1, pos2]) pin = self.ctrl_en_inst.get_pin("Gp0") self.co_shift = vector(contact.poly.height, -contact.poly.width) off = vector(self.ack_xoff, pin.lc().y) + self.co_shift self.add_contact(self.poly_stack, off, rotate=90) self.shift = vector(contact.m1m2.height - self.via_shift("v1"), -contact.poly.width + self.via_co_shift) off = vector(self.ack_xoff, pin.lc().y) + self.shift self.add_via(self.m1_stack, off, rotate=90) self.min_area_shift = 0.5 * contact.m1m2.width + self.via_co_shift off = (self.ack_xoff, pin.lc().y - self.min_area_shift) self.add_metal_minarea("metal1", off) #route the "clk" input to ctrl_latch gate pin = self.ctrl_en_inst.get_pin("Gp1") self.add_path("poly", [(self.clk_xoff + self.via_shift("co"), pin.lc().y), pin.lc()]) off = vector(self.clk_xoff, pin.lc().y) + self.co_shift self.add_contact(self.poly_stack, off, rotate=90) off = vector(self.clk_xoff, pin.lc().y) + self.shift self.add_via(self.m1_stack, off, rotate=90) off = (self.clk_xoff, pin.lc().y - self.min_area_shift) self.add_metal_minarea("metal1", off) self.add_path("metal1", [ self.ctrl_en_inst.get_pin("Dn0").uc(), self.ctrl_en_inst.get_pin("Dp1").lc() ])
def cal_modules_offset(self): pinv_error_offset = 0.025 # leave some room for metal1 routing margin = 3 * drc["minwidth_metal1"] # witdth + min_spacing of M1 & M2 m1rail_space = drc["minwidth_metal1"] + drc["metal1_to_metal1"] m2rail_space = drc["minwidth_metal2"] + drc["metal2_to_metal2"] # leave some margin as bit cell layout exceeds its own orgin route_margin = 8 * m2rail_space well_margin = 2 * drc["pwell_enclose_nwell"] bitcell_array_spacing = max(route_margin, well_margin) # now extra space for BL and WL of RBC gnd_route_margin = 5 * m2rail_space y_off = (self.inv.height * 2 + pinv_error_offset + max(drc["pwell_enclose_nwell"], m1rail_space * 4)) self.delay_chain_offset = vector(self.delay_chain.height, y_off) self.en_input_offset = vector(0, y_off - m2rail_space) self.en_nor_offset = vector(self.nor.width + margin, self.inv.height * 2) self.BL_inv_offset = vector(self.en_nor_offset.x - self.inv.width, 0) self.access_tx_offset = vector( self.en_nor_offset.x - self.nor.width + self.access_tx.height + margin, self.inv.height * 0.5) self.replica_bitline_offset = vector( self.delay_chain_offset.x + bitcell_array_spacing, self.bitcell_chars["height"] + gnd_route_margin) self.delay_inv_offset = vector( self.delay_chain_offset.x - self.inv.width, self.inv.height * 2) self.height = m1rail_space + max( self.delay_chain_offset.y + self.inv.height, self.replica_bitline_offset.y + self.bitline_load.height + 0.5 * self.bitcell_chars["height"]) self.width = (self.replica_bitline_offset.x + self.replica_bitcell.width)
def add_ptx(self): """ transistors are added and placed inside the layout """ # determines the spacing between the edge and nmos (rail to active # metal or poly_to_poly spacing) edge_to_nmos = max( drc["metal1_to_metal1"] - self.nmos1.active_contact_positions[0][1], 0.5 * (drc["poly_to_poly"] - drc["minwidth_metal1"]) - self.nmos1.poly_positions[0][1]) # determine the position of the first transistor from the left self.nmos_position1 = vector( 0, 0.5 * drc["minwidth_metal1"] + edge_to_nmos) offset = self.nmos_position1 + vector(0, self.nmos1.height) self.add_inst(name="nmos1", mod=self.nmos1, offset=offset, mirror="MX") self.connect_inst(["Z", "A", "net1", "gnd"]) self.nmos_position2 = vector( self.nmos2.active_width - self.nmos2.active_contact.width, self.nmos_position1[1]) offset = self.nmos_position2 + vector(0, self.nmos2.height) self.add_inst(name="nmos2", mod=self.nmos2, offset=offset, mirror="MX") self.connect_inst(["net1", "B", "gnd", "gnd"]) # determines the spacing between the edge and pmos edge_to_pmos = max(drc["metal1_to_metal1"] \ - self.pmos1.active_contact_positions[0][1], 0.5 * drc["poly_to_poly"] - 0.5 * drc["minwidth_metal1"] \ - self.pmos1.poly_positions[0][1]) self.pmos_position1 = vector( 0, self.height - 0.5 * drc["minwidth_metal1"] - edge_to_pmos - self.pmos1.height) self.add_inst(name="pmos1", mod=self.pmos1, offset=self.pmos_position1) self.connect_inst(["vdd", "A", "Z", "vdd"]) self.pmos_position2 = vector(self.nmos_position2.x, self.pmos_position1.y) self.add_inst(name="pmos2", mod=self.pmos2, offset=self.pmos_position2) self.connect_inst(["Z", "B", "vdd", "vdd"])
def calculate_module_offsets(self): """ Calculate all the module offsets """ # delay chain and inv will be rotated 90 self.rbl_inv_offset = vector(self.inv.height, self.inv.width) # access TX goes right on top of inverter self.access_tx_offset = vector(0.5*self.inv.height,self.rbl_inv_offset.y) + \ vector(0,0.5*self.inv.height) self.delay_chain_offset = self.rbl_inv_offset + vector( -contact.m1m2.width, self.inv.width) # Replica bitline is not rotated, it is placed 2 M1 pitch away from the delay chain self.bitcell_offset = self.rbl_inv_offset + vector( 2 * self.m_pitch("m2"), self.bitcell.height) self.rbl_offset = self.bitcell_offset + vector(0, -self.rbl.y_shift) self.height = max(self.rbl_offset.y + self.rbl.height, self.delay_chain_offset.y + self.delay_chain.width) self.width = self.rbl_offset.x + self.bitcell.width + 3 * self.m_pitch( "m1")
def compute_connector(self, pin, enclosure): """ Compute a shape to connect the pin to the enclosure shape. This assumes the shape will be the dimension of the pin. """ if pin.xoverlaps(enclosure): # Is it vertical overlap, extend pin shape to enclosure plc = pin.lc() prc = pin.rc() elc = enclosure.lc() # erc = enclosure.rc() ymin = min(plc.y, elc.y) ymax = max(plc.y, elc.y) ll = vector(plc.x, ymin) ur = vector(prc.x, ymax) elif pin.yoverlaps(enclosure): # Is it horizontal overlap, extend pin shape to enclosure pbc = pin.bc() puc = pin.uc() ebc = enclosure.bc() # euc = enclosure.uc() xmin = min(pbc.x, ebc.x) xmax = max(pbc.x, ebc.x) ll = vector(xmin, pbc.y) ur = vector(xmax, puc.y) else: # Neither, so we must do a corner-to corner pc = pin.center() ec = enclosure.center() xmin = min(pc.x, ec.x) xmax = max(pc.x, ec.x) ymin = min(pc.y, ec.y) ymax = max(pc.y, ec.y) ll = vector(xmin, ymin) ur = vector(xmax, ymax) if ll.x == ur.x or ll.y == ur.y: return None p = pin_layout(pin.name, [ll, ur], pin.layer) return p
def connect_stages_of_ring(self): """ Connect output of each stage to input of next stage in delay_chains (dc1 and dc2) """ for i in range(self.stage - 1): pos1 = self.dc2_inst[i].get_pin("out").lc() if i % 2: pos2 = vector(pos1.x - 2 * self.m_pitch("m1"), pos1.y) else: pos2 = vector(pos1.x - self.m_pitch("m1"), pos1.y) pos4 = self.dc2_inst[i + 1].get_pin("in").lc() pos3 = vector(pos2.x, pos4.y) self.add_wire(self.m1_stack, [pos1, pos2, pos3, pos4]) for i in range(self.stage - 1): pos1 = self.dc1_inst[i].get_pin("out").lc() if i % 2: pos2 = vector(pos1.x - 2 * self.m_pitch("m1"), pos1.y) else: pos2 = vector(pos1.x - self.m_pitch("m1"), pos1.y) pos4 = self.dc1_inst[i + 1].get_pin("in").lc() pos3 = vector(pos2.x, pos4.y) self.add_wire(self.m1_stack, [pos1, pos2, pos3, pos4])
def add_wells(self): """ Add a well and implant over the whole cell. Also, add the pwell contact (if it exists) """ # find right most gnd rail gnd_pins = self.bitcell.get_pins("gnd") right_gnd = None for gnd_pin in gnd_pins: if right_gnd == None or gnd_pin.lx()>right_gnd.lx(): right_gnd = gnd_pin # Add to the right (first) gnd rail m1m2_offset = right_gnd.bc() + vector(0,0.5*self.nmos.poly_height) self.add_via_center(layers=("metal1", "via1", "metal2"), offset=m1m2_offset) active_offset = right_gnd.bc() + vector(0,0.5*self.nmos.poly_height) self.add_via_center(layers=("active", "contact", "metal1"), offset=active_offset) # implant must surround the active area active_correct = vector(contact.well.width,contact.well.height).scale(0.5,0.5) offset_implant = active_offset - vector([drc["implant_to_contact"]]*2) - active_correct implant_width = 2*drc["implant_to_contact"] + contact.well.width implant_height = 2*drc["implant_to_contact"] + contact.well.height self.add_rect(layer="pimplant", offset=offset_implant, width=implant_width, height=implant_height) # Add a well around the whole cell if info["has_pwell"]: self.add_rect(layer="pwell", offset=vector(0,0), width=self.width + contact.well.width + drc["well_enclosure_active"], height=self.height) self.add_rect(layer="vtg", offset=vector(0,0), width=self.width + contact.well.width, height=self.height)
def add_vertical_trunk_route(self, pins, trunk_offset, layer_stack=("metal1", "via1", "metal2"), pitch=None): """ Create a trunk route for all pins with the trunk located at the given x offset. """ if not pitch: pitch = self.m2_pitch max_y = max([pin.center().y for pin in pins]) min_y = min([pin.center().y for pin in pins]) # Add the vertical trunk half_minwidth = 0.5*drc["minwidth_{}".format(layer_stack[2])] # if we are less than a pitch, just create a non-preferred layer jog if max_y-min_y < pitch: # Add the horizontal trunk on the vertical layer! self.add_path(layer_stack[0],[vector(trunk_offset.x,min_y-half_minwidth), vector(trunk_offset.x,max_y+half_minwidth)]) # Route each pin to the trunk for pin in pins: # No bend needed here mid = vector(trunk_offset.x, pin.center().y) self.add_path(layer_stack[0], [pin.center(), mid]) else: # Add the vertical trunk self.add_path(layer_stack[2],[vector(trunk_offset.x,min_y), vector(trunk_offset.x,max_y)]) trunk_mid = vector(trunk_offset.x,0.5*(max_y+min_y),) # Route each pin to the trunk for pin in pins: mid = vector(trunk_offset.x, pin.center().y) self.add_path(layer_stack[0], [pin.center(), mid]) self.add_via_center(layers=layer_stack, offset=mid)
def route_instances(self): # bank_sel is vertical wire bank_sel_inv_pin = self.bank_sel_inv.get_pin("A") xoffset_bank_sel = bank_sel_inv_pin.lx() bank_sel_line_pos = vector(xoffset_bank_sel, 0) bank_sel_line_end = vector(xoffset_bank_sel, self.yoffset_maxpoint) self.add_path("metal2", [bank_sel_line_pos, bank_sel_line_end]) self.add_via_center(layers=("metal1", "via1", "metal2"), offset=bank_sel_inv_pin.lc()) # Route the pin to the left edge as well bank_sel_pin_pos = vector(0, 0) bank_sel_pin_end = vector(bank_sel_line_pos.x, bank_sel_pin_pos.y) self.add_layout_pin_segment_center(text="bank_sel", layer="metal3", start=bank_sel_pin_pos, end=bank_sel_pin_end) self.add_via_center(layers=("metal2", "via2", "metal3"), offset=bank_sel_pin_end, directions=("H", "H")) # bank_sel_bar is vertical wire bank_sel_bar_pin = self.bank_sel_inv.get_pin("Z") xoffset_bank_sel_bar = bank_sel_bar_pin.rx() self.add_label_pin(text="bank_sel_bar", layer="metal2", offset=vector(xoffset_bank_sel_bar, 0), height=self.inv4x.height) self.add_via_center(layers=("metal1", "via1", "metal2"), offset=bank_sel_bar_pin.rc()) for i in range(self.num_control_lines): logic_inst = self.logic_inst[i] inv_inst = self.inv_inst[i] input_name = self.input_control_signals[i] gated_name = self.control_signals[i] if input_name in ("clk_buf"): xoffset_bank_signal = xoffset_bank_sel_bar else: xoffset_bank_signal = xoffset_bank_sel # Connect the logic output to inverter input pre = logic_inst.get_pin("Z").lc() out_position = logic_inst.get_pin("Z").rc() + vector( 0.5 * self.m1_width, 0) in_position = inv_inst.get_pin("A").lc() + vector( 0.5 * self.m1_width, 0) post = inv_inst.get_pin("A").rc() self.add_path("metal1", [pre, out_position, in_position, post]) # Connect the logic B input to bank_sel/bank_sel_bar logic_pos = logic_inst.get_pin("B").lc() - vector( 0.5 * contact.m1m2.height, 0) input_pos = vector(xoffset_bank_signal, logic_pos.y) self.add_path("metal2", [logic_pos, input_pos]) self.add_via_center(layers=("metal1", "via1", "metal2"), offset=logic_pos, directions=("H", "H")) # Connect the logic A input to the input pin logic_pos = logic_inst.get_pin("A").lc() input_pos = vector(0, logic_pos.y) self.add_via_center(layers=("metal1", "via1", "metal2"), offset=logic_pos, directions=("H", "H")) self.add_via_center(layers=("metal2", "via2", "metal3"), offset=logic_pos, directions=("H", "H")) self.add_layout_pin_segment_center(text=input_name, layer="metal3", start=input_pos, end=logic_pos) # Add output pins out_pin = inv_inst.get_pin("Z") self.add_layout_pin(text=gated_name, layer=out_pin.layer, offset=out_pin.ll(), width=inv_inst.rx() - out_pin.lx(), height=out_pin.height()) # Find the x offsets for where the vias/pins should be placed a_xoffset = self.logic_inst[0].lx() b_xoffset = self.inv_inst[0].lx() for num in range(self.num_control_lines): # Route both supplies for n in ["vdd", "gnd"]: supply_pin = self.inv_inst[num].get_pin(n) supply_offset = supply_pin.ll().scale(0, 1) self.add_rect(layer="metal1", offset=supply_offset, width=self.width) # Add pins in two locations for xoffset in [a_xoffset, b_xoffset]: pin_pos = vector(xoffset, supply_pin.cy()) self.add_via_center(layers=("metal1", "via1", "metal2"), offset=pin_pos, directions=("H", "H")) self.add_via_center(layers=("metal2", "via2", "metal3"), offset=pin_pos, directions=("H", "H")) self.add_layout_pin_rect_center(text=n, layer="metal3", offset=pin_pos) # Add vdd/gnd supply rails gnd_pin = inv_inst.get_pin("gnd") left_gnd_pos = vector(0, gnd_pin.cy()) self.add_layout_pin_segment_center(text="gnd", layer="metal1", start=left_gnd_pos, end=gnd_pin.rc()) vdd_pin = inv_inst.get_pin("vdd") left_vdd_pos = vector(0, vdd_pin.cy()) self.add_layout_pin_segment_center(text="vdd", layer="metal1", start=left_vdd_pos, end=vdd_pin.rc())
def place_bitcell_array(self): """ Placing Bitcell Array """ self.bitcell_array_inst.place(vector(0, 0))
def create_layout(self): # Wordline enable connection en_pin=self.add_layout_pin(text="en", layer="metal2", offset=[drc["minwidth_metal1"] + 2 * drc["metal1_to_metal1"],0], width=drc["minwidth_metal2"], height=self.height) self.add_layout_pin(text="gnd", layer="metal1", offset=[0, -0.5*drc["minwidth_metal1"]], width=self.x_offset0, height=drc["minwidth_metal1"]) for row in range(self.rows): name_inv1 = "wl_driver_inv_en{}".format(row) name_nand = "wl_driver_nand{}".format(row) name_inv2 = "wl_driver_inv{}".format(row) inv_nand2B_connection_height = (abs(self.inv.get_pin("Z").ll().y - self.nand2.get_pin("B").ll().y) + drc["minwidth_metal1"]) if (row % 2): y_offset = self.inv.height*(row + 1) inst_mirror = "MX" cell_dir = vector(0,-1) m1tm2_rotate=270 m1tm2_mirror="R0" else: y_offset = self.inv.height*row inst_mirror = "R0" cell_dir = vector(0,1) m1tm2_rotate=90 m1tm2_mirror="MX" name_inv1_offset = [self.x_offset0, y_offset] nand2_offset=[self.x_offset1, y_offset] inv2_offset=[self.x_offset2, y_offset] base_offset = vector(self.width, y_offset) # Extend vdd and gnd of wordline_driver yoffset = (row + 1) * self.inv.height - 0.5 * drc["minwidth_metal1"] if (row % 2): pin_name = "gnd" else: pin_name = "vdd" self.add_layout_pin(text=pin_name, layer="metal1", offset=[0, yoffset], width=self.x_offset0, height=drc["minwidth_metal1"]) # add inv1 based on the info above inv1_inst=self.add_inst(name=name_inv1, mod=self.inv, offset=name_inv1_offset, mirror=inst_mirror ) self.connect_inst(["en", "en_bar[{0}]".format(row), "vdd", "gnd"]) # add nand 2 nand_inst=self.add_inst(name=name_nand, mod=self.nand2, offset=nand2_offset, mirror=inst_mirror) self.connect_inst(["in[{0}]".format(row), "en_bar[{0}]".format(row), "net[{0}]".format(row), "vdd", "gnd"]) # add inv2 inv2_inst=self.add_inst(name=name_inv2, mod=self.inv, offset=inv2_offset, mirror=inst_mirror) self.connect_inst(["net[{0}]".format(row), "wl[{0}]".format(row), "vdd", "gnd"]) # en connection a_pin = inv1_inst.get_pin("A") a_pos = a_pin.lc() clk_offset = vector(en_pin.bc().x,a_pos.y) self.add_center_rect(layer="metal1", start=clk_offset, end=a_pos) m1m2_via = self.add_center_via(layers=("metal1", "via1", "metal2"), offset=clk_offset) # first inv to nand2 B zl_pos = inv1_inst.get_pin("Z").lc() zr_pos = inv1_inst.get_pin("Z").rc() bl_pos = nand_inst.get_pin("B").lc() br_pos = nand_inst.get_pin("B").rc() self.add_path("metal1", [zl_pos, zr_pos, bl_pos, br_pos]) # Nand2 out to 2nd inv zl_pos = nand_inst.get_pin("Z").lc() zr_pos = nand_inst.get_pin("Z").rc() bl_pos = inv2_inst.get_pin("A").lc() br_pos = inv2_inst.get_pin("A").rc() self.add_path("metal1", [zl_pos, zr_pos, bl_pos, br_pos]) # connect the decoder input pin to nand2 A a_pin = nand_inst.get_pin("A") a_pos = a_pin.lc() input_offset = vector(0,a_pos.y) mid_via_offset = vector(clk_offset.x,a_pos.y) + vector(0.5*drc["minwidth_metal2"]+drc["metal2_to_metal2"]+0.5*m1m2_via.width,0) # must under the clk line in M1 self.add_center_layout_pin(text="in[{0}]".format(row), layer="metal1", start=input_offset, end=mid_via_offset) self.add_center_via(layers=("metal1", "via1", "metal2"), offset=mid_via_offset) # now connect to the nand2 A self.add_center_rect(layer="metal2", start=mid_via_offset, end=a_pos) self.add_center_via(layers=("metal1", "via1", "metal2"), offset=a_pos + vector(0.5*m1m2_via.height,0), rotate=90) # output each WL on the right wl_offset = inv2_inst.get_pin("Z").rc() self.add_center_layout_pin(text="wl[{0}]".format(row), layer="metal1", start=wl_offset, end=wl_offset-vector(drc["minwidth_metal1"],0))
def setup_layout_constants(self): """ Pre-compute some handy layout parameters. """ if self.num_contacts == None: self.num_contacts = self.calculate_num_contacts() # Determine layer types needed if self.tx_type == "nmos": self.implant_type = "n" self.well_type = "p" elif self.tx_type == "pmos": self.implant_type = "p" self.well_type = "n" else: self.error("Invalid transitor type.", -1) # This is not actually instantiated but used for calculations self.active_contact = contact(layer_stack=("active", "contact", "metal1"), dimensions=(1, self.num_contacts)) # The contacted poly pitch (or uncontacted in an odd technology) self.poly_pitch = max( 2 * self.contact_to_gate + self.contact_width + self.poly_width, self.poly_space) # The contacted poly pitch (or uncontacted in an odd technology) self.contact_pitch = 2 * self.contact_to_gate + self.contact_width + self.poly_width # The enclosure of an active contact. Not sure about second term. active_enclose_contact = max(drc["active_enclosure_contact"], (self.active_width - self.contact_width) / 2) # This is the distance from the edge of poly to the contacted end of active self.end_to_poly = active_enclose_contact + self.contact_width + self.contact_to_gate # Active width is determined by enclosure on both ends and contacted pitch, # at least one poly and n-1 poly pitches self.active_width = 2 * self.end_to_poly + self.poly_width + ( self.mults - 1) * self.poly_pitch # Active height is just the transistor width self.active_height = self.tx_width # Poly height must include poly extension over active self.poly_height = self.tx_width + 2 * self.poly_extend_active # The active offset is due to the well extension self.active_offset = vector([self.well_enclose_active] * 2) # Well enclosure of active, ensure minwidth as well if info["has_{}well".format(self.well_type)]: self.cell_well_width = max( self.active_width + 2 * self.well_enclose_active, self.well_width) self.cell_well_height = max( self.tx_width + 2 * self.well_enclose_active, self.well_width) # We are going to shift the 0,0, so include that in the width and height self.height = self.cell_well_height - self.active_offset.y self.width = self.cell_well_width - self.active_offset.x else: # If no well, use the boundary of the active and poly self.height = self.poly_height self.width = self.active_width # The active offset is due to the well extension self.active_offset = vector([self.well_enclose_active] * 2) # This is the center of the first active contact offset (centered vertically) self.contact_offset = self.active_offset + vector( active_enclose_contact + 0.5 * self.contact_width, 0.5 * self.active_height) # Min area results are just flagged for now. debug.check( self.active_width * self.active_height >= drc["minarea_active"], "Minimum active area violated.") # We do not want to increase the poly dimensions to fix an area problem as it would cause an LVS issue. debug.check(self.poly_width * self.poly_height >= drc["minarea_poly"], "Minimum poly area violated.")
def normalize(self): """ Re-find the LL and UR points after a transform """ (first, second) = self.boundary ll = vector(min(first[0], second[0]), min(first[1], second[1])) ur = vector(max(first[0], second[0]), max(first[1], second[1])) self.boundary = [ll, ur]
def route_dff(self, port): route_map = [] # column mux dff if self.col_addr_size > 0: dff_names = ["dout_{}".format(x) for x in range(self.col_addr_size)] dff_pins = [self.col_addr_dff_insts[port].get_pin(x) for x in dff_names] bank_names = ["addr{0}_{1}".format(port, x) for x in range(self.col_addr_size)] bank_pins = [self.bank_inst.get_pin(x) for x in bank_names] route_map.extend(list(zip(bank_pins, dff_pins))) # wmask dff if self.num_wmasks > 0 and port in self.write_ports: dff_names = ["dout_{}".format(x) for x in range(self.num_wmasks)] dff_pins = [self.wmask_dff_insts[port].get_pin(x) for x in dff_names] bank_names = ["bank_wmask{0}_{1}".format(port, x) for x in range(self.num_wmasks)] bank_pins = [self.bank_inst.get_pin(x) for x in bank_names] route_map.extend(list(zip(bank_pins, dff_pins))) if port in self.write_ports: # synchronized inputs from data dff dff_names = ["dout_{}".format(x) for x in range(self.word_size + self.num_spare_cols)] dff_pins = [self.data_dff_insts[port].get_pin(x) for x in dff_names] bank_names = ["din{0}_{1}".format(port, x) for x in range(self.word_size + self.num_spare_cols)] bank_pins = [self.bank_inst.get_pin(x) for x in bank_names] route_map.extend(list(zip(bank_pins, dff_pins))) if port in self.readwrite_ports and OPTS.perimeter_pins: # outputs from sense amp # These are the output pins which had their pin placed on the perimeter, so route from the # sense amp which should not align with write driver input sram_names = ["dout{0}[{1}]".format(port, x) for x in range(self.word_size + self.num_spare_cols)] sram_pins = [self.get_pin(x) for x in sram_names] bank_names = ["dout{0}_{1}".format(port, x) for x in range(self.word_size + self.num_spare_cols)] bank_pins = [self.bank_inst.get_pin(x) for x in bank_names] route_map.extend(list(zip(bank_pins, sram_pins))) if self.num_wmasks > 0 and port in self.write_ports: layer_stack = self.m3_stack else: layer_stack = self.m1_stack if port == 0: offset = vector(self.control_logic_insts[port].rx() + self.dff.width, - self.data_bus_size[port] + 2 * self.m1_pitch) else: offset = vector(0, self.bank.height + 2 * self.m1_space) if len(route_map) > 0: self.create_horizontal_channel_route(netlist=route_map, offset=offset, layer_stack=layer_stack) # Route these separately because sometimes the pin pitch on the write driver is too narrow for M3 (FreePDK45) # spare wen dff if self.num_spare_cols > 0 and port in self.write_ports: dff_names = ["dout_{}".format(x) for x in range(self.num_spare_cols)] dff_pins = [self.spare_wen_dff_insts[port].get_pin(x) for x in dff_names] bank_names = ["bank_spare_wen{0}_{1}".format(port, x) for x in range(self.num_spare_cols)] bank_pins = [self.bank_inst.get_pin(x) for x in bank_names] route_map = zip(bank_pins, dff_pins) self.create_horizontal_channel_route(netlist=route_map, offset=offset, layer_stack=self.m1_stack)
def place_insts(self): # Add INV1 to the right self.inv1_inst.place(vector(0, 0)) # Add INV2 to the right self.inv2_inst.place(vector(self.inv1_inst.rx(), 0))
def route_read_access(self): """ Routes read access transistors to the storage component of the bitcell """ # add poly to metal1 contacts for gates of the inverters left_storage_contact = vector( self.inverter_nmos_left.get_pin("G").lc().x - drc["poly_to_polycontact"] - 0.5 * contact.poly.width, self.cross_couple_upper_ypos) self.add_contact_center(layers=("poly", "contact", "metal1"), offset=left_storage_contact, rotate=90) right_storage_contact = vector( self.inverter_nmos_right.get_pin("G").rc().x + drc["poly_to_polycontact"] + 0.5 * contact.poly.width, self.cross_couple_upper_ypos) self.add_contact_center(layers=("poly", "contact", "metal1"), offset=right_storage_contact, rotate=90) inverter_gate_offset_left = vector( self.inverter_nmos_left.get_pin("G").lc().x, self.cross_couple_upper_ypos) self.add_path("poly", [left_storage_contact, inverter_gate_offset_left]) inverter_gate_offset_right = vector( self.inverter_nmos_right.get_pin("G").rc().x, self.cross_couple_upper_ypos) self.add_path("poly", [right_storage_contact, inverter_gate_offset_right]) # add poly to metal1 contacts for gates of read-access transistors # route from read-access contacts to inverter contacts on metal1 for k in range(self.num_r_ports): port_contact_offset = self.read_access_nmos_left[k].get_pin( "G").uc() + vector( 0, self.gate_contact_yoffset - self.poly_extend_active) self.add_contact_center(layers=("poly", "contact", "metal1"), offset=port_contact_offset) self.add_path("poly", [ self.read_access_nmos_left[k].get_pin("G").uc(), port_contact_offset ]) mid = vector(self.read_access_nmos_left[k].get_pin("G").uc().x, self.cross_couple_upper_ypos) self.add_path( "metal1", [port_contact_offset, mid + vector(0, 0.5 * self.m1_width)], width=contact.poly.second_layer_width) self.add_path("metal1", [mid, left_storage_contact]) port_contact_offset = self.read_access_nmos_right[k].get_pin( "G").uc() + vector( 0, self.gate_contact_yoffset - self.poly_extend_active) self.add_contact_center(layers=("poly", "contact", "metal1"), offset=port_contact_offset) self.add_path("poly", [ self.read_access_nmos_right[k].get_pin("G").uc(), port_contact_offset ]) mid = vector(self.read_access_nmos_right[k].get_pin("G").uc().x, self.cross_couple_upper_ypos) self.add_path( "metal1", [port_contact_offset, mid + vector(0, 0.5 * self.m1_width)], width=contact.poly.second_layer_width) self.add_path("metal1", [mid, right_storage_contact])
def place_read_ports(self): """ Places the read ports in the bit cell """ # define read transistor variables as empty arrays based on the number of read ports self.rwl_positions = [None] * self.num_r_ports self.rbl_positions = [None] * self.num_r_ports self.rbr_positions = [None] * self.num_r_ports # calculate offset to overlap the drain of the read-access transistor with the source of the read transistor overlap_offset = self.read_nmos.get_pin( "D").cx() - self.read_nmos.get_pin("S").cx() # iterate over the number of read ports for k in range(0, self.num_r_ports): # calculate transistor offsets left_read_transistor_xpos = self.left_building_edge \ - (k+1)*self.read_port_spacing \ - (k+1)*self.read_port_width right_read_transistor_xpos = self.right_building_edge \ + (k+1)*self.read_port_spacing \ + k*self.read_port_width # add read-access transistors self.read_access_nmos_left[k].place(offset=[ left_read_transistor_xpos + overlap_offset, self.port_ypos ]) self.read_access_nmos_right[k].place( offset=[right_read_transistor_xpos, self.port_ypos]) # add read transistors self.read_nmos_left[k].place( offset=[left_read_transistor_xpos, self.port_ypos]) self.read_nmos_right[k].place(offset=[ right_read_transistor_xpos + overlap_offset, self.port_ypos ]) # add pin for RWL rwl_ypos = rwwl_ypos = self.rowline_offset - self.num_rw_ports * self.rowline_spacing - self.num_w_ports * self.rowline_spacing - k * self.rowline_spacing self.rwl_positions[k] = vector(0, rwl_ypos) self.add_layout_pin_rect_center(text=self.r_wl_names[k], layer="metal1", offset=self.rwl_positions[k], width=self.width, height=self.m1_width) # add pins for RBL and RBR rbl_xpos = left_read_transistor_xpos - self.bitline_offset + 0.5 * self.m2_width self.rbl_positions[k] = vector(rbl_xpos, self.center_ypos) self.add_layout_pin_rect_center(text=self.r_bl_names[k], layer="metal2", offset=self.rbl_positions[k], width=drc["minwidth_metal2"], height=self.height) rbr_xpos = right_read_transistor_xpos + self.read_port_width + self.bitline_offset - 0.5 * self.m2_width self.rbr_positions[k] = vector(rbr_xpos, self.center_ypos) self.add_layout_pin_rect_center(text=self.r_br_names[k], layer="metal2", offset=self.rbr_positions[k], width=drc["minwidth_metal2"], height=self.height)
def add_supply_routing(self): rows_start = self.rail_1_start_x + self.overall_rail_1_gap rows_end = max(self.row_1_end_x, self.row_2_end_x, self.row_3_end_x) vdd_rail_position = vector(self.rail_1_x_offsets["vdd"], 0) well_width = drc["minwidth_well"] # M1 gnd rail from inv1 to max start_offset = self.clk_inv1.get_pin("gnd").lc() row1_gnd_end_offset = vector(rows_end, start_offset.y) self.add_path("metal1", [start_offset, row1_gnd_end_offset]) rail_position = vector(self.rail_1_x_offsets["gnd"], start_offset.y) self.add_wire(("metal1", "via1", "metal2"), [ vector(rows_start, start_offset.y), rail_position, rail_position + vector(0, self.m2_pitch) ]) # also add a well + around the rail self.add_rect(layer="pwell", offset=vector(rows_start, start_offset.y), width=rows_end - rows_start, height=well_width) self.add_rect(layer="vtg", offset=vector(rows_start, start_offset.y), width=rows_end - rows_start, height=well_width) # M1 vdd rail from inv1 to max start_offset = self.clk_inv1.get_pin("vdd").lc() row1_vdd_end_offset = vector(rows_end, start_offset.y) self.add_path("metal1", [start_offset, row1_vdd_end_offset]) rail_position = vector(self.rail_1_x_offsets["vdd"], start_offset.y) self.add_wire(("metal1", "via1", "metal2"), [ vector(rows_start, start_offset.y), rail_position, rail_position - vector(0, self.m2_pitch) ]) # also add a well +- around the rail self.add_rect(layer="nwell", offset=vector(rows_start, start_offset.y) - vector(0, 0.5 * well_width), width=rows_end - rows_start, height=well_width) self.add_rect(layer="vtg", offset=vector(rows_start, start_offset.y) - vector(0, 0.5 * well_width), width=rows_end - rows_start, height=well_width) # M1 gnd rail from inv1 to max start_offset = vector(rows_start, self.tri_en.get_pin("gnd").lc().y) row3_gnd_end_offset = vector(rows_end, start_offset.y) self.add_path("metal1", [start_offset, row3_gnd_end_offset]) rail_position = vector(self.rail_1_x_offsets["gnd"], start_offset.y) self.add_wire(("metal1", "via1", "metal2"), [ vector(rows_start, start_offset.y), rail_position, rail_position - vector(0, self.m2_pitch) ]) # also add a well +- around the rail self.add_rect(layer="pwell", offset=vector(rows_start, start_offset.y) - vector(0, 0.5 * well_width), width=rows_end - rows_start, height=well_width) self.add_rect(layer="vtg", offset=vector(rows_start, start_offset.y) - vector(0, 0.5 * well_width), width=rows_end - rows_start, height=well_width) # M1 vdd rail from inv1 to max start_offset = vector(rows_start, self.w_en_bar.get_pin("vdd").lc().y) row3_vdd_end_offset = vector(rows_end, start_offset.y) self.add_path("metal1", [start_offset, row3_vdd_end_offset]) rail_position = vector(self.rail_1_x_offsets["vdd"], start_offset.y) self.add_wire(("metal1", "via1", "metal2"), [ vector(rows_start, start_offset.y), rail_position, rail_position - vector(0, self.m2_pitch) ]) # Now connect the vdd and gnd rails between the replica bitline and the control logic (rbl_row3_gnd, rbl_row1_gnd) = self.rbl.get_pins("gnd") (rbl_row3_vdd, rbl_row1_vdd) = self.rbl.get_pins("vdd") self.add_path("metal1", [row1_gnd_end_offset, rbl_row1_gnd.lc()]) self.add_path("metal1", [row1_vdd_end_offset, rbl_row1_vdd.lc()]) self.add_path("metal1", [row3_gnd_end_offset, rbl_row3_gnd.lc()]) # row 3 may have a jog due to unequal row heights, so force the full overlap at the end self.add_path("metal1", [ row3_vdd_end_offset - vector(self.m1_pitch, 0), row3_vdd_end_offset, rbl_row3_vdd.ul() ]) # also add a well - around the rail self.add_rect(layer="nwell", offset=vector(rows_start, start_offset.y) - vector(0, well_width), width=rows_end - rows_start, height=well_width) self.add_rect(layer="vtg", offset=vector(rows_start, start_offset.y) - vector(0, well_width), width=rows_end - rows_start, height=well_width)
def add_layout_pins(self): """ Add the top-level pins for a single bank SRAM with control. """ highest_coord = self.find_highest_coords() lowest_coord = self.find_lowest_coords() bbox = [lowest_coord, highest_coord] for port in self.all_ports: # Depending on the port, use the bottom/top or left/right sides # Port 0 is left/bottom # Port 1 is right/top bottom_or_top = "bottom" if port==0 else "top" left_or_right = "left" if port==0 else "right" # Connect the control pins as inputs for signal in self.control_logic_inputs[port]: if signal == "clk": continue if OPTS.perimeter_pins: self.add_perimeter_pin(name=signal + "{}".format(port), pin=self.control_logic_insts[port].get_pin(signal), side=left_or_right, bbox=bbox) else: self.copy_layout_pin(self.control_logic_insts[port], signal, signal + "{}".format(port)) if OPTS.perimeter_pins: self.add_perimeter_pin(name="clk{}".format(port), pin=self.control_logic_insts[port].get_pin("clk"), side=bottom_or_top, bbox=bbox) else: self.copy_layout_pin(self.control_logic_insts[port], "clk", "clk{}".format(port)) # Data input pins go to BOTTOM/TOP din_ports = [] if port in self.write_ports: for bit in range(self.word_size + self.num_spare_cols): if OPTS.perimeter_pins: p = self.add_perimeter_pin(name="din{0}[{1}]".format(port, bit), pin=self.data_dff_insts[port].get_pin("din_{0}".format(bit)), side=bottom_or_top, bbox=bbox) din_ports.append(p) else: self.copy_layout_pin(self.data_dff_insts[port], "din_{}".format(bit), "din{0}[{1}]".format(port, bit)) # Data output pins go to BOTTOM/TOP if port in self.readwrite_ports and OPTS.perimeter_pins: for bit in range(self.word_size + self.num_spare_cols): # This should be routed next to the din pin p = din_ports[bit] self.add_layout_pin_rect_center(text="dout{0}[{1}]".format(port, bit), layer=p.layer, offset=p.center() + vector(self.m3_pitch, 0), width=p.width(), height=p.height()) elif port in self.read_ports: for bit in range(self.word_size + self.num_spare_cols): if OPTS.perimeter_pins: # This should have a clear route to the perimeter if there are no din routes self.add_perimeter_pin(name="dout{0}[{1}]".format(port, bit), pin=self.bank_inst.get_pin("dout{0}_{1}".format(port, bit)), side=bottom_or_top, bbox=bbox) else: self.copy_layout_pin(self.bank_inst, "dout{0}_{1}".format(port, bit), "dout{0}[{1}]".format(port, bit)) # Lower address bits go to BOTTOM/TOP for bit in range(self.col_addr_size): if OPTS.perimeter_pins: self.add_perimeter_pin(name="addr{0}[{1}]".format(port, bit), pin=self.col_addr_dff_insts[port].get_pin("din_{}".format(bit)), side=bottom_or_top, bbox=bbox) else: self.copy_layout_pin(self.col_addr_dff_insts[port], "din_{}".format(bit), "addr{0}[{1}]".format(port, bit)) # Upper address bits go to LEFT/RIGHT for bit in range(self.row_addr_size): if OPTS.perimeter_pins: self.add_perimeter_pin(name="addr{0}[{1}]".format(port, bit + self.col_addr_size), pin=self.row_addr_dff_insts[port].get_pin("din_{}".format(bit)), side=left_or_right, bbox=bbox) else: self.copy_layout_pin(self.row_addr_dff_insts[port], "din_{}".format(bit), "addr{0}[{1}]".format(port, bit + self.col_addr_size)) # Write mask pins go to BOTTOM/TOP if port in self.write_ports: if self.write_size: for bit in range(self.num_wmasks): if OPTS.perimeter_pins: self.add_perimeter_pin(name="wmask{0}[{1}]".format(port, bit), pin=self.wmask_dff_insts[port].get_pin("din_{}".format(bit)), side=bottom_or_top, bbox=bbox) else: self.copy_layout_pin(self.wmask_dff_insts[port], "din_{}".format(bit), "wmask{0}[{1}]".format(port, bit)) # Spare wen pins go to BOTTOM/TOP if port in self.write_ports: for bit in range(self.num_spare_cols): if OPTS.perimeter_pins: self.add_perimeter_pin(name="spare_wen{0}[{1}]".format(port, bit), pin=self.spare_wen_dff_insts[port].get_pin("din_{}".format(bit)), side=left_or_right, bbox=bbox) else: self.copy_layout_pin(self.spare_wen_dff_insts[port], "din_{}".format(bit), "spare_wen{0}[{1}]".format(port, bit))
def lr(self): """ Return the lower right corner """ return vector(self.boundary[1].x, self.boundary[0].y)
def place_instances(self): """ This places the instances for a single bank SRAM with control logic and up to 2 ports. """ # No orientation or offset self.place_bank(self.bank_inst, [0, 0], 1, 1) # The control logic is placed such that the vertical center (between the delay/RBL and # the actual control logic is aligned with the vertical center of the bank (between # the sense amps/column mux and cell array) # The x-coordinate is placed to allow a single clock wire (plus an extra pitch) # up to the row address DFFs. control_pos = [None] * len(self.all_ports) row_addr_pos = [None] * len(self.all_ports) col_addr_pos = [None] * len(self.all_ports) wmask_pos = [None] * len(self.all_ports) spare_wen_pos = [None] * len(self.all_ports) data_pos = [None] * len(self.all_ports) # These positions utilize the channel route sizes. # FIXME: Auto-compute these rather than manual computation. # If a horizontal channel, they rely on the vertical channel non-preferred (contacted) pitch. # If a vertical channel, they rely on the horizontal channel non-preferred (contacted) pitch. # So, m3 non-pref pitch means that this is routed on the m2 layer. self.data_bus_gap = self.m4_nonpref_pitch * 2 # Spare wen are on a separate layer so not included # Start with 1 track minimum self.data_bus_size = [1] * len(self.all_ports) for port in self.all_ports: # All ports need the col addr flops self.data_bus_size[port] += self.col_addr_size # Write ports need the data input flops and write mask flops if port in self.write_ports: self.data_bus_size[port] += self.num_wmasks + self.word_size # This is for the din pins that get routed in the same channel # when we have dout and din together if port in self.readwrite_ports: self.data_bus_size[port] += self.word_size # Convert to length self.data_bus_size[port] *= self.m4_nonpref_pitch # Add the gap in unit length self.data_bus_size[port] += self.data_bus_gap # Port 0 port = 0 # This includes 2 M2 pitches for the row addr clock line. # The delay line is aligned with the bitcell array while the control logic is aligned with the port_data # using the control_logic_center value. control_pos[port] = vector(-self.control_logic_insts[port].width - 2 * self.m2_pitch, self.bank.bank_array_ll.y - self.control_logic_insts[port].mod.control_logic_center.y) self.control_logic_insts[port].place(control_pos[port]) # The row address bits are placed above the control logic aligned on the right. x_offset = self.control_logic_insts[port].rx() - self.row_addr_dff_insts[port].width # It is above the control logic but below the top of the bitcell array y_offset = max(self.control_logic_insts[port].uy(), self.bank_inst.uy() - self.row_addr_dff_insts[port].height) row_addr_pos[port] = vector(x_offset, y_offset) self.row_addr_dff_insts[port].place(row_addr_pos[port]) # Add the col address flops below the bank to the right of the control logic x_offset = self.control_logic_insts[port].rx() + self.dff.width y_offset = - self.data_bus_size[port] - self.dff.height if self.col_addr_dff: col_addr_pos[port] = vector(x_offset, y_offset) self.col_addr_dff_insts[port].place(col_addr_pos[port]) x_offset = self.col_addr_dff_insts[port].rx() else: col_addr_pos[port] = vector(x_offset, 0) if port in self.write_ports: if self.write_size: # Add the write mask flops below the write mask AND array. wmask_pos[port] = vector(x_offset, y_offset) self.wmask_dff_insts[port].place(wmask_pos[port]) x_offset = self.wmask_dff_insts[port].rx() # Add the data flops below the write mask flops. data_pos[port] = vector(x_offset, y_offset) self.data_dff_insts[port].place(data_pos[port]) x_offset = self.data_dff_insts[port].rx() # Add spare write enable flops to the right of data flops since the spare columns # will be on the right if self.num_spare_cols: spare_wen_pos[port] = vector(x_offset, y_offset) self.spare_wen_dff_insts[port].place(spare_wen_pos[port]) x_offset = self.spare_wen_dff_insts[port].rx() else: wmask_pos[port] = vector(x_offset, y_offset) data_pos[port] = vector(x_offset, y_offset) spare_wen_pos[port] = vector(x_offset, y_offset) if len(self.all_ports)>1: # Port 1 port = 1 # This includes 2 M2 pitches for the row addr clock line # The delay line is aligned with the bitcell array while the control logic is aligned with the port_data # using the control_logic_center value. control_pos[port] = vector(self.bank_inst.rx() + self.control_logic_insts[port].width + 2 * self.m2_pitch, self.bank.bank_array_ur.y + self.control_logic_insts[port].height - self.control_logic_insts[port].height + self.control_logic_insts[port].mod.control_logic_center.y) self.control_logic_insts[port].place(control_pos[port], mirror="XY") # The row address bits are placed above the control logic aligned on the left. x_offset = control_pos[port].x - self.control_logic_insts[port].width + self.row_addr_dff_insts[port].width # It is below the control logic but below the bottom of the bitcell array y_offset = min(self.control_logic_insts[port].by(), self.bank_inst.by() + self.row_addr_dff_insts[port].height) row_addr_pos[port] = vector(x_offset, y_offset) self.row_addr_dff_insts[port].place(row_addr_pos[port], mirror="XY") # Add the col address flops below the bank to the right of the control logic x_offset = self.control_logic_insts[port].lx() - 2 * self.dff.width y_offset = self.bank.height + self.data_bus_size[port] + self.dff.height if self.col_addr_dff: col_addr_pos[port] = vector(x_offset - self.col_addr_dff_insts[port].width, y_offset) self.col_addr_dff_insts[port].place(col_addr_pos[port], mirror="MX") x_offset = self.col_addr_dff_insts[port].lx() else: col_addr_pos[port] = vector(x_offset, y_offset) if port in self.write_ports: # Add spare write enable flops to the right of the data flops since the spare # columns will be on the left if self.num_spare_cols: spare_wen_pos[port] = vector(x_offset - self.spare_wen_dff_insts[port].width, y_offset) self.spare_wen_dff_insts[port].place(spare_wen_pos[port], mirror="MX") x_offset = self.spare_wen_dff_insts[port].lx() if self.write_size: # Add the write mask flops below the write mask AND array. wmask_pos[port] = vector(x_offset - self.wmask_dff_insts[port].width, y_offset) self.wmask_dff_insts[port].place(wmask_pos[port], mirror="MX") x_offset = self.wmask_dff_insts[port].lx() # Add the data flops below the write mask flops. data_pos[port] = vector(x_offset - self.data_dff_insts[port].width, y_offset) self.data_dff_insts[port].place(data_pos[port], mirror="MX") else: wmask_pos[port] = vector(x_offset, y_offset) data_pos[port] = vector(x_offset, y_offset) spare_wen_pos[port] = vector(x_offset, y_offset)
def ul(self): """ Return the upper left corner """ return vector(self.boundary[0].x, self.boundary[1].y)