The Xilinx constraint RLOC - relative placement - can be very helpful to improve the timing of timing-critical logic.

There is some struggle involved with getting the RLOC's recognized by the Xilinx tools if we wish to enter them through the HDL and on inferred logic rather than on instantiated primitives.

The Xilinx Constraints Guide (XCG) is often - and especially for RLOC's - hard to interpret. For example, under "RLOC Propagation Rules", we can read:

RLOC is a design element constraint and any attachment to a net is illegal. When attached to a design element, RLOC propagates to all applicable elements in the hierarchy within the design element."

Don't make the mistake I did: believing this means that you shouldn't add RLOC attributes to signals - rather we will soon see that that's exactly what we need to do.

By any means, getting the RLOC constraints from our HDL into the Xilinx tools is done by VHDL attributes. XCG gives the syntax as:

attribute rloc of {component_name|entity_name|label_name}:
{component|entity|label} is “[element]XmYn[.extension]”;

This is as well very mis-leading. The reason is that this refers to the RLOC attribute syntax for a translated netlist (after synthesis). It doesn't tell us how to write the attributes in our (pre-synthesis) HDL.

What won't work for inferred logic is to try to attach the RLOC attribute to a label (e.g. a label for a process, block, or instantiation):

entity dff is
  port(clk, d: in  std_logic;
       q     : out std_logic);
end dff;

architecture arch of dff is
  attribute rloc: string;
  attribute rloc of dff_inst1: label is "X0Y0";
begin

  -- the RLOC for this one doesn't work:
  dff_inst1: process(clk)
  begin
    if rising_edge(clk) then
      q <= d;
    end if;
  end process;

end arch;

Note that the above won't give any errors or warnings by any of the tools - it's just that the placement won't respect the RLOC we're trying to use.

What we need in the end is to have our RLOC's propagated through the different netlists on to the netlist read by Place-and-Route, which will be a physical netlist of physical primitives (FDCE's etc.). Moreover, we need those attribute to match the names that those primitives are given in the physical netlist.

In order to get there we need to anticipate the name that the synthesis tool will have given our DFF when it comes out of synthesis and then been translated to a physical primitive in MAP. That name has nothing to do with any labels we put on an instance or on a process and that's why the above example doesn't work.

Before continuing, let's see what happens if we instantiate a physical primitive. In this case, the name we give will be retained through both synthesis and MAP and therefore attaching the RLOC attribute to the label of that primitive instantiation works fine:

entity dff is
  port(clk, d: in  std_logic;
       q     : out std_logic);
end dff;

architecture arch of dff is
  attribute rloc: string;
  attribute rloc of dff_inst2: label is "X0Y0";
begin

  -- the RLOC for this one works fine:
  dff_inst2 : FDCE
    generic map (
      INIT => '0')
    port map (
      Q   => q,
      C   => clk,
      CE  => '1',
      CLR => '0',
      D   => d);

end arch;

I give the above example for completeness, that's not what we wanted to do, we wanted to get the RLOC's working for inferred logic (which is the way we describe logic in 90% of the cases).

So, the physical primitive we end up with won't get its name from our process label - it will get its name from the signal we drive. Therefore we do this:

entity dff is
  port(clk, d: in  std_logic;
       q     : out std_logic);
end dff;

architecture arch of dff is
  attribute rloc: string;
  attribute rloc of q: signal is "X0Y0";
begin

  -- the RLOC for this one *will* work:
  dff_inst1: process(clk)
  begin
    if rising_edge(clk) then
      q <= d;
    end if;
  end process;

end arch;

The above will propagate the attribute to the correct physical instance in the physical netlist (the output netlist of MAP, and input of PAR).

It took me quite some trouble-shooting to figure all this out, and the Xilinx manuals weren't to much help. I hope the above can make things easier for others.