Reading an Array of vectors.

N

Niv (KP)

Guest
I have an interface to a set of identical ADC's, the number of which
is set by a generic "N", (range of 1 to 16)
All are triggered & sampled in parallel, so only one clock & enable
generated, but "N" data paths back

I have to read the vectors over a CPU bus one at a time.

As the number of vectors is generic dependant, how to I code the case
statement for the reads.

e.g.

CASE address IS
WHEN "0000" => read_bus <= adc_bus(0);
WHEN "0001" => read_bus <= adc_bus(1);

-- etc
END CASE;

What I want to do is loop on 0 to (N-1 ) on the CASE satement.

The only way I can think of is to create an array 16 wide of (31
downto 0), default assign all to '0's
then in a loop of 0 to (N- 1) assign this new array to the incoming
array.

I then code the case statement so it has all 16 possible states, and
reads this new array one slice at a time.
Software can decide how to read depending on the actual number of ADCs
instantiated.

Is there a better way? (Not used arrays of vectors with generic ports
etc before, if you hadn't already guessed)!
I could hard code the whole lot for a fixed number of ADC's very
easily, but RE_USE is key at the moment.

TIA, Niv.
 
Niv (KP) wrote:

I have an interface to a set of identical ADC's, the number of which
is set by a generic "N", (range of 1 to 16)
All are triggered & sampled in parallel, so only one clock & enable
generated, but "N" data paths back

I have to read the vectors over a CPU bus one at a time.

As the number of vectors is generic dependant, how to I code the case
statement for the reads.

e.g.

CASE address IS
WHEN "0000" => read_bus <= adc_bus(0);
WHEN "0001" => read_bus <= adc_bus(1);

-- etc
END CASE;

What I want to do is loop on 0 to (N-1 ) on the CASE satement.

The only way I can think of is to create an array 16 wide of (31
downto 0), default assign all to '0's
then in a loop of 0 to (N- 1) assign this new array to the incoming
array.

I then code the case statement so it has all 16 possible states, and
reads this new array one slice at a time.
Software can decide how to read depending on the actual number of ADCs
instantiated.

Is there a better way? (Not used arrays of vectors with generic ports
etc before, if you hadn't already guessed)!
I could hard code the whole lot for a fixed number of ADC's very
easily, but RE_USE is key at the moment.

TIA, Niv.
I haven't studied the netlist to verify how this gets translated, but the way I've solved this in my projects that seems to simulate and synthesize fine is:

FOR i IN adc_bus'RANGE LOOP
IF (address = CONV_STD_LOGIC_VECTOR(i, 5)) THEN
read_bus <= adc_bus(i);
END IF;
END LOOP;

This should unroll into a series of IF statements at synthesis time - one for each possible value of address.

Ken
 
Ken Cecka wrote:

Niv (KP) wrote:

I have an interface to a set of identical ADC's, the number of which
is set by a generic "N", (range of 1 to 16)
All are triggered & sampled in parallel, so only one clock & enable
generated, but "N" data paths back

I have to read the vectors over a CPU bus one at a time.

As the number of vectors is generic dependant, how to I code the case
statement for the reads.

e.g.

CASE address IS
WHEN "0000" => read_bus <= adc_bus(0);
WHEN "0001" => read_bus <= adc_bus(1);

-- etc
END CASE;

What I want to do is loop on 0 to (N-1 ) on the CASE satement.

The only way I can think of is to create an array 16 wide of (31
downto 0), default assign all to '0's
then in a loop of 0 to (N- 1) assign this new array to the incoming
array.

I then code the case statement so it has all 16 possible states, and
reads this new array one slice at a time.
Software can decide how to read depending on the actual number of ADCs
instantiated.

Is there a better way? (Not used arrays of vectors with generic ports
etc before, if you hadn't already guessed)!
I could hard code the whole lot for a fixed number of ADC's very
easily, but RE_USE is key at the moment.

TIA, Niv.

I haven't studied the netlist to verify how this gets translated, but the
way I've solved this in my projects that seems to simulate and synthesize
fine is:

FOR i IN adc_bus'RANGE LOOP
IF (address = CONV_STD_LOGIC_VECTOR(i, 5)) THEN
read_bus <= adc_bus(i);
END IF;
END LOOP;

This should unroll into a series of IF statements at synthesis time - one
for each possible value of address.

Ken
Forgot to mention that you can get the the equivalent of a WHEN OTHERS by adding an initial assignment to read_bus before the loop:

read_bus <= (OTHERS => '0');
FOR i IN adc_bus'RANGE LOOP
IF (address = CONV_STD_LOGIC_VECTOR(i, 5)) THEN
read_bus <= adc_bus(i);
END IF;
END LOOP;
 
Ken Cecka wrote:
I haven't studied the netlist to verify how this gets translated, but the way I've solved this in my projects that seems to simulate and synthesize fine is:

FOR i IN adc_bus'RANGE LOOP
IF (address = CONV_STD_LOGIC_VECTOR(i, 5)) THEN
read_bus <= adc_bus(i);
END IF;
END LOOP;
Note that conv_std_logic_vector is from the package
ieee.std_logic_unsigned. Either include that package or
do it using numeric_std only:
if ( unsigned(address) = i ) then

Note the overloading of numeric_std allows comparison of a
signed or unsigned type with an integer.

On the other hand, you can avoid the looping by using the
following (one step, no loops, no if statement). to_integer
is from package numeric_std.

library ieee ;
use ieee.numeric_std.all ;
.. . .

read_bus <= adc_bus(to_integer(unsigned(address))) ;

Cheers,
Jim
--
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Jim Lewis SynthWorks VHDL Training http://www.synthworks.com

A bird in the hand may be worth two in the bush,
but it sure makes it hard to type.
 
Niv (KP) wrote:
I have an interface to a set of identical ADC's, the number of which
is set by a generic "N", (range of 1 to 16)
All are triggered & sampled in parallel, so only one clock & enable
generated, but "N" data paths back

I have to read the vectors over a CPU bus one at a time.

As the number of vectors is generic dependant, how to I code the case
statement for the reads.

e.g.

CASE address IS
WHEN "0000" => read_bus <= adc_bus(0);
WHEN "0001" => read_bus <= adc_bus(1);

-- etc
END CASE;

What I want to do is loop on 0 to (N-1 ) on the CASE satement.
For case statements, the selector has to be static. For if statements,
that's not true. So you could do

library ieee;
use ieee.numeric_std.all;

....

process(clock)
begin
if rising_edge(clock) then
if enable = '1' then
for i in 0 to n-1 loop
if to_integer(unsigned(address)) = i then
adc_bus(i) <= read_bus;
end if;
end loop;
end if;
end process;

This assumes you want a set of flip-flops on each adc_bus output.
You could also specify a reset as well of course if you have one.

regards
Alan

--
Alan Fitch
Doulos
http://www.doulos.com
 
Alan Fitch wrote:
Niv (KP) wrote:
I have an interface to a set of identical ADC's, the number of which
is set by a generic "N", (range of 1 to 16)
All are triggered & sampled in parallel, so only one clock & enable
generated, but "N" data paths back

I have to read the vectors over a CPU bus one at a time.

As the number of vectors is generic dependant, how to I code the case
statement for the reads.

e.g.

CASE address IS
WHEN "0000" => read_bus <= adc_bus(0);
WHEN "0001" => read_bus <= adc_bus(1);

-- etc
END CASE;

What I want to do is loop on 0 to (N-1 ) on the CASE satement.


For case statements, the selector has to be static. For if statements,
that's not true. So you could do

library ieee;
use ieee.numeric_std.all;

....

process(clock)
begin
if rising_edge(clock) then
if enable = '1' then
for i in 0 to n-1 loop
if to_integer(unsigned(address)) = i then
adc_bus(i) <= read_bus;
end if;
end loop;
end if;
end process;

This assumes you want a set of flip-flops on each adc_bus output.
You could also specify a reset as well of course if you have one.

regards
Alan
Oops, I've done that all wrong - of course you are assigning read_bus,

process(clock)
begin
if rising_edge(clock) then
if enable = '1' then
for i in 0 to n-1 loop
if to_integer(unsigned(address)) = i then
read_bus(i) <= adc_bus(i);
end if;
end loop;
end if;
end process;

Due to "last assignment wins" a.k.a intertial delay, this will mean
effectively there is a higher priority to higher addresses - but as only
one address can be true (they're mutually exclusive) I would hope
synthesis would produce sensible code,

regards
Alan

--
Alan Fitch
Doulos
http://www.doulos.com
 
On Fri, 13 Feb 2009 17:34:05 +0000, Alan Fitch wrote:

process(clock)
begin
if rising_edge(clock) then
if enable = '1' then
for i in 0 to n-1 loop
if to_integer(unsigned(address)) = i then
read_bus(i) <= adc_bus(i);
end if;
end loop;
end if;
end process;

Due to "last assignment wins" a.k.a intertial delay, this will mean
effectively there is a higher priority to higher addresses - but as only
one address can be true (they're mutually exclusive) I would hope
synthesis would produce sensible code,
If synthesis doesn't sort out the mutual-exclusivity
for itself, you can give it a helpful hint by implying
an AND-OR tree instead of the pri-mux. I've used this
with some success in designs that have a large number
of readable registers.

Similar overall shape to Alan's solution, but with an
extra variable to accumulate the sum-of-products:

process(clock)
variable readback: std_logic_vector(read_bus'range);
begin
if rising_edge(clock) then
readback := (others => '0'); -- start with all-zero
if enable = '1' then
for i in 0 to n-1 loop
if to_integer(unsigned(address)) = i then
readback := readback or adc_bus(i);
end if;
end loop;
read_bus <= readback;
end if;
end process;

Note that this iterate-through-the-addresses arrangement
also allows for more complex address matching schemes.
Instead of the equality test on the address, consider
using a user-written address matching function:

if adrs_match(address, i) then ...

This allows for sparse address spaces, "registers" that
span more than one address (they would then use a few
low-order address bits internally to determine their
functionality), and "interesting" variable register maps.
--
Jonathan Bromley, Consultant

DOULOS - Developing Design Know-how
VHDL * Verilog * SystemC * e * Perl * Tcl/Tk * Project Services

Doulos Ltd., 22 Market Place, Ringwood, BH24 1AW, UK
jonathan.bromley@MYCOMPANY.com
http://www.MYCOMPANY.com

The contents of this message may contain personal views which
are not the views of Doulos Ltd., unless specifically stated.
 
On 13 Feb, 17:46, Jonathan Bromley <jonathan.brom...@MYCOMPANY.com>
wrote:
On Fri, 13 Feb 2009 17:34:05 +0000, Alan Fitch wrote:
   process(clock)
   begin
     if rising_edge(clock) then
       if enable = '1' then
         for i in 0 to n-1 loop
           if to_integer(unsigned(address)) = i then
             read_bus(i) <= adc_bus(i);
           end if;
         end loop;
       end if;
   end process;

Due to "last assignment wins" a.k.a intertial delay, this will mean
effectively there is a higher priority to higher addresses - but as only
one address can be true (they're mutually exclusive) I would hope
synthesis would produce sensible code,

If synthesis doesn't sort out the mutual-exclusivity
for itself, you can give it a helpful hint by implying
an AND-OR tree instead of the pri-mux.  I've used this
with some success in designs that have a large number
of readable registers.  

Similar overall shape to Alan's solution, but with an
extra variable to accumulate the sum-of-products:

    process(clock)
      variable readback: std_logic_vector(read_bus'range);
    begin
      if rising_edge(clock) then
        readback := (others => '0');  -- start with all-zero
        if enable = '1' then
          for i in 0 to n-1 loop
            if to_integer(unsigned(address)) = i then
              readback := readback or adc_bus(i);
            end if;
          end loop;
          read_bus <= readback;
        end if;
    end process;

Note that this iterate-through-the-addresses arrangement
also allows for more complex address matching schemes.
Instead of the equality test on the address, consider
using a user-written address matching function:

  if adrs_match(address, i) then ...

This allows for sparse address spaces, "registers" that
span more than one address (they would then use a few
low-order address bits internally to determine their
functionality), and "interesting" variable register maps.
--
Jonathan Bromley, Consultant

DOULOS - Developing Design Know-how
VHDL * Verilog * SystemC * e * Perl * Tcl/Tk * Project Services

Doulos Ltd., 22 Market Place, Ringwood, BH24 1AW, UK
jonathan.brom...@MYCOMPANY.comhttp://www.MYCOMPANY.com

The contents of this message may contain personal views which
are not the views of Doulos Ltd., unless specifically stated.- Hide quoted text -

- Show quoted text -
Thanks chaps,
Plenty of options & food for thought there.
Regards, Niv.
 
On Feb 13, 11:27 am, Jim Lewis <j...@synthworks.com> wrote:
On the other hand, you can avoid the looping by using the
following (one step, no loops, no if statement).  to_integer
is from package numeric_std.

library ieee ;
use ieee.numeric_std.all ;
. . .

read_bus <= adc_bus(to_integer(unsigned(address))) ;
This works so long as any possible value on address is within the
index range of adc_bus (or if you can use the mod operator on address
to limit it). It is quick and to the point though. Otherwise, the for-
loop with a comparison of address and each possible index is required.

Andy
 
On Feb 13, 11:34 am, Alan Fitch <alan.fi...@spamtrap.com> wrote:
Due to "last assignment wins" a.k.a intertial delay, this will mean
effectively there is a higher priority to higher addresses - but as only
one address can be true (they're mutually exclusive) I would hope
synthesis would produce sensible code,
You can use an exit statement, and/or alter the direction of the loop
index range to alter the priority of the unrolled if statements.
Although in most cases using equality comparisons to the same object
(address in this case), the synthesis tool will figure out that the
comparisons are mutually exclusive. Thus no priority is assigned,
rendering index order and/or exit statements moot, except with regard
to simulation performance.

Within vhdl/process contexts, "last assignment wins" and "inertial
delay" are completely different concepts. Inertial delay has to do
with multiple waveforms driven onto one signal at different simulation
times, and which one will take precedence. Inside a process, multiple
assignments between waits happen in zero simulation time, and there is
only one value/waveform that actually gets promoted to that process's
lone driver for the signal (and it is the last assignment that is
promoted).

Andy
 
On Fri, 13 Feb 2009 13:09:13 -0800 (PST), Andy wrote:

Within vhdl/process contexts, "last assignment wins" and "inertial
delay" are completely different concepts.
Sorry Andy, I don't think that is correct at all.

Inertial delay has to do
with multiple waveforms driven onto one signal at different simulation
times
Or at the same time. One consistent set of rules covers all cases.

Inside a process, multiple
assignments between waits happen in zero simulation time
For sure.

only one value/waveform that actually gets promoted to that process's
lone driver for the signal (and it is the last assignment that is
promoted).
Yes, but that behaviour is only guaranteed BECAUSE OF the
inertial delay rules. Contrast with zero-delay nonblocking
assignments in Verilog, which use non-overwriting transport
delay; Verilog is obliged to introduce an intuitively
sensible, but conceptually tiresome, kludge into the
language definition to guarantee that multiple updates
on the same signal have the effect that you expect.
Even then, the use of transport rather than inertial
delay raises the possibility of unwanted zero-width
glitches that may or may not be seen as events in
simulation. And the absence of inertial delay for
procedural signal assignment makes it impossible to
rescind a future scheduled signal update in a reliable
manner.

So, all in all, inertial delay is rather important for
getting sensible last-write-wins behaviour for signal
assignment.

Of course you get last-write-wins for variable assignment,
because each new assignment supplants the variable's
existing value immediately.
--
Jonathan Bromley, Consultant

DOULOS - Developing Design Know-how
VHDL * Verilog * SystemC * e * Perl * Tcl/Tk * Project Services

Doulos Ltd., 22 Market Place, Ringwood, BH24 1AW, UK
jonathan.bromley@MYCOMPANY.com
http://www.MYCOMPANY.com

The contents of this message may contain personal views which
are not the views of Doulos Ltd., unless specifically stated.
 
On Mon, 16 Feb 2009 11:23:28 -0800 (PST), Andy wrote:

If I read your argument correctly, one should be able to use transport
delay to allow prior writes (in same process cycle) to show up in the
waveform...
I'm not 100% sure I understand what you mean, so please
excuse me if I've misunderstood.

A transport signal assignment in VHDL:

S <= transport EXPR after T;

schedules an update of S to EXPR at time NOW+T,
and also deletes any pending updates on S for
times >= NOW+T. Pending updates on S for
times < NOW+T are preserved, however.

You can't get more than one update on a signal
in a given delta cycle in VHDL, no matter what you do.
In Verilog, however, you *can* get multiple updates
on a signal within whatever passes for a delta in
Verilog. Most simulators would probably optimize away
any but the last such update at any time in the future,
but they are not gnerally able to do so if the updates
happen at time NOW. This awkwardness occurs because
*any* variable in Verilog, including those updated
by blocking assignment (which works like := in
VHDL), can trigger a value-change event.

Whether you can see such within-a-delta changes on
a wave viewer is moot. It's not even guaranteed
that they would yield a value-change event. Try
this in Verilog:

module intra_delta;
reg s;
initial begin
s = 0; // at time 0
#10 // get to time 10
s = 1;
s = 0; // happens in the same "delta" as s=1
#10
s = 1;
end
always @(s)
$display("s changed to %b at t=%0d", s, $time);
endmodule

You will very definitely get a value-change event on s
at time 0 (change from X to 0) and at 20 (from 0 to 1),
although the event at time 0 may not be detected
because it races with the always @(s) - which may get
to its @(s) event control *after* the event has occurred.
But what happens at time 10? Hmmm.

I get three different results from three different
major simulators on that piece of code, and they're
all correct:

Simulator A:
s changed to 0 at t=0
s changed to 0 at t=10
s changed to 1 at t=20

Simulator B:
s changed to 0 at t=0
s changed to 1 at t=20

Simulator C:
s changed to 0 at t=10
s changed to 1 at t=20

If that doesn't send you scurrying back to the comfort
and sanity of VHDL, I don't know what will :)
--
Jonathan Bromley, Consultant

DOULOS - Developing Design Know-how
VHDL * Verilog * SystemC * e * Perl * Tcl/Tk * Project Services

Doulos Ltd., 22 Market Place, Ringwood, BH24 1AW, UK
jonathan.bromley@MYCOMPANY.com
http://www.MYCOMPANY.com

The contents of this message may contain personal views which
are not the views of Doulos Ltd., unless specifically stated.
 
On Feb 14, 2:58 am, Jonathan Bromley <jonathan.brom...@MYCOMPANY.com>
wrote:
On Fri, 13 Feb 2009 13:09:13 -0800 (PST), Andy wrote:
Within vhdl/process contexts, "last assignment wins" and "inertial
delay" are completely different concepts.

Sorry Andy, I don't think that is correct at all.

Inertial delay has to do
with multiple waveforms driven onto one signal at different simulation
times

Or at the same time.  One consistent set of rules covers all cases.

Inside a process, multiple
assignments between waits happen in zero simulation time

For sure.

only one value/waveform that actually gets promoted to that process's
lone driver for the signal (and it is the last assignment that is
promoted).

Yes, but that behaviour is only guaranteed BECAUSE OF the
inertial delay rules.  Contrast with zero-delay nonblocking
assignments in Verilog, which use non-overwriting transport
delay; Verilog is obliged to introduce an intuitively
sensible, but conceptually tiresome, kludge into the
language definition to guarantee that multiple updates
on the same signal have the effect that you expect.
Even then, the use of transport rather than inertial
delay raises the possibility of unwanted zero-width
glitches that may or may not be seen as events in
simulation.  And the absence of inertial delay for
procedural signal assignment makes it impossible to
rescind a future scheduled signal update in a reliable
manner.

So, all in all, inertial delay is rather important for
getting sensible last-write-wins behaviour for signal
assignment.
If I read your argument correctly, one should be able to use transport
delay to allow prior writes (in same process cycle) to show up in the
waveform...

Andy
 
On Mon, 16 Feb 2009 12:03:12 -0800 (PST), Andy wrote:

process(clk) is
begin
if rising_edge(clk) then
s <= transport e;
s <= transport e + 1;
s <= transport e + 2;
end if;
end process;

Per your argument, for how long should e and e+1 show up before the
value of s settles on e+2?
They won't show up at all - see my post of a few minutes ago.

However, the point I was making is this: the fact that such
value changes *can* turn up in Verilog is living proof that
inertial delay is necessary to get sensible last-write-wins.
Here's my favourite example of this: Suppose someone uses
"after" delay to mimic clock-to-output delay:

if rising_edge(clk) then
s <= '0' after 1 ns; -- default
if (horrible) then
s <= '1'; --- oops, forgot the delay
end if;
end if;

In VHDL, as above, executing the conditional s<='1'
trumps the delayed default assignment. You don't get
the delay model, but everything works just fine.
s goes to '1' at the clock edge and then stays there.

In Verilog, by contrast,

always @(posedge clk) begin
s <= #1 0; -- default
if (horrible)
s <= 1; -- oops, forgot the delay
end

s<=1 does NOT trump the default assignment, so you
get s pulsing to 1 (the correct value) one delta
after the clock, and then lapsing back to the (wrong)
default after one time unit.

So, what happens in Verilog if you do

module intra_delta;
reg s;
initial begin
s <= 0; // at time 0
#10 // get to time 10
s <= 1;
s <= 0; // happens in the same "delta" as s=1
#10
s <= 1;
end
always @(s)
$display("s changed to %b at t=%0d", s, $time);
endmodule

(same example as in my previous post, but with
nonblocking assignments)? Answer: Same as you
saw with the blocking assignments:

s changed to 0 at t=0
s changed to 0 at t=10 // there must have been a glitch
s changed to 1 at t=20

Weird or what?

Of course, if I comment out the s<=1 statement at #10,
I see no event at t=10.
--
Jonathan Bromley, Consultant

DOULOS - Developing Design Know-how
VHDL * Verilog * SystemC * e * Perl * Tcl/Tk * Project Services

Doulos Ltd., 22 Market Place, Ringwood, BH24 1AW, UK
jonathan.bromley@MYCOMPANY.com
http://www.MYCOMPANY.com

The contents of this message may contain personal views which
are not the views of Doulos Ltd., unless specifically stated.
 
On Feb 16, 1:23 pm, Andy <jonesa...@comcast.net> wrote:
If I read your argument correctly, one should be able to use transport
delay to allow prior writes (in same process cycle) to show up in the
waveform...

Andy
To illustrate:

process(clk) is
begin
if rising_edge(clk) then
s <= transport e;
s <= transport e + 1;
s <= transport e + 2;
end if;
end process;

Per your argument, for how long should e and e+1 show up before the
value of s settles on e+2?

Andy
 
On Tue, 17 Feb 2009 05:23:03 -0800 (PST), "Niv (KP)" wrote:

I wasn't quite right when I said "N" was a generic!

PACKAGE ad7476_pkg IS

CONSTANT num_of_convs : INTEGER := 5; -- Number of parallel
ADC's.
I don't think you need this constant at all.

TYPE d_conv_bus IS ARRAY(num_of_convs-1 DOWNTO 0)
OF STD_LOGIC_VECTOR(31 DOWNTO 0);
Can you use an unconstrained type?

SUBTYPE adc_word IS STD_LOGIC_VECTOR(31 DOWNTO 0);
TYPE d_conv_bus IS ARRAY(natural range <>) OF adc_word;
TYPE d_convs_in IS ARRAY(natural range <>) OF std_logic;

I would like to instnace this top level block with a generic that
overrides the package constant
"num_of_convs", but can't figure how to do this.

As it is, with the constant 5, all works fine, and I can read the
values back as suggested in earlier posts.
I'd just like to make it more flexible.
OK, so now you can write stuff like

entity ADC_controller is
generic (num_ADCs: positive := 5);
port (adc_data: IN d_conv_bus(num_ADCs-1 downto 0);
adc_rdy : IN d_convs_in(num_ADCs-1 downto 0);

Note that d_convs_in is now equivalent to std_logic_vector,
but is a different type - so you will lose all the
usual std_logic_vector operations on it. You may prefer
to use an ordinary std_logic_vector for that one.

Another thought: you could consider

TYPE d_conv_rec IS RECORD
data: adc_word;
strobe: std_logic;
END RECORD;

TYPE d_conv_array IS ARRAY (natural range <>) OF d_conv_rec;

This may be convenient, maybe not, depending on what your
design actually does with it. If you do declare such a record
you will also need some "bit-picking" functions. For example,
to get the "d_conv_in" array of std_logic, one bit per ADC,
from the big array of records:

function get_dconv_in(d: d_conv_array) return std_logic_vector is
variable it: std_logic_vector(d'range);
begin
for i in d'range loop
it(i) := d(i).strobe;
end loop;
return it;
end;

Functions like this are doing nothing more than steering
connections, so they are free in synthesis (apart from any
routing implications). It's still worth the trouble of
writing them, to keep the code clean, in my experience.
--
Jonathan Bromley, Consultant

DOULOS - Developing Design Know-how
VHDL * Verilog * SystemC * e * Perl * Tcl/Tk * Project Services

Doulos Ltd., 22 Market Place, Ringwood, BH24 1AW, UK
jonathan.bromley@MYCOMPANY.com
http://www.MYCOMPANY.com

The contents of this message may contain personal views which
are not the views of Doulos Ltd., unless specifically stated.
 
On 13 Feb, 19:49, "Niv (KP)" <kev.pars...@mbda-systems.com> wrote:
On 13 Feb, 17:46, Jonathan Bromley <jonathan.brom...@MYCOMPANY.com
wrote:





On Fri, 13 Feb 2009 17:34:05 +0000, Alan Fitch wrote:
   process(clock)
   begin
     if rising_edge(clock) then
       if enable = '1' then
         for i in 0 to n-1 loop
           if to_integer(unsigned(address)) = i then
             read_bus(i) <= adc_bus(i);
           end if;
         end loop;
       end if;
   end process;

Due to "last assignment wins" a.k.a intertial delay, this will mean
effectively there is a higher priority to higher addresses - but as only
one address can be true (they're mutually exclusive) I would hope
synthesis would produce sensible code,

If synthesis doesn't sort out the mutual-exclusivity
for itself, you can give it a helpful hint by implying
an AND-OR tree instead of the pri-mux.  I've used this
with some success in designs that have a large number
of readable registers.  

Similar overall shape to Alan's solution, but with an
extra variable to accumulate the sum-of-products:

    process(clock)
      variable readback: std_logic_vector(read_bus'range);
    begin
      if rising_edge(clock) then
        readback := (others => '0');  -- start with all-zero
        if enable = '1' then
          for i in 0 to n-1 loop
            if to_integer(unsigned(address)) = i then
              readback := readback or adc_bus(i);
            end if;
          end loop;
          read_bus <= readback;
        end if;
    end process;

Note that this iterate-through-the-addresses arrangement
also allows for more complex address matching schemes.
Instead of the equality test on the address, consider
using a user-written address matching function:

  if adrs_match(address, i) then ...

This allows for sparse address spaces, "registers" that
span more than one address (they would then use a few
low-order address bits internally to determine their
functionality), and "interesting" variable register maps.
--
Jonathan Bromley, Consultant

DOULOS - Developing Design Know-how
VHDL * Verilog * SystemC * e * Perl * Tcl/Tk * Project Services

Doulos Ltd., 22 Market Place, Ringwood, BH24 1AW, UK
jonathan.brom...@MYCOMPANY.comhttp://www.MYCOMPANY.com

The contents of this message may contain personal views which
are not the views of Doulos Ltd., unless specifically stated.- Hide quoted text -

- Show quoted text -

Thanks chaps,
Plenty of options & food for thought there.
Regards, Niv.- Hide quoted text -

- Show quoted text -
I wasn't quite right when I said "N" was a generic!

I have a package with the following:

PACKAGE ad7476_pkg IS

CONSTANT num_of_convs : INTEGER := 5; -- Number of parallel
ADC's.


TYPE d_conv_bus IS ARRAY(num_of_convs-1 DOWNTO 0)
OF STD_LOGIC_VECTOR(31 DOWNTO 0);

TYPE d_convs_in IS ARRAY(num_of_convs-1 DOWNTO 0)
OF STD_LOGIC;

END ad7476_pkg;

The "d_conv_bus" is used to declare the o/p port on teh ADC controller
and the
i/p port on my address decoder, so the results get read in ascending
order (loop dependent).
The ADC controller also has an i/p port of "d_convs_in", which
promulgates to the top level of the block.

I would like to instnace this top level block with a generic that
overrides the package constant
"num_of_convs", but can't figure how to do this.

As it is, with the constant 5, all works fine, and I can read the
values back as suggested in earlier posts.
I'd just like to make it more flexible.

TIA, Niv.
 
On Feb 16, 2:13 pm, Jonathan Bromley <jonathan.brom...@MYCOMPANY.com>
wrote:
On Mon, 16 Feb 2009 11:23:28 -0800 (PST), Andy wrote:
If I read your argument correctly, one should be able to use transport
delay to allow prior writes (in same process cycle) to show up in the
waveform...

I'm not 100% sure I understand what you mean, so please
excuse me if I've misunderstood.

A transport signal assignment in VHDL:

  S <= transport EXPR after T;

schedules an update of S to EXPR at time NOW+T,
and also deletes any pending updates on S for
times >= NOW+T.  Pending updates on S for
times < NOW+T are preserved, however.
In my way of thinking, Inertial and Transport mechanisms are opposite
of each other. If (and I don't think it does) the inertial delay
mechanism is causing some behavior (discarding all but the last of all
non-delayed outputs in the delta cycle following the process cycle),
then transport delay ought to avoid that behavior (allow the multiple
outputs to appear), which we both agree will not happen.

You can't get more than one update on a signal
in a given delta cycle in VHDL, no matter what you do.
THIS is exactly what I'm getting at. The fact that the last assignment
in a process cycle wins (esp. among assignments with no delays) has
nothing to do with inertial delay, it has to do with the fact that all
of the (zero delay) signal updates from a single process cycle are
effective in the same delta cycle, and there is no smaller subdivision
of "time" to spread multiple updates over. So the last assignment is
chosen as the update for that delta cycle, and earlier ones are
discarded.

In other words, the reason that multiple delta cycles are not used,
and therefore only one (any one) assignment must be chosen to be
promoted, has nothing to do with inertial delay. I understand your
assertion that the reason the last one is chosen is at least
consistent with, if not controlled by, the inertial delay mechanism.

Perhaps to help illustrate this:

process (e)
variable temp : unsigned(s'range);
begin
temp := e;
temp := e+1;
temp := e+2;
s <= temp;
end process;

Is the above any different from:

process (e)
begin
s <= e;
s <= e+1;
s <= e+2;
end process;

Maybe this is all in how the simulator optimizes execution (i.e.
removing prior writes up front, rather than using the signal update
(inertial) mechanism to do so). Just for the record, I think we both
agree there is absolutely no difference WRT synthesis.

Thank you for the explanation of verilog behavior. It reinforces my
desire not to migrate to verilog (or SystemVerilog, if it has these
same issues).

Andy
 
On Feb 17, 9:44 am, Andy <jonesa...@comcast.net> wrote:
On Feb 16, 2:13 pm, Jonathan Bromley <jonathan.brom...@MYCOMPANY.com
wrote:



On Mon, 16 Feb 2009 11:23:28 -0800 (PST), Andy wrote:
If I read your argument correctly, one should be able to use transport
delay to allow prior writes (in same process cycle) to show up in the
waveform...

I'm not 100% sure I understand what you mean, so please
excuse me if I've misunderstood.

A transport signal assignment in VHDL:

  S <= transport EXPR after T;

schedules an update of S to EXPR at time NOW+T,
and also deletes any pending updates on S for
times >= NOW+T.  Pending updates on S for
times < NOW+T are preserved, however.

In my way of thinking, Inertial and Transport mechanisms are opposite
of each other. If (and I don't think it does) the inertial delay
mechanism is causing some behavior (discarding all but the last of all
non-delayed outputs in the delta cycle following the process cycle),
then transport delay ought to avoid that behavior (allow the multiple
outputs to appear), which we both agree will not happen.

You can't get more than one update on a signal
in a given delta cycle in VHDL, no matter what you do.

THIS is exactly what I'm getting at. The fact that the last assignment
in a process cycle wins (esp. among assignments with no delays) has
nothing to do with inertial delay, it has to do with the fact that all
of the (zero delay) signal updates from a single process cycle are
effective in the same delta cycle, and there is no smaller subdivision
of "time" to spread multiple updates over. So the last assignment is
chosen as the update for that delta cycle, and earlier ones are
discarded.

In other words, the reason that multiple delta cycles are not used,
and therefore only one (any one) assignment must be chosen to be
promoted, has nothing to do with inertial delay. I understand your
assertion that the reason the last one is chosen is at least
consistent with, if not controlled by, the inertial delay mechanism.

Perhaps to help illustrate this:

process (e)
  variable temp : unsigned(s'range);
begin
  temp := e;
  temp := e+1;
  temp := e+2;
  s <= temp;
end process;

Is the above any different from:

process (e)
begin
  s <= e;
  s <= e+1;
  s <= e+2;
end process;

Maybe this is all in how the simulator optimizes execution (i.e.
removing prior writes up front, rather than using the signal update
(inertial) mechanism to do so). Just for the record, I think we both
agree there is absolutely no difference WRT synthesis.

Thank you for the explanation of verilog behavior. It reinforces my
desire not to migrate to verilog (or SystemVerilog, if it has these
same issues).

Andy
Never one to leave a dead horse undisturbed, I'll throw in a few
kicks, too. I think there's a bit of confusion arising because of the
unconcscious meanings we associate with the word "inertial", as well
as what the inertial versus transport concept is applied to.

Inertial (to me at least) implies that some kind of time filtering or
pulse rejection is happening, while transport is more of a pure
transmission line delay. However, this intuition can be misleading.
This separation only comes into play for non-zero delays, and is not
really an accurate picture of what happens in the simplest case. In
the simplest case (where there is no delay specified), the inertial
assignment
s <= e;
is completely identical to
s <= e after 0 ns;
which is also completely identical to
s <= e after 0 ns reject 0 NS;
which is also completely identical to
s <= transport e after 0 ns;

The rules of the language say that for each assignment statement in
one process (i.e. assignment to the same driver), you can think of the
current state of the driver as a queue of scheduled events, and the
waveform on the right hand side of the assignment as another list of
events. The assignment is just the act of merging these event lists.
The transport and inertial concepts, as Jonathan pointed out, only
change what happens to events earlier than NOW+t1, for t1 the earliest
time in your waveform. For the usual "s <= e;" statement, t1 is zero,
and there's no difference between transport and inertial for that
case.

The "magic" of resolving everything so that it does what we expect is
because the rules make it so that the driver only ever has one event
scheduled for a given time. This is simply to ensure sanity of the
queue - you will never have six things scheduled up for time now+0. It
has nothing to do with whether the assignment is inertial, or
transport, or has zero or non-zero time delay. When the time in
question is "now", then the rules mean that

s <= e;
s <= e+1;
s <= e+2;

make the first waveform element in the driver queue (scheduled for
"now", hence the next delta cycle) keep getting overwritten at each
assignment, before the process ever blocks. The first two assignments
effectively disappear before they're ever acted on.

The other fine point is that transport and inertial refer only to one
specific assignment statement. It is completely possible to mix
inertial and transport assignments in a process:

s <= transport e after 5 ns;
s <= e+1 after 0 ns reject 10 ns;
s <= e + 2;

and still have completely well-defined behaviour.

- Kenn
 
On Tue, 17 Feb 2009 06:44:08 -0800 (PST), Andy wrote:

In my way of thinking, Inertial and Transport mechanisms are opposite
of each other.
I would say that it's not quite that simple. In particular,
note that BOTH mechanisms invariably delete all existing
scheduled assignments at times >= NOW+T when you do
s <= [transport] expr after T;
(and, of course, T is one delta when it's specified as
zero-time or not specified at all).

You can't get more than one update on a signal
in a given delta cycle in VHDL, no matter what you do.

THIS is exactly what I'm getting at. The fact that the last assignment
in a process cycle wins (esp. among assignments with no delays) has
nothing to do with inertial delay
I don't imagine either of us wants to get into hair-splitting
and basically I completely agree with you. However, it is
only because of the inertial (and, by-the-by, transport) delay
rules that you are guaranteed last-write-wins. If the rules
had been written slightly differently you might have got
first-write-wins. Of course that isn't what happens,
because the rules are sensible. But the fact that there
can be only one update in a given delta does not, of itself,
necessarily mean that it's the LAST update that wins.
Additional rules are required to assure that.

the last assignment is
chosen as the update for that delta cycle, and earlier ones are
discarded.
Indeed. It would be a strange and bad world were this not so :)

I understand your
assertion that the reason the last one is chosen is at least
consistent with, if not controlled by, the inertial delay mechanism.
Yeah... the whole mechanism is really just one coherent thing,
and last-write-wins is just one of many outcomes of that.
I guess it may be unfair to lay all the blame on inertial
delay. Nevertheless, we find it's a very useful prop for
students to hang their understanding on. The behaviour
of the future-update queue of a VHDL signal is far from
trivial and is a lot to bite off at one sitting.

begin
temp := e;
temp := e+1;
temp := e+2;
s <= temp;
end process;

Is the above any different from:

process (e)
begin
s <= e;
s <= e+1;
s <= e+2;
end process;
In terms of the effect on s, definitely not. As far as I
understand, there would be no way in VHDL to tell the
difference unless you were running a debugger that could
single-step the statements in that process, and thus see
the variable updates happening one-by-one. I'm not aware
of any simulator that shows you the future-event queue of
a signal in its debugger - does anyone know otherwise?

I would also hope that a good simulator would take
the obvious optimization opportunity in both cases.

Thank you for the explanation of verilog behavior. It reinforces my
desire not to migrate to verilog (or SystemVerilog, if it has these
same issues).
Verilog's scheduling semantics have been painstakingly preserved
in SystemVerilog. To be fair, it's rare (and pathologically
silly) for any user code to be affected by these issues.
And VHDL has its (rather smaller) share of such ill-defined
things, too.

Thanks for keeping me honest :)
--
Jonathan Bromley, Consultant

DOULOS - Developing Design Know-how
VHDL * Verilog * SystemC * e * Perl * Tcl/Tk * Project Services

Doulos Ltd., 22 Market Place, Ringwood, BH24 1AW, UK
jonathan.bromley@MYCOMPANY.com
http://www.MYCOMPANY.com

The contents of this message may contain personal views which
are not the views of Doulos Ltd., unless specifically stated.
 

Welcome to EDABoard.com

Sponsor

Back
Top