Shortening common idioms: bus assignment and 'prev' generation

E

Eli Bendersky

Hello all,

In high level programming languages it's possible to shorten common
programming idioms by encapsulating them into functions / modules /
classes. It is similarly possible in simulation-aimed VHDL, but is much
more difficult in synthesis-aimed VHDL.

Here is one:

some_bus(31 downto 27) <= (others => '0');
some_bus(26) <= my_sig_3;
some_bus(25) <= my_sig_4;
some_bus(24 downto 20) <= (others => '0');
some_bus(19) <= my_sig_11;
some_bus(18 downto 2) <= (others => '0');
some_bus(1 downto 0) <= another_bus(1 downto 0);

I wish there sould be a way to just assign 'all zeros' to some_bus and
then the signals to relevant bits:

some_bus(31 downto 0) <= (others => '0');
some_bus(26) <= my_sig_3;
some_bus(25) <= my_sig_4;
some_bus(19) <= my_sig_11;
some_bus(1 downto 0) <= another_bus(1 downto 0);

This, unfortunately, doesn't work. Setting some_bus to 'L' also doesn't
work for synthesis (only simulation).

Another common idiom is seeing when a signal changed:

signal my_sig, my_sig_prev: std_logic;
....
....
process (clk, reset_n)
begin
if reset_n = '0' then
my_sig_prev <= '0';
elsif rising_edge(clk) then
my_sig_prev <= my_sig;
end if;
end process;

And then:

some process:
....
if rising_edge(clk) then
if my_sig_prev /= my_sig then
...
....

How can this be shortened, in synthesis ? I find myself writing this or
similar code (checking for a rise, or fall, of my_sig, for instance, by
(my_sig = '0' and my_sig_prev = '1') for fall) too many times !

Eli

....
 
M

Mike Treseler

Eli said:
I wish there sould be a way to just assign 'all zeros' to some_bus and
then the signals to relevant bits:
some_bus(31 downto 0) <= (others => '0');
some_bus(26) <= my_sig_3;
some_bus(25) <= my_sig_4;
some_bus(19) <= my_sig_11;
some_bus(1 downto 0) <= another_bus(1 downto 0);
This, unfortunately, doesn't work.

It would work inside a process.
Another common idiom is seeing when a signal changed:
signal my_sig, my_sig_prev: std_logic;
...
...
process (clk, reset_n)
begin
if reset_n = '0' then
my_sig_prev <= '0';
elsif rising_edge(clk) then
my_sig_prev <= my_sig;
end if;
end process;
And then:
some process:
...
if rising_edge(clk) then
if my_sig_prev /= my_sig then
...
...
How can this be shortened, in synthesis ?

You only have to do this once
in a single process design.
See the examples here:

http://home.comcast.net/~mike_treseler/

-- Mike Treseler
 
E

Eli Bendersky

Mike said:
It would work inside a process.

Yes, but then it would also be delayed by one clock, which isn't always
desirable. I wonder why synthesis tools don't like the 'H' value. Since
they resolve all drivers for each signal anyway, it should be quite
simple to implement.
You only have to do this once
in a single process design.
See the examples here:

http://home.comcast.net/~mike_treseler/

Interesting approach, but not to my taste, I think:

1) It interferes with the way I understand synthesizable code. I like
to be able to envision exactly how the hardware that's built from my
code looks - it aids greatly in understanding complex timing demands,
and overall I find that code written is such a way behaves very
similarly in real life and in simulation.
2) It is not flexible enough for large designs where usage of
'generate' is sometimes essential to avoid duplications, and where the
additional level of abstraction allowed by multiple processes is
precious.
3) I really like knowing where each signal is assigned, without looking
around too much. When each signal has one and only one process that
'drives' it, things are simpler to understand.

This all is, of course, IMHO :)

Eli
 
A

Allan Herriman

Yes, but then it would also be delayed by one clock, which isn't always
desirable. I wonder why synthesis tools don't like the 'H' value. Since
they resolve all drivers for each signal anyway, it should be quite
simple to implement.

It works inside a combinatorial process. No clock delay is generated.
The language already does what you want in this regard. There is no
need to change anything.

'H' is for modeling driver strengths which is a completely independent
concept.

Regards,
Allan
 
B

Ben Jones

I wish there sould be a way to just assign 'all zeros' to some_bus and
then the signals to relevant bits:
some_bus(31 downto 0) <= (others => '0');
some_bus(26) <= my_sig_3;
some_bus(25) <= my_sig_4;
some_bus(19) <= my_sig_11;
some_bus(1 downto 0) <= another_bus(1 downto 0);

some_bus <=
( 26 => my_sig_3,
25 => my_sig_4,
19 => my_sig_11,
1 => another_bus(1),
0 => another_bus(0),
others => '0');

Not ideal when you have a lot of sub-buses to map, because AFAIK you have to
do them bit-by-bit... but is that what you had in mind?

This notation works the same both inside and outside a process.

-Ben-
 
M

Magne Munkejord

Eli said:
Hello all,

In high level programming languages it's possible to shorten common
programming idioms by encapsulating them into functions / modules /
classes. It is similarly possible in simulation-aimed VHDL, but is much
more difficult in synthesis-aimed VHDL.

Here is one:

some_bus(31 downto 27) <= (others => '0');
some_bus(26) <= my_sig_3;
some_bus(25) <= my_sig_4;
some_bus(24 downto 20) <= (others => '0');
some_bus(19) <= my_sig_11;
some_bus(18 downto 2) <= (others => '0');
some_bus(1 downto 0) <= another_bus(1 downto 0);

