FPGA best practices for processes - Page 1
Forum section: electronics / general

author: Myndale
author_rank: Newbie
timestamp: March 03, 2018, 08:41:49 am
content: Hi everyone, first time post here.I've been playing around with FPGAs on-and-off for a while now, but I'm still quite new to them and I have a question about the best practices to use when you're writing VHDL processes. I realize that what I'm asking here is probably very vague, but everything I've learned about FPGAs so far has been self-taught (insofar as anything you learn on the internet is "self-taught") and I could really use some guidance from people who know how these things should be done. My question concerns how you should split logic in a single entity across processes.The most basic example I can come up with is simulating and synthesizing the TTL 7400 quad-nand chip. There are 3 ways I can think to do this. First is the most obvious and the most easiest to read, at least to me, when you're sticking with discrete std_logic signals:Code:process(A1, A2, A3, A4, B1, B2, B3, B4)beginY1 <= A1 nand B1;Y2 <= A2 nand B2;Y3 <= A3 nand B3;Y4 <= A4 nand B4;end process;Second is to minimize the work done in each process, as well as the dependency lists:Code:process(A1, B1)beginY1 <= A1 nand B1;end process;process(A2, B2)beginY2 <= A2 nand B2;end process;// ...etc...Third is to exploit parallelism and use vectors:Code:-- A, B and Y are now all std_logic_vector(3 downto 0)process(A, B)beginY <= A nand B;end process;My intuition is telling me that #3 would typically be more helpful to the synthesizer and thus the most most preferred of the three. I'm also guessing that #1 would be second choice because while it would (in theory) slow down simulation it's much easier to read. If simulation started becoming an issue then I'd start looking at #2...although as a beginner I have no idea whether that even happens.Can anyone give me some general guidelines as to the circumstances in which you would use these or if there are any others that I'm missing?

author: hans
author_rank: Super Contributor
timestamp: March 03, 2018, 09:20:12 am
content: I only use process definitions for RTL designs that have a clk and asynchronous reset.The rest (i.e. all asynchronous statements) can sit outside a process.If you're concerned about simulation, I would guess that #1 and perhaps #3 will be the slowest, due to the large sensitivity list.But to be honest, a design like this is so infinitesimal that I wouldn't worry about these things.As far as the synthesizers go.. most if not all of them are good enough to recognize what you're doing and create the same design. I think decision between the 3 is about readability, in which case I would prefer #3.

author: hamster_nz
author_rank: Super Contributor
timestamp: March 03, 2018, 09:25:25 am
content: For something simple like your example, just use a concurrent statement.For complex combinatorial logic try to limit the size and scope of a process as much as possible - more smaller processes are better than on huge one.For clocked processes I prefer to have one process per clock domain - with multiple processes in the same domain the interactions between processes becomes subtle and non-obvious (or at least to me).

author: Myndale
author_rank: Newbie
timestamp: March 03, 2018, 10:16:17 am
content: Thanks Hamster, I appreciate the advice. One process-per-clock does make sense when you put it like that.FWIW your site was the one that first got me interested in FPGAs and convinced me to buy my first one, a Papillio Pro. I didn't understand the importance of simulation though and my frustrations with the Xilinx toolchain at the time got the better of me, so I shelved it for a while. I eventually got a FleaFPGA Uno from Valentin Angelovski and switched to the Lattice toolchain and I've been having a hell of a lot more fun since. Slowly working on a little project that I hope to release publicly, so I'm starting to make the mental shift from "how do I do this" to "how do I do this better".

author: Myndale
author_rank: Newbie
timestamp: March 03, 2018, 10:18:30 am
content: Quote from: hans on March 03, 2018, 09:20:12 amThanks hans. I probably shouldn't have used a combinatorial example in hindsight because my question was meant to be specifically about processes, but I appreciate the feedback. Cheers!

author: SiliconWizard
author_rank: Super Contributor
timestamp: March 03, 2018, 03:05:07 pm
content: Your example is purely combinatorial logic, this can sit outside any process.As for synthesis, I think any decent optimiser will give you pretty much the same result with any of your 3 cases as well as without any process.Concerning simulation, it would surely depend on the simulator itself, but I'm not sure it would make a significant difference.I personally never use processes for anything other than clocked processes.

author: rstofer
author_rank: Super Contributor
timestamp: March 03, 2018, 04:30:12 pm
content: I'm not sure I understand example 3. If the signals were a vector to start with, fine! But if the elements are std_logic signals that need to be concatenated to form a std_logic_vector just to use a vector approach to combinatorial logic, well, I don't think I would do it that way.I wouldn't put combinatorial logic inside a process; there is no need. I don't go as far as hamster_nz in having only one clocked process (per entity?) and I usually break my FSMs up into a clocked next_state process and a separate process for the state logic (not clocked).Most of these things are just a matter of style. What is interesting is to look at the RTL schematic after synthesis. You might gain some insight into just how each of your 3 examples are synthesized. It may turn out that they are all identical. Synthesizer writers are a pretty smart bunch.

author: Myndale
author_rank: Newbie
timestamp: March 03, 2018, 08:51:44 pm
content: Quote from: rstofer on March 03, 2018, 04:30:12 pmMy thinking was that you'd adjust the entities port list to be vectors as well, so no need to concatenate anything. My reasoning was that a vector is a closer match to the I/O design of the LUTs that will ultimately be used to implement the logic and that using them might somehow help the synthesizer do a better job, at least with more complex logic.The commonality I'm seeing in your answers though suggests that I seem to be over-thinking all of this....which is also a perfectly valid answer of course, so thanks.

author: rstofer
author_rank: Super Contributor
timestamp: March 04, 2018, 11:21:20 pm
content: You can use a std_logic_vector and 'alias' individual signal names or subfields if you wish.This comes up quite a bit when there are registers in a peripheral. The register might be an 8 bit std_logic_vector but I want to alias DataReady to register for later use.

author: SiliconWizard
author_rank: Super Contributor
timestamp: March 05, 2018, 02:04:19 pm
content: Quote from: Myndale on March 03, 2018, 08:51:44 pmAs you figured from our answers, the synthesis, mapping and PAR tools of FPGAs do this job much better than you could ever do by hand, so you don't have to bother indeed. They would probably even place your signals in different LUTs in unexpected ways (for the human eye anyway) to optimize for speed or area, so there is no point.Packing individual logic signals in vectors doesn't really make sense in this way, but if the signals are related, it would certainly make your VHDL code more readable and less redundant. As you can see for yourself, your third option is much more compact. But if you pack in vectors signals that are not really functionally related, it would hinder readability. So it's pretty much a coding style choice you have to make depending on your underlying logic.That said, the global architecture of your design can have a big impact on the way it's implemented on an FPGA. So I'm not saying that you should never think about the way it would get mapped in hardware, but not on a (relatively) trivial level such as the example you gave (signal packing). You usually have to think on a higher structural level, such as which functional blocks in your design could be shared or which blocks would have more chances getting duplicated.To get a better grasp, you can try different coding approaches on a given design and evaluate the result (resource usage, timing) up to PAR (place and route) in your preferred FPGA design software suite. Don't jump to conclusions with minor differences though (which you will get from one run to another even with small code changes) - look for significant differences in timing and resource usage.