summaryrefslogtreecommitdiffstats
path: root/tinyusb/docs/reference/concurrency.rst
blob: 776fa4b6dcdd6f4af87d2e184da2e875c035528b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
***********
Concurrency
***********

The TinyUSB library is designed to operate on single-core MCUs with multi-threaded applications in mind. Interaction with interrupts is especially important to pay attention to.
It is compatible with optionally using a RTOS.

General
-------

When writing code, keep in mind that the OS (if using a RTOS) may swap out your code at any time. Also, your code can be preempted by an interrupt at any time.

Application Code
----------------

The USB core does not execute application callbacks while in an interrupt context. Calls to application code are from within the USB core task context. Note that the application core will call class drivers from within their own task.

Class Drivers
-------------

Class driver code should never be called from an interrupt context by the USB core, though the application is allowed to call class driver functions from interrupts. USB core functions may be called simultaneously by multiple tasks. Use care that proper locking is used to guard the USBD core functions from this case.

Class drivers are allowed to call ``usbd_*`` functions, but not ``dcd_*`` functions.

USB Core
--------

All functions that may be called from an (USB core) interrupt context have a ``bool in_isr`` parameter to remind the implementer that special care must be taken.

Interrupt handlers must not directly call class driver code, they must pass a message to the USB core's task.

 ``usbd_*`` functions may be called from interrupts without any notice. They may also be called simultaneously by multiple tasks.

Device Drivers
--------------

Much of the processing of the USB stack is done in an interrupt context, and care must be taken in order to ensure variables are handled in the appropriate ways by the compiler and optimizer.

In particular:

*  Ensure that all memory-mapped registers (including packet memory) are marked as volatile. GCC's optimizer will even combine memory access (like two 16-bit to be a 32-bit) if you don't mark the pointers as volatile. On some architectures, this can use macros like _I , _O , or _IO.
*  All defined global variables are marked as  ``static``.
d='n321' href='#n321'>321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713
/**CFile****************************************************************

  FileName    [cmdUtils.c]

  SystemName  [ABC: Logic synthesis and verification system.]

  PackageName [Command processing package.]

  Synopsis    [Various utilities of the command package.]

  Author      [Alan Mishchenko]
  
  Affiliation [UC Berkeley]

  Date        [Ver. 1.0. Started - June 20, 2005.]

  Revision    [$Id: cmdUtils.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $]

***********************************************************************/

#include "base/abc/abc.h"
#include "base/main/mainInt.h"
#include "cmdInt.h"
#include <ctype.h>

ABC_NAMESPACE_IMPL_START
    // proper declaration of isspace

////////////////////////////////////////////////////////////////////////
///                        DECLARATIONS                              ///
////////////////////////////////////////////////////////////////////////

static int CmdCommandPrintCompare( Abc_Command ** ppC1, Abc_Command ** ppC2 );

////////////////////////////////////////////////////////////////////////
///                     FUNCTION DEFINITIONS                         ///
////////////////////////////////////////////////////////////////////////

