combinational loops

On 10/2/2013 11:04 AM, Andy wrote:
On Wednesday, October 2, 2013 1:30:57 AM UTC-5, rickman wrote:
The local/global thing is not so important in HDL the way most
people use it. Very few designers put their entire design in
one process with a large number of procedures. Rather the
modularize by using multiple processes to describe the hardware
they are designing a section at a time. When using signals they
can only be driven validly by one process, so you get warnings,
no, actually errors, when a signal is driven by multiple
processes.

Encapsulation is often less about which code can modify/drive a variable/signal than it is about which code can read it, depend on it, and quit working if it is modified.

For example, say I have a couple of processes in an architecture. One uses a counter to step through some data, and it is not important which order it is processed, so the author decides to use an up-counter.

Another process that can see that counter, uses that same counter's value to control something else within it, but it depends on the implementation decision in the first process to use an up-counter.

What happens if the first process is modified to optimize the counter by converting it to a down-counter? If the counter had been a local variable, then there would be nothing outside that process that could be directly dependent upon its behavior, and changing the direction of the counter would have no impacts elsewhere. But if it is a signal, then the entire architecture has to be understood to make sure that any changes to the counter's behavior do not have an unforeseen impact.

That makes no sense to me. Whether the counter implementation affects
other logic depends on whether the other logic uses the value of the
counter. Why wouldn't you know this if a signal is used?


> Sometimes shared counters are a good thing; great, make them signals so that it is known that it is intended to be shared. Otherwise, keep it local so that it cannot be shared. Better yet, if the counter is shared among only two of the processes, put those two processes in a block, and declare the counter signal locally within the block. This protects the counter from dependencies in the other processes in the architecture.

Sometimes??? A counter is part of a design, created by a designer. If
the counter is intended to be shared it is shared, otherwise it is not.
You are talking about a totally different situation than the OP is
talking about using procedures.


> Al's solution of passing state variables around between different processes is another example. Generally, state variables are pure implementation, and should not be shared. A better solution might be to define the interfaces between the procedures as explicit control (start) and status (finished) parameters, so that one procedure can be modified to change the way its FSM works, while maintaining the interface signals' functionality, and the other procedures would not be impacted.

I don't follow exactly. My problem with alb's implementation is that
the order of the procedure calls affects the values read by each
procedure, all within *one process*. That has got to be clumsy if not
impossible to make work. Or maybe I read his example wrong. If they
are separate processes then they communicate by signals, no?


The other way the global/local thing is not an issue is because
most designers don't even use a single entity. Each entity has
its own set of signals making them all local.

If designers are using a single process per entity, then yes, there is no practical difference in scope between a signal and a variable. Most designers use multiple processes per entity, so there is a difference for most designers.

Yeah, but I don't buy into the idea that using signals creates problems
from lack of isolation. Modularization allows isolation. I use
entities, you want to use processes, I don't see much difference. I put
different state machines into different processes for clarity, I think
you (or alb) are putting different state machines into different
procedures in the same process. But I can't see how this woudl work the
way he shows it with one variable for each state variable. With no
isolation between the present state and next state I can't see how to
code separate procedures.


I have never wanted to add combinatorial logic to a clock process,
I keep the combinatorial logic separate because it is... well,
separate. This is really an issue of what you are comfortable with.
I don't care for what are fairly subtle usages to create such logic
in a clocked process. It will confuse many designers and is subject
to error.

This is a matter of how most designers are taught HDL: by examples of what kind of code structure creates what kind of circuit, and then just write concurrent islands of code that generates those circuits, and wire them up (in code).

Sure, it is important to know what kind of circuit will be created from a certain piece of code. But the problem is, the synthesis tool is analyzing the behavior of the code, not the structure, and inferring the circuit from that behavior. The problem is that designers are taught that "code that looks like this" creates a register, and "code that looks like that" creates a combinatorial circuit.

Designers should be taught that "code that BEHAVES like this" creates a register, etc. It is amazing to me how many different approaches to avoiding latches in RTL are based on a fundamental misunderstanding of the behavior that infers a latch (which is very similar to the behavior that creates a register).

Design productivity can only progress so far by continuing to focus on describing the circuitry (gates and registers). To improve design productivity, we have to start designing more at the behavioral level (functions, throughput and latency). Why do you think high level synthesis tools (that can synthesize untimed models in C, etc.) are becoming so popular? I don't think it is the language as much as it is the concept of describing behavior separate from throughput and latency (those are provided to the HLS tool separately), and getting working hardware out the other end.

I don't agree really. RTL doesn't describe literal registers and gates.
It describes behavior at the level of registers. If you need that
level of control, which many do just so they can understand what is
being produced, then there is nothing wrong with RTL. Abstractions
create obfuscation with the hardware produced. I often have trouble
predicting and controlling the size and efficiency of a design. What
you are a describing would likely make that much worse.


Isn't this the same as 2? No, wait, you can't output variables, so you have to use signals to output anything from a clocked process, no?

Of course, any output from a process must be a signal. But for that signal to be a combinatorial function of registered values in the same process, the registers must be inferred from variables. If you use a signal for the register in the process, you have to use a separate process for the combinatorial function.

I don't have a problem with that although I would like to learn the
technique better, I might end up liking it. I have seen it, but never
used it. I'm usually too busy designing the circuit in my head to worry
about the coding really. I just don't see problems with the coding.
I'd like to be better at test benches though. There I sometimes code
purely behaviorally. But only when timing is not such an issue.


Intuitive is a nice word for "it matches my bias". If your background
is sequential code like C, then yes, you will likely feel more
comfortable with variables. I learned such languages, but when I
learned HDL I was taught to describe the hardware (after all, were
were hardware designers learning to use an HDL in place of schematics)
and processes and signals are natural tools for that.

Perhaps so, but my background is hardware design (analog and digital circuit cards and later, FPGAs), not SW. My first few XC3090 FPGA designs were by schematic entry. I did not immediately embrace HDL design (I actually lobbied managment against it), but once I tried it, I was hooked. My first VHDL training was for simulation, not synthesis, so maybe that too has influenced the way I use VHDL even for synthesis. Over the decades I have seen first hand the value of designing the desired behavior of a circuit, rather than describing the circuit itself. There are times where performance or synchronization still require focus on the circuit. But even for those, I tend to tweak the behavior I am describing to get the circuit I need (using RTL& Technology viewers in the synthesis tool), rather than brute-force the circuit description.

There is also the issue that I don't use FPGAs and HDL every day, or any
other tool for that matter. I move around enough that I want to learn a
way to use a tool and then tend to stick with it so I don't have to keep
relearning. The tools change enough as it is.


I have never had an issue with running time in a simulation that didn't
involve the amount of data being recorded rather than the simulation
itself. But then I don't design huge FPGAs, I tend to do small designs,
about the same complexity as you might find on a small MCU. I have a
different approach to HDL use. My background is likely very different
from yours. Also, my approach is widely used by the industry and so it
well debugged in terms of the tools supporting it. I seem to have good
productivity and my designs are solid (as long as I don't make newbie
mistakes like I did recently where I forgot the synchronizing FF on an
input).

Among causes for slow simulations, using signals where variables would work is pretty low on the list of big hitters. But using lots of combinatorial processes is a much bigger hitter (gate level models are the extreme example of this). Some simulators can merge execution of processes that share the same sensitivity list, saving the overhead of separately starting and stopping the individual processes. Combinatorial processes rarely share the same sensitivities, so they are rarely combined, and the performance shows it.

So different horses for different courses.

Of course!

Andy

Well, like I said, next design I do I will try the combinatorial output
from a clocked process to see how I like it. Not sure when that will be.

--

Rick
 
On Wednesday, October 2, 2013 9:21:12 PM UTC-5, KJ wrote:
This is a weak argument. If a signal that is a counter can be 'seen'
and used for some other than the original intended purpose, then so
can a variable...you simply put the 'other' code inside that same
process and watch things not work in the same way as when the counter
is a signal. Pretending you have some sort of firewall here doesn't
make it one.

If you really believed this, why bother with more than one entity/architecture containing a single process in the entire design? That way you can easily find all your signals and see how they are used, all in one file!

Assuming that not all of the code is in one process per architecture, but that the processes are limited in size and in purpose, then understanding where and how a variable is used is much simpler than for a signal under the same circumstances.

If someone later wants to use a variable for some other purpose, then they have to add code to its process. This is much more likely to be noticed, and if not appropriate, flagged in review (speaking of design process issues....)

There is no firewall to prevent people from doing stupind things, only from doing them in ways that are difficult to catch in review.

You do raise a good point about capturing waveforms of variables. It sounds like a tool issue to be solved. In the meantime, I might suggest local signals declared in a block statement surrounding each process.

There have also been suggestions in the past to amend the standard to allow signals to be declared locally in a process (and therefore not visible outside the process). That way, if you want the suspended update semantics, you can have them without having to give up on encapsulation (or jump through more hoops to keep it).

Good code design standards (one of which is encapsulation) are intended to avoid many problems before compilation, rather than waiting for a testbench to be written to expose them in simulation, or even worse, system integration.

Andy
 
Hi Rick,

On 03/10/2013 05:24, rickman wrote:
[]
I do not see what's wrong with the second procedure needing to be aware
of the changed variable. When the second procedure runs all previous
variables have been updated accordingly and depending on their values
the procedure runs accordingly.

That *is* the problem. The second procedure sees the next state rather
than the current state.

That's the reason why the order of procedures matter. I let the higher
FSM (the one that supposedly 'controls' everything) be the last to start
and then respectively the second in rank and so on...

To infer a register simply use the variable before it is assigned.

I thought they were all registers? If not, they aren't state variables.

They *are* registered. What I meant is that if you write this:

<code>
a := a + 1;
</code>

then 'a' is used before it is assigned, leading to a register. This is
what happens in my fsm procedure, whether the procedure has a single
case-switch which rolls over the state value, using it before assigning it.

[]
exactly the same here. In each individual FSM the inout mode in the
parameter list is for the state parameter that the FSM will run with.
Like in one process FSM, you do not need to formally separate the
'present state' and the 'next state'.

And therein lies the problem. When using a single variable like this,
if some state variable have been updated but not others, the FSM gets
very complex.

I don't get this. My state variable at each state depends on several
conditions, some of them been states of another FSM (like you would have
with a counter), where is the problem?

The isolated procedures for updating each state variable
in your FSM have to be aware of one another and the order in which they
are invoked. This greatly complicates the code and understanding of it.
I would find that to be impossibly difficult to use. I don't consider
this to be useful decomposition.

See the following snippet of code:

<code>
-------------------------------------------------------------------------------
procedure writ_read (
state_v : inout state_t;
mstate_v : in mstate_t;
nstate_v : in nstate_t) is
begin
case state_v is
when IDLE =>
if mstate_v = START_READ or nstate_v = START_READ then
state_v := READ_BGN;
elsif mstate_v = START_WRITE or nstate_v = START_WRITE then
state_v := WRITE_BGN;
end if;
when READ_BGN =>
state_v := READ_END; -- this is one clock cycle
nRD_v := not nRD_v;
when READ_END =>
state_v := IDLE;
nRD_v := not nRD_v;
DATA_v := DATA;
when WRITE_BGN =>
state_v := WRITE_END; -- this is one clock cycle
nWR_v := not nWR_v;
when WRITE_END =>
state_v := DONE;
nWR_v := not nWR_v;
when DONE =>
state_v := IDLE;
DATA_v := (others => 'Z'); -- release the bus
when others => null;
end case;
end procedure writ_read;
</code>

the 'awareness' you refer to is in the IDLE state case, where the 'if'
branch reacts on the others' state variables... Being the author of this
code I certainly cannot see the 'complication' that lies behind it, but
please advise on how to write it *more* clear than this [1].

[]
I've thrown Mealy and Moore definitions away long ago. I think about the
function, not a particular implementation.
I prefer to have registered outputs and in my case those outputs are
described directly in the appropriate states of the FSM, whether this is
Mealy or Moore I could care less with all due respect.

Yes, Mealy and Moore are not often used in a strict sense, but the point
is access to the *next* value of the state rather than the current
value. This lets you get registered outputs out on *this* clock edge
rather than having them wait a clock cycle.

'this' or 'that' clock cycle does not really matter to me, latency apart
I get the same throughput.
 
I forgot to add a note to my previous post.

On 03/10/2013 17:17, alb wrote:
the 'awareness' you refer to is in the IDLE state case, where the 'if'
branch reacts on the others' state variables... Being the author of this
code I certainly cannot see the 'complication' that lies behind it, but
please advise on how to write it *more* clear than this [1].

[1] I'm currently trying to work on a 'message passing' method which can
provide a cleaner interface and increase the level of encapsulation,
similar to what Andy referred to as 'explicit control (start) and status
(finished) parameters'.

I've followed a similar path in a C application for an embedded system
and the pay off was huge.
 
On Wednesday, October 2, 2013 9:21:12 PM UTC-5, KJ wrote:
This is a weak argument. If a signal that is a counter can be 'seen'
and used for some other than the original intended purpose, then so
can a variable...you simply put the 'other' code inside that same
process and watch things not work in the same way as when the counter
is a signal. Pretending you have some sort of firewall here doesn't
make it one.

If you really believed this, why bother with more than one entity/architecture containing
a single process in the entire design? That way you can easily find all your signals and
see how they are used, all in one file!

You're missing your original point which I was responding to with this comment.

Assuming that not all of the code is in one process per architecture, but that the
processes are limited in size and in purpose, then understanding where and how a
variable is used is much simpler than for a signal under the same circumstances.

That's an assumption and, unless the code is very fresh in your mind a poor one to make. Your original point was that making something a variable would somehow isolate an implementation change from rippling into some unintended operation. But if you change the implementation of signal or variable 'xyz', you need to search for every usage of 'xyz' in the entity (and beyond if 'xyz' is an entity output or affects an entity output) and insure you won't break something. Thinking that just because 'xyz' is a variable will in any limit the scope of that search is incorrect. You'll still have to do a text search on 'xyz'. If you're not doing that, you're simply hoping that the implementation change has no other unintended effects.

If someone later wants to use a variable for some other purpose, then they have to add
code to its process. This is much more likely to be noticed, and if not appropriate,
flagged in review (speaking of design process issues...)

I doubt it...but accept that you may have seen inappropriate usage of variables being flagged in a review where similarly inappropriate usage of a signal was not caught in a similar review.

There is no firewall to prevent people from doing stupind things, only from doing
them in ways that are difficult to catch in review.

Not sure what you think is a 'stupid' thing. You've already conceded that there may be appropriate usages that violate your statement.

Your suggestion was that it is somehow easier to search for 'xyz' in the process rather than the architecture. My point is that you have to do the search for 'xyz' and that you should use a tool. If the tool you're using is simply your eyeball scan of the code (or the eyeballs of a review team) then you're right, but if you're using your eyeball (and the eyeball of others) to scan you're using the wrong tool. You should be using the search function of your text editor. When you use that tool you'll get to the "xyz not found" with exactly the same effort regardless of whether 'xyz' is a signal or variable and regardless of the size of the architecture being searched.

You do raise a good point about capturing waveforms of variables. It sounds like a tool
issue to be solved.

It is a gripe of mine too, but it doesn't seem to be a tool issue that will go away soon.

In the meantime, I might suggest local signals declared in a block
statement surrounding each process.

Yep.


There have also been suggestions in the past to amend the standard to allow signals
to be declared locally in a process (and therefore not visible outside the process).
That way, if you want the suspended update semantics, you can have them without having
to give up on encapsulation (or jump through more hoops to keep it).

And a good suggestion at that.

Good code design standards (one of which is encapsulation) are intended to avoid many
problems before compilation, rather than waiting for a testbench to be written to expose > them in simulation, or even worse, system integration.

I agree completely here, but this is off on another tangent. We simply seem to disagree on whether the effort to check whether a change will have an unintended effect is any different when using the most appropriate tool for the job.

Kevin Jennings
 
Hi Kevin,

On 04/10/2013 03:10, KJ wrote:
[]
Your original point was that making something a
variable would somehow isolate an implementation change from rippling
into some unintended operation. But if you change the implementation
of signal or variable 'xyz', you need to search for every usage of
'xyz' in the entity (and beyond if 'xyz' is an entity output or
affects an entity output) and insure you won't break something.
Thinking that just because 'xyz' is a variable will in any limit the
scope of that search is incorrect. You'll still have to do a text
search on 'xyz'. If you're not doing that, you're simply hoping that
the implementation change has no other unintended effects.

while if you have a signal 'xyz' and change its behavior you *must*
verify the impact in the rest of the architecture and if needed, as you
said, beyond the entity boundary, with a variable your search is limited
to the process where it has been declared.

Moreover, if your variable logic change does not affect the signals
logic that are related to it within that process, you do not have to
search beyond the process itself. Meaning you have kept the interface
between processes the same, while changing the internal logic of the
process, hence providing encapsulation.

[]
Your suggestion was that it is somehow easier to search for 'xyz' in
the process rather than the architecture. My point is that you have
to do the search for 'xyz' and that you should use a tool. If the
tool you're using is simply your eyeball scan of the code (or the
eyeballs of a review team) then you're right, but if you're using
your eyeball (and the eyeball of others) to scan you're using the
wrong tool. You should be using the search function of your text
editor. When you use that tool you'll get to the "xyz not found"
with exactly the same effort regardless of whether 'xyz' is a signal
or variable and regardless of the size of the architecture being
searched.

Having the variable local scope nobody prevents me calling all variables
with the same name; I may have tens of variables all named 'cnt' but
having a completely different logic for each individual process they
belong too. Your tool now is in your way instead.

When interfaces between processes (signals) have been defined you only
need to make sure the change to the logic does not affect the interface
and I'm not sure there's a tool out there (other than a simulator) to
help you out.
 
On Friday, October 4, 2013 4:55:43 AM UTC-4, alb wrote:
On 04/10/2013 03:10, KJ wrote:
Your original point was that making something a
variable would somehow isolate an implementation change from rippling
into some unintended operation. But if you change the implementation
of signal or variable 'xyz', you need to search for every usage of
'xyz' in the entity (and beyond if 'xyz' is an entity output or
affects an entity output) and insure you won't break something.
Thinking that just because 'xyz' is a variable will in any limit the
scope of that search is incorrect. You'll still have to do a text
search on 'xyz'. If you're not doing that, you're simply hoping that
the implementation change has no other unintended effects.


while if you have a signal 'xyz' and change its behavior you *must*
verify the impact in the rest of the architecture and if needed, as you
said, beyond the entity boundary, with a variable your search is limited
to the process where it has been declared.

Your search does not end at the end of a process (or entity) just because you use a variable. Consider the following simple statement added within a process where 'xyz' is the variable:
some_sig <= xyz;
By your reasoning, you would not need to verify the impact of 'some_sig' since 'xyz' is a local variable and cannot escape. That reasoning is incorrect. My only point is that the use of a *variable* does not aid you in any way when it comes to making a change to the implementation of that variable (i.e. changing the logic that generates the variable). I'm not debating whether using the variable localizes the use or whether or not using variables is 'good' or not.

Moreover, if your variable logic change does not affect the signals
logic that are related to it within that process, you do not have to
search beyond the process itself. Meaning you have kept the interface
between processes the same, while changing the internal logic of the
process, hence providing encapsulation.

Encapulation is not the point. This sub-thread was about how supposedly using a variable rather than a signal somehow provides additional protection from some unintended functional changes when the design is modified in the future. Already mentioned was that locally scoped signals within a block statement would be equivalent in scope with a variable, but the locally scoped signal could still escape the block statement in exactly the same manner as shown for a variable. Therefore, the search for dependencies would continue outside the local scope whether that scope was a process or a block.

Having the variable local scope nobody prevents me calling all variables
with the same name; I may have tens of variables all named 'cnt' but
having a completely different logic for each individual process they
belong too. Your tool now is in your way instead.

Might I suggest that if you have lots of variables all named the same, that you are likely leaving a messy design trail for somebody to have to decipher down the road. You can also reuse a particular variable and give it completely different functional definitions within a process due to the sequential nature of a process...but that doesn't mean you should do things that way.

When interfaces between processes (signals) have been defined you only
need to make sure the change to the logic does not affect the interface
and I'm not sure there's a tool out there (other than a simulator) to
help you out.

The text editor is one effective tool but yes it fails if everything is named the same and you depend on scope alone to sort it out. The dataflow window in a simulator will give you precisely what you need. It will show you all dependencies no matter they are in the entire simulation model (i.e. not limited in scope by process or entity). It works with signals...not variables. Score another for signal and a whiff for variables.

Kevin Jennings
 
On 10/3/2013 11:17 AM, alb wrote:
Hi Rick,

On 03/10/2013 05:24, rickman wrote:
[]
I do not see what's wrong with the second procedure needing to be aware
of the changed variable. When the second procedure runs all previous
variables have been updated accordingly and depending on their values
the procedure runs accordingly.

That *is* the problem. The second procedure sees the next state rather
than the current state.

That's the reason why the order of procedures matter. I let the higher
FSM (the one that supposedly 'controls' everything) be the last to start
and then respectively the second in rank and so on...

This is the awkward part. The way you have it written, procedures get
the "next" state of variables that have been updated already and the
"current" state of variables still to be updated! The current and next
states of a registered variable correspond to the outputs and inputs of
the register respectively.

When using signals, the order of sequential statements is not important,
but for variables it *is*. By partitioning the variable assignments
this way you make it difficult for the designer to keep track of whether
variables are in the current state or next state mode.

Normally the various FSMs of a design all work on the same clock edge so
the code needs to reflect that. If a signal is used it is not actually
updated until after a delta cycle and so all procedures have the same
input values regardless of the order called.


exactly the same here. In each individual FSM the inout mode in the
parameter list is for the state parameter that the FSM will run with.
Like in one process FSM, you do not need to formally separate the
'present state' and the 'next state'.

And therein lies the problem. When using a single variable like this,
if some state variable have been updated but not others, the FSM gets
very complex.

I don't get this. My state variable at each state depends on several
conditions, some of them been states of another FSM (like you would have
with a counter), where is the problem?

The problem is that in hardware each register has inputs and outputs.
When you use a variable before it is assigned you are using the output
of the register, when you use the variable after it has been assigned
you are using the output of the logic and the input to the register.

Try drawing some logic blocks (no need to actually define the gates)
that implement your design. I bet either a) the resulting logic is not
what you are picturing in your mind or b) the resulting logic is just
not correct.


The isolated procedures for updating each state variable
in your FSM have to be aware of one another and the order in which they
are invoked. This greatly complicates the code and understanding of it.
I would find that to be impossibly difficult to use. I don't consider
this to be useful decomposition.

See the following snippet of code:

code
-------------------------------------------------------------------------------
procedure writ_read (
state_v : inout state_t;
mstate_v : in mstate_t;
nstate_v : in nstate_t) is
begin
case state_v is
when IDLE =
if mstate_v = START_READ or nstate_v = START_READ then
state_v := READ_BGN;
elsif mstate_v = START_WRITE or nstate_v = START_WRITE then
state_v := WRITE_BGN;
end if;
when READ_BGN =
state_v := READ_END; -- this is one clock cycle
nRD_v := not nRD_v;
when READ_END =
state_v := IDLE;
nRD_v := not nRD_v;
DATA_v := DATA;
when WRITE_BGN =
state_v := WRITE_END; -- this is one clock cycle
nWR_v := not nWR_v;
when WRITE_END =
state_v := DONE;
nWR_v := not nWR_v;
when DONE =
state_v := IDLE;
DATA_v := (others => 'Z'); -- release the bus
when others => null;
end case;
end procedure writ_read;
/code

the 'awareness' you refer to is in the IDLE state case, where the 'if'
branch reacts on the others' state variables... Being the author of this
code I certainly cannot see the 'complication' that lies behind it, but
please advise on how to write it *more* clear than this [1].

Has mstate_v or nstate_v been updated in their procedures yet? You have
to know this in order to know what value they will have.

This is exactly the type of problems that happen when designers forget
they are describing hardware rather than writing software.


I've thrown Mealy and Moore definitions away long ago. I think about the
function, not a particular implementation.
I prefer to have registered outputs and in my case those outputs are
described directly in the appropriate states of the FSM, whether this is
Mealy or Moore I could care less with all due respect.

Yes, Mealy and Moore are not often used in a strict sense, but the point
is access to the *next* value of the state rather than the current
value. This lets you get registered outputs out on *this* clock edge
rather than having them wait a clock cycle.

'this' or 'that' clock cycle does not really matter to me, latency apart
I get the same throughput.

Ok, so now I understand. You don't *care* about what hardware is
produced. Your design may be implemented very inefficiently, but it
doesn't matter. I'm not sure if you care about latency or not. In most
of my designs latency *is* an issue so I use HDL to describe the
hardware rather than writing software and letting the chips fall where
they may. (pun intended)

Clock cycles count very much in my designs as well.

Do you care about the clock speed? By using the next state value of a
variable the logic created will most likely be slower than using the
current state value since it is daisy chained. If you have three FSMs
as in your earlier example machine C will be a function of all the
inputs to FSM B as which is a function of all the inputs to FSM A and
very likely result in cascaded or duplicated logic running three times
slower than a design all based on the current state of a variable or
signal.

--

Rick
 
On 10/3/2013 1:50 PM, Andy wrote:
You do raise a good point about capturing waveforms of variables. It sounds like a tool issue to be solved. In the meantime, I might suggest local signals declared in a block statement surrounding each process.

Variables are handled differently in simulation because they *are*
different. A signal is never updated until the end of a delta cycle.
Variables can be updated at any time. How do you plot a variable having
three values in the same delta cycle? The ability to show signals after
a simulation has started is only available if the flag to save *all*
simulation data is set. This takes up a lot of disk space and makes the
simulation run more slowly. No free lunches... To do this for
variables would require saving a lot of additional information.

--

Rick
 
On Friday, October 4, 2013 8:19:02 AM UTC-5, rickman wrote:
When using signals, the order of sequential statements is not
important, but for variables it *is*.

The order of read (access) relative to write (update) is not important, but the order of writes relative to other writes is!

Consider sequential signal assignment statements in a process:

count <= 0;
if a then
count <= count + 1;
end if;
if b then
count <= count; -- ;^)
end if;

This is why I consider sequential signal assignments pseudo-sequential. Some aspects are sequential, others are not.

Variable assignments are purely sequential.

Andy
 
Hi Rick,

On 04/10/2013 15:19, rickman wrote:
[]
That *is* the problem. The second procedure sees the next state rather
than the current state.

That's the reason why the order of procedures matter. I let the higher
FSM (the one that supposedly 'controls' everything) be the last to start
and then respectively the second in rank and so on...

[]
When using signals, the order of sequential statements is not important,
but for variables it *is*. By partitioning the variable assignments
this way you make it difficult for the designer to keep track of whether
variables are in the current state or next state mode.

Read from top to bottom, this may help. The synthesis tool seem to get
it pretty straight forward.

Normally the various FSMs of a design all work on the same clock edge so
the code needs to reflect that. If a signal is used it is not actually
updated until after a delta cycle and so all procedures have the same
input values regardless of the order called.

if your state depends on a value why do you bother so much if this value
happens in a specific clock cycle? On top of this, I'm not thinking in
terms of clock cycles, my FSM remains in a state until the other one has
completed whatever it needs to complete. Timing has completely
disappeared in my logic, what matters is only the functionality.

[]
I don't get this. My state variable at each state depends on several
conditions, some of them been states of another FSM (like you would have
with a counter), where is the problem?

[]
When you use a variable before it is assigned you are using the output
of the register, when you use the variable after it has been assigned
you are using the output of the logic and the input to the register.

a variable can infer both a register and a wire as I already posted:
<code>
process (clk)
variable something : std_logic;
if rising_edge(clk) then
if reset = '1' then
something := '0';
else
output_b <= something or input c; -- using the previous clock's
value of 'something' infers a register
something := input_a and input_b; -- comb. logic for a new value
output_a <= something or input_c; -- which is used immediately,
not registered here
end if;
end if;
end process;
</code>

while a signal in a clocked process can only generate registers. That's
a flexibility I like to profit from.

Try drawing some logic blocks (no need to actually define the gates)
that implement your design. I bet either a) the resulting logic is not
what you are picturing in your mind or b) the resulting logic is just
not correct.

They are three FSM with two counters and few registers, whether this is
correct or not I'm not sure, that is why I simulate it. Being correct or
wrong has nothing to do with the style used.

The isolated procedures for updating each state variable
in your FSM have to be aware of one another and the order in which they
are invoked. This greatly complicates the code and understanding of it.
I would find that to be impossibly difficult to use. I don't consider
this to be useful decomposition.

See the following snippet of code:

code
-------------------------------------------------------------------------------

procedure writ_read (
state_v : inout state_t;
mstate_v : in mstate_t;
nstate_v : in nstate_t) is
begin
case state_v is
when IDLE =
if mstate_v = START_READ or nstate_v = START_READ then
state_v := READ_BGN;
elsif mstate_v = START_WRITE or nstate_v = START_WRITE then
state_v := WRITE_BGN;
end if;
when READ_BGN =
state_v := READ_END; -- this is one clock cycle
nRD_v := not nRD_v;
when READ_END =
state_v := IDLE;
nRD_v := not nRD_v;
DATA_v := DATA;
when WRITE_BGN =
state_v := WRITE_END; -- this is one clock cycle
nWR_v := not nWR_v;
when WRITE_END =
state_v := DONE;
nWR_v := not nWR_v;
when DONE =
state_v := IDLE;
DATA_v := (others => 'Z'); -- release the bus
when others => null;
end case;
end procedure writ_read;
/code

[]
Has mstate_v or nstate_v been updated in their procedures yet? You have
to know this in order to know what value they will have.

Why should I care? When the clock will tick the state_v register will
either move on or stay where it is according to the values of mstate_v
or nstate_v for *that* clock cycle. I'm not thinking on *when* the state
changes, I actually do not want to rely on timing since I may need to
add a pipeline stage to get an operation done, while still keep the
functionality in place.

This is exactly the type of problems that happen when designers forget
they are describing hardware rather than writing software.

You are certainly entitled to draw any conclusion and I do not intend to
change your opinion, I can only say that I see the flops and gates as
you claim you're capable to do with your style.

[]
Yes, Mealy and Moore are not often used in a strict sense, but the point
is access to the *next* value of the state rather than the current
value. This lets you get registered outputs out on *this* clock edge
rather than having them wait a clock cycle.

'this' or 'that' clock cycle does not really matter to me, latency apart
I get the same throughput.

Ok, so now I understand. You don't *care* about what hardware is
produced.

This is what you say, not me.

Your design may be implemented very inefficiently, but it
doesn't matter.

In my case, area is not an issue and timing...well I can argue that if I
half the clock rate I still get what I need (I'm only too late in the
project phase to debate about the system clock rate!).

Having said that, my code needs to be ported and extended on different
FPGAs and my pure goal is readability. Writing the building blocks
(procedures) to allow a more complex functionality to be added later is
more important than your acclaimed inefficiency.

> I'm not sure if you care about latency or not.

why are you not sure? What is your point in doubting on this?

In most
of my designs latency *is* an issue so I use HDL to describe the
hardware rather than writing software and letting the chips fall where
they may. (pun intended)

When latency *is* a problem than you're overall speed starts to suffer
since you cannot pipeline your design.

> Clock cycles count very much in my designs as well.

While counting how many clock cycles there are between two operations
make sense (latency), I do not see what sense makes doing the operation
at the 4302423th clock cycle vs 4302424th, but again it all depends on
the specs.

Do you care about the clock speed? By using the next state value of a
variable the logic created will most likely be slower than using the
current state value since it is daisy chained.

Why you keep saying I'm using the 'next state'? A synchronous flop
sensitive to a clock edge can *only* be sensitive to values from the
past. If you manage to get it to be sensitive to values from the future
I'll be interested to see it.

It's a one process only, every register only reacts on a clock edge, the
value of a state depends only on the value of a combination of registers
which have been updated in the previous clock, certainly not in the next
clock cycle.

If you have three FSMs
as in your earlier example machine C will be a function of all the
inputs to FSM B as which is a function of all the inputs to FSM A and
very likely result in cascaded or duplicated logic running three times
slower than a design all based on the current state of a variable or
signal.

You are entitled to /believe/ that, I normally verify what I say with a
test and if it proves me wrong or right than there's nothing else I need
to believe.
 
On 10/4/2013 11:13 AM, alb wrote:
Hi Rick,

On 04/10/2013 15:19, rickman wrote:
[]
That *is* the problem. The second procedure sees the next state
rather
than the current state.

That's the reason why the order of procedures matter. I let the higher
FSM (the one that supposedly 'controls' everything) be the last to
start
and then respectively the second in rank and so on...

[]
When using signals, the order of sequential statements is not important,
but for variables it *is*. By partitioning the variable assignments
this way you make it difficult for the designer to keep track of whether
variables are in the current state or next state mode.

Read from top to bottom, this may help. The synthesis tool seem to get
it pretty straight forward.

Normally the various FSMs of a design all work on the same clock edge so
the code needs to reflect that. If a signal is used it is not actually
updated until after a delta cycle and so all procedures have the same
input values regardless of the order called.

if your state depends on a value why do you bother so much if this value
happens in a specific clock cycle? On top of this, I'm not thinking in
terms of clock cycles, my FSM remains in a state until the other one has
completed whatever it needs to complete. Timing has completely
disappeared in my logic, what matters is only the functionality.

I *design* hardware. If I don't care when or how something happens I
can use graphical tools and just skip HDL altogether... which I don't do
because I find it much easier to just "do it". Why have multiple
methodologies for designing FSMs when one method works, is simple to
design and simple to read?


I don't get this. My state variable at each state depends on several
conditions, some of them been states of another FSM (like you would
have
with a counter), where is the problem?

[]
When you use a variable before it is assigned you are using the output
of the register, when you use the variable after it has been assigned
you are using the output of the logic and the input to the register.

a variable can infer both a register and a wire as I already posted:
code
process (clk)
variable something : std_logic;
if rising_edge(clk) then
if reset = '1' then
something := '0';
else
output_b<= something or input c; -- using the previous clock's
value of 'something' infers a register
something := input_a and input_b; -- comb. logic for a new value
output_a<= something or input_c; -- which is used immediately,
not registered here
end if;
end if;
end process;
/code

while a signal in a clocked process can only generate registers. That's
a flexibility I like to profit from.

You aren't addressing my point. You either don't know what hardware is
being generated from your code or you don't care. You have indicated
you don't try to control the hardware generated, so I guess that is
what's going on, you don't care. I do care. I *always* want to know
what hardware is generated and have control.


Try drawing some logic blocks (no need to actually define the gates)
that implement your design. I bet either a) the resulting logic is not
what you are picturing in your mind or b) the resulting logic is just
not correct.

They are three FSM with two counters and few registers, whether this is
correct or not I'm not sure, that is why I simulate it. Being correct or
wrong has nothing to do with the style used.

No but the efficiency will vary and is *very much* a function of style.


Has mstate_v or nstate_v been updated in their procedures yet? You have
to know this in order to know what value they will have.

Why should I care? When the clock will tick the state_v register will
either move on or stay where it is according to the values of mstate_v
or nstate_v for *that* clock cycle. I'm not thinking on *when* the state
changes, I actually do not want to rely on timing since I may need to
add a pipeline stage to get an operation done, while still keep the
functionality in place.

You should care because depending on whether the variables have been
updated will determine what FSM you are *actually* designing. Designing
hardware without understanding what hardware is generated is a *bad* idea.



This is exactly the type of problems that happen when designers forget
they are describing hardware rather than writing software.

You are certainly entitled to draw any conclusion and I do not intend to
change your opinion, I can only say that I see the flops and gates as
you claim you're capable to do with your style.

But you have said you don't care how the FSMs depend on one another. If
you don't care, how can you know? Much of your code will produce
combinatorial logic without registers in the path. It is entirely
possible with your style, if you *don't care* about the hardware, to
produce a purely combinatorial path from input to output also depending
on decodes of the states without realizing it. That circuit will be
subject to glitching which may or may *not* show up in simulation.

No, you won't change my point of view because I have looked at yours and
found it wanting, no, I have found it ignoring the facts of hardware
design.


Yes, Mealy and Moore are not often used in a strict sense, but the
point
is access to the *next* value of the state rather than the current
value. This lets you get registered outputs out on *this* clock edge
rather than having them wait a clock cycle.

'this' or 'that' clock cycle does not really matter to me, latency
apart
I get the same throughput.

Ok, so now I understand. You don't *care* about what hardware is
produced.

This is what you say, not me.

To quote you from above in the very post I am replying to...

"> Why should I care?"


Your design may be implemented very inefficiently, but it
doesn't matter.

In my case, area is not an issue and timing...well I can argue that if I
half the clock rate I still get what I need (I'm only too late in the
project phase to debate about the system clock rate!).

Having said that, my code needs to be ported and extended on different
FPGAs and my pure goal is readability. Writing the building blocks
(procedures) to allow a more complex functionality to be added later is
more important than your acclaimed inefficiency.

Yes, you talk about readability but you have not shown anything
"unreadable" about using signals or *not* glomming all the design into
one process.


I'm not sure if you care about latency or not.

why are you not sure? What is your point in doubting on this?

In most
of my designs latency *is* an issue so I use HDL to describe the
hardware rather than writing software and letting the chips fall where
they may. (pun intended)

When latency *is* a problem than you're overall speed starts to suffer
since you cannot pipeline your design.

Yes, what is your point? You have said you don't try to control the
hardware with your coding style, so you don't have control over any of
this.


Clock cycles count very much in my designs as well.

While counting how many clock cycles there are between two operations
make sense (latency), I do not see what sense makes doing the operation
at the 4302423th clock cycle vs 4302424th, but again it all depends on
the specs.

Do you care about the clock speed? By using the next state value of a
variable the logic created will most likely be slower than using the
current state value since it is daisy chained.

Why you keep saying I'm using the 'next state'? A synchronous flop
sensitive to a clock edge can *only* be sensitive to values from the
past. If you manage to get it to be sensitive to values from the future
I'll be interested to see it.

Much of the logic generated by your design will depend on the "next
state" logic of any variable that has been updated before the one being
assigned. If you don't understand that you don't understand variable.


It's a one process only, every register only reacts on a clock edge, the
value of a state depends only on the value of a combination of registers
which have been updated in the previous clock, certainly not in the next
clock cycle.

But as you have observed, variables in a process can generate
combinatorial logic and that is what an updated variable has done.


If you have three FSMs
as in your earlier example machine C will be a function of all the
inputs to FSM B as which is a function of all the inputs to FSM A and
very likely result in cascaded or duplicated logic running three times
slower than a design all based on the current state of a variable or
signal.

You are entitled to /believe/ that, I normally verify what I say with a
test and if it proves me wrong or right than there's nothing else I need
to believe.

Ok, so take a look at your synthesis result. One of two things will
result from the code you have described. If A is updated before B and B
depends on A, then either B will depend on the inputs to the A register
or B will duplicate all the logic that was generated for the A register.
You have the code, you have the tool, let us know how it works out.

I don't wish to continue to discuss this further. I believe I have
completed all the explanations of my points. But I would be interested
in seeing the results of your synthesis.

--

Rick
 
On Friday, October 4, 2013 11:24:28 AM UTC-5, KJ wrote:
Your search does not end at the end of a process (or entity) just because
you use a variable. Consider the following simple statement added within a
process where 'xyz' is the variable:
some_sig <= xyz;
By your reasoning, you would not need to verify the impact of 'some_sig'
since 'xyz' is a local variable and cannot escape. That reasoning is
incorrect.

By gosh, you're right, KJ!

Whoah! This is an even bigger problem than we thought: local signals in an architecture suffer the same exact fate when assigned to a port (gasp)!

Thanks to your enlightenment, we should use only one huge entity/architecture and skip all this useless hierarchical encapsulation window-dressing, to nip this problem in the bud!



Seriously, exporting a variable by assignment to a signal is just another of the variable's "uses" that must be considered when modifying the variable.

Andy
 
On Friday, October 4, 2013 11:24:28 AM UTC-5, KJ wrote:
Your search does not end at the end of a process (or entity) just because
you use a variable. Consider the following simple statement added within a
process where 'xyz' is the variable:
some_sig <= xyz;
By your reasoning, you would not need to verify the impact of 'some_sig'
since 'xyz' is a local variable and cannot escape. That reasoning is
incorrect.


By gosh, you're right, KJ!

Whoah! This is an even bigger problem than we thought: local signals in an architecture suffer the same exact fate when assigned to a port (gasp)!

Glad I could help clear this up for you Andy.

Thanks to your enlightenment, we should use only one huge entity/architecture and skip all this useless hierarchical encapsulation window-dressing, to nip this problem in the bud!

Note that you are on the only one in this thread that has suggested "huge entity/architecture" or "skip all this useless hierarchical encapsulation" so on this point you're nipping your own bud.

Seriously, exporting a variable by assignment to a signal is just another of the variable's "uses" that must be considered when modifying the variable.

Thanks for the update Captain Obvious. Report back soon, love to hear from you.
 
On Friday, September 27, 2013 12:48:07 AM UTC-7, alb wrote:

After reviewing your FSM though, I think you might do better with
separate, smaller state machine(s) for the reusable states, and
implement a hierarchical state machine. There is no point in
combining all the reusable and unique states into the same FSM.

I prefer to concatenate blocks of state machine update code for multiple machines with one state variable per block.

-- Mike Treseler
 
Hi Mike,

On 28/10/2013 21:06, Mike Treseler wrote:
After reviewing your FSM though, I think you might do better
with separate, smaller state machine(s) for the reusable states,
and implement a hierarchical state machine. There is no point in
combining all the reusable and unique states into the same FSM.

I prefer to concatenate blocks of state machine update code for
multiple machines with one state variable per block.

how do you prevent state machines not to 'step on each other toes'?
I had three state machines with three state variables, but living in one
procedure only I found myself 'tempted' at changing a state variable
from within another state machine logic.

Being very skeptical about my rigorousness, I waved this problem
separating the fsms in three different procedures with state variables
passed through parameters called in the same way as the global variables
[1] but with different type (in or inout) for each fsm. The level of
separation I achieve is good enough, with a minimal overhead.

[1] the variables are locally scoped to the process but, being the
process the only statement in the architecture, they act as if they were
'global' in the architecture scope.
 

Welcome to EDABoard.com

Sponsor

Back
Top