Reading an Array of vectors.

N

Niv (KP)

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.
 
K

Ken Cecka

Niv said:
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
 
K

Ken Cecka

Ken said:
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;
 
N

Niv (KP)

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
(e-mail address removed)://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.
 
A

Andy

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
 
A

Andy

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
 
A

Andy

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


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


For sure.


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
 
A

Andy

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
 
N

Niv (KP)

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.
 
A

Andy

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
 
K

kennheinrich

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.


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
 

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,764
Messages
2,569,566
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top