/**Function*************************************************************

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int cmdCheckShellEscape( Abc_Frame_t * pAbc, int argc, char ** argv)
{
    int RetValue;
    if (argv[0][0] == '!') 
    {
        const int size = 4096;
        int i;
        char * buffer = ABC_ALLOC(char, 10000);
        strncpy (buffer, &argv[0][1], size);
        for (i = 1; i < argc; ++i)
        {
                strncat (buffer, " ", size);
                strncat (buffer, argv[i], size);
        }
        if (buffer[0] == 0) 
            strncpy (buffer, "/bin/sh", size);
        RetValue = system (buffer);
        ABC_FREE( buffer );

        // NOTE: Since we reconstruct the cmdline by concatenating
        // the parts, we lose information. So a command like
        // `!ls "file name"` will be sent to the system as
        // `ls file name` which is a BUG

        return 1;
    }
    else
    {
        return 0;
    }
}

/**Function*************************************************************

  Synopsis    [Executes one command.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int CmdCommandDispatch( Abc_Frame_t * pAbc, int * pargc, char *** pargv )
{
    int argc = *pargc;
    char ** argv = *pargv;
    char ** argv2;

    Abc_Ntk_t * pNetCopy;
    int (*pFunc) ( Abc_Frame_t *, int, char ** );
    Abc_Command * pCommand;
    char * value;
    int fError;
    double clk;

    if ( argc == 0 )
        return 0;

    if ( cmdCheckShellEscape( pAbc, argc, argv ) == 1 )
        return 0;

    // get the command
    if ( ! st__lookup( pAbc->tCommands, argv[0], (char **)&pCommand ) )
    {   // the command is not in the table
        // if there is only one word with an extension, assume this is file to be read
        if ( argc == 1 && strstr( argv[0], "." ) )
        {
            // add command 'read' assuming that this is the file name
            argv2 = CmdAddToArgv( argc, argv );
            CmdFreeArgv( argc, argv );
            argc = argc+1;
            argv = argv2;
            *pargc = argc;
            *pargv = argv;
            if ( ! st__lookup( pAbc->tCommands, argv[0], (char **)&pCommand ) )
                assert( 0 );
        }
        else
        {
            fprintf( pAbc->Err, "** cmd error: unknown command '%s'\n", argv[0] );
            fprintf( pAbc->Err, "(this is likely caused by using an alias defined in \"abc.rc\"\n" );
            fprintf( pAbc->Err, "without having this file in the current or parent directory)\n" );
            return 1;
        }
    }

    // get the backup network if the command is going to change the network
    if ( pCommand->fChange ) 
    {
        if ( pAbc->pNtkCur && Abc_FrameIsFlagEnabled( "backup" ) )
        {
            pNetCopy = Abc_NtkDup( pAbc->pNtkCur );
            Abc_FrameSetCurrentNetwork( pAbc, pNetCopy );
            // swap the current network and the backup network 
            // to prevent the effect of resetting the short names
            Abc_FrameSwapCurrentAndBackup( pAbc );
        }
    }

    // execute the command
    clk = Extra_CpuTimeDouble();
    pFunc = (int (*)(Abc_Frame_t *, int, char **))pCommand->pFunc;
    fError = (*pFunc)( pAbc, argc, argv );
    pAbc->TimeCommand += Extra_CpuTimeDouble() - clk;

    // automatic execution of arbitrary command after each command 
    // usually this is a passive command ... 
    if ( fError == 0 && !pAbc->fAutoexac )
    {
        if ( st__lookup( pAbc->tFlags, "autoexec", &value ) )
        {
            pAbc->fAutoexac = 1;
            fError = Cmd_CommandExecute( pAbc, value );
            pAbc->fAutoexac = 0;
        }
    }
    return fError;
}

/**Function*************************************************************

  Synopsis    [Splits the command line string into individual commands.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
const char * CmdSplitLine( Abc_Frame_t * pAbc, const char *sCommand, int *argc, char ***argv )
{
    const char *p, *start;
    char c;
    int i, j;
    char *new_arg;
    Vec_Ptr_t * vArgs;
    int single_quote, double_quote;

    vArgs = Vec_PtrAlloc( 10 );

    p = sCommand;
    for ( ;; )
    {
        // skip leading white space 
        while ( isspace( ( int ) *p ) )
        {
            p++;
        }

        // skip until end of this token 
        single_quote = double_quote = 0;
        for ( start = p; ( c = *p ) != '\0'; p++ )
        {
            if ( c == ';' || c == '#' || isspace( ( int ) c ) )
            {
                if ( !single_quote && !double_quote )
                {
                    break;
                }
            }
            if ( c == '\'' )
            {
                single_quote = !single_quote;
            }
            if ( c == '"' )
            {
                double_quote = !double_quote;
            }
        }
        if ( single_quote || double_quote )
        {
            ( void ) fprintf( pAbc->Err, "** cmd warning: ignoring unbalanced quote ...\n" );
        }
        if ( start == p )
            break;

        new_arg = ABC_ALLOC( char, p - start + 1 );
        j = 0;
        for ( i = 0; i < p - start; i++ )
        {
            c = start[i];
            if ( ( c != '\'' ) && ( c != '\"' ) )
            {
                new_arg[j++] = isspace( ( int ) c ) ? ' ' : start[i];
            }
        }
        new_arg[j] = '\0';
        Vec_PtrPush( vArgs, new_arg );
    }

    *argc = vArgs->nSize;
    *argv = (char **)Vec_PtrReleaseArray( vArgs );
    Vec_PtrFree( vArgs );
    if ( *p == ';' )
    {
        p++;
    }
    else if ( *p == '#' )
    {
        for ( ; *p != 0; p++ ); // skip to end of line 
    }
    return p;
}

/**Function*************************************************************

  Synopsis    [Replaces parts of the command line string by aliases if given.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int CmdApplyAlias( Abc_Frame_t * pAbc, int *argcp, char ***argvp, int *loop )
{
    int i, argc, stopit, added, offset, did_subst, subst, fError, newc, j;
    const char *arg;
    char **argv, **newv;
    Abc_Alias *alias;

    argc = *argcp;
    argv = *argvp;
    stopit = 0;
    for ( ; *loop < 200; ( *loop )++ )
    {
        if ( argc == 0 )
            return 0;
        if ( stopit != 0 || st__lookup( pAbc->tAliases, argv[0],  (char **) &alias ) == 0 )
        {
            return 0;
        }
        if ( strcmp( argv[0], alias->argv[0] ) == 0 )
        {
            stopit = 1;
        }
        ABC_FREE( argv[0] );
        added = alias->argc - 1;

        /* shift all the arguments to the right */
        if ( added != 0 )
        {
            argv = ABC_REALLOC( char *, argv, argc + added );
            for ( i = argc - 1; i >= 1; i-- )
            {
                argv[i + added] = argv[i];
            }
            for ( i = 1; i <= added; i++ )
            {
                argv[i] = NULL;
            }
            argc += added;
        }
        subst = 0;
        for ( i = 0, offset = 0; i < alias->argc; i++, offset++ )
        {
            arg = CmdHistorySubstitution( pAbc, alias->argv[i], &did_subst );
            if ( arg == NULL )
            {
                *argcp = argc;
                *argvp = argv;
                return ( 1 );
            }
            if ( did_subst != 0 )
            {
                subst = 1;
            }
            fError = 0;
            do
            {
                arg = CmdSplitLine( pAbc, arg, &newc, &newv );
                /*
                 * If there's a complete `;' terminated command in `arg',
                 * when split_line() returns arg[0] != '\0'.
                 */
                if ( arg[0] == '\0' )
                { /* just a bunch of words */
                    break;
                }
                fError = CmdApplyAlias( pAbc, &newc, &newv, loop );
                if ( fError == 0 )
                {
                       fError = CmdCommandDispatch( pAbc, &newc, &newv );
                }
                CmdFreeArgv( newc, newv );
            }
            while ( fError == 0 );
            if ( fError != 0 )
            {
                *argcp = argc;
                *argvp = argv;
                return ( 1 );
            }
            added = newc - 1;
            if ( added != 0 )
            {
                argv = ABC_REALLOC( char *, argv, argc + added );
                for ( j = argc - 1; j > offset; j-- )
                {
                    argv[j + added] = argv[j];
                }
                argc += added;
            }
            for ( j = 0; j <= added; j++ )
            {
                argv[j + offset] = newv[j];
            }
            ABC_FREE( newv );
            offset += added;
        }
        if ( subst == 1 )
        {
            for ( i = offset; i < argc; i++ )
            {
                ABC_FREE( argv[i] );
            }
            argc = offset;
        }
        *argcp = argc;
        *argvp = argv;
    }

    fprintf( pAbc->Err, "** cmd warning: alias loop\n" );
    return 1;
}

/**Function*************************************************************

  Synopsis    [Performs history substitution (now, disabled).]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
char * CmdHistorySubstitution( Abc_Frame_t * pAbc, char *line, int *changed )
{
    // as of today, no history substitution 
    *changed = 0;
    return line;
}

/**Function*************************************************************

  Synopsis    [Opens the file with path (now, disabled).]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
FILE * CmdFileOpen( Abc_Frame_t * pAbc, char *sFileName, char *sMode, char **pFileNameReal, int silent )
{
    char * sRealName, * sPathUsr, * sPathLib, * sPathAll;
    FILE * pFile;
    
    if (strcmp(sFileName, "-") == 0) {
        if (strcmp(sMode, "w") == 0) {
            sRealName = Extra_UtilStrsav( "stdout" );
            pFile = stdout;
        }
        else {
            sRealName = Extra_UtilStrsav( "stdin" );
            pFile = stdin;
        }
    }
    else {
        sRealName = NULL;
        if (strcmp(sMode, "r") == 0) {
            
            /* combine both pathes if exist */
            sPathUsr = Cmd_FlagReadByName(pAbc,"open_path");
            sPathLib = Cmd_FlagReadByName(pAbc,"lib_path");
            
            if ( sPathUsr == NULL && sPathLib == NULL ) {
                sPathAll = NULL;
            }
            else if ( sPathUsr == NULL ) {
                sPathAll = Extra_UtilStrsav( sPathLib );
            }
            else if ( sPathLib == NULL ) {
                sPathAll = Extra_UtilStrsav( sPathUsr );
            }
            else {
                sPathAll = ABC_ALLOC( char, strlen(sPathLib)+strlen(sPathUsr)+5 );
                sprintf( sPathAll, "%s:%s",sPathUsr, sPathLib );
            }
            if ( sPathAll != NULL ) {
                sRealName = Extra_UtilFileSearch(sFileName, sPathAll, "r");
                ABC_FREE( sPathAll );
            }
        }
        if (sRealName == NULL) {
            sRealName = Extra_UtilTildeExpand(sFileName);
        }

        if ((pFile = fopen(sRealName, sMode)) == NULL) {
            if (! silent) {
//                perror(sRealName);
                Abc_Print( 1, "Cannot open file \"%s\".\n", sRealName );
            }
        }
        else
        {
            // print the path/name of the resource file 'abc.rc' that is being loaded
            if ( !silent && strlen(sRealName) >= 6 && strcmp( sRealName + strlen(sRealName) - 6, "abc.rc" ) == 0 )            
                Abc_Print( 1, "Loading resource file \"%s\".\n", sRealName );
        }
    }
    if ( pFileNameReal )
        *pFileNameReal = sRealName;
    else
        ABC_FREE(sRealName);
    
    return pFile;
}

