Single bank passing DRC and LVS again.

Unfold hierarchical decoder to improve routability.
This commit is contained in:
Matt Guthaus 2018-03-16 17:46:29 -07:00
parent b867e163a6
commit 1f81b24e96
10 changed files with 163 additions and 117 deletions

View File

@ -178,7 +178,7 @@ class layout(lef.lef):
""" Return the pin or list of pins """
try:
if len(self.pin_map[text])>1:
debug.warning("Should use a pin iterator since more than one pin {}".format(text))
debug.error("Should use a pin iterator since more than one pin {}".format(text),-1)
# If we have one pin, return it and not the list.
# Otherwise, should use get_pins()
return self.pin_map[text][0]

View File

@ -96,7 +96,6 @@ class bank(design.design):
self.route_tri_gate_out()
self.route_wordline_driver()
self.route_row_decoder()
self.route_address()
self.route_column_address_lines()
self.route_control_lines()
self.add_control_pins()
@ -174,11 +173,6 @@ class bank(design.design):
# The width of this bus is needed to place other modules (e.g. decoder)
self.central_bus_width = self.m2_pitch * (self.num_control_lines + self.num_addr_lines + 1)
# The width of the bus below the decoder to route to the central bus
self.addr_bus_height = self.m1_pitch * (self.addr_size + 1)
@ -411,7 +405,7 @@ class bank(design.design):
"""
# Place the col decoder aligned left to row decoder
x_off = -(self.central_bus_width + self.row_decoder.width)
y_off = -(self.row_decoder.predecoder_height + self.col_decoder.height + self.addr_bus_height)
y_off = -(self.row_decoder.predecoder_height + self.col_decoder.height + 2*drc["well_to_well"])
self.col_decoder_inst=self.add_inst(name="col_address_decoder",
mod=self.col_decoder,
offset=vector(x_off,y_off))
@ -460,10 +454,8 @@ class bank(design.design):
offset=self.bank_select_pos)
temp = []
for i in range(self.num_control_lines):
temp.append(self.input_control_signals[i])
for i in range(self.num_control_lines):
temp.append(self.control_signals[i])
temp.extend(self.input_control_signals)
temp.extend(self.control_signals)
temp.extend(["vdd", "gnd"])
self.connect_inst(temp)
@ -505,9 +497,7 @@ class bank(design.design):
if self.col_addr_size > 0:
col_decoder_min_point = self.col_decoder_inst.by()
else:
col_decoder_min_point = row_decoder_min_point - self.addr_bus_height
self.addr_min_point = row_decoder_min_point - self.addr_bus_height
col_decoder_min_point = row_decoder_min_point
if self.num_banks>1:
# The control gating logic is below the decoder
@ -575,11 +565,11 @@ class bank(design.design):
# Make the xoffset map the center of the rail
self.bus_xoffset[name]=x_offset + 0.5*self.m2_width
# Add a label pin for LVS correspondence and visual help inspecting the rail.
self.add_label_pin(text=name,
self.add_layout_pin(text=name,
layer="metal2",
offset=vector(x_offset, self.addr_min_point),
offset=vector(x_offset, self.min_point),
width=self.m2_width,
height=-self.addr_min_point)
height=-self.min_point)
# Column mux lines if there is column mux
# goes from bottom of bitcell array down to the bottom of the column decoder/addresses
@ -775,11 +765,11 @@ class bank(design.design):
# The Address LSB
decode_in_pin = self.col_decoder_inst.get_pin("A")
pin_pos = vector(self.left_gnd_x_offset, decode_in_pin.cy())
pin_pos = vector(decode_in_pin.cx(), self.min_point)
self.add_layout_pin_center_segment(text="A[0]",
layer="metal1",
layer="metal2",
start=pin_pos,
end=decode_in_pin.lc())
end=decode_in_pin.bc())
elif self.col_addr_size > 1:
# Route the col decoder outputs to the col select bus
@ -794,14 +784,14 @@ class bank(design.design):
# Route from the col decoder up to the address bus
for i in range(self.col_addr_size):
name = "A[{}]".format(i)
decode_in_pin = self.col_decoder_inst.get_pin("in[{}]".format(i))
addr_pin = self.get_pin(name)
addr_pos = vector(decode_in_pin.cx(), addr_pin.by() + 0.5*self.m1_width)
self.add_path("metal2", [addr_pos, decode_in_pin.uc()])
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=addr_pos,
rotate=90)
decoder_name = "in[{}]".format(i)
addr_name = "A[{}]".format(i)
decode_in_pin = self.col_decoder_inst.get_pin(decoder_name)
pin_pos = vector(decode_in_pin.cx(), self.min_point)
self.add_layout_pin_center_segment(text=addr_name,
layer="metal2",
start=pin_pos,
end=decode_in_pin.bc())
# route the gnd rails, add contact to rail as well
@ -823,26 +813,6 @@ class bank(design.design):
rotate=90)
def route_address(self):
""" Routing the row address lines from the address ms-flop array to the row-decoder """
for i in range(self.addr_size):
name = "A[{}]".format(i)
address_y_offset = self.addr_min_point + (i+1)*self.m1_pitch
pin_pos = vector(self.left_gnd_x_offset, address_y_offset)
if name in self.bus_xoffset:
rail_pos = vector(self.bus_xoffset[name],pin_pos.y)
else:
# Route to right of col decoder if it's not part of central bus
rail_pos = vector(self.col_decoder_inst.rx(),pin_pos.y)
self.add_layout_pin_center_segment(text=name,
layer="metal1",
start=pin_pos,
end=rail_pos)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=rail_pos,
rotate=90)
def add_lvs_correspondence_points(self):

View File

@ -21,18 +21,14 @@ class bank_select(design.design):
# Number of control lines in the bus
self.num_control_lines = 6
# The order of the control signals on the control bus:
self.input_control_signals = ["clk_buf", "tri_en_bar", "tri_en", "clk_bar", "w_en", "s_en"]
self.input_control_signals = ["clk_buf", "tri_en_bar", "tri_en", "clk_buf_bar", "w_en", "s_en"]
# These will be outputs of the gaters if this is multibank
self.control_signals = ["gated_"+str for str in self.input_control_signals]
for i in range(self.num_control_lines):
input_name = self.input_control_signals[i]
self.add_pin(input_name)
for i in range(self.num_control_lines):
gated_name = self.control_signals[i]
self.add_pin(gated_name)
self.add_pin("vdd")
self.add_pin("gnd")
self.add_pin_list(self.input_control_signals, "INPUT")
self.add_pin_list(self.control_signals, "OUTPUT")
self.add_pin("vdd","POWER")
self.add_pin("gnd","GROUND")
self.create_modules()
self.calculate_module_offsets()

View File

@ -82,7 +82,7 @@ class control_logic(design.design):
# Some cells may have pwell/nwell spacing problems too when the wells are different heights.
#self.cell_gap = max(self.m2_pitch,drc["pwell_to_nwell"])
self.input_list =["web","csb","oeb"]
self.input_list =["csb","web","oeb"]
self.input_width = len(self.input_list)*self.m2_pitch
self.input_bar_list = ["clk_buf_bar", "we", "cs", "oe"]
self.input_bar_width = len(self.input_bar_list)*self.m2_pitch
@ -602,7 +602,7 @@ class control_logic(design.design):
rows_end = self.width
well_width = drc["minwidth_well"]
for i in range(7):
for i in range(8):
if i%2:
name = "vdd"
well_type = "nwell"
@ -648,6 +648,24 @@ class control_logic(design.design):
start=left,
end=right)
for vdd_pin in self.rbl_inst.get_pins("vdd"):
if vdd_pin.layer != "metal2":
continue
self.add_layout_pin(text="vdd",
layer="metal2",
offset=vdd_pin.ll(),
height=vdd_pin.height(),
width=vdd_pin.width())
for gnd_pin in self.rbl_inst.get_pins("gnd"):
if gnd_pin.layer != "metal2":
continue
self.add_layout_pin(text="gnd",
layer="metal2",
offset=gnd_pin.ll(),
height=gnd_pin.height(),
width=gnd_pin.width())
def add_lvs_correspondence_points(self):
""" This adds some points for easier debugging if LVS goes wrong.

