f4pga: split_inouts moved from arch-defs (#625)
This commit is contained in:
commit
1f36d814ff
|
@ -23,6 +23,7 @@ from pathlib import Path
|
|||
from f4pga.flows.common import decompose_depname, get_verbosity_level, sub as common_sub
|
||||
from f4pga.flows.module import Module, ModuleContext
|
||||
from f4pga.wrappers.tcl import get_script_path as get_tcl_wrapper_path
|
||||
from f4pga.utils import split_inouts
|
||||
|
||||
|
||||
class SynthModule(Module):
|
||||
|
@ -83,14 +84,7 @@ class SynthModule(Module):
|
|||
)
|
||||
|
||||
yield f"Splitting in/outs..."
|
||||
common_sub(
|
||||
"python3",
|
||||
str(Path(tcl_env["UTILS_PATH"]) / "split_inouts.py"),
|
||||
"-i",
|
||||
ctx.outputs.json,
|
||||
"-o",
|
||||
ctx.outputs.synth_json,
|
||||
)
|
||||
split_inouts(ctx.outputs.json, ctx.outputs.synth_json)
|
||||
|
||||
if not Path(ctx.produces.fasm_extra).is_file():
|
||||
with Path(ctx.produces.fasm_extra).open("w") as wfptr:
|
||||
|
|
|
@ -78,7 +78,6 @@ setuptools_setup(
|
|||
author="F4PGA Authors",
|
||||
description="F4PGA.",
|
||||
url="https://github.com/chipsalliance/f4pga",
|
||||
packages=["f4pga", "f4pga.flows", "f4pga.flows.common_modules", "f4pga.wrappers.sh", "f4pga.wrappers.tcl"],
|
||||
package_dir={"f4pga": "."},
|
||||
package_data={
|
||||
"f4pga.flows": [
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2022 F4PGA Authors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from f4pga.utils.split_inouts import main as split_inouts
|
|
@ -0,0 +1,278 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2019-2022 F4PGA Authors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
"""
|
||||
This script provides a temporary solution for the problem of inout top level
|
||||
port representation of the BLIF format.
|
||||
|
||||
The script loads the design from a JSON file generated by Yosys. Then it splits
|
||||
all inout ports along with their nets and connections to cell ports into two.
|
||||
Suffixes are automatically added to distinguish between the input and the
|
||||
output part.
|
||||
|
||||
The JSON design format used by Yosys is documented there:
|
||||
- http://www.clifford.at/yosys/cmd_write_json.html
|
||||
- http://www.clifford.at/yosys/cmd_read_json.html
|
||||
|
||||
For example in the given design (in verilog):
|
||||
|
||||
module top(
|
||||
input A,
|
||||
output B,
|
||||
inout C
|
||||
);
|
||||
|
||||
IOBUF iobuf (
|
||||
.I(A),
|
||||
.O(B),
|
||||
.IO_$inp(C),
|
||||
.IO_$out(C)
|
||||
);
|
||||
|
||||
endmodule
|
||||
|
||||
the resulting design would be:
|
||||
|
||||
module top(
|
||||
input A,
|
||||
output B,
|
||||
input C_$inp,
|
||||
output C_$out
|
||||
);
|
||||
|
||||
IOBUF iobuf (
|
||||
.I(A),
|
||||
.O(B),
|
||||
.IO_$inp(C_$inp),
|
||||
.IO_$out(C_$out)
|
||||
);
|
||||
|
||||
endmodule
|
||||
|
||||
"""
|
||||
|
||||
|
||||
from pathlib import Path
|
||||
from os.path import splitext
|
||||
import simplejson as json
|
||||
from argparse import ArgumentParser, RawDescriptionHelpFormatter
|
||||
|
||||
|
||||
def find_top_module(design):
|
||||
"""
|
||||
Looks for the top-level module in the design. Returns its name. Throws
|
||||
an exception if none was found.
|
||||
"""
|
||||
for name, module in design["modules"].items():
|
||||
attrs = module["attributes"]
|
||||
if "top" in attrs and int(attrs["top"]) == 1:
|
||||
return name
|
||||
raise RuntimeError("No top-level module found in the design!")
|
||||
|
||||
|
||||
def get_nets(bits):
|
||||
"""
|
||||
Returns a set of numbers corresponding to net indices effectively skipping
|
||||
connections to consts ("0", "1", "x").
|
||||
|
||||
>>> get_nets([0, 1, 2, "0", "1", "x", 3, 4, 5])
|
||||
{0, 1, 2, 3, 4, 5}
|
||||
"""
|
||||
return set([n for n in bits if isinstance(n, int)])
|
||||
|
||||
|
||||
def get_free_net(nets):
|
||||
"""
|
||||
Given a set of used net indices, returns a new, free index.
|
||||
|
||||
>>> get_free_net({0, 1, 2, 4, 5, 6})
|
||||
3
|
||||
>>> get_free_net({0, 1, 2, 3, 4, 5, 6})
|
||||
7
|
||||
"""
|
||||
sorted_nets = sorted(list(nets))
|
||||
|
||||
# Find a gap in the sequence
|
||||
for i in range(len(nets) - 1):
|
||||
n0 = sorted_nets[i]
|
||||
n1 = sorted_nets[i + 1]
|
||||
if n1 != (n0 + 1):
|
||||
return n0 + 1
|
||||
|
||||
# No gap was found, return max + 1.
|
||||
return sorted_nets[-1] + 1
|
||||
|
||||
|
||||
def main(input: str, output: str = None):
|
||||
if output is None:
|
||||
output = splitext(input)[0] + "_out.json"
|
||||
|
||||
with Path(input).open("r") as fp:
|
||||
design = json.load(fp)
|
||||
|
||||
module_name = find_top_module(design)
|
||||
|
||||
# Take a module from the design and split all of its inout ports into pairs of inputs and outputs.
|
||||
# Newly created ports are given suffixed.
|
||||
# For example an inout port named "A" is going to be replaced by a pair consisting of "A_$inp" and "A_$out" ports.
|
||||
|
||||
# The function also looks for "netnames" that correspond to inout ports being split.
|
||||
# These ones are removed and replaced with new ones related to newly added input and output ports.
|
||||
|
||||
# If any other "netname" mentions a net index connected to a former inout port, then the index is removed from the
|
||||
# "netname" (replaced by "x").
|
||||
# If there are only "x" left in the "netname", then it is removed.
|
||||
|
||||
# The function returns port name map and net index map.
|
||||
# The port map is a list of pairs (input name, input/output name).
|
||||
# There are two entries per an inout.
|
||||
# The net map is a dict indexed by indices of nets associated with inout ports.
|
||||
# Each item contains a dict like {"i": int, "o": int} with indices of the inout net split products.
|
||||
|
||||
# Get the module
|
||||
module = design["modules"][module_name]
|
||||
|
||||
# Find indices of all used nets
|
||||
nets = set()
|
||||
for port in module["ports"].values():
|
||||
nets |= get_nets(port["bits"])
|
||||
|
||||
for netname in module["netnames"].values():
|
||||
nets |= get_nets(netname["bits"])
|
||||
|
||||
for cell in module["cells"].values():
|
||||
for connection in cell["connections"].values():
|
||||
nets |= get_nets(connection)
|
||||
|
||||
# Get all inout ports
|
||||
inouts = {k: v for k, v in module["ports"].items() if v["direction"] == "inout"}
|
||||
|
||||
# Split ports
|
||||
new_ports = {}
|
||||
net_map = {}
|
||||
port_map = []
|
||||
for name, port in inouts.items():
|
||||
|
||||
# Remove the inout port from the module
|
||||
del module["ports"][name]
|
||||
nets -= get_nets(port["bits"])
|
||||
|
||||
# Make an input and output port
|
||||
for dir in ["input", "output"]:
|
||||
new_name = name + "_$" + dir[:3]
|
||||
new_port = {"direction": dir, "bits": []}
|
||||
|
||||
print("Mapping port '{}' to '{}'".format(name, new_name))
|
||||
|
||||
for n in port["bits"]:
|
||||
if isinstance(n, int):
|
||||
mapped_n = get_free_net(nets)
|
||||
print("Mapping net {} to {} ({})".format(n, mapped_n, dir))
|
||||
|
||||
if n not in net_map:
|
||||
net_map[n] = {}
|
||||
net_map[n][dir[0]] = mapped_n
|
||||
nets.add(mapped_n)
|
||||
|
||||
new_port["bits"].append(mapped_n)
|
||||
else:
|
||||
new_port["bits"].append(n)
|
||||
|
||||
port_map.append(
|
||||
(
|
||||
name,
|
||||
new_name,
|
||||
)
|
||||
)
|
||||
new_ports[new_name] = new_port
|
||||
|
||||
# Add inputs and outputs
|
||||
module["ports"].update(new_ports)
|
||||
|
||||
netnames = module["netnames"]
|
||||
|
||||
# Remove netnames related to inout ports
|
||||
for name, net in list(netnames.items()):
|
||||
if name in inouts:
|
||||
print(f"Removing netname '{name}'")
|
||||
del netnames[name]
|
||||
|
||||
# Remove remapped nets
|
||||
for name, net in list(netnames.items()):
|
||||
|
||||
# Remove "bits" used by the net that were re-mapped.
|
||||
if len(set(net["bits"]) & set(net_map.keys())):
|
||||
|
||||
# Remove
|
||||
net["bits"] = ["x" if b in net_map else b for b in net["bits"]]
|
||||
|
||||
# If there is nothing left, remove the whole net.
|
||||
if all([b == "x" for b in net["bits"]]):
|
||||
print(f"Removing netname '{name}'")
|
||||
del netnames[name]
|
||||
|
||||
# Add netnames related to new input and output ports
|
||||
for name, port in new_ports.items():
|
||||
netnames[name] = {"hide_name": 0, "bits": port["bits"], "attributes": {}}
|
||||
|
||||
# Remap cell connections that mention inout ports being split.
|
||||
# Loop over all cells and their ports.
|
||||
# If a port contains a connection to an inout net, then the connection is remapped according to the given
|
||||
# net_map.
|
||||
# Only ports which names ends on "_$inp" and "_$out" are affected.
|
||||
|
||||
module = design["modules"][module_name]
|
||||
cells = module["cells"]
|
||||
|
||||
# Process cells
|
||||
for name, cell in cells.items():
|
||||
if "port_directions" not in cell:
|
||||
continue
|
||||
|
||||
port_directions = cell["port_directions"]
|
||||
connections = cell["connections"]
|
||||
|
||||
# Process cell connections
|
||||
for port_name, port_nets in list(connections.items()):
|
||||
|
||||
# Skip if no net of this connection were remapped
|
||||
if len(set(net_map.keys()) & set(port_nets)) == 0:
|
||||
continue
|
||||
|
||||
# Remove connections to the output net from input port and vice
|
||||
# versa.
|
||||
for dir in ["input", "output"]:
|
||||
if port_directions[port_name] == dir and port_name.endswith("$" + dir[:3]):
|
||||
|
||||
for i, n in enumerate(port_nets):
|
||||
if n in net_map:
|
||||
mapped_n = net_map[n][dir[0]]
|
||||
port_nets[i] = mapped_n
|
||||
print("Mapping connection {}.{}[{}] from {} to {}".format(name, port_name, i, n, mapped_n))
|
||||
|
||||
with Path(output).open("w") as fp:
|
||||
json.dump(design, fp, sort_keys=True, indent=2)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = ArgumentParser(description=__doc__, formatter_class=RawDescriptionHelpFormatter)
|
||||
parser.add_argument("-i", required=True, type=str, help="Input JSON")
|
||||
parser.add_argument("-o", default=None, type=str, help="Output JSON")
|
||||
args = parser.parse_args()
|
||||
main(args.i, args.o)
|
|
@ -18,7 +18,6 @@
|
|||
|
||||
set -e
|
||||
|
||||
SPLIT_INOUTS="${F4PGA_SHARE_DIR}"/scripts/split_inouts.py
|
||||
CONVERT_OPTS="${F4PGA_SHARE_DIR}"/scripts/convert_compile_opts.py
|
||||
|
||||
print_usage () {
|
||||
|
@ -177,5 +176,5 @@ if [ ! -z "${YOSYS_COMMANDS}" ]; then
|
|||
fi
|
||||
|
||||
`which yosys` -p "${YOSYS_SCRIPT}" -l $LOG
|
||||
`which python3` ${SPLIT_INOUTS} -i ${OUT_JSON} -o ${SYNTH_JSON}
|
||||
`which python3` -m f4pga.utils.split_inouts -i ${OUT_JSON} -o ${SYNTH_JSON}
|
||||
`which yosys` -p "read_json $SYNTH_JSON; tcl ${CONV_TCL_PATH}"
|
||||
|
|
|
@ -139,5 +139,5 @@ else
|
|||
yosys -p "plugin -i uhdm" -p "read_verilog_with_uhdm ${SURELOG_CMD[*]} ${VERILOG_FILES[*]}" -p "tcl ${SYNTH_TCL_PATH}" -l $LOG
|
||||
fi
|
||||
|
||||
python3 ${UTILS_PATH}/split_inouts.py -i ${OUT_JSON} -o ${SYNTH_JSON}
|
||||
python3 -m f4pga.utils.split_inouts -i ${OUT_JSON} -o ${SYNTH_JSON}
|
||||
yosys -p "read_json $SYNTH_JSON; tcl $(python3 -m f4pga.wrappers.tcl conv)"
|
||||
|
|
|
@ -70,6 +70,8 @@ for PKG in $PACKAGES; do
|
|||
| tar -xJC $F4PGA_INSTALL_DIR_FAM
|
||||
done
|
||||
|
||||
find $F4PGA_INSTALL_DIR_FAM/share -name split_inouts.py | xargs rm -v
|
||||
|
||||
echo '::endgroup::'
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue