9
\$\begingroup\$

I have just started learning digital logic design with FPGA's, and have been building a lot of projects. Most of the times (since I am kind of a noob), I have a design that simulates perfectly (Behavioural simulation) but does not synthesize properly.

So, my question is "what are the design steps that I can incorporate in my workflow, that will ensure that I have a working design that will work right on my FPGA ?"

I have two main areas where I expect advise, but this is absolutely based on a very narrow viewpoint of mine as a beginner, and more are welcome :

  • What all steps (viewing RTL schematic, post synthesis simulation, ...) should I undertake learning for the best practice.
  • What all things should I keep in mind while designing my logic (say FSM's or sequential circuits) to avoid any unexpected results.

I am using a Xilinx Spartan 6 FPGA, and Xilinx ISE Design suite for my work.

\$\endgroup\$
9
  • 1
    \$\begingroup\$ What sort of problems are you encountering with synthesis? Do you achieve a high level of coverage in the simulation? \$\endgroup\$
    – pjc50
    Commented Mar 2, 2016 at 9:55
  • \$\begingroup\$ @pjc50 I don't understand the question. What do you mean "high level of coverage in simulation" ? \$\endgroup\$
    – ironstein
    Commented Mar 2, 2016 at 9:56
  • 1
    \$\begingroup\$ You have a testbench or stimulus driving the simulation. Coverage tools tell you how much of the design is actually being exercised by the test, as a percentage. If this number is too low then your testbench is inadequate and you're not testing some cases which could come up in real usage. \$\endgroup\$
    – pjc50
    Commented Mar 2, 2016 at 10:29
  • 1
    \$\begingroup\$ Worth noting: synthesizes and "definitely works on actual hardware" are different levels of rigor. There are patterns one can follow to make sure it synthesizes. However, when it comes to making it work on actual hardware, with certainty, one must remember the maxim of simulation: "All models are wrong; some are useful." \$\endgroup\$
    – Cort Ammon
    Commented Mar 2, 2016 at 14:23
  • 1
    \$\begingroup\$ In comments on your earlier question you shared your code. I don't see any reason this code should fail to synthesize. But you never mentioned constraints. Did you provide a constraint file that properly specifies the clock timing? Even a simple clock period constraint should be adequate for your design. \$\endgroup\$
    – The Photon
    Commented Mar 2, 2016 at 17:04

5 Answers 5

15
\$\begingroup\$

At a place I worked there were two camps of FPGA designers. One camp I called simulate, simulate, simulate or s cubed. The other camp was all about design.

The s cubed guys used a simulator like modelsim, they would come up with an initial design via coding methods and\or blocks in the design suite. Then they would simulate it and find the things that wouldn't work, then change the code. This process was iterated several times until they came up with a design that worked.

The design camp (which I preferred) would design the waveform on paper (or digital paper like visio), exactly what was required. Then come up with a logic diagram. This is a self-documenting process. Then the diagram was translated to code (the code and the diagram were 1:1 if there was something in the diagram, there was a process for it in the code). Then it was simulated, and the simulation waveform was compared with the designed waveform on paper, and was expected to be the same.

I ended up doing both, sometimes I would get into s cubed mode, and it wasn't very fun. I found that I lost sight of my goal sometimes. For example, I would change a state in a state machine, and the change would ripple down to the next state, then I would have to fix that. I ended up spending more time than thinking about it.

Which camp would you rather be in? I think there needs to be rigorous design, do what works for you, but I think the more detailed and rigorous you are at designing, the less problems you will have in the long run. I gave some examples of what is possible, they may not fit the organizational structure of your workplace. The reason why design detail and careful planning is so useful, is it forces you to think about what your doing. It makes it easy to debug. Develop a design workflow that allows this to happen. Also, get really familiar with the simulation tools and write good testbenches that will test all of the conditions that the simulated device might experience. This of course needs to be balanced with time. For example write ADC HDL code that will simulate the device in your simulations.

The most valuable tool to have in FPGA design (in my opinion) is a good testing procedure that will allow you to fully test your design and run it through its paces. An FPGA design cannot be expected "to just work" it takes effort to make sure all of the pieces work. If you spot errors, then go back to the simulation and design and learn what the differences between a simulated FPGA and RTL are. That mainly comes with experience, but if the design works in simulation but not in hardware then you need to find out why there is a difference.

A few key things that I learned:
1) Sanitize your inputs, the clock and reset circuits need to be clean or you can get metastablity propagating through your system. Know what a dual rank synchronizer is. There are many different topologies for reset circuits, know how to use them (there is a great article out there on the web, I don't have it on hand though).
2) Get the requirements of the design up front and then design around those. If the people around you won't give you definite requirements, then come up with some on your own.
3) Matlab fixed point toolbox is great for simulating control systems and DSP applications, but you might not have access to that. It's a great way to prove a design before you code.
4) Design comes first, then coding, then simulating.
5) Strongly typed, also keep signal names consistent on pcb schematic and hdl. (this is also why I much prefer VHDL over verilog.

\$\endgroup\$
4
  • 2
    \$\begingroup\$ +1 for "s cubed" or \$ {simulation}^3 \$ \$\endgroup\$
    – Paebbels
    Commented Mar 2, 2016 at 9:27
  • \$\begingroup\$ Quite good : to "rigorous design" I would add "using the type system". Example : an array index of appropriate type such as the array's range, no need to test for out of bounds condition. I would only disagree with "waveform compared with the designed waveform on paper " ... the designed waveform should be in VHDL by that stage, (or perhaps read from text file) and the simulator should perform the comparison. \$\endgroup\$
    – user16324
    Commented Mar 2, 2016 at 14:32
  • \$\begingroup\$ It could be done that way also, I found it useful to design a waveform on paper because it gave something to compare against. Like an ADC waveform, the timing was designed and then compared to the modlesim output, then verified physically. If the modelsim output is correct, then compare it to that. The code was strongly typed (I forgot to mention that), but that is really important. That is why I much prefer VHDL over verilog, there are less shortcuts you can take. And it makes the code much more readable. \$\endgroup\$
    – Voltage Spike
    Commented Mar 2, 2016 at 17:12
  • \$\begingroup\$ yes. Actually just like other areas such as software or conventional hardware, the starting point is to break the problem into blocks, and then ask yourself "how will I know when that block works". Then do it. Build your design block by block, then put the blocks together and again test that what you get is what is expected. Sometimes you might realise that with a better block-level design it would be cleaner or easier, so you backtrack. \$\endgroup\$
    – danmcb
    Commented Sep 18, 2019 at 7:29
6
\$\begingroup\$

Main things are:

  • Careful coding to avoid non-synthesizable structures
  • Minimize logic levels for better timing performance (make logic between registers as simple as possible)
  • test, test, test to ensure functional correctness and check for things like uninitialized regs and disconnected wires
  • synthesis and check synthesis logs for warnings, make sure the warnings do not indicate issues (I.e. Removed register warning could be intentional (didn't use a module output) or unintentional (forgot to connect module output/typo/etc.))
  • mapping and check map report for utilization figures, make sure the FPGA is not too full
  • place and route and timing analysis, make sure that your design will run at the required clock speed

I have had several rather complex designs work correctly (or at least mostly correctly) on the first test on an actual FPGA by following the above. No need to check the RTL schematic, that's just extremely cumbersome and a complete waste of time for large designs. A post-synthesis simulation would be much more useful.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ thank you for your quick reply. Could you please elaborate upon the second point (minimize logic levels). \$\endgroup\$
    – ironstein
    Commented Mar 2, 2016 at 6:20
5
\$\begingroup\$

All your synthesizable code needs to be expressable as:

  • LUTs
  • Flip-flops
  • Vendor-specific primitives

Vendor-specific primitives are either instantiated explicitly, or generated by the vendor's wizard, or inferred by very specific coding patterns, so there shouldn't be any ambiguity there.

In VHDL for example, you can't use wait for in synthesizable code. To understand why, try deterministically expressing wait for 100 ns using LUTs or flip-flops. You can't.

That doesn't mean that you can't implement it by setting up a counter with a known clock frequency (with period that can divide 100 ns) and use its count to know when the time is up. But the synthesis engine won't come up with this scheme automatically, you need to be explicit about the architecture in terms of combinational logic (gates/LUTs) and registers.

So the main thing to keep in mind in order to generate synthesizable code, is to have a relatively clear picture of how your code becomes logic gates and flip flops. That is really it.

\$\endgroup\$
1
  • \$\begingroup\$ wait until rising_edge(clk); is certainly synthesisable, though some tools impose restrictions on its use. \$\endgroup\$
    – user16324
    Commented Mar 2, 2016 at 14:34
2
\$\begingroup\$

The most obvious first step is to check the warnings.

Xilinx tools produce log files which warn about anything that could be not what the coder intended. Sometimes this is annoying, when you have reams of warnings about unused signals which you know perfectly well are unused. But sometimes it catches genuine bugs. If you're a newbie, the chances of you making a mistake are significantly higher.

Then you need to set up timing constraints. How quickly after a rising edge on clock A does data line B need to be set? Or how long does data line B need to be held before a falling edge on clock A? Timing constraints will let you specify all this. If you don't have timing constraints, the compiler may assume that you don't particularly care and could route your signals anywhere. If you have timing constraints, the compiler will work to make sure your signals meet those constraints by moving placement around. And if it can't meet the timing constraints, it will put up a warning.

If your problem is that outputs aren't doing what you expect, look in detail at the I/O blocks. Each I/O pin has a bit of associated logic and a flip-flop. The order in which you specify your logic and state variables in your code may not make it possible for your code to fit into this architecture, so you get an extra delay from wherever it happens to be placed. Warnings on timing constraints will tell you if this happens (assuming you've set up your timing constraints), but fixing it requires you to understand the hardware and how your design will map into hardware. Generally this is only an issue when you start hitting high clock rates, but it's worth mentioning.

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

I was not majoring in integrated circuit design, however my first job after graduation was to design digital modules in an MCU design company. I even had no knowledge about verilog then.

In my experience, the most important thing is to think in a hardware way. Read several industry hdl modules, and if possible check the corresponding schematic with the help of EDA tools. Learn and remember some basic building blocks, like edge detection/gray coding/FIFO controller/FSM, etc. The synthesizable code is a subset of the whole standard. For purposes of robust/bug-free design, the language structure in real use in a company is further a subset of synthesizable code. So you don't need much language structrue.

A circuit is an interconnection among different blocks performing specific functions, and that block itself can further be broken down to interconnection of more detailed items. So to start design, use the fundamental blocks to design the cuicuit using pencil or schematic drawing tool or in your mind, whatever, examine it. And write down hdl code to describe your design in a ASCII format.

\$\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.