Merged with dev. Fixed conflicts in tests.

This commit is contained in:
Hunter Nichols 2018-12-02 23:09:00 -08:00
commit 722bc907c4
58 changed files with 1379 additions and 1164 deletions

14
.coveragerc Normal file
View File

@ -0,0 +1,14 @@
[run]
omit =
# omit anything in a .local directory anywhere
*/.local/*
# omit everything in /usr
/usr/*
[paths]
source =
/home/gitlab-runner/builds/2fd64746/0
/home/gitlab-runner/builds/2fd64746/1
/home/gitlab-runner/builds/2fd64746/2
/home/gitlab-runner/builds/2fd64746/3
/home/gitlab-runner/builds/2fd64746/4
/home/gitlab-runner/builds/2fd64746/5

View File

@ -1,6 +1,39 @@
before_script:
- . /home/gitlab-runner/setup-paths.sh
- export OPENRAM_HOME="`pwd`/compiler"
- export OPENRAM_TECH="`pwd`/technology"
stages:
- test
- coverage
freepdk45: freepdk45:
script: "/home/gitlab-runner/regress_freepdk45.sh" stage: test
script:
- coverage run -p $OPENRAM_HOME/tests/regress.py -t freepdk45
artifacts:
paths:
- .coverage.*
expire_in: 1 week
scn4m_subm: scn4m_subm:
script: "/home/gitlab-runner/regress_scn4m_subm.sh" stage: test
script:
- coverage run -p $OPENRAM_HOME/tests/regress.py -t scn4m_subm
artifacts:
paths:
- .coverage.*
expire_in: 1 week
coverage:
stage: coverage
script:
- coverage combine
- coverage report
- coverage html -d coverage_html
artifacts:
paths:
- coverage_html
expire_in: 1 week
coverage: '/TOTAL.+ ([0-9]{1,3}%)/'

View File

@ -1,9 +1,18 @@
# OpenRAM # OpenRAM
Master: [![pipeline status](https://scone.soe.ucsc.edu:8888/mrg/PrivateRAM/badges/master/pipeline.svg?private_token=ynB6rSFLzvKUseoBPcwV)](https://github.com/VLSIDA/PrivateRAM/commits/master)
Dev: [![pipeline status](https://scone.soe.ucsc.edu:8888/mrg/PrivateRAM/badges/dev/pipeline.svg?private_token=ynB6rSFLzvKUseoBPcwV)](https://github.com/VLSIDA/PrivateRAM/commits/dev) [![Python 3.5](https://img.shields.io/badge/Python-3.5-green.svg)](https://www.python.org/)
[![Download](images/download.svg)](https://github.com/VLSIDA/PrivateRAM/archive/master.zip)
[![License: BSD 3-clause](./images/license_badge.svg)](./LICENSE) [![License: BSD 3-clause](./images/license_badge.svg)](./LICENSE)
Master:
[![Pipeline Status](https://scone.soe.ucsc.edu:8888/mrg/PrivateRAM/badges/master/pipeline.svg?private_token=ynB6rSFLzvKUseoBPcwV)](https://github.com/VLSIDA/PrivateRAM/commits/master)
![Coverage](https://scone.soe.ucsc.edu:8888/mrg/PrivateRAM/badges/master/coverage.svg?private_token=ynB6rSFLzvKUseoBPcwV)
[![Download](./images/download-stable-blue.svg)](https://github.com/VLSIDA/PrivateRAM/archive/master.zip)
Dev:
[![Pipeline Status](https://scone.soe.ucsc.edu:8888/mrg/PrivateRAM/badges/dev/pipeline.svg?private_token=ynB6rSFLzvKUseoBPcwV)](https://github.com/VLSIDA/PrivateRAM/commits/dev)
![Coverage](https://scone.soe.ucsc.edu:8888/mrg/PrivateRAM/badges/dev/coverage.svg?private_token=ynB6rSFLzvKUseoBPcwV)
[![Download](./images/download-unstable-blue.svg)](https://github.com/VLSIDA/PrivateRAM/archive/dev.zip)
An open-source static random access memory (SRAM) compiler. An open-source static random access memory (SRAM) compiler.
# What is OpenRAM? # What is OpenRAM?

View File

@ -673,11 +673,13 @@ class layout(lef.lef):
offset=bus_pos, offset=bus_pos,
rotate=90) rotate=90)
def add_horizontal_trunk_route(self, pins, trunk_offset, def add_horizontal_trunk_route(self,
pins,
trunk_offset,
layer_stack=("metal1", "via1", "metal2"), layer_stack=("metal1", "via1", "metal2"),
pitch=None): pitch=None):
""" """
Create a trunk route for all pins with the the trunk located at the given y offset. Create a trunk route for all pins with the trunk located at the given y offset.
""" """
if not pitch: if not pitch:
pitch = self.m1_pitch pitch = self.m1_pitch
@ -704,15 +706,18 @@ class layout(lef.lef):
# Route each pin to the trunk # Route each pin to the trunk
for pin in pins: for pin in pins:
# Bend to the center of the trunk so it adds a via automatically
mid = vector(pin.center().x, trunk_offset.y) mid = vector(pin.center().x, trunk_offset.y)
self.add_wire(layer_stack, [pin.center(), mid, trunk_mid]) self.add_path(layer_stack[2], [pin.center(), mid])
self.add_via_center(layers=layer_stack,
offset=mid)
def add_vertical_trunk_route(self, pins, trunk_offset, def add_vertical_trunk_route(self,
pins,
trunk_offset,
layer_stack=("metal1", "via1", "metal2"), layer_stack=("metal1", "via1", "metal2"),
pitch=None): pitch=None):
""" """
Create a trunk route for all pins with the the trunk located at the given x offset. Create a trunk route for all pins with the trunk located at the given x offset.
""" """
if not pitch: if not pitch:
pitch = self.m2_pitch pitch = self.m2_pitch
@ -740,18 +745,21 @@ class layout(lef.lef):
# Route each pin to the trunk # Route each pin to the trunk
for pin in pins: for pin in pins:
# Bend to the center of the trunk so it adds a via automatically
mid = vector(trunk_offset.x, pin.center().y) mid = vector(trunk_offset.x, pin.center().y)
self.add_wire(layer_stack, [pin.center(), mid, trunk_mid]) self.add_path(layer_stack[0], [pin.center(), mid])
self.add_via_center(layers=layer_stack,
offset=mid,
rotate=90)
def create_channel_route(self, netlist, pins, offset, def create_channel_route(self, netlist,
layer_stack=("metal1", "via1", "metal2"), pitch=None, offset,
layer_stack=("metal1", "via1", "metal2"),
pitch=None,
vertical=False): vertical=False):
""" """
The net list is a list of the nets. Each net is a list of pin The net list is a list of the nets. Each net is a list of pins
names to be connected. Pins is a dictionary of the pin names to be connected. Offset is the lower-left of where the
to the pin structures. Offset is the lower-left of where the
routing channel will start. This does NOT try to minimize the routing channel will start. This does NOT try to minimize the
number of tracks -- instead, it picks an order to avoid the number of tracks -- instead, it picks an order to avoid the
vertical conflicts between pins. vertical conflicts between pins.
@ -786,7 +794,10 @@ class layout(lef.lef):
def vcg_pin_overlap(pin1, pin2, vertical): def vcg_pin_overlap(pin1, pin2, vertical):
""" Check for vertical or horizontal overlap of the two pins """ """ Check for vertical or horizontal overlap of the two pins """
# FIXME: If the pins are not in a row, this may break.
# However, a top pin shouldn't overlap another top pin, for example, so the
# extra comparison *shouldn't* matter.
# Pin 1 must be in the "BOTTOM" set # Pin 1 must be in the "BOTTOM" set
x_overlap = pin1.by() < pin2.by() and abs(pin1.center().x-pin2.center().x)<pitch x_overlap = pin1.by() < pin2.by() and abs(pin1.center().x-pin2.center().x)<pitch
@ -815,10 +826,7 @@ class layout(lef.lef):
for pin_list in netlist: for pin_list in netlist:
net_name = "n{}".format(index) net_name = "n{}".format(index)
index += 1 index += 1
nets[net_name] = [] nets[net_name] = pin_list
for pin_name in pin_list:
pin = pins[pin_name]
nets[net_name].append(pin)
# Find the vertical pin conflicts # Find the vertical pin conflicts
# FIXME: O(n^2) but who cares for now # FIXME: O(n^2) but who cares for now
@ -834,8 +842,6 @@ class layout(lef.lef):
if vcg_nets_overlap(nets[net_name1], nets[net_name2], vertical): if vcg_nets_overlap(nets[net_name1], nets[net_name2], vertical):
vcg[net_name2].append(net_name1) vcg[net_name2].append(net_name1)
#FIXME: What if we have a cycle?
# list of routes to do # list of routes to do
while vcg: while vcg:
#from pprint import pformat #from pprint import pformat
@ -868,23 +874,21 @@ class layout(lef.lef):
offset += vector(0,pitch) offset += vector(0,pitch)
def create_vertical_channel_route(self, netlist, pins, offset, def create_vertical_channel_route(self, netlist, offset,
layer_stack=("metal1", "via1", "metal2"), layer_stack=("metal1", "via1", "metal2"),
pitch=None): pitch=None):
""" """
Wrapper to create a vertical channel route Wrapper to create a vertical channel route
""" """
self.create_channel_route(netlist, pins, offset, layer_stack, self.create_channel_route(netlist, offset, layer_stack, pitch, vertical=True)
pitch, vertical=True)
def create_horizontal_channel_route(self, netlist, pins, offset, def create_horizontal_channel_route(self, netlist, offset,
layer_stack=("metal1", "via1", "metal2"), layer_stack=("metal1", "via1", "metal2"),
pitch=None): pitch=None):
""" """
Wrapper to create a horizontal channel route Wrapper to create a horizontal channel route
""" """
self.create_channel_route(netlist, pins, offset, self.create_channel_route(netlist, offset, layer_stack, pitch, vertical=False)
layer_stack, pitch, vertical=False)
def add_enclosure(self, insts, layer="nwell"): def add_enclosure(self, insts, layer="nwell"):
""" Add a layer that surrounds the given instances. Useful """ Add a layer that surrounds the given instances. Useful
@ -922,21 +926,30 @@ class layout(lef.lef):
def add_power_pin(self, name, loc, rotate=90): def add_power_pin(self, name, loc, rotate=90, start_layer="metal1"):
""" """
Add a single power pin from M3 down to M1 at the given center location Add a single power pin from M3 down to M1 at the given center location.
The starting layer is specified to determine which vias are needed.
""" """
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=loc, if start_layer=="metal1":
rotate=float(rotate)) self.add_via_center(layers=("metal1", "via1", "metal2"),
via=self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=loc, offset=loc,
rotate=float(rotate)) rotate=float(rotate))
self.add_layout_pin_rect_center(text=name, if start_layer=="metal1" or start_layer=="metal2":
layer="metal3", via=self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=loc, offset=loc,
width=via.width, rotate=float(rotate))
height=via.height) if start_layer=="metal3":
self.add_layout_pin_rect_center(text=name,
layer="metal3",
offset=loc)
else:
self.add_layout_pin_rect_center(text=name,
layer="metal3",
offset=loc,
width=via.width,
height=via.height)
def add_power_ring(self, bbox): def add_power_ring(self, bbox):
""" """

View File

@ -80,7 +80,7 @@ class pbitcell(design.design):
self.offset_all_coordinates() self.offset_all_coordinates()
gnd_overlap = vector(0, 0.5*contact.well.width) gnd_overlap = vector(0, 0.5*contact.well.width)
self.translate_all(gnd_overlap) self.translate_all(gnd_overlap)
self.DRC_LVS()
def add_pins(self): def add_pins(self):
""" add pins and set names for bitlines and wordlines """ """ add pins and set names for bitlines and wordlines """
@ -323,20 +323,21 @@ class pbitcell(design.design):
# Add rails for vdd and gnd # Add rails for vdd and gnd
gnd_ypos = self.rowline_offset - self.total_ports*self.rowline_spacing gnd_ypos = self.rowline_offset - self.total_ports*self.rowline_spacing
self.gnd_position = vector(0, gnd_ypos) self.gnd_position = vector(0, gnd_ypos)
self.gnd = self.add_layout_pin_rect_center(text="gnd", self.add_rect_center(layer="metal1",
layer="metal1", offset=self.gnd_position,
offset=self.gnd_position, width=self.width,
width=self.width, height=self.m1_width)
height=self.m1_width) self.add_power_pin("gnd", vector(0,gnd_ypos))
vdd_ypos = self.inverter_nmos_ypos + self.inverter_nmos.active_height + self.inverter_gap + self.inverter_pmos.active_height + self.vdd_offset vdd_ypos = self.inverter_nmos_ypos + self.inverter_nmos.active_height + self.inverter_gap + self.inverter_pmos.active_height + self.vdd_offset
self.vdd_position = vector(0, vdd_ypos) self.vdd_position = vector(0, vdd_ypos)
self.vdd = self.add_layout_pin_rect_center(text="vdd", self.add_rect_center(layer="metal1",
layer="metal1", offset=self.vdd_position,
offset=self.vdd_position, width=self.width,
width=self.width, height=self.m1_width)
height=self.m1_width) self.add_power_pin("vdd", vector(0,vdd_ypos))
def create_readwrite_ports(self): def create_readwrite_ports(self):
""" """
Creates read/write ports to the bit cell. A differential pair of transistor can both read and write, like in a 6T cell. Creates read/write ports to the bit cell. A differential pair of transistor can both read and write, like in a 6T cell.

View File

@ -30,6 +30,7 @@ class functional(simulation):
self.set_feasible_period(sram, spfile, corner) self.set_feasible_period(sram, spfile, corner)
self.set_stimulus_variables() self.set_stimulus_variables()
self.create_signal_names() self.create_signal_names()
# Number of checks can be changed # Number of checks can be changed
self.num_cycles = 2 self.num_cycles = 2
@ -154,17 +155,17 @@ class functional(simulation):
sp_read_value = "" sp_read_value = ""
for bit in range(self.word_size): for bit in range(self.word_size):
value = parse_spice_list("timing", "v{0}.{1}ck{2}".format(dout_port.lower(),bit,check)) value = parse_spice_list("timing", "v{0}.{1}ck{2}".format(dout_port.lower(),bit,check))
if value > 0.88 * self.vdd_voltage: if value > self.v_high:
sp_read_value = "1" + sp_read_value sp_read_value = "1" + sp_read_value
elif value < 0.12 * self.vdd_voltage: elif value < self.v_low:
sp_read_value = "0" + sp_read_value sp_read_value = "0" + sp_read_value
else: else:
error ="FAILED: {0}_{1} value {2} at time {3}n does not fall within noise margins <{4} or >{5}.".format(dout_port, error ="FAILED: {0}_{1} value {2} at time {3}n does not fall within noise margins <{4} or >{5}.".format(dout_port,
bit, bit,
value, value,
eo_period, eo_period,
0.12*self.vdd_voltage, self.v_low,
0.88*self.vdd_voltage) self.v_high)
return (0, error) return (0, error)
self.read_check.append([sp_read_value, dout_port, eo_period, check]) self.read_check.append([sp_read_value, dout_port, eo_period, check])

View File

@ -37,6 +37,9 @@ class simulation():
self.period = tech.spice["feasible_period"] self.period = tech.spice["feasible_period"]
self.slew = tech.spice["rise_time"]*2 self.slew = tech.spice["rise_time"]*2
self.load = tech.spice["msflop_in_cap"]*4 self.load = tech.spice["msflop_in_cap"]*4
self.v_high = self.vdd_voltage - tech.spice["v_threshold_typical"]
self.v_low = tech.spice["v_threshold_typical"]
self.gnd_voltage = 0 self.gnd_voltage = 0
def set_stimulus_variables(self): def set_stimulus_variables(self):

View File

@ -3,8 +3,8 @@ num_words = 16
tech_name = "scn4m_subm" tech_name = "scn4m_subm"
process_corners = ["TT"] process_corners = ["TT"]
supply_voltages = [ 3.3 ] supply_voltages = [5.0]
temperatures = [ 25 ] temperatures = [25]
output_path = "temp" output_path = "temp"
output_name = "sram_{0}_{1}_{2}".format(word_size,num_words,tech_name) output_name = "sram_{0}_{1}_{2}".format(word_size,num_words,tech_name)

View File

@ -10,7 +10,6 @@ from pinv import pinv
from pnand2 import pnand2 from pnand2 import pnand2
from pnor2 import pnor2 from pnor2 import pnor2
from vector import vector from vector import vector
from pinvbuf import pinvbuf
from globals import OPTS from globals import OPTS
@ -85,11 +84,12 @@ class bank(design.design):
self.add_pin("bank_sel{}".format(port),"INPUT") self.add_pin("bank_sel{}".format(port),"INPUT")
for port in self.read_ports: for port in self.read_ports:
self.add_pin("s_en{0}".format(port), "INPUT") self.add_pin("s_en{0}".format(port), "INPUT")
for port in self.read_ports:
self.add_pin("p_en_bar{0}".format(port), "INPUT")
for port in self.write_ports: for port in self.write_ports:
self.add_pin("w_en{0}".format(port), "INPUT") self.add_pin("w_en{0}".format(port), "INPUT")
for port in self.all_ports: for port in self.all_ports:
self.add_pin("clk_buf_bar{0}".format(port),"INPUT") self.add_pin("wl_en{0}".format(port), "INPUT")
self.add_pin("clk_buf{0}".format(port),"INPUT")
self.add_pin("vdd","POWER") self.add_pin("vdd","POWER")
self.add_pin("gnd","GROUND") self.add_pin("gnd","GROUND")
@ -226,16 +226,19 @@ class bank(design.design):
# UPPER LEFT QUADRANT # UPPER LEFT QUADRANT
# To the left of the bitcell array # To the left of the bitcell array
# The wordline driver is placed to the right of the main decoder width. # The wordline driver is placed to the right of the main decoder width.
x_offset = self.central_bus_width + self.wordline_driver.width x_offset = self.m2_gap + self.wordline_driver.width
self.wordline_driver_offsets[port] = vector(-x_offset,0) self.wordline_driver_offsets[port] = vector(-x_offset,0)
x_offset += self.row_decoder.width + self.m2_gap x_offset += self.row_decoder.width + self.m2_gap
self.row_decoder_offsets[port] = vector(-x_offset,0) self.row_decoder_offsets[port] = vector(-x_offset,0)
# LOWER LEFT QUADRANT # LOWER LEFT QUADRANT
# Place the col decoder left aligned with wordline driver plus halfway under row decoder
# Place the col decoder left aligned with row decoder (x_offset doesn't change) # Place the col decoder left aligned with row decoder (x_offset doesn't change)
# Below the bitcell array with well spacing # Below the bitcell array with well spacing
x_offset = self.central_bus_width[port] + self.wordline_driver.width
if self.col_addr_size > 0: if self.col_addr_size > 0:
y_offset = self.column_decoder.height x_offset += self.column_decoder.width + self.col_addr_bus_width
y_offset = self.m2_gap + self.column_decoder.height
else: else:
y_offset = 0 y_offset = 0
y_offset += 2*drc("well_to_well") y_offset += 2*drc("well_to_well")
@ -283,16 +286,18 @@ class bank(design.design):
# LOWER RIGHT QUADRANT # LOWER RIGHT QUADRANT
# To the left of the bitcell array # To the left of the bitcell array
# The wordline driver is placed to the right of the main decoder width. # The wordline driver is placed to the right of the main decoder width.
x_offset = self.bitcell_array.width + self.central_bus_width + self.wordline_driver.width x_offset = self.bitcell_array.width + self.m2_gap + self.wordline_driver.width
self.wordline_driver_offsets[port] = vector(x_offset,0) self.wordline_driver_offsets[port] = vector(x_offset,0)
x_offset += self.row_decoder.width + self.m2_gap x_offset += self.row_decoder.width + self.m2_gap
self.row_decoder_offsets[port] = vector(x_offset,0) self.row_decoder_offsets[port] = vector(x_offset,0)
# UPPER RIGHT QUADRANT # UPPER RIGHT QUADRANT
# Place the col decoder right aligned with row decoder (x_offset doesn't change) # Place the col decoder right aligned with wordline driver plus halfway under row decoder
# Above the bitcell array with a well spacing # Above the bitcell array with a well spacing
x_offset = self.bitcell_array.width + self.central_bus_width[port] + self.wordline_driver.width
if self.col_addr_size > 0: if self.col_addr_size > 0:
y_offset = self.bitcell_array.height + self.column_decoder.height x_offset += self.column_decoder.width + self.col_addr_bus_width
y_offset = self.bitcell_array.height + self.column_decoder.height + self.m2_gap
else: else:
y_offset = self.bitcell_array.height y_offset = self.bitcell_array.height
y_offset += 2*drc("well_to_well") y_offset += 2*drc("well_to_well")
@ -349,21 +354,25 @@ class bank(design.design):
# FIXME: This spacing should be width dependent... # FIXME: This spacing should be width dependent...
self.supply_rail_pitch = self.supply_rail_width + 4*self.m2_space self.supply_rail_pitch = self.supply_rail_width + 4*self.m2_space
# Number of control lines in the bus
self.num_control_lines = 4
# The order of the control signals on the control bus: # The order of the control signals on the control bus:
self.input_control_signals = [] self.input_control_signals = []
port_num = 0 port_num = 0
for port in range(OPTS.num_rw_ports): for port in range(OPTS.num_rw_ports):
self.input_control_signals.append(["clk_buf{}".format(port_num), "clk_buf_bar{}".format(port_num), "w_en{}".format(port_num), "s_en{}".format(port_num)]) self.input_control_signals.append(["wl_en{}".format(port_num), "w_en{}".format(port_num), "s_en{}".format(port_num), "p_en_bar{}".format(port_num)])
port_num += 1 port_num += 1
for port in range(OPTS.num_w_ports): for port in range(OPTS.num_w_ports):
self.input_control_signals.append(["clk_buf{}".format(port_num), "clk_buf_bar{}".format(port_num), "w_en{}".format(port_num)]) self.input_control_signals.append(["wl_en{}".format(port_num), "w_en{}".format(port_num)])
port_num += 1 port_num += 1
for port in range(OPTS.num_r_ports): for port in range(OPTS.num_r_ports):
self.input_control_signals.append(["clk_buf{}".format(port_num), "clk_buf_bar{}".format(port_num), "s_en{}".format(port_num)]) self.input_control_signals.append(["wl_en{}".format(port_num), "s_en{}".format(port_num), "p_en_bar{}".format(port_num)])
port_num += 1 port_num += 1
# Number of control lines in the bus for each port
self.num_control_lines = [len(x) for x in self.input_control_signals]
# The width of this bus is needed to place other modules (e.g. decoder) for each port
self.central_bus_width = [self.m2_pitch*x + self.m2_width for x in self.num_control_lines]
# These will be outputs of the gaters if this is multibank, if not, normal signals. # These will be outputs of the gaters if this is multibank, if not, normal signals.
self.control_signals = [] self.control_signals = []
for port in self.all_ports: for port in self.all_ports:
@ -371,15 +380,13 @@ class bank(design.design):
self.control_signals.append(["gated_"+str for str in self.input_control_signals[port]]) self.control_signals.append(["gated_"+str for str in self.input_control_signals[port]])
else: else:
self.control_signals.append(self.input_control_signals[port]) self.control_signals.append(self.input_control_signals[port])
# The central bus is the column address (one hot) and row address (binary) # The central bus is the column address (one hot) and row address (binary)
if self.col_addr_size>0: if self.col_addr_size>0:
self.num_col_addr_lines = 2**self.col_addr_size self.num_col_addr_lines = 2**self.col_addr_size
else: else:
self.num_col_addr_lines = 0 self.num_col_addr_lines = 0
self.col_addr_bus_width = self.m2_pitch*self.num_col_addr_lines
# The width of this bus is needed to place other modules (e.g. decoder)
# A width on each side too
self.central_bus_width = self.m2_pitch * self.num_control_lines + self.m2_width
# A space for wells or jogging m2 # A space for wells or jogging m2
self.m2_gap = max(2*drc("pwell_to_nwell") + drc("well_enclosure_active"), self.m2_gap = max(2*drc("pwell_to_nwell") + drc("well_enclosure_active"),
@ -401,16 +408,15 @@ class bank(design.design):
setattr (self, "mod_"+mod_name, mod_class) setattr (self, "mod_"+mod_name, mod_class)
self.bitcell = self.mod_bitcell()
self.bitcell_array = self.mod_bitcell_array(cols=self.num_cols, self.bitcell_array = self.mod_bitcell_array(cols=self.num_cols,
rows=self.num_rows) rows=self.num_rows)
self.add_mod(self.bitcell_array) self.add_mod(self.bitcell_array)
# create arrays of bitline and bitline_bar names for read, write, or all ports # create arrays of bitline and bitline_bar names for read, write, or all ports
self.bitcell = self.mod_bitcell()
self.bl_names = self.bitcell.list_all_bl_names() self.bl_names = self.bitcell.list_all_bl_names()
self.br_names = self.bitcell.list_all_br_names() self.br_names = self.bitcell.list_all_br_names()
self.wl_names = self.bitcell.list_all_wl_names() self.wl_names = self.bitcell.list_all_wl_names()
self.bitline_names = self.bitcell.list_all_bitline_names() self.bitline_names = self.bitcell.list_all_bitline_names()
@ -489,7 +495,7 @@ class bank(design.design):
for i in range(self.num_cols): for i in range(self.num_cols):
temp.append(self.bl_names[port]+"_{0}".format(i)) temp.append(self.bl_names[port]+"_{0}".format(i))
temp.append(self.br_names[port]+"_{0}".format(i)) temp.append(self.br_names[port]+"_{0}".format(i))
temp.extend([self.prefix+"clk_buf_bar{0}".format(port), "vdd"]) temp.extend([self.prefix+"p_en_bar{0}".format(port), "vdd"])
self.connect_inst(temp) self.connect_inst(temp)
@ -664,7 +670,7 @@ class bank(design.design):
temp.append("dec_out{0}_{1}".format(port,row)) temp.append("dec_out{0}_{1}".format(port,row))
for row in range(self.num_rows): for row in range(self.num_rows):
temp.append(self.wl_names[port]+"_{0}".format(row)) temp.append(self.wl_names[port]+"_{0}".format(row))
temp.append(self.prefix+"clk_buf{0}".format(port)) temp.append(self.prefix+"wl_en{0}".format(port))
temp.append("vdd") temp.append("vdd")
temp.append("gnd") temp.append("gnd")
self.connect_inst(temp) self.connect_inst(temp)
@ -691,16 +697,19 @@ class bank(design.design):
if self.col_addr_size == 0: if self.col_addr_size == 0:
return return
elif self.col_addr_size == 1: elif self.col_addr_size == 1:
from pinvbuf import pinvbuf
self.column_decoder = pinvbuf(height=self.mod_dff.height) self.column_decoder = pinvbuf(height=self.mod_dff.height)
self.add_mod(self.column_decoder)
elif self.col_addr_size == 2: elif self.col_addr_size == 2:
self.column_decoder = self.row_decoder.pre2_4 from hierarchical_predecode2x4 import hierarchical_predecode2x4 as pre2x4
self.column_decoder = pre2x4(height=self.mod_dff.height)
elif self.col_addr_size == 3: elif self.col_addr_size == 3:
self.column_decoder = self.row_decoder.pre3_8 from hierarchical_predecode3x8 import hierarchical_predecode3x8 as pre3x8
self.column_decoder = pre3x8(height=self.mod_dff.height)
else: else:
# No error checking before? # No error checking before?
debug.error("Invalid column decoder?",-1) debug.error("Invalid column decoder?",-1)
self.add_mod(self.column_decoder)
self.column_decoder_inst = [None]*len(self.all_ports) self.column_decoder_inst = [None]*len(self.all_ports)
for port in self.all_ports: for port in self.all_ports:
self.column_decoder_inst[port] = self.add_inst(name="col_address_decoder{}".format(port), self.column_decoder_inst[port] = self.add_inst(name="col_address_decoder{}".format(port),
@ -774,14 +783,14 @@ class bank(design.design):
""" Route the bank select logic. """ """ Route the bank select logic. """
if self.port_id[port] == "rw": if self.port_id[port] == "rw":
bank_sel_signals = ["clk_buf", "clk_buf_bar", "w_en", "s_en", "bank_sel"] bank_sel_signals = ["clk_buf", "w_en", "s_en", "p_en_bar", "bank_sel"]
gated_bank_sel_signals = ["gated_clk_buf", "gated_clk_buf_bar", "gated_w_en", "gated_s_en"] gated_bank_sel_signals = ["gated_clk_buf", "gated_w_en", "gated_s_en", "gated_p_en_bar"]
elif self.port_id[port] == "w": elif self.port_id[port] == "w":
bank_sel_signals = ["clk_buf", "clk_buf_bar", "w_en", "bank_sel"] bank_sel_signals = ["clk_buf", "w_en", "bank_sel"]
gated_bank_sel_signals = ["gated_clk_buf", "gated_clk_buf_bar", "gated_w_en"] gated_bank_sel_signals = ["gated_clk_buf", "gated_w_en"]
else: else:
bank_sel_signals = ["clk_buf", "clk_buf_bar", "s_en", "bank_sel"] bank_sel_signals = ["clk_buf", "s_en", "p_en_bar", "bank_sel"]
gated_bank_sel_signals = ["gated_clk_buf", "gated_clk_buf_bar", "gated_s_en"] gated_bank_sel_signals = ["gated_clk_buf", "gated_s_en", "gated_p_en_bar"]
copy_control_signals = self.input_control_signals[port]+["bank_sel{}".format(port)] copy_control_signals = self.input_control_signals[port]+["bank_sel{}".format(port)]
for signal in range(len(copy_control_signals)): for signal in range(len(copy_control_signals)):
@ -835,8 +844,9 @@ class bank(design.design):
# Port 0 # Port 0
# The bank is at (0,0), so this is to the left of the y-axis. # The bank is at (0,0), so this is to the left of the y-axis.
# 2 pitches on the right for vias/jogs to access the inputs # 2 pitches on the right for vias/jogs to access the inputs
control_bus_offset = vector(-self.m2_pitch * self.num_control_lines - self.m2_width, self.min_y_offset) control_bus_offset = vector(-self.m2_pitch * self.num_control_lines[0] - self.m2_width, self.min_y_offset)
control_bus_length = self.max_y_offset - self.min_y_offset # The control bus is routed up to two pitches below the bitcell array
control_bus_length = -2*self.m1_pitch - self.min_y_offset
self.bus_xoffset[0] = self.create_bus(layer="metal2", self.bus_xoffset[0] = self.create_bus(layer="metal2",
pitch=self.m2_pitch, pitch=self.m2_pitch,
offset=control_bus_offset, offset=control_bus_offset,
@ -847,7 +857,11 @@ class bank(design.design):
# Port 1 # Port 1
if len(self.all_ports)==2: if len(self.all_ports)==2:
control_bus_offset = vector(self.bitcell_array.width + self.m2_width, self.min_y_offset) # The other control bus is routed up to two pitches above the bitcell array
control_bus_length = self.max_y_offset - self.bitcell_array.height - 2*self.m1_pitch
control_bus_offset = vector(self.bitcell_array.width + self.m2_width,
self.max_y_offset - control_bus_length)
self.bus_xoffset[1] = self.create_bus(layer="metal2", self.bus_xoffset[1] = self.create_bus(layer="metal2",
pitch=self.m2_pitch, pitch=self.m2_pitch,
offset=control_bus_offset, offset=control_bus_offset,
@ -891,6 +905,9 @@ class bank(design.design):
inst1 = self.bitcell_array_inst inst1 = self.bitcell_array_inst
inst1_bl_name = self.bl_names[port]+"_{}" inst1_bl_name = self.bl_names[port]+"_{}"
inst1_br_name = self.br_names[port]+"_{}" inst1_br_name = self.br_names[port]+"_{}"
# The column mux is constructed to match the bitline pitch, so we can directly connect
# here and not channel route the bitlines.
self.connect_bitlines(inst1=inst1, inst2=inst2, num_bits=self.num_cols, self.connect_bitlines(inst1=inst1, inst2=inst2, num_bits=self.num_cols,
inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name) inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name)
@ -911,8 +928,8 @@ class bank(design.design):
inst1_bl_name = "bl_{}" inst1_bl_name = "bl_{}"
inst1_br_name = "br_{}" inst1_br_name = "br_{}"
self.connect_bitlines(inst1=inst1, inst2=inst2, num_bits=self.word_size, self.channel_route_bitlines(inst1=inst1, inst2=inst2, num_bits=self.word_size,
inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name) inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name)
def route_write_driver_to_column_mux_or_bitcell_array(self, port): def route_write_driver_to_column_mux_or_bitcell_array(self, port):
""" Routing of BL and BR between sense_amp and column mux or bitcell array """ """ Routing of BL and BR between sense_amp and column mux or bitcell array """
@ -937,7 +954,11 @@ class bank(design.design):
inst1 = self.write_driver_array_inst[port] inst1 = self.write_driver_array_inst[port]
inst2 = self.sense_amp_array_inst[port] inst2 = self.sense_amp_array_inst[port]
self.connect_bitlines(inst1, inst2, self.word_size)
# These should be pitch matched in the cell library,
# but just in case, do a channel route.
self.channel_route_bitlines(inst1=inst1, inst2=inst2, num_bits=self.word_size)
def route_sense_amp_out(self, port): def route_sense_amp_out(self, port):
@ -992,14 +1013,10 @@ class bank(design.design):
# of tracks in teh channel router yet. If we did, we could route all the bits at once! # of tracks in teh channel router yet. If we did, we could route all the bits at once!
offset = bottom_inst.ul() + vector(0,self.m1_pitch) offset = bottom_inst.ul() + vector(0,self.m1_pitch)
for bit in range(num_bits): for bit in range(num_bits):
bottom_names = [bottom_bl_name.format(bit), bottom_br_name.format(bit)] bottom_names = [bottom_inst.get_pin(bottom_bl_name.format(bit)), bottom_inst.get_pin(bottom_br_name.format(bit))]
top_names = [top_bl_name.format(bit), top_br_name.format(bit)] top_names = [top_inst.get_pin(top_bl_name.format(bit)), top_inst.get_pin(top_br_name.format(bit))]
route_map = list(zip(bottom_names, top_names)) route_map = list(zip(bottom_names, top_names))
bottom_pins = {key: bottom_inst.get_pin(key) for key in bottom_names } self.create_horizontal_channel_route(route_map, offset)
top_pins = {key: top_inst.get_pin(key) for key in top_names }
all_pins = {**bottom_pins, **top_pins}
debug.check(len(all_pins)==len(bottom_pins)+len(top_pins),"Duplicate named pins in bitline channel route.")
self.create_horizontal_channel_route(route_map, all_pins, offset)
def connect_bitlines(self, inst1, inst2, num_bits, def connect_bitlines(self, inst1, inst2, num_bits,
@ -1078,79 +1095,41 @@ class bank(design.design):
self.add_path("metal1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos]) self.add_path("metal1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos])
def route_column_address_lines(self, port): def route_column_address_lines(self, port):
""" Connecting the select lines of column mux to the address bus """
if not self.col_addr_size>0:
return
if self.col_addr_size == 1:
# Connect to sel[0] and sel[1]
decode_names = ["Zb", "Z"]
# The Address LSB
self.copy_layout_pin(self.column_decoder_inst[port], "A", "addr{}_0".format(port))
elif self.col_addr_size > 1:
decode_names = []
for i in range(self.num_col_addr_lines):
decode_names.append("out_{}".format(i))
for i in range(self.col_addr_size):
decoder_name = "in_{}".format(i)
addr_name = "addr{0}_{1}".format(port,i)
self.copy_layout_pin(self.column_decoder_inst[port], decoder_name, addr_name)
if port%2: if port%2:
self.route_column_address_lines_right(port) offset = self.column_decoder_inst[port].ll() - vector(self.num_col_addr_lines*self.m2_pitch, 0)
else: else:
self.route_column_address_lines_left(port) offset = self.column_decoder_inst[port].lr() + vector(self.m2_pitch, 0)
def route_column_address_lines_left(self, port): decode_pins = [self.column_decoder_inst[port].get_pin(x) for x in decode_names]
""" Connecting the select lines of column mux to the address bus """
if not self.col_addr_size>0:
return
if self.col_addr_size == 1:
# Connect to sel[0] and sel[1]
decode_names = ["Zb", "Z"]
# The Address LSB
self.copy_layout_pin(self.column_decoder_inst[port], "A", "addr{}_0".format(port))
elif self.col_addr_size > 1:
decode_names = []
for i in range(self.num_col_addr_lines):
decode_names.append("out_{}".format(i))
for i in range(self.col_addr_size):
decoder_name = "in_{}".format(i)
addr_name = "addr{0}_{1}".format(port,i)
self.copy_layout_pin(self.column_decoder_inst[port], decoder_name, addr_name)
offset = self.column_decoder_inst[port].lr() + vector(self.m2_pitch, 0)
sel_names = ["sel_{}".format(x) for x in range(self.num_col_addr_lines)]
route_map = list(zip(decode_names, sel_names))
decode_pins = {key: self.column_decoder_inst[port].get_pin(key) for key in decode_names }
column_mux_pins = {key: self.column_mux_array_inst[port].get_pin(key) for key in sel_names }
# Combine the dff and bank pins into a single dictionary of pin name to pin.
all_pins = {**decode_pins, **column_mux_pins}
self.create_vertical_channel_route(route_map, all_pins, offset)
def route_column_address_lines_right(self, port):
""" Connecting the select lines of column mux to the address bus """
if not self.col_addr_size>0:
return
if self.col_addr_size == 1:
# Connect to sel[0] and sel[1]
decode_names = ["Zb", "Z"]
# The Address LSB
self.copy_layout_pin(self.column_decoder_inst[port], "A", "addr{}_0".format(port))
elif self.col_addr_size > 1:
decode_names = []
for i in range(self.num_col_addr_lines):
decode_names.append("out_{}".format(i))
for i in range(self.col_addr_size):
decoder_name = "in_{}".format(i)
addr_name = "addr{0}_{1}".format(port,i)
self.copy_layout_pin(self.column_decoder_inst[port], decoder_name, addr_name)
offset = self.column_decoder_inst[port].ll() - vector(self.num_col_addr_lines*self.m2_pitch, 0)
sel_names = ["sel_{}".format(x) for x in range(self.num_col_addr_lines)]
route_map = list(zip(decode_names, sel_names))
decode_pins = {key: self.column_decoder_inst[port].get_pin(key) for key in decode_names }
column_mux_pins = {key: self.column_mux_array_inst[port].get_pin(key) for key in sel_names }
# Combine the dff and bank pins into a single dictionary of pin name to pin.
all_pins = {**decode_pins, **column_mux_pins}
self.create_vertical_channel_route(route_map, all_pins, offset)
sel_names = ["sel_{}".format(x) for x in range(self.num_col_addr_lines)]
column_mux_pins = [self.column_mux_array_inst[port].get_pin(x) for x in sel_names]
route_map = list(zip(decode_pins, column_mux_pins))
self.create_vertical_channel_route(route_map, offset)
def add_lvs_correspondence_points(self): def add_lvs_correspondence_points(self):
""" This adds some points for easier debugging if LVS goes wrong. """ This adds some points for easier debugging if LVS goes wrong.
@ -1209,7 +1188,7 @@ class bank(design.design):
connection = [] connection = []
if port in self.read_ports: if port in self.read_ports:
connection.append((self.prefix+"clk_buf_bar{}".format(port), self.precharge_array_inst[port].get_pin("en").lc())) connection.append((self.prefix+"p_en_bar{}".format(port), self.precharge_array_inst[port].get_pin("en_bar").lc()))
if port in self.write_ports: if port in self.write_ports:
connection.append((self.prefix+"w_en{}".format(port), self.write_driver_array_inst[port].get_pin("en").lc())) connection.append((self.prefix+"w_en{}".format(port), self.write_driver_array_inst[port].get_pin("en").lc()))
@ -1225,9 +1204,13 @@ class bank(design.design):
rotate=90) rotate=90)
# clk to wordline_driver # clk to wordline_driver
control_signal = self.prefix+"clk_buf{}".format(port) control_signal = self.prefix+"wl_en{}".format(port)
pin_pos = self.wordline_driver_inst[port].get_pin("en").bc() if port%2:
mid_pos = pin_pos - vector(0,self.m1_pitch) pin_pos = self.wordline_driver_inst[port].get_pin("en_bar").uc()
mid_pos = pin_pos + vector(0,self.m2_gap) # to route down to the top of the bus
else:
pin_pos = self.wordline_driver_inst[port].get_pin("en_bar").bc()
mid_pos = pin_pos - vector(0,self.m2_gap) # to route down to the top of the bus
control_x_offset = self.bus_xoffset[port][control_signal].x control_x_offset = self.bus_xoffset[port][control_signal].x
control_pos = vector(control_x_offset, mid_pos.y) control_pos = vector(control_x_offset, mid_pos.y)
self.add_wire(("metal1","via1","metal2"),[pin_pos, mid_pos, control_pos]) self.add_wire(("metal1","via1","metal2"),[pin_pos, mid_pos, control_pos])

View File

@ -132,40 +132,14 @@ class bitcell_array(design.design):
# increments to the next row height # increments to the next row height
offset.y += self.cell.height offset.y += self.cell.height
# For every second row and column, add a via for vdd # For every second row and column, add a via for gnd and vdd
for row in range(self.row_size): for row in range(self.row_size):
for col in range(self.column_size): for col in range(self.column_size):
inst = self.cell_inst[row,col] inst = self.cell_inst[row,col]
for vdd_pin in inst.get_pins("vdd"): for pin_name in ["vdd", "gnd"]:
# Drop to M1 if needed for pin in inst.get_pins(pin_name):
if vdd_pin.layer == "metal1": self.add_power_pin(pin_name, pin.center(), 0, pin.layer)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=vdd_pin.center(),
rotate=90)
# Always drop to M2
self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=vdd_pin.center())
self.add_layout_pin_rect_center(text="vdd",
layer="metal3",
offset=vdd_pin.center())
# For every second row and column (+1), add a via for gnd
for row in range(self.row_size):
for col in range(self.column_size):
inst = self.cell_inst[row,col]
for gnd_pin in inst.get_pins("gnd"):
# Drop to M1 if needed
if gnd_pin.layer == "metal1":
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=gnd_pin.center(),
rotate=90)
# Always drop to M2
self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=gnd_pin.center())
self.add_layout_pin_rect_center(text="gnd",
layer="metal3",
offset=gnd_pin.center())
def analytical_delay(self, slew, load=0): def analytical_delay(self, slew, load=0):
from tech import drc from tech import drc

View File

@ -4,11 +4,12 @@ from tech import drc, parameter
import debug import debug
import contact import contact
from pinv import pinv from pinv import pinv
from pbuf import pbuf
from pand2 import pand2
from pnand2 import pnand2 from pnand2 import pnand2
from pnand3 import pnand3
from pinvbuf import pinvbuf from pinvbuf import pinvbuf
from dff_inv import dff_inv from dff_buf import dff_buf
from dff_inv_array import dff_inv_array from dff_buf_array import dff_buf_array
import math import math
from vector import vector from vector import vector
from globals import OPTS from globals import OPTS
@ -30,8 +31,8 @@ class control_logic(design.design):
self.port_type = port_type self.port_type = port_type
#This is needed to resize the delay chain. Likely to be changed at some point. #This is needed to resize the delay chain. Likely to be changed at some point.
self.sram=sram #self.sram=sram
#self.sram=None #disable re-sizing for debugging self.sram=None #disable re-sizing for debugging, FIXME: resizing is not working, needs to be adjusted for new control logic.
self.wl_timing_tolerance = 1 #Determines how much larger the sen delay should be. Accounts for possible error in model. self.wl_timing_tolerance = 1 #Determines how much larger the sen delay should be. Accounts for possible error in model.
self.parasitic_inv_delay = parameter["min_inv_para_delay"] #Keeping 0 for now until further testing. self.parasitic_inv_delay = parameter["min_inv_para_delay"] #Keeping 0 for now until further testing.
@ -54,9 +55,7 @@ class control_logic(design.design):
""" Create layout and route between modules """ """ Create layout and route between modules """
self.place_instances() self.place_instances()
self.route_all() self.route_all()
#self.add_lvs_correspondence_points() #self.add_lvs_correspondence_points()
self.DRC_LVS() self.DRC_LVS()
@ -72,28 +71,37 @@ class control_logic(design.design):
def add_modules(self): def add_modules(self):
""" Add all the required modules """ """ Add all the required modules """
dff = dff_inv() dff = dff_buf()
dff_height = dff.height dff_height = dff.height
self.ctrl_dff_array = dff_inv_array(rows=self.num_control_signals,columns=1) self.ctrl_dff_array = dff_buf_array(rows=self.num_control_signals,columns=1)
self.add_mod(self.ctrl_dff_array) self.add_mod(self.ctrl_dff_array)
self.nand2 = pnand2(height=dff_height) self.and2 = pand2(size=4,height=dff_height)
self.add_mod(self.nand2) self.add_mod(self.and2)
self.nand3 = pnand3(height=dff_height)
self.add_mod(self.nand3)
# Special gates: inverters for buffering # Special gates: inverters for buffering
# Size the clock for the number of rows (fanout) # Size the clock for the number of rows (fanout)
clock_driver_size = max(1,int(self.num_rows/4)) clock_driver_size = max(1,int(self.num_rows/4))
self.clkbuf = pinvbuf(clock_driver_size,height=dff_height) self.clkbuf = pbuf(size=clock_driver_size, height=dff_height)
self.add_mod(self.clkbuf) self.add_mod(self.clkbuf)
self.buf16 = pbuf(size=16, height=dff_height)
self.add_mod(self.buf16)
self.buf8 = pbuf(size=8, height=dff_height)
self.add_mod(self.buf8)
self.inv = self.inv1 = pinv(size=1, height=dff_height) self.inv = self.inv1 = pinv(size=1, height=dff_height)
self.add_mod(self.inv1) self.add_mod(self.inv1)
self.inv2 = pinv(size=4, height=dff_height)
self.add_mod(self.inv2) self.inv8 = pinv(size=8, height=dff_height)
self.inv8 = pinv(size=16, height=dff_height)
self.add_mod(self.inv8) self.add_mod(self.inv8)
# self.inv2 = pinv(size=4, height=dff_height)
# self.add_mod(self.inv2)
#self.inv16 = pinv(size=16, height=dff_height)
#self.add_mod(self.inv16)
if (self.port_type == "rw") or (self.port_type == "r"): if (self.port_type == "rw") or (self.port_type == "r"):
from importlib import reload from importlib import reload
@ -228,6 +236,7 @@ class control_logic(design.design):
for i in range(total_stages): for i in range(total_stages):
if i%2 == 0: if i%2 == 0:
stage_list.append() stage_list.append()
def setup_signal_busses(self): def setup_signal_busses(self):
""" Setup bus names, determine the size of the busses etc """ """ Setup bus names, determine the size of the busses etc """
@ -244,20 +253,22 @@ class control_logic(design.design):
# list of output control signals (for making a vertical bus) # list of output control signals (for making a vertical bus)
if self.port_type == "rw": if self.port_type == "rw":
self.internal_bus_list = ["clk_buf", "clk_buf_bar", "we", "cs"] self.internal_bus_list = ["gated_clk_bar", "gated_clk_buf", "we", "clk_buf", "we_bar", "cs"]
elif self.port_type == "r":
self.internal_bus_list = ["gated_clk_bar", "gated_clk_buf", "clk_buf", "cs_bar", "cs"]
else: else:
self.internal_bus_list = ["clk_buf", "clk_buf_bar", "cs"] self.internal_bus_list = ["gated_clk_bar", "gated_clk_buf", "clk_buf", "cs"]
# leave space for the bus plus one extra space # leave space for the bus plus one extra space
self.internal_bus_width = (len(self.internal_bus_list)+1)*self.m2_pitch self.internal_bus_width = (len(self.internal_bus_list)+1)*self.m2_pitch
# Outputs to the bank # Outputs to the bank
if self.port_type == "r": if self.port_type == "rw":
self.output_list = ["s_en"] self.output_list = ["s_en", "w_en", "p_en_bar"]
elif self.port_type == "w": elif self.port_type == "r":
self.output_list = ["w_en"] self.output_list = ["s_en", "p_en_bar"]
else: else:
self.output_list = ["s_en", "w_en"] self.output_list = ["w_en"]
self.output_list.append("clk_buf_bar") self.output_list.append("wl_en")
self.output_list.append("clk_buf") self.output_list.append("clk_buf")
self.supply_list = ["vdd", "gnd"] self.supply_list = ["vdd", "gnd"]
@ -274,11 +285,16 @@ class control_logic(design.design):
def create_instances(self): def create_instances(self):
""" Create all the instances """ """ Create all the instances """
self.create_dffs() self.create_dffs()
self.create_clk_row() self.create_clk_buf_row()
self.create_gated_clk_bar_row()
self.create_gated_clk_buf_row()
self.create_wlen_row()
if (self.port_type == "rw") or (self.port_type == "w"): if (self.port_type == "rw") or (self.port_type == "w"):
self.create_we_row() self.create_wen_row()
if (self.port_type == "rw") or (self.port_type == "r"): if self.port_type == "rw":
self.create_rbl_in_row() self.create_rbl_in_row()
if (self.port_type == "rw") or (self.port_type == "r"):
self.create_pen_row()
self.create_sen_row() self.create_sen_row()
self.create_rbl() self.create_rbl()
@ -292,19 +308,33 @@ class control_logic(design.design):
# Add the control flops on the left of the bus # Add the control flops on the left of the bus
self.place_dffs() self.place_dffs()
# All of the control logic is placed to the right of the DFFs and bus
self.control_x_offset = self.ctrl_dff_array.width + self.internal_bus_width
row = 0 row = 0
# Add the logic on the right of the bus # Add the logic on the right of the bus
self.place_clk_row(row=row) # clk is a double-high cell self.place_clk_buf_row(row)
row += 2 row += 1
self.place_gated_clk_bar_row(row)
row += 1
self.place_gated_clk_buf_row(row)
row += 1
self.place_wlen_row(row)
row += 1
if (self.port_type == "rw") or (self.port_type == "w"): if (self.port_type == "rw") or (self.port_type == "w"):
self.place_we_row(row=row) self.place_wen_row(row)
height = self.w_en_inst.uy() height = self.w_en_inst.uy()
control_center_y = self.w_en_inst.uy() control_center_y = self.w_en_inst.uy()
row += 1 row += 1
if (self.port_type == "rw") or (self.port_type == "r"): if self.port_type == "rw":
self.place_rbl_in_row(row=row) self.place_rbl_in_row(row)
self.place_sen_row(row=row+1) row += 1
self.place_rbl(row=row+2) if (self.port_type == "rw") or (self.port_type == "r"):
self.place_pen_row(row)
row += 1
self.place_sen_row(row)
row += 1
self.place_rbl(row)
height = self.rbl_inst.uy() height = self.rbl_inst.uy()
control_center_y = self.rbl_inst.by() control_center_y = self.rbl_inst.by()
@ -314,122 +344,350 @@ class control_logic(design.design):
# Extra pitch on top and right # Extra pitch on top and right
self.height = height + 2*self.m1_pitch self.height = height + 2*self.m1_pitch
# Max of modules or logic rows # Max of modules or logic rows
self.width = max([inst.rx() for inst in self.row_end_inst])
if (self.port_type == "rw") or (self.port_type == "r"): if (self.port_type == "rw") or (self.port_type == "r"):
self.width = max(self.rbl_inst.rx(), max([inst.rx() for inst in self.row_end_inst])) + self.m2_pitch self.width = max(self.rbl_inst.rx() , self.width)
else: self.width += self.m2_pitch
self.width = max([inst.rx() for inst in self.row_end_inst]) + self.m2_pitch
def route_all(self): def route_all(self):
""" Routing between modules """ """ Routing between modules """
self.route_rails() self.route_rails()
self.route_dffs() self.route_dffs()
self.route_wlen()
if (self.port_type == "rw") or (self.port_type == "w"): if (self.port_type == "rw") or (self.port_type == "w"):
self.route_wen() self.route_wen()
if (self.port_type == "rw") or (self.port_type == "r"): if (self.port_type == "rw") or (self.port_type == "r"):
self.route_rbl_in() self.route_rbl_in()
self.route_pen()
self.route_sen() self.route_sen()
self.route_clk() self.route_clk_buf()
self.route_gated_clk_bar()
self.route_gated_clk_buf()
self.route_supply() self.route_supply()
def create_rbl(self): def create_rbl(self):
""" Create the replica bitline """ """ Create the replica bitline """
if self.port_type == "r":
input_name = "gated_clk_bar"
else:
input_name = "rbl_in"
self.rbl_inst=self.add_inst(name="replica_bitline", self.rbl_inst=self.add_inst(name="replica_bitline",
mod=self.replica_bitline) mod=self.replica_bitline)
self.connect_inst(["rbl_in", "pre_s_en", "vdd", "gnd"]) self.connect_inst([input_name, "pre_s_en", "vdd", "gnd"])
def place_rbl(self,row): def place_rbl(self,row):
""" Place the replica bitline """ """ Place the replica bitline """
y_off = row * self.inv1.height + 2*self.m1_pitch y_off = row * self.and2.height + 2*self.m1_pitch
# Add the RBL above the rows # Add the RBL above the rows
# Add to the right of the control rows and routing channel # Add to the right of the control rows and routing channel
self.replica_bitline_offset = vector(0, y_off) offset = vector(0, y_off)
self.rbl_inst.place(self.replica_bitline_offset) self.rbl_inst.place(offset)
def create_clk_row(self): def create_clk_buf_row(self):
""" Create the multistage clock buffer """ """ Create the multistage and gated clock buffer """
self.clkbuf_inst = self.add_inst(name="clkbuf", self.clkbuf_inst = self.add_inst(name="clkbuf",
mod=self.clkbuf) mod=self.clkbuf)
self.connect_inst(["clk","clk_buf_bar","clk_buf","vdd","gnd"]) self.connect_inst(["clk","clk_buf","vdd","gnd"])
def place_clk_row(self,row):
""" Place the multistage clock buffer below the control flops """
x_off = self.ctrl_dff_array.width + self.internal_bus_width
(y_off,mirror)=self.get_offset(row)
clkbuf_offset = vector(x_off,y_off)
self.clkbuf_inst.place(clkbuf_offset)
self.row_end_inst.append(self.clkbuf_inst)
def place_clk_buf_row(self,row):
""" Place the multistage clock buffer below the control flops """
x_off = self.control_x_offset
(y_off,mirror)=self.get_offset(row)
offset = vector(x_off,y_off)
self.clkbuf_inst.place(offset, mirror)
self.row_end_inst.append(self.clkbuf_inst)
def route_clk_buf(self):
clk_pin = self.clkbuf_inst.get_pin("A")
clk_pos = clk_pin.center()
self.add_layout_pin_segment_center(text="clk",
layer="metal2",
start=clk_pos,
end=clk_pos.scale(1,0))
self.add_via_center(layers=("metal1","via1","metal2"),
offset=clk_pos)
clkbuf_map = zip(["Z"], ["clk_buf"])
self.connect_vertical_bus(clkbuf_map, self.clkbuf_inst, self.rail_offsets, ("metal3", "via2", "metal2"))
# The pin is on M1, so we need another via as well
self.add_via_center(layers=("metal1","via1","metal2"),
offset=self.clkbuf_inst.get_pin("Z").center())
self.connect_output(self.clkbuf_inst, "Z", "clk_buf")
def create_gated_clk_bar_row(self):
self.clk_bar_inst = self.add_inst(name="inv_clk_bar",
mod=self.inv)
self.connect_inst(["clk_buf","clk_bar","vdd","gnd"])
self.gated_clk_bar_inst = self.add_inst(name="and2_gated_clk_bar",
mod=self.and2)
self.connect_inst(["cs","clk_bar","gated_clk_bar","vdd","gnd"])
def place_gated_clk_bar_row(self,row):
""" Place the gated clk logic below the control flops """
x_off = self.control_x_offset
(y_off,mirror)=self.get_offset(row)
offset = vector(x_off,y_off)
self.clk_bar_inst.place(offset, mirror)
x_off += self.inv.width
offset = vector(x_off,y_off)
self.gated_clk_bar_inst.place(offset, mirror)
self.row_end_inst.append(self.gated_clk_bar_inst)
def route_gated_clk_bar(self):
clkbuf_map = zip(["A"], ["clk_buf"])
self.connect_vertical_bus(clkbuf_map, self.clk_bar_inst, self.rail_offsets)
out_pos = self.clk_bar_inst.get_pin("Z").center()
in_pos = self.gated_clk_bar_inst.get_pin("B").center()
mid1 = vector(in_pos.x,out_pos.y)
self.add_path("metal1",[out_pos, mid1, in_pos])
# This is the second gate over, so it needs to be on M3
clkbuf_map = zip(["A"], ["cs"])
self.connect_vertical_bus(clkbuf_map, self.gated_clk_bar_inst, self.rail_offsets, ("metal3", "via2", "metal2"))
# The pin is on M1, so we need another via as well
self.add_via_center(layers=("metal1","via1","metal2"),
offset=self.gated_clk_bar_inst.get_pin("A").center())
# This is the second gate over, so it needs to be on M3
clkbuf_map = zip(["Z"], ["gated_clk_bar"])
self.connect_vertical_bus(clkbuf_map, self.gated_clk_bar_inst, self.rail_offsets, ("metal3", "via2", "metal2"))
# The pin is on M1, so we need another via as well
self.add_via_center(layers=("metal1","via1","metal2"),
offset=self.gated_clk_bar_inst.get_pin("Z").center())
def create_gated_clk_buf_row(self):
self.gated_clk_buf_inst = self.add_inst(name="and2_gated_clk_buf",
mod=self.and2)
self.connect_inst(["clk_buf", "cs","gated_clk_buf","vdd","gnd"])
def place_gated_clk_buf_row(self,row):
""" Place the gated clk logic below the control flops """
x_off = self.control_x_offset
(y_off,mirror)=self.get_offset(row)
offset = vector(x_off,y_off)
self.gated_clk_buf_inst.place(offset, mirror)
self.row_end_inst.append(self.gated_clk_buf_inst)
def route_gated_clk_buf(self):
clkbuf_map = zip(["A", "B"], ["clk_buf", "cs"])
self.connect_vertical_bus(clkbuf_map, self.gated_clk_buf_inst, self.rail_offsets)
clkbuf_map = zip(["Z"], ["gated_clk_buf"])
self.connect_vertical_bus(clkbuf_map, self.gated_clk_buf_inst, self.rail_offsets, ("metal3", "via2", "metal2"))
# The pin is on M1, so we need another via as well
self.add_via_center(layers=("metal1","via1","metal2"),
offset=self.gated_clk_buf_inst.get_pin("Z").center())
def create_wlen_row(self):
# input pre_p_en, output: wl_en
self.wl_en_inst=self.add_inst(name="buf_wl_en",
mod=self.buf16)
self.connect_inst(["gated_clk_bar", "wl_en", "vdd", "gnd"])
def place_wlen_row(self, row):
x_off = self.control_x_offset
(y_off,mirror)=self.get_offset(row)
offset = vector(x_off, y_off)
self.wl_en_inst.place(offset, mirror)
self.row_end_inst.append(self.wl_en_inst)
def route_wlen(self):
wlen_map = zip(["A"], ["gated_clk_bar"])
self.connect_vertical_bus(wlen_map, self.wl_en_inst, self.rail_offsets)
self.connect_output(self.wl_en_inst, "Z", "wl_en")
def create_rbl_in_row(self): def create_rbl_in_row(self):
self.rbl_in_bar_inst=self.add_inst(name="nand2_rbl_in_bar",
mod=self.nand2)
self.connect_inst(["clk_buf_bar", "cs", "rbl_in_bar", "vdd", "gnd"])
# input: rbl_in_bar, output: rbl_in
self.rbl_in_inst=self.add_inst(name="inv_rbl_in",
mod=self.inv1)
self.connect_inst(["rbl_in_bar", "rbl_in", "vdd", "gnd"])
# input: gated_clk_bar, we_bar, output: rbl_in
self.rbl_in_inst=self.add_inst(name="and2_rbl_in",
mod=self.and2)
self.connect_inst(["gated_clk_bar", "we_bar", "rbl_in", "vdd", "gnd"])
def place_rbl_in_row(self,row): def place_rbl_in_row(self,row):
x_off = self.ctrl_dff_array.width + self.internal_bus_width x_off = self.control_x_offset
(y_off,mirror)=self.get_offset(row) (y_off,mirror)=self.get_offset(row)
offset = vector(x_off, y_off)
self.rbl_in_inst.place(offset, mirror)
self.rbl_in_bar_offset = vector(x_off, y_off)
self.rbl_in_bar_inst.place(offset=self.rbl_in_bar_offset,
mirror=mirror)
x_off += self.nand2.width
self.rbl_in_offset = vector(x_off, y_off)
self.rbl_in_inst.place(offset=self.rbl_in_offset,
mirror=mirror)
self.row_end_inst.append(self.rbl_in_inst) self.row_end_inst.append(self.rbl_in_inst)
def route_rbl_in(self):
""" Connect the logic for the rbl_in generation """
if self.port_type == "rw":
input_name = "we_bar"
# Connect the NAND gate inputs to the bus
rbl_in_map = zip(["A", "B"], ["gated_clk_bar", "we_bar"])
self.connect_vertical_bus(rbl_in_map, self.rbl_in_inst, self.rail_offsets)
# Connect the output of the precharge enable to the RBL input
if self.port_type == "rw":
out_pos = self.rbl_in_inst.get_pin("Z").center()
else:
out_pos = vector(self.rail_offsets["gated_clk_bar"].x, self.rbl_inst.by()-3*self.m2_pitch)
in_pos = self.rbl_inst.get_pin("en").center()
mid1 = vector(in_pos.x,out_pos.y)
self.add_wire(("metal3","via2","metal2"),[out_pos, mid1, in_pos])
self.add_via_center(layers=("metal1","via1","metal2"),
offset=out_pos,
rotate=90)
self.add_via_center(layers=("metal2","via2","metal3"),
offset=out_pos,
rotate=90)
def create_pen_row(self):
if self.port_type == "rw":
# input: gated_clk_bar, we_bar, output: pre_p_en
self.pre_p_en_inst=self.add_inst(name="and2_pre_p_en",
mod=self.and2)
self.connect_inst(["gated_clk_buf", "we_bar", "pre_p_en", "vdd", "gnd"])
input_name = "pre_p_en"
else:
input_name = "gated_clk_buf"
# input: pre_p_en, output: p_en_bar
self.p_en_bar_inst=self.add_inst(name="inv_p_en_bar",
mod=self.inv8)
self.connect_inst([input_name, "p_en_bar", "vdd", "gnd"])
def place_pen_row(self,row):
x_off = self.control_x_offset
(y_off,mirror)=self.get_offset(row)
if self.port_type == "rw":
offset = vector(x_off, y_off)
self.pre_p_en_inst.place(offset, mirror)
x_off += self.and2.width
offset = vector(x_off,y_off)
self.p_en_bar_inst.place(offset, mirror)
self.row_end_inst.append(self.p_en_bar_inst)
def route_pen(self):
if self.port_type == "rw":
# Connect the NAND gate inputs to the bus
pre_p_en_in_map = zip(["A", "B"], ["gated_clk_buf", "we_bar"])
self.connect_vertical_bus(pre_p_en_in_map, self.pre_p_en_inst, self.rail_offsets)
out_pos = self.pre_p_en_inst.get_pin("Z").center()
in_pos = self.p_en_bar_inst.get_pin("A").lc()
mid1 = vector(out_pos.x,in_pos.y)
self.add_wire(("metal1","via1","metal2"),[out_pos,mid1,in_pos])
else:
in_map = zip(["A"], ["gated_clk_buf"])
self.connect_vertical_bus(in_map, self.p_en_bar_inst, self.rail_offsets)
self.connect_output(self.p_en_bar_inst, "Z", "p_en_bar")
def create_sen_row(self): def create_sen_row(self):
""" Create the sense enable buffer. """ """ Create the sense enable buffer. """
# input: pre_s_en, output: pre_s_en_bar # BUFFER FOR S_EN
self.pre_s_en_bar_inst=self.add_inst(name="inv_pre_s_en_bar", # input: pre_s_en, output: s_en
mod=self.inv2) self.s_en_inst=self.add_inst(name="buf_s_en",
self.connect_inst(["pre_s_en", "pre_s_en_bar", "vdd", "gnd"]) mod=self.buf8)
self.connect_inst(["pre_s_en", "s_en", "vdd", "gnd"])
# BUFFER INVERTERS FOR S_EN
# input: input: pre_s_en_bar, output: s_en
self.s_en_inst=self.add_inst(name="inv_s_en",
mod=self.inv8)
self.connect_inst(["pre_s_en_bar", "s_en", "vdd", "gnd"])
def place_sen_row(self,row): def place_sen_row(self,row):
""" """
The sense enable buffer gets placed to the far right of the The sense enable buffer gets placed to the far right of the
row. row.
""" """
x_off = self.ctrl_dff_array.width + self.internal_bus_width x_off = self.control_x_offset
(y_off,mirror)=self.get_offset(row) (y_off,mirror)=self.get_offset(row)
self.pre_s_en_bar_offset = vector(x_off, y_off) offset = vector(x_off, y_off)
self.pre_s_en_bar_inst.place(offset=self.pre_s_en_bar_offset, self.s_en_inst.place(offset, mirror)
mirror=mirror)
x_off += self.inv2.width
self.s_en_offset = vector(x_off, y_off)
self.s_en_inst.place(offset=self.s_en_offset,
mirror=mirror)
self.row_end_inst.append(self.s_en_inst) self.row_end_inst.append(self.s_en_inst)
def route_sen(self):
out_pos = self.rbl_inst.get_pin("out").bc()
in_pos = self.s_en_inst.get_pin("A").lc()
mid1 = vector(out_pos.x,in_pos.y)
self.add_wire(("metal1","via1","metal2"),[out_pos, mid1,in_pos])
self.connect_output(self.s_en_inst, "Z", "s_en")
def create_wen_row(self):
# input: we (or cs) output: w_en
if self.port_type == "rw":
input_name = "we"
else:
# No we for write-only reports, so use cs
input_name = "cs"
# BUFFER FOR W_EN
self.w_en_inst = self.add_inst(name="buf_w_en_buf",
mod=self.buf8)
self.connect_inst([input_name, "w_en", "vdd", "gnd"])
def place_wen_row(self,row):
x_off = self.ctrl_dff_inst.width + self.internal_bus_width
(y_off,mirror)=self.get_offset(row)
offset = vector(x_off, y_off)
self.w_en_inst.place(offset, mirror)
self.row_end_inst.append(self.w_en_inst)
def route_wen(self):
if self.port_type == "rw":
input_name = "we"
else:
# No we for write-only reports, so use cs
input_name = "cs"
wen_map = zip(["A"], [input_name])
self.connect_vertical_bus(wen_map, self.w_en_inst, self.rail_offsets)
self.connect_output(self.w_en_inst, "Z", "w_en")
def create_dffs(self):
self.ctrl_dff_inst=self.add_inst(name="ctrl_dffs",
mod=self.ctrl_dff_array)
self.connect_inst(self.input_list + self.dff_output_list + ["clk_buf"] + self.supply_list)
def place_dffs(self):
self.ctrl_dff_inst.place(vector(0,0))
def route_dffs(self): def route_dffs(self):
""" Route the input inverters """ if self.port_type == "rw":
dff_out_map = zip(["dout_bar_0", "dout_bar_1", "dout_1"], ["cs", "we", "we_bar"])
if self.port_type == "r": elif self.port_type == "r":
control_inputs = ["cs"] dff_out_map = zip(["dout_bar_0", "dout_0"], ["cs", "cs_bar"])
else: else:
control_inputs = ["cs", "we"] dff_out_map = zip(["dout_bar_0"], ["cs"])
dff_out_map = zip(["dout_bar_{}".format(i) for i in range(2*self.num_control_signals - 1)], control_inputs) self.connect_vertical_bus(dff_out_map, self.ctrl_dff_inst, self.rail_offsets, ("metal3", "via2", "metal2"))
self.connect_vertical_bus(dff_out_map, self.ctrl_dff_inst, self.rail_offsets)
# Connect the clock rail to the other clock rail # Connect the clock rail to the other clock rail
in_pos = self.ctrl_dff_inst.get_pin("clk").uc() in_pos = self.ctrl_dff_inst.get_pin("clk").uc()
@ -444,207 +702,18 @@ class control_logic(design.design):
if (self.port_type == "rw"): if (self.port_type == "rw"):
self.copy_layout_pin(self.ctrl_dff_inst, "din_1", "web") self.copy_layout_pin(self.ctrl_dff_inst, "din_1", "web")
def create_dffs(self):
""" Add the three input DFFs (with inverters) """
self.ctrl_dff_inst=self.add_inst(name="ctrl_dffs",
mod=self.ctrl_dff_array)
self.connect_inst(self.input_list + self.dff_output_list + ["clk_buf"] + self.supply_list)
def place_dffs(self):
""" Place the input DFFs (with inverters) """
self.ctrl_dff_inst.place(vector(0,0))
def get_offset(self,row): def get_offset(self,row):
""" Compute the y-offset and mirroring """ """ Compute the y-offset and mirroring """
y_off = row*self.inv1.height y_off = row*self.and2.height
if row % 2: if row % 2:
y_off += self.inv1.height y_off += self.and2.height
mirror="MX" mirror="MX"
else: else:
mirror="R0" mirror="R0"
return (y_off,mirror) return (y_off,mirror)
def create_we_row(self):
# input: WE, CS output: w_en_bar
if self.port_type == "rw":
nand_mod = self.nand3
temp = ["clk_buf_bar", "cs", "we", "w_en_bar", "vdd", "gnd"]
else:
nand_mod = self.nand2
temp = ["clk_buf_bar", "cs", "w_en_bar", "vdd", "gnd"]
self.w_en_bar_inst = self.add_inst(name="nand3_w_en_bar",
mod=nand_mod)
self.connect_inst(temp)
# input: w_en_bar, output: pre_w_en
self.pre_w_en_inst = self.add_inst(name="inv_pre_w_en",
mod=self.inv1)
self.connect_inst(["w_en_bar", "pre_w_en", "vdd", "gnd"])
# BUFFER INVERTERS FOR W_EN
self.pre_w_en_bar_inst = self.add_inst(name="inv_pre_w_en_bar",
mod=self.inv2)
self.connect_inst(["pre_w_en", "pre_w_en_bar", "vdd", "gnd"])
self.w_en_inst = self.add_inst(name="inv_w_en2",
mod=self.inv8)
self.connect_inst(["pre_w_en_bar", "w_en", "vdd", "gnd"])
def place_we_row(self,row):
x_off = self.ctrl_dff_inst.width + self.internal_bus_width
(y_off,mirror)=self.get_offset(row)
w_en_bar_offset = vector(x_off, y_off)
self.w_en_bar_inst.place(offset=w_en_bar_offset,
mirror=mirror)
if self.port_type == "rw":
x_off += self.nand3.width
else:
x_off += self.nand2.width
pre_w_en_offset = vector(x_off, y_off)
self.pre_w_en_inst.place(offset=pre_w_en_offset,
mirror=mirror)
x_off += self.inv1.width
pre_w_en_bar_offset = vector(x_off, y_off)
self.pre_w_en_bar_inst.place(offset=pre_w_en_bar_offset,
mirror=mirror)
x_off += self.inv2.width
w_en_offset = vector(x_off, y_off)
self.w_en_inst.place(offset=w_en_offset,
mirror=mirror)
x_off += self.inv8.width
self.row_end_inst.append(self.w_en_inst)
def route_rbl_in(self):
""" Connect the logic for the rbl_in generation """
rbl_in_map = zip(["A", "B"], ["clk_buf_bar", "cs"])
self.connect_vertical_bus(rbl_in_map, self.rbl_in_bar_inst, self.rail_offsets)
# Connect the NAND3 output to the inverter
# The pins are assumed to extend all the way to the cell edge
rbl_in_bar_pos = self.rbl_in_bar_inst.get_pin("Z").center()
inv_in_pos = self.rbl_in_inst.get_pin("A").center()
mid1 = vector(inv_in_pos.x,rbl_in_bar_pos.y)
self.add_path("metal1",[rbl_in_bar_pos,mid1,inv_in_pos])
# Connect the output to the RBL
rbl_out_pos = self.rbl_in_inst.get_pin("Z").center()
rbl_in_pos = self.rbl_inst.get_pin("en").center()
mid1 = vector(rbl_in_pos.x,rbl_out_pos.y)
self.add_wire(("metal3","via2","metal2"),[rbl_out_pos,mid1,rbl_in_pos])
self.add_via_center(layers=("metal1","via1","metal2"),
offset=rbl_out_pos,
rotate=90)
self.add_via_center(layers=("metal2","via2","metal3"),
offset=rbl_out_pos,
rotate=90)
def connect_rail_from_right(self,inst, pin, rail):
""" Helper routine to connect an unrotated/mirrored oriented instance to the rails """
in_pos = inst.get_pin(pin).center()
rail_pos = vector(self.rail_offsets[rail].x, in_pos.y)
self.add_wire(("metal1","via1","metal2"),[in_pos, rail_pos])
self.add_via_center(layers=("metal1","via1","metal2"),
offset=rail_pos,
rotate=90)
def connect_rail_from_right_m2m3(self,inst, pin, rail):
""" Helper routine to connect an unrotated/mirrored oriented instance to the rails """
in_pos = inst.get_pin(pin).center()
rail_pos = vector(self.rail_offsets[rail].x, in_pos.y)
self.add_wire(("metal3","via2","metal2"),[in_pos, rail_pos])
# Bring it up to M2 for M2/M3 routing
self.add_via_center(layers=("metal1","via1","metal2"),
offset=in_pos,
rotate=90)
self.add_via_center(layers=("metal2","via2","metal3"),
offset=in_pos,
rotate=90)
self.add_via_center(layers=("metal2","via2","metal3"),
offset=rail_pos,
rotate=90)
def connect_rail_from_left(self,inst, pin, rail):
""" Helper routine to connect an unrotated/mirrored oriented instance to the rails """
in_pos = inst.get_pin(pin).lc()
rail_pos = vector(self.rail_offsets[rail].x, in_pos.y)
self.add_wire(("metal1","via1","metal2"),[in_pos, rail_pos])
self.add_via_center(layers=("metal1","via1","metal2"),
offset=rail_pos,
rotate=90)
def connect_rail_from_left_m2m3(self,inst, pin, rail):
""" Helper routine to connect an unrotated/mirrored oriented instance to the rails """
in_pos = inst.get_pin(pin).lc()
rail_pos = vector(self.rail_offsets[rail].x, in_pos.y)
self.add_wire(("metal3","via2","metal2"),[in_pos, rail_pos])
self.add_via_center(layers=("metal2","via2","metal3"),
offset=in_pos,
rotate=90)
self.add_via_center(layers=("metal2","via2","metal3"),
offset=rail_pos,
rotate=90)
def route_wen(self):
if self.port_type == "rw":
wen_map = zip(["A", "B", "C"], ["clk_buf_bar", "cs", "we"])
else:
wen_map = zip(["A", "B"], ["clk_buf_bar", "cs"])
self.connect_vertical_bus(wen_map, self.w_en_bar_inst, self.rail_offsets)
# Connect the NAND3 output to the inverter
# The pins are assumed to extend all the way to the cell edge
w_en_bar_pos = self.w_en_bar_inst.get_pin("Z").center()
inv_in_pos = self.pre_w_en_inst.get_pin("A").center()
mid1 = vector(inv_in_pos.x,w_en_bar_pos.y)
self.add_path("metal1",[w_en_bar_pos,mid1,inv_in_pos])
self.add_path("metal1",[self.pre_w_en_inst.get_pin("Z").center(), self.pre_w_en_bar_inst.get_pin("A").center()])
self.add_path("metal1",[self.pre_w_en_bar_inst.get_pin("Z").center(), self.w_en_inst.get_pin("A").center()])
self.connect_output(self.w_en_inst, "Z", "w_en")
def route_sen(self):
rbl_out_pos = self.rbl_inst.get_pin("out").bc()
in_pos = self.pre_s_en_bar_inst.get_pin("A").lc()
mid1 = vector(rbl_out_pos.x,in_pos.y)
self.add_wire(("metal1","via1","metal2"),[rbl_out_pos,mid1,in_pos])
#s_en_pos = self.s_en.get_pin("Z").lc()
self.add_path("metal1",[self.pre_s_en_bar_inst.get_pin("Z").center(), self.s_en_inst.get_pin("A").center()])
self.connect_output(self.s_en_inst, "Z", "s_en")
def route_clk(self):
""" Route the clk and clk_buf_bar signal internally """
clk_pin = self.clkbuf_inst.get_pin("A")
self.add_layout_pin_segment_center(text="clk",
layer="metal2",
start=clk_pin.bc(),
end=clk_pin.bc().scale(1,0))
clkbuf_map = zip(["Z", "Zb"], ["clk_buf", "clk_buf_bar"])
self.connect_vertical_bus(clkbuf_map, self.clkbuf_inst, self.rail_offsets, ("metal3", "via2", "metal2"))
# self.connect_rail_from_right_m2m3(self.clkbuf_inst, "Z", "clk_buf")
# self.connect_rail_from_right_m2m3(self.clkbuf_inst, "Zb", "clk_buf_bar")
self.connect_output(self.clkbuf_inst, "Z", "clk_buf")
self.connect_output(self.clkbuf_inst, "Zb", "clk_buf_bar")
def connect_output(self, inst, pin_name, out_name): def connect_output(self, inst, pin_name, out_name):
""" Create an output pin on the right side from the pin of a given instance. """ """ Create an output pin on the right side from the pin of a given instance. """
@ -735,7 +804,7 @@ class control_logic(design.design):
internal_cout = self.ctrl_dff_array.get_clk_cin() internal_cout = self.ctrl_dff_array.get_clk_cin()
external_cout = self.sram.get_clk_cin() external_cout = self.sram.get_clk_cin()
#First stage is the clock buffer #First stage is the clock buffer
stage_effort_list += self.clkbuf.determine_clk_buf_stage_efforts(internal_cout+external_cout, is_clk_rise) stage_effort_list += self.clkbuf.determine_z_stage_efforts(internal_cout+external_cout, is_clk_rise)
last_stage_is_rise = stage_effort_list[-1].is_rise last_stage_is_rise = stage_effort_list[-1].is_rise
#Then ask the sram for the other path delays (from the bank) #Then ask the sram for the other path delays (from the bank)

View File

@ -59,7 +59,7 @@ class dff_array(design.design):
self.dff_insts={} self.dff_insts={}
for row in range(self.rows): for row in range(self.rows):
for col in range(self.columns): for col in range(self.columns):
name = "Xdff_r{0}_c{1}".format(row,col) name = "dff_r{0}_c{1}".format(row,col)
self.dff_insts[row,col]=self.add_inst(name=name, self.dff_insts[row,col]=self.add_inst(name=name,
mod=self.dff) mod=self.dff)
self.connect_inst([self.get_din_name(row,col), self.connect_inst([self.get_din_name(row,col),
@ -71,7 +71,7 @@ class dff_array(design.design):
def place_dff_array(self): def place_dff_array(self):
for row in range(self.rows): for row in range(self.rows):
for col in range(self.columns): for col in range(self.columns):
name = "Xdff_r{0}_c{1}".format(row,col) name = "dff_r{0}_c{1}".format(row,col)
if (row % 2 == 0): if (row % 2 == 0):
base = vector(col*self.dff.width,row*self.dff.height) base = vector(col*self.dff.width,row*self.dff.height)
mirror = "R0" mirror = "R0"

View File

@ -1,6 +1,6 @@
import debug import debug
import design import design
from tech import drc from tech import drc,parameter
from math import log from math import log
from vector import vector from vector import vector
from globals import OPTS from globals import OPTS
@ -102,8 +102,7 @@ class dff_buf(design.design):
mid_x_offset = 0.5*(a1_pin.cx() + q_pin.cx()) mid_x_offset = 0.5*(a1_pin.cx() + q_pin.cx())
mid1 = vector(mid_x_offset, q_pin.cy()) mid1 = vector(mid_x_offset, q_pin.cy())
mid2 = vector(mid_x_offset, a1_pin.cy()) mid2 = vector(mid_x_offset, a1_pin.cy())
self.add_path("metal3", self.add_path("metal3", [q_pin.center(), mid1, mid2, a1_pin.center()])
[q_pin.center(), mid1, mid2, a1_pin.center()])
self.add_via_center(layers=("metal2","via2","metal3"), self.add_via_center(layers=("metal2","via2","metal3"),
offset=q_pin.center()) offset=q_pin.center())
self.add_via_center(layers=("metal2","via2","metal3"), self.add_via_center(layers=("metal2","via2","metal3"),
@ -114,8 +113,10 @@ class dff_buf(design.design):
# Route inv1 z to inv2 a # Route inv1 z to inv2 a
z1_pin = self.inv1_inst.get_pin("Z") z1_pin = self.inv1_inst.get_pin("Z")
a2_pin = self.inv2_inst.get_pin("A") a2_pin = self.inv2_inst.get_pin("A")
mid_point = vector(z1_pin.cx(), a2_pin.cy()) mid_x_offset = 0.5*(z1_pin.cx() + a2_pin.cx())
self.add_path("metal1", [z1_pin.center(), mid_point, a2_pin.center()]) self.mid_qb_pos = vector(mid_x_offset, z1_pin.cy())
mid2 = vector(mid_x_offset, a2_pin.cy())
self.add_path("metal1", [z1_pin.center(), self.mid_qb_pos, mid2, a2_pin.center()])
def add_layout_pins(self): def add_layout_pins(self):
@ -150,18 +151,22 @@ class dff_buf(design.design):
height=din_pin.height()) height=din_pin.height())
dout_pin = self.inv2_inst.get_pin("Z") dout_pin = self.inv2_inst.get_pin("Z")
mid_pos = dout_pin.center() + vector(self.m1_pitch,0)
q_pos = mid_pos - vector(0,self.m2_pitch)
self.add_layout_pin_rect_center(text="Q", self.add_layout_pin_rect_center(text="Q",
layer="metal2", layer="metal2",
offset=dout_pin.center()) offset=q_pos)
self.add_path("metal1", [dout_pin.center(), mid_pos, q_pos])
self.add_via_center(layers=("metal1","via1","metal2"), self.add_via_center(layers=("metal1","via1","metal2"),
offset=dout_pin.center()) offset=q_pos)
dout_pin = self.inv2_inst.get_pin("A") qb_pos = self.mid_qb_pos + vector(0,self.m2_pitch)
self.add_layout_pin_rect_center(text="Qb", self.add_layout_pin_rect_center(text="Qb",
layer="metal2", layer="metal2",
offset=dout_pin.center()) offset=qb_pos)
self.add_path("metal1", [self.mid_qb_pos, qb_pos])
self.add_via_center(layers=("metal1","via1","metal2"), self.add_via_center(layers=("metal1","via1","metal2"),
offset=dout_pin.center()) offset=qb_pos)
@ -172,3 +177,9 @@ class dff_buf(design.design):
inv2_delay = self.inv2.analytical_delay(slew=inv1_delay.slew, load=load) inv2_delay = self.inv2.analytical_delay(slew=inv1_delay.slew, load=load)
return dff_delay + inv1_delay + inv2_delay return dff_delay + inv1_delay + inv2_delay
def get_clk_cin(self):
"""Return the total capacitance (in relative units) that the clock is loaded by in the dff"""
#This is a handmade cell so the value must be entered in the tech.py file or estimated.
#Calculated in the tech file by summing the widths of all the gates and dividing by the minimum width.
#FIXME: Dff changed in a past commit. The parameter need to be updated.
return parameter["dff_clk_cin"]

View File

@ -61,7 +61,7 @@ class dff_buf_array(design.design):
self.dff_insts={} self.dff_insts={}
for row in range(self.rows): for row in range(self.rows):
for col in range(self.columns): for col in range(self.columns):
name = "Xdff_r{0}_c{1}".format(row,col) name = "dff_r{0}_c{1}".format(row,col)
self.dff_insts[row,col]=self.add_inst(name=name, self.dff_insts[row,col]=self.add_inst(name=name,
mod=self.dff) mod=self.dff)
self.connect_inst([self.get_din_name(row,col), self.connect_inst([self.get_din_name(row,col),
@ -184,3 +184,9 @@ class dff_buf_array(design.design):
def analytical_delay(self, slew, load=0.0): def analytical_delay(self, slew, load=0.0):
return self.dff.analytical_delay(slew=slew, load=load) return self.dff.analytical_delay(slew=slew, load=load)
def get_clk_cin(self):
"""Return the total capacitance (in relative units) that the clock is loaded by in the dff array"""
dff_clk_cin = self.dff.get_clk_cin()
total_cin = dff_clk_cin * self.rows * self.columns
return total_cin

View File

@ -17,15 +17,11 @@ class hierarchical_decoder(design.design):
""" """
Dynamically generated hierarchical decoder. Dynamically generated hierarchical decoder.
""" """
unique_id = 1
def __init__(self, rows):
design.design.__init__(self, "hierarchical_decoder_{0}rows".format(rows)) def __init__(self, rows, height=None):
design.design.__init__(self, "hierarchical_decoder_{0}rows_{1}".format(rows,hierarchical_decoder.unique_id))
from importlib import reload hierarchical_decoder.unique_id += 1
c = reload(__import__(OPTS.bitcell))
self.mod_bitcell = getattr(c, OPTS.bitcell)
b = self.mod_bitcell()
self.bitcell_height = b.height
self.NAND_FORMAT = "DEC_NAND_{0}" self.NAND_FORMAT = "DEC_NAND_{0}"
self.INV_FORMAT = "DEC_INV_{0}" self.INV_FORMAT = "DEC_INV_{0}"
@ -33,6 +29,7 @@ class hierarchical_decoder(design.design):
self.pre2x4_inst = [] self.pre2x4_inst = []
self.pre3x8_inst = [] self.pre3x8_inst = []
self.cell_height = height
self.rows = rows self.rows = rows
self.num_inputs = int(math.log(self.rows, 2)) self.num_inputs = int(math.log(self.rows, 2))
(self.no_of_pre2x4,self.no_of_pre3x8)=self.determine_predecodes(self.num_inputs) (self.no_of_pre2x4,self.no_of_pre3x8)=self.determine_predecodes(self.num_inputs)
@ -60,21 +57,21 @@ class hierarchical_decoder(design.design):
self.DRC_LVS() self.DRC_LVS()
def add_modules(self): def add_modules(self):
self.inv = pinv() self.inv = pinv(height=self.cell_height)
self.add_mod(self.inv) self.add_mod(self.inv)
self.nand2 = pnand2() self.nand2 = pnand2(height=self.cell_height)
self.add_mod(self.nand2) self.add_mod(self.nand2)
self.nand3 = pnand3() self.nand3 = pnand3(height=self.cell_height)
self.add_mod(self.nand3) self.add_mod(self.nand3)
self.add_decoders() self.add_decoders()
def add_decoders(self): def add_decoders(self):
""" Create the decoders based on the number of pre-decodes """ """ Create the decoders based on the number of pre-decodes """
self.pre2_4 = pre2x4() self.pre2_4 = pre2x4(height=self.cell_height)
self.add_mod(self.pre2_4) self.add_mod(self.pre2_4)
self.pre3_8 = pre3x8() self.pre3_8 = pre3x8(height=self.cell_height)
self.add_mod(self.pre3_8) self.add_mod(self.pre3_8)
def determine_predecodes(self,num_inputs): def determine_predecodes(self,num_inputs):
@ -336,7 +333,7 @@ class hierarchical_decoder(design.design):
if (self.num_inputs == 4 or self.num_inputs == 5): if (self.num_inputs == 4 or self.num_inputs == 5):
for i in range(len(self.predec_groups[0])): for i in range(len(self.predec_groups[0])):
for j in range(len(self.predec_groups[1])): for j in range(len(self.predec_groups[1])):
row = len(self.predec_groups[1])*i + j row = len(self.predec_groups[0])*j + i
name = self.NAND_FORMAT.format(row) name = self.NAND_FORMAT.format(row)
self.nand_inst.append(self.add_inst(name=name, self.nand_inst.append(self.add_inst(name=name,
mod=self.nand2)) mod=self.nand2))
@ -352,8 +349,8 @@ class hierarchical_decoder(design.design):
for i in range(len(self.predec_groups[0])): for i in range(len(self.predec_groups[0])):
for j in range(len(self.predec_groups[1])): for j in range(len(self.predec_groups[1])):
for k in range(len(self.predec_groups[2])): for k in range(len(self.predec_groups[2])):
row = len(self.predec_groups[1])*len(self.predec_groups[2]) * i \ row = (len(self.predec_groups[0])*len(self.predec_groups[1])) * k \
+ len(self.predec_groups[2])*j + k + len(self.predec_groups[0])*j + i
name = self.NAND_FORMAT.format(row) name = self.NAND_FORMAT.format(row)
self.nand_inst.append(self.add_inst(name=name, self.nand_inst.append(self.add_inst(name=name,
@ -523,8 +520,8 @@ class hierarchical_decoder(design.design):
""" """
row_index = 0 row_index = 0
if (self.num_inputs == 4 or self.num_inputs == 5): if (self.num_inputs == 4 or self.num_inputs == 5):
for index_A in self.predec_groups[0]: for index_B in self.predec_groups[1]:
for index_B in self.predec_groups[1]: for index_A in self.predec_groups[0]:
# FIXME: convert to connect_bus? # FIXME: convert to connect_bus?
predecode_name = "predecode_{}".format(index_A) predecode_name = "predecode_{}".format(index_A)
self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("A")) self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("A"))
@ -533,9 +530,9 @@ class hierarchical_decoder(design.design):
row_index = row_index + 1 row_index = row_index + 1
elif (self.num_inputs > 5): elif (self.num_inputs > 5):
for index_A in self.predec_groups[0]: for index_C in self.predec_groups[2]:
for index_B in self.predec_groups[1]: for index_B in self.predec_groups[1]:
for index_C in self.predec_groups[2]: for index_A in self.predec_groups[0]:
# FIXME: convert to connect_bus? # FIXME: convert to connect_bus?
predecode_name = "predecode_{}".format(index_A) predecode_name = "predecode_{}".format(index_A)
self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("A")) self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("A"))

View File

@ -9,19 +9,18 @@ from globals import OPTS
from pnand2 import pnand2 from pnand2 import pnand2
from pnand3 import pnand3 from pnand3 import pnand3
class hierarchical_predecode(design.design): class hierarchical_predecode(design.design):
""" """
Pre 2x4 and 3x8 decoder shared code. Pre 2x4 and 3x8 decoder shared code.
""" """
def __init__(self, input_number): unique_id = 1
def __init__(self, input_number, height=None):
self.number_of_inputs = input_number self.number_of_inputs = input_number
self.cell_height = height
self.number_of_outputs = int(math.pow(2, self.number_of_inputs)) self.number_of_outputs = int(math.pow(2, self.number_of_inputs))
design.design.__init__(self, name="pre{0}x{1}".format(self.number_of_inputs,self.number_of_outputs)) design.design.__init__(self, name="pre{0}x{1}_{2}".format(self.number_of_inputs,self.number_of_outputs,hierarchical_predecode.unique_id))
hierarchical_predecode.unique_id += 1
from importlib import reload
c = reload(__import__(OPTS.bitcell))
self.mod_bitcell = getattr(c, OPTS.bitcell)
def add_pins(self): def add_pins(self):
for k in range(self.number_of_inputs): for k in range(self.number_of_inputs):
@ -34,7 +33,7 @@ class hierarchical_predecode(design.design):
def add_modules(self): def add_modules(self):
""" Add the INV and NAND gate modules """ """ Add the INV and NAND gate modules """
self.inv = pinv() self.inv = pinv(height=self.cell_height)
self.add_mod(self.inv) self.add_mod(self.inv)
self.add_nand(self.number_of_inputs) self.add_nand(self.number_of_inputs)
@ -43,9 +42,9 @@ class hierarchical_predecode(design.design):
def add_nand(self,inputs): def add_nand(self,inputs):
""" Create the NAND for the predecode input stage """ """ Create the NAND for the predecode input stage """
if inputs==2: if inputs==2:
self.nand = pnand2() self.nand = pnand2(height=self.cell_height)
elif inputs==3: elif inputs==3:
self.nand = pnand3() self.nand = pnand3(height=self.cell_height)
else: else:
debug.error("Invalid number of predecode inputs: {}".format(inputs),-1) debug.error("Invalid number of predecode inputs: {}".format(inputs),-1)
@ -90,7 +89,7 @@ class hierarchical_predecode(design.design):
""" Create the input inverters to invert input signals for the decode stage. """ """ Create the input inverters to invert input signals for the decode stage. """
self.in_inst = [] self.in_inst = []
for inv_num in range(self.number_of_inputs): for inv_num in range(self.number_of_inputs):
name = "Xpre_inv_{0}".format(inv_num) name = "pre_inv_{0}".format(inv_num)
self.in_inst.append(self.add_inst(name=name, self.in_inst.append(self.add_inst(name=name,
mod=self.inv)) mod=self.inv))
self.connect_inst(["in_{0}".format(inv_num), self.connect_inst(["in_{0}".format(inv_num),
@ -114,7 +113,7 @@ class hierarchical_predecode(design.design):
""" Create inverters for the inverted output decode signals. """ """ Create inverters for the inverted output decode signals. """
self.inv_inst = [] self.inv_inst = []
for inv_num in range(self.number_of_outputs): for inv_num in range(self.number_of_outputs):
name = "Xpre_nand_inv_{}".format(inv_num) name = "pre_nand_inv_{}".format(inv_num)
self.inv_inst.append(self.add_inst(name=name, self.inv_inst.append(self.add_inst(name=name,
mod=self.inv)) mod=self.inv))
self.connect_inst(["Z_{}".format(inv_num), self.connect_inst(["Z_{}".format(inv_num),

View File

@ -9,8 +9,8 @@ class hierarchical_predecode2x4(hierarchical_predecode):
""" """
Pre 2x4 decoder used in hierarchical_decoder. Pre 2x4 decoder used in hierarchical_decoder.
""" """
def __init__(self): def __init__(self, height=None):
hierarchical_predecode.__init__(self, 2) hierarchical_predecode.__init__(self, 2, height)
self.create_netlist() self.create_netlist()
if not OPTS.netlist_only: if not OPTS.netlist_only:

View File

@ -9,8 +9,8 @@ class hierarchical_predecode3x8(hierarchical_predecode):
""" """
Pre 3x8 decoder used in hierarchical_decoder. Pre 3x8 decoder used in hierarchical_decoder.
""" """
def __init__(self): def __init__(self, height=None):
hierarchical_predecode.__init__(self, 3) hierarchical_predecode.__init__(self, 3, height)
self.create_netlist() self.create_netlist()
if not OPTS.netlist_only: if not OPTS.netlist_only:

View File

@ -33,7 +33,7 @@ class precharge_array(design.design):
for i in range(self.columns): for i in range(self.columns):
self.add_pin("bl_{0}".format(i)) self.add_pin("bl_{0}".format(i))
self.add_pin("br_{0}".format(i)) self.add_pin("br_{0}".format(i))
self.add_pin("en") self.add_pin("en_bar")
self.add_pin("vdd") self.add_pin("vdd")
def create_netlist(self): def create_netlist(self):
@ -59,9 +59,9 @@ class precharge_array(design.design):
def add_layout_pins(self): def add_layout_pins(self):
self.add_layout_pin(text="en", self.add_layout_pin(text="en_bar",
layer="metal1", layer="metal1",
offset=self.pc_cell.get_pin("en").ll(), offset=self.pc_cell.get_pin("en_bar").ll(),
width=self.width, width=self.width,
height=drc("minwidth_metal1")) height=drc("minwidth_metal1"))
@ -94,7 +94,7 @@ class precharge_array(design.design):
mod=self.pc_cell, mod=self.pc_cell,
offset=offset) offset=offset)
self.local_insts.append(inst) self.local_insts.append(inst)
self.connect_inst(["bl_{0}".format(i), "br_{0}".format(i), "en", "vdd"]) self.connect_inst(["bl_{0}".format(i), "br_{0}".format(i), "en_bar", "vdd"])
def place_insts(self): def place_insts(self):

View File

@ -264,15 +264,8 @@ class replica_bitline(design.design):
pin = self.rbl_inv_inst.get_pin("vdd") pin = self.rbl_inv_inst.get_pin("vdd")
self.add_power_pin("vdd", pin.lc()) self.add_power_pin("vdd", pin.lc())
# Replica bitcell needs to be routed up to M3
pin=self.rbc_inst.get_pin("vdd") pin=self.rbc_inst.get_pin("vdd")
# Don't rotate this via to vit in FreePDK45. In the custom cell, the pin cannot be placed self.add_power_pin("vdd", pin.center(), 0, pin.layer)
# directly on vdd or there will be a drc error with a wordline. Place the pin slightly farther
# away then route to it. A better solution would be to rotate the m1 in the via or move the pin
# a m1_pitch below the entire cell.
pin_extension = pin.center() - vector(0,self.m1_pitch)
self.add_power_pin("vdd", pin_extension, rotate=0)
self.add_path("metal1", [pin.center(), pin_extension])
for pin in self.rbc_inst.get_pins("gnd"): for pin in self.rbc_inst.get_pins("gnd"):
self.add_power_pin("gnd", pin.center()) self.add_power_pin("gnd", pin.center())

View File

@ -44,7 +44,7 @@ class wordline_driver(design.design):
# Outputs from wordline_driver. # Outputs from wordline_driver.
for i in range(self.rows): for i in range(self.rows):
self.add_pin("wl_{0}".format(i)) self.add_pin("wl_{0}".format(i))
self.add_pin("en") self.add_pin("en_bar")
self.add_pin("vdd") self.add_pin("vdd")
self.add_pin("gnd") self.add_pin("gnd")
@ -67,7 +67,7 @@ class wordline_driver(design.design):
""" Add a pin for each row of vdd/gnd which are must-connects next level up. """ """ Add a pin for each row of vdd/gnd which are must-connects next level up. """
# Find the x offsets for where the vias/pins should be placed # Find the x offsets for where the vias/pins should be placed
a_xoffset = self.inv1_inst[0].rx() a_xoffset = self.nand_inst[0].rx()
b_xoffset = self.inv2_inst[0].lx() b_xoffset = self.inv2_inst[0].lx()
for num in range(self.rows): for num in range(self.rows):
# this will result in duplicate polygons for rails, but who cares # this will result in duplicate polygons for rails, but who cares
@ -95,24 +95,16 @@ class wordline_driver(design.design):
def create_drivers(self): def create_drivers(self):
self.inv1_inst = []
self.nand_inst = [] self.nand_inst = []
self.inv2_inst = [] self.inv2_inst = []
for row in range(self.rows): for row in range(self.rows):
name_inv1 = "wl_driver_inv_en{}".format(row)
name_nand = "wl_driver_nand{}".format(row) name_nand = "wl_driver_nand{}".format(row)
name_inv2 = "wl_driver_inv{}".format(row) name_inv2 = "wl_driver_inv{}".format(row)
# add inv1 based on the info above
self.inv1_inst.append(self.add_inst(name=name_inv1,
mod=self.inv_no_output))
self.connect_inst(["en",
"en_bar_{0}".format(row),
"vdd", "gnd"])
# add nand 2 # add nand 2
self.nand_inst.append(self.add_inst(name=name_nand, self.nand_inst.append(self.add_inst(name=name_nand,
mod=self.nand2)) mod=self.nand2))
self.connect_inst(["en_bar_{0}".format(row), self.connect_inst(["en_bar",
"in_{0}".format(row), "in_{0}".format(row),
"wl_bar_{0}".format(row), "wl_bar_{0}".format(row),
"vdd", "gnd"]) "vdd", "gnd"])
@ -125,8 +117,7 @@ class wordline_driver(design.design):
def place_drivers(self): def place_drivers(self):
inv1_xoffset = 2*self.m1_width + 5*self.m1_space nand2_xoffset = 2*self.m1_width + 5*self.m1_space
nand2_xoffset = inv1_xoffset + self.inv.width
inv2_xoffset = nand2_xoffset + self.nand2.width inv2_xoffset = nand2_xoffset + self.nand2.width
self.width = inv2_xoffset + self.inv.width self.width = inv2_xoffset + self.inv.width
@ -140,13 +131,9 @@ class wordline_driver(design.design):
y_offset = self.inv.height*row y_offset = self.inv.height*row
inst_mirror = "R0" inst_mirror = "R0"
inv1_offset = [inv1_xoffset, y_offset]
nand2_offset=[nand2_xoffset, y_offset] nand2_offset=[nand2_xoffset, y_offset]
inv2_offset=[inv2_xoffset, y_offset] inv2_offset=[inv2_xoffset, y_offset]
# add inv1 based on the info above
self.inv1_inst[row].place(offset=inv1_offset,
mirror=inst_mirror)
# add nand 2 # add nand 2
self.nand_inst[row].place(offset=nand2_offset, self.nand_inst[row].place(offset=nand2_offset,
mirror=inst_mirror) mirror=inst_mirror)
@ -159,7 +146,7 @@ class wordline_driver(design.design):
""" Route all of the signals """ """ Route all of the signals """
# Wordline enable connection # Wordline enable connection
en_pin=self.add_layout_pin(text="en", en_pin=self.add_layout_pin(text="en_bar",
layer="metal2", layer="metal2",
offset=[self.m1_width + 2*self.m1_space,0], offset=[self.m1_width + 2*self.m1_space,0],
width=self.m2_width, width=self.m2_width,
@ -167,12 +154,11 @@ class wordline_driver(design.design):
for row in range(self.rows): for row in range(self.rows):
inv1_inst = self.inv1_inst[row]
nand_inst = self.nand_inst[row] nand_inst = self.nand_inst[row]
inv2_inst = self.inv2_inst[row] inv2_inst = self.inv2_inst[row]
# en connection # en_bar connection
a_pin = inv1_inst.get_pin("A") a_pin = nand_inst.get_pin("A")
a_pos = a_pin.lc() a_pos = a_pin.lc()
clk_offset = vector(en_pin.bc().x,a_pos.y) clk_offset = vector(en_pin.bc().x,a_pos.y)
self.add_segment_center(layer="metal1", self.add_segment_center(layer="metal1",
@ -181,13 +167,6 @@ class wordline_driver(design.design):
self.add_via_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=clk_offset) offset=clk_offset)
# first inv to nand2 A
zb_pos = inv1_inst.get_pin("Z").bc()
zu_pos = inv1_inst.get_pin("Z").uc()
bl_pos = nand_inst.get_pin("A").lc()
br_pos = nand_inst.get_pin("A").rc()
self.add_path("metal1", [zb_pos, zu_pos, bl_pos, br_pos])
# Nand2 out to 2nd inv # Nand2 out to 2nd inv
zr_pos = nand_inst.get_pin("Z").rc() zr_pos = nand_inst.get_pin("Z").rc()
al_pos = inv2_inst.get_pin("A").lc() al_pos = inv2_inst.get_pin("A").lc()

View File

@ -68,7 +68,7 @@ class write_driver_array(design.design):
def create_write_array(self): def create_write_array(self):
self.driver_insts = {} self.driver_insts = {}
for i in range(0,self.columns,self.words_per_row): for i in range(0,self.columns,self.words_per_row):
name = "Xwrite_driver{}".format(i) name = "write_driver{}".format(i)
index = int(i/self.words_per_row) index = int(i/self.words_per_row)
self.driver_insts[index]=self.add_inst(name=name, self.driver_insts[index]=self.add_inst(name=name,
mod=self.driver) mod=self.driver)

128
compiler/pgates/pand2.py Normal file
View File

@ -0,0 +1,128 @@
import debug
from tech import drc
from math import log
from vector import vector
from globals import OPTS
from pnand2 import pnand2
from pinv import pinv
import pgate
class pand2(pgate.pgate):
"""
This is a simple buffer used for driving loads.
"""
from importlib import reload
c = reload(__import__(OPTS.bitcell))
bitcell = getattr(c, OPTS.bitcell)
unique_id = 1
def __init__(self, size=1, height=None, name=""):
self.size = size
if name=="":
name = "pand2_{0}_{1}".format(size, pand2.unique_id)
pand2.unique_id += 1
pgate.pgate.__init__(self, name, height)
debug.info(1, "Creating {}".format(self.name))
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
def create_netlist(self):
self.add_pins()
self.create_modules()
self.create_insts()
def create_modules(self):
# Shield the cap, but have at least a stage effort of 4
self.nand = pnand2(height=self.height)
self.add_mod(self.nand)
self.inv = pinv(size=self.size, height=self.height)
self.add_mod(self.inv)
def create_layout(self):
self.width = self.nand.width + self.inv.width
self.place_insts()
self.add_wires()
self.add_layout_pins()
def add_pins(self):
self.add_pin("A")
self.add_pin("B")
self.add_pin("Z")
self.add_pin("vdd")
self.add_pin("gnd")
def create_insts(self):
self.nand_inst=self.add_inst(name="pand2_nand",
mod=self.nand)
self.connect_inst(["A", "B", "zb_int", "vdd", "gnd"])
self.inv_inst=self.add_inst(name="pand2_inv",
mod=self.inv)
self.connect_inst(["zb_int", "Z", "vdd", "gnd"])
def place_insts(self):
# Add NAND to the right
self.nand_inst.place(offset=vector(0,0))
# Add INV to the right
self.inv_inst.place(offset=vector(self.nand_inst.rx(),0))
def add_wires(self):
# nand Z to inv A
z1_pin = self.nand_inst.get_pin("Z")
a2_pin = self.inv_inst.get_pin("A")
mid1_point = vector(0.5*(z1_pin.cx()+a2_pin.cx()), z1_pin.cy())
mid2_point = vector(mid1_point, a2_pin.cy())
self.add_path("metal1", [z1_pin.center(), mid1_point, mid2_point, a2_pin.center()])
def add_layout_pins(self):
# Continous vdd rail along with label.
vdd_pin=self.inv_inst.get_pin("vdd")
self.add_layout_pin(text="vdd",
layer="metal1",
offset=vdd_pin.ll().scale(0,1),
width=self.width,
height=vdd_pin.height())
# Continous gnd rail along with label.
gnd_pin=self.inv_inst.get_pin("gnd")
self.add_layout_pin(text="gnd",
layer="metal1",
offset=gnd_pin.ll().scale(0,1),
width=self.width,
height=vdd_pin.height())
pin = self.inv_inst.get_pin("Z")
self.add_layout_pin_rect_center(text="Z",
layer=pin.layer,
offset=pin.center(),
width=pin.width(),
height=pin.height())
for pin_name in ["A","B"]:
pin = self.nand_inst.get_pin(pin_name)
self.add_layout_pin_rect_center(text=pin_name,
layer=pin.layer,
offset=pin.center(),
width=pin.width(),
height=pin.height())
def analytical_delay(self, slew, load=0.0):
""" Calculate the analytical delay of DFF-> INV -> INV """
nand_delay = selfnand.analytical_delay(slew=slew, load=self.inv.input_load())
inv_delay = self.inv.analytical_delay(slew=nand_delay.slew, load=load)
return nand_delay + inv_delay

139
compiler/pgates/pbuf.py Normal file
View File

@ -0,0 +1,139 @@
import debug
from tech import drc
from math import log
from vector import vector
from globals import OPTS
from pinv import pinv
import pgate
class pbuf(pgate.pgate):
"""
This is a simple buffer used for driving loads.
"""
from importlib import reload
c = reload(__import__(OPTS.bitcell))
bitcell = getattr(c, OPTS.bitcell)
unique_id = 1
def __init__(self, size=4, height=None, name=""):
self.stage_effort = 4
self.size = size
self.height = height
if name=="":
name = "pbuf_{0}_{1}".format(self.size, pbuf.unique_id)
pbuf.unique_id += 1
pgate.pgate.__init__(self, name, height)
debug.info(1, "creating {0} with size of {1}".format(self.name,self.size))
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
def create_netlist(self):
self.add_pins()
self.create_modules()
self.create_insts()
def create_layout(self):
self.width = self.inv1.width + self.inv2.width
self.place_insts()
self.add_wires()
self.add_layout_pins()
def add_pins(self):
self.add_pin("A")
self.add_pin("Z")
self.add_pin("vdd")
self.add_pin("gnd")
def create_modules(self):
# Shield the cap, but have at least a stage effort of 4
input_size = max(1,int(self.size/self.stage_effort))
self.inv1 = pinv(size=input_size, height=self.height)
self.add_mod(self.inv1)
self.inv2 = pinv(size=self.size, height=self.height)
self.add_mod(self.inv2)
def create_insts(self):
self.inv1_inst=self.add_inst(name="buf_inv1",
mod=self.inv1)
self.connect_inst(["A", "zb_int", "vdd", "gnd"])
self.inv2_inst=self.add_inst(name="buf_inv2",
mod=self.inv2)
self.connect_inst(["zb_int", "Z", "vdd", "gnd"])
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 add_wires(self):
# inv1 Z to inv2 A
z1_pin = self.inv1_inst.get_pin("Z")
a2_pin = self.inv2_inst.get_pin("A")
mid_point = vector(z1_pin.cx(), a2_pin.cy())
self.add_path("metal1", [z1_pin.center(), mid_point, a2_pin.center()])
def add_layout_pins(self):
# Continous vdd rail along with label.
vdd_pin=self.inv1_inst.get_pin("vdd")
self.add_layout_pin(text="vdd",
layer="metal1",
offset=vdd_pin.ll().scale(0,1),
width=self.width,
height=vdd_pin.height())
# Continous gnd rail along with label.
gnd_pin=self.inv1_inst.get_pin("gnd")
self.add_layout_pin(text="gnd",
layer="metal1",
offset=gnd_pin.ll().scale(0,1),
width=self.width,
height=vdd_pin.height())
z_pin = self.inv2_inst.get_pin("Z")
self.add_layout_pin_rect_center(text="Z",
layer=z_pin.layer,
offset=z_pin.center(),
width=z_pin.width(),
height=z_pin.height())
a_pin = self.inv1_inst.get_pin("A")
self.add_layout_pin_rect_center(text="A",
layer=a_pin.layer,
offset=a_pin.center(),
width=a_pin.width(),
height=a_pin.height())
def analytical_delay(self, slew, load=0.0):
""" Calculate the analytical delay of DFF-> INV -> INV """
inv1_delay = self.inv1.analytical_delay(slew=slew, load=self.inv2.input_load())
inv2_delay = self.inv2.analytical_delay(slew=inv1_delay.slew, load=load)
return inv1_delay + inv2_delay
def determine_z_stage_efforts(self, external_cout, inp_is_rise=False):
"""Get the stage efforts of the A -> Z path"""
stage_effort_list = []
stage1_cout = self.inv2.get_cin()
stage1 = self.inv1.get_effort_stage(stage1_cout, inp_is_rise)
stage_effort_list.append(stage1)
last_stage_is_rise = stage1.is_rise
stage2 = self.inv2.get_effort_stage(external_cout, last_stage_is_rise)
stage_effort_list.append(stage2)
return stage_effort_list

View File

@ -35,7 +35,6 @@ class pnand2(pgate.pgate):
self.create_netlist() self.create_netlist()
if not OPTS.netlist_only: if not OPTS.netlist_only:
self.create_layout() self.create_layout()
#self.DRC_LVS()
def create_netlist(self): def create_netlist(self):
@ -194,26 +193,33 @@ class pnand2(pgate.pgate):
""" Route the Z output """ """ Route the Z output """
# PMOS1 drain # PMOS1 drain
pmos_pin = self.pmos1_inst.get_pin("D") pmos_pin = self.pmos1_inst.get_pin("D")
top_pin_offset = pmos_pin.center()
# NMOS2 drain # NMOS2 drain
nmos_pin = self.nmos2_inst.get_pin("D") nmos_pin = self.nmos2_inst.get_pin("D")
bottom_pin_offset = nmos_pin.center()
# Output pin # Output pin
mid_offset = vector(nmos_pin.center().x,self.inputA_yoffset) out_offset = vector(nmos_pin.center().x + self.m1_pitch,self.inputA_yoffset)
# Midpoints of the L routes go horizontal first then vertical
mid1_offset = vector(out_offset.x, top_pin_offset.y)
mid2_offset = vector(out_offset.x, bottom_pin_offset.y)
self.add_contact_center(layers=("metal1", "via1", "metal2"), self.add_contact_center(layers=("metal1", "via1", "metal2"),
offset=pmos_pin.center()) offset=pmos_pin.center())
self.add_contact_center(layers=("metal1", "via1", "metal2"), self.add_contact_center(layers=("metal1", "via1", "metal2"),
offset=nmos_pin.center()) offset=nmos_pin.center())
self.add_contact_center(layers=("metal1", "via1", "metal2"), self.add_contact_center(layers=("metal1", "via1", "metal2"),
offset=mid_offset, offset=out_offset,
rotate=90) rotate=90)
# PMOS1 to mid-drain to NMOS2 drain # PMOS1 to mid-drain to NMOS2 drain
self.add_path("metal2",[pmos_pin.bc(), mid_offset, nmos_pin.uc()]) self.add_path("metal2",[top_pin_offset, mid1_offset, out_offset, mid2_offset, bottom_pin_offset])
# This extends the output to the edge of the cell # This extends the output to the edge of the cell
self.add_layout_pin_rect_center(text="Z", self.add_layout_pin_rect_center(text="Z",
layer="metal1", layer="metal1",
offset=mid_offset, offset=out_offset,
width=contact.m1m2.first_layer_height, width=contact.m1m2.first_layer_height,
height=contact.m1m2.first_layer_width) height=contact.m1m2.first_layer_width)

View File

@ -51,7 +51,7 @@ class precharge(pgate.pgate):
self.DRC_LVS() self.DRC_LVS()
def add_pins(self): def add_pins(self):
self.add_pin_list(["bl", "br", "en", "vdd"]) self.add_pin_list(["bl", "br", "en_bar", "vdd"])
def add_ptx(self): def add_ptx(self):
""" """
@ -92,15 +92,15 @@ class precharge(pgate.pgate):
self.lower_pmos_inst=self.add_inst(name="lower_pmos", self.lower_pmos_inst=self.add_inst(name="lower_pmos",
mod=self.pmos) mod=self.pmos)
self.connect_inst(["bl", "en", "br", "vdd"]) self.connect_inst(["bl", "en_bar", "br", "vdd"])
self.upper_pmos1_inst=self.add_inst(name="upper_pmos1", self.upper_pmos1_inst=self.add_inst(name="upper_pmos1",
mod=self.pmos) mod=self.pmos)
self.connect_inst(["bl", "en", "vdd", "vdd"]) self.connect_inst(["bl", "en_bar", "vdd", "vdd"])
self.upper_pmos2_inst=self.add_inst(name="upper_pmos2", self.upper_pmos2_inst=self.add_inst(name="upper_pmos2",
mod=self.pmos) mod=self.pmos)
self.connect_inst(["br", "en", "vdd", "vdd"]) self.connect_inst(["br", "en_bar", "vdd", "vdd"])
def place_ptx(self): def place_ptx(self):
@ -161,7 +161,7 @@ class precharge(pgate.pgate):
rotate=90) rotate=90)
# adds the en rail on metal1 # adds the en rail on metal1
self.add_layout_pin_segment_center(text="en", self.add_layout_pin_segment_center(text="en_bar",
layer="metal1", layer="metal1",
start=offset.scale(0,1), start=offset.scale(0,1),
end=offset.scale(0,1)+vector(self.width,0)) end=offset.scale(0,1)+vector(self.width,0))

View File

@ -68,12 +68,12 @@ class ptx(design.design):
# Just make a guess since these will actually be decided in the layout later. # Just make a guess since these will actually be decided in the layout later.
area_sd = 2.5*drc("minwidth_poly")*self.tx_width area_sd = 2.5*drc("minwidth_poly")*self.tx_width
perimeter_sd = 2*drc("minwidth_poly") + 2*self.tx_width perimeter_sd = 2*drc("minwidth_poly") + 2*self.tx_width
self.spice_device="M{{0}} {{1}} {0} m={1} w={2}u l={3}u pd={4}u ps={4}u as={5}p ad={5}p".format(spice[self.tx_type], self.spice_device="M{{0}} {{1}} {0} m={1} w={2}u l={3}u pd={4:.2f}u ps={4:.2f}u as={5:.2f}p ad={5:.2f}p".format(spice[self.tx_type],
self.mults, self.mults,
self.tx_width, self.tx_width,
drc("minwidth_poly"), drc("minwidth_poly"),
perimeter_sd, perimeter_sd,
area_sd) area_sd)
self.spice.append("\n* ptx " + self.spice_device) self.spice.append("\n* ptx " + self.spice_device)
# self.spice.append(".ENDS {0}".format(self.name)) # self.spice.append(".ENDS {0}".format(self.name))

View File

@ -35,8 +35,6 @@ class sram():
from sram_1bank import sram_1bank as sram from sram_1bank import sram_1bank as sram
elif self.num_banks == 2: elif self.num_banks == 2:
from sram_2bank import sram_2bank as sram from sram_2bank import sram_2bank as sram
elif self.num_banks == 4:
from sram_4bank import sram_4bank as sram
else: else:
debug.error("Invalid number of banks.",-1) debug.error("Invalid number of banks.",-1)

View File

@ -61,15 +61,18 @@ class sram_1bank(sram_base):
row_addr_pos = [None]*len(self.all_ports) row_addr_pos = [None]*len(self.all_ports)
col_addr_pos = [None]*len(self.all_ports) col_addr_pos = [None]*len(self.all_ports)
data_pos = [None]*len(self.all_ports) data_pos = [None]*len(self.all_ports)
# This is M2 pitch even though it is on M1 to help stem via spacings on the trunk
data_gap = self.m2_pitch*(self.word_size+1)
# This is M2 pitch even though it is on M1 to help stem via spacings on the trunk
# The M1 pitch is for supply rail spacings
max_gap_size = self.m2_pitch*max(self.word_size+1,self.col_addr_size+1) + 2*self.m1_pitch
# Port 0 # Port 0
port = 0 port = 0
# This includes 2 M2 pitches for the row addr clock line
# This includes 2 M2 pitches for the row addr clock line.
# It is also placed to align with the column decoder (if it exists hence the bank gap)
control_pos[port] = vector(-self.control_logic_insts[port].width - 2*self.m2_pitch, 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.bank.bank_array_ll.y - self.control_logic_insts[port].mod.control_logic_center.y - self.bank.m2_gap)
self.control_logic_insts[port].place(control_pos[port]) self.control_logic_insts[port].place(control_pos[port])
# The row address bits are placed above the control logic aligned on the right. # The row address bits are placed above the control logic aligned on the right.
@ -81,8 +84,8 @@ class sram_1bank(sram_base):
# Add the col address flops below the bank to the left of the lower-left of bank array # Add the col address flops below the bank to the left of the lower-left of bank array
if self.col_addr_dff: if self.col_addr_dff:
col_addr_pos[port] = vector(self.bank.bank_array_ll.x - self.col_addr_dff_insts[port].width - self.bank.central_bus_width, col_addr_pos[port] = vector(self.bank.bank_array_ll.x - self.col_addr_dff_insts[port].width - self.bank.m2_gap,
-data_gap - self.col_addr_dff_insts[port].height) -max_gap_size - self.col_addr_dff_insts[port].height)
self.col_addr_dff_insts[port].place(col_addr_pos[port]) self.col_addr_dff_insts[port].place(col_addr_pos[port])
# Add the data flops below the bank to the right of the lower-left of bank array # Add the data flops below the bank to the right of the lower-left of bank array
@ -92,16 +95,18 @@ class sram_1bank(sram_base):
# sense amps. # sense amps.
if port in self.write_ports: if port in self.write_ports:
data_pos[port] = vector(self.bank.bank_array_ll.x, data_pos[port] = vector(self.bank.bank_array_ll.x,
-data_gap - self.data_dff_insts[port].height) -max_gap_size - self.data_dff_insts[port].height)
self.data_dff_insts[port].place(data_pos[port]) self.data_dff_insts[port].place(data_pos[port])
if len(self.all_ports)>1: if len(self.all_ports)>1:
# Port 1 # Port 1
port = 1 port = 1
# This includes 2 M2 pitches for the row addr clock line # This includes 2 M2 pitches for the row addr clock line
# It is also placed to align with the column decoder (if it exists hence the bank gap)
control_pos[port] = vector(self.bank_inst.rx() + self.control_logic_insts[port].width + 2*self.m2_pitch, control_pos[port] = vector(self.bank_inst.rx() + 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.bank.bank_array_ll.y - self.control_logic_insts[port].mod.control_logic_center.y + self.bank.m2_gap)
self.control_logic_insts[port].place(control_pos[port], mirror="MY") self.control_logic_insts[port].place(control_pos[port], mirror="MY")
# The row address bits are placed above the control logic aligned on the left. # The row address bits are placed above the control logic aligned on the left.
@ -113,8 +118,8 @@ class sram_1bank(sram_base):
# Add the col address flops above the bank to the right of the upper-right of bank array # Add the col address flops above the bank to the right of the upper-right of bank array
if self.col_addr_dff: if self.col_addr_dff:
col_addr_pos[port] = vector(self.bank.bank_array_ur.x + self.bank.central_bus_width, col_addr_pos[port] = vector(self.bank.bank_array_ur.x + self.bank.m2_gap,
self.bank_inst.uy() + data_gap + self.col_addr_dff_insts[port].height) self.bank.height + max_gap_size + self.col_addr_dff_insts[port].height)
self.col_addr_dff_insts[port].place(col_addr_pos[port], mirror="MX") self.col_addr_dff_insts[port].place(col_addr_pos[port], mirror="MX")
# Add the data flops above the bank to the left of the upper-right of bank array # Add the data flops above the bank to the left of the upper-right of bank array
@ -124,7 +129,7 @@ class sram_1bank(sram_base):
# sense amps. # sense amps.
if port in self.write_ports: if port in self.write_ports:
data_pos[port] = vector(self.bank.bank_array_ur.x - self.data_dff_insts[port].width, data_pos[port] = vector(self.bank.bank_array_ur.x - self.data_dff_insts[port].width,
self.bank.uy() + data_gap + self.data_dff_insts[port].height) self.bank.height + max_gap_size + self.data_dff_insts[port].height)
self.data_dff_insts[port].place(data_pos[port], mirror="MX") self.data_dff_insts[port].place(data_pos[port], mirror="MX")
@ -178,27 +183,11 @@ class sram_1bank(sram_base):
# Connect all of these clock pins to the clock in the central bus # Connect all of these clock pins to the clock in the central bus
# This is something like a "spine" clock distribution. The two spines # This is something like a "spine" clock distribution. The two spines
# are clk_buf and clk_buf_bar # are clk_buf and clk_buf_bar
control_clk_buf_pin = self.control_logic_insts[port].get_pin("clk_buf")
control_clk_buf_pos = control_clk_buf_pin.center()
bank_clk_buf_pin = self.bank_inst.get_pin("clk_buf{}".format(port))
bank_clk_buf_pos = bank_clk_buf_pin.center()
bank_clk_buf_bar_pin = self.bank_inst.get_pin("clk_buf_bar{}".format(port))
bank_clk_buf_bar_pos = bank_clk_buf_bar_pin.center()
if self.col_addr_dff:
dff_clk_pin = self.col_addr_dff_insts[port].get_pin("clk")
dff_clk_pos = dff_clk_pin.center()
mid_pos = vector(bank_clk_buf_pos.x, dff_clk_pos.y)
self.add_wire(("metal3","via2","metal2"),[dff_clk_pos, mid_pos, bank_clk_buf_pos])
if port in self.write_ports:
data_dff_clk_pin = self.data_dff_insts[port].get_pin("clk")
data_dff_clk_pos = data_dff_clk_pin.center()
mid_pos = vector(bank_clk_buf_pos.x, data_dff_clk_pos.y)
self.add_wire(("metal3","via2","metal2"),[data_dff_clk_pos, mid_pos, bank_clk_buf_pos])
# This uses a metal2 track to the right (for port0) of the control/row addr DFF # This uses a metal2 track to the right (for port0) of the control/row addr DFF
# to route vertically. For port1, it is to the left. # to route vertically. For port1, it is to the left.
control_clk_buf_pin = self.control_logic_insts[port].get_pin("clk_buf")
row_addr_clk_pin = self.row_addr_dff_insts[port].get_pin("clk") row_addr_clk_pin = self.row_addr_dff_insts[port].get_pin("clk")
if port%2: if port%2:
control_clk_buf_pos = control_clk_buf_pin.lc() control_clk_buf_pos = control_clk_buf_pin.lc()
@ -210,17 +199,38 @@ class sram_1bank(sram_base):
row_addr_clk_pos = row_addr_clk_pin.rc() row_addr_clk_pos = row_addr_clk_pin.rc()
mid1_pos = vector(self.row_addr_dff_insts[port].rx() + self.m2_pitch, mid1_pos = vector(self.row_addr_dff_insts[port].rx() + self.m2_pitch,
row_addr_clk_pos.y) row_addr_clk_pos.y)
mid2_pos = vector(mid1_pos.x,
control_clk_buf_pos.y) # This is the steiner point where the net branches out
clk_steiner_pos = vector(mid1_pos.x, control_clk_buf_pos.y)
self.add_path("metal1", [control_clk_buf_pos, clk_steiner_pos])
self.add_via_center(layers=("metal1","via1","metal2"),
offset=clk_steiner_pos,
rotate=90)
# Note, the via to the control logic is taken care of when we route # Note, the via to the control logic is taken care of when we route
# the control logic to the bank # the control logic to the bank
self.add_wire(("metal3","via2","metal2"),[row_addr_clk_pos, mid1_pos, mid2_pos, control_clk_buf_pos]) self.add_wire(("metal3","via2","metal2"),[row_addr_clk_pos, mid1_pos, clk_steiner_pos, control_clk_buf_pos])
if self.col_addr_dff:
dff_clk_pin = self.col_addr_dff_insts[port].get_pin("clk")
dff_clk_pos = dff_clk_pin.center()
mid_pos = vector(clk_steiner_pos.x, dff_clk_pos.y)
self.add_wire(("metal3","via2","metal2"),[dff_clk_pos, mid_pos, clk_steiner_pos])
if port in self.write_ports:
data_dff_clk_pin = self.data_dff_insts[port].get_pin("clk")
data_dff_clk_pos = data_dff_clk_pin.center()
mid_pos = vector(clk_steiner_pos.x, data_dff_clk_pos.y)
self.add_wire(("metal3","via2","metal2"),[data_dff_clk_pos, mid_pos, clk_steiner_pos])
def route_control_logic(self): def route_control_logic(self):
""" Route the outputs from the control logic module """ """ Route the outputs from the control logic module """
for port in self.all_ports: for port in self.all_ports:
for signal in self.control_logic_outputs[port]: for signal in self.control_logic_outputs[port]:
# The clock gets routed separately and is not a part of the bank
if "clk" in signal:
continue
src_pin = self.control_logic_insts[port].get_pin(signal) src_pin = self.control_logic_insts[port].get_pin(signal)
dest_pin = self.bank_inst.get_pin(signal+"{}".format(port)) dest_pin = self.bank_inst.get_pin(signal+"{}".format(port))
self.connect_rail_from_left_m2m3(src_pin, dest_pin) self.connect_rail_from_left_m2m3(src_pin, dest_pin)
@ -268,17 +278,20 @@ class sram_1bank(sram_base):
""" Connect the output of the data flops to the write driver """ """ Connect the output of the data flops to the write driver """
# This is where the channel will start (y-dimension at least) # This is where the channel will start (y-dimension at least)
for port in self.write_ports: for port in self.write_ports:
offset = self.data_dff_insts[port].ul() + vector(0, 2*self.m1_pitch) if port%2:
offset = self.data_dff_insts[port].ll() - vector(0, (self.word_size+2)*self.m1_pitch)
else:
offset = self.data_dff_insts[port].ul() + vector(0, 2*self.m1_pitch)
dff_names = ["dout_{}".format(x) for x in range(self.word_size)] dff_names = ["dout_{}".format(x) for x in range(self.word_size)]
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)] bank_names = ["din{0}_{1}".format(port,x) for x in range(self.word_size)]
bank_pins = [self.bank_inst.get_pin(x) for x in bank_names]
route_map = list(zip(bank_names, dff_names))
dff_pins = {key: self.data_dff_insts[port].get_pin(key) for key in dff_names } route_map = list(zip(bank_pins, dff_pins))
bank_pins = {key: self.bank_inst.get_pin(key) for key in bank_names } self.create_horizontal_channel_route(route_map, offset)
# Combine the dff and bank pins into a single dictionary of pin name to pin.
all_pins = {**dff_pins, **bank_pins}
self.create_horizontal_channel_route(route_map, all_pins, offset)

View File

@ -1,331 +0,0 @@
import sys
from tech import drc, spice
import debug
from math import log,sqrt,ceil
import datetime
import getpass
from vector import vector
from globals import OPTS, print_time
from sram_base import sram_base
from bank import bank
from dff_buf_array import dff_buf_array
from dff_array import dff_array
class sram_4bank(sram_base):
"""
Procedures specific to a four bank SRAM.
"""
def __init__(self, name, sram_config):
sram_base.__init__(self, name, sram_config)
def compute_bank_offsets(self):
""" Compute the overall offsets for a four bank SRAM """
# The main difference is that the four bank SRAM has the data bus in the middle of the four banks
# as opposed to the top of the banks.
# In 4 bank SRAM, the height is determined by the bank decoder and address flop
self.vertical_bus_height = 2*self.bank.height + 4*self.bank_to_bus_distance + self.data_bus_height \
+ self.supply_bus_height + self.msb_decoder.height + self.msb_address.width
# The address bus extends down through the power rails, but control and bank_sel bus don't
self.addr_bus_height = self.vertical_bus_height
self.vertical_bus_offset = vector(self.bank.width + self.bank_to_bus_distance, 0)
self.data_bus_offset = vector(0, self.bank.height + self.bank_to_bus_distance)
self.supply_bus_offset = vector(0, self.data_bus_offset.y + self.data_bus_height \
+ self.bank.height + 2*self.bank_to_bus_distance)
self.control_bus_offset = vector(0, self.supply_bus_offset.y + self.supply_bus_height)
self.bank_sel_bus_offset = self.vertical_bus_offset + vector(self.m2_pitch*self.control_size,0)
self.addr_bus_offset = self.bank_sel_bus_offset.scale(1,0) + vector(self.m2_pitch*self.num_banks,0)
# Control is placed at the top above the control bus and everything
self.control_logic_position = vector(0, self.control_bus_offset.y + self.control_bus_height + self.m1_pitch)
# Bank select flops get put to the right of control logic above bank1 and the buses
# Leave a pitch to get the vdd rails up to M2
self.msb_address_position = vector(self.bank_inst[1].lx() + 3*self.supply_rail_pitch,
self.supply_bus_offset.y + self.supply_bus_height \
+ 2*self.m1_pitch + self.msb_address.width)
# Decoder goes above the MSB address flops, and is flipped in Y
# separate the two by a bank to bus distance for nwell rules, just in case
self.msb_decoder_position = self.msb_address_position + vector(self.msb_decoder.width, self.bank_to_bus_distance)
def add_modules(self):
""" Adds the modules and the buses to the top level """
self.compute_bus_sizes()
self.add_banks()
self.compute_bank_offsets()
self.add_busses()
self.add_logic()
self.width = self.bank_inst[1].ur().x
self.height = max(self.control_logic_inst.uy(),self.msb_decoder_inst.uy())
def add_banks(self):
# Placement of bank 0 (upper left)
bank_position_0 = vector(self.bank.width,
self.bank.height + self.data_bus_height + 2*self.bank_to_bus_distance)
self.bank_inst=[self.add_bank(0, bank_position_0, 1, -1)]
# Placement of bank 1 (upper right)
x_off = self.bank.width + self.vertical_bus_width + 2*self.bank_to_bus_distance
bank_position_1 = vector(x_off, bank_position_0.y)
self.bank_inst.append(self.add_bank(1, bank_position_1, 1, 1))
# Placement of bank 2 (bottom left)
y_off = self.bank.height
bank_position_2 = vector(bank_position_0.x, y_off)
self.bank_inst.append(self.add_bank(2, bank_position_2, -1, -1))
# Placement of bank 3 (bottom right)
bank_position_3 = vector(bank_position_1.x, bank_position_2.y)
self.bank_inst.append(self.add_bank(3, bank_position_3, -1, 1))
def add_logic(self):
""" Add the control and MSB decode/bank select logic for four banks """
self.add_control_logic(position=self.control_logic_position)
self.msb_address_inst = self.add_inst(name="msb_address",
mod=self.msb_address,
offset=self.msb_address_position,
rotate=270)
self.msb_bank_sel_addr = ["ADDR[{}]".format(i) for i in range(self.addr_size-2,self.addr_size,1)]
temp = list(self.msb_bank_sel_addr)
temp.extend(["msb{0}[{1}]".format(j,i) for i in range(2) for j in ["","_bar"]])
temp.extend(["clk_buf", "vdd", "gnd"])
self.connect_inst(temp)
self.msb_decoder_inst = self.add_inst(name="msb_decoder",
mod=self.msb_decoder,
offset=self.msb_decoder_position,
mirror="MY")
temp = ["msb[{}]".format(i) for i in range(2)]
temp.extend(["bank_sel[{}]".format(i) for i in range(4)])
temp.extend(["vdd", "gnd"])
self.connect_inst(temp)
def route_double_msb_address(self):
""" Route two MSB address bits and the bank decoder for 4-bank SRAM """
# connect the MSB flops to the address input bus
for i in [0,1]:
msb_pins = self.msb_address_inst.get_pins("din_{}".format(i))
for msb_pin in msb_pins:
if msb_pin.layer == "metal3":
msb_pin_pos = msb_pin.lc()
break
rail_pos = vector(self.vert_control_bus_positions[self.msb_bank_sel_addr[i]].x,msb_pin_pos.y)
self.add_path("metal3",[msb_pin_pos,rail_pos])
self.add_via_center(("metal2","via2","metal3"),rail_pos)
# Connect clk
clk_pin = self.msb_address_inst.get_pin("clk")
clk_pos = clk_pin.bc()
rail_pos = self.horz_control_bus_positions["clk_buf"]
bend_pos = vector(clk_pos.x,self.horz_control_bus_positions["clk_buf"].y)
self.add_path("metal1",[clk_pos,bend_pos,rail_pos])
# Connect bank decoder outputs to the bank select vertical bus wires
for i in range(self.num_banks):
msb_pin = self.msb_decoder_inst.get_pin("out_{}".format(i))
msb_pin_pos = msb_pin.lc()
rail_pos = vector(self.vert_control_bus_positions["bank_sel[{}]".format(i)].x,msb_pin_pos.y)
self.add_path("metal1",[msb_pin_pos,rail_pos])
self.add_via_center(("metal1","via1","metal2"),rail_pos)
# connect MSB flop outputs to the bank decoder inputs
msb_pin = self.msb_address_inst.get_pin("dout[0]")
msb_pin_pos = msb_pin.rc()
in_pin = self.msb_decoder_inst.get_pin("in[0]")
in_pos = in_pin.bc() + vector(0,1*self.m2_pitch,) # pin is up from bottom
out_pos = msb_pin_pos + vector(1*self.m2_pitch,0) # route out to the right
up_pos = vector(out_pos.x,in_pos.y) # and route up to the decoer
self.add_wire(("metal1","via1","metal2"),[msb_pin_pos,out_pos,up_pos,in_pos])
self.add_via_center(("metal1","via1","metal2"),in_pos)
self.add_via_center(("metal1","via1","metal2"),msb_pin_pos,rotate=90)
msb_pin = self.msb_address_inst.get_pin("dout[1]")
msb_pin_pos = msb_pin.rc()
in_pin = self.msb_decoder_inst.get_pin("in[1]")
in_pos = in_pin.bc() + vector(0,self.bitcell.height+self.m2_pitch) # route the next row up
out_pos = msb_pin_pos + vector(2*self.m2_pitch,0) # route out to the right
up_pos = vector(out_pos.x,in_pos.y) # and route up to the decoer
self.add_wire(("metal1","via1","metal2"),[msb_pin_pos,out_pos,up_pos,in_pos])
self.add_via_center(("metal1","via1","metal2"),in_pos)
self.add_via_center(("metal1","via1","metal2"),msb_pin_pos,rotate=90)
self.route_double_msb_address_supplies()
def route_double_msb_address_supplies(self):
""" Route the vdd/gnd bits of the 2-bit bank decoder. """
# Route the right-most vdd/gnd of the right upper bank to the top of the decoder
vdd_pins = self.bank_inst[1].get_pins("vdd")
left_bank_vdd_pin = None
right_bank_vdd_pin = None
for vdd_pin in vdd_pins:
if vdd_pin.layer != "metal2":
continue
if left_bank_vdd_pin == None or vdd_pin.lx()<left_bank_vdd_pin.lx():
left_bank_vdd_pin = vdd_pin
if right_bank_vdd_pin == None or vdd_pin.lx()>right_bank_vdd_pin.lx():
right_bank_vdd_pin = vdd_pin
# Route to top
self.add_rect(layer="metal2",
offset=vdd_pin.ul(),
height=self.height-vdd_pin.uy(),
width=vdd_pin.width())
gnd_pins = self.bank_inst[1].get_pins("gnd")
left_bank_gnd_pin = None
right_bank_gnd_pin = None
for gnd_pin in gnd_pins:
if gnd_pin.layer != "metal2":
continue
if left_bank_gnd_pin == None or gnd_pin.lx()<left_bank_gnd_pin.lx():
left_bank_gnd_pin = gnd_pin
if right_bank_gnd_pin == None or gnd_pin.lx()>right_bank_gnd_pin.lx():
right_bank_gnd_pin = gnd_pin
# Route to top
self.add_rect(layer="metal2",
offset=gnd_pin.ul(),
height=self.height-gnd_pin.uy(),
width=gnd_pin.width())
# Connect bank decoder vdd/gnd supplies using the previous bank pins
vdd_pins = self.msb_decoder_inst.get_pins("vdd")
for vdd_pin in vdd_pins:
if vdd_pin.layer != "metal1":
continue
rail1_pos = vector(left_bank_vdd_pin.cx(),vdd_pin.cy())
rail2_pos = vector(right_bank_vdd_pin.cx(),vdd_pin.cy())
self.add_path("metal1",[rail1_pos,rail2_pos])
self.add_via_center(layers=("metal1","via1","metal2"),
offset=rail1_pos,
rotate=90,
size=[1,3])
self.add_via_center(layers=("metal1","via1","metal2"),
offset=rail2_pos,
rotate=90,
size=[1,3])
gnd_pins = self.msb_decoder_inst.get_pins("gnd")
for gnd_pin in gnd_pins:
if gnd_pin.layer != "metal1":
continue
rail1_pos = vector(left_bank_gnd_pin.cx(),gnd_pin.cy())
rail2_pos = vector(right_bank_gnd_pin.cx(),gnd_pin.cy())
self.add_path("metal1",[rail1_pos,rail2_pos])
self.add_via_center(layers=("metal1","via1","metal2"),
offset=rail1_pos,
rotate=90,
size=[1,3])
self.add_via_center(layers=("metal1","via1","metal2"),
offset=rail2_pos,
rotate=90,
size=[1,3])
# connect the bank MSB flop supplies
vdd_pins = self.msb_address_inst.get_pins("vdd")
# vdd pins go down to the rail
for vdd_pin in vdd_pins:
if vdd_pin.layer != "metal1":
continue
vdd_pos = vdd_pin.bc()
down_pos = vdd_pos - vector(0,self.m1_pitch)
rail_pos = vector(vdd_pos.x,self.horz_control_bus_positions["vdd"].y)
self.add_path("metal1",[vdd_pos,down_pos])
self.add_via_center(layers=("metal1","via1","metal2"),
offset=down_pos,
rotate=90)
self.add_path("metal2",[down_pos,rail_pos])
self.add_via_center(layers=("metal1","via1","metal2"),
offset=rail_pos)
# gnd pins go right to the rail
gnd_pins = self.msb_address_inst.get_pins("gnd")
for gnd_pin in gnd_pins:
if gnd_pin.layer != "metal2":
continue
rail1_pos = vector(left_bank_gnd_pin.cx(),gnd_pin.cy())
self.add_path("metal1",[rail1_pos,gnd_pin.lc()])
self.add_via_center(layers=("metal1","via1","metal2"),
offset=gnd_pin.lc(),
rotate=90)
self.add_via_center(layers=("metal1","via1","metal2"),
offset=rail1_pos,
rotate=90,
size=[1,3])
def route(self):
""" Route all of the signals for the four bank SRAM. """
self.route_shared_banks()
# connect the data output to the data bus
for n in self.data_bus_names:
for i in [0,1]:
pin_pos = self.bank_inst[i].get_pin(n).bc()
rail_pos = vector(pin_pos.x,self.data_bus_positions[n].y)
self.add_path("metal2",[pin_pos,rail_pos])
self.add_via_center(("metal2","via2","metal3"),rail_pos)
for i in [2,3]:
pin_pos = self.bank_inst[i].get_pin(n).uc()
rail_pos = vector(pin_pos.x,self.data_bus_positions[n].y)
self.add_path("metal2",[pin_pos,rail_pos])
self.add_via_center(("metal2","via2","metal3"),rail_pos)
# route msb address bits
# route 2:4 decoder
self.route_double_msb_address()
# connect the banks to the vertical address bus
# connect the banks to the vertical control bus
for n in self.addr_bus_names + self.control_bus_names:
# Skip these from the horizontal bus
if n in ["vdd", "gnd"]: continue
# This will be the bank select, so skip it
if n in self.msb_bank_sel_addr: continue
for bank_id in [0,2]:
pin0_pos = self.bank_inst[bank_id].get_pin(n).rc()
pin1_pos = self.bank_inst[bank_id+1].get_pin(n).lc()
rail_pos = vector(self.vert_control_bus_positions[n].x,pin0_pos.y)
self.add_path("metal3",[pin0_pos,pin1_pos])
self.add_via_center(("metal2","via2","metal3"),rail_pos)
self.route_bank_supply_rails(left_banks=[0,2], bottom_banks=[2,3])
def add_lvs_correspondence_points(self):
"""
This adds some points for easier debugging if LVS goes wrong.
These should probably be turned off by default though, since extraction
will show these as ports in the extracted netlist.
"""
if self.num_banks==1: return
for n in self.control_bus_names:
self.add_label(text=n,
layer="metal2",
offset=self.vert_control_bus_positions[n])
for n in self.bank_sel_bus_names:
self.add_label(text=n,
layer="metal2",
offset=self.vert_control_bus_positions[n])

View File

@ -141,11 +141,16 @@ class sram_base(design):
# The order of the control signals on the control bus: # The order of the control signals on the control bus:
self.control_bus_names = [] self.control_bus_names = []
for port in self.all_ports: for port in self.all_ports:
self.control_bus_names[port] = ["clk_buf{}".format(port), "clk_buf_bar{}".format(port)] self.control_bus_names[port] = ["clk_buf{}".format(port)]
if (self.port_id[port] == "rw") or (self.port_id[port] == "w"): wen = "w_en{}".format(port)
self.control_bus_names[port].append("w_en{}".format(port)) sen = "s_en{}".format(port)
if (self.port_id[port] == "rw") or (self.port_id[port] == "r"): pen = "p_en_bar{}".format(port)
self.control_bus_names[port].append("s_en{}".format(port)) if self.port_id[port] == "r":
self.control_bus_names[port].extend([sen, pen])
elif self.port_id[port] == "w":
self.control_bus_names[port].extend([wen])
else:
self.control_bus_names[port].extend([sen, wen, pen])
self.vert_control_bus_positions = self.create_vertical_bus(layer="metal2", self.vert_control_bus_positions = self.create_vertical_bus(layer="metal2",
pitch=self.m2_pitch, pitch=self.m2_pitch,
offset=self.vertical_bus_offset, offset=self.vertical_bus_offset,
@ -293,11 +298,12 @@ class sram_base(design):
temp.append("bank_sel{0}[{1}]".format(port,bank_num)) temp.append("bank_sel{0}[{1}]".format(port,bank_num))
for port in self.read_ports: for port in self.read_ports:
temp.append("s_en{0}".format(port)) temp.append("s_en{0}".format(port))
for port in self.read_ports:
temp.append("p_en_bar{0}".format(port))
for port in self.write_ports: for port in self.write_ports:
temp.append("w_en{0}".format(port)) temp.append("w_en{0}".format(port))
for port in self.all_ports: for port in self.all_ports:
temp.append("clk_buf_bar{0}".format(port)) temp.append("wl_en{0}".format(port))
temp.append("clk_buf{0}".format(port))
temp.extend(["vdd", "gnd"]) temp.extend(["vdd", "gnd"])
self.connect_inst(temp) self.connect_inst(temp)
@ -409,16 +415,21 @@ class sram_base(design):
mod = self.control_logic_r mod = self.control_logic_r
insts.append(self.add_inst(name="control{}".format(port), mod=mod)) insts.append(self.add_inst(name="control{}".format(port), mod=mod))
# Inputs
temp = ["csb{}".format(port)] temp = ["csb{}".format(port)]
if port in self.readwrite_ports: if port in self.readwrite_ports:
temp.append("web{}".format(port)) temp.append("web{}".format(port))
temp.append("clk{}".format(port)) temp.append("clk{}".format(port))
# Ouputs
if port in self.read_ports: if port in self.read_ports:
temp.append("s_en{}".format(port)) temp.append("s_en{}".format(port))
if port in self.write_ports: if port in self.write_ports:
temp.append("w_en{}".format(port)) temp.append("w_en{}".format(port))
temp.extend(["clk_buf_bar{}".format(port), "clk_buf{}".format(port), "vdd", "gnd"]) if port in self.read_ports:
temp.append("p_en_bar{}".format(port))
temp.extend(["wl_en{}".format(port), "clk_buf{}".format(port), "vdd", "gnd"])
self.connect_inst(temp) self.connect_inst(temp)
return insts return insts

View File

@ -1,6 +1,6 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
""" """
Run a regression test on a dff_inv. Run a regression test on a pand2 cell
""" """
import unittest import unittest
@ -11,19 +11,22 @@ import globals
from globals import OPTS from globals import OPTS
import debug import debug
class dff_inv_test(openram_test): class pand2_test(openram_test):
def runTest(self): def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
import dff_inv global verify
import verify
debug.info(2, "Testing dff_inv 4x") import pand2
a = dff_inv.dff_inv(4)
debug.info(2, "Testing pand2 gate 4x")
a = pand2.pand2(4)
self.local_check(a) self.local_check(a)
globals.end_openram() globals.end_openram()
# run the test from the command line # instantiate a copdsay of the class to actually run the test
if __name__ == "__main__": if __name__ == "__main__":
(OPTS, args) = globals.parse_args() (OPTS, args) = globals.parse_args()
del sys.argv[1:] del sys.argv[1:]

34
compiler/tests/04_pbuf_test.py Executable file
View File

@ -0,0 +1,34 @@
#!/usr/bin/env python3
"""
Run a regression test on a 2-row buffer cell
"""
import unittest
from testutils import header,openram_test
import sys,os
sys.path.append(os.path.join(sys.path[0],".."))
import globals
from globals import OPTS
import debug
class pbuf_test(openram_test):
def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
global verify
import verify
import pbuf
debug.info(2, "Testing inverter/buffer 4x 8x")
a = pbuf.pbuf(8)
self.local_check(a)
globals.end_openram()
# instantiate a copdsay of the class to actually run the test
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main()

View File

@ -1,39 +0,0 @@
#!/usr/bin/env python3
"""
Run a regression test on a dff_array.
"""
import unittest
from testutils import header,openram_test
import sys,os
sys.path.append(os.path.join(sys.path[0],".."))
import globals
from globals import OPTS
import debug
class dff_inv_array_test(openram_test):
def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
import dff_inv_array
debug.info(2, "Testing dff_inv_array for 3x3")
a = dff_inv_array.dff_inv_array(rows=3, columns=3)
self.local_check(a)
debug.info(2, "Testing dff_inv_array for 1x3")
a = dff_inv_array.dff_inv_array(rows=1, columns=3)
self.local_check(a)
debug.info(2, "Testing dff_inv_array for 3x1")
a = dff_inv_array.dff_inv_array(rows=3, columns=1)
self.local_check(a)
globals.end_openram()
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main()

View File

@ -0,0 +1,50 @@
#!/usr/bin/env python3
"""
Run a regression test on a 1 bank SRAM
"""
import unittest
from testutils import header,openram_test
import sys,os
sys.path.append(os.path.join(sys.path[0],".."))
import globals
from globals import OPTS
import debug
#@unittest.skip("SKIPPING 20_psram_1bank_test, multiport layout not complete")
class psram_1bank_2mux_1rw_1w_test(openram_test):
def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
from sram import sram
from sram_config import sram_config
OPTS.bitcell = "pbitcell"
OPTS.replica_bitcell="replica_pbitcell"
OPTS.num_rw_ports = 1
OPTS.num_w_ports = 1
OPTS.num_r_ports = 0
c = sram_config(word_size=4,
num_words=32,
num_banks=1)
c.num_words=32
c.words_per_row=2
debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
OPTS.num_r_ports,
OPTS.num_w_ports,
c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
a = sram(c, "sram")
self.local_check(a, final_verification=True)
globals.end_openram()
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main()

View File

@ -11,8 +11,8 @@ import globals
from globals import OPTS from globals import OPTS
import debug import debug
#@unittest.skip("SKIPPING 20_psram_1bank_test, multiport layout not complete") #@unittest.skip("SKIPPING 20_psram_1bank_2mux_1w_1r_test, odd supply routing error")
class psram_1bank_2mux_test(openram_test): class psram_1bank_2mux_1w_1r_test(openram_test):
def runTest(self): def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
@ -30,7 +30,13 @@ class psram_1bank_2mux_test(openram_test):
num_banks=1) num_banks=1)
c.num_words=32 c.num_words=32
c.words_per_row=2 c.words_per_row=2
debug.info(1, "Single bank two way column mux 1w/1r with control logic") debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
OPTS.num_r_ports,
OPTS.num_w_ports,
c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
a = sram(c, "sram") a = sram(c, "sram")
self.local_check(a, final_verification=True) self.local_check(a, final_verification=True)

View File

@ -11,7 +11,7 @@ import globals
from globals import OPTS from globals import OPTS
import debug import debug
#@unittest.skip("SKIPPING 20_psram_1bank_test, multiport layout not complete") @unittest.skip("SKIPPING 20_psram_1bank_2mux_test, wide metal supply routing error")
class psram_1bank_2mux_test(openram_test): class psram_1bank_2mux_test(openram_test):
def runTest(self): def runTest(self):
@ -31,7 +31,13 @@ class psram_1bank_2mux_test(openram_test):
num_banks=1) num_banks=1)
c.num_words=32 c.num_words=32
c.words_per_row=2 c.words_per_row=2
debug.info(1, "Single bank two way column mux with control logic") debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
OPTS.num_r_ports,
OPTS.num_w_ports,
c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
a = sram(c, "sram") a = sram(c, "sram")
self.local_check(a, final_verification=True) self.local_check(a, final_verification=True)

View File

@ -0,0 +1,49 @@
#!/usr/bin/env python3
"""
Run a regression test on a 1 bank SRAM
"""
import unittest
from testutils import header,openram_test
import sys,os
sys.path.append(os.path.join(sys.path[0],".."))
import globals
from globals import OPTS
import debug
class psram_1bank_4mux_1rw_1r_test(openram_test):
def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
from sram import sram
from sram_config import sram_config
OPTS.bitcell = "pbitcell"
OPTS.replica_bitcell="replica_pbitcell"
OPTS.num_rw_ports = 1
OPTS.num_w_ports = 0
OPTS.num_r_ports = 1
c = sram_config(word_size=4,
num_words=64,
num_banks=1)
c.num_words=64
c.words_per_row=4
debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
OPTS.num_r_ports,
OPTS.num_w_ports,
c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
a = sram(c, "sram")
self.local_check(a, final_verification=True)
globals.end_openram()
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main()

View File

@ -29,7 +29,13 @@ class sram_1bank_2mux_1rw_1r_test(openram_test):
num_banks=1) num_banks=1)
c.words_per_row=2 c.words_per_row=2
debug.info(1, "Single bank, two way column mux 1rw, 1r with control logic") debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
OPTS.num_r_ports,
OPTS.num_w_ports,
c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
a = sram(c, "sram") a = sram(c, "sram")
self.local_check(a, final_verification=True) self.local_check(a, final_verification=True)

View File

@ -23,7 +23,13 @@ class sram_1bank_2mux_test(openram_test):
num_banks=1) num_banks=1)
c.words_per_row=2 c.words_per_row=2
debug.info(1, "Single bank two way column mux with control logic") debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
OPTS.num_r_ports,
OPTS.num_w_ports,
c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
a = sram(c, "sram") a = sram(c, "sram")
self.local_check(a, final_verification=True) self.local_check(a, final_verification=True)

View File

@ -23,7 +23,13 @@ class sram_1bank_4mux_test(openram_test):
num_banks=1) num_banks=1)
c.words_per_row=4 c.words_per_row=4
debug.info(1, "Single bank, four way column mux with control logic") debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
OPTS.num_r_ports,
OPTS.num_w_ports,
c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
a = sram(c, "sram") a = sram(c, "sram")
self.local_check(a, final_verification=True) self.local_check(a, final_verification=True)

View File

@ -0,0 +1,49 @@
#!/usr/bin/env python3
"""
Run a regression test on a 1 bank SRAM
"""
import unittest
from testutils import header,openram_test
import sys,os
sys.path.append(os.path.join(sys.path[0],".."))
import globals
from globals import OPTS
import debug
class sram_1bank_8mux_1rw_1r_test(openram_test):
def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
from sram import sram
from sram_config import sram_config
OPTS.bitcell = "bitcell_1rw_1r"
OPTS.replica_bitcell = "replica_bitcell_1rw_1r"
OPTS.num_rw_ports = 1
OPTS.num_r_ports = 1
OPTS.num_w_ports = 0
c = sram_config(word_size=2,
num_words=128,
num_banks=1)
c.words_per_row=8
debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
OPTS.num_r_ports,
OPTS.num_w_ports,
c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
a = sram(c, "sram")
self.local_check(a, final_verification=True)
globals.end_openram()
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main()

View File

@ -23,7 +23,13 @@ class sram_1bank_8mux_test(openram_test):
num_banks=1) num_banks=1)
c.words_per_row=8 c.words_per_row=8
debug.info(1, "Single bank, eight way column mux with control logic") debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
OPTS.num_r_ports,
OPTS.num_w_ports,
c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
a = sram(c, "sram") a = sram(c, "sram")
self.local_check(a, final_verification=True) self.local_check(a, final_verification=True)

View File

@ -29,7 +29,13 @@ class sram_1bank_nomux_1rw_1r_test(openram_test):
num_banks=1) num_banks=1)
c.words_per_row=1 c.words_per_row=1
debug.info(1, "Single bank, no column mux 1rw, 1r with control logic") debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
OPTS.num_r_ports,
OPTS.num_w_ports,
c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
a = sram(c, "sram") a = sram(c, "sram")
self.local_check(a, final_verification=True) self.local_check(a, final_verification=True)

View File

@ -23,7 +23,13 @@ class sram_1bank_nomux_test(openram_test):
num_banks=1) num_banks=1)
c.words_per_row=1 c.words_per_row=1
debug.info(1, "Single bank, no column mux with control logic") debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
OPTS.num_r_ports,
OPTS.num_w_ports,
c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
a = sram(c, "sram") a = sram(c, "sram")
self.local_check(a, final_verification=True) self.local_check(a, final_verification=True)

View File

@ -1,55 +0,0 @@
#!/usr/bin/env python3
"""
Run a regression test on a 4 bank SRAM
"""
import unittest
from testutils import header,openram_test
import sys,os
sys.path.append(os.path.join(sys.path[0],".."))
import globals
from globals import OPTS
import debug
@unittest.skip("Multibank is not working yet.")
class sram_4bank_test(openram_test):
def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
from sram import sram
from sram_config import sram_config
c = sram_config(word_size=16,
num_words=64,
num_banks=4)
debug.info(1, "Four bank, no column mux with control logic")
a = sram(c, "sram1")
self.local_check(a, final_verification=True)
c.num_words=128
c.words_per_row=2
debug.info(1, "Four bank two way column mux with control logic")
a = sram(c, "sram2")
self.local_check(a, final_verification=True)
c.num_words=256
c.words_per_row=4
debug.info(1, "Four bank, four way column mux with control logic")
a = sram(c, "sram3")
self.local_check(a, final_verification=True)
c.word_size=2
c.num_words=256
c.words_per_row=8
debug.info(1, "Four bank, eight way column mux with control logic")
a = sram.sram(c, "sram4")
self.local_check(a, final_verification=True)
globals.end_openram()
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main()

View File

@ -49,7 +49,6 @@ class timing_sram_test(openram_test):
#Combine info about port into all data #Combine info about port into all data
data.update(port_data[0]) data.update(port_data[0])
#Assumes single rw port (6t sram)
if OPTS.tech_name == "freepdk45": if OPTS.tech_name == "freepdk45":
golden_data = {'delay_hl': [0.15801], golden_data = {'delay_hl': [0.15801],
'delay_lh': [0.15801], 'delay_lh': [0.15801],

View File

@ -11,8 +11,8 @@ import globals
from globals import OPTS from globals import OPTS
import debug import debug
#@unittest.skip("SKIPPING 22_psram_1bank_2mux_func_test") @unittest.skip("SKIPPING 22_psram_1bank_2mux_1rw_1r_1w_func_test, third port reads are broken?")
class psram_1bank_2mux_func_test(openram_test): class psram_1bank_2mux_1rw_1r_1w_func_test(openram_test):
def runTest(self): def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
@ -35,10 +35,13 @@ class psram_1bank_2mux_func_test(openram_test):
num_words=64, num_words=64,
num_banks=1) num_banks=1)
c.words_per_row=2 c.words_per_row=2
debug.info(1, "Functional test for psram with {} bit words, {} words, {} words per row, {} banks".format(c.word_size, debug.info(1, "Functional test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
c.num_words, OPTS.num_r_ports,
c.words_per_row, OPTS.num_w_ports,
c.num_banks)) c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
s = sram(c, name="sram") s = sram(c, name="sram")
tempspice = OPTS.openram_temp + "temp.sp" tempspice = OPTS.openram_temp + "temp.sp"
s.sp_write(tempspice) s.sp_write(tempspice)

View File

@ -11,7 +11,7 @@ import globals
from globals import OPTS from globals import OPTS
import debug import debug
#@unittest.skip("SKIPPING 22_psram_1bank_4mux_func_test") @unittest.skip("SKIPPING 22_psram_1bank_4mux_func_test, third port reads are broken?")
class psram_1bank_4mux_func_test(openram_test): class psram_1bank_4mux_func_test(openram_test):
def runTest(self): def runTest(self):
@ -35,10 +35,13 @@ class psram_1bank_4mux_func_test(openram_test):
num_words=256, num_words=256,
num_banks=1) num_banks=1)
c.words_per_row=4 c.words_per_row=4
debug.info(1, "Functional test for psram with {} bit words, {} words, {} words per row, {} banks".format(c.word_size, debug.info(1, "Functional test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
c.num_words, OPTS.num_r_ports,
c.words_per_row, OPTS.num_w_ports,
c.num_banks)) c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
s = sram(c, name="sram") s = sram(c, name="sram")
tempspice = OPTS.openram_temp + "temp.sp" tempspice = OPTS.openram_temp + "temp.sp"
s.sp_write(tempspice) s.sp_write(tempspice)

View File

@ -35,10 +35,13 @@ class psram_1bank_8mux_func_test(openram_test):
num_words=256, num_words=256,
num_banks=1) num_banks=1)
c.words_per_row=8 c.words_per_row=8
debug.info(1, "Functional test for psram with {} bit words, {} words, {} words per row, {} banks".format(c.word_size, debug.info(1, "Functional test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
c.num_words, OPTS.num_r_ports,
c.words_per_row, OPTS.num_w_ports,
c.num_banks)) c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
s = sram(c, name="sram") s = sram(c, name="sram")
tempspice = OPTS.openram_temp + "temp.sp" tempspice = OPTS.openram_temp + "temp.sp"
s.sp_write(tempspice) s.sp_write(tempspice)

View File

@ -35,10 +35,13 @@ class psram_1bank_nomux_func_test(openram_test):
num_words=32, num_words=32,
num_banks=1) num_banks=1)
c.words_per_row=1 c.words_per_row=1
debug.info(1, "Functional test for psram with {} bit words, {} words, {} words per row, {} banks".format(c.word_size, debug.info(1, "Functional test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
c.num_words, OPTS.num_r_ports,
c.words_per_row, OPTS.num_w_ports,
c.num_banks)) c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
s = sram(c, name="sram") s = sram(c, name="sram")
tempspice = OPTS.openram_temp + "temp.sp" tempspice = OPTS.openram_temp + "temp.sp"
s.sp_write(tempspice) s.sp_write(tempspice)

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="76" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="a"><rect width="76" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#a)"><path fill="#555" d="M0 0h49v20H0z"/><path fill="#97CA00" d="M49 0h27v20H49z"/><path fill="url(#b)" d="M0 0h76v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110"> <text x="255" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="390">Python</text><text x="255" y="140" transform="scale(.1)" textLength="390">Python</text><text x="615" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="170">3.5</text><text x="615" y="140" transform="scale(.1)" textLength="170">3.5</text></g> </svg>

After

Width:  |  Height:  |  Size: 948 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="106" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="a"><rect width="106" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#a)"><path fill="#555" d="M0 0h63v20H0z"/><path fill="#007ec6" d="M63 0h43v20H63z"/><path fill="url(#b)" d="M0 0h106v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110"> <text x="325" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="530">download</text><text x="325" y="140" transform="scale(.1)" textLength="530">download</text><text x="835" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="330">stable</text><text x="835" y="140" transform="scale(.1)" textLength="330">stable</text></g> </svg>

After

Width:  |  Height:  |  Size: 961 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="120" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="a"><rect width="120" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#a)"><path fill="#555" d="M0 0h63v20H0z"/><path fill="#007ec6" d="M63 0h57v20H63z"/><path fill="url(#b)" d="M0 0h120v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110"> <text x="325" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="530">download</text><text x="325" y="140" transform="scale(.1)" textLength="530">download</text><text x="905" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="470">unstable</text><text x="905" y="140" transform="scale(.1)" textLength="470">unstable</text></g> </svg>

After

Width:  |  Height:  |  Size: 965 B

View File

@ -1,2 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="112" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="a"><rect width="112" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#a)"><path fill="#555" d="M0 0h71v20H0z"/><path fill="#007ec6" d="M71 0h41v20H71z"/><path fill="url(#b)" d="M0 0h112v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110"><text x="365" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="610">download </text><text x="365" y="140" transform="scale(.1)" textLength="610">download </text><text x="905" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="310">latest</text><text x="905" y="140" transform="scale(.1)" textLength="310">latest</text></g> </svg>

Before

Width:  |  Height:  |  Size: 1004 B

View File

@ -2,34 +2,35 @@
.SUBCKT write_driver din bl br en vdd gnd .SUBCKT write_driver din bl br en vdd gnd
**** Inverter to conver Data_in to data_in_bar ****** **** Inverter to conver Data_in to data_in_bar ******
M_1 din_bar din gnd gnd n W='1.2*1u' L=0.6u * din_bar = inv(din)
M_2 din_bar din vdd vdd p W='2.1*1u' L=0.6u M_1 din_bar din gnd gnd n W=1.2u L=0.6u
M_2 din_bar din vdd vdd p W=2.1u L=0.6u
**** 2input nand gate follwed by inverter to drive BL ****** **** 2input nand gate follwed by inverter to drive BL ******
M_3 din_bar_gated en net_7 gnd n W='2.1*1u' L=0.6u * din_bar_gated = nand(en, din)
M_4 net_7 din gnd gnd n W='2.1*1u' L=0.6u M_3 din_bar_gated en net_7 gnd n W=2.1u L=0.6u
M_5 din_bar_gated en vdd vdd p W='2.1*1u' L=0.6u M_4 net_7 din gnd gnd n W=2.1u L=0.6u
M_6 din_bar_gated din vdd vdd p W='2.1*1u' L=0.6u M_5 din_bar_gated en vdd vdd p W=2.1u L=0.6u
M_6 din_bar_gated din vdd vdd p W=2.1u L=0.6u
* din_bar_gated_bar = inv(din_bar_gated)
M_7 net_1 din_bar_gated vdd vdd p W='2.1*1u' L=0.6u M_7 din_bar_gated_bar din_bar_gated vdd vdd p W=2.1u L=0.6u
M_8 net_1 din_bar_gated gnd gnd n W='1.2*1u' L=0.6u M_8 din_bar_gated_bar din_bar_gated gnd gnd n W=1.2u L=0.6u
**** 2input nand gate follwed by inverter to drive BR****** **** 2input nand gate follwed by inverter to drive BR******
* din_gated = nand(en, din_bar)
M_9 din_gated en vdd vdd p W='2.1*1u' L=0.6u M_9 din_gated en vdd vdd p W=2.1u L=0.6u
M_10 din_gated en net_8 gnd n W='2.1*1u' L=0.6u M_10 din_gated en net_8 gnd n W=2.1u L=0.6u
M_11 net_8 din_bar gnd gnd n W='2.1*1u' L=0.6u M_11 net_8 din_bar gnd gnd n W=2.1u L=0.6u
M_12 din_gated din_bar vdd vdd p W='2.1*1u' L=0.6u M_12 din_gated din_bar vdd vdd p W=2.1u L=0.6u
* din_gated_bar = inv(din_gated)
M_13 net_6 din_gated vdd vdd p W='2.1*1u' L=0.6u M_13 din_gated_bar din_gated vdd vdd p W=2.1u L=0.6u
M_14 net_6 din_gated gnd gnd n W='1.2*1u' L=0.6u M_14 din_gated_bar din_gated gnd gnd n W=1.2u L=0.6u
************************************************ ************************************************
* pull down with en enable
M_15 bl net_6 net_5 gnd n W='3.6*1u' L=0.6u M_15 bl din_gated_bar net_5 gnd n W=3.6u L=0.6u
M_16 br net_1 net_5 gnd n W='3.6*1u' L=0.6u M_16 br din_bar_gated_bar net_5 gnd n W=3.6u L=0.6u
M_17 net_5 en gnd gnd n W='3.6*1u' L=0.6u M_17 net_5 en gnd gnd n W=3.6u L=0.6u

View File

@ -1,23 +1,37 @@
*********************** Write_Driver ****************************** *********************** Write_Driver ******************************
.SUBCKT write_driver din bl br en vdd gnd .SUBCKT write_driver din bl br en vdd gnd
* SPICE3 file created from write_driver.ext - technology: scmos
M1000 a_44_708# a_36_700# bl gnd n w=2.4u l=0.4u **** Inverter to conver Data_in to data_in_bar ******
M1001 br a_16_500# a_44_708# gnd n w=2.4u l=0.4u * din_bar = inv(din)
M1002 a_44_708# en gnd gnd n w=2.4u l=0.4u M_1 din_bar din gnd gnd n W=0.8u L=0.4u
M1003 gnd a_8_284# a_16_500# gnd n w=0.8u l=0.4u M_2 din_bar din vdd vdd p W=1.4u L=0.4u
M1004 a_36_700# a_20_328# gnd gnd n w=0.8u l=0.4u
M1005 vdd a_8_284# a_16_500# vdd p w=1.4u l=0.4u
M1006 a_36_700# a_20_328# vdd vdd p w=1.4u l=0.4u
M1007 vdd en a_20_328# vdd p w=1.4u l=0.4u
M1008 a_20_328# a_64_360# vdd vdd p w=1.4u l=0.4u
M1009 a_48_328# en a_20_328# gnd n w=1.4u l=0.4u
M1010 gnd a_64_360# a_48_328# gnd n w=1.4u l=0.4u
M1011 a_40_228# en a_8_284# gnd n w=1.4u l=0.4u
M1012 gnd din a_40_228# gnd n w=1.4u l=0.4u
M1013 a_64_360# din gnd gnd n w=0.8u l=0.4u
M1014 a_8_284# en vdd vdd p w=1.4u l=0.4u
M1015 vdd din a_8_284# vdd p w=1.4u l=0.4u
M1016 a_64_360# din vdd vdd p w=1.4u l=0.4u
.ENDS **** 2input nand gate follwed by inverter to drive BL ******
* din_bar_gated = nand(en, din)
M_3 din_bar_gated en net_7 gnd n W=1.4u L=0.4u
M_4 net_7 din gnd gnd n W=1.4u L=0.4u
M_5 din_bar_gated en vdd vdd p W=1.4u L=0.4u
M_6 din_bar_gated din vdd vdd p W=1.4u L=0.4u
* din_bar_gated_bar = inv(din_bar_gated)
M_7 din_bar_gated_bar din_bar_gated vdd vdd p W=1.4u L=0.4u
M_8 din_bar_gated_bar din_bar_gated gnd gnd n W=0.8u L=0.4u
**** 2input nand gate follwed by inverter to drive BR******
* din_gated = nand(en, din_bar)
M_9 din_gated en vdd vdd p W=1.4u L=0.4u
M_10 din_gated en net_8 gnd n W=1.4u L=0.4u
M_11 net_8 din_bar gnd gnd n W=1.4u L=0.4u
M_12 din_gated din_bar vdd vdd p W=1.4u L=0.4u
* din_gated_bar = inv(din_gated)
M_13 din_gated_bar din_gated vdd vdd p W=1.4u L=0.4u
M_14 din_gated_bar din_gated gnd gnd n W=0.8u L=0.4u
************************************************
* pull down with en enable
M_15 bl din_gated_bar net_5 gnd n W=2.4u L=0.4u
M_16 br din_bar_gated_bar net_5 gnd n W=2.4u L=0.4u
M_17 net_5 en gnd gnd n W=2.4u L=0.4u
.ENDS $ write_driver

View File

@ -240,7 +240,7 @@ spice["fet_models"] = { "TT" : [SPICE_MODEL_DIR+"/nom/pmos.sp",SPICE_MODEL_DIR+"
#spice stimulus related variables #spice stimulus related variables
spice["feasible_period"] = 5 # estimated feasible period in ns spice["feasible_period"] = 10 # estimated feasible period in ns
spice["supply_voltages"] = [4.5, 5.0, 5.5] # Supply voltage corners in [Volts] spice["supply_voltages"] = [4.5, 5.0, 5.5] # Supply voltage corners in [Volts]
spice["nom_supply_voltage"] = 5.0 # Nominal supply voltage in [Volts] spice["nom_supply_voltage"] = 5.0 # Nominal supply voltage in [Volts]
spice["rise_time"] = 0.05 # rise time in [Nano-seconds] spice["rise_time"] = 0.05 # rise time in [Nano-seconds]