24
\$\begingroup\$

I'm a software engineer. As a programmer, I have an understanding of what my compiler does for me because I've manually written a close textual representation of what it outputs (e.g. assembly). For precise details of what's output I can look at the ELF/COFF/MachO specifications (e.g. the file type that it translates my code into, results may vary by language). To figure out what the encodings of instructions are I can look at look my processor's instruction manual. That gives me enough information to understand the kind of data the compiler is outputting. It also gives me the vocabulary to ask questions like "How do I inspect details of an ELF file" and that question is relatively well-formed. I could also ask "How do I look at the assembly generated by my compiler" and that question would be well-formed. Eventually the bytes specified by the ELF file get put into memory, and the processor runs each instruction in order and I understand the semantics of each instruction.

The equivalent steps/questions are entirely unclear to me for an FPGA. I don't know what Verilog or VHDL get translated to. I don't know what the underlying primitives the FPGA operates with are. I don't know how to ask questions like the two well-formed questions above because I just lack the words to ask. It might be the case that equivalent questions make no sense in this context but I have no way to know that at this moment. All I know is that I can write some Verilog and then it winds up being run on a simulator or on an FPGA.

I write some Verilog which is then synthesized to...something? What is that something? Is it a file that I can inspect? Is there a standard format used that I can look up? For instance if I wanted to write a simulator myself, what format would my simulator consume?

After that, the synthesized output is programmed onto an FPGA. What primitives does that FPGA use? If this was an embedded device then generally bytes would be written raw to flash or some kind of storage. Is there an equivalent for FPGAs? Maybe a more abstract and more answerable question would be "what bytes go over the write when an FPGA is being programmed?"

\$\endgroup\$
3
  • 5
    \$\begingroup\$ Was going to write a long answer, but I found this which does a much better of explaining things than I could. Note that this process flow is particular to Xilins FPGAs, but is similar for all major FPGA vendors. The 3 major steps are 1) Design entry (writing the VHDL/Verilog code), 2) Synthesis, and 3) Implementation. The other two steps are functional (logical) and timing simulations. Timing simulation is usually the last step. xilinx.com/support/documentation/sw_manuals/xilinx10/isehelp/… \$\endgroup\$
    – SteveSh
    May 4, 2020 at 23:17
  • 5
    \$\begingroup\$ Generally the actual meaning of an FPGA bitstream is proprietary and secret to the FPGA manufacturer, an engineer just has tools to create such from various levels of design input, to review the output of certain early to mid stage parts of a tool flow, and documentation on how to get the bitstream into the chip. If you want to play at a lower level, one of extremely few moderately modern options would be the lattice iCE40 for which the bitstream meaning is known in enough detail for an open source toolchain to exist. \$\endgroup\$ May 5, 2020 at 16:39
  • 2
    \$\begingroup\$ Note that you can write a simulator by compiling the Verilog to a different backend; have a look at "Verilator" which compiles it to C. \$\endgroup\$
    – pjc50
    May 6, 2020 at 14:27

7 Answers 7

23
\$\begingroup\$

Just like a procedural programming language goes through several steps (compile, assemble, link) to produce an executable, HDLs must pass through several processes before a usable configuration file for the FPGA is generated. These include

  • Synthesis --- convert the HDL code into a netlist describing connections between logical elements.

  • Mapping --- Convert the netlist into a more refined netlist that uses resources actually available on your FPGA device.

  • Place and route --- select which of the actual resources on the device will be used for each of the required elements in the mapper output, and choose which routing resources will be used to interconnect them.

  • Bitfile generation --- convert the place and route output to the format actually used to program the device.

So if, when you ask what is the output of synthesis, you mean what's the output of the first step of this process, then it's an intermediate file used as input to the mapper. If you mean what is the output of the whole process, it's a bitfile that the FPGA can use to configure all of its logic and routing resources.

\$\endgroup\$
13
  • 3
    \$\begingroup\$ Is there an explanation of the bitefile format(s) used somewhere? Like if I have a board, how do I know what bits are supposed to be in the bitfile format? \$\endgroup\$
    – Jake
    May 4, 2020 at 23:40
  • 4
    \$\begingroup\$ @Jake, if you're trying to write a simulator, you might rather look at the place and route output file than the bitfile. \$\endgroup\$
    – The Photon
    May 5, 2020 at 0:02
  • 4
    \$\begingroup\$ But those are the equivalent of the ELF spec. The tell you how to get things loaded into memory, but not what the meaning of the data you load is. \$\endgroup\$ May 5, 2020 at 0:33
  • 3
    \$\begingroup\$ There is a standard (JEDEC) bit file format (file extension .jed) which is still in common use for small (usually eeprom / flash based) programmable devices (such as power sequencers) despite first appearing in the 80s pldtool.com/pdf/jesd3c_jedecfmt.pdf \$\endgroup\$ May 5, 2020 at 12:20
  • 5
    \$\begingroup\$ @Jake There are several ongoing efforts for reverse engineering bitstream formats, for example clifford.at/icestorm (includes links to other project near the bottom). icestorm in particular has nice human-readable documentation and it's one of the “simpler” FPGA families, so it's relatively easy to grasp. \$\endgroup\$
    – wrtlprnft
    May 5, 2020 at 21:42
17
\$\begingroup\$

Register Transfer Logic (RTL) is the result of the first translation phase, before it is mapped to the vendor-specific resources, which are not portable between vendors or even between different FPGA from the same vendor. Essentially RTL shows both the combinational logic and the synchronous registers (D flip flops), so state machines are recognizable. RTL is pretty consistent between Altera and Xilinx, and is probably the most interesting and useful phase to inspect. Synthesis problems first become visible at the RTL phase, and the design is still recognizable. Once it goes to vendor-specific mapping it gets chopped up and scrambled. Trying to decode a chip-specific bitstream is high-cost, low-benefit, and will be useless when you move to a different vendor or even a different size FPGA in the same family. You can see what you need to see at the RTL level.

It's always good practice to test your newly developed Verilog or VHDL code by instantiating it inside a test bench or a simple toplevel module, and inspecting the RTL code. Xilinx ISE is very nice for inspecting RTL as a schematic (though it sometimes misses things.) The most common issues are:

  • 1-bit nets where a bus was intended
  • chunks of logic unexpectedly being removed by the optimizer... similar to how a simple spinlock delay loop gets silently deleted by code optimization.
  • outputs not completely specified, because of procedural approach instead of truth-table approach. If tool thinks output ends up always 0 or always 1, it will drop all the logic that generates that result.
  • module logic gets trimmed out because one of the sub-modules was optimized to always 0 or always 1; this can cascade all the way up to toplevel

This RTL inspection gets very unwieldy unless you keep your modules small and simple. Using a testbench is an important tool.

I too came from embedded systems programming first and verilog second, and the biggest hazard for people like us when learning HDL coding is that it looks like a procedural programming language, and it feels like a procedural programming language (during simulation), but then everything blows up when you try to synthesize working code. You really have to think about what the hardware has to look like, and make sure the RTL code includes all the hardware you expect.

Other than the fact that Verilog/VHDL involve typing some source code into a computer file, there's not really much resemblance to traditional C/C++/etc. Very little of your programming experience will transfer. Focus on dividing big problems into little problems, documenting everything in great detail, and writing test benches. Also invest in a good digital sampling oscilloscope if you don't already have one. Take a look at some of the example code published on opencores.org, as with C/C++ you can learn a lot of technique (both good and bad) from reading other people's code.

One thing that drives me nuts about FPGA development is that source control is not something that the toolchain vendors seem to think is an important feature. Xilinx Vivado is particularly bad in this regard, their advice seems to be to re-generate the project files from scratch when doing a new checkout. Trying to do a project handoff with 100Mb+ zip files is daunting.

The other thing that drives me nuts about FPGA development is that Quartus/ISE/Vivado tools don't really have a satisfying way to quell the flood of warning messages. When I write C/C++ programs, I expect to be able to address every warning message individually and either fix it or sanction it, so that I can eventually get a clean compile with zero warnings. Never really seen anyone achieve that in FPGA development; other FPGA developers (who are smarter than me) seem to just accept that a normal project has lots of diagnostic messages, which they often simply ignore, leaving it down to doing labwork and verifying on real hardware.

If you ever develop your own FPGA board (which I don't recommend), be sure to bring out any unused I/O pins to a header somewhere -- as many as you can manage -- because that's going to be your lifeline when you have to debug the FPGA code, or implement some eleventh-hour patch.

You mentioned programming in assembly language as a way to exercise precise control over what the computer is doing, and it is possible to exercise similarly precise control over FPGA code by using non-portable, vendor-specific primitives. This will be different for each vendor and each FPGA, just as assembly language is different for different CPUs. For Xilinx you would write a constraints file (different for ISE toolchain or Vivado toolchain). The constraints file would call out specific instances or specific nets, and specify timing requirements. Typically the low-level CLBs/LUTs/whateverUnits are arranged in a grid, so you can pin down a specific low-level primitive to live at a specific X,Y grid location. Look up the old Xilinx "FPGA Editor" for the Spartan 3 series, they used to encourage people to use it that way. I think the newer series 7 and Zynq chips it's not supported. Like assembly, it's very specific to the technology, and is thus kind of a volatile skill set.

Similar to assembly, for anything other than a trivial 'homework' exercise, you really want to minimize how much assembly you write; use C/C++ for 98%-99% and only write assembly for the 1% that is performance-sensitive. If for example you have an FPGA design that requires some sub-process to run at 200MHz, it's worth diving into the low-level mapping to see what the tools are doing. Best payoff for optimization is if you can eliminate unnecessary work stages. Only after you're pared the hot elements down to the bare minimum, only then is it worthwhile to start manually routing which IOBs belong at which grid locations. Let the machine do the bulk of the work, so you can focus your efforts.

\$\endgroup\$
11
\$\begingroup\$

The physical primitive of an FPGA is a configurable logic block (CLB).

Each logic block is given a dedicated location in memory, so-called configuration memory, that determines how it is configured and where it connects to.

HDL ultimately ends up as a bunch of ones and zeroes, a so-called bitstream that is placed in this configuration memory.

Most FPGAs do not have on-board non-volatile configuration memory. Instead, the configuration bitstream is stored on an external configuration FLASH ROM and on power-up the FPGA loads that bitstream from external non-volatile memory into its internal configuration SRAM which is directly connected to and controls the CLBs.

Unlike software, this bitstream is not "run". It is just loaded and afterwards it simply "is". It is less like instructions being executed and more like registers containing settings.

It is a file such as a *.bit. There is no standard format. I am not sure why you would want to write a simulator yourself when FPGA development tools come with a simulator. Much effort is put into this and they know their devices better than anyone else because unlike software, each primitive that is specified in the bitstream must be physically located somewhere on the FPGA die and the floor plan can make or break some designs.

\$\endgroup\$
3
  • \$\begingroup\$ I believe the term CLB is specific to Xilinx products. Microsemi (formerly Actel) uses terms like register cells (r-cells) and combinatorial cells (c-cells). \$\endgroup\$
    – SteveSh
    May 4, 2020 at 23:35
  • \$\begingroup\$ @SteveSh Yes, different terms all around. If they can't even agree what they're called they won't be able to agree on standard bistream format. \$\endgroup\$
    – DKNguyen
    May 5, 2020 at 0:32
  • \$\begingroup\$ I mean, they use different terms because they use different architectures. I think a Lookup Table would be a better example for the physical primitive (and why the major brands usually list LUT-equivalents) even though the elements are more complex than a standard 4-LUT. \$\endgroup\$
    – wilcroft
    May 5, 2020 at 16:40
5
\$\begingroup\$

what bytes go over the write when an FPGA is being programed?

This is less answerable generally, because it is 100% manufacturer-specific and device-specific. Some manufacturers publish datasheets for this; other manufacturers consider this a "trade secret" and you would need to sign an NDA to find out.

With a C (or any other language) compiler anyway, the raw bytes are not the most basic part. The most basic part is the series of processor instructions which implement your program, and the raw bytes are simply how you tell the processor what those instructions are. These instructions cause the processor to carry out operations using its various hardware facilities such as adders, multipliers and the like, and store or retrieve data in registers and memories.

This is very similar in an FPGA, except that you're starting at a lower level. Instead of having a list of instructions to run, what you have is a list of how every gate in the FPGA should be interconnected. Most FPGAs also contain specialised sections for RAM and other features, and your program will also include how these are hooked up.

What you end up with then is a netlist, the same as if you were designing a PCB with a million logic chips. This is conceptually the most basic output from your FPGA compiler to tell you what it's doing, in the same way as an assembler listing is conceptually the most basic output from your C compiler to tell you what the processor is doing.

Of course the compiler then continues to produce a binary file which will program the FPGA with that netlist, in the same way as a C compiler continues to produce a binary file which programs your micro with that assembler.

\$\endgroup\$
2
\$\begingroup\$

"what bytes go over the write when an FPGA is being programed?"

Simplistically, these bytes contain the information for:

1) Configuring the FPGA logic & I/O blocks (do you want this block to be a register, a multiplexer, a general purpose look up table), and

