Auto allocation of Indexes

H

Hubble

Inside testbenches, it is often the case that multiple instances of
some stimuli (think ethernet interfaces, PCI busses) have a generic
number (G_ETHERNET_INTERFACE_NO) which is then set to 0, 1, 2, ...
manually.

I want to auto-initialize such interfaces, which means that each such
interface has a shared variable or signal (interface_no) and an init
process, say like this:

architecture tst of einterface is
shared variable interface_no: natural;
begin
...
Get_Interface_No: process
begin
alloc_number(interface_no);
wait;
end;
end;

A package should provide the alloc_number procedure:
package allocator is
procedure alloc_number(variable n: out natural);
end allocator;
package body allocator is
shared variable numbers: natural:=0;
procedure alloc_number(variable n: out natural) is
begin
n:=numbers;
numbers:=number+1;
end;
end allocator;

If you use 10 interfaces, the interfaces should get different interface
numbers from 0 to 9. The order is insignificant, but is is required
that all interfaces get their own number.

While this works on my Modelsim 6 on my single processor machine, it is
not portable in VHDL'93. The LRM states, that if shared variables are
accessed in the same simulation cycle (like here), we can assume
*nothing* about the order of execution (not a problem here) and also if
(the problem here) the accesses are sequential:

"A description is erroneous if it depends on whether or how an
implementation sequentializes access to shared variables."

Is there a way in VHDL'93 do solve this problem (in a portable, non
erroneous way)?

Hubble
 
N

Nicolas Matringe

Use a for...generate to instantiate your components and use the loop
index as your interface number.

Nicolas
 
H

Hubble

This is not possible, sorry. To be more specific:

I designed a "testbench bus", where I can attach various standard
entities, which I want to address by name. Currently, I have to assign
a number and a name to each of these entities. I want to allocate these
numbers automatically and have already working code for modelsim,
however erroneous according to the LRM.

Hubble
 
S

Srinivasan Venkataramanan

Hi Hubble,
I am not sure where you read that line in your OP:

----
"A description is erroneous if it depends on whether or how an
implementation sequentializes access to shared variables."
------

I've 2002 LRM and a quick search didn't reveal it. However I found an
interesting stuff - there is this "protected type" definition that might
satisfy your requirement (In my gut feeling your implementation is fine
too).

As per LRM 2002 version, section 3.5
--------
A protected type definition defines a protected type. A protected type
implements instantiatiable regions of
sequential statements, each of which are guaranteed exclusive access to
shared data

-------

Good Luck
Sri
--
Srinivasan Venkataramanan
Co-Author: SystemVerilog Assertions Handbook, http://www.abv-sva.org
Co-Author: Using PSL/SUGAR for Formal and Dynamic Verification 2nd Edition.
http://www.noveldv.com
I own my words and not my employer, unless specifically mentioned
 
H

Hubble

Just to be more specific, here is a prototype implementation of
"dynamic string" in VHDL'93
A process/procedure can call tbs_dynstring_new and gets an integer id.
Using this id, it can get/set the strings. One can use things like

report "message was " & tbs_dynstring_get(dstring);

and pass dstrings as signals a.s.o.

When a simulator executes each process one after the other, but in
arbitrary order, the package will run. But if it decides (on a
multiprocessor machine) to execute id:=idcnt; idcnt:=idcnt+1 in
parallel, it will fail.

(Note that the package is not well tested yet)

Hubble.


-- VHDL'93 code
-- tbs_dynstring_new and free suffer from concurrent shared variable
access problems

package tbs_dynstring is
subtype tbs_dynstring is natural;
constant C_NODYNSTRING: tbs_dynstring:=0;

-- basic functions
impure function tbs_dynstring_new(s: string:=""; minlen: natural:=0)
return tbs_dynstring;
procedure tbs_dynstring_free(id: tbs_dynstring);

impure function tbs_dynstring_get(id: tbs_dynstring) return string;
impure function tbs_dynstring_length(id: tbs_dynstring) return
natural;
procedure tbs_dynstring_set(id: tbs_dynstring; s: string:=""; minlen:
natural:=0);

end tbs_dynstring;

package body tbs_dynstring is
constant C_INITIALIDS: natural:=100;
constant C_MINLENGTH: natural:=8;

type tbs_dynstring_ref is access string;
type id_fields is record
lenfree: natural; -- current length or next free element
str: tbs_dynstring_ref; -- allocated memory
end record;
type id_array is array (natural range <>) of id_fields;
type ids_ref is access id_array;

--
-- ids holds string pointers and length and grows exponentially if
necessary
-- if a string is freed, it is not deallocated, but its index is
copied to
-- freelist and the old freelist is copied to its length field.
Therefore,
-- the lenfree sequence is a list of freed elements.
--
shared variable ids : ids_ref;
shared variable idcnt: natural:=1; -- occupied and free elements in
ids
shared variable freelist: natural:=C_NODYNSTRING;

