[long] look up table for procedure call


A

alb

Hi all,

this thread is intended for testbench only, no RTL needed.

I've just finished to read JB's 'Writing Testbenches' 2nd edition and I
must say that I found it quite enlightening.

The model he proposes to write testbenches is essentially to wrap away
the physical interface of the DUT and provide a level of abstraction to
the testcases so that interface implementation changes do not affect the
rewrite of the testcases.

The book covers well the fact that a package is not a 'structural
element' in VHDL, therefore you cannot abstract away the interface by
means of a package (you always need to pass all the signals in your
procedure call) and a bus functional model entity is proposed in place
(for details see pag. 325 - VHDL TEST HARNESS).

His proposal, as I think I have understood, is to use an entity with a
'server' process which is implementing the bus functional model while
serving 'abstract' requests from the client process which does not know
the details of the physical interface. Abstract signals to handle
handshake between client and server are also needed, but the great
advantage of the proposed method is that test cases are now at a much
higher level and have no details referred to the DUT (a test case for a
read/modify/write operation can be completely independent on the DUT and
the test case may be reused for very many projects where read/write
operations are meaningful).

I started out writing like hell the various elements of my bus
functional model, the test harness and some of the test cases, but now
my problem is the following: my 'server' has to perform abstract actions
(read_data, write_data, setup_interface, generate_input_pattern, ...)
which are essentially mapped to an 'address'. To be more specific, my
test case would call a procedure like this:

procedure exec( type: in bool; -- read/write
addr: in natural; -- what to do
data: inout natural; -- data provided or returned
signal: to_server: out to_srv_control; --handshake
signal: from_server: in frm_srv_control;); --handshake

while the test case will look like:

-- RD/WR are type defined as true/false
exec(RD, reg0, data, to_srv, frm_srv);
exec(WR, reg1, data, to_srv, frm_srv);
exec(WR, reg2, data, to_srv, frm_srv);
....

by changing the address (reg[0,1,2]) we may want to either send a pulse
to the interface or have a write cycle, the server needs to have some
sort of switch statement based on the 'address' content and call the
appropriate procedure where the appropriate operation is performed.

Given the fact that I'm extremely lazy, in C I would solve this problem
with a simple table (possibly stored in a struct) with two elements, an
address and a function pointer initialized to a specific function. A
'for loop' will then check the function to call every time there's a new
request from the client (only problem is that with this approach the
function parameters should be standardized for all functions, but this
is a different problem).

Now, after all this chatter, comes the real question: is there a way to
write a 'table' (maybe an array of registers), in which one element is
the address and the other is a procedure call, in such a way that every
time I need to add a new address and a new procedure call I simply
extend the table without touching the 'for loop' part of the code?

I'm not sure the problem is clear and if the additional context is
helping understanding my question, but that was my intent.

Thank you all for any hint.

Al
 
Ad

Advertisements

J

Jim Lewis

I use a case statement. If you use procedures like you suggest and keep
the choice and procedure call on one line, it looks similar to a table.

process
begin
wait <sync_event> ;
case to_srv.addr is
when ADDR1 => DO_ADDR1_ACTION(...) ;
when ADDR2 => DO_ADDR2_ACTION(...);


For most BFMs (TLM/server) that I do I don't bother with calling procedures, I just write the code following the case target. I suppose it depends onthe complexity of the code.

A couple of things I do different:
Rather than using a generalized "exec", I put the operation name into the procedure. So I do CpuWrite rather than CpuExec(WR, ... With writing, this means that data can be a constant rather than a variable and you can passa value directly to the call. CpuWrite(reg0, X"A5", ...) ;

I use a single record and resolution functions. The resolution functions make it more complicated, but I prefer only one record in the call.

Jim

SynthWorks' VHDL Testbench and Verification class covers this, OSVVM, and more. See http://www.synthworks.com/vhdl_testbench_verification.htm
 
Ad

Advertisements

A

alb

I use a case statement. If you use procedures like you suggest and keep
the choice and procedure call on one line, it looks similar to a table.

process
begin
wait <sync_event> ;
case to_srv.addr is
when ADDR1 => DO_ADDR1_ACTION(...) ;
when ADDR2 => DO_ADDR2_ACTION(...);


Indeed it is actually quite easy to maintain as long as you use
procedures to simplify the 'when' clause. Adding testcases to the server
should be straight forward.
For most BFMs (TLM/server) that I do I don't bother with calling
procedures, I just write the code following the case target. I
suppose it depends on the complexity of the code.

I'm somehow quite reluctant with writing code for the case target since
it exposes too many details at the server level. Encapsulating the bus
functional details in packaged procedures allows to hide and separate
the physical interface a great deal.
A couple of things I do different: Rather than using a generalized
"exec", I put the operation name into the procedure. So I do
CpuWrite rather than CpuExec(WR, ... With writing, this means that
data can be a constant rather than a variable and you can pass a
value directly to the call. CpuWrite(reg0, X"A5", ...) ;

I agree with that, my example was a bit oversimplified. The 'CpuWrite'
name you suggest simplifies also the interface since you would not need
an extra bit of information to pass along in order to distinguish
between Write/Read.
I use a single record and resolution functions. The resolution
functions make it more complicated, but I prefer only one record in
the call.

Could you elaborate on that?
The 'client' call still passes only one register with the appropriate
elements (data, address, to_srv, frm_srv) so there's only one register
passed between the test cases and the test harness. Am I missing
something? Why would you need resolution functions?
 

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

Top