View File

@ -33,6 +33,9 @@ class hierarchical_decoder(design.design):
(self.no_of_pre2x4,self.no_of_pre3x8)=self.determine_predecodes(self.num_inputs)
self.create_layout()
self.offset_all_coordinates()
self.DRC_LVS()
def create_layout(self):
@ -155,8 +158,8 @@ class hierarchical_decoder(design.design):
self.row_decoder_height = self.inv.height * self.rows
# Calculates height and width of hierarchical decoder
self.height = self.predecoder_height + self.row_decoder_height
self.width = self.predecoder_width + self.routing_width
self.height = self.row_decoder_height
self.width = self.predecoder_width + self.row_decoder_width
def create_pre_decoder(self):
""" Creates pre-decoder and places labels input address [A] """
@ -171,12 +174,10 @@ class hierarchical_decoder(design.design):
""" Add a 2x4 predecoder """
if (self.num_inputs == 2):
base = vector(self.routing_width,0)
mirror = "RO"
base = vector(-self.pre2_4.width,0)
index_off1 = index_off2 = 0
else:
base= vector(self.routing_width+self.pre2_4.width, num * self.pre2_4.height)
mirror = "MY"
base= vector(-self.pre2_4.width, num * self.pre2_4.height)
index_off1 = num * 2
index_off2 = num * 4
@ -189,8 +190,7 @@ class hierarchical_decoder(design.design):
self.pre2x4_inst.append(self.add_inst(name="pre[{0}]".format(num),
mod=self.pre2_4,
offset=base,
mirror=mirror))
offset=base))
self.connect_inst(pins)
self.add_pre2x4_pins(num)
@ -215,12 +215,11 @@ class hierarchical_decoder(design.design):
def add_pre3x8(self,num):
""" Add 3x8 numbered predecoder """
if (self.num_inputs == 3):
offset = vector(self.routing_width,0)
offset = vector(-self.pre_3_8.width,0)
mirror ="R0"
else:
height = self.no_of_pre2x4*self.pre2_4.height + num*self.pre3_8.height
offset = vector(self.routing_width+self.pre3_8.width, height)
mirror="MY"
offset = vector(-self.pre3_8.width, height)
# If we had 2x4 predecodes, those are used as the lower
# decode output bits
@ -236,8 +235,7 @@ class hierarchical_decoder(design.design):
self.pre3x8_inst.append(self.add_inst(name="pre3x8[{0}]".format(num),
mod=self.pre3_8,
offset=offset,
mirror=mirror))
offset=offset))
self.connect_inst(pins)
# The 3x8 predecoders will be stacked, so use yoffset
@ -305,11 +303,11 @@ class hierarchical_decoder(design.design):
for row in range(self.rows):
name = "DEC_NAND[{0}]".format(row)
if ((row % 2) == 0):
y_off = self.predecoder_height + nand_mod.height*row
y_off = nand_mod.height*row
y_dir = 1
mirror = "R0"
else:
y_off = self.predecoder_height + nand_mod.height*(row + 1)
y_off = nand_mod.height*(row + 1)
y_dir = -1
mirror = "MX"
@ -342,7 +340,7 @@ class hierarchical_decoder(design.design):
inv_row_height = self.inv.height * (row + 1)
mirror = "MX"
y_dir = -1
y_off = self.predecoder_height + inv_row_height
y_off = inv_row_height
offset = vector(x_off,y_off)
self.inv_inst.append(self.add_inst(name=name,
@ -408,7 +406,7 @@ class hierarchical_decoder(design.design):
index = pre_num * 4 + i
out_name = "out[{}]".format(i)
pin = self.pre2x4_inst[pre_num].get_pin(out_name)
self.connect_rail(index, pin)
self.connect_rail_m3(index, pin)
for pre_num in range(self.no_of_pre3x8):
@ -416,7 +414,7 @@ class hierarchical_decoder(design.design):
index = pre_num * 8 + i + self.no_of_pre2x4 * 4
out_name = "out[{}]".format(i)
pin = self.pre3x8_inst[pre_num].get_pin(out_name)
self.connect_rail(index, pin)
self.connect_rail_m3(index, pin)
@ -448,11 +446,11 @@ class hierarchical_decoder(design.design):
def route_vdd_gnd(self):
""" Add a pin for each row of vdd/gnd which are must-connects next level up. """
for num in range(0,self.total_number_of_predecoder_outputs + self.rows):
for num in range(0,self.rows):
# this will result in duplicate polygons for rails, but who cares
# use the inverter offset even though it will be the nand's too
(gate_offset, y_dir) = self.get_gate_offset(0, self.inv.height, num)
(gate_offset, y_dir) = self.get_gate_offset(-self.predecoder_width, self.inv.height, num)
# route vdd
vdd_offset = gate_offset + self.inv.get_pin("vdd").ll().scale(1,y_dir)
self.add_layout_pin(text="vdd",
@ -479,6 +477,19 @@ class hierarchical_decoder(design.design):
rotate=90)
def connect_rail_m3(self, rail_index, pin):
""" Connect the routing rail to the given metal1 pin """
mid_point = vector(pin.cx(), pin.cy()-self.inv.height/2)
rail_pos = vector(self.rail_x_offsets[rail_index],mid_point.y)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=pin.center(),
rotate=90)
self.add_wire(("metal3","via2","metal2"), [rail_pos, mid_point, pin.uc()])
self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=rail_pos,
rotate=90)
def analytical_delay(self, slew, load = 0.0):
# A -> out
if self.determine_predecodes(self.num_inputs)[1]==0:

View File

@ -49,9 +49,8 @@ class hierarchical_predecode(design.design):
debug.error("Invalid number of predecode inputs.",-1)
def setup_constraints(self):
# we are going to use horizontal vias, so use the via height
# use a conservative douple spacing just to get rid of annoying via DRCs
self.m2_pitch = contact.m1m2.height + 2*self.m2_space
self.m2_pitch = max(contact.m2m3.width,contact.m2m3.height) + max(self.m2_space,self.m3_space)
# The rail offsets are indexed by the label
self.rails = {}
@ -203,11 +202,12 @@ class hierarchical_predecode(design.design):
mid2_pos = vector(0.5*(zr_pos.x+al_pos.x), al_pos.y)
self.add_path("metal1", [zr_pos, mid1_pos, mid2_pos, al_pos])
z_pos = self.inv_inst[num].get_pin("Z").rc()
self.add_layout_pin_center_segment(text="out[{}]".format(num),
z_pin = self.inv_inst[num].get_pin("Z")
self.add_layout_pin(text="out[{}]".format(num),
layer="metal1",
start=z_pos,
end=z_pos + vector(self.inv.width - self.inv.get_pin("Z").rx(),0))
offset=z_pin.ll(),
height=z_pin.height(),
width=z_pin.width())
def route_input_inverters(self):

View File

@ -48,9 +48,6 @@ class sram(design.design):
design.design.__init__(self, name)
# For different layer width vias
self.m2m3_offset_fix = vector(0,0.5*(self.m3_width-self.m2_width))
# M1/M2 routing pitch is based on contacted pitch of the biggest layer
self.m1_pitch = max(contact.m1m2.width,contact.m1m2.height) + max(self.m1_space,self.m2_space)
self.m2_pitch = max(contact.m2m3.width,contact.m2m3.height) + max(self.m2_space,self.m3_space)
@ -149,7 +146,7 @@ class sram(design.design):
self.control_logic_inputs=self.control_logic.get_inputs()
self.control_logic_outputs=self.control_logic.get_outputs()
self.add_pin_list(self.control_logic_inputs + ["clk"],"INPUT")
self.add_pin_list(self.control_logic_inputs,"INPUT")
self.add_pin("vdd","POWER")
self.add_pin("gnd","GROUND")
@ -388,7 +385,8 @@ class sram(design.design):
offset=self.bank_sel_bus_offset,
names=self.bank_sel_bus_names,
length=self.vertical_bus_height,
vertical=True))
vertical=True,
make_pins=True))
# Horizontal data bus
@ -797,7 +795,7 @@ class sram(design.design):
# Create the address and control flops (but not the clk)
dff_size = self.addr_size + len(self.control_logic.get_inputs())-1
self.addr_ctrl_dff = self.mod_dff_array(rows=dff_size, columns=1)
self.addr_ctrl_dff = self.mod_dff_array(name="dff_array", rows=dff_size, columns=1)
self.add_mod(self.addr_ctrl_dff)
# Create the bank module (up to four are instantiated)
@ -853,7 +851,7 @@ class sram(design.design):
for i in range(self.word_size):
temp.append("DATA[{0}]".format(i))
for i in range(self.bank_addr_size):
temp.append("ADDR[{0}]".format(i))
temp.append("A[{0}]".format(i))
if(self.num_banks > 1):
temp.append("bank_sel[{0}]".format(bank_num))
temp.extend(["s_en", "w_en", "tri_en_bar", "tri_en",
@ -924,10 +922,17 @@ class sram(design.design):
def add_control_logic(self, position):
""" Add and place control logic """
inputs = []
for i in self.control_logic_inputs:
if i != "clk":
inputs.append(i+"_s")
else:
inputs.append(i)
self.control_logic_inst=self.add_inst(name="control",
mod=self.control_logic,
offset=position)
self.connect_inst(self.control_logic_inputs + self.control_logic_outputs + ["vdd", "gnd"])
self.connect_inst(inputs + self.control_logic_outputs + ["vdd", "gnd"])
def add_lvs_correspondence_points(self):
@ -986,10 +991,9 @@ class sram(design.design):
for (old,new) in zip(ctrl_flops,["CSb","WEb","OEb"]):
self.copy_layout_pin(self.addr_ctrl_dff_inst, old, new)
self.copy_layout_pin(self.control_logic_inst, "clk")
self.copy_layout_pin(self.bank_inst, "vdd")
self.copy_layout_pin(self.bank_inst, "gnd")
self.copy_layout_pin(self.addr_ctrl_dff_inst, "clk")
# Power ring contains the power pins
def add_two_banks(self):
# Placement of bank 0 (left)
@ -1030,8 +1034,8 @@ class sram(design.design):
in_pos = src_pin.rc()
out_pos = vector(dest_pin.cx(), in_pos.y)
self.add_wire(("metal3","via2","metal2"),[in_pos, out_pos, out_pos - vector(0,self.m2_pitch)])
self.add_via(layers=("metal2","via2","metal3"),
offset=src_pin.lr() - self.m2m3_offset_fix,
self.add_via_center(layers=("metal2","via2","metal3"),
offset=src_pin.rc(),
rotate=90)
def connect_rail_from_left_m2m1(self, src_pin, dest_pin):
@ -1042,34 +1046,41 @@ class sram(design.design):
def route_single_bank(self):
""" Route a single bank SRAM """
# Route the outputs from the control logic module
for n in self.control_logic_outputs:
src_pin = self.control_logic_inst.get_pin(n)
dest_pin = self.bank_inst.get_pin(n)
self.connect_rail_from_left_m2m3(src_pin, dest_pin)
self.add_via_center(layers=("metal1","via1","metal2"),
offset=src_pin.rc(),
rotate=90)
# Expand the ring around the bank
# Expand the ring around the bank to include flops and control logic
bbox_lr = vector(self.control_logic_inst.lx(), self.bank_inst.by() + 2*self.supply_rail_pitch)
bbox_ur = self.bank_inst.ur() - vector(2*self.supply_rail_pitch, 2*self.supply_rail_pitch)
self.add_power_ring([bbox_lr, bbox_ur])
self.route_single_bank_vdd()
self.route_single_bank_gnd()
# Connect the output of the flops to the bank pins
for i in range(self.addr_size):
flop_name = "dout[{}]".format(i)
bank_name = "A[{}]".format(i)
flop_pin = self.addr_ctrl_dff_inst.get_pin(flop_name)
bank_pin = self.bank_inst.get_pin(bank_name)
flop_pos = flop_pin.center()
bank_pos = bank_pin.lc()
mid_x_pos = 0.5*(flop_pos.x + bank_pos.x)
mid_pos = vector(mid_x_pos - i*self.m2_pitch, flop_pos.y)
self.add_wire(("metal1","via1","metal2"),[flop_pos, mid_pos, bank_pos])
# There should be M1 in the flop already, but just in case
self.add_via_center(layers=("metal1","via1","metal2"),
bank_pos = vector(bank_pin.cx(),flop_pos.y)
self.add_path("metal3",[flop_pos, bank_pos])
self.add_via_center(layers=("metal2","via2","metal3"),
offset=flop_pos,
rotate=90)
self.add_via_center(layers=("metal2","via2","metal3"),
offset=bank_pos,
rotate=90)
# Connect the output of the flops to the control pins
for i in range(3):
flop_name = "dout[{}]".format(self.addr_size+i)
ctrl_name = ["csb","web","oeb"][i]
@ -1083,6 +1094,19 @@ class sram(design.design):
offset=flop_pos,
rotate=90)
# Connect the clock between the flops and control module
# FIXME: Buffered clock should drive the flops, but then
# it would change the setup time...
flop_pin = self.addr_ctrl_dff_inst.get_pin("clk")
ctrl_pin = self.control_logic_inst.get_pin("clk")
flop_pos = flop_pin.uc()
ctrl_pos = ctrl_pin.bc()
mid_ypos = 0.5*(ctrl_pos.y+flop_pos.y)
mid1_pos = vector(flop_pos.x, mid_ypos)
mid2_pos = vector(ctrl_pos.x, mid_ypos)
self.add_wire(("metal1","via1","metal2"),[flop_pin.uc(), mid1_pos, mid2_pos, ctrl_pin.bc()])
def route_single_bank_vdd(self):
""" Route vdd for the control and dff array """
@ -1100,6 +1124,17 @@ class sram(design.design):
size = (1,3),
rotate=90)
# Route the vdd rails to the TOP
for vdd_pin in self.control_logic_inst.get_pins("vdd"):
if vdd_pin.layer != "metal2":
continue
vdd_pos = vdd_pin.uc()
top_rail_pos = vector(vdd_pos.x, self.top_vdd_y_center)
self.add_path("metal2", [top_rail_pos, vdd_pos])
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=top_rail_pos,
size = (1,3))
def route_single_bank_gnd(self):
""" Route gnd for the control and dff array """
@ -1118,6 +1153,16 @@ class sram(design.design):
size = (1,3),
rotate=90)
# Route the vdd rails to the TOP
for gnd_pin in self.control_logic_inst.get_pins("gnd"):
if gnd_pin.layer != "metal2":
continue
gnd_pos = gnd_pin.uc()
top_rail_pos = vector(gnd_pos.x, self.top_gnd_y_center)
self.add_path("metal2", [top_rail_pos, gnd_pos])
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=top_rail_pos,
size = (1,3))
def sp_write(self, sp_name):

