Semihosting

From Texas Instruments Wiki
Jump to: navigation, search

Semihosting

For more information on semihosting please refer to here


CCS Semihosting

Semihosting support could be enabled/disabled via the “Program/Memory Load Options” debugger options. It is important to note that any program built with semi hosting support will not run correctly when target is detached from the debugger or is attached and semi hosting support is disabled.

Device Support

  • Sitara A8 devices: support added in CCSv5.4
  • CortexM devices: support added in CCSv6.0
  • A15 devices: support added in CCSv5.5


Semihosting.jpeg

Creating an example Semihosting project in CCS

Note that the below example is for the Project Wizard in CCSv5. The CCSv6 Project Wizard varies slightly.

In CCS perform the following.

  • File -> CCS Project
  • Provide a project name
  • Select the ARM Device ( AM3359 as an exmaple )
  • Expand the "advanced settings" node and set the compiler version to GNU

Semihosting3.png

  • Expand the "Project templates and examples" node and select "Hello World" under basic examples

Semihosting2.png

Limitations and Dependencies

SVC_Handler

The CCS debugger supports semihosting by emulating a SWI exception handler using breakpoints. It requires that any target program utilizing semihosting install a SWI exception handler named SVC_Handler. The debugger will add a breakpoint at the installed SVC_Handler to intercept and handle any SWI exceptions ( Semihosting calls ). The handler itself will not get invoked in the presence of the debugger.

Supported Calls

  • SYS_CLOSE (0x02)
  • SYS_ERRNO (0x13)
  • SYS_FLEN (0x0C)
  • SYS_GET_CMDLINE (0x15)
Use Memory.loadHostedProgram() API to load a program via DSS Scripting and provide command line arguments that should be passed to it.
Alternatively, the GEL function GEL_SetSemihostingMainArgs() could be used to set the argument string to be passed to main. This semihosting call is generally made during the initialization by the c runtime library before main is reached. Therefore, the GEL function must be called prior to the initialization code is executed.


  • SYS_HEAPINFO (0x16)
In order to support this call the following symbols must be defined in the executable
__HeapBase
__HeapLimit
__StackLimit
__StackBase/__StackTop
The address of these symbols is used by the debugger to fulfill the requirements of the SYS_HEAPINFO call. Note: This call is generally made by the initialization code of the semihosting enabled c library implementation.
  • SYS_ISERROR (0x08)
  • SYS_ISTTY (0x09)
  • SYS_OPEN (0x01)
  • SYS_READ (0x06)
  • SYS_READC (0x07)
  • SYS_REMOVE (0x0E)
  • SYS_SEEK (0x0A)
  • SYS_TMPNAM (0x0D)
  • SYS_WRITE (0x05)
  • SYS_WRITEC (0x03)
  • SYS_WRITE0 (0x04)
  • SYS_TIME (0x11)
  • SYS_CLOCK (0x10)

Unsupported

  • SYS_SYSTEM (0x12)
  • SYS_TICKFREQ (0x31)
  • SYS_ELAPSED (0x30)
  • SYS_RENAME (0x0F)

Stdio

Semihosting is in theory is independent of c stdio. However, most c libraries provide semi hosting support in a hosted environment by implementing the standard c stdio functions. To find out which stdio calls are supported the specific c library documentation should be consulted.

CCS semihosting support is verified against the following test program built with the Linaro gcc toolchain.




#include <stdio.h>

#define long_len 1202
#define short_len 96


/********************************************************************************
* IOtest.c *
* *
* This is a test block for CIO. *
* Listing the functions tested by the block on stdin/stdout: *
* fflush(), puts(), fputs(), putc(), putchar(), fputc(), printf(), fwrite(), *
* gets(), fgets(), getc(), getchar(), fgetc(), scanf(), fread() *
* Listing the functions tested by the block on files: *
* fopen(), fclose(), remove(), fputs(), fgets(), fprintf(), fscanf(), fread(), *
* fwrite(), ftell(), rewind(), fgetpos(), fsetpos(), fseek(), ferror(), *
* perror(), clearerr(), feof() *
* *
*********************************************************************************/


char tmpch, ch = 'c';
// shortstring - string containing printable ASCII characters and some special characters
char * shortstring
 = "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~";
// longstring - string of more than 1056 bytes to test overflow
char longstring[long_len];
char longtmpstring[long_len];
char tmpstring[short_len];
FILE * stream;
fpos_t pos;

//============================================================================

void puts_test (char * str) {

 // Testing puts(<string>)
 puts (str);
 printf ("\n"); fflush (stdout);
}

