Pin Constraints File (PCF)¶
A pin constraints file (.pcf) maps the top-level ports of your user design to
physical I/O BELs on the fabric, letting you pin an ordinary design without
hand-instantiating any I/O BEL.
This is the recommended alternative to the manual
absolute-placement approach, where every I/O is wired up
by instantiating the I/O BEL (e.g. IO_1_bidirectional_frame_config_pass)
inside a top_wrapper and constraining each one with a (* BEL=... *)
attribute. With a PCF you keep your design’s port list clean and describe the
pin assignment separately.
How it works¶
The PCF flow has two parts, one in synthesis and one in place-and-route:
Synthesis (
-iopad): Passing-iopadto thesynth_fabulousYosys pass runs I/O pad mapping, which inserts an I/O buffer cell ($nextpnr_ibuf/$nextpnr_obuf/$nextpnr_iobuf) on every top-level port. Without this step there are no I/O cells for nextpnr to constrain, and the PCF has nothing to bind to.Place-and-route (
-o pcf=...): nextpnr reads the PCF, and for eachset_ioline it finds the I/O cell connected to the named port and locks it to the requested I/O BEL.
Because the port list stays intact, you set nextpnr’s top module to your design
itself (via -top), not to a wrapper.
Note
-iopad is the simplest way to get I/O cells and is what this flow uses. Some
tile libraries (for example the primitives in
fabulous-tiles) instead
provide explicit IBUF/OBUF/TBUF/IOBUF primitives that you map in with
-extra-plib and -extra-map; the fabrics in
fabulous-fabrics drive their
designs this way. Either approach produces the PAD-connected I/O cells that the
same set_io PCF then constrains — the PCF syntax below is identical in both.
PCF syntax¶
Each non-blank, non-comment line is a single set_io constraint:
set_io <port> X<col>Y<row>/<bel>
Field |
Meaning |
|---|---|
|
A top-level port of your design. Bus bits are indexed, e.g. |
|
The tile coordinate of the target I/O BEL (column |
|
The BEL letter that selects one BEL within that tile — see below. |
What the BEL letter means¶
A tile usually holds more than one BEL — an I/O tile, for instance, exposes
several physical I/O. The letter picks which one. BELs are lettered A, B,
C, … in the order they are declared in the tile, so A is the first BEL in
the tile, B the second, and so on. This letter is the BEL’s Z position (its
slot index inside the tile); together with the tile’s X/Y coordinate it
uniquely identifies one BEL on the whole fabric.
You never have to guess the letter — it is column 4 of each bel.txt row, or
the third field of each BelBegin line in bel.v2.txt (see
below). For example these two bel.txt rows
X0Y1,X0,Y1,A,IO_1_bidirectional_frame_config_pass,A_I,A_T,A_O,A_Q
X0Y1,X0,Y1,B,IO_1_bidirectional_frame_config_pass,B_I,B_T,B_O,B_Q
describe two I/O BELs in tile X0Y1: the first at slot A, the second at slot
B. So X0Y1/A and X0Y1/B are the two locations you can pin a port to in that
tile, and the A/B in the examples on this page are simply “the first I/O in
the tile” and “the second I/O in the tile”.
Lines beginning with # are comments and blank lines are ignored. Constrain one
port (or one bus bit) per line:
# clock and control
set_io clk X0Y1/A
set_io rst X0Y1/B
set_io ena X0Y2/A
# 4-bit input bus
set_io a[0] X0Y3/A
set_io a[1] X0Y3/B
set_io a[2] X0Y4/A
set_io a[3] X0Y4/B
Note
The location uses a slash and a bare tile coordinate (X0Y1/A), matching the
BEL names nextpnr builds internally. This differs from the port names that
appear inside the fabric netlist (Tile_X0Y1_A_...); do not use the
Tile_...-prefixed form in a PCF.
Finding valid I/O locations¶
Only tiles that contain an I/O BEL can be targeted. After the fabric flow has
run, two files in <project-dir>/.FABulous/ list them:
bel.txt— every BEL in the fabric, one CSV row per BEL. I/O BELs are the ones whose primitive name isIO_1_bidirectional_frame_config_pass,InPass4_frame_config,OutPass4_frame_config, or their_muxvariants. Each row gives the tile coordinate and BEL letter you need, for example:X0Y1,X0,Y1,A,IO_1_bidirectional_frame_config_pass,A_I,A_T,A_O,A_Q X0Y1,X0,Y1,B,IO_1_bidirectional_frame_config_pass,B_I,B_T,B_O,B_Q
gives you
X0Y1/AandX0Y1/B.Note
The letter is the slot name only for
IO_1_bidirectional_frame_config_passBELs.InPass4_frame_config/OutPass4_frame_configBELs (the BRAM-adjacent data I/O) are addressed by their port prefix instead of the letter — a row such asX9Y1,X9,Y1,A,InPass4_frame_config_mux,RAM2FAB_D0_O0,...is the locationX9Y1/RAM2FAB_D0, notX9Y1/A.bel.v2.txt— the same BELs in the newer structured format, one multi-lineBelBegin … BelEndblock per BEL. The tile coordinate and BEL letter are the second and third fields of theBelBeginline, so the two BELs above appear as:BelBegin,X0Y1,A,IO_1_bidirectional_frame_config_pass,A_ BelBegin,X0Y1,B,IO_1_bidirectional_frame_config_pass,B_
also giving
X0Y1/AandX0Y1/B.
Running the flow¶
Drive the full synthesis -> place-and-route -> bitstream flow with
compile_design, enabling -iopad in synthesis and pointing nextpnr at your
PCF. From the FABulous shell, with the fabric already loaded:
FABulous> compile_design user_design/counter.v -top counter \
--synth-extra-args=-iopad \
--nextpnr-extra-args "-o pcf=user_design/counter.pcf"
Argument |
Purpose |
|---|---|
|
The design source(s) to compile. |
|
Use the design module as the top (no wrapper). |
|
Insert I/O buffers so the PCF has cells to bind. |
|
Forward the PCF to nextpnr’s |
The result is the usual bitstream (counter.bin) next to the design, ready to
configure the fabric.
Worked example¶
A small design with a clock, reset, enable, a 4-bit input a, and a 4-bit
output b. Note the clock is still instantiated as a blackbox — a PCF pins
regular I/O, not the dedicated clock network.
user_design/counter.v:
module counter (input clk, input rst, input ena, input [3:0] a, output [3:0] b);
wire clk_int;
(* keep *) Global_Clock inst_clk (.CLK(clk_int));
reg [3:0] b_r;
always @(posedge clk_int)
if (rst) b_r <= 4'd0;
else if (ena) b_r <= b_r + a;
assign b = b_r;
endmodule
user_design/counter.pcf:
set_io rst X0Y1/A
set_io ena X0Y1/B
set_io a[0] X0Y3/A
set_io a[1] X0Y3/B
set_io a[2] X0Y4/A
set_io a[3] X0Y4/B
set_io b[0] X0Y5/A
set_io b[1] X0Y5/B
set_io b[2] X0Y6/A
set_io b[3] X0Y6/B
Compile it:
FABulous> compile_design user_design/counter.v -top counter \
--synth-extra-args=-iopad \
--nextpnr-extra-args "-o pcf=user_design/counter.pcf"
Troubleshooting¶
No IO cell found connected to '<port>' via PAD port ... Was iopadmap run in Yosys?
: nextpnr found the port but no I/O buffer to constrain. Add
--synth-extra-args=-iopad so synthesis inserts the I/O pads.
Can only constrain IO cells
: The name on the left of set_io is a top-level port, but it is not an I/O
buffer — check the port name and that -iopad ran.
Cannot find a pin named '<location>'
: The X<col>Y<row>/<bel> location does not exist or is not an I/O BEL. Confirm
it against bel.txt / bel.v2.txt, and use the slash form X0Y1/A rather
than Tile_X0Y1.A.