Welcome to FP2P’s documentation!
Overview
FP2P (FPGA Port To Pin) is a tool for robust port to pin assignment in multi-board FPGA designs. Fig. 1 shows an example of a multi-board FPGA design.

Example of a multi-board FPGA design. This setup is a development setup and needs assignment for 42 differential signals. The final design needs assignment for 210 differential signals.
The problem has been known for a long time and is especially annoying in data acquisition systems, where hundreds of signals are routed via multiple boards. However, there is still no generic user-friendly open-source solution (or at least none has been found). The implementation has two main goals, safety (check as many potential human mistakes as possible) and reusability (reuse connections mappings, defined in files, in multiple designs). It is fully declarative and programming language-agnostic from the users perspective. Currently 2 target EDA tools are supported Vivado and Quartus. Adding support for another target EDA tool is very easy, as the analysis and resolving are completely decoupled from the constraint file generation.
If you want to jump straight to the examples check example and tests.
Status
The project is considered as finished. Only bug fixes and minor improvements not breaking backward compatibility will be accepted.
License
The project is licensed under the GPL-2.0 License.
Citation
If you find fp2p useful, and write any academic publication on a project utilizing fp2p please consider citing.
@ARTICLE{mkru_fp2p_ieee,
author={Kruszewski, Michał and Zabołotny, Wojciech Marek},
journal={IEEE Transactions on Nuclear Science},
title={Safe and Reusable Approach for Pin-to-Port Assignment in Multiboard FPGA Data Acquisition and Control Designs},
year={2021},
volume={68},
number={6},
pages={1186-1193},
doi={10.1109/TNS.2021.3074530}
}
Glossary
assignment file
See Assignment file.
logical mapping
Mapping done in software or firmware (within FPGA logic).
mapping file
See Mapping file .
mistake
Fault introduced by a human (mistake != error).
setup
Set of physical, connected PCBs.
tree file
See Tree file.
Installation and usage
Installation
fp2p is not available on the PyPI website. Why? fp2p is pure program, contained within single file, there is no library part, it can’t be imported. This will never change. What is more, I didn’t want to litter PyPI with a program that is relatively rarely used (although being very useful). It is not like new boards are designed or manufactured once per week or month.
User has full freedom in terms of installation. For example, you can do one of the followings:
Copy
fp2p.py
file to the repository with the FPGA project design.Add fp2p repository as a submodule to the repository with the FPGA project design.
Clone the repository somewhere in the filesystem and call the program providing path
python ~/your/path/fp2p/fp2p.py ...
.Copy
fp2p.py
file to some directory included in thePATH
. You may want to add shebang to the file, for example#!/bin/python
andchmod +x fp2p.py
.
Usage
fp2p supports 3 subcommands:
assign
- assings ports to pins,graph
- resolves a mapping tree and prints graph,resolve
- resolves a mapping tree and prints the result.
After preparing proper mapping tree files or assignment files user only needs to call fp2p
with one of the subcommands and proper order of arguments.
This is further explained in the section Subcommands.
You can also always run python fp2p.py -h
or python fp2p.py {subcommand} -h
.
Windows
The program has not been tested on Windows OS, and no one has so far reported that it works corretly. However, it should work, as no OS specific mechanisms or system calls are used.
File types
The whole fp2p concept is based on 3 files types:
mapping file - one or more per setup,
tree file - one per setup,
assignment file - one per setup.
These three types share the same format and syntax, however their semantics is different. YAML has been chosen as the file format. Another user-friendly alternative was JSON, which is provided in the Python Standard Library. However, YAML has been chosen as it is less verbose and allows placing comments in the files, what might be useful in case of documenting any peculiarities in the mappings.
Mapping file
The mapping file is used for defining physical connections between pins within the board, cables or connectors.
A[1-3]:
end: s[1-3]
regex:
A4:
end: end_pin_4
terminal:
B1[2-3]:
end: s_diff_pin_[pn]
regex:
The mapping file is a dictionary, where the keys are the names of an input ends and values are dictionaries.
So, mapping file is a dictionary of dictionaries (single outer dictionary, one or more inner dictionaries).
The inner dictionary has one mandatory key, namely end
, that denotes the name of the output end.
Within the inner dictionary, some metadata about particular mapping can be placed. There are two optional special keys regex
and terminal
, that do not need any value to be correctly interpreted (can be solely the key).
Tree file
The tree file is used for defining the structure of the setup (how boards, connectors and cables are connected).
name: board_1
files:
- b_1.yaml
nodes:
- name: board_2_connector_1
files:
- b_2_c_1.yaml
- name: board_2_connector_2
files:
- b_2_c_2_f_1.yaml
- b_2_c_2_f_2.yaml
Tree file consists of nodes, each node is a dictionary.
Single node has 2 mandatory keys name
, files
and one optional key nodes
.
The name
of the node must be unique and serves as a scope for pin names within this node.
It is necessary in order to avoid pin name collisions.
Otherwise, name collisions could potentially occur in two scenarios:
same pin names in two different mapping files,
same mapping file used more than once in the tree file.
The files
is a list of mapping files that constitute a given node.
Each tree node can consist of multiple mapping files.
Thanks to this, it is possible to define mappings for different connectors located on the same board in separate files or split connector mappings into multiple files to group them by functionality.
The nodes
is a list of children nodes connected with a given node.
Assignment file
The assignment file is used for defining port assignments to the terminal pins.
_default_:
set_property:
IOSTANDARD: LVDS_33
DIFF_TERM: "TRUE"
board_2_connector_1:
port[1]:
end: end_pin_1
set_property:
IOSTANDARD: LVDS_25
board_2_connector_2:
port[2]:
end: end_pin_2
set_property:
DIFF_TERM: "FALSE"
port[3]:
end: end_pin_3
# Differential pair example
diff_[pn]:
node: board_2_connector_2
end: end_diff_pin_[pn]
regex:
board_1:
port[4]:
end: end_pin_4
end: s3
The assignment file can also be seen as a dictionary of dictionaries.
Within the outer dictionary, single item is a dictionary defining assignments within the particular node (except the _default_
key, see _default_).
The key is the name of the node, and the value is a dictionary.
Within the inner dictionaries, single item is an assignment.
The key is the name of the port.
The destination pin name is placed under the end
key.
Special attributes
Special attributes are optional keys, that ease the work and reduce verbosity.
_default_
Both mapping and assignment file types have one reserved key _default_
.
All information, set within value of the default key, is automatically applied to all remaining items in the file.
Setting the same option within given entry overwrites the default option.
In other words, everything defined within given entry has higher precedence than what is copied from the default.
Anything can be placed in the default section.
If it has no meaning, it will be simply ignored.
In case of the assignment file, there can be single _default_ section per file and single _default_ section per node. Valuse set in the per node section take precedence. Values set within the _default_ sections can always be overwritten within the particular items.
_default_
attribute example._default_:
set_property:
IOSTANDARD: LVDS_33
DIFF_TERM: "TRUE"
prefix: foo_
suffix: _suffix
board_1:
_default_:
prefix: prefix_
A:
end: pin_A
B:
end: pin_B
set_property:
DIFF_TERM: "FALSE"
prefix: ""
C:
end: pin_C
set_property:
IOSTANDARD: LVDS_25
suffix: ""
terminal
The terminal
key functions like a type annotation.
By default end pins are not intended to be connected to the FPGA ports.
To indicate, that the end pin must be connected to the port, a special property called terminal must be added to this end pin.
What is more, nothing more can be mapped to the pin marked as terminal.
To some extent, this mechanism is similar in its nature to the mutability aspect of the Rust programming language and provides the basics for extensive mistakes detection.
Terminal pins can be defined in each node of the tree, they are not limited only to the leaf nodes.
regex
The regex
key is used to reduce verbosity.
It enables regex expanding (sre_yield) for the given mapping.
After expansion, natural (human) sorting (natsort) is applied to both names so that they can be mapped in a deterministic way.
In case of any mismatch of the lengths of generated lists the error is immediately reported.
regex
attribute example.A[1-3]:
end: s[1-3]
regex:
# Above is equivalent to the following:
A1:
end: s1
A2:
end: s2
A3:
end: s3
prefix
If prefix
is set, its value is prepended to the name of each key denoting port name or pin name.
prefix
attribute example in a mapping file._default_:
prefix: foo_
A1:
end: s1
A2:
end: s2
# Above is equivalent to the following:
foo_A1:
end: s1
foo_A2:
end: s2
suffix
If suffix
is set, its value is appended to the name of each key denoting port name or pin name.
end_prefix
Same as prefix
, but applied to the value under the end
key.
Not yet implemented as there was no need for it so far.
end_suffix
Same as suffix
, but applied to the value under the end
key.
Not yet implemented as there was no need for it so far.
set_property
The set_property
section is used for setting FPGA pin properties.
This attribute makes sense only for the assignment file and Vivado EDA tool.
When setting property, key is the name of the property and value is the value of the property.
See also TRUE and FALSE in design constraint properties, as it may save you some time.
Properties are not checked by the fp2p tool in any way. They are simply forwarded to the auto generated constraint file, and later checked by the EDA tool.
set_instance_assignment
The set_instance_assignment
section is used for setting FPGA pin properties.
This attribute makes sense only for the assignment file and Quartus EDA tool.
Subcommands
resolve
The resolve
subcommand resolves mapping tree and prints it to the standard output.
It takes only single positional argument, namely the tree file to resolve.
The resolve
subcommand is useful for debugging and in initial design phases to quickly check what is connected with what.
resolve
subcommand call.python fp2p.py resolve path/to/your/tree_file.yml
resolve
subcommand output.{'board_1': {'end_pin_4': {'pin': 'A4', 'terminal': None}},
'board_2_connector_1': {'end_pin_1': {'pin': 'A1', 'terminal': None},
'led_0': {'pin': 'C2'}},
'board_2_connector_2': {'end_diff_pin_n': {'pin': 'B12', 'terminal': None},
'end_diff_pin_p': {'pin': 'B13', 'terminal': None},
'end_pin_2': {'pin': 'A2', 'terminal': None},
'end_pin_3': {'pin': 'A3', 'terminal': None}}}
graph
The graph
subcommand renders the graph for tree file.
It has been added to ease debugging of relatively long tree files.
After exceeding a certain number of nodes in the tree file it becomes a bit hard to see how nodes relate with each other and what files constitute particular nodes.
With graph it becomes trivial.
graph
subcommand call.python fp2p.py graph path/to/your/tree_file.yml
Fig. 2 shows an example output graph. Bold text within the node is the node name. Below the node name there is a list of files constituting given node.

