cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
Highlighted
Adventurer
Adventurer
9,057 Views
Registered: ‎07-14-2010

Coding style

Jump to solution

Hi all

        what is the diffrence between a design with one process inside one entity, and  the same design but with several processes for the same entity?

0 Kudos
1 Solution

Accepted Solutions
Highlighted
Visitor
Visitor
11,647 Views
Registered: ‎07-07-2009

Coding with multiple processes is typically an old-school approach to a structural/RTL driven design. If you can identify your blocks and pipeline stages and you want to count logic levels while designing your code, you are best off with multiple processes and asynchronous signal assignments.

 

Single or dual process designs lead to a more behavioral kind of design. If you care more about  the state transitions of your entity as a whole, you will probably use this development strategy.

 

Read more about the advantages of dual process design here: Gaisler Method

View solution in original post

0 Kudos
9 Replies
Highlighted
Explorer
Explorer
9,044 Views
Registered: ‎09-20-2007

Generally coding with multiple process, improves readablity, ease the debugging and maintainance. You can't code 1000 lines in one process. you will go nuts.

FPGA freak
0 Kudos
Highlighted
Visitor
Visitor
11,648 Views
Registered: ‎07-07-2009

Coding with multiple processes is typically an old-school approach to a structural/RTL driven design. If you can identify your blocks and pipeline stages and you want to count logic levels while designing your code, you are best off with multiple processes and asynchronous signal assignments.

 

Single or dual process designs lead to a more behavioral kind of design. If you care more about  the state transitions of your entity as a whole, you will probably use this development strategy.

 

Read more about the advantages of dual process design here: Gaisler Method

View solution in original post

0 Kudos
Highlighted
Xilinx Employee
Xilinx Employee
9,022 Views
Registered: ‎01-03-2008

I read the PDF link that you had posted on and personally I wouldn't suggest this design style due to the cumbersome nature of records, the high chance of creating latches due to a missed variable in sensitivity list, lack of readibility for others and the high potential for synthesizers to not optimize logic into the register functions (clock enable, set, reset).

 

The author went through a number of steps purporting  to show why his method was better than others, but he left out a direct comparision between the "data flow" or "one process" method and the "two process" method for the counter example.  If this had been included it would have looked like this.

 

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;


entity count8 is
port (
  clk : in std_logic;
 load : in std_logic;
count : in std_logic;
    d : in std_logic_vector(7 downto 0);
    q : out std_logic_vector(7 downto 0));
end;


architecture oneproc of count8 is
signal r: std_logic_vector(7 downto 0);

begin
  process(clk)
  begin

    if rising_edge (clk) then
      if load = ’1’ then

        r <= d;
      elsif count = ’1’ then

        r <= r + 1;
      end if;

    end if;
  end process;

  q <= r;

end;

The architecture section is only 15 lines in comparison to the 23 lines in the "two process" example and IMHO is easier to read and understand.

 

When I first started learning VHDL I did think that records were very attractive especially for defining a bus with control signals.  However, the support for records by synthesizers was almost non-existant at the time and the code could not be easily converted or used with Verilog code.   While synthesizers have improved the support for records over the years so that it is now usable, using records in the way that the author suggests I would not recommend to anyone due to the extra complexity and increase chance for errors.

 

------Have you tried typing your question into Google? If not you should before posting.
Too many results? Try adding site:www.xilinx.com
0 Kudos
Highlighted
Visitor
Visitor
9,016 Views
Registered: ‎07-07-2009

 


@mcgett wrote:

I read the PDF link that you had posted on and personally I wouldn't suggest this design style due to the cumbersome nature of records, the high chance of creating latches due to a missed variable in sensitivity list, lack of readibility for others and the high potential for synthesizers to not optimize logic into the register functions (clock enable, set, reset).

 


 

My experience is that hardware designers have a hard time learning to abstract their interfaces and states for use as records. At the first place, that is not VHDL record’s fault. Opposed to your feelings, this design approach has proven very beneficial to anything beyond an 8 bit adder. First, using records for interfaces and registers avoids latches once and for all, instead of having a “high chance of creating latches“. Furthermore, as variables are never part of the sensitivity list, one cannot miss them. Even if you meant signals missed on the sensitivity list, you cannot miss a signal, because you typically have just two signals on the sensitivity list, that is R – the state record –, and your input signal which is typically a single record as well, if you follow the coding style.

 

Once familiar with Gaisler’s coding style, readability is greatly improved. Certainly, this approach is not magical in any way, so anyone can produce unreadable code in that style at will. Our experience is that optimization is not harmed by this coding style.

 

 


The author went through a number of steps purporting  to show why his method was better than others, but he left out a direct comparision between the "data flow" or "one process" method and the "two process" method for the counter example.  If this had been included it would have looked like this.

You totally miss the point. Data flow is not equal to single process, contrary it’s the multi process approach which is typical for a textual representation of a schematic/structural design. At the lower levels of design entry, structural design makes analysis/review very impractical.

 

The difference between one process and two processes in the way Gaisler suggests it, is that one process can be used only for Moore-type state machines. If you have any combinatorial output, you have to put the commands in concurrent assignments – counting as separate processes – or into a declared second process. This would make the code split into multiple functional parts: Here you set the new state based on input and current state, there you set your outputs based on state and probably input. These separate places are hard to maintain for larger designs. It is better to split it into a pure sequential and a pure combinatorial process, then all the logic is together in the combinatorial process.

 

Don’t count lines for such a simple entity. Hello World in Basic is shorter than in C, too, now what?

 

 


When I first started learning VHDL I did think that records were very attractive especially for defining a bus with control signals.  However, the support for records by synthesizers was almost non-existant at the time and the code could not be easily converted or used with Verilog code.   While synthesizers have improved the support for records over the years so that it is now usable, using records in the way that the author suggests I would not recommend to anyone due to the extra complexity and increase chance for errors.

 


 

You are right saying that support for VHDL records had to improve over time. But: They are usable now, so don’t stick with history. We started using them in 2004 and had problems with some synthesis tools, but these problems are gone.

 

Look at some real code from GRLIB, pick the LEON3/GRLIB source code. E.g. look at something of modest size: lib/gaisler/pci/pci_target.vhd (mind the GPL, though). The entity has 4 signal records (apart from clk, reset), abstracting all the interface details away. The architecture defines three more records, used in two separate combinatorial processes and two sequential processes – it’s a multi-clock design. Look at the instantiation of ahbmst0: A one-liner. Look at the code in the combinatorial process: Just variable assignments, so it’s easy to follow what’s going on and what part of the state is updated to what value.

 

You can do that in any standard coding style as well, and old-school hardware engineers will love you as they can count bits everywhere and can avoid visiting packages for record definitions, but the code will be less readable and much larger. Consider that you will have to update the code, the interfaces, sometime in the future. Now such an improved coding style pays back.

 

0 Kudos
Highlighted
Historian
Historian
9,004 Views
Registered: ‎02-25-2008

 


@mwaechter wrote:

Coding with multiple processes is typically an old-school approach to a structural/RTL driven design. If you can identify your blocks and pipeline stages and you want to count logic levels while designing your code, you are best off with multiple processes and asynchronous signal assignments.

 

Single or dual process designs lead to a more behavioral kind of design. If you care more about  the state transitions of your entity as a whole, you will probably use this development strategy.

 

Read more about the advantages of dual process design here: Gaisler Method


 

I tend to use as many (or as few) processes as necessary to make the design readable and maintainable. Things that are "closely related" are all in the same process. Things that are unrelated tend to be in different entities.

 

As for the two-process (combinatorial and synchronous), my bias against them is well-known. Except for the smallest designs, they are unmaintainable, prone to errors and can lead to poor results.  Someone on this forum posted a question about lousy synthesis results, and he was coding a counter as part of a state machine, and that state machine used the two-process model and he was updating the counter in the combinatorial process!

 

I agree that there are cases where you need the combinatorial output of a state machine, bu these cases are, at least in my experience, rare and can be worked around by understanding the pipelined natural of synchronous state machines.

 

As for Mealy vs Moore machines -- who the hell cares whose name is attached? Design logic that meets the system requirements and don't try to force the logic to meet an academic construct.

 

As for records: I like records. It's just that many designs don't lend themselves to their use.

----------------------------Yes, I do this for a living.
Highlighted
Visitor
Visitor
8,975 Views
Registered: ‎07-07-2009

bassman59 wrote:

As for the two-process (combinatorial and synchronous), my bias against them is well-known. Except for the smallest designs, they are unmaintainable, prone to errors and can lead to poor results.  Someone on this forum posted a question about lousy synthesis results, and he was coding a counter as part of a state machine, and that state machine used the two-process model and he was updating the counter in the combinatorial process!


You obviously never followed a two-process design methodology. Properly followed, code is much more maintainable and less prone to errors. Whatever a “good result” or a “poor result” is for you – for me a good result is if the functionality is correct and the timing is not (yet) met, and a poor result is a design that is hard to review, fix and expand but meets the timing. The former is fixable, the latter typically not. One cannot do everything in one cycle, and it’s the art of system design to find a balance between non-obvious pipelining and a 1:1 implementation of the requirements.

 

To be more precise: If I have to choose between an adder in one state of the state machine that has not the perfect performance, and a structural approach of a separated counter process with fast timing, which has to be controlled by the state machine, I go for the former. Not only do synthesis tools get better every month, but functional code beats structural code in terms of maintainability and correctness.

 

Your bias is based on what observation? That some designers forget to set the output signals of the combinatorial process in all cases? That’s a designer’s fault, and it can be neglected if using records.


@bassman59 wrote:

I agree that there are cases where you need the combinatorial output of a state machine, bu these cases are, at least in my experience, rare and can be worked around by understanding the pipelined natural of synchronous state machines.


For someone working on a 250++ MHz design, this statement is okay as a general rule. For someone working in the 125-- MHz range, it is of no practical use. Yes, it’s good design practice to design an interface in a way that the outputs are registered, this avoids combinatorial loops and not meeting the timing. But it’s not necessary in the general case.

 

E.g. consider an entity that has to be split into multiple, for whatever reason. If one introduces registered outputs everywhere, this will introduce waitstates on all these interfaces.

 

0 Kudos
Highlighted
Historian
Historian
8,960 Views
Registered: ‎02-25-2008

 


@mwaechter wrote:

bassman59 wrote:

As for the two-process (combinatorial and synchronous), my bias against them is well-known. Except for the smallest designs, they are unmaintainable, prone to errors and can lead to poor results.  Someone on this forum posted a question about lousy synthesis results, and he was coding a counter as part of a state machine, and that state machine used the two-process model and he was updating the counter in the combinatorial process!


You obviously never followed a two-process design methodology. Properly followed, code is much more maintainable and less prone to errors. Whatever a “good result” or a “poor result” is for you – for me a good result is if the functionality is correct and the timing is not (yet) met, and a poor result is a design that is hard to review, fix and expand but meets the timing. The former is fixable, the latter typically not. One cannot do everything in one cycle, and it’s the art of system design to find a balance between non-obvious pipelining and a 1:1 implementation of the requirements.

 


 

 

Yes, when I was first learning VHDL and back when synthesis tools (Synopsys FPGA Express? Ugh) required a two-process state machine description, I had to write state machines in that manner. And there's no doubt that keeping two signals around for each actual signal (one the combinatorial result driven in the state decoder process, the other the registered version in the synchronous process) just makes housekeeping more difficult.

 

Finally, the necessity of ensuring that each and every combinatorial signal is assigned in the combinatorial state-decoder process means that the description of the machine gets overly complicated. With a single synchronous process, you need only assign those signals which actually change in a particular state.

 

As for what defines a good result: it's one that the logic is functionally correct AND meets timing AND is written well enough (quality comments, informative signal names, clear code) for someone else to understand and review. It might be some beautiful code but if it doesn't meet timing, it's worthless.

 

 

 


 

To be more precise: If I have to choose between an adder in one state of the state machine that has not the perfect performance, and a structural approach of a separated counter process with fast timing, which has to be controlled by the state machine, I go for the former. Not only do synthesis tools get better every month, but functional code beats structural code in terms of maintainability and correctness.

 

 

i've typically included counters and adders as part of the state-machine description -- when in a particular state, update an accumulator, increment a counter, etc, with the assignment to the accumulator or counter in that state. Obviously this makes the most sense in terms of understanding the state-machine flow.

 

My current design was not meeting a somewhat stringent timing requirement in a couple of paths, and when I looked at the synthesis report for the failing paths, I noticed that counters were being synthesized as adders, and obviously a full 20-bit adder is slower and larger than a 20-bit counter. For these failing paths I recoded with a standalone fast counter with count enable and count reset signals asserted as needed by the state machine and all was well.

 

Did I prefer to do this? No, the previous description is more obvious to someone new to the code. But when the choice is either failing to meet timing, or modifying the code, whatchagonnadoooooo?

 

 

 


 

Your bias is based on what observation? That some designers forget to set the output signals of the combinatorial process in all cases? That’s a designer’s fault, and it can be neglected if using records.

My bias is that it makes the code overly complicated, for the reasons stated above.
And using records won't help. Records are excellent for simplifying the interface but consider the case of a processor bus with address, data in, data out and various controls. Obviously this can be abstracted using a record. But not all signals are updated in every state, and as such in the combinatorial process you still must assign the unchanged members.

 


 

E.g. consider an entity that has to be split into multiple, for whatever reason. If one introduces registered outputs everywhere, this will introduce waitstates on all these interfaces.

If this is indeed the case, then the interface can be designed to accomodate and/or eliminate wait states. It's not as if this is a new and unique problem.

 

----------------------------Yes, I do this for a living.
0 Kudos
Highlighted
Visitor
Visitor
8,957 Views
Registered: ‎07-07-2009

 


bassman59 wrote:

Yes, when I was first learning VHDL and back when synthesis tools (Synopsys FPGA Express? Ugh) required a two-process state machine description, I had to write state machines in that manner. And there's no doubt that keeping two signals around for each actual signal (one the combinatorial result driven in the state decoder process, the other the registered version in the synchronous process) just makes housekeeping more difficult.

 

Finally, the necessity of ensuring that each and every combinatorial signal is assigned in the combinatorial state-decoder process means that the description of the machine gets overly complicated. With a single synchronous process, you need only assign those signals which actually change in a particular state.


First of all, sorry for my offense in the last post. You are certainly right: Like you describe, before failing another time with a dataflow-like development model with n++ processes, I started around Y2K with a 1+n process model as well: one process making *_reg from a myriad of next_*_reg signals at edge time, and 1 or multiple combinatorial processes building next_*_reg from inputs and *_reg. And your observations about that method are certainly right: Either you forget to assign *_reg or next_*_reg, and latches are your first enemy.

 

But: That’s all gone when using records, and that is what Gaisler describes: Put all your *_reg signals into a single register record (named: R), then do the same with the next_*_reg signals and name it Rin. Then the code is really maintainable. Look at the following template we use:

 

 

entity ent is port(clk    : std_logic;
                   areset : std_logic;
                   inp    : TYPE_TO_ENT;
                   outp   : TYPE_FROM_ENT);
end entity ent;

architecure behaviour of ent is type TYPE_REG is record -- your register elements go here, like:
  end record; constant REG_Reset : TYPE_REG := ( -- that's the register reset value.
  ); signal R, Rin : TYPE_REG := REG_Reset; begin SEQ: process(clk, areset) begin if areset = '1' then R <= REG_Reset; elsif rising_edge(clk) then R <= Rin; end if; end process SEQ; COMB: process(R, inp) variable V : TYPE_REG; begin V := R; -- your logic goes here

    outp <= R.outp;
    Rin <= V; end process COMB; end architecture behaviour;

 

 

That’s quite some LOC for an architecture that does nothing yet, but let’s walk through some typical tasks in development.

 

First, add a register: Just put it in the record, move down a couple of lines and give it a reset value. Done, that’s all. To give the register some life, you just have to add code to give it new values whenever necessary, just modify the respective element of V in the COMB process.

 

Look for all the things you did not have to do: Add the register to the SEQ process, add it to the sensitivity list of COMB, give it an explicit default value in the COMB process to avoid latches.

 

Add an input signal: Go to the package defining your input record type, add the signal there, done. Just add code to make use of the signal. No changes on the entity, none on the the sensitivity list.

 

Further benefit arises from (proper) use of the variable V in the COMB process: It starts off as the current register contents, a.k.a. the STATE. Further down the process you modify that variable until finally the variable serves as the value to give to Rin, the updated value of the register at the next edge. Rin is not used in the COMB process except for that single assignment at the bottom. Having a variable for all intermediate calculations allows to take immediate use from precalculated results making it easily DRY (don’t repeat yourself).

 

Assigning an output is equally easy, with added benefit: You can assign the output a value from a register (synchronous or quasi synchronous output) or from a variable (combinatorial output) without added complicated code constructs. It’s all in there.

 

Adding a signal to the output is like: add it in the record definition of the package, give it a value down in COMB. No change in the entity.

 

Summary: This coding style comes at an overhead given above, plus the packages for interfaces, plus a wrapper on the top level. But it gives you a code base where you don’t have to worry about uninitialized signals and forgotten register updates. And it’s maintainable, and it allows to use intermediate results from V without additional variable declaration.

 

The price: Learn some software-level skills, learn to abstract functionality, learn to use variables right (you run out of timing margin quickly if you don’t know what you are doing), learn to abstract the interfaces in packages.

 


As for what defines a good result: it's one that the logic is functionally correct AND meets timing AND is written well enough (quality comments, informative signal names, clear code) for someone else to understand and review. It might be some beautiful code but if it doesn't meet timing, it's worthless.

 


 

If it’s maintainable, it can be made into a design that meets timing, in one way or the other.

 

If it’s not maintainable you cannot fix or extend the functionality. Then it’s broken by design, and no-one cares about its property of a perfect timing.

 


i've typically included counters and adders as part of the state-machine description -- when in a particular state, update an accumulator, increment a counter, etc, with the assignment to the accumulator or counter in that state. Obviously this makes the most sense in terms of understanding the state-machine flow.

 

My current design was not meeting a somewhat stringent timing requirement in a couple of paths, and when I looked at the synthesis report for the failing paths, I noticed that counters were being synthesized as adders, and obviously a full 20-bit adder is slower and larger than a 20-bit counter. For these failing paths I recoded with a standalone fast counter with count enable and count reset signals asserted as needed by the state machine and all was well.

 

Did I prefer to do this? No, the previous description is more obvious to someone new to the code. But when the choice is either failing to meet timing, or modifying the code, whatchagonnadoooooo?


So we agree in general: Try to code as beautiful and maintainable as possible until you stumble over bad timing.Then do whatever is appropriate to fix it, but try to adhere to coding standards.

 

For designs with challenging space and timing, there are no good rules anyway. You might be forced to go down to LUT level in critical paths anyway.


My bias is that it makes the code overly complicated, for the reasons stated above.
And using records won't help. Records are excellent for simplifying the interface but consider the case of a processor bus with address, data in, data out and various controls. Obviously this can be abstracted using a record. But not all signals are updated in every state, and as such in the combinatorial process you still must assign the unchanged members.

Look at the code above, records are the key to that design paradigm.

 

The variable V and later on Rin is preset to the current state, so there is nothing one has to do for making some record elements keep their value. Just don’t modify V, and the record element will not even bat.

Give it a try once. You will love it.

0 Kudos
Highlighted
8,807 Views
Registered: ‎07-23-2010

Hi

I have an entity with inout port and instantiated this entity in the
test bench to send some test waveforms to it. Even though I assign a
value to this port in the test bench it always gets "U". The debugger
shows that the assignment istatement s getting executed but the value
remains "U". When I change the port type to "in" in the inner entity
and leave the port type as inout in the top level entity (test bench)
it works fine. Why? I would really appreciate if anyone could clarify
this behaviour.
plz reply it is urgent.

 

thanks in advance

 

Anupam

anupam.mandlas@gmail.com

Anupam Mandlas
(Thanks & Regards)
0 Kudos