void fputs_test (char * str) {

 // Testing fputs(<string>, stdout)
 fputs (str, stdout);
 printf ("\n"); fflush (stdout);
}

void printf_test (char * str) {

 // Testing printf() on strings
 printf ("%s", str);
 printf ("\n"); fflush (stdout);
}

void fwrite_test (char * str) {

 // Testing fwrite()
 fwrite (str, sizeof(char), strlen(str), stdout);
 printf ("\n"); fflush (stdout);
}

void reopen_stdin () {

 // creating cio.in with information stored
 stream = NULL;
 if ((stream = fopen("cio.in", "w+")) != NULL) printf ("cio.in created\n");
 fprintf (stream, "%s\n%s", shortstring, shortstring);
 fflush (stream);
 // Set pointer to beginning of file:
 fseek (stream, 0L, SEEK_SET);
 fscanf (stream, "%s", tmpstring);
 if (strcmp(shortstring, tmpstring) == 0) printf ("cio.in initialized with a string\n");
 else printf("cio.in not initialized with a string\n");
 fclose (stream);
 stream = freopen ("cio.in", "r+", stdin); // freopen() - reassigning stdin for further use
 if (stream != NULL) printf ("stdin reassigned to cio.in\n");
 else printf("stdin NOT reassigned cio.in\n");
}

void gets_test () {

 // Testing gets()
 rewind (stdin);
 tmpstring[0] = '\0';
 gets (tmpstring);
 printf ("%s\n", tmpstring);
}

void return_value_of_gets_test () {

 // No error should occur and gets() should not return NULL
 rewind (stdin);
 tmpstring[0] = '\0';
 if (gets(tmpstring) != NULL) printf ("gets() successful\n");
 else printf ("gets() not successful\n");
}

void fgets_test () {

 // Testing fgets()
 rewind (stdin);
 tmpstring[0] = '\0';
 fgets(tmpstring, short_len-1, stdin);
 printf("%s\n", tmpstring);
}

void return_value_of_fgets_test () {

 // No error should occur and fgets() should not return NULL
 rewind (stdin);
 tmpstring[0] = '\0';
 if ( fgets(tmpstring, short_len-1, stdin) != NULL ) printf("fgets() successful\n");
 else printf("fgets() not successful\n");
}

void getc_test () {

 // Testing getc()
 rewind (stdin);
 tmpch = '\0';
 tmpch = getc(stdin);
 printf("%c\n", tmpch);
}

void ungetc_test () {

 // Has to be run after getc_test or other call to getc
 // Continuing the previous test - testing ungetc(<char>, stdin)
 ungetc(tmpch, stdin);
 printf("After ungetc(), the first character in the stream is %c\n", getc(stdin));
}

void getchar_test () {

 // Testing getchar()
 rewind (stdin);
 tmpch = '\0';
 tmpch = getchar();
 printf("%c\n", tmpch);
}

void fgetc_test () {

 // Testing fgetc(stdin)
 rewind (stdin);
 tmpch = '\0';
 tmpch = fgetc(stdin);
 printf("%c\n", tmpch);
}

void scanf_test () {

 // Testing scanf()
 rewind (stdin);
 tmpstring[0] = '\0';
 scanf("%s", tmpstring);
 printf("%s\n", tmpstring);
}

void fread_test () {

 // Testing fread()
 rewind (stdin);
 tmpstring[0] = '\0';
 fread(tmpstring, sizeof(char), short_len, stdin);
 tmpstring[short_len-1] = '\0';
 printf("%s\n", tmpstring);
}

void fclose_remove_file () {

 // Has to be run after reopen_stdin ()
 if( !fclose( stream ) ) printf ( "cio.in closed\n" );
 if ( remove( "cio.in" ) == 0 ) printf ( "cio.in deleted\n" );
}

void fputs_fgets_test (char * str) {

 // Testing fputs() and fgets() with files
 // shortstring
 rewind (stream);
 fputs (str, stream); fflush (stream);
 longtmpstring[0] = '\0';
 rewind (stream);
 fgets (longtmpstring, strlen (str) + 1, stream); // 1 is to account for the not counted '\0'
 puts (longtmpstring); fflush (stdout);
}

void fprintf_fscanf_test (char * str) {

 rewind (stream);
 fprintf (stream, "%s", str); fflush (stream);
 longtmpstring[0] = '\0';
 rewind (stream);
 fscanf (stream, "%s", longtmpstring);
 puts (longtmpstring); fflush (stdout);
}

void fwrite_fread_test (char * str) {

 rewind (stream);
 fwrite (str, sizeof( char ), strlen(str), stream); fflush(stream);
 longtmpstring[0] = '\0';
 rewind (stream);
 fread (longtmpstring, sizeof (char), strlen (str), stream); longtmpstring[strlen(str)] = '\0';
 puts (longtmpstring); fflush (stdout);
}

