Legacy recTr Trace Format

The Impulse trace format (*.recTr) is an open format targeting typical embedded system use-cases. The trace data is packed into a binary format. To generate a trace, you can use open-source multi-language emitters (currently C/C++, systemC and Java).

How to generate an impulse trace

1 Define the signals

First step is to define the required signals. For each signal you define the path, process type, signal type, type descriptor and domain.As a result you get a descriptor, used for further operations on the signals.

Process type
Usually the discrete process type is chosen. Just (at the moment) float signals support the continuous type.
Signal type
Select the signal type of your trace (Float, String, Integer, Event, ..).
Signal descriptor
The signal descriptor describes the signal type in more details (e.g the bit width of a logic vector).
Domain Base
e.g. ns, ms, us, ... . Each signal may have its own base.

With the java version of the emitter, this step is optional.

2 Open

This step is optional and just required in case of continuous signals. With the open function, you define the starting point (and rate) of a signal.

3 Write

There are dedicated write functions per signal type. Typical arguments are:

units
No of domain units (e.g. ns)
conflict
Define this sample as a conflict one.
value
The given value in different formats (float/int/..)

4 Close

This step again is optional. It gives the signal the ending point.

Connect to your transmission

Main task of the trace emitters is to form a binary sequence. Its on the user where this binary data goes to.

In the C language version you need to implement the writeOutput function. Java has a default implementation of a tcp server. To use other connections, you can override the Trace class.

Connect impulse

From impulse side the user has two choices:

1 Using files

Store the binary data into files with the ending trace (e.g.test.trace).

2 Using ports

Choose and create a ports that allows you to connect to your transmission (TCP, Pipe, Serial line,.. ) and select the Trace reader.

Using Java

A simple example looks like this:

// open tcp port
        Impulse.open(55668);        
        // wait for somebody to connect - not required
        while(!Impulse.isConnected())
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
            }
        
        // trace
        for (int n=0;n<1000;n++){
            long current = n * 10;
            Impulse.writeInt("Integer/Integer1",current,n==100,n-1000);
            Impulse.writeInt("Integer/Integer2",current,n==100,-n);
            Impulse.writeInt("Integer/Integer3",current,n==100,n*3-100);
            Impulse.writeInt("Integer/Integer4",current,n==100,(char)n);
            Impulse.writeFloat("Float/Float1",current,n==100,(float)Math.sin(n/23.0));
            Impulse.writeFloat("Float/Float2",current,n==100,(double)Math.cos(n/55.0)*10);
                if ((n%100)==0)
                    Impulse.writeText("Text/Text1",current,n==100,"text");
                Impulse.writeEvent("Event/Event1",current,n==100,n%8);
        }
        Impulse.closeAllSignals(10000);

        // close server
        Impulse.close();

By using the signal path as references, you don't need to define the signals. Default domain base is DomainBase.ms.

A more complete example looks like this:

// open tcp port
        Impulse.open(55668);
        // wait for somebody to connect - not required
        while (!Impulse.isConnected())
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
            }

        // define signals
        Object s1 = Impulse.addSignal("Int1", Process.Discrete, Type.Integer, null, DomainBase.ns);
        Object s2 = Impulse.addSignal("Int2", Process.Discrete, Type.Integer, null, DomainBase.ns);
        Object s3 = Impulse.addSignal("Int3", Process.Discrete, Type.Integer, null, DomainBase.ns);
        	......

        // trace
        for (int n = 0; n < 1000; n++) {
        	Impulse.writeInt(s1, current, n < 100, n - 1000);
        	......

In the previous examples, we were waiting for impulse to be connected. This is not required. A scenario where we continuously use the trace functions (and let impulse to be connected at any time, even multiple times) looks like this:

// open tcp port
        Impulse.open(55668);

        // define signals
        Object s1 = Impulse.addSignal("Int1", Process.Discrete, Type.Integer, null, DomainBase.ns);
        Object s2 = Impulse.addSignal("Int2", Process.Discrete, Type.Integer, null, DomainBase.ns);
        Object s3 = Impulse.addSignal("Int3", Process.Discrete, Type.Integer, null, DomainBase.ns);
        	......

        // trace
        long start = System.currentTimeMillis();
        while (true) {
            long current = System.currentTimeMillis()-start;
            int n = (int) (current%10000);
            Impulse.writeInt(s1, current, n < 100, n - 1000);
            Impulse.writeInt(s2, current, n < 100, -n);
            Impulse.writeInt(s3, current, n < 100, n * 3 - 100);
            Impulse.writeInt(s4, current, n < 100, (char) n);
            Impulse.writeFloat(s5, current, n < 100, (float) Math.sin(n / 23.0));
            Impulse.writeFloat(s6, current, n < 100, (double) Math.cos(n / 55.0) * 10);
            if ((current % 20)==0)
                Impulse.writeText(s7, current, n < 100, "text");
            Impulse.writeEvent(s8, current, n < 100, n % 8);
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
            }
        }

Using C and C++

A C example is shown here:

