21
\$\begingroup\$

With software, when we compile the project for first time it may take a while but afterwards, it does not take so long anymore. If we change a single file in the project, everything does not need to be compiled again.

This does not seem to hold true for compiling FPGA designs.

Now I have used Intel Quartus Prime Standard with MAX 10 and Cyclone IV & V and Microsemi Libero SoC with IGLOO2 and SmartFusion2. With both programs and all the FPGAs I mentioned, it always takes the same amount of time for the project to compile. I want to know, why? Am I doing something wrong? It should take less time to compile after the first time isn't it? How should this be fixed?

\$\endgroup\$
3
  • 8
    \$\begingroup\$ There are plenty of compilers that don't support incremental compilation and/or independent compilation. \$\endgroup\$ Sep 10 '20 at 7:42
  • \$\begingroup\$ Try compiling a large program that consists of about 200 include files, rather than separately compiled procedures. How long does it take to recompile if you change one include file? \$\endgroup\$
    – Hot Licks
    Sep 13 '20 at 0:59
  • \$\begingroup\$ I have not had to write really massive C or C++ programs. \$\endgroup\$ Sep 13 '20 at 23:54
37
\$\begingroup\$

With software ... if we change a single file in the project, everything does not need to be compiled again.

Only if your compilation creates intermediate files to avoid recompiling unchanged files.

FPGAs ... It should take less time to compile after the first time isn't it?

In an FPGA compile, unless you are using incremental compilation (which is a complex endeavour), then it will have to resynthesise everything all the time.

Remember that an FPGA design is not software. You are describing a physical circuit. Any changes to any part of that circuit can have knock-on effects on other parts of the circuit, due to the way the synthesis tools optimise. Furthermore the tools do not and in most cases cannot "compile" each file in isolation, because the files are typically descriptions of small parts of the overall circuit.

When the design is fit into the FPGA, unless you lock down certain parts of it (post-fit netlists and incremental compilation), the fitter will start from scratch each time to try and find the most optimal way of fitting the design. Usually the compile time is heavily impacted by timing constraints as the fitter tries to minimise routing delays to meet your required clock frequencies.

\$\endgroup\$
4
  • 1
    \$\begingroup\$ Isn't incremental compilation enabled by default? The strange thing is that even synthsis takes the same amount of time. Atleast synthesis should take less time after the first time since only the files changed need to be resynthesized. How does one use this incremental compilation feature? Why is incremental compilation not enabled by default? \$\endgroup\$ Sep 9 '20 at 20:28
  • \$\begingroup\$ @Quantum0xE7 incremental comp requires partitioning of your design into sections, which are then set as post-fit or post-synthesis netlists. Those partitions with valid netlists for which no portion of the design has changed will skip synthesis or fitting. Essentially a design partition is analagous to a compilation unit for compiled software. \$\endgroup\$ Sep 9 '20 at 20:46
  • \$\begingroup\$ Are you referring to use of logic lock regions in the design? The term logic lock region is used in Quartus I think. \$\endgroup\$ Sep 9 '20 at 21:04
  • \$\begingroup\$ @Quantum0xE7 not logiclock, no. Design partitions \$\endgroup\$ Sep 9 '20 at 21:24
20
\$\begingroup\$

This is because of how the FPGA toolchain works. There are several main steps: synthesis, mapping, placement, and routing. Synthesis is the only one that is similar to compilation. Synthesis is also the only step that is easiest to parallelize (each module can be synthesized independently of the other modules) and as a result it is usually only a small part of the overall time it takes to run the tools. You can kind-of, sort-of compare mapping, placement, and routing to linking as these tasks work on the complete design. However, linking is basically a trivial operation that mainly involves reading in all of the object code from the compiler, fixing up a few addresses, and writing out the executable file. Every time you re-run your software tools, only a few files might need to be recompiled, but the whole output file will need to be re-linked. But this is fast because the linker isn't doing anything particularly complicated.

Mapping, placement, and routing, on the other hand, are highly computationally intensive, cannot be easily parallelized, and they are also highly non-trivial to do incrementally. Mapping takes a 'high-level' netlist from synthesis (AND gates, OR gates, muxes, flip-flops, adders, etc.) and converts it to device primitives (LUTs, flip-flops, carry-chains, etc.). Placement then decides where to place each primitive on the device, and routing figures out how to route all of the connections using the interconnect network. And all of this is timing-driven - the tools attempt to map, place, and route all of the logic such that all of the timing constraints are met. It is a vastly more complex problem than simply converting a bunch of C into a bunch of assembly.

Doing this incrementally means keeping track of what your change at the HDL level results in in the final routed design. This is highly non-trivial to do, and pretty much impossible to do well. The tools basically need to compare the old synthesized netlist with the new one, propagate the changes down, and hope that things will work out in the end and the timing constraints can be met.

I have some personal experience with incremental build going wrong. Recently, I had to delete and recreate an Intel FPGA project when it refused to re-place a particular register after changing some timing constraints, as the incremental build thought that since the logic that generated that register did not change, the register didn't have to be moved. This was after wasting several hours trying various combinations of obscure directives to try to get the tools to cooperate before I realized what the problem actually was.

\$\endgroup\$
16
\$\begingroup\$

Because in software, there is a linking phase. The linker takes all objects files (resulting from the compilation of each source files), and only alter the function calls so that everything works together. The linker has a rather simple job, and does not have to go through the process of generating code from the source, and optimizing everything (which is very resource-intensive). Because that has already been done by the compiler, independently, for each source file.

So, if you change one source file, you just need to recompile this specific source file and redo the entire linking, but the compilation of the other source files isn't necessary.

For hardware synthesis, there is no linking phase. The entire design has to be optimized as a whole. If it wasn't done this way, designs would be very suboptimal. All modules need to be fully known for the compiler efficiently organize the design in the available LUTs and meet the timing requirements. Have you ever noticed that, sometimes, if you change just a few lines of code in your HDL, the resulting design is completely different? Everything impacts everything. If a LUT is moved, timings may change, and it has to be compensated by moving other things, sometimes in areas that seem totally unrelated. So, even if your design is organized in multiple source files, the synthesis tool has to go through everything each time.

In software, the compilation units can be made much more independant, and it is much easier for the toolchain to avoid recompiling parts that haven't been affected by changes in the source.

\$\endgroup\$
4
  • 7
    \$\begingroup\$ You should mention that, while it's not the linkers' job to optimize - we do have LTO (Link-Time Optimization). See GCC LTO. MSVC also has this. This can take a considerable amount of time. You'll know when you compile Chromium :') It's even noticeable on smaller projects. \$\endgroup\$
    – Gizmo
    Sep 10 '20 at 9:34
  • \$\begingroup\$ Even in the days when linkers were just linkers, they were often very slow. I'd guess it's because they were use the same approaches to link programs that were small enough to be linked in memory at the same time as the linker, as they would use to link much larger programs, and thus have to read every input file while writing a file containing all the symbols, then sort that file, and re-read every input file while combining it with the symbol table to produce the final output. \$\endgroup\$
    – supercat
    Sep 10 '20 at 22:17
  • \$\begingroup\$ I wonder how hard or how limiting it would be for a toolchain to provide an option to partition a chip into blocks, each containing a certain subset of the chip's resources, and then allow synthesis to be performed on each block as though it were an entire project, and when synthesizing the whole chip fill in the blocks with their pre-synthesized circuits (so the whole-chip-synthesis step would mainly involve routing the interconnects between the blocks and the outside world). \$\endgroup\$
    – supercat
    Sep 11 '20 at 17:27
  • \$\begingroup\$ @supercat many toolchains do provide this feature. Any tools that can do partial reconfiguration theoretically have the ability to do that. Vivado can also do out-of-context synthesis runs for IP cores, but the output of that gets placed and routed along with the rest of the design. I'm pretty sure you can partition/floorplan a design for place and route as well, although I have never tried that myself. \$\endgroup\$ Sep 16 '20 at 22:58
8
\$\begingroup\$

NOTE (not explicit in other answers): FPGA build tools and software build tools both have to fit the end-product into a space. For software it's a logical address space, and the spatial relationship of one subroutine to another is completely irrelevant to how they interact. But, for an FPGA, it's a physical space. And the spatial relationships beteween different components of the design matters very much.

\$\endgroup\$
1
  • 2
    \$\begingroup\$ ...and a 3D space too. Like a game of 3D chess with a billion+ squares. \$\endgroup\$
    – tim
    Sep 10 '20 at 17:30
6
\$\begingroup\$

I have used incremental FPGA compile in the distant past without any design partition, but IIRC it failed every 1 in 2 or 3 attempts and I ended up doing a full compile anyway.

However, if you have a large design and you want to cut the overall compile time, there are some tricks that you could try. They are not trivial, and not quick to implement, so you will only gain in the long term. You could turn some of your sub-blocks into pre-packaged IP and fix the relative placement, so the sub-block is placed as a single item.

Apart from a high overhead in terms of the initial work, as other have already noted, this could adversely impact the overall efficiency of the placement as you cannot optimise across the full design.

\$\endgroup\$
1
  • 3
    \$\begingroup\$ I can see why this technique exists in documents but I have not see someone use it enthusiastically \$\endgroup\$ Sep 10 '20 at 14:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.