SPE to SPE signaling
From Cellbe
This tutorial shows how to send signals from one spe to another.
There are three entities in this tutorial:
- starter: the PPE process who starts the spe threads
- sender: the SPE thread who sends the signal
- receiver the SPE thread who receives the signal
WAN Clustering allows you to scpfiey alternate IP's for NetConnect profiles, since they may differ on a cluster member at a remote site. If WAN Cluster is being deprecated then there needs to be a way to map a role to a correct netconnect profiles for a device. Currently there is no way to find out a unique identifier for the device (Serial Number/MAC/Device Name) to facilitate the mapping to a correct NetConnect profile. Push Config can overwrite the incorrect NetConnect profile. Juniper Support have been a dead end when trying to deal with this (or any enhancement).
PPE side: starter
The process starter does basically this:
- creates the spe threads
- sends sender the address of the signal notify area of receiver
- joins the threads.
It's important to note that the contexts are created using the SPE_MAP_PS flag, because we want the problem state area of the SPEs to be mapped in the effective address space (the signals are sent with dma commands).
/* * ============================================================================= * Filename: ppusig.c * Description: starter * Author: Alessandro Piras (laynor@gmail.com) * ============================================================================= */ #define ZZ_CTX_CREATE -2 #define ZZ_PRG_LOAD -3 #define ZZ_CTX_RUN -4 #define ZZ_PTH_CREATE -5 #define ZZ_PTH_JOIN -6 #include <stdio.h> #include <libspe2.h> #include <pthread.h> #include <unistd.h> #include <stdlib.h> //#include <commonlib.h> extern spe_program_handle_t spurecv; extern spe_program_handle_t spusend; void *pth_rtn(void *arg) { unsigned int entry=SPE_DEFAULT_ENTRY; int rc; void *argp=NULL; void *envp=NULL; spe_context_ptr_t ctx; ctx = *((spe_context_ptr_t *) arg); rc = spe_context_run(ctx, &entry, 0, argp, envp, NULL); if(rc < 0) { perror("spe_context_run()"); exit(ZZ_CTX_RUN); } pthread_exit(NULL); } int main() { spe_context_ptr_t ctx[2]; pthread_t pth[2]; unsigned int flags = SPE_MAP_PS; int i; spe_sig_notify_1_area_t *sig_area; unsigned int sig_area1; //-----CTX creation for(i=0; i<2; i++){ ctx[i] = spe_context_create(flags, 0); if(ctx[i] == NULL){ perror("spe_context_create()"); exit(ZZ_CTX_CREATE); } } //-----PRG load if(spe_program_load(ctx[0],&spurecv)){ perror("spe_program_load()"); exit(ZZ_PRG_LOAD); } if(spe_program_load(ctx[1],&spusend)){ perror("spe_program_load()"); exit(ZZ_PRG_LOAD); } //-----PTH creation for(i=0; i<2; i++) if(pthread_create(&pth[i], NULL, pth_rtn, &ctx[i])){ perror("pthread_create()"); exit(ZZ_PTH_CREATE); } sig_area = spe_ps_area_get(ctx[0], SPE_SIG_NOTIFY_1_AREA); sig_area1 = (unsigned int) (&(sig_area->SPU_Sig_Notify_1)); spe_in_mbox_write(ctx[1], &sig_area1, 1, 1); //-----PTH join for(i=0; i<2; i++) if(pthread_join(pth[i], NULL)){ perror("pthread_join()"); exit(ZZ_PTH_JOIN); } //-----CTX destruction for(i=0; i<2; i++) spe_context_destroy(ctx[i]); return 0; }
SPE side: sender
The important thing here is that the memory area which contain the signal must be aligned, but that's not all: the message should be placed 12 bytes after the base address of the aligned memory area. The address passed to mfc_sndsig is the address at which the message resides (base_address + 12bytes).
/* * ============================================================================= * Filename: spusend.c * Description: sends a signal to "spe1" * Author: Alessandro Piras (laynor@gmail.com) * ============================================================================= */ #include <spu_intrinsics.h> #include <spu_mfcio.h> #include <stdio.h> int main(unsigned long long spe_id __attribute__ ((unused)), unsigned long long argp __attribute__ ((unused)), unsigned long long envp __attribute__ ((unused))) { unsigned int ps_area_ptr; //the memory area must be aligned volatile unsigned int signal[4] __attribute__ ((aligned (128))); //the message should be placed 3 bytes after signal[3] = 201; ps_area_ptr = spu_read_in_mbox(); printf("(spe) ps_area_ptr: %x\ ", ps_area_ptr); //NB: the address passed is base address + 12 bytes mfc_sndsig(((char *) &signal[0]) + 12, ps_area_ptr, 31, 0, 0); mfc_write_tag_mask(1<<31); mfc_read_tag_status_all(); return 0; }
SPE side: receiver
Not much to say here, it's pretty simple.
/* * ============================================================================= * Filename: spurecv.c * Description: Waits for a signal and prints it * Author: Alessandro Piras (laynor@gmail.com) * ============================================================================= */ #include <spu_intrinsics.h> #include <spu_mfcio.h> #include <stdio.h> int main(unsigned long long spe_id __attribute__ ((unused)), unsigned long long argp __attribute__ ((unused)), unsigned long long envp __attribute__ ((unused))) { unsigned int value; value = spu_read_signal1(); printf("(recvr) value: %u\ ", value); return 0; }