mirror of https://github.com/VLSIDA/OpenRAM.git
Use 4x16 decoder with dual port bitcell in tests.
This commit is contained in:
parent
c06b02e6fc
commit
4a58f09c1c
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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 """
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue