Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions edg/abstract_parts/test_kicad_import_netlist.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,6 @@ def test_netlist(self) -> None:
"Sensor_Temperature:MCP9700AT-ETT",
"MCP9700AT-ETT",
["dut", "U1"],
["dut", "U1"],
[
"edg.electronics_model.test_kicad_import_blackbox.KiCadBlackboxBlock",
"edg.electronics_model.KiCadSchematicBlock.KiCadBlackbox",
Expand All @@ -119,7 +118,6 @@ def test_netlist(self) -> None:
"Graphic:SYM_ESD_Small",
"SYM_ESD_Small",
["dut", "SYM1"],
["dut", "SYM1"],
[
"edg.electronics_model.test_kicad_import_blackbox.KiCadBlackboxBlock",
"edg.electronics_model.KiCadSchematicBlock.KiCadBlackbox",
Expand All @@ -134,7 +132,6 @@ def test_netlist(self) -> None:
"",
"",
["dut", "res"],
["dut", "res"],
[
"edg.electronics_model.test_kicad_import_blackbox.KiCadBlackboxBlock",
"edg.abstract_parts.test_kicad_import_netlist.DummyResistor",
Expand Down
4 changes: 1 addition & 3 deletions edg/electronics_model/NetlistBackend.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,7 @@ def run(self, design: CompiledDesign, args: Dict[str, str] = {}) -> List[Tuple[e
if set(args.keys()) - {"RefdesMode"} != set():
raise ValueError("Invalid argument found in args")
refdes_mode_arg = args.get("RefdesMode", "refdesPathNameValue")
if refdes_mode_arg == "pathName":
refdes_mode = kicad.RefdesMode.Pathname
elif refdes_mode_arg == "refdes":
if refdes_mode_arg == "refdes":
refdes_mode = kicad.RefdesMode.Conventional
elif refdes_mode_arg == "refdesPathNameValue":
refdes_mode = kicad.RefdesMode.PathnameAsValue
Expand Down
74 changes: 42 additions & 32 deletions edg/electronics_model/NetlistGenerator.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@ class NetBlock(NamedTuple):
part: str
value: str # gets written directly to footprint
full_path: TransformUtil.Path # full path to this footprint
path: List[str] # short path to this footprint
class_path: List[edgir.LibraryPath] # classes on short path to this footprint
path_classes: List[edgir.LibraryPath] # all classes on the full path, index-aligned with full_path


class NetPin(NamedTuple):
Expand Down Expand Up @@ -82,8 +81,6 @@ def flatten_port(path: TransformUtil.Path, port: edgir.PortLike) -> Iterable[Tra
def __init__(self, design: CompiledDesign):
self.all_scopes = [BoardScope.empty(TransformUtil.Path.empty())] # list of unique scopes
self.scopes: Scopes = {TransformUtil.Path.empty(): self.all_scopes[0]}

self.short_paths: Dict[TransformUtil.Path, List[str]] = {TransformUtil.Path.empty(): []} # seed root
self.class_paths: ClassPaths = {TransformUtil.Path.empty(): []} # seed root

self.design = design
Expand All @@ -103,33 +100,12 @@ def process_blocklike(
for link_pair in block.links: # links considered to be the same scope as self
self.scopes[path.append_link(link_pair.name)] = scope

# generate short paths for children first, for Blocks only
main_internal_blocks: Dict[str, edgir.BlockLike] = {}
other_internal_blocks: Dict[str, edgir.BlockLike] = {}

for block_pair in block.blocks:
subblock = block_pair.value
# ignore pseudoblocks like bridges and adapters that have no internals
if not subblock.hierarchy.blocks and "fp_is_footprint" not in subblock.hierarchy.meta.members.node:
other_internal_blocks[block_pair.name] = block_pair.value
else:
main_internal_blocks[block_pair.name] = block_pair.value

short_path = self.short_paths[path]
class_path = self.class_paths[path]
for block_pair in block.blocks:
self.class_paths[path.append_block(block_pair.name)] = class_path + [
block_pair.value.hierarchy.self_class
]

if len(main_internal_blocks) == 1 and short_path: # never shorten top-level blocks
name = list(main_internal_blocks.keys())[0]
self.short_paths[path.append_block(name)] = short_path
self.class_paths[path.append_block(name)] = class_path
else:
for name, subblock in main_internal_blocks.items():
self.short_paths[path.append_block(name)] = short_path + [name]
self.class_paths[path.append_block(name)] = class_path + [subblock.hierarchy.self_class]

for name, subblock in other_internal_blocks.items():
self.short_paths[path.append_block(name)] = short_path + [name]
self.class_paths[path.append_block(name)] = class_path + [subblock.hierarchy.self_class]
elif isinstance(block, (edgir.Link, edgir.LinkArray)):
for link_pair in block.links:
self.scopes[path.append_link(link_pair.name)] = scope
Expand Down Expand Up @@ -185,9 +161,7 @@ def process_blocklike(
part_comps = [part, f"({mfr})" if mfr else ""]
part_str = " ".join(filter(None, part_comps))
value_str = value if value else (part if part else "")
scope.footprints[path] = NetBlock(
footprint_name, refdes, part_str, value_str, path, self.short_paths[path], self.class_paths[path]
)
scope.footprints[path] = NetBlock(footprint_name, refdes, part_str, value_str, path, self.class_paths[path])

for pin_spec in footprint_pinning:
assert isinstance(pin_spec, str)
Expand Down Expand Up @@ -370,3 +344,39 @@ def run(self) -> Netlist:
self.transform_design(self.design.design)

return self.scope_to_netlist(self.all_scopes[0]) # TODO support multiple scopes


class PathShortener:
"""Given a bunch of blocks with full paths, determine path shortenings that eliminate
path components that are the only footprint-containing internal block."""

def __init__(self, blocks: List[NetBlock]) -> None:
# construct list of children for each path
# note since this is created from a list of footprints, all paths are guaranteed to contain footprints
self._block_children: Dict[Tuple[str, ...], List[str]] = {}
for block in blocks:
block_path = block.full_path.blocks
for i in range(len(block_path)):
parent_path = block_path[:i]
path_component = block_path[i]
parent_list = self._block_children.setdefault(parent_path, [])
if path_component not in parent_list:
parent_list.append(path_component)

def shorten(
self, path: TransformUtil.Path, classes: List[edgir.LibraryPath]
) -> Tuple[List[str], List[edgir.LibraryPath]]:
assert len(path.blocks) == len(classes)
new_blocks = []
new_classes = []
block_path = path.blocks
for i, path_comp in enumerate(block_path):
# test whether to add component i
# always keep rootmost component
full_parent_path = tuple(block_path[:i])
if i > 0 and len(self._block_children.get(full_parent_path, [])) <= 1: # only one child, so can shorten
continue
else:
new_blocks.append(path_comp)
new_classes.append(classes[i])
return new_blocks, new_classes
46 changes: 20 additions & 26 deletions edg/electronics_model/footprint.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@
from enum import Enum, auto
from typing import List

from .NetlistGenerator import Netlist, NetBlock, Net
from .NetlistGenerator import Netlist, NetBlock, Net, PathShortener
from .. import edgir


class RefdesMode(Enum):
PathnameAsValue = auto() # conventional refdes w/ pathname as value, except for TPs
Conventional = auto() # conventional refdes only, value passed through
Pathname = auto() # pathname as refdes, value passed through


###############################################################################################################################################################################################
Expand All @@ -26,23 +25,16 @@ def gen_header() -> str:
"""2. Generating Blocks"""


def block_name(block: NetBlock, refdes_mode: RefdesMode) -> str:
if refdes_mode == RefdesMode.Pathname:
return ".".join(block.path)
else:
return block.refdes # default is conventional refdes


def gen_block_comp(block_name: str) -> str:
return f'(comp (ref "{block_name}")'


def gen_block_value(block: NetBlock, refdes_mode: RefdesMode) -> str:
def gen_block_value(block: NetBlock, short_path: List[str], refdes_mode: RefdesMode) -> str:
if refdes_mode == RefdesMode.PathnameAsValue:
if "TP" in block.refdes: # test points keep their value
return f'(value "{block.value}")'
else:
pathname = ".".join(block.path)
pathname = ".".join(short_path)
return f'(value "{pathname}")'
else:
return f'(value "{block.value}")'
Expand Down Expand Up @@ -83,18 +75,18 @@ def gen_block_prop_sheetfile(block_path: List[edgir.LibraryPath]) -> str:
return f'(property (name "Sheetfile") (value "{value}"))'


def gen_block_prop_edg(block: NetBlock) -> str:
def gen_block_prop_edg(block: NetBlock, short_path: List[str]) -> str:
return (
f'(property (name "edg_path") (value "{".".join(block.full_path.to_tuple())}"))\n'
+ f' (property (name "edg_short_path") (value "{".".join(block.path)}"))\n'
+ f' (property (name "edg_short_path") (value "{".".join(short_path)}"))\n'
+ f' (property (name "edg_refdes") (value "{block.refdes}"))\n'
+ f' (property (name "edg_part") (value "{block.part}"))\n'
+ f' (property (name "edg_value") (value "{block.value}"))'
)


def block_exp(blocks: List[NetBlock], refdes_mode: RefdesMode) -> str:
"""Given a dictionary of block_names (strings) as keys and Blocks (namedtuples) as corresponding values
def block_exp(blocks: List[NetBlock], shortener: PathShortener, refdes_mode: RefdesMode) -> str:
"""Generate the blocks section of the netlist from a list of blocks.

Example:
(components
Expand All @@ -109,30 +101,31 @@ def block_exp(blocks: List[NetBlock], refdes_mode: RefdesMode) -> str:
"""
result = "(components"
for block in blocks:
short_path, short_class = shortener.shorten(block.full_path, block.path_classes)
result += (
"\n"
+ gen_block_comp(block_name(block, refdes_mode))
+ gen_block_comp(block.refdes)
+ "\n"
+ " "
+ gen_block_value(block, refdes_mode)
+ gen_block_value(block, short_path, refdes_mode)
+ "\n"
+ " "
+ gen_block_footprint(block.footprint)
+ "\n"
+ " "
+ gen_block_prop_sheetname(block.path)
+ gen_block_prop_sheetname(short_path)
+ "\n"
+ " "
+ gen_block_prop_sheetfile(block.class_path)
+ gen_block_prop_sheetfile(short_class)
+ "\n"
+ " "
+ gen_block_prop_edg(block)
+ gen_block_prop_edg(block, short_path)
+ "\n"
+ " "
+ gen_block_sheetpath(block.path[:-1])
+ gen_block_sheetpath(short_path[:-1])
+ "\n"
+ " "
+ gen_block_tstamp(block.path)
+ gen_block_tstamp(short_path)
)
return result + ")"

Expand All @@ -150,7 +143,7 @@ def gen_net_pin(block_name: str, pin_name: str) -> str:
return "(node (ref {}) (pin {}))".format(block_name, pin_name)


def net_exp(nets: List[Net], blocks: List[NetBlock], refdes_mode: RefdesMode) -> str:
def net_exp(nets: List[Net], blocks: List[NetBlock]) -> str:
"""Given a dictionary of net names (strings) as keys and a list of connected Pins (namedtuples) as corresponding values

Example:
Expand All @@ -170,7 +163,7 @@ def net_exp(nets: List[Net], blocks: List[NetBlock], refdes_mode: RefdesMode) ->
for i, net in enumerate(nets):
result += "\n" + gen_net_header(i + 1, net.name)
for pin in net.pins:
result += "\n " + gen_net_pin(block_name(block_dict[pin.block_path], refdes_mode), pin.pin_name)
result += "\n " + gen_net_pin(block_dict[pin.block_path].refdes, pin.pin_name)
result += ")"
return result + ")"

Expand All @@ -181,12 +174,13 @@ def net_exp(nets: List[Net], blocks: List[NetBlock], refdes_mode: RefdesMode) ->


def generate_netlist(netlist: Netlist, refdes_mode: RefdesMode) -> str:
shortener = PathShortener(list(netlist.blocks))
return (
gen_header()
+ "\n"
+ block_exp(netlist.blocks, refdes_mode)
+ block_exp(netlist.blocks, shortener, refdes_mode)
+ "\n"
+ net_exp(netlist.nets, netlist.blocks, refdes_mode)
+ net_exp(netlist.nets, netlist.blocks)
+ "\n"
+ ")"
)
8 changes: 0 additions & 8 deletions edg/electronics_model/test_bundle_netlist.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,6 @@ def test_spi_netlist(self) -> None:
"",
"WeirdSpiController",
["controller"],
["controller"],
["edg.electronics_model.test_bundle_netlist.TestFakeSpiController"],
),
net.blocks,
Expand All @@ -221,7 +220,6 @@ def test_spi_netlist(self) -> None:
"",
"WeirdSpiPeripheral",
["peripheral1"],
["peripheral1"],
["edg.electronics_model.test_bundle_netlist.TestFakeSpiPeripheral"],
),
net.blocks,
Expand All @@ -233,7 +231,6 @@ def test_spi_netlist(self) -> None:
"",
"WeirdSpiPeripheral",
["peripheral2"],
["peripheral2"],
["edg.electronics_model.test_bundle_netlist.TestFakeSpiPeripheral"],
),
net.blocks,
Expand Down Expand Up @@ -271,7 +268,6 @@ def test_uart_netlist(self) -> None:
"",
"1k",
["a"],
["a"],
["edg.electronics_model.test_bundle_netlist.TestFakeUartBlock"],
),
net.blocks,
Expand All @@ -283,7 +279,6 @@ def test_uart_netlist(self) -> None:
"",
"1k",
["b"],
["b"],
["edg.electronics_model.test_bundle_netlist.TestFakeUartBlock"],
),
net.blocks,
Expand Down Expand Up @@ -323,7 +318,6 @@ def test_can_netlist(self) -> None:
"",
"120",
["node1"],
["node1"],
["edg.electronics_model.test_bundle_netlist.TestFakeCanBlock"],
),
net.blocks,
Expand All @@ -335,7 +329,6 @@ def test_can_netlist(self) -> None:
"",
"120",
["node2"],
["node2"],
["edg.electronics_model.test_bundle_netlist.TestFakeCanBlock"],
),
net.blocks,
Expand All @@ -347,7 +340,6 @@ def test_can_netlist(self) -> None:
"",
"120",
["node3"],
["node3"],
["edg.electronics_model.test_bundle_netlist.TestFakeCanBlock"],
),
net.blocks,
Expand Down
7 changes: 4 additions & 3 deletions edg/electronics_model/test_multipack_netlist.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,6 @@ def test_packed_netlist(self) -> None:
"",
"1uF",
["source"],
["source"],
["edg.electronics_model.test_netlist.TestFakeSource"],
),
net.blocks,
Expand All @@ -135,8 +134,10 @@ def test_packed_netlist(self) -> None:
"",
"1k",
["sink", "device"],
["sink"],
["edg.electronics_model.test_multipack_netlist.TestPackedSink"],
[
"edg.electronics_model.test_multipack_netlist.TestPackedSink",
"edg.electronics_model.test_netlist.TestFakeSink",
],
),
net.blocks,
)
Expand Down
Loading
Loading