View File

@ -135,6 +135,7 @@ def write_netgen_script(cell_name, sp_name):
# This circuit has symmetries and needs to be flattened to resolve them or the banks won't pass
# Is there a more elegant way to add this when needed?
f.write("flatten class {{{0}.spice precharge_array}}\n".format(cell_name))
f.write("flatten class {{{0}.spice dff_array}}\n".format(cell_name))
f.write("property {{nfet {0}.spice}} remove as ad ps pd\n".format(cell_name))
f.write("property {{pfet {0}.spice}} remove as ad ps pd\n".format(cell_name))
f.write("property {{n {0}}} remove as ad ps pd\n".format(sp_name))

View File

@ -85,8 +85,10 @@ drc["layer_map"]=os.environ.get("OPENRAM_TECH")+"/freepdk45/layers.map"
drc["minwidth_tx"]=0.09
drc["minlength_channel"] = 0.05
# WELL.1 Minimum spacing of nwell/pwell at different potential
# WELL.2 Minimum spacing of nwell/pwell at different potential
drc["pwell_to_nwell"] = 0.225
# WELL.3 Minimum spacing of nwell/pwell at the same potential
drc["well_to_well"] = 0.135
# WELL.4 Minimum width of nwell/pwell
drc["minwidth_well"] = 0.2

View File

@ -70,10 +70,13 @@ drc["layer_map"]=os.environ.get("OPENRAM_TECH")+"/scn3me_subm/layers.map"
drc["minwidth_tx"] = 1.2
drc["minlength_channel"] = 0.6
# 1.3 Minimum spacing between wells of same type (if both are drawn)
drc["well_to_well"] = 1.8
# 1.4 Minimum spacing between wells of different type (if both are drawn)
drc["pwell_to_nwell"] = 0
# 1.1 Minimum width
drc["minwidth_well"] = 3.6
# 3.1 Minimum width
drc["minwidth_poly"] = 0.6
# 3.2 Minimum spacing over active