FILE *ptr_myfile;
int main( int argc, const char* argv[] )
{	
	int n = 0;
	ptr_myfile=fopen("test.trace","wb");	
	
	int h1 = IpAddSignal("Integer/writeInt64",IpDiscrete,IpInteger,0,"ns");
	int h2 = IpAddSignal("Integer/writeInt32",IpDiscrete,IpInteger,0,"ns");
	int h3 = IpAddSignal("Integer/writeInt16",IpDiscrete,IpInteger,0,"ns");
	int h4 = IpAddSignal("Integer/writeInt8",IpDiscrete,IpInteger,0,"ns");
	int h5 = IpAddSignal("Integer/writeIntU64",IpDiscrete,IpInteger,0,"ns");
	int h6 = IpAddSignal("Integer/writeIntU32",IpDiscrete,IpInteger,0,"ns");
	int h7 = IpAddSignal("Integer/writeIntU16",IpDiscrete,IpInteger,0,"ns");
	int h8 = IpAddSignal("Integer/writeIntU8",IpDiscrete,IpInteger,0,"ns");
	int h9 = IpAddSignal("Float/writeFloat32",IpDiscrete,IpFloat,0,"ns");
	int h10 = IpAddSignal("Float/writeFloat64",IpDiscrete,IpFloat,0,"ns");
	int h11 = IpAddSignal("Text/writeText",IpDiscrete,IpText,0,"ns");
	int h12 = IpAddSignal("Event/writeEvent",IpDiscrete,IpEvent,0,"ns");
	
	IpSetOutput(IpDefaultOutput);
	
	for (n=0;n<1000;n++){
		long long current = n * 10;
		
	#ifndef __cplusplus
		IpWriteInt64(h1,current,n==100,n-1000);
		IpWriteInt32(h2,current,n==100,-n);
		IpWriteInt16(h3,current,n==100,n*3-100);
		IpWriteInt8(h4,current,n==100,(char)n);
		IpWriteIntU64(h5,current,n==100,n);
		IpWriteIntU32(h6,current,n==100,n%100);
		IpWriteIntU16(h7,current,n==100,n);
		IpWriteIntU8(h8,current,n==100,n*8);
		IpWriteFloat32(h9,current,n==100,(float)sin(n/23.0));
		IpWriteFloat64(h10,current,n==100,(double)cos(n/55.0)*10);
	#else
		IpWriteInt(h1,current,n==100,n-1000);
		IpWriteInt(h2,current,n==100,-n);
		IpWriteInt(h3,current,n==100,n*3-100);
		IpWriteInt(h4,current,n==100,(char)n);
		IpWriteInt(h5,current,n==100,n);
		IpWriteInt(h6,current,n==100,n%100);
		IpWriteInt(h7,current,n==100,n);
		IpWriteInt(h8,current,n==100,n*8);
		IpWriteFloat(h9,current,n==100,(float)sin(n/23.0));
		IpWriteFloat(h10,current,n==100,(double)cos(n/55.0)*10);
	#endif
		if (!(n%100))
			IpWriteText(h11,current,n==100,"text");
		IpWriteEvent(h12,current,n==100,n%8);
	}
	IpCloseAllSignals(10000);
	fclose(ptr_myfile);
}


void IpWriteOutput(int output, byte* buffer, int pos, int length) {
	fwrite(buffer+pos,length, 1, ptr_myfile);
}

To support different outputs and to disable the output, the setOutput function is used. Doing setOutput(0) will disable any output.

Using systemC

The systemC version has some add-ons compared to C++:

  • Hierarchical names are generated based on modules.
  • Implicit or explicit module parameter.
  • Current time is taken from systemC .
  • Signal handle table.
  • Trace macros

Still you can use the same style as in C/C++, but with the new extensions and macros tracing gets much easier.

	IP_STAT_WRITE_DISC_INT_SC(CurrentState,,false, (int)State);

Adding this macro into any module will create a trace signal modulepath.CurrentState and update the value every time the macro code is entered. Therefore a static handle is created.

If you need to trace from multiple locations, use:

	IP_TABL_WRITE_DISC_INT_SC(CurrentState,,false, (int)State);

This macro uses a global table for all signal handles.

Both macro types (one for each signal type) can be used with implicit module declaration (using this pointer) or explicit module parameter.

// static handle
// module == this
#define IP_STAT_WRITE_DISC_INT_SC(name,descriptor,conflict,value) ...
#define IP_STAT_WRITE_DISC_FLOAT_SC(name,descriptor,conflict,value) ...
#define IP_STAT_WRITE_DISC_EVENT_SC(name,descriptor,conflict,value) ...
#define IP_STAT_WRITE_DISC_BINARY_SC(name,descriptor,conflict,value, length) ...

// static handle
// explicit module
#define IP_STAT_WRITE_DISC_INT_SCM(module,name,descriptor,conflict,value) ...
#define IP_STAT_WRITE_DISC_FLOAT_SCM(module,name,descriptor,conflict,value) ...
#define IP_STAT_WRITE_DISC_EVENT_SCM(module,name,descriptor,conflict,value) ...
#define IP_STAT_WRITE_DISC_BINARY_SCM(module,name,descriptor,conflict,value, length) ...

// using global handles table
// module == this
#define IP_TABL_WRITE_DISC_INT_SC(name,descriptor,conflict,value) ...
#define IP_TABL_WRITE_DISC_FLOAT_SC(name,descriptor,conflict,value) ...
#define IP_TABL_WRITE_DISC_EVENT_SC(name,descriptor,conflict,value) ...
#define IP_TABL_WRITE_DISC_BINARY_SC(name,descriptor,conflict,value, length) ...

// using global handles table
// explicit module
#define IP_TABL_WRITE_DISC_INT_SCM(module,name,descriptor,conflict,value) ...
#define IP_TABL_WRITE_DISC_FLOAT_SCM(module,name,descriptor,conflict,value) ...
#define IP_TABL_WRITE_DISC_EVENT_SCM(module,name,descriptor,conflict,value) ...
#define IP_TABL_WRITE_DISC_BINARY_SCM(module,name,descriptor,conflict,value, length) ...

Getting the sources

All sources (implementation and examples) are available at our github repositories.

Versions

Version 1
Minimum requirement is impulse 0.9.3

Result

Print

User Rating: 0 / 5

Star InactiveStar InactiveStar InactiveStar InactiveStar Inactive