Working with the conventional Specman C language interface has two major disadvantages:
1. There is a tight dependency between the e code and the C code. The user must include the Specman header file which was generated based on the e code. Every minor change in the e code requires regeneration of the header file.
2. The C interface doesn't support calling e TCMs (Time Consuming Methods) from C code.
Let's take a look at the following C interface example:
<' //top.e
struct packet { ... };
unit u {
send(p:packet) is dynamic C routine libtest.so:;
update()@sys.any is {
wait [10];
message(LOW,"[e] update after waiting 10 cycles");
//do something
};
main_thread()@sys.any is {
gen p; //a packet
send(p);
stop_run();
};
};
C export u.update();
'>
In the above example we show the send() method which is implemented in C and therefore defined as dynamic C routine. update() is exported so you can call it from C code.
The C code then implements send() and calls update() with SN_DISPATCH:
/* test.c */
#include "sn_top.h"
#include <stdio.h>
void send(SN_TYPE(u) me, SN_TYPE(packet) p) {
/* Do something */
SN_DISPATCH(update,me,u,(me));
}
This simple example illustrates the two disadvantages we mentioned earlier:
1. Tight dependency - sn_top.h which is included in the C code must be regenerated each time the e code is changed no matter what the change was. Of course, regenerating the header enforces recompilation of the C code...
2. Calling TCMs - update() is a TCM, "The run-time behavior of a TCM called from C is undefined" (quoted from Specman docs). Trying to run the example above will give an OS11 error. Undefined indeed...
The new Function Level C interface addresses those issues. Let's take a look in the following modified example:
unit u {
//send(p:packet) is dynamic C routine libtest.so:; old declaration
send(p:packet)@sys.any is import C libtest.so:;
};
//C export u.update(); old declaration
export C u.update();
As you can see, the e code remains the same but we changed the way we export and import the functions. Please note that send() was changed to be a TCM; this way we can call update() TCM from the C code.
And the new C code looks like:
#include "sn_fli_top.h"
#include "e_func_intf_user.h"
#include <stdio.h>
void send(eStructHandle me, eStructHandle p) {
/* Do something */
update(me);
}
You might want to pay attention to the following:
- We use a header file sn_fli_top.h which is a generated header file, but in this case we need to generate a new file only if we change one of the export or import prototypes.
- Structs and lists parameters are passed as handles (eStructHandle or eListHandle).
- e_func_intf_user.h contains some auxiliary routines to take care of the handles. For example, to get or to set an item of a list. This file resides in Specman installation directory.
- We call update() directly, SN_DISPATCH and other C interface macros are not needed anymore. Note that the first argument is the instance which routes the call.
How to generate the new header file? Which auxiliary routines does e_func_intf_user.h define? What about save-restore flow? You can get the answers to these questions and more details in Specman documentation.
Enjoy,
Nir Hadaya
Specman R&D