Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > VHDL > [long] look up table for procedure call

Reply
Thread Tools

[long] look up table for procedure call

 
 
alb
Guest
Posts: n/a
 
      06-28-2013
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

--
A: Because it fouls the order in which people normally read text.
Q: Why is top-posting such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
 
Reply With Quote
 
 
 
 
Jim Lewis
Guest
Posts: n/a
 
      06-28-2013
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_testb...rification.htm

 
Reply With Quote
 
 
 
 
alb
Guest
Posts: n/a
 
      07-03-2013
On 28/06/2013 17:02, Jim Lewis wrote:
> 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?

 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Having compilation error: no match for call to (const __gnu_cxx::hash<long long int>) (const long long int&) veryhotsausage C++ 1 07-04-2008 05:41 PM
long long and long Mathieu Dutour C Programming 4 07-24-2007 11:15 AM
unsigned long long int to long double Daniel Rudy C Programming 5 09-20-2005 02:37 AM
<tr> with a 1x1 image as a filler on a table with padding of 2 look thicker in netscape but they look ok in IE. Serial # 19781010 HTML 1 08-10-2003 09:05 PM
Assigning unsigned long to unsigned long long George Marsaglia C Programming 1 07-08-2003 05:16 PM



Advertisments