2019-04-26 21:21:50 +02:00
|
|
|
# See LICENSE for licensing information.
|
|
|
|
|
#
|
2023-01-29 07:56:27 +01:00
|
|
|
# Copyright (c) 2016-2023 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.base import design
|
|
|
|
|
from openram.base import vector
|
|
|
|
|
from openram.sram_factory import factory
|
|
|
|
|
from openram import OPTS
|
2021-01-13 22:01:33 +01:00
|
|
|
|
2018-03-20 00:23:13 +01:00
|
|
|
|
2022-07-13 19:57:56 +02:00
|
|
|
class dff_inv_array(design):
|
2018-03-20 00:23:13 +01:00
|
|
|
"""
|
|
|
|
|
This is a simple row (or multiple rows) of flops.
|
|
|
|
|
Unlike the data flops, these are never spaced out.
|
|
|
|
|
"""
|
2018-11-16 20:48:41 +01:00
|
|
|
unique_id = 1
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2018-03-20 00:23:13 +01:00
|
|
|
def __init__(self, rows, columns, inv_size=2, name=""):
|
|
|
|
|
self.rows = rows
|
|
|
|
|
self.columns = columns
|
|
|
|
|
|
|
|
|
|
if name=="":
|
2018-11-16 20:48:41 +01:00
|
|
|
name = "dff_inv_array_{0}x{1}_{2}".format(rows, columns, dff_inv_array.unique_id)
|
|
|
|
|
dff_inv_array.unique_id += 1
|
2020-08-06 20:33:26 +02:00
|
|
|
super().__init__(name)
|
2018-03-20 00:23:13 +01:00
|
|
|
debug.info(1, "Creating {}".format(self.name))
|
2019-01-26 00:00:00 +01:00
|
|
|
self.add_comment("rows: {0} cols: {1}".format(rows, columns))
|
|
|
|
|
self.add_comment("inv1: {0}".format(inv1_size))
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2018-08-28 01:42:48 +02:00
|
|
|
self.inv_size = inv_size
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2018-08-28 01:42:48 +02:00
|
|
|
self.create_netlist()
|
|
|
|
|
if not OPTS.netlist_only:
|
|
|
|
|
self.create_layout()
|
2018-03-20 00:23:13 +01:00
|
|
|
|
2018-08-28 01:42:48 +02:00
|
|
|
def create_netlist(self):
|
|
|
|
|
self.add_pins()
|
2018-08-28 19:24:09 +02:00
|
|
|
self.add_modules()
|
2018-08-28 01:42:48 +02:00
|
|
|
self.create_dff_array()
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2018-08-28 01:42:48 +02:00
|
|
|
def create_layout(self):
|
2018-03-20 00:23:13 +01:00
|
|
|
self.width = self.columns * self.dff.width
|
|
|
|
|
self.height = self.rows * self.dff.height
|
|
|
|
|
|
2018-08-28 01:42:48 +02:00
|
|
|
self.place_dff_array()
|
2018-03-20 00:23:13 +01:00
|
|
|
self.add_layout_pins()
|
2019-05-28 01:32:38 +02:00
|
|
|
self.add_boundary()
|
2018-03-20 00:23:13 +01:00
|
|
|
self.DRC_LVS()
|
|
|
|
|
|
2018-08-28 19:24:09 +02:00
|
|
|
def add_modules(self):
|
2020-11-03 15:29:17 +01:00
|
|
|
self.dff = factory.create(module_type="dff")
|
2018-08-28 01:42:48 +02:00
|
|
|
|
2018-03-20 00:23:13 +01:00
|
|
|
def add_pins(self):
|
2020-11-03 15:29:17 +01:00
|
|
|
for row in range(self.rows):
|
2018-07-18 20:38:58 +02:00
|
|
|
for col in range(self.columns):
|
2019-08-06 23:14:09 +02:00
|
|
|
self.add_pin(self.get_din_name(row,col), "INPUT")
|
2020-11-03 15:29:17 +01:00
|
|
|
for row in range(self.rows):
|
2018-07-18 20:38:58 +02:00
|
|
|
for col in range(self.columns):
|
2019-08-06 23:14:09 +02:00
|
|
|
self.add_pin(self.get_dout_name(row,col), "OUTPUT")
|
|
|
|
|
self.add_pin(self.get_dout_bar_name(row,col), "OUTPUT")
|
|
|
|
|
self.add_pin("clk", "INPUT")
|
|
|
|
|
self.add_pin("vdd", "POWER")
|
|
|
|
|
self.add_pin("gnd", "GROUND")
|
2018-03-20 00:23:13 +01:00
|
|
|
|
|
|
|
|
def create_dff_array(self):
|
|
|
|
|
self.dff_insts={}
|
2020-11-03 15:29:17 +01:00
|
|
|
for row in range(self.rows):
|
2018-07-18 20:38:58 +02:00
|
|
|
for col in range(self.columns):
|
|
|
|
|
name = "Xdff_r{0}_c{1}".format(row,col)
|
|
|
|
|
self.dff_insts[row,col]=self.add_inst(name=name,
|
2018-08-28 01:42:48 +02:00
|
|
|
mod=self.dff)
|
2018-07-18 20:38:58 +02:00
|
|
|
self.connect_inst([self.get_din_name(row,col),
|
|
|
|
|
self.get_dout_name(row,col),
|
2020-11-03 15:29:17 +01:00
|
|
|
self.get_dout_bar_name(row,col),
|
2018-03-20 00:23:13 +01:00
|
|
|
"clk",
|
|
|
|
|
"vdd",
|
|
|
|
|
"gnd"])
|
|
|
|
|
|
2018-08-28 01:42:48 +02:00
|
|
|
def place_dff_array(self):
|
2020-11-03 15:29:17 +01:00
|
|
|
for row in range(self.rows):
|
2018-08-28 01:42:48 +02:00
|
|
|
for col in range(self.columns):
|
|
|
|
|
name = "Xdff_r{0}_c{1}".format(row,col)
|
|
|
|
|
if (row % 2 == 0):
|
|
|
|
|
base = vector(col*self.dff.width,row*self.dff.height)
|
|
|
|
|
mirror = "R0"
|
|
|
|
|
else:
|
|
|
|
|
base = vector(col*self.dff.width,(row+1)*self.dff.height)
|
|
|
|
|
mirror = "MX"
|
2020-11-03 15:29:17 +01:00
|
|
|
self.dff_insts[row,col].place(offset=base,
|
2018-08-28 02:25:39 +02:00
|
|
|
mirror=mirror)
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2018-03-20 00:23:13 +01:00
|
|
|
def get_din_name(self, row, col):
|
|
|
|
|
if self.columns == 1:
|
2018-10-11 18:53:08 +02:00
|
|
|
din_name = "din_{0}".format(row)
|
2018-03-20 00:23:13 +01:00
|
|
|
elif self.rows == 1:
|
2018-10-11 18:53:08 +02:00
|
|
|
din_name = "din_{0}".format(col)
|
2018-03-20 00:23:13 +01:00
|
|
|
else:
|
2018-10-11 18:53:08 +02:00
|
|
|
din_name = "din_{0}_{1}".format(row,col)
|
2018-03-20 00:23:13 +01:00
|
|
|
|
|
|
|
|
return din_name
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2018-03-20 00:23:13 +01:00
|
|
|
def get_dout_name(self, row, col):
|
|
|
|
|
if self.columns == 1:
|
2018-10-11 18:53:08 +02:00
|
|
|
dout_name = "dout_{0}".format(row)
|
2018-03-20 00:23:13 +01:00
|
|
|
elif self.rows == 1:
|
2018-10-11 18:53:08 +02:00
|
|
|
dout_name = "dout_{0}".format(col)
|
2018-03-20 00:23:13 +01:00
|
|
|
else:
|
2018-10-11 18:53:08 +02:00
|
|
|
dout_name = "dout_{0}_{1}".format(row,col)
|
2018-03-20 00:23:13 +01:00
|
|
|
|
|
|
|
|
return dout_name
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2018-03-20 00:23:13 +01:00
|
|
|
def get_dout_bar_name(self, row, col):
|
|
|
|
|
if self.columns == 1:
|
2018-10-11 18:53:08 +02:00
|
|
|
dout_bar_name = "dout_bar_{0}".format(row)
|
2018-03-20 00:23:13 +01:00
|
|
|
elif self.rows == 1:
|
2018-10-11 18:53:08 +02:00
|
|
|
dout_bar_name = "dout_bar_{0}".format(col)
|
2018-03-20 00:23:13 +01:00
|
|
|
else:
|
2018-10-11 18:53:08 +02:00
|
|
|
dout_bar_name = "dout_bar_{0}_{1}".format(row,col)
|
2018-03-20 00:23:13 +01:00
|
|
|
|
|
|
|
|
return dout_bar_name
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2018-03-20 00:23:13 +01:00
|
|
|
def add_layout_pins(self):
|
2018-07-18 20:38:58 +02:00
|
|
|
for row in range(self.rows):
|
2020-11-03 15:29:17 +01:00
|
|
|
for col in range(self.columns):
|
2018-09-05 01:35:40 +02:00
|
|
|
# Adds power pin on left of row
|
2018-07-18 20:38:58 +02:00
|
|
|
vdd_pin=self.dff_insts[row,col].get_pin("vdd")
|
2022-06-09 02:19:26 +02:00
|
|
|
self.add_power_pin(vdd_pin, loc=vdd_pin.lc(), start_layer=vdd_pin.layer)
|
2018-07-18 20:38:58 +02:00
|
|
|
|
2018-09-05 01:35:40 +02:00
|
|
|
# Adds gnd pin on left of row
|
2018-07-18 20:38:58 +02:00
|
|
|
gnd_pin=self.dff_insts[row,col].get_pin("gnd")
|
2022-06-09 02:19:26 +02:00
|
|
|
self.add_power_pin(gnd_pin, loc=gnd_pin.lc(), start_layer=gnd_pin.layer)
|
2020-11-03 15:29:17 +01:00
|
|
|
|
|
|
|
|
for row in range(self.rows):
|
|
|
|
|
for col in range(self.columns):
|
2018-07-18 20:38:58 +02:00
|
|
|
din_pin = self.dff_insts[row,col].get_pin("D")
|
2019-12-17 20:03:36 +01:00
|
|
|
debug.check(din_pin.layer=="m2","DFF D pin not on metal2")
|
2018-07-18 20:38:58 +02:00
|
|
|
self.add_layout_pin(text=self.get_din_name(row,col),
|
2018-03-20 00:23:13 +01:00
|
|
|
layer=din_pin.layer,
|
|
|
|
|
offset=din_pin.ll(),
|
|
|
|
|
width=din_pin.width(),
|
|
|
|
|
height=din_pin.height())
|
|
|
|
|
|
2018-07-18 20:38:58 +02:00
|
|
|
dout_pin = self.dff_insts[row,col].get_pin("Q")
|
2019-12-17 20:03:36 +01:00
|
|
|
debug.check(dout_pin.layer=="m2","DFF Q pin not on metal2")
|
2018-07-18 20:38:58 +02:00
|
|
|
self.add_layout_pin(text=self.get_dout_name(row,col),
|
2018-03-20 00:23:13 +01:00
|
|
|
layer=dout_pin.layer,
|
|
|
|
|
offset=dout_pin.ll(),
|
|
|
|
|
width=dout_pin.width(),
|
|
|
|
|
height=dout_pin.height())
|
|
|
|
|
|
2018-07-18 20:38:58 +02:00
|
|
|
dout_bar_pin = self.dff_insts[row,col].get_pin("Qb")
|
2019-12-17 20:03:36 +01:00
|
|
|
debug.check(dout_bar_pin.layer=="m2","DFF Qb pin not on metal2")
|
2018-07-18 20:38:58 +02:00
|
|
|
self.add_layout_pin(text=self.get_dout_bar_name(row,col),
|
2018-03-20 00:23:13 +01:00
|
|
|
layer=dout_bar_pin.layer,
|
|
|
|
|
offset=dout_bar_pin.ll(),
|
|
|
|
|
width=dout_bar_pin.width(),
|
|
|
|
|
height=dout_bar_pin.height())
|
|
|
|
|
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2018-03-20 00:23:13 +01:00
|
|
|
# Create vertical spines to a single horizontal rail
|
|
|
|
|
clk_pin = self.dff_insts[0,0].get_pin("clk")
|
2018-10-05 17:18:38 +02:00
|
|
|
clk_ypos = 2*self.m3_pitch+self.m3_width
|
2019-12-17 20:03:36 +01:00
|
|
|
debug.check(clk_pin.layer=="m2","DFF clk pin not on metal2")
|
2018-03-20 00:23:13 +01:00
|
|
|
if self.columns==1:
|
|
|
|
|
self.add_layout_pin(text="clk",
|
2019-12-17 20:03:36 +01:00
|
|
|
layer="m2",
|
2018-03-20 00:23:13 +01:00
|
|
|
offset=clk_pin.ll().scale(1,0),
|
|
|
|
|
width=self.m2_width,
|
|
|
|
|
height=self.height)
|
|
|
|
|
else:
|
2018-07-18 20:38:58 +02:00
|
|
|
self.add_layout_pin_segment_center(text="clk",
|
2019-12-17 20:03:36 +01:00
|
|
|
layer="m3",
|
2018-10-05 17:18:38 +02:00
|
|
|
start=vector(0,clk_ypos),
|
|
|
|
|
end=vector(self.width,clk_ypos))
|
2018-07-18 20:38:58 +02:00
|
|
|
for col in range(self.columns):
|
|
|
|
|
clk_pin = self.dff_insts[0,col].get_pin("clk")
|
2018-03-20 00:23:13 +01:00
|
|
|
# Make a vertical strip for each column
|
2019-12-17 20:03:36 +01:00
|
|
|
self.add_rect(layer="m2",
|
2018-07-18 20:38:58 +02:00
|
|
|
offset=clk_pin.ll().scale(1,0),
|
|
|
|
|
width=self.m2_width,
|
|
|
|
|
height=self.height)
|
2018-03-20 00:23:13 +01:00
|
|
|
# Drop a via to the M3 pin
|
2019-12-17 20:03:36 +01:00
|
|
|
self.add_via_center(layers=self.m2_stack,
|
2020-11-03 15:29:17 +01:00
|
|
|
offset=vector(clk_pin.cx(),clk_ypos))
|