"""Contains functions for processing BEL related information from HDL files."""
from pathlib import Path
from FABulous.custom_exception import (
FabricParsingError,
InvalidBelDefinition,
)
from FABulous.fabric_definition.Bel import Bel
from FABulous.fabric_definition.define import IO, FABulousAttribute
from FABulous.fabric_definition.Yosys_obj import YosysJson, YosysModule
[docs]
def belMapProcessing(module_info: YosysModule) -> dict:
"""Extract and transform BEL mapping attributes in `YosysModule`.
Parameters
----------
module_info : dict
A dictionary containing the module's attributes, including
potential BEL mapping information.
Returns
-------
dic
Dictionary containing the parsed bel mapping information.
"""
belMapDic = {}
# if BelMap not present defaults belMapDic to {}
if "BelMap" not in module_info.attributes:
return belMapDic
# Passed attributes that dont need appending. (May need refining.)
exclude_attributes = {
"BelMap",
"FABulous",
"dynports",
"cells_not_processed",
"src",
"top",
}
# match case for INIT. (may need modifying for other naming conventions.)
for key, _value in module_info.attributes.items():
if key in exclude_attributes:
continue
match key:
case key if key.startswith("INIT_") and key[5:].isdigit():
index = key[5:]
new_key = f"INIT[{index}]"
case "INIT":
new_key = "INIT"
case key if key.isupper() and "_" not in key:
new_key = key
case _:
new_key = key
belMapDic[new_key] = {0: {0: "1"}}
# yosys reverses belmap, reverse back to keep original belmap.
return dict(reversed(list(belMapDic.items())))
[docs]
def parseBelFile(
filename: Path,
belPrefix: str = "",
) -> Bel:
"""Parse a Verilog or VHDL BEL file and return related information of the BEL.
The function will also parse and record all the FABulous attributes
which all start with:
(* FABulous, <type>, ... *)
The <type> can be one the following:
* **BelMap**
* **EXTERNAL**
* **SHARED_PORT**
* **GLOBAL**
* **CONFIG_PORT**
* **SHARED_ENABLE**
* **SHARED_RESET**
The **BelMap** attribute will specify the bel mapping for the bel.
This attribute should be placed before the start of the module.
The bel mapping is then used for generating the bitstream specification.
Each of the entry in the attribute will have the following format::
<name> = <value>
``<name>`` is the name of the feature and ``<value>`` will be the
bit position of the feature. ie. ``INIT=0`` will specify that the feature
``INIT`` is located at bit 0.
Since a single feature can be mapped to multiple bits, this is currently done by
specifying multiple entries for the same feature.
This will be changed in the future.
The bit specification is done in the following way::
INIT_a_1=1, INIT_a_2=2, ...
The name of the feature will be converted to ``INIT_a[1]``, ``INIT_a[2]``
for the above example. This is necessary
because Verilog does not allow square brackets as part of the attribute name.
**EXTERNAL** attribute will notify FABulous to put the pin in the top module during
the fabric generation.
**SHARED_PORT** attribute will notify FABulous this the pin is shared between
multiple bels. Attribute need to go with
the **EXTERNAL** attribute.
**GLOBAL** attribute will notify FABulous to stop parsing any pin after this
attribute.
**CONFIG_PORT** attribute will notify FABulous the port is for configuration.
Example
-------
Verilog
::
(* FABulous, BelMap,
single_bit_feature=0, //single bit feature, single_bit_feature=0
multiple_bits_0=1, //multiple bit feature bit0, multiple_bits[0]=1
multiple_bits_1=2 //multiple bit feature bit1, multiple_bits[1]=2
*)
module exampleModule (
externalPin,
normalPin1,
normalPin2,
sharedPin,
globalPin);
(* FABulous, EXTERNAL *) input externalPin;
input normalPin;
(* FABulous, EXTERNAL, SHARED_PORT *) input sharedPin;
(* FABulous, GLOBAL) input globalPin;
output normalPin2; //do not get parsed
...
Parameters
----------
filename : str
The filename of the bel file.
belPrefix : str, optional)
The bel prefix provided by the CSV file. Defaults to "".
Returns
-------
Tuple containing
- List of Bel Internal ports (belName, IO).
- List of Bel External ports (belName, IO).
- List of Bel Config ports (belName, IO).
- List of Bel Shared ports (belName, IO).
- Number of configuration bits in the bel.
- Whether the bel has UserCLK.
- Bel config bit mapping as a dict {port_name: bit_number}.
Raises
------
ValueError
File not found
ValueError
No permission to access the file
ValueError
Bel file contains no or more than one module
"""
internal: list[tuple[str, IO]] = []
external: list[tuple[str, IO]] = []
config: list[tuple[str, IO]] = []
shared: list[tuple[str, IO]] = []
localSharedPorts: dict[str, tuple[str, IO]] = {}
belMapDic = {}
carry: dict[str, dict[IO, str]] = {}
carryPrefix: str = ""
userClk = False
noConfigBits = 0
belMapDic = {}
ports_vectors: dict[str, dict[str, tuple[IO, int]]] = {}
# define port types
ports_vectors["internal"] = {}
ports_vectors["external"] = {}
ports_vectors["config"] = {}
ports_vectors["shared"] = {}
yosys_json = YosysJson(filename)
filtered_ports: dict[str, tuple[IO, list]] = {}
if len(yosys_json.modules) == 0:
raise FabricParsingError(f"File {filename} does not contain any modules.")
# Gathers port name and direction, filters out configbits as they show in ports.
# modules should only contain one module
module_name, module_info = yosys_json.getTopModule()
for port_name, details in module_info.ports.items():
if "ConfigBits" in port_name:
continue
if "UserCLK" in port_name:
userClk = True
direction = IO[details.direction.upper()]
filtered_ports[port_name] = (direction, details.bits)
configBitsPort = module_info.ports.get("ConfigBits")
noConfigBits = 0
if configBitsPort:
noConfigBits = len(configBitsPort.bits)
# Passed attributes dont show in port list, checks for attributes in netnames.
# (If passed attributes missing, may need to expand to check other lists
# e.g "memories".)
for portName, (direction, bits) in filtered_ports.items():
attributes = module_info.netnames[portName].attributes
# Unrolled Ports
for index in range(len(bits)):
new_port_name = (
f"{portName}{index}" if len(bits) > 1 else portName
) # Multi-bit ports get index
if (
FABulousAttribute.EXTERNAL in attributes
and FABulousAttribute.SHARED_PORT not in attributes
):
external.append((f"{belPrefix}{new_port_name}", direction))
elif FABulousAttribute.CONFIG_BIT in attributes:
config.append((f"{belPrefix}{new_port_name}", direction))
elif FABulousAttribute.SHARED_PORT in attributes:
shared.append((new_port_name, direction))
else:
internal.append((f"{belPrefix}{new_port_name}", direction))
if "SHARED_ENABLE" in attributes or "SHARED_RESET" in attributes:
if direction is not IO["INPUT"]:
raise InvalidBelDefinition(
"SHARED_ENABLE or SHARED_RESET can only be used with "
"INPUT ports."
)
if "SHARED_ENABLE" in attributes:
localSharedPorts["ENABLE"] = (
f"{belPrefix}{new_port_name}",
direction,
)
elif "SHARED_RESET" in attributes:
localSharedPorts["RESET"] = (
f"{belPrefix}{new_port_name}",
direction,
)
if "CARRY" in attributes:
# For prefix after carry
carryPrefix = attributes.get("CARRY")
if carryPrefix == 1:
# Default carry prefix, yosys uses 1 if no value is specified
carryPrefix = "FABulous_default"
if direction is IO["INOUT"]:
raise ValueError(
f"CARRY can't be used with INOUT ports for port "
f"{new_port_name}!"
)
if carryPrefix not in carry:
carry[carryPrefix] = {}
if direction not in carry[carryPrefix]:
carry[carryPrefix][direction] = f"{belPrefix}{new_port_name}"
else:
raise ValueError(
f"Port {portName} with prefix {carryPrefix} can't be a "
f"carry {direction}, since port "
f"{carry[carryPrefix][direction]} already is!"
)
# Port vectors:
if "EXTERNAL" in attributes and "SHARED_PORT" not in attributes:
ports_vectors["external"][portName] = (direction, len(bits))
elif "CONFIG" in attributes:
ports_vectors["config"][portName] = (direction, len(bits))
elif "SHARED_PORT" in attributes:
ports_vectors["shared"][portName] = (direction, len(bits))
else:
ports_vectors["internal"][portName] = (direction, len(bits))
belMapDic = belMapProcessing(module_info)
if len(belMapDic) != noConfigBits:
raise ValueError(
f"NoConfigBits does not match with the BEL map in file {filename}, "
f"length of BelMap is {len(belMapDic)}, but with {noConfigBits} "
"config bits"
)
return Bel(
src=filename,
prefix=belPrefix,
module_name=module_name,
internal=internal,
external=external,
configPort=config,
sharedPort=shared,
configBit=noConfigBits,
belMap=belMapDic,
userCLK=userClk,
ports_vectors=ports_vectors,
carry=carry,
localShared=localSharedPorts,
)