Example output graph.
assign
The assign
subcommand is used for generating design constraint file.
This is the core reason why the fp2p tool has been implemented.
assign
subcommand call.python fp2p.py assign tree.yaml assignment.yaml vivado
# This file has been auto generated by the fp2p tool.
# Do not modify it by hand!
# Files used for generation:
# Tree file: tree.yaml
# Assignment file: assignment.yaml
# More information on the website https://github.com/m-kru/fp2p.
set_property PACKAGE_PIN A1 [get_ports {port[1]}]
set_property IOSTANDARD LVDS_25 [get_ports {port[1]}]
set_property DIFF_TERM TRUE [get_ports {port[1]}]
set_property PACKAGE_PIN A2 [get_ports {port[2]}]
set_property IOSTANDARD LVDS_33 [get_ports {port[2]}]
set_property DIFF_TERM FALSE [get_ports {port[2]}]
set_property PACKAGE_PIN A3 [get_ports {port[3]}]
set_property IOSTANDARD LVDS_33 [get_ports {port[3]}]
set_property DIFF_TERM TRUE [get_ports {port[3]}]
set_property PACKAGE_PIN B12 [get_ports {diff_n}]
set_property IOSTANDARD LVDS_33 [get_ports {diff_n}]
set_property DIFF_TERM TRUE [get_ports {diff_n}]
set_property PACKAGE_PIN B13 [get_ports {diff_p}]
set_property IOSTANDARD LVDS_33 [get_ports {diff_p}]
set_property DIFF_TERM TRUE [get_ports {diff_p}]
set_property PACKAGE_PIN A4 [get_ports {port[4]}]
set_property IOSTANDARD LVDS_33 [get_ports {port[4]}]
set_property DIFF_TERM TRUE [get_ports {port[4]}]
Mistakes detection
Safety and support in finding mistakes were among the core goals of the implementation. The idea of the program is to report mistakes as soon as possible and exit with verbose mistake description. It does not gather mistakes to report them all at the end, but reports them immediately at a given check point. The application is capable of detecting and reporting the following mistakes:
Unassigned terminal pins - terminal pins not assigned to any ports.
Dangling terminal pins - terminal pins not mapped to any FPGA pins.
Assigning to non-terminal pins.
Conflicting mappings in each tree node. It detects such conflicts even if a given tree node is defined in multiple files.
Conflicting assignments - the same port assigned twice.
Assigning to missing pins.
Mapping to terminal pins.
Duplicated node names - at least 2 nodes with the same name.
Tips and quirks
TRUE and FALSE in design constraint properties
By default 'TRUE'
, 'true'
, 'True'
are interpreted by YAML as boolean and then printed to the file as 'True'
string.
In .xdc file we want 'TRUE'
string so double qoutes "
are needed.
Not sure which EDA tools can handle correctly 'True'
or 'true'
strings, but Xilinx Vivado can’t.
The same applies to the "FALSE"
.
_default_:
set_property:
IOSTANDARD: LVDS_33
# Use "TRUE" or "FALSE" strings for boolean properties.
DIFF_TERM: "TRUE"
Net names on schematics
It is always preferred to put numbers at the end of net names in the board schematics files. This is because in case of HDL, the array indexes are always at the end. For example, following declaration:
_default_:
regex:
foo_[pn]_\[[0-4]\]:
end: bar_[0-4]_[pn]
leads to improper assignment as order in natural sorting is different than what user expects.
This is because [0-4]
is before [pn]
in bar_[0-4]_[pn]
.
Instead user needs to write it in a much more verbose way:
_default_:
regex:
foo_[pn]_\[0\]:
end: bar_0_[pn]
foo_[pn]_\[1\]:
end: bar_1_[pn]
foo_[pn]_\[2\]:
end: bar_2_[pn]
foo_[pn]_\[3\]:
end: bar_3_[pn]
foo_[pn]_\[4\]:
end: bar_4_[pn]
Such verbosity could be avoided if the nets on the schematic were named in the following way bar_[pn]_[0-4]
instead of bar_[0-4]_[pn]
. Then following declaration
_default_:
regex:
foo_[pn]_\[[0-4]\]:
end: bar_[pn]_[0-4]
would work as user expects.