I wish there sould be a way to just assign 'all zeros' to some_bus and
then the signals to relevant bits:

some_bus(31 downto 0) <= (others => '0');
some_bus(26) <= my_sig_3;
some_bus(25) <= my_sig_4;
some_bus(19) <= my_sig_11;
some_bus(1 downto 0) <= another_bus(1 downto 0);

This, unfortunately, doesn't work. Setting some_bus to 'L' also doesn't
work for synthesis (only simulation).

Another common idiom is seeing when a signal changed:

signal my_sig, my_sig_prev: std_logic;
...
...
process (clk, reset_n)
begin
if reset_n = '0' then
my_sig_prev <= '0';
elsif rising_edge(clk) then
my_sig_prev <= my_sig;
end if;
end process;

And then:

some process:
...
if rising_edge(clk) then
if my_sig_prev /= my_sig then
...
...

How can this be shortened, in synthesis ? I find myself writing this or
similar code (checking for a rise, or fall, of my_sig, for instance, by
(my_sig = '0' and my_sig_prev = '1') for fall) too many times !

Eli

...

I have used the concatenation operator '&' for signal assignment outside
a process like this
signal some_bus : std_logic_vector(15 downto 0);
signal another_bus : std_logic_vector(13 downto 0);
signal a_signal : std_logic;
signal b_signal : std_logic;

some_bus <= (another_bus & a_signal & b_signal);

also i _think_ this should work:
some_bus <= ((15 downto 10) => another_bus(13 downto 8), 5 => a_signal,
3 => '1', others => '0');

another trick for handling wide busses is to use aliases for segments of
your bus. Not sure about the syntax but it is something like:
alias bus_segment(7 downto 0) is some_bus(15 downto 8);

As far as i know, you can use the 'last_value and 'event attributes for
your other problem.
 
A

Andy

Eli Bendersky wrote:

1) It interferes with the way I understand synthesizable code. I like
to be able to envision exactly how the hardware that's built from my
code looks - it aids greatly in understanding complex timing demands,
and overall I find that code written is such a way behaves very
similarly in real life and in simulation.
2) It is not flexible enough for large designs where usage of
'generate' is sometimes essential to avoid duplications, and where the
additional level of abstraction allowed by multiple processes is
precious.
3) I really like knowing where each signal is assigned, without looking
around too much. When each signal has one and only one process that
'drives' it, things are simpler to understand.

This all is, of course, IMHO :)

Eli

I know and respect several designers that share your preferences on
coding style WRT separate processes. I think that style stems from the
earliest synthesis tools that could not infer storage (you had to
instantiate registers), and therefore all of the combinatorial code was
split up into multiple processes to keep the sensitivity lists as
simple as possible. As you pointed out, it has its advantages.

I am not a software person, but one reason I prefer RTL descriptions
that use variables instead of signals is because the sequential code
behaves like software, where the effect of order of read/write on
variables within a process means exactly what you would think from
reading code, whereas with signal assignments, it is irrelevent (a read
of a signal always gets the previous value).

If/then and for/loop constructs within processes, using globally static
conditions/bounds, operate exactly the same as equivalent generate
constructs for synthesis. I see no loss of flexibilty here.

I prefer more compact, single process code (not necessarily separate
procedures for init, update, output, etc.) rather than having the
redundant process & clock edge code. For me it's a matter of how much
(and how spread out) code there is to look through. I sometimes use
separate processes to keep somewhat isolated functions separate without
the overhead of full-up entity/architecture declarations, while still
taking advantage of the local scoping rules on variables.

All of this, though, is just IMHO too...

Andy
 
M

Mike Treseler

Eli said:
Interesting approach, but not to my taste, I think:
1) It interferes with the way I understand synthesizable code. I like
to be able to envision exactly how the hardware that's built from my
code looks

The actual hardware that's built is an
incomprehensible netlist of look up tables,
registers and wires. All else is abstraction.
I like to be assured that synthesis will
supply a netlist that works exactly the
same as the simulation of my preferred abstraction,
the vhdl synchronous process. Being assured
that this same netlist will also meet my Fmax constraints
seals the deal for me.
- it aids greatly in understanding complex timing demands,
and overall I find that code written is such a way behaves very
similarly in real life and in simulation.

If that is your preference, then the overhead
of maintaining multiple processes and the
wiring between them is part of the bargain.
2) It is not flexible enough for large designs where usage of
'generate' is sometimes essential to avoid duplications, and where the
additional level of abstraction allowed by multiple processes is
precious.

Except for the case of tri-state nodes,
any synchronous logic description that can be
generated from multiple processes can also
be described by a single synchronous process
by using variable, procedure and function declarations.
3) I really like knowing where each signal is assigned, without looking
around too much. When each signal has one and only one process that
'drives' it, things are simpler to understand.

I prefer to use signals at the top level
to wire up single-process entity instances.
Port modes (in, out) provide a compile-time
wiring check that is lacking in process to
process wiring.
This all is, of course, IMHO :)

Same here.

-- Mike Treseler
 
E

Eli Bendersky

Allan said:
It works inside a combinatorial process. No clock delay is generated.
The language already does what you want in this regard. There is no
need to change anything.

What should be in the sensitivity list of this process ? All of my_sig
signals ?
 
A

Allan Herriman

What should be in the sensitivity list of this process ? All of my_sig
signals ?

another_bus is needed as well. Or you could wait for the next version
of VHDL which allows you to have a sensitivity list that automatically
includes all the signals needed.

Regards,
Allan
 

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,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top