The ONL NPR Tutorial

NPR Tutorial >> Writing A Plugin TOC

New Window?

Control Messages

Contents

The page Tour Of A Simple Plugin described the basics involved in handling simple control messages (e.g., "g" (get counts)) in the mycount plugin. Here, simple means:

Many of the early plugins (e.g., nstats, stringSub) used this approach to control messages.

In plugins with more complex commands (e.g., the delay plugin), the handle_msg() routine handles longer command names, and in some cases, control messages contain one or more command arguments (i.e., "delay= 80" sets the delay to 80 msec). Starting with the delay plugin, we began to adopt the following command naming convention in the more complex plugins:

Also, additional control message handling routines have been written with the intent of using SRAM instead of local memory so as to reduce the need for the programmer to deal with local memory exhaustion. These conventions were adopted in the priq plugin. This page describes useful features from the handle_msg() routines in the delayand priq plugins.
 

Control Message Architecture

[[ control-msgs-resize.png Figure ]]

The Xscale Control Processor (CP) communicates with plugins through control message SRAM rings. There are eight such rings paired for each microengine (ME) such that each ME has a control message input ring and a control message output ring. In fact, debug messages from a plugin also use the control message output ring. The Xscale CP distinguishes between these two types of control messages through the type field in the 4-byte (1-word) message header.

 

Longer Command Names

We interpret longer command names by using string functions (similar to what you would do in other systems). The only difference is that you must use memory-aware functions. The example below also uses primarily SRAM instead of local memory to store the command name constants (e.g., =counts). For example, strncmp_sram() is the equivalent to the standard strncmp() C-library function except that its arguments be in SRAM.

The use of SRAM substantially reduces the need for the programmer to deal with local memory exhaustion. The slower execution speed from using SRAM is typically not an issue since control messages are usually used between experiments. The handle_message() routine might look like this:

    void handle_msg() {
1       __declspec(gp_reg) unsigned int message[8];
2       __declspec(gp_reg) onl_api_ctrl_msg_hdr hdr;
3       __declspec(local_mem) char inmsgstr[28];		// inbound
4       __declspec(sram) char sram_inmsgstr[28];
5       __declspec(local_mem) char outmsgstr[28];		// outbound
6   
7       char SET_delay[8]	= "delay=";			// request msgs
8       char GET_counts[8]	= "=counts";
9   
10      message[0] = 0;	message[1] = 0;	message[2] = 0;	message[3] = 0;
11      message[4] = 0;	message[5] = 0;	message[6] = 0;	message[7] = 0;
12  
13      dl_source_message(msgFromBlock, message);
14  
15      hdr.value = message[0];
16      if( hdr.type != CM_CONTROLMSG )	return;
17      if( hdr.response_requested != 1 )	return;
18  
19      onl_api_intarr2str( &message[1], inmsgstr );
20  
21      memcpy_sram_lmem( sram_inmsgstr, inmsgstr, 28 );
22  
23      if( strncmp_sram(sram_inmsgstr, SET_delay, 6) == 0 ) {
24	    ... Handle "delay=" command to set the delay ...
25      } else if( strncmp_sram(sram_inmsgstr, GET_counts, 8) == 0 ) {
26	    helper_sram_outmsg_3ul( npkts, maxinq, ndrops, outmsgstr );
27      } else {
28          ... Handle Error ...
29      }
30  
31      if( onl_api_str2intarr(outmsgstr, &message[1]) < 0 )	return;
32  
33      hdr.type = CM_CONTROLMSGRSP;
34      hdr.response_requested = 0;
35      hdr.num_words = 7;
36      message[0] = hdr.value;
37  
38      dl_sink_message(msgNextBlock, message);
    }

Most of the differences from the handle_msg() routine in the mycount plugin are shown in lines 21-26 and lines 7-8. Line 21 copies the inbound message from local memory (inmsgstr[28]) to SRAM (sram_inmsgstr[28]) using the memory function memcpy_sram_lmem() so that SRAM functions can be used to compare strings later. Note that memcpy_sram_lmem() in line 21 is the memory-aware version of the standard memcpy C-library function.

Lines 23 and 25 use the strn_cmp_sram() function to do a string compare. Note that because the declarations in lines 7-8 have no __declspec() modifier, they will be stored in SRAM.

 

Forming a Message in SRAM

In line 26 above, the helper function helper_sram_outmsg_3ul forms a message in SRAM given three unsigned long values for npkts (number of packets), maxinq (maximum number of packets queued), and ndrops (number of packets dropped). A side-effect of calling helper_sram_outmsg_3ul in line 26 is to make SRAM copies of npkts, maxinq, and ndrops because the function arguments are declared to be in SRAM. (Note: We introduced the helper_sram_outmsg_3ul() function earlier when discussing debug messages since it can also be used to form debug messages.)

It is worthwhile looking inside the helper_sram_outmsg_3ul function.

    void
    helper_sram_outmsg_3ul(	unsigned long x0,
    				unsigned long x1,
    				unsigned long x2,
    				__declspec(local_mem) char *outmsgstr ) {
1       __declspec(sram) char	SPACE[2] = " ";
2       __declspec(sram) char	sram_msg_buf[28];	// work area
3       __declspec(sram) char	sram_tmpstr[16];
4   
5       helper_ultoa_sram( x0, sram_msg_buf, 28 );
6       strcat_sram( sram_msg_buf, SPACE );
7       helper_ultoa_sram( x1, sram_tmpstr, 16 );
8       strcat_sram( sram_msg_buf, sram_tmpstr );
9       strcat_sram( sram_msg_buf, SPACE );
10      helper_ultoa_sram( x2, sram_tmpstr, 16 );
11      strcat_sram( sram_msg_buf, sram_tmpstr );
12      memcpy_lmem_sram( outmsgstr, sram_msg_buf, 28 );
    }

This function is not surprising. A string is built in the SRAM message area sram_msg_buf[28] by concatentating together words separated by a single space. First, helper_ultoa_sram() is used to convert an unsigned long value to a decimal number (ASCII). Again, since the arguments x0, x1 and x2 have no __declspec() modifier, they will be stored in SRAM. Then, a space character is appended to the string. Then, the next unsigned long value is converted and concatenated to the first string. These steps are repeated for all of the unsigned long values. The last step (line 12) copies the message in SRAM to the one in local memory. The last argument in all of these functions indicate the size of the array being used.

 

Reading a Command Argument

The delay= command in the delay plugin is used to change the delay; e.g., "delay= 100" sets the delay to 100 milliseconds. The argument is the second word following one or more space characters which separate the argument from the command word.

 	__declspec(sram) char *valptr;
 	...
27a	valptr = helper_nxt_token( sram_inmsgstr, 28 ); 
27b	if( valptr == 0 ) {
27c	    ... Handle Error ...
27d	} else {
27e	    delay = helper_atou_sram( valptr );
27f	    memcpy_lmem_sram( outmsgstr, valptr, 21 );
	}

Line 27 in the original handle_msg() function handled the delay= command. The code fragment above shows the missing code. The function helper_nxt_token() in line 27a is used to locate the first non-space character following the current pointer position indicated by sram_inmsgstr. The last argument (28), limits the search to 28 bytes. Line 27e converts the decimal ASCII delay value and returns an unsigned long in SRAM.

If there were more command arguments, helper_nxt_token() could be used to continue the search for these words.


 Revised:  Tue, Feb 3, 2009 

  
  

NPR Tutorial >> Writing A Plugin TOC