vhdl questions from a verilog person

A

Andy

Clock cycle matters, but is a second-order issue.  The way we grade
things about 20% of the grade is  for performance, and that is
basically measured by how long it takes the various testbenches to
run.  Good architecture can get you there (lots of CPI) but clock
cycle obviously plays a big role too.  

I assume you are talking about simulated time (i.e. simulated clock
cycles), not wall time running the simulation? Do the students get to
optimize the clock speed?

While RTL coding style does not have a direct impact on simulated time
performance, it does impact wall time spent in simulation. Faster
simulations (wall time) yield more time for debugging, "what if"
evaluations, etc., which then can improve simulated time performance.

Single process coding styles simulate faster than dual process
(combinatorial & clocked) styles. Most modern simulators "merge"
multiple processes that share the same sensitivity list, saving
overhead. Single process coding styles maximize this optimization,
since all the processes are sensitive to the same clock and async
reset signals. Combinatorial processes rarely share complete
sensitivity lists, so there is little optimization.

Integer types simulate MUCH faster than slv/signed/unsigned. Use
multiply/divide for shifting, divide/mod for slicing, and multiply/add
for concatenation. The synthesis tool will reduce it down to the
shifts & masks just fine. If their any good at writing low level SW,
they'll be right at home anyway.

Variables are slightly faster than signals that are used only locally,
but not by much with a good simulator.

Andy
 
J

Jeff Cunningham

Andy said:
deferred constants (still not synthesizable?!), procedures/functions,

Synplicity has handled deferred constants for years. I agree it's lame
that XST does not.

-Jeff
 
J

Jan Decaluwe

Mark said:
I guess the first point is to keep in mind the students will have done
a handful of trivial verilog modules before taking this class. And
they have to learn a huge number of other things. So we necessarily
oversimplify things. That said, it would be very useful for me to
understand why people believe these are over simplified. Even if we
don't change our rules, it's important for me to understand "truth" so
I can let them know the issues involved.

Presumably they have also done some programming in a "normal"
programming language. So they know variables already. Nothing new to
teach here, except bizarre Verilog terminology ("blocking assignments").
Between variables and signals, variables are the easy stuff!

So it's likely that your students will want to use the things they
already know for their modeling: variable semantics, for loops,
while loops, ... and for good reason. Teaching that they shouldn't is
artificial and confusing.

For modelling (e.g. test benches), the only thing they need to know is
that variables shouldn't be used for communication. For synthesis,
unfortunately, they will need to know about the restrictions. But not
using variable semantics is not part of that, and never was. Actually
it's the best part of RTL synthesis, the part that at least raises
the abstraction level a little bit above schematic entry.

Jan
 
M

Mike Treseler

Jonathan said:
Nice example, thanks.
Of course, any example of this level of complexity
immediately sets one to thinking about functions and
procedures

Me too.
But that's exactly where the hardware guys head for the exits.
I think Andreas' version might make a nice
intro comment for the function below.

-- Mike Treseler

