Use 4x16 decoder with dual port bitcell in tests.

This commit is contained in:
mrg 2020-10-05 10:52:56 -07:00
parent c06b02e6fc
commit 4a58f09c1c
5 changed files with 124 additions and 35 deletions

View File

@ -122,7 +122,7 @@ class and4_dec(design.design):
width=pin.width(),
height=pin.height())
for pin_name in ["A", "B", "C"]:
for pin_name in ["A", "B", "C", "D"]:
pin = self.nand_inst.get_pin(pin_name)
self.add_layout_pin_rect_center(text=pin_name,
layer=pin.layer,

View File

@ -24,13 +24,14 @@ class hierarchical_decoder(design.design):
self.pre2x4_inst = []
self.pre3x8_inst = []
self.pre4x16_inst = []
b = factory.create(module_type="bitcell")
self.cell_height = b.height
self.num_outputs = num_outputs
self.num_inputs = math.ceil(math.log(self.num_outputs, 2))
(self.no_of_pre2x4, self.no_of_pre3x8)=self.determine_predecodes(self.num_inputs)
(self.no_of_pre2x4, self.no_of_pre3x8, self.no_of_pre4x16)=self.determine_predecodes(self.num_inputs)
self.create_netlist()
if not OPTS.netlist_only:
@ -86,25 +87,37 @@ class hierarchical_decoder(design.design):
height=self.cell_height)
self.add_mod(self.pre3_8)
self.pre4_16 = factory.create(module_type="hierarchical_predecode4x16",
height=self.cell_height)
self.add_mod(self.pre4_16)
def determine_predecodes(self, num_inputs):
""" Determines the number of 2:4 pre-decoder and 3:8 pre-decoder
needed based on the number of inputs """
"""
Determines the number of 2:4, 3:8 and 4:16 pre-decoders
needed based on the number of inputs
"""
if (num_inputs == 2):
return (1, 0)
return (1, 0, 0)
elif (num_inputs == 3):
return(0, 1)
return(0, 1, 0)
elif (num_inputs == 4):
return(2, 0)
return(2, 0, 0)
elif (num_inputs == 5):
return(1, 1)
return(1, 1, 0)
elif (num_inputs == 6):
return(3, 0)
return(3, 0, 0)
elif (num_inputs == 7):
return(2, 1)
return(2, 1, 0)
elif (num_inputs == 8):
return(1, 2)
return(1, 2, 0)
elif (num_inputs == 9):
return(0, 3)
return(0, 3, 0)
elif (num_inputs == 10):
return(0, 2, 1)
elif (num_inputs == 11):
return(0, 1, 2)
elif (num_inputs == 12):
return(0, 0, 3)
else:
debug.error("Invalid number of inputs for hierarchical decoder", -1)
@ -131,12 +144,19 @@ class hierarchical_decoder(design.design):
index = index + 1
self.predec_groups.append(lines)
for i in range(self.no_of_pre4x16):
lines = []
for j in range(16):
lines.append(index)
index = index + 1
self.predec_groups.append(lines)
def setup_layout_constants(self):
""" Calculate the overall dimensions of the hierarchical decoder """
# If we have 4 or fewer rows, the predecoder is the decoder itself
if self.num_inputs>=4:
self.total_number_of_predecoder_outputs = 4 * self.no_of_pre2x4 + 8 * self.no_of_pre3x8
self.total_number_of_predecoder_outputs = 4 * self.no_of_pre2x4 + 8 * self.no_of_pre3x8 + 16 * self.no_of_pre4x16
else:
self.total_number_of_predecoder_outputs = 0
debug.error("Not enough rows ({}) for a hierarchical decoder. Non-hierarchical not supported yet.".format(self.num_inputs),
@ -144,17 +164,20 @@ class hierarchical_decoder(design.design):
# Calculates height and width of pre-decoder,
# FIXME: Update with 4x16
if self.no_of_pre3x8 > 0 and self.no_of_pre2x4 > 0:
self.predecoder_width = max(self.pre3_8.width, self.pre2_4.width)
elif self.no_of_pre3x8 > 0:
self.predecoder_width = self.pre3_8.width
else:
self.predecoder_width = self.pre2_4.width
self.predecoder_width = 0
if self.no_of_pre2x4 > 0:
self.predecoder_width = max(self.predecoder_width, self.pre2_4.width)
if self.no_of_pre3x8 > 0:
self.predecoder_width = max(self.predecoder_width, self.pre3_8.width)
if self.no_of_pre4x16 > 0:
self.predecoder_width = max(self.predecoder_width, self.pre4_16.width)
# How much space between each predecoder
self.predecoder_spacing = 2 * self.and2.height
self.predecoder_height = self.pre2_4.height * self.no_of_pre2x4 + self.pre3_8.height * self.no_of_pre3x8 \
+ (self.no_of_pre2x4 + self.no_of_pre3x8 - 1) * self.predecoder_spacing
self.predecoder_height = self.pre2_4.height * self.no_of_pre2x4 \
+ self.pre3_8.height * self.no_of_pre3x8 \
+ self.pre4_16.height * self.no_of_pre4x16 \
+ (self.no_of_pre2x4 + self.no_of_pre3x8 + self.no_of_pre4x16 - 1) * self.predecoder_spacing
# Inputs to cells are on input layer
# Outputs from cells are on output layer
@ -192,6 +215,8 @@ class hierarchical_decoder(design.design):
min_x = min(min_x, self.pre2x4_inst[0].lx())
if self.no_of_pre3x8 > 0:
min_x = min(min_x, self.pre3x8_inst[0].lx())
if self.no_of_pre4x16 > 0:
min_x = min(min_x, self.pre4x16_inst[0].lx())
input_offset=vector(min_x - self.input_routing_width, 0)
input_bus_names = ["addr_{0}".format(i) for i in range(self.num_inputs)]
@ -232,6 +257,20 @@ class hierarchical_decoder(design.design):
self.route_input_bus(decoder_offset, input_offset)
for pre_num in range(self.no_of_pre4x16):
for i in range(4):
index = pre_num * 4 + i + self.no_of_pre3x8 * 3 + self.no_of_pre2x4 * 2
input_pos = self.input_bus["addr_{}".format(index)].center()
in_name = "in_{}".format(i)
decoder_pin = self.pre4x16_inst[pre_num].get_pin(in_name)
decoder_offset = decoder_pin.center()
input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1)
self.route_input_bus(decoder_offset, input_offset)
def route_input_bus(self, input_offset, output_offset):
"""
Route a vertical M2 coordinate to another
@ -267,6 +306,9 @@ class hierarchical_decoder(design.design):
for i in range(self.no_of_pre3x8):
self.create_pre3x8(i)
for i in range(self.no_of_pre4x16):
self.create_pre4x16(i)
def create_pre2x4(self, num):
""" Add a 2x4 predecoder to the left of the origin """
@ -305,6 +347,24 @@ class hierarchical_decoder(design.design):
mod=self.pre3_8))
self.connect_inst(pins)
def create_pre4x16(self, num):
""" Add 4x16 predecoder to the left of the origin and above any 3x8 decoders """
# If we had 2x4 predecodes, those are used as the lower
# decode output bits
in_index_offset = num * 4 + self.no_of_pre3x8 * 3 + self.no_of_pre2x4 * 2
out_index_offset = num * 16 + self.no_of_pre3x8 * 8 + self.no_of_pre2x4 * 4
pins = []
for input_index in range(4):
pins.append("addr_{0}".format(input_index + in_index_offset))
for output_index in range(16):
pins.append("out_{0}".format(output_index + out_index_offset))
pins.extend(["vdd", "gnd"])
self.pre4x16_inst.append(self.add_inst(name="pre4x16_{0}".format(num),
mod=self.pre4_16))
self.connect_inst(pins)
def place_pre_decoder(self):
""" Creates pre-decoder and places labels input address [A] """
@ -314,11 +374,16 @@ class hierarchical_decoder(design.design):
for i in range(self.no_of_pre3x8):
self.place_pre3x8(i)
for i in range(self.no_of_pre4x16):
self.place_pre4x16(i)
self.predecode_height = 0
if self.no_of_pre2x4 > 0:
self.predecode_height = self.pre2x4_inst[-1].uy()
if self.no_of_pre3x8 > 0:
self.predecode_height = self.pre3x8_inst[-1].uy()
if self.no_of_pre4x16 > 0:
self.predecode_height = self.pre4x16_inst[-1].uy()
def place_pre2x4(self, num):
""" Place 2x4 predecoder to the left of the origin """
@ -333,6 +398,14 @@ class hierarchical_decoder(design.design):
offset = vector(-self.pre3_8.width, height)
self.pre3x8_inst[num].place(offset)
def place_pre4x16(self, num):
""" Place 3x8 predecoder to the left of the origin and above any 2x4 decoders """
height = self.no_of_pre2x4 * (self.pre2_4.height + self.predecoder_spacing) \
+ self.no_of_pre3x8 * (self.pre3_8.height + self.predecoder_spacing) \
+ num * (self.pre4_16.height + self.predecoder_spacing)
offset = vector(-self.pre4_16.width, height)
self.pre4x16_inst[num].place(offset)
def create_row_decoder(self):
""" Create the row-decoder by placing AND2/AND3 and Inverters
and add the primary decoder output pins. """
@ -468,7 +541,17 @@ class hierarchical_decoder(design.design):
x_offset = self.pre3x8_inst[pre_num].rx() + self.output_layer_pitch
y_offset = self.pre3x8_inst[pre_num].by() + i * self.cell_height
self.route_predecode_bus_inputs(predecode_name, pin, x_offset, y_offset)
# FIXME: convert to connect_bus
for pre_num in range(self.no_of_pre4x16):
for i in range(16):
predecode_name = "predecode_{}".format(pre_num * 16 + i + self.no_of_pre3x8 * 8 + self.no_of_pre2x4 * 4)
out_name = "out_{}".format(i)
pin = self.pre4x16_inst[pre_num].get_pin(out_name)
x_offset = self.pre4x16_inst[pre_num].rx() + self.output_layer_pitch
y_offset = self.pre4x16_inst[pre_num].by() + i * self.cell_height
self.route_predecode_bus_inputs(predecode_name, pin, x_offset, y_offset)
def route_bus_to_decoder(self):
"""
Use the self.predec_groups to determine the connections to the decoder AND gates.
@ -559,7 +642,7 @@ class hierarchical_decoder(design.design):
start_layer=supply_pin.layer)
# Copy the pins from the predecoders
for pre in self.pre2x4_inst + self.pre3x8_inst:
for pre in self.pre2x4_inst + self.pre3x8_inst + self.pre4x16_inst:
for pin_name in ["vdd", "gnd"]:
self.copy_layout_pin(pre, pin_name)