2) Configuring the interconnection on the FPGA to hook up the logic blocks to one another and to connect then to the outside world.

\$\endgroup\$
2
\$\begingroup\$

So many answers I didnt read them all, being someone that sits on both sides of the fence, hardware and software....

So while in every way it is a programming langauge that gets compiled into other languages ideally lower level (C to asm or machine code, JAVA to bytecode or other langauge or assembly or machine code, etc). There are a lot more steps and the targets are much more varied than isa that are as common as they are different: add, mov, jne, push, pop, etc.

The machine code for fpgas or asics is not just discrete transistors, but a library of things. For asics for a particular foundry and process there is one or more cell libraries available and they will have simple and and or gates but also larger and more complicated things, why build a latch out of discrete components when the library authors can make some common sized ones and pack it up in an efficient (real-estate) way. So as with any compiler or high level author you go through the list of options and pick some. including a long list of srams of various widths and depths. For an FPGA it is LUTs or kinda fixed generic modules that can act as various modules that are more complicated than a simple and, or, xor gate.

The FPGA world likes to keep things close to their chest, also they try to be way more affordable than asic tools, tens of thousands of dollars per year rather than millions. And like any other integrated environment you at times have various vendors that were purchased or licensed and the tools glued together (often not very pretty).

So all the steps other folks mentioned happened. which is more than software does and the file formats are very proprietary and not expected to be documented nor supported, each version could change. Again these folks seem to be competitive and secretive (if they were to open up we could have significantly better tools and they could sell more product IMO, but may cut down on support money which is maybe what they live off of with their crappy tools).

When folks say netlist, the verilog is compiled into a netlist, the ones I have seen are also in verilog or vhdl as those languages cover that. ultimately you will then target the specific whatever, fpga, cpld, asic, etc. The fpga being an already wired/fixed target you ultimately end up with a list of fuses or switches if you will, take a generic lut and connect this input to that and that input to that by opening and closing connections in the massive mesh of stuff. Which ends up being a sorta simple list of ons and offs. And I think there may be a jedec standard on this but often called a bitstream and at least we use a bitstream player to load.

CPLDs in general you program this information in the part itself be it a flash on board that then opens/closes things on power up and/or the non volatile storage inside powers up with the items wired.

FPGAs typically have an external flash and on power on the information to connect things is loaded from that then the rest of the part comes up using that setup. The ones I know about you can while powered also load this information into the part and make all the connections, but that information is lost when the part is powered down. so depending on your design you might have some other solution and not use the flash. The flash format is probably prioprietary, I have not looked personally, when you come in through the programming interface that goes through the fpga to the flash so it could go as is or it could be converted on its way into something else.

With software esp this time and place we are used to mostly open stuff, somewhat good free tools that many folks use. Which also means the file formats are documented and somewhat common, some had history before this time .com and .exes and some others for other operating systems of the day. But again the software world is more common than different you ideally are aiming for machine code or bytecode. With logic you are going from high level to a lower level but using the same language to some point. Then you may be targetting a simulator which has its own library of modules or fpga n with its library or fpga m with its library and so on. And those folks are very protective of information.

Clifford and project IceStorm IMO is the right way to go, it is so far the only time I have built something for an fpga that is simple, it works, no warnings or errors which software folks often like. I have spent countless nights trying to get the simplest thing to build for an fpga from all the major vendors without warnings...and would always give up. From verilog to programmed part it is at least three separate projects which implies there are intermediate files that both sides need to support so the file formats are there. but would be expected to be project specific and not necessarily like an elf file that is widely used for more than one use case.

Understand that the chip world including fpgas is insanely expensive so it is money driven which often means sell tools and most importantly annual support contracts. Which means closed source, closed information, not publicly documented file formats.

\$\endgroup\$
1
\$\begingroup\$

Before you get to synthesis, you can verify the design in simulation. In this case, your design is treated as software - and for at least one simulator (open source GHDL, not surprisingly for VHDL) the compiler can use either gcc or LLVM backends to generate an ordinary executable.

Generally you embed your actual design in a "testbench" (also written in VHDL) which generates input signals and reports on the correctness of the outputs - either to the console (via Assert/Report statements) or using verification and logging tools like OSVVM, under a unit testing framework (VUnit).

If all else fails you can dump and inspect waveforms in a GUI.

Once it's all working, proceed to synthesis as described in the other answers.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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