--________________
-- Base serial shifter, all of the other crc_shifts end up here
-- This gets called n times for the parallel versions above
function crc_shift
(
data : in std_ulogic; -- input bit
crc : in unsigned; -- crc starting value
crc_type : in crc_t
)
return unsigned is
variable crc_v : unsigned(crc'range); -- CRC value
constant reg_len : natural :=
crc_table(crc_type).crc_len; -- look up length
subtype crc_vec is unsigned(reg_len-1 downto 0);
constant mask : crc_vec :=
crc_table(crc_type).poly_vec(crc_vec'range);
begin
crc_v := crc sll 1; -- shift it
if (crc(crc'left) xor data) = '1' then -- maybe invert mask bits
crc_v := crc_v xor mask;
end if;
return unsigned(crc_v); -- whole register value each shift
end function crc_shift;
--________________
 
A

Andy

I guess the first point is to keep in mind the students will have done
a handful of trivial verilog modules before taking this class. And
they have to learn a huge number of other things. So we necessarily
oversimplify things. That said, it would be very useful for me to
understand why people believe these are over simplified. Even if we
don't change our rules, it's important for me to understand "truth" so
I can let them know the issues involved.

From your verilog guidelines:

8. When modeling sequential logic, use nonblocking assignments.
9. When modeling combinational logic with an always block, use
blocking assignments.
10. Do not mix blocking and nonblocking assignments in the same always
block.

These, taken together, prohibit a single process style. They are good
advice to avoid problems in verilog. They do not avoid any problems in
vhdl.

14. Make sure that all paths through a combinatorial always block
assign all variables.

This is a moot point with single process style. You cannot get a latch
from a clocked process.

2. Make your squential block as simple as possible. They shouldn’t
have much control flow.
12. while and for don’t work like you think, make sure that you
actually should be using them!

These two seem related. The most common reason users have problems
with while/for loops has to do with non-blocking assignments not
updating for each loop iteration, rendering combinatorial iteration
difficult. If you use blocking assignments, they work just like you'd
think. There remain synthesizable restrictions on them, but most of it
has to do with the synthesis tool needing to unroll the loops, which
means the iteration bounds need to be static.

Consider the following non-blocking (vhdl signal) assignment
statements:

if rising_edge(clk) then
a <= input1;
b <= a;
c <= b;
end if;

They'd have exactly the same behavior, regardless of which order they
are written in.

But if I do something like:

if rising_edge(clk) then
a <= input1;
b <= a;
c <= b;
a <= input2;
end if;

Suddenly the relative order of the first and last assignments matters
a lot! But their order relative to the middle assignments still
doesn't matter. Confusing? Thus it is with the "pseudo-sequential"
behavior of non-blocking assignments in sequential contexts.

Why not write the code such that the order is uniformly important?
Given that your students have only limited experience with verilog, I
bet they have much more experience with programming languages. Why not
use the order that every other programming language uses: pure
sequential execution. This also provides for a cleaner separation
between sequential and concurrent contexts: statements are
(completely) sequential with each other, processes are concurrent with
each other.

Re-writing the second example for equivalent functionality, with
blocking assignments (vhdl variables):

if rising_edge(clk) then
c := b;
b := a;
a := input1;
a := input2;
end if;

Andy
 
A

Andy

Synplicity has handled deferred constants for years. I agree it's lame
that XST does not.

-Jeff

The last time I checked, Synplicity handled deferred constants just
like regular ones. You had to recompile the package body and the
package itself (and everything that referenced it) to promote the new
value. Deferred constants in simulation only require the package body
to be recompiled, and then nothing else.

On the other hand, with direct entity instantiation, I get a lot more
recompilations than I used to anyway.

BTW, Mark, be sure to let your students know about direct entity
instantiations! It completely avoids component declarations and
configurations. Much simpler and easier in the vast majority of
applications.

Andy
 
J

Jeff Cunningham

Andy said:
The last time I checked, Synplicity handled deferred constants just
like regular ones. You had to recompile the package body and the
package itself (and everything that referenced it) to promote the new
value. Deferred constants in simulation only require the package body
to be recompiled, and then nothing else.

On the other hand, with direct entity instantiation, I get a lot more
recompilations than I used to anyway.

I see your point. At least Synplicity doesn't abort with a fatal error
with a deferred constant like XST does (or did last time I checked).

-Jeff
 
P

Paul Taylor

How do you handle the rare case where you need a mealy
output unregistered? I have had some that go to the
load enable input of a datapath register, and hence,
its timing path terminates there. It seems like your
choices would be:
1) Eat a clock cycle of latency
2) Move data path into statemachine
3) Write code outside of statemachine

I use both one and two process state machines depending on what it is
that I'm doing. A problem with the two process state machine is if you then
need some signals registered you need extra signals to the clocked process
to communicate what you want - more typing!! (I'm lazy)

I can't help thinking that modelling registers with the rising edge
construct was the wrong way to go. If you could declare signals as
registered instead, you could have assignments to registered and
unregistered signals in the same block of code, rather than have one block
of code only for the assignment of registered signals, and another block
of code only for unregistered signals - which I find awkward,
and ultimately time-consuming.

Regards,

Paul.
 
M

Mike Treseler

Paul said:
A problem with the two process state machine is if you then
need some signals registered you need extra signals to the clocked process
to communicate what you want - more typing!! (I'm lazy)

Me too.
I can't help thinking that modeling registers with the rising edge
construct was the wrong way to go. If you could declare signals as
registered instead, you could have assignments to registered and
unregistered signals in the same block of code, rather than have one block
of code only for the assignment of registered signals, and another block
of code only for unregistered signals - which I find awkward,
and ultimately time-consuming.

Plain vanilla vhdl can do that, if I model the
nodes using process variables instead of signals.
I code up a functional description that
matches a synthesis template and
let synthesis worry about the gates and flops.


begin
a:a_v := a_v + 1; -- fast count
b:if a_v(a_v'left) = '1' then -- a carry?
a_v(a_v'left) := '0'; -- msb is asynch, rest is regs
b_v := b_v + 1;
c:if b_v(b_v'left) = '1' then -- b carry?
b_v(b_v'left) := '0'; -- msb is async, rest is regs
c_v := c_v + 1; -- slow count, unsigned rolls over, no carry
end if c;
end if b;


-- Mike Treseler
 
P

Paul Taylor

Me too.


Plain vanilla vhdl can do that, if I model the
nodes using process variables instead of signals.
I code up a functional description that
matches a synthesis template and
let synthesis worry about the gates and flops.


begin
a:a_v := a_v + 1; -- fast count
b:if a_v(a_v'left) = '1' then -- a carry?
a_v(a_v'left) := '0'; -- msb is asynch, rest is regs
b_v := b_v + 1;
c:if b_v(b_v'left) = '1' then -- b carry?
b_v(b_v'left) := '0'; -- msb is async, rest is regs
c_v := c_v + 1; -- slow count, unsigned rolls over, no carry
end if c;
end if b;

Yes. But the variables can't be taken out of the process and say assigned
to ports unregistered as (I'm assumming) they are in a rising_edge if
statement somewhere? So the signals at the ports become registered.

My comment about assigning to registered and unregistered signals in the
same block was about signals that go out of the process, say to ports for
example. In which case with VHDL the registered signals would be in a
rising_edge block of code, and the unregistered signal would have to be in
another block, for example as per a two process state machine.

Hence the comment about the rising_edge construct not being a great idea
for modelling registers - IMHO of course :) I wouldn't be at all suprised
if someone tells me the good reasons why they are done that way.

Paul.
 
M

Mike Treseler

Paul said:
So the signals at the ports become registered.

Yes, but that is a good thing in my book.
It simplifies timing analysis.
I will make a large process if I have to.
Hence the comment about the rising_edge construct not being a great idea
for modelling registers - IMHO of course :) I wouldn't be at all suprised
if someone tells me the good reasons why they are done that way.

I will defer.
The main point is that it isn't done this way
and that there are several coding styles
that get the job done as is.

-- Mike Treseler
 
P

Paul Taylor

I will defer.
The main point is that it isn't done this way
and that there are several coding styles
that get the job done as is.

Sure, I was just musing on why rising edge construct was used to model
registers. All very hypothetical, and taking the thread way off on a
tangent to boot.

The point that I was making was if vhdl didn't model registers with a
rising edge construct but with a declaration, then a vhdl module module
implementing a state machine with registered and unregistered output
signals might have looked _something_ like:

architecture...
-- signal declarations, one is declared registered
registered signal LED_ON_s : ...
signal DONE_s : ...
...
begin
process...
begin
if ARST = '1' then
LED_ON_s := '0'; -- asynchronously set LED_ON_s with ':='
...
else -- note no rising_edge
...
DONE_s <= '0';
case STATE_s is
...
when A_STATE =>
LED_ON_s <= '1'; -- registered
DONE_s <= '1'; -- unregistered
....

And then you wouldn't need two-process state machines for situations where
you have unregistered and registered outputs.

The above seems to me to be a better way of describing that hardware,
eschewing rising_edge and two processes. But what do I know!! Not as much
as those who specified VHDL for sure :)

Regards,

Paul.
 
P

Paul Taylor

architecture...
-- signal declarations, one is declared registered
registered signal LED_ON_s : ...
signal DONE_s : ...
...
begin
process...
begin
if ARST = '1' then
LED_ON_s := '0'; -- asynchronously set LED_ON_s with ':='
...
else -- note no rising_edge
...
DONE_s <= '0';
case STATE_s is
...
when A_STATE =>
LED_ON_s <= '1'; -- registered
DONE_s <= '1'; -- unregistered
...

Ignore this post (it doesn't work) !!!! I knew I shouldn't have 'mused' out
in the open!!

Paul.
 
K

KJ

Paul Taylor said:
The point that I was making was if vhdl didn't model registers with a
rising edge construct but with a declaration, then

The defunct tool called PlDesigner from Minc used to do just this, when a
signal was declared you would say something like "xyz clocked_by
clock"...the declaration could also have things like "reset_by" or
"preset_by" or "enabled_by" as needed as well...if memory serves, they went
out of business around 10 years ago or so.

ABEL does the same thing with the "istype" property in the declaration.

VHDL trumped them both though

KJ
 
T

Tricky

The defunct tool called PlDesigner from Minc used to do just this, when a
signal was declared you would say something like "xyz clocked_by
clock"...the declaration could also have things like "reset_by" or
"preset_by" or "enabled_by" as needed as well...if memory serves, they went
out of business around 10 years ago or so.

ABEL does the same thing with the "istype" property in the declaration.

VHDL trumped them both though

KJ

Again, altera's proprietry language AHDL does exactly this. things
like:

my_reg : DFFE;

my_reg.clk = clk;
my_reg.enable = enable;
my_reg.d = input;
output = my_reg.q;


TBH, ID have VHDL and its rising_edge processes any day.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,766
Messages
2,569,569
Members
45,045
Latest member
DRCM

Latest Threads

Top