/**Function*************************************************************

  Synopsis    [Frees the previously allocated argv array.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void CmdFreeArgv( int argc, char **argv )
{
    int i;
    for ( i = 0; i < argc; i++ )
        ABC_FREE( argv[i] );
    ABC_FREE( argv );
}

/**Function*************************************************************

  Synopsis    [Frees the previously allocated argv array.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
char ** CmdAddToArgv( int argc, char ** argv )
{
    char ** argv2;
    int i;
    argv2 = ABC_ALLOC( char *, argc + 1 ); 
    argv2[0] = Extra_UtilStrsav( "read" ); 
//    argv2[0] = Extra_UtilStrsav( "&r" ); 
    for ( i = 0; i < argc; i++ )
        argv2[i+1] = Extra_UtilStrsav( argv[i] ); 
    return argv2;
}

/**Function*************************************************************

  Synopsis    [Frees the previously allocated command.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void CmdCommandFree( Abc_Command * pCommand )
{
    ABC_FREE( pCommand->sGroup );
    ABC_FREE( pCommand->sName );
    ABC_FREE( pCommand );
}


/**Function*************************************************************

  Synopsis    [Prints commands alphabetically by group.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void CmdCommandPrint( Abc_Frame_t * pAbc, int fPrintAll )
{
    const char *key;
    char *value;
    st__generator * gen;
    Abc_Command ** ppCommands;
    Abc_Command * pCommands;
    int nCommands, i;
    char * sGroupCur;
    int LenghtMax, nColumns, iCom = 0;

    // put all commands into one array
    nCommands = st__count( pAbc->tCommands );
    ppCommands = ABC_ALLOC( Abc_Command *, nCommands );
    i = 0;
    st__foreach_item( pAbc->tCommands, gen, &key, &value )
    {
        pCommands = (Abc_Command *)value;
        if ( fPrintAll || pCommands->sName[0] != '_' )
            ppCommands[i++] = pCommands;
    }
    nCommands = i;

    // sort command by group and then by name, alphabetically
    qsort( (void *)ppCommands, nCommands, sizeof(Abc_Command *), 
            (int (*)(const void *, const void *)) CmdCommandPrintCompare );
    assert( CmdCommandPrintCompare( ppCommands, ppCommands + nCommands - 1 ) <= 0 );

    // get the longest command name
    LenghtMax = 0;
    for ( i = 0; i < nCommands; i++ )
        if ( LenghtMax < (int)strlen(ppCommands[i]->sName) )
             LenghtMax = (int)strlen(ppCommands[i]->sName); 
    // get the number of columns
    nColumns = 79 / (LenghtMax + 2);

    // print the starting message 
    fprintf( pAbc->Out, "      Welcome to ABC compiled on %s %s!", __DATE__, __TIME__ );

    // print the command by group
    sGroupCur = NULL;
    for ( i = 0; i < nCommands; i++ )
        if ( sGroupCur && strcmp( sGroupCur, ppCommands[i]->sGroup ) == 0 )
        { // this command belongs to the same group as the previous one
            if ( iCom++ % nColumns == 0 )
                fprintf( pAbc->Out, "\n" ); 
            // print this command
            fprintf( pAbc->Out, " %-*s", LenghtMax, ppCommands[i]->sName );
        }
        else
        { // this command starts the new group of commands
            // start the new group
            fprintf( pAbc->Out, "\n" );
            fprintf( pAbc->Out, "\n" );
            fprintf( pAbc->Out, "%s commands:\n", ppCommands[i]->sGroup );
            // print this command
            fprintf( pAbc->Out, " %-*s", LenghtMax, ppCommands[i]->sName );
            // remember current command group
            sGroupCur = ppCommands[i]->sGroup;
            // reset the command counter
            iCom = 1;
        }
    fprintf( pAbc->Out, "\n" );
    ABC_FREE( ppCommands );
}
 
/**Function*************************************************************

  Synopsis    [Comparision function used for sorting commands.]

  Description []
                
  SideEffects []

  SeeAlso     []

***********************************************************************/
int CmdCommandPrintCompare( Abc_Command ** ppC1, Abc_Command ** ppC2 )
{
    Abc_Command * pC1 = *ppC1;
    Abc_Command * pC2 = *ppC2;
    int RetValue;

    RetValue = strcmp( pC1->sGroup, pC2->sGroup );
    if ( RetValue < 0 )
        return -1;
    if ( RetValue > 0 )
        return 1;
    // the command belong to the same group

    // put commands with "_" at the end of the list
    if ( pC1->sName[0] != '_' && pC2->sName[0] == '_' )
        return -1;
    if ( pC1->sName[0] == '_' && pC2->sName[0] != '_' )
        return 1;

    RetValue = strcmp( pC1->sName, pC2->sName );
    if ( RetValue < 0 )
        return -1;
    if ( RetValue > 0 )
        return 1;
     // should not be two indentical commands
    assert( 0 );
    return 0;
}
 
