text: 
  Programming the synergistic processing elements of the Sony PLAYSTATION 3
  Document options requiring JavaScript are not displayed
  Help us improve this content
  Level: Intermediate
  Jonathan Bartlett (johnnyb@eskimo.com), Director of Technology, New Medio
  22 Feb 2007
  Continue looking in depth at the Cell Broadband Engine™ (Cell BE) processor's synergistic processor elements (SPEs) and how they work at the lowest level. This installment explores storage alignment issues and the communication facilities of the SPEs.
  Non:
  ###Load byte unaligned address $3 into preferred slot of register $4###
  #Loads from nearest quadword boundary
  lqd $4, 0($3)
  #Rotate value to the beginning of the register
  rotqby $4, $4, $3
  #Rotate value to the preferred slot (-3 for bytes, -2 for halfwords, and nothing for
  words or doublewords)
  rotqbyi $4, $4, -3.
  lqd
  rotqby
  rotqbyi
  Storing is more difficult. Here is the code to store a byte that is in the
  preferred slot of register $4 into the address specified by register $3:
  ###Store preferred byte slot $4 into unaligned address $3
  #Load the data into a temporary register
  lqd $5, 0($3)
  #Generate the controls for a byte insertion
  cbd $6, 0($3)
  #Shuffle the data in
  shufb $7, $4, $5, $6
  #Store it back
  stqd $7, 0($3).
  cbd
  shufb
  cbd $6,
  0($3)
  shufb $7, $4,
  $5, $6):
  convert_to_upper
  main
  convert_main.s
  .data
  string_start:
  .ascii "We will convert the following letter, "
  letter_to_convert:
  .ascii "q"
  remaining:
  .ascii ", to uppercase\n\0"
  .text
  .global main
  .type main, @function
  main:
  .equ MAIN_FRAME_SIZE, 32
  .equ LR_OFFSET, 16
  #PROLOGUE
  stqd $lr, LR_OFFSET($sp)
  stqd $sp, -MAIN_FRAME_SIZE($sp)
  ai $sp, $sp, -MAIN_FRAME_SIZE
  #MAIN FUNCTION
  ila $3, letter_to_convert
  brsl $lr, convert_to_upper
  ila $3, string_start
  brsl $lr, printf
  #EPILOGUE
  ai $sp, $sp, MAIN_FRAME_SIZE
  lqd $lr, LR_OFFSET($sp)
  bi $lr
  Now enter the function that actually does the uppercase conversion (enter as
  convert_to_upper.s):
  convert_to_upper.s
  .text
  .global convert_to_upper
  .type convert_to_upper, @function
  convert_to_upper:
  #Register usage
  # $3 - parameter 1 -- address of byte to be converted
  # $4 - byte value to be converted
  # $5 - $4 greater than 'a' - 1?
  # $6 - $4 greater than 'z'?
  # $7 - $4 less than or equal to 'z'?
  # $8 - $4 between 'a' and 'z' (inclusive)?
  # $9 through $12 - temporary storage for final store
  # $13 - conversion factor
  #address of letter stored in unaligned address in $3
  #UNALIGNED LOAD
  lqd $4, 0($3)
  rotqby $4, $4, $3
  rotqbyi $4, $4, -3
  #IS IN RANGE 'a'-'z'?
  cgtbi $5, $4, 'a' - 1
  cgtbi $6, $4, 'z'
  nand $7, $6, $6
  and $8, $5, $7
  #Mask out irrelevant bits
  andi $8, $8, 255
  #Skip uppercase conversion and store if $4 is not lowercase (based on $8)
  brz $8, end_convert
  is_lowercase:
  #Perform Conversion
  il $13, 'a' - 'A'
  absdb $4, $4, $13
  #Unaligned Store
  lqd $9, 0($3)
  cbd $10, 0($3)
  shufb $11, $4, $9, $10
  stqd $11, 0($3)
  end_convert:
  #no stack frame, no return value, just return
  bi $lr
  To compile and run, perform the following commands:
  spu-gcc convert_main.s convert_to_upper.s -o convert
  ./convert).
  a
  z
  'a' - 1
  'z.'
  nand
  and
  xor.
  absdb
  Communication with the PPE
  So far I have concentrated on SPE-only programs. Now I will look into
  PPE-controlled programs, and for that, I need to know how to get the PPE and the
  SPE to communicate.
  Channels and the MFCid_t
  spe_create_thread(spe_gid_t spe_gid, spe_program_handle_t *spe_program_handle,
  void *argp, void *envp, unsigned long mask, int flags)
  dlopen()
  dlsym()
  spe_open_image()):
  convert_dma_main.s
  .data
  .align 4
  conversion_info:
  conversion_length:
  .octa 0
  conversion_data:
  .octa 0
  .equ CONVERSION_STRUCT_SIZE, 32
  .section .bss #Uninitialized Data Section
  .align 4
  .lcomm conversion_buffer, 16384
  .text
  .global main
  .type main, @function
  #MFC Constants
  .equ MFC_GET_CMD, 0x40
  .equ MFC_PUT_CMD, 0x20
  #Stack Frame Constants
  .equ MAIN_FRAME_SIZE, 80
  .equ MAIN_REG_SAVE_OFFSET, 32
  .equ LR_OFFSET, 16
  main:
  #Prologue
  stqd $lr, LR_OFFSET($sp)
  stqd $sp, -MAIN_FRAME_SIZE($sp)
  ai $sp, $sp, -MAIN_FRAME_SIZE
  #Save Registers
  #Save register $127 (will be used for current index)
  stqd $127, MAIN_REG_SAVE_OFFSET($sp)
  #Save register $126 (will be used for base pointer)
  stqd $126, MAIN_REG_SAVE_OFFSET+16($sp)
  #Save register $125 (will be used for final size)
  stqd $125, MAIN_REG_SAVE_OFFSET+24($sp)
  ##COPY IN CONVERSION INFORMATION##
  ila $3, conversion_info #Local Store Address
  #register 4 already has address #64-bit Effective Address
  il $5, CONVERSION_STRUCT_SIZE #Transfer size
  il $6, 0 #DMA Tag
  il $7, MFC_GET_CMD #DMA Command
  brsl $lr, perform_dma
  #Wait for DMA to complete
  il $3, 0
  brsl $lr, wait_for_dma_completion
  ##COPY STRING IN TO BUFFER##
  #Load buffer data pointer
  ila $3, conversion_buffer #Local Store
  lqr $4, conversion_data #64-bit Effective Address
  lqr $5, conversion_length #SIZE
  il $6, 0 #DMA Tag
  il $7, MFC_GET_CMD #DMA Command
  brsl $lr, perform_dma
  #Wait for DMA to complete
  il $3, 0
  brsl $lr, wait_for_dma_completion
  #LOOP THROUGH BUFFER
  #Load buffer size
  lqr $125, conversion_length
  #Load buffer pointer
  ila $126, conversion_buffer
  #Load buffer index
  il $127, 0
  loop:
  ceq $7, $125, $127
  brnz $7, loop_end
  #Compute address for function parameter
  a $3, $127, $126
  #Next index
  ai $127, $127, 1
  #Run function
  brsl $lr, convert_to_upper
  #Repeat loop
  br loop
  loop_end:
  #Copy data back
  ila $3, conversion_buffer #Local Store Address
  lqr $4, conversion_data #64-bit effective address
  lqr $5, conversion_length #Size
  il $6, 0 #DMA Tag
  il $7, MFC_PUT_CMD #DMA Command
  brsl $lr, perform_dma
  #Wait for DMA to complete
  il $3, 0
  brsl $lr, wait_for_dma_completion
  #Return Value
  il $3, 0
  #Epilogue
  ai $sp, $sp, MAIN_FRAME_SIZE
  lqd $lr, LR_OFFSET($sp)
  bi $lr
  This code relies on some utility functions for handling DMA commands. Enter
  those functions as dma_utils.s:
  dma_utils.s
  ##UTILITY FUNCTION TO PERFORM DMA OPS##
  #Parameters - Local Store Address, 64-bit Effective Address, Transfer Size,
  DMA Tag, DMA Command
  .global perform_dma
  .type perform_dma, @function
  perform_dma:
  shlqbyi $9, $4, 4 #Get the low-order 32-bits of the address
  wrch $MFC_LSA, $3
  wrch $MFC_EAH, $4
  wrch $MFC_EAL, $9
  wrch $MFC_Size, $5
  wrch $MFC_TagID, $6
  wrch $MFC_Cmd, $7
  bi $lr
  .global wait_for_dma_completion
  .type wait_for_dma_completion, @function
  wait_for_dma_completion:
  #We receive a tag in register 3 - convert to a tag mask
  il $4, 1
  shl $4, $4, $3
  wrch $MFC_WrTagMask, $4
  #Tell the DMA that we only want it to inform us on DMA completion
  il $5, 2
  wrch $MFC_WrTagUpdate, $5
  #Wait for DMA Completion, and store the result in the return value
  rdch $3, $MFC_RdTagStat
  #Return
  bi $lr:
  spu-gcc convert_dma_main.s dma_utils.s convert_to_upper.s -o spe_convert
  embedspu -m64 convert_to_upper_handle spe_convert spe_convert_csf.o):
  ppu_dma_main.c
  #include <stdio.h>
  #include <libspe.h>
  #include <errno.h>
  #include <string.h>
  /*;
  /* Pad string to a quadword -- there are 12 spaces at the end. */
  char *tmp_str = "This is the string we want to convert to uppercase. ";
  /* Copy it to an aligned boundary */
  char *str = memalign(16, strlen(tmp_str) + 1);
  strcpy(str, tmp_str);
  /* Create conversion structure on an aligned boundary */
  conversion_structure conversion_info __attribute__((aligned(16)));
  /* Set the data elements in the parameter structure */
  conversion_info.length = strlen(str) + 1; /* add one for null byte */
  conversion_info.data = (unsigned long long)str;
  /*);
  /* Print out result */
  printf("The converted string is: %s\n", str);
  return 0;
  }
  To build and execute the program, enter the following commands:
  gcc -m64 spe_convert_csf.o ppu_dma_main.c -lspe -o dma_convert
  ./dma_convert.
  libspe.h
  convert_to_upper_handle
  extern
  spe_convert_csf.o
  embedspu.
  unsigned long long
  memalign
  strcpy.
  spe_create_thread
  spe_wait.
  perform_dma
  wrch
  MFC_GET_CMD
  MFC_PUT_CMD.
  wait_for_dma_completion
  $MFC_WrTagUpdate
  $MFC_RdTagStat
  rdch.
  .align 4
  __attribute__((aligned(16)))
  .octa
  CONVERSION_STRUCT_SIZE).
  .bss
  .data
  .lcomm
  conversion_buffer, 16384
  conversion_buffer:
  ila
  ilhu $3, conversion_buffer@h #load high-order 16 bits of conversion_buffer
  iohu $3, conversion_buffer@l #"or" it with the low-order 16 bits of conversion_buffer.
  loop_end):
  square_server.s
  .text
  .global main
  .type main, @function
  main:
  #Read the value from the inbox (stalls if no value until one is available)
  rdch $3, $SPU_RdInMbox
  #Square the value
  mpyu $3, $3, $3
  #Write the value back
  wrch $SPU_WrOutMbox, $3
  #Go back and do it again
  br main):
  square_client.c
  #include <libspe.h>
  #include <stdio.h>
  extern spe_program_handle_t square_server_handle;
  int main() {
  int status = 0;
  /* Create SPE thread */
  speid_t spe_id = spe_create_thread(0, &square_server_handle, NULL, NULL, -1, 0);
  if(spe_id == 0) {
  fprintf(stderr, "Unable to create SPE thread!\n");
  return 1;
  }
  /* Request a square */
  spe_write_in_mbox(spe_id, 4);
  /* Wait for result to be available */
  while(!spe_stat_out_mbox(spe_id)) {}
  /* Read and display result */
  printf("The square of 4 is %d\n", spe_read_out_mbox(spe_id));
  /* Do it again */
  spe_write_in_mbox(spe_id, 10);
  while(!spe_stat_out_mbox(spe_id)) {}
  printf("The square of 10 is %d\n", spe_read_out_mbox(spe_id));
  return 0;
  }
  To compile and run this program, issue the following commands:
  spu-gcc square_server.s -o square_server
  embedspu -m64 square_server_handle square_server square_server_csf.o
  gcc -m64 square_client.c square_server_csf.o -lspe -o square
  ./square.
  spe_stat_out_mbox
  spe_stat_in_mbox.
  Conclusion.
  Resources
  copy_from_ls
  copy_to_ls.