-- check if ids has idcnt elements at min and grow ids if necessary
procedure sufficientids is
variable newsize: natural;
variable old: ids_ref;
begin
if ids=null then
ids:=new id_array(0 to C_INITIALIDS-1);
end if;
if idcnt<ids'length then
return;
end if;
newsize:=2*ids'length;
old:=ids;
ids:=new id_array(0 to newsize-1);
ids(0 to idcnt-1):=old(0 to idcnt-1);
deallocate(old);
end procedure sufficientids;

impure function tbs_dynstring_new(s: string:=""; minlen: natural:=0)
return tbs_dynstring
is
variable id: tbs_dynstring;
begin
if freelist/=C_NODYNSTRING then
-- take last element from freelist
id:=freelist;
freelist:=ids(id).lenfree; -- restore old freelist
else
-- take next element, grow ids if necessary
id:=idcnt;
idcnt:=idcnt+1;
sufficientids;
end if;
tbs_dynstring_set(id,s,minlen);
return id;
end tbs_dynstring_new;

procedure tbs_dynstring_free(id: tbs_dynstring) is
begin
ids(id).lenfree:=freelist; -- save old freelist
freelist:=id;
end procedure tbs_dynstring_free;

impure function tbs_dynstring_get(id: tbs_dynstring) return string is
begin
return ids(id).str(1 to ids(id).lenfree);
end function tbs_dynstring_get;

impure function tbs_dynstring_length(id: tbs_dynstring) return
natural is
begin
return ids(id).lenfree;
end function tbs_dynstring_length;

procedure tbs_dynstring_set(id: tbs_dynstring; s: string:=""; minlen:
natural:=0) is
variable bytes: natural;
begin
bytes:=s'length;
if bytes<=C_MINLENGTH then
bytes:=C_MINLENGTH;
end if;
if minlen /= 0 and bytes<minlen then
bytes:=minlen;
end if;

if ids(id).str=null then
-- if not previously allocated, simply allocate
ids(id).str:=new string(1 to bytes);
elsif ids(id).str'length<bytes then
-- reallocated
deallocate(ids(id).str);
ids(id).str:=new string(1 to bytes);
end if;
-- set str and length
ids(id).str(1 to s'length):=s;
ids(id).lenfree:=s'length;
end procedure tbs_dynstring_set;

end tbs_dynstring;
 
C

Charles, SAG

You can do what you want quite legally in VHDL but it takes a few steps.
Basically it involves defining a new resolved type based on integer and
introducing a package containing a global signal of the new resolved
integer type which is referenced by all DUT instances. This is from
memory, as I don't have the code here on my laptop but it is something
along these lines:

1) Define the resolved type based on integer (or unsigned or anything else)

2) Define a signal og the type defined in 1) in a global package.
Initialise this signal to 0;

3) In each of your instances add an initialisation process. The
initialisation process does the following:
a) read the external signal
b) attempt to increment the external signal
c) wait 0 ns (VERY IMPORTANT)
d) read back the external signal
e) if the read-back value is the same as the locally computed
incremented value then THIS instance has succeeded so this part of
the initialisation loop. If not, repeat the above from a)
downward.
f) When THIS instance has won the resolved increment step, use the
previous external signal value as your local index.

I hope I didn't miss something here, but I know it basically works. I
used the same principle in a previous test bench to allow multiple
processes to write strings to a shared external string signal without
consuming time. It may be the weekend before I find my code. If you are
still having problems drop a line to the NG on friday.

Regards,
Charles
 
H

Hubble

Thanky you, Charles

I already thought of this a bit, but it seemed to me somewhat
complicated. So in the package, dyn_string_new and dynstring_free have
to arbirtrate through the external signal and -unfortunatly- wait for 0
ns (in some cases several times), so they cannot be used in processes
with a sensitivity list. Not a real problem for me.

Since my "testbench bus" already uses self defined resolved signals for
the responses, it should not be a problem for me to implement it like
this.

Regards
Hubble.
 
C

Charles, SAG

Hi Hubble,

I have since taken a look at my old code. I was mistaken in thinking I
had the same situation as you. My resolution function received a
structure containing a text-message, the requesting unit-id and the
resolution-winner id (which was passed in on a generic). The units
repeatedly posted the message until they were accepted as winner.

Anyway, to your situation, there are however two ways I can think of
that might work.
1) Create a file of unique integers one per line. Declare the file in
your package. The init routine in your unit reads a line and extracts
the integer which then becomes the unit-id. This should work because, to
my knowledge, the LRM guarantees serialisation as long as the file is
open globally and your individual processes always read a line at a time.

2) If you don't like file-io, hash your unit names (or at least as much
of the name required for the smallest unique string) to a unique
simulation time (e.g. in fs or ps). When each unit reaches it's unique
simulation time it takes the current value of a global integer and then
increments it.

Regards,
Charles
 

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

No members online now.

Forum statistics

Threads
473,766
Messages
2,569,569
Members
45,042
Latest member
icassiem

Latest Threads

Top