/**Function*************************************************************

  Synopsis    [Comparision function used for sorting commands.]

  Description []
                
  SideEffects []

  SeeAlso     []

***********************************************************************/
int CmdNamePrintCompare( char ** ppC1, char ** ppC2 )
{
    return strcmp( *ppC1, *ppC2 );
}

/**Function*************************************************************

  Synopsis    [Comparision function used for sorting commands.]

  Description []
                
  SideEffects []

  SeeAlso     []

***********************************************************************/
void CmdPrintTable( st__table * tTable, int fAliases )
{
    st__generator * gen;
    const char ** ppNames;
    const char * key;
    char* value;
    int nNames, i;

    // collect keys in the array
    ppNames = ABC_ALLOC( const char *, st__count(tTable) );
    nNames = 0;
    st__foreach_item( tTable, gen, &key, &value )
        ppNames[nNames++] = key;

    // sort array by name
    qsort( (void *)ppNames, nNames, sizeof(char *), 
        (int (*)(const void *, const void *))CmdNamePrintCompare );

    // print in this order
    for ( i = 0; i < nNames; i++ )
    {
        st__lookup( tTable, ppNames[i], &value );
        if ( fAliases )
            CmdCommandAliasPrint( Abc_FrameGetGlobalFrame(), (Abc_Alias *)value );
        else
            fprintf( stdout, "%-15s %-15s\n", ppNames[i], value );
    }
    ABC_FREE( ppNames );
}

////////////////////////////////////////////////////////////////////////
///                       END OF FILE                                ///
////////////////////////////////////////////////////////////////////////
ABC_NAMESPACE_IMPL_END