void file_ftell_rewind_fgetpos_fsetpos_fseek_test () {

 //------------------------------------------------------------------------
 // Testing ftell(), rewind(), fgetpos(), fsetpos(), fseek()
 // The following tests rely on the assumption that each character in the file is one byte

 stream = fopen( "testfile2.tmp", "w+" );

 fputs (shortstring, stream); fflush(stream);
 rewind (stream);
 // The position should be 0
 printf ("%ld\n", ftell(stream));
 fgets (tmpstring, 10, stream);
 // After reading 10 bytes from the beginning of the file, the position should be 9
 printf ("%ld\n", ftell(stream));
 // Save the position 9 to pos
 if (fgetpos(stream, &pos) != 0) printf("fgetpos() returned an error\n");
 fgets (tmpstring, 10, stream );
 // After reading 10 more bytes from the beginning of the file, the position should be 18
 printf ("%ld\n", ftell(stream));
 if (fsetpos( stream, &pos ) != 0 ) printf("fsetpos() returned an error\n");
 // Now, the position should be 9 again
 printf ("%ld\n", ftell(stream));
 // Move pointer to the beginning of the file
 if (fseek(stream, 0L, SEEK_SET)) printf ("fseek() returned an error\n");
 // The position should be 0
 printf ("%ld\n", ftell(stream));
 fclose(stream);
}

void file_feof_test () {

 // Testing feof() with testfile2.tmp from the previous test
 stream = fopen("testfile2.tmp", "r");
 fseek(stream, 0L, SEEK_END);
 // go past the end of the file and attempt a read
 tmpch = getc(stream);
 if (feof(stream)) printf("end of file error\n");
 else printf("no end of file error\n");
}

void file_ferror_perror_clearerr_test () {

 // Testing stderr, ferror(), perror(), clearerr() with stdout
 // Creating an error on stdin by attempting to write to it
 
 clearerr(stdin);
 if (ferror(stdin)) fprintf(stderr, "error not removed");
 else perror("error removed");
}

//============================================================================

void main () {

 // initializing longstring
 int i;
 for (i = 0; i < long_len-1; ++i) { longstring[i] = 'a';}
 longstring[long_len-1] = '\0';

 //------------------------------------------------------------------------

 // Testing fflush(stdout)
 fflush(stdout);
 // Flushing stdout on a presumably empty buffer
 if (fflush (stdout) == 0) printf ("stdout flushed successfully");
 else printf ("stdout did not flush successfully");
 fflush (stdout);
 printf ("\n");

 //------------------------------------------------------------------------
 // Working with stdout
 //------------------------------------------------------------------------

 puts_test (shortstring);
 puts_test (longstring);
 fputs_test (shortstring);
 fputs_test (longstring);
 putc (ch, stdout); printf("\n"); fflush(stdout);
 putchar (ch); printf("\n"); fflush(stdout);
 fputc (ch, stdout); printf("\n"); fflush(stdout);
 printf_test (shortstring);
 printf_test (longstring);
 printf("%c", ch); printf("\n"); fflush(stdout);
 fwrite_test (shortstring);
 fwrite_test (longstring);

 //------------------------------------------------------------------------
 // Testing standard input (reassigned to a file)
 //------------------------------------------------------------------------

 reopen_stdin ();
 gets_test ();
 return_value_of_gets_test ();
 fgets_test ();
 return_value_of_fgets_test ();
 getc_test ();
 ungetc_test ();
 getchar_test ();
 fgetc_test ();
 scanf_test ();
 fread_test ();
 fclose_remove_file ();

 //------------------------------------------------------------------------
 // Working with files, input and output
 //------------------------------------------------------------------------

 if ((stream = fopen ("testfile1.tmp", "w+")) != NULL) printf ("testfile1.tmp opened\n");
 fputs_fgets_test (shortstring);
 fputs_fgets_test (longstring);
 fclose (stream);

 stream = fopen ("testfile1.tmp", "w+");
 fprintf_fscanf_test (shortstring);
 fprintf_fscanf_test (longstring);
 fclose (stream);

 stream = fopen ("testfile1.tmp", "w+");
 fwrite_fread_test (shortstring);
 fwrite_fread_test (longstring);
 fclose (stream);

 file_ftell_rewind_fgetpos_fsetpos_fseek_test (); // opens testfile2.tmp
 file_feof_test ();
 file_ferror_perror_clearerr_test ();
 fclose (stream);

 remove ("testfile1.tmp");
 remove ("testfile2.tmp");
}