View File

@ -133,7 +133,6 @@ class pnand4(pgate.pgate):
# This is the extra space needed to ensure DRC rules
# to the active contacts
nmos = factory.create(module_type="ptx", tx_type="nmos")
extra_contact_space = max(-nmos.get_pin("D").by(), 0)
def create_ptx(self):
"""
@ -248,29 +247,29 @@ class pnand4(pgate.pgate):
"A",
position="left")
self.inputB_yoffset = self.inputA_yoffset + self.m3_pitch
self.inputB_yoffset = self.inputA_yoffset + self.m1_pitch
bpin = self.route_input_gate(self.pmos2_inst,
self.nmos2_inst,
self.inputB_yoffset,
"B",
position="center")
self.inputC_yoffset = self.inputB_yoffset + self.m3_pitch
self.inputC_yoffset = self.inputB_yoffset + self.m1_pitch
cpin = self.route_input_gate(self.pmos3_inst,
self.nmos3_inst,
self.inputC_yoffset,
"C",
position="right")
self.inputD_yoffset = self.inputC_yoffset + self.m3_pitch
cpin = self.route_input_gate(self.pmos4_inst,
self.inputD_yoffset = self.inputC_yoffset + self.m1_pitch
dpin = self.route_input_gate(self.pmos4_inst,
self.nmos4_inst,
self.inputD_yoffset,
"D",
position="right")
if OPTS.tech_name == "sky130":
self.add_enclosure([apin, bpin, cpin], "npc", drc("npc_enclose_poly"))
self.add_enclosure([apin, bpin, cpin, dpin], "npc", drc("npc_enclose_poly"))
def route_output(self):
""" Route the Z output """

View File

@ -26,11 +26,6 @@ class pnand4_test(openram_test):
tx = factory.create(module_type="pnand4", size=1)
self.local_check(tx)
# debug.info(2, "Checking 3-input nand gate")
# tx = factory.create(module_type="pnand3", size=1, add_wells=False)
# # Only DRC because well contacts will fail LVS
# self.local_drc_check(tx)
globals.end_openram()

View File

@ -8,19 +8,26 @@
#
import unittest
from testutils import *
import sys,os
import sys, os
sys.path.append(os.getenv("OPENRAM_HOME"))
import globals
from globals import OPTS
from sram_factory import factory
import debug
class hierarchical_decoder_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
globals.init_openram(config_file)
# Use the 2 port cell since it is usually bigger/easier
OPTS.bitcell = "bitcell_1rw_1r"
OPTS.num_rw_ports = 1
OPTS.num_r_ports = 1
OPTS.num_w_ports = 0
# Checks 2x4 and 2-input NAND decoder
debug.info(1, "Testing 16 row sample for hierarchical_decoder")
a = factory.create(module_type="hierarchical_decoder", num_outputs=16)
@ -50,6 +57,11 @@ class hierarchical_decoder_test(openram_test):
debug.info(1, "Testing 512 row sample for hierarchical_decoder")
a = factory.create(module_type="hierarchical_decoder", num_outputs=512)
self.local_check(a)
# Checks 3 x 4x16 and 4-input NAND decoder
debug.info(1, "Testing 4096 row sample for hierarchical_decoder")
a = factory.create(module_type="hierarchical_decoder", num_outputs=4096)
self.local_check(a)
globals.end_openram()