Combinatorial inside or outside of a process - Page 1
Forum section: electronics / general

author: SiliconWizard
author_rank: Super Contributor
timestamp: October 18, 2020, 04:39:01 pm
content: This may look like a basic question, but I don't think I have a definite answer for this even after years of VHDL. (Guess the same applies to Verilog.)When dealing with purely combinatorial concurrent statements, is there any difference whether you put them inside of a non-clocked process (which I have seen quite a few times) or outside of any process (just in the body of some entity)? I think there probably is (or at least there could be) for simulation (I guess a process with a proper sensitivity list would make simulation faster), but I can't think of any when it comes to synthesis. I may be missing something obvious though.Your thoughts?Simple example:Code:Q <= A and B;vs.Code:process (A, B)beginQ <= A and B;end process;

author: NorthGuy
author_rank: Super Contributor
timestamp: October 18, 2020, 06:45:15 pm
content: No difference in terms of the result.VHDL limits what you can do outside of the process. Say, you cannot use "if" outside of a process. VHDL is hugely overrestrictive. I don't know if this the case with Verilog.

author: SiliconWizard
author_rank: Super Contributor
timestamp: October 18, 2020, 07:00:18 pm
content: Quote from: NorthGuy on October 18, 2020, 06:45:15 pmThat's what I've always assumed. Yes there are things you can't do outside of a process, such as "if" but also "case". Conversely, you couldn't use a "when" statement inside of a process, but it's allowed since VHDL-2008.I've seen code such as exposed above though, and am wondering what the point is. As I said, it may have made simulation faster in relatively "primitive" simulators, I don't think it really makes a difference there either with modern simulators.

author: rstofer
author_rank: Super Contributor
timestamp: October 19, 2020, 03:42:06 am
content: Processes have sensitivity lists, I wonder if it impacts simulation?

author: Daixiwen
author_rank: Frequent Contributor
timestamp: October 19, 2020, 07:06:39 am
content: It does, because the simulator will only run the process if one of the signals on the sensitivity list changes. On the other hand the synthesizer will ignore the sensitivity list and will just generate the combinatorial logic described inside the process. This means that if you forget some signals in the sensitivity list you will have differences between the simulation and the real hardware. Most tools give out a warning when this happens.With VHDL 2008 you can also just use "all" keyword in the sensitivity list to generate it automatically from all the inputs in the process.

author: SiliconWizard
author_rank: Super Contributor
timestamp: October 19, 2020, 05:12:38 pm
content: Quote from: rstofer on October 19, 2020, 03:42:06 amAs I hinted, it can, and AFAIR, it used to in early simulators. In practice, it depends on the simulator. Modern simulators tend to ignore sensitivity lists IME, as do most synthesis tools these days. The list of signals inside of a process that are "read" and can change state is usually straightforward to derive, and a close match in behavior between simulation and synthesis tends to be favored these days.Do no hesitate to experiment with various simulators though and report back, that could be interesting.You could take the very simple example I gave and play with it with your favorite simulator:Code:process (A, B)beginQ <= A and B;end process;now remove either A, B or both in the sensitivity list, and test all cases in a test bench.Except when I need post-PAR simulation (some simulators shipped with some vendor tools offer that), I most often use GHDL as my main simulator, which has become very good. I'll try and make some experiments about that later, out of curiosity.I personally use sensitivity lists mostly as "documentation" - thus to improve readability, and since the large majority of my processes are clocked ones, the sensitivity list is basically just the clock signal they use, plus a reset signal if they ever use an asynchronous reset.

author: rstofer
author_rank: Super Contributor
timestamp: October 19, 2020, 06:24:18 pm
content: Apparently, a concurrent assignment outside a process is interpreted as a process with a sensitivity list containing all the signals on the right hand side of the <=An un-clocked process will create exactly the same result so it is really a matter of taste. You can avoid typing the surrounding 'process' stuff if you wish.Sensitivity lists are probably an anachronism because VHDL now allows 'all'. The simulators are smarter and the lists are not involved with synthesis.https://vhdlwhiz.com/concurrent-statement/For folks wanting to learn a lot more about VHDL, I highly recommend that site. The author spends a lot of time talking about test benches which is a topic about which I know very little. ISE didn't include a free simulator when I first started with FPGAs. Transmorgifying the examples from ModelSim to Vivado is left as an exercise for the reader.Since the topics are usually simulation, the author tends to use just Visual Studio Code and ModelSim. Having multiple documents side-by-side (on a wide monitor) allows the user to see both the entity and the testbench code all at the same time. I am getting to truly appreciate Visual Studio Code.

author: rstofer
author_rank: Super Contributor
timestamp: October 19, 2020, 07:18:36 pm
content: In the case of a process with multiple assignments to a signal, the last one sticks, right? How do you achieve that using concurrent statements outside a process? You can't because you will get a 'multiple driver' error.Code:library IEEE;use IEEE.STD_LOGIC_1164.ALL;entity assignment isPort ( a : in STD_LOGIC;b : in STD_LOGIC;c : out STD_LOGIC;d : out STD_LOGIC);end assignment;architecture Behavioral of assignment isbegind <= a and b;process(a,b)beginc <= a and b;if a = '0' thenc <= a or b; -- this is ok inside a processend if;end process;d <= a or b; -- multiple driver errorend Behavioral;With Vivado, the code will synthesize but it won't pass implementation.

author: SiliconWizard
author_rank: Super Contributor
timestamp: October 20, 2020, 12:05:51 am
content: Quote from: rstofer on October 19, 2020, 07:18:36 pmYes, that's indeed one syntactic property of processes that you can't use outside of a process, along with others we mentioned above.

author: Daixiwen
author_rank: Frequent Contributor
timestamp: October 20, 2020, 06:29:46 am
content: I know that at least Modelsim uses the sensitivity lists in processes and won't execute the process if an input that isn't in the list changes.As for your code rstofer, the solution is to put a conditional statement:Code:c <= (a or b) when (a = '0') else (a and b);but in this particular case, it could be simplified toCode:c <= b;As a side note some DO254 validation tools don't like multiple assignments to the same signal inside a process because it can affect readability. When the process gets longer and more complex, it can be difficult to follow what the signal is actually assigned to if it is done from multiple places that are spread around. I tend to agree with that. Even if it is perfectly legal VHDL and understandable, when reviewing tens of thousands of lines of VHDL code I try to keep things as simple as possible in order not to spend too much time on each small part of the code. If I had to do your code inside a process, I'd use an if-else construction so that I know that all conditions are covered. The only exception I tolerate is giving default values to some signals at the beginning of the process.

author: emece67
author_rank: Frequent Contributor
timestamp: October 20, 2020, 09:45:55 am
content: .

author: Daixiwen
author_rank: Frequent Contributor
timestamp: October 20, 2020, 10:23:32 am
content: Quote from: emece67 on October 20, 2020, 09:45:55 amNone of the synthesizer I've worked with do that. They all ignore the sensitivity list. Take this process for example:Code:process(a)beginc <= a and b;end processWhat you are describing here is logic that would ignore any changes on b while a is stable. This is not reliably synthesizable in an FPGA, even with a latch. Any synthesizer I know will just put an AND gate and call it a day. If you know of any synthesizer that would actually do what is described, I'd be very curious to know about it and see what kind of logic it generates.Latches warnings from combinatorial processes often come when you forget to assign a value to an output in some cases. It kind of works most of the time but is not very reliable either, and you get a warning.

author: emece67
author_rank: Frequent Contributor
timestamp: October 20, 2020, 11:03:10 am
content: .

author: rstofer
author_rank: Super Contributor
timestamp: October 20, 2020, 02:17:23 pm
content: Quote from: Daixiwen on October 20, 2020, 06:29:46 amIt was a particularly bad example! It forced the multiple driver issue and demonstrated the lack of an error inside a process but that's about all it was good for.QuoteFSMs where only a few of the dozens of states manipulate an output signal will almost always have multiple assignments and default values at the top. The alternative might be to create a separate process to manipulate that signal (say "LoadAccumulator") that tests particular values of the state variable. I don't think that approach reads very well. When I'm looking at a state, I want to see everything it does in one place.Failure to provide a default value will result in a latch and a failure in the logic. It will build but it generally won't run. It definitely doesn't result in a build error but there will be a warning about creating a latch (Xilinx tools).I seem to use concurrent assignments to create a port output signal from an internal signal that has been on the right hand side of another assignment.

author: SiliconWizard
author_rank: Super Contributor
timestamp: October 20, 2020, 03:23:07 pm
content: Quote from: rstofer on October 20, 2020, 02:17:23 pmI agree with you there. But I can still understand why it's frowned upon in some contexts. It's similar to coding rules in software. Some look way too strict to the point of looking ridiculous, but the rationale is always to limit the possibility of writing bad or hard-to-read code. In other words, hard rules are there for the sloppy ones (but we can all be sloppy at some point...)But, when it comes to "default values", the merit of such a rule (never using them) is indeed questionable.In trivial cases, I agree they add nothing and hinder "structure" a bit: (warning: very simple examples)Code:process (...)beginQ <= A and B;if A = 0 thenQ <= B;end if;end process;it's clear here that an "else" clause would make more sense and be easier to read. (YMMV though, code style is not an absolute.) But if there is a whole series of tests and particular cases following the default value assignment, then assigning a value for all possible cases may be a lot clunkier to write than it's worth (and the clunkier it is, the easier it becomes to introduce bugs.)

author: SiliconWizard
author_rank: Super Contributor
timestamp: October 20, 2020, 03:44:11 pm
content: Regarding sensitivity lists, I try my best to make them correct out of discipline (and as I said, for documentation reasons if nothing else.)But I'm not convinced at all about the merits of them in the general case, even for simulation. Potentially ignoring signal changes in processes is atrocious, and willingly doing so, even more. That makes code hard to read and in fact gets you implicit latches. To me it's a big no-no.Another point is that I don't agree with any potential mismatch between synthesis and simulation (as long as the code is synthesizable of course) from a purely *behavioral* standpoint. Of course I expect potential differences due to timing issues (which is a huge topic in itself), but I would never expect an actual difference for the intended *behavior*. That would make code validation itself nearly impossible, and if you reply that it's all up to the coder's discipline, then it kind of contradicts with any hard rules and/or language features made to help avoiding shooting yourself in the foot.As I said earlier, and IIRC (that's old memories), I think that was a feature to help simulators more so than to help people. Any signal not in the sensitivity list would just be considered as never changing states, which would make the whole simulation faster. So, omitting a signal in the sensitivity list would usually be done if you were certain this signal actually never changed (so, as an hint to the simulator or to the reader) rather than to infer implicit latches. I may be wrong though. And I don't think it really matters on modern, event-driven simulators. Now older people may have gotten used to that and, as any habit, may be sure of the merits, but as for me, I'm just not.If you can think of a single valid merit (except for the "it's just the way it should be"), please explain. Because if you actually use it as a way to potentially infer implicit latches in simulation, that to me looks a lot worse than using default values for signals that we just talked about above.

author: emece67
author_rank: Frequent Contributor
timestamp: October 20, 2020, 05:43:15 pm
content: .

author: rstofer
author_rank: Super Contributor
timestamp: October 20, 2020, 06:53:50 pm
content: Fundamental rule: Every output from an FSM must be defined under ALL conditions. For my IBM1130 project, there are 117 states, 52 inputs and 49 outputs in the main FSM. ISE is rather insistent that I put all the inputs in a sensitivity list but it's not an error to forget. I'll concede, a multiline sensitivity list looks a little bizarre.It's also up to me to make sure that every output signal is defined under all conditions. Clearly, default values are the only practical way to go. Trying to define 49 outputs in each of 117 states is out of the question. It really blows up when you think about if-else or case statements.My biggest problem was with 'stepwise refinement' in that when I added a new signal, I had to remember to create a default value.How do you work around default values if the company's coding rules preclude them? It would seem like I would have to break up the FSM somehow but I'm not sure how to approach the problem.Of course, being retired means I can make up my own coding rules!

author: rstofer
author_rank: Super Contributor
timestamp: October 20, 2020, 06:57:52 pm
content: Quote from: emece67 on October 20, 2020, 05:43:15 pmThat is a brilliant demonstration! I'll have to feed the code into Vivado and see how it comes out.ETA:OK, I did that and I got the same result with Vivado.I assume the file thing.vhd has a process like this:Code:architecture Behavioral of thing isbeginprocess(a)beginc <= a and b;end process;end Behavioral;Thanks for the example, it has been educational!

author: emece67
author_rank: Frequent Contributor
timestamp: October 21, 2020, 10:23:34 am
content: .

author: SiliconWizard
author_rank: Super Contributor
timestamp: October 21, 2020, 04:11:32 pm
content: This actually all triggered a more interesting discussion than I initially expected.To better understand what was actually strictly conforming to the VHDL standard (as, possibly, opposed to what many of us may assume, and as, sometimes, opposed to what tools vendors implement), I took the VHDL-2008 standard and read the part about processes ("11.3 Process statement"). I have read quite a few books on VHDL over the years, but I admit I never read the standard itself (whereas this is something I'm more used to with software programming languages.) Until now.QuoteMore subtleties follow, but this is the basic concept. What that means is twofold: first, that a process is supposed to "wake up" only if at least one of the signals from the sensitivity list changes states (which I guess, most of us had learned), but also that a process will always "execute" at least once, since the implicit "wait" is at the end of the process. This part is - AFAIK - a lot less known.When reading the standard further, you get to understand that MANY of the VHDL features were designed with only simulation in mind (it talks about how things are supposed to be simulated throughout the standard, but very little if at all about how things are supposed to be physically implemented.) I guess this has historical roots, but is IMHO a weakness of the language. This is, after all, a "hardware description language", not just a simulation language. Thing is, most of us more or less "know" what features are not "synthesizable", or at least we think we know. But if you strictly stick to the standard, actually most features of the language, even the basic ones we use all the time for synthesis, are not "synthesizable" per se strictly speaking, as their implementation is not clearly defined.Back to the "process", as someone said, sensitivity lists, in the general case, are not necessarily synthesizable, even though there are many simple cases for which they are (and, as we already said, would be by inferring latches for omitted signals). Thinking about it with some hindsight, I personally think the "process" is a construct that is something more software-oriented than hardware-oriented. As there is the notion of "waking up" a process on some arbitrary condition, it is a very general construct that doesn't always map - at least easily - to a simple logic structure hardware-wise. The "process" is thus more a pure simulation construct than one to describe hardware. Yet we use it all the time to describe hardware. This is kind of a discrepancy from the language POV. But as I noted while reading the standard, I personally find it a bit ambiguous when it comes to actually describing hardware.So that explains why synthesis tools usually take liberties? But strictly speaking, synthesis tools that just ignore sensitivity lists are NOT conforming to the standard.Now as I expressed earlier, when using the language for digital design, it usually doesn't really matter. Back to sensitivity lists for instance, as I mentioned earlier, I've seen very few (if at all) designs (outside of just theoretical courses) in which some signals were voluntarily omitted from the sensitivity list. That's likely why VHDL-2008 introduced the "all" keyword - out of pragmatism.To sum it up, sensitivity lists as a feature are a peculiarity of the language and are more than questionable for any practical use IMHO (outside of pure simulation), but that was a decision made a long time ago. The "all" keyword is a sign that this was acknowledged as a pecularity, but of course kept as is for backward compatibility reasons.Now I better understand the whole idea, but I still recommend *against* voluntarily omitting signals in sensitivity lists, in the sense that would infer implicit latches. Not just because that wouldn't work when synthesized with many tools, but also because it makes code harder to understand IMHO.

author: rstofer
author_rank: Super Contributor
timestamp: October 21, 2020, 04:36:16 pm
content: Quote from: emece67 on October 21, 2020, 10:23:34 amDuh! I completely missed it! My bad...

author: rstofer
author_rank: Super Contributor
timestamp: October 21, 2020, 04:53:56 pm
content: Quote from: SiliconWizard on October 21, 2020, 04:11:32 pmIt certainly has!QuoteI thought latches were created for outputs that weren't defined under all conditions. Are they really created for inputs omitted from the sensitivity list?Apparently, Vivado supports some subset of VHDL 2008 but I couldn't get it to acceptCode:process(all)This is probably a user error!

author: SiliconWizard
author_rank: Super Contributor
timestamp: October 21, 2020, 05:07:26 pm
content: Quote from: rstofer on October 21, 2020, 04:53:56 pmI hope what I wrote was clear.As we saw (and/or know), most simulators seem to be standard-compliant regarding this. But most synthesis tools are NOT compliant, and ignore sensitivity lists, so obviously they won't infer any latch, or anything else for omitted signals. Simulators will of course implement this any way they see fit, as long as they do NOT react to a signal change if it's not in the list.Now if that part was clear, your question is still interesting.Let's take this same "A and B" example. If both A and B are in the list, that's pretty obvious, this just yields an AND gate.But with one of A or B omitted in the list (say B), let's think about how this could be implementedhardware-wiseif the implementation had to bestandard-compliant(just remember this is just hypothetical, as most synthesis tools I know of again ignore sensitivity lists). The output C should not change if only B changes, but should if A changes. Not just that, but the process has to execute at least once, so the output C must still be initially "A and B", with the states of A and B at the beginning (beginning of what, if we deal with hardware? Power-on?)So let's not consider the initial state for now, only the following events. One possible way of implementing it I can think of would be to use a dual-edge FF to both detect the state change of A, and hold the output value if A doesn't change. Something like:(A AND B) -> C'Delay(A) -> A'A' -> clock input of dual-edge FFC' -> data input of dual-edge FFdata output of dual-edge FF -> CDoes that look clunky? Yes. The Delay would add a small delay to make sure (A AND B) has propagated through the AND gate when it's registered upon a state change of A.I have just given that a short thought, so there may be other ways to achieve the same - possibly simpler ways, or not requiring compensating for propagation delays. If you can think of something, please share! (Cool little exercise.)Now to take into account the initial states, we need to add a way to force registering C' at - say - power-on.We begin to understand why synthesis tools do not even bother.Quote from: rstofer on October 21, 2020, 04:53:56 pmXilinx tools are still notoriously behind in terms of VHDL-2008 compliance. That really sucks.Funnily enough, in ISE (that I sill use occasionally for Spartan 6 - based designs), there was NO support of VHDL-2008, but the tools still detected VHDL-2008 features and just told you they were unavailable. So this is progressing.

author: rstofer
author_rank: Super Contributor
timestamp: October 21, 2020, 07:28:12 pm
content: Part of MY problem is that I have never done much simulation. The simulator wasn't free with earlier versions of ISE and I would have had to go the Linux route to use that simulator. So, I just gave it a pass.The simulator is free with Vivado but I still don't spend much time with it. I think my projects are just too trivial to bother with simulation. The errors I have found with synthesis are far too deep in the structure to get much help from simulation. The right answer, of course, is to design for simulation and hope synthesis works out. Where's the fun in that! I want to see blinking lights!The one block of code I should simulate is my non-restoring division of signed integers. I copied the microcode ideas from a textbook without really understanding how it works. The good news is that it worked right out of the keyboard.

author: emece67
author_rank: Frequent Contributor
timestamp: October 21, 2020, 09:01:39 pm
content: .

author: SiliconWizard
author_rank: Super Contributor
timestamp: October 23, 2020, 04:26:49 pm
content: Actually, the subset of VHDL that is potentially synthesizable is not that small IMO (but let's not nitpick about what small means - I'm just saying this because I have seen many people shy away from some useful language features that are perfectly fine for synthesis in the general case.) Main problem is, as you said, that most vendors tend to use slightly different subsets depending on the underlying hardware (which is understandable but could make things hard for designers.)So yes, read the docs for your synthesis tools.This "process" thing, with their sensitivity lists, I think, was interesting, because it's one of the VHDL features that is not necessarily intuitively non-synthesizable. So that was interesting IMO as a reminder.The small "exercise" I showed about how to possibly implement them in hardware was also interesting (at least to me!), and was an attempt to show why that would be hard to synthesize (to the point of not making much sense). I'd be curious what others could come up with, if anyone feels like trying.

author: emece67
author_rank: Frequent Contributor
timestamp: October 24, 2020, 01:58:41 pm
content: .

author: SiliconWizard
author_rank: Super Contributor
timestamp: October 24, 2020, 02:51:31 pm
content: Yep, nice find. (You cheated a bit by letting the synthesizer figure out some hardware implementation for you, but at least you know it's synthesizable.)In my approach, I didn't think of optimizing for the specific logic operation (AND), and provided a solution for C being any combination of A and B, so I think my solution was more general. Optimizing is of course clever, but it shows that the problem can't be solved with such simple hardware in the general case.Whereas your goal was likely to eventually provide the hardware structure, I'll give a bit of caution though, related to synthesis for FPGAs specifically: using asynchronous signals to "clock" some process (so eventually as a clock for FF's) is in general a bad idea. The internals of FPGAs, notably FF's, are optimized for clocks with some minimal characteristics (skew for instance). So, using this kind of process is likely to give all kinds of issues if 'a' is a random asynchronous signal, and usually timing analysis will also not be conducted properly, leaving you in the unknown. Another issue is that if 'a' and 'b' are not on the same clock domain, the you're going to have metastability issues. So I certainly don't suggest doing this on FPGAs.

author: emece67
author_rank: Frequent Contributor
timestamp: October 24, 2020, 02:52:11 pm
content: .

author: emece67
author_rank: Frequent Contributor
timestamp: October 24, 2020, 02:58:13 pm
content: .

author: SiliconWizard
author_rank: Super Contributor
timestamp: October 24, 2020, 03:04:17 pm
content: Certainly. I think the warnings we express are worthwhile though for people not aware of the pitfalls and tempted to do this kind of stuff.As to the challenge, my point, beyond a fun challenge thing, was to illustrate why sensitivity lists were usually ignored by synthesis tools.