This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

WEBENCH® Tools/AM3352: BIOS_exit causes an assertion

Part Number: AM3352
Other Parts Discussed in Thread: SYSBIOS,

Tool/software: WEBENCH® Design Tools

I have an application that hits an assertion when it calls BIOS_exit(0), I'm using SYS/BIOS 6.73.1.1.  The error message that comes out is:  A_badContext: bad calling context. Must be called from a Task.  Googling a bit shows that this error will pop up when calling Semaphore_pend while not in a task.  Couple of questions:

- Is calling Semaphore_pend while not in a task the only time one gets a A_badContext error?

- Is there an API function that one can call to determine if they are in a task?

Interestingly, I have two other applications that have exactly the same code leading up to and into the call to BIOS_exit(0) that work just fine.  Unclear at present what is actually different with this one application compared with the other two.

Kevin Jennings

  • I found the answer to my second question:  The function BIOS_getThreadType() returns the thread type and if it is a task then it should return BIOS_ThreadType_Task.  Unfortunately, enveloping the calls to Semaphore_pend with this check did not fix the problem.

    Would still like to know if there are any other conditions that can cause a A_badContext assertion other than calling Semaphore_pend outside of a task.

    Kevin Jennings

  • While it looks like there might be other A_bakContext sources, in my particular case, the assertion is being flagged on line 215 of an unknown file.  Line 215 of SYS/BIOS file Semaphore.c is the following assertion statement inside function Semaphore_pend which would seem to be pretty strong evidence that Semaphore_pend is being called from outside of a task.

    Assert_isTrue((BIOS_getThreadType() == BIOS_ThreadType_Task),
    Semaphore_A_badContext);

    The problem is that I've wrapped all of my calls to Semaphore_pend inside a new function Safe_Semaphore_pend (shown below) which verifies that we're in a task before calling Semaphore_pend...but the system still crashes 'somewhere' inside the call to BIOS_exit.

        bool Safe_Semaphore_pend(Semaphore_Handle handle, UInt32 timeout)
        {
            const BIOS_ThreadType CurrentThreadType = BIOS_getThreadType();
            const bool RetVal = (CurrentThreadType == BIOS_ThreadType_Task) ? true : false;
            if (RetVal)
                return(Semaphore_pend(handle, timeout));
            else
                return(false);
        }
    

    Kevin Jennings

  • Hi Kevin,

    Where are you calling BIOS_exit from (e.g. main, a task, idle function, etc.)? When BIOS_exit is called, we change the threadType to be "main" so the asserts should not be happening regardless of where BIOS_exit is called.

    Do you have a simple project that shows the problem? Can you export it and attach it?

    Todd

  • The call to BIOS_exit comes from a task thread type. Unfortunately I don't have a simple project that demonstrates the problem.  In fact, I have two other equally complex and very similar projects that work just fine.  One of those projects uses the same SYS/BIOS 6.73.1.01 as the failing project, the other uses 6.46.5.55.

    If the thread type is set to main from within BIOS_exit then if some semaphore gets waited on via semaphore_pend, it will trip the assertion which is looking for the thread type to be a task:

    Assert_isTrue((BIOS_getThreadType() == BIOS_ThreadType_Task), Semaphore_A_badContext);

    The reason that BIOS_exit is getting called in the first place is because we're getting ready to reboot our system.  Since the code won't run in a debug environment, the only way of getting clues about what is going on is via serial port printf (which cannot always be called, depending on thread type) as well as tracing which can add monitoring of activity under any context.  This monitoring gets stored in a Flash device on our system whenever an assertion occurs and can be retrieved for analysis at the next power up.

    The problem with getting more info is that I can't find the place in SYS/BIOS where the BIOS_exit code is actually running from.  I've been able to confirm through tracing that the pointer to the BIOS_module->exitFunc just prior to the call to BIOS_exit is correct and is pointing to the TI function ti_sysbios_BIOS_exitFunc__I.

    In case it helps, to give a bit more background:

    This project itself has had no problems either with BIOS_exit until the last couple days.  What changed at that time is that I found a place where a semaphore needed to be added to protect a shared resource to address a different problem that I was fixing.  Adding the exact same protection code to each of the three projects works just fine as far as protecting that resource.  However, in this one project, when the BIOS_exit function is called (which has nothing to do with this shared resource) it triggers the assert.  In the other two projects it does not.

    Based on that, I highly suspect that the code that was changed is the likely culprit but it's not clear how.  Most of what I was doing to address the resource sharing problem was to take existing code and put it into a new C++ class.  Around the class functions that needed to share the resource I added a 'Lock' and 'Unlock' function.  This lock/unlock code was an existing C++ class that has been used for a long time so I suspect it is OK.  Here are some of the code snippets:

    The code for the 'CriticalSection' class which has the 'Lock' and 'Unlock' API and envelops the 'Semaphore_pend', 'Semaphore_post' calls.

    	CriticalSection::CriticalSection()
    	{
    	    Semaphore_Params_init(&sem_params_);
    	    sem_params_.mode = Semaphore_Mode_BINARY;
    	    sem_ = Semaphore_create(1, &sem_params_, NULL);
    	}
    	CriticalSection::~CriticalSection()
    	{
    		Semaphore_delete(&sem_);
    	}
    	bool CriticalSection::Lock()
    	{
            return(Safe_Semaphore_pend(sem_, BIOS_WAIT_FOREVER));
    	}
    	bool CriticalSection::Unlock()
    	{
    		Semaphore_post(sem_);
    		return true;
    	}

    The 'Safe_Semaphore_pend' which surrounds the call to Semaphore_pend by first checking to see if we're in a task or not.  This is new code that I added yesterday.  It's was not clear whether each and every call to Semaphore_pend in the project was really done within a task thread so I changed all calls to Semaphore_pend to instead call Safe_Semaphore_pend.  The only raw call to Semaphore_pend that is left in the project is within the Safe_Semaphore_pend function.

        bool Safe_Semaphore_pend(Semaphore_Handle handle, UInt32 timeout)
        {
            const BIOS_ThreadType CurrentThreadType = BIOS_getThreadType();
            const bool RetVal = (CurrentThreadType == BIOS_ThreadType_Task) ? true : false;
            if (RetVal)
                return(Semaphore_pend(handle, timeout));
            else
                return(false);
        }
    

    For the resource that needed to be protected for proper sharing, there is a class that contains a number of member functions and contains a static variable of the 'CriticalSection' class.  All of the functions within this class are previously existing code that was written as free standing C functions.  What I did was to add the appropriate class prefix to the function to make it part of the new class and to add the calls to GateKeeperNorFlash.Lock() and GateKeeperNorFlash.Unlock() surrounding the code that needs to do the resource sharing in order to fix the original problem.  Other free standing C functions were moved into the class either as private members to prevent unintended access or they were functions that did not actually need to share the resource in order to perform their function.

    class nor_s25fl032p
    {
        public:
        nor_s25fl032p(void);
        /*
         * The NOR Flash requires exclusive access to the SPI bus that controls the device while the function
         * is executing.  Each of these functions will first acquire that bus then release it when complete.
         */
        unsigned int NorReadData(unsigned int Addr,unsigned int size,unsigned char *data);
        int SpiFlashWritePage(unsigned int address, unsigned int size, const unsigned char * const data);
        int NorEraseSector(unsigned int Addr);
        unsigned int NorEraseParameterSector(unsigned int Addr);
        unsigned int NorGetManufacturerId(void);
        unsigned int NorGetDeviceId(void);
    
        ...etc...
    
        private:
        ...etc...
    
        /*
         * Private member variable GateKeeperNorFlash is declared static so that there will only be one of these
         * regardless of how many instances of a nor_s25fl032p there may be.  The DCC::CriticalSection object
         * implements the semaphore creation, destruction, locking and locking needed to provide a way to
         * 'acquire' and release the SPI bus to the flash.  In order to acquire, the public functions first
         * calls GateKeeperNorFlash.Lock.  Before exiting, the function calls GateKeeperNorFlash.Unlock
         */
        static DCC::CriticalSection GateKeeperNorFlash;
    };

    An example of one of the member functions that needs to share the resource that calls the Lock and Unlock functions

    unsigned int nor_s25fl032p::NorReadData(unsigned int Addr,unsigned int size,unsigned char *data)
    {
    	...etc...
    
        GateKeeperNorFlash.Lock();
        McSPITransfer(txBuffer,NULL,5,SPI_HOLD_ACTIVE);
        McSPITransfer(NULL,data,size,SPI_HOLD_CLR);
        GateKeeperNorFlash.Unlock();
    
        return(0);
    }
    

    And lastly usage of this API to perform a read of data from the NOR Flash which invokes a function of an object of this new class.

    NorFlashDevice.NorReadData((unsigned int)addr,sizeof(r),(unsigned char*)&r);

    Since the Safe_Semaphore_pend prevents any calls to Semaphore_pend that do not come from a task thread and yet the assertion being flagged is coming from a call to Semaphore_pend it suggests that the semaphore_pend is coming from something else.  But what?

    Kevin Jennings

  • Kevin,

    Can you share your project's .cfg file?

    Are you using the TI or GNU tools?

    If you're using the TI tools, the Ti runtime support library requires a "lock" to provide thread protection for certain APIs.

    By default, SYS/BIOS leverages GateMutex for this lock function when SYS/BIOS is used. GateMutex is implemented using an internal Semaphore object.

    So, when certain RTS library APIs are called (printf(), rand(), various IO funcs, etc) Semaphore_pend() is invoked.

    Alan

  • Alan,

    Using TI tools.  I didn't see a way to upload a file to here so the .cfg is copy/pasted below.

    Kevin

    /*
     *  ======== package/cfg/app_pea8fnv.cfg ========
     *  This generated configuration script runs the user's configuration script
     *  the context of a specific target and platform in order to generate
     *  all the files necessary to create an executable; e.g., linker command
     *  files, static C/C++ data structures, etc.
     */
    
    /*
     *  ======== _applyChanges ========
     *  Changes that bring the build target to the state at the end of the build
     *  model
     */
    function _applyChanges(obj, chgObj) {
        var wasSealed = false;
        if (obj.$sealed) {
            wasSealed = true;
            obj.$unseal();
        }
        for (var prop in chgObj) {
            if (typeof obj[prop] == 'object' && obj[prop] != undefined) {
                if ("$category" in obj[prop] && obj[prop].$category == "Vector") {
                   obj[prop].length = chgObj[prop].length;
                   for (var i = 0; i < chgObj[prop].length; i++) {
                       if (obj[prop].length < i + 1) {
                           obj[prop].length++;
                       }
                       obj[prop][i] = chgObj[prop][i];
                   }
                }
                else {
            	_applyChanges(obj[prop], chgObj[prop]);
                }
    	}
    	else {
                obj[prop] = chgObj[prop];
    	}
        }
        if (wasSealed) {
            obj.$seal();
        }
    }
    
    /*
     *  ======== _runescape ========
     *  Recursive unescape to decode serialized strings
     */
    function _runescape(obj) {
        for (var i in obj) {
    	if (obj[i] != null) {
    	    if (typeof obj[i] == 'string') {
    		obj[i] = unescape(obj[i]);
    	    }
    	    else if (typeof obj[i] == 'object') {
    		_runescape(obj[i]);
    	    }
    	}
        }
    }
    
    /*
     *  ======== _getPlatCfg ========
     */
    function _getPlatCfg() {
        var tmp = {};
        _runescape(tmp);
        return (tmp);
    }
    /*
     *  ======== _cfginit ========
     */
    function _cfginit() {
        xdc.loadPackage('xdc.services.intern.cmd');
        var prog = xdc.om['xdc.cfg.Program'];
    
        /* initialize prog attrs from build model */
        var build = {
            profile: "release",
            cfgScript: "C%3A/SourceSafe/SmartSourceSE/ti/Smart_HID_SingleFeed/app.cfg",
            cfgHome: "configPkg",
            cfgArgs: "null",
            cfgArgsEncoded: true,
            releases: {
                0: {
                    name: "configPkg",
                    attrs: {
                        prefix: "",
                        label: "default"
                    },
                    otherFiles: {},
                    excludeDirs: {}
                }
            },
            prelink: false
        };
        _runescape(build);
        build.cfgArgs = null;
        build.target = xdc.module("ti.targets.arm.elf.A8Fnv");
        var targChange = {
            platforms: [
                "ti.platforms.evmDM8168"
            ],
            version: "ti.targets.arm.elf.A8Fnv%7B1%2C0%2C18.1%2C3",
            extensions: {
                ".sea8fnve": {
                    suf: ".sea8fnve",
                    typ: "asm"
                },
                ".sea8fnv": {
                    suf: ".sea8fnv",
                    typ: "asm"
                },
                ".sv7A": {
                    suf: ".sv7A",
                    typ: "asm"
                },
                ".sv6": {
                    suf: ".sv6",
                    typ: "asm"
                },
                ".sv5T": {
                    suf: ".sv5T",
                    typ: "asm"
                },
                ".sv4T": {
                    suf: ".sv4T",
                    typ: "asm"
                },
                ".s470": {
                    suf: ".s470",
                    typ: "asm"
                }
            },
            rootDir: "C%3A/ti/ccsv7/tools/compiler/ti-cgt-arm_18.1.3.LTS",
            ccOpts: {
                prefix: "-g%20--optimize_with_debug%20-qq%20-pdsw225"
            },
            rawVersion: "18.1.3"
        };
        _runescape(targChange);
        _applyChanges(build.target, targChange);
    
        prog.build = build;
    
        prog.name = "app.xea8fnv";
        prog.cfgBase = "package/cfg/app_pea8fnv";
    
        prog.endian = prog.build.target.model.endian;
    
        /* use the platform package's Platform module */
        var Platform = xdc.useModule("ti.platforms.evmAM3359.Platform");
        var platParams = _getPlatCfg();
        var invalidParams = [];
        for (var prop in platParams) {
            if (!(prop in Platform.PARAMS)) {
                delete platParams[prop];
                invalidParams.push(prop);
            }
        }
        prog.platformName = "ti.platforms.evmAM3359";
        prog.platform = Platform.create("", platParams);
        for (var i = 0; i < invalidParams.length; i++) {
            Platform.$logWarning("The parameter '" + invalidParams[i] + "' is " +
                "passed to this platform instance through Build.platformTable, " +
                "but the instance does not have a configuration parameter with " +
                "that name.", prog.platform, "");
        }
    
        /* record the executable's package name */
        prog.buildPackage = "configPkg";
    
        /* record build-model information required during config generation */
        prog.$$bind("$$isasm", 0);
        prog.$$bind("$$isrom", 0);
        prog.$$bind("$$gentab", [
        ]);
    
        /* bind prog to an appropriate execution context */
        prog.cpu = prog.platform.getExeContext(prog);
    
        /* import the target's run-time support pkg */
        xdc.loadPackage("ti.targets.arm.rtsarm");
    }
    
    /* function to import the cfg script's package */
    function _userscript(script) {
        var home;
        var spath;
        home = xdc.loadPackage("configPkg");
    
        xdc.om.$$bind('$homepkg', home);
    
        var cfgScript = "C:/SourceSafe/SmartSourceSE/ti/Smart_HID_SingleFeed/app.cfg";
        if (!script) {
            utils.loadCapsule(cfgScript, false, spath);
        }
        else {
            /* set up the same environment that loadCapsule would */
            var $saveCsd = utils.csd;
            var $cfgFile = utils.findFile(cfgScript, spath);
            var cname = cfgScript;
            if ($cfgFile) {
                $cfgFile = java.io.File($cfgFile);
                utils.csd = $cfgFile.getParentFile().getCanonicalPath();
                cname = "" + $cfgFile.getCanonicalPath();
            }
    
            /* create the capsule object */
            var cap = {
                prototype:  utils.global,
                $path:      cname,
                $private:   {path: cname},
                $capsule:   undefined,      /* set to cap below */
            };
    
            /* 'this.$capsule' is always cap object */
            cap.$capsule = cap;
    
            /* save the capsule object */
            utils.$$capmap[cname] = cap;
    
            try {
                var cx =
                    Packages.org.mozilla.javascript.Context.getCurrentContext();
                var rdr = new
                    java.io.BufferedReader(new java.io.StringReader(script));
                Packages.config.Shell.evaluateLoad(cx, cap, rdr, cname, 1);
            }
            finally {
                rdr.close();
                utils.csd = $saveCsd;
            }
        }
    }
    
    function _postinit() {
        var cout = null;
    
        var Program = xdc.om['xdc.cfg.Program'];
        /* get the exec command for this executable */
        if (Program.execCmd == null) {
            Program.execCmd = Program.platform.getExecCmd(Program,
                xdc.om["ti.platforms.evmAM3359"].packageBase);
        }
        cout = "define EXEC." + Program.name + '\n\n';
        cout += Program.execCmd;
        cout += "\nendef\n\n";
    
        /* if SourceDir generates a makefile, we need to run it */
        _genSourceDirMak("package/cfg/app_pea8fnv", "app.pea8fnv");
    
        utils.genDep("package/cfg/app_pea8fnv", "configPkg", utils.loadedFiles, cout, null);
    }
    
    function _genSourceDirMak(cfgBase, cfgName)
    {
        var SourceDir = xdc.om['xdc.cfg.SourceDir'];
    
        if (SourceDir && SourceDir.$instances.length > 0) {
            /* construct rule to run SourceDir generated makefile */
            var make = "\t$(MAKE) -f "
                + SourceDir.outputDir + "/" + SourceDir.makefileName;
    
            /* this file is included by package.mak (if it exists) */
            var file = new java.io.File(cfgBase + ".cfg.mak");
            file["delete"]();
            var out = new java.io.BufferedWriter(new java.io.FileWriter(file));
    
            /* add rules to run SourceDir generated makefile */
            out.write("# invoke SourceDir generated makefile for " + cfgName
                + "\n" + cfgName + ": .libraries," + cfgName
                + "\n.libraries," + cfgName + ": " + cfgBase + ".xdl\n"
                + make + "\n\n"
                + "clean::\n" + make + " clean\n\n"
            );
            out.close();
            out = null;
        }
    }

  • I was able to verify using printf to a serial port on the AM3352 that, at the time of the assert being triggered, the Semaphore_pend is being called with a thread type of main (i.e. BIOS_getThreadType() == BIOS_ThreadType_Main).

    The printf statement was added inside Semaphore_pend immediately before the assert statement.  To Alan's point "So, when certain RTS library APIs are called (printf(), rand(), various IO funcs, etc) Semaphore_pend() is invoked.", since the printf statement itself did not trigger an assert the semaphore being waited on must be coming from somewhere else.  Still looking for the semaphore.

    Kevin Jennings

  • Adding additional printf statements to Semaphore.c immediately before the assert statement shows :

    - The semaphore handle is not one that was issued by our code

    - The task handle is not a task that was spawned by our code either.

    Below is the list of tasks that were running prior to, 'during' and after the call to Bios_Exit (where 'after the call' refers to inside Semaphore.c right before the assert).

    Any ideas on where this semaphore might be coming from?  Searching the .map file for something close to the task handle 800DDAD8 which is the output of task_self() at the time of the assert hasn't yielded anything yet.  Where should I look to find where that task is coming from (and/or the semaphore that is being used).

    *** This is from before the call to BIOS_exit ***
    Task_self()=80096838
    SYS/BIOS TASK LIST
      Static Tasks
            Handle                       Task name  Pri     Mode Stk Size Stk Used
        0x8085059c:        {unknown-instance-name}    0    Ready    65536      124
      Dynamic Tasks
            Handle                       Task name  Pri     Mode Stk Size Stk Used
    *   0x80096838:        {unknown-instance-name}   10  Running    65536    14336
        0x800a68a0:        {unknown-instance-name}    1    Ready    32768      560
        0x800af900:        {unknown-instance-name}    8    Ready    32768      664
        0x800ba950:        {unknown-instance-name}   11  Blocked    32768     3080
        0x800c29b8:        {unknown-instance-name}    4  Blocked    32768     1408
        0x800cba18:        {unknown-instance-name}    6    Ready    32768     5192
        0x800d4a78:        {unknown-instance-name}    5  Blocked    32768      388
        0x800ddad8:        {unknown-instance-name}   14  Blocked   262144      544
        0x8011eb38:        {unknown-instance-name}    9  Blocked    65536    33428
        0x8012fb98:        {unknown-instance-name}    7  Blocked    65536     1844
        0x80140bf8:        {unknown-instance-name}    7  Blocked    65536     1932
        0x80151c58:        {unknown-instance-name}    7  Blocked    32768      524
        0x80159cc0:        {unknown-instance-name}    7  Blocked    32768      412
        0x80163d18:        {unknown-instance-name}    9  Blocked    32768      644
        0x8016cd78:        {unknown-instance-name}    8  Blocked    32768      420
    Total stack usage: 65500
    
    *** Here we call BIOS_exit
    About to call BIOS_exit(0).  Line #=1294
    
    *** This is within the BIOS_exit routine itself
    BIOS_exit line 135.  BIOS_module=80850A90, BIOS_module->exitFunc=8080DCD8, stat=0
    BIOS_getThreadType() = 3, BIOS_getThreadType() == BIOS_ThreadType_Main=1
    
    *** These final printf are done in Semaphore.c immediately before the assert statement
    CurrentTime=43163772
    Sem=8011DB68
    sem->event=00000000
    sem->eventId=1
    sem->count=0
    sem->mode=0
    Task_self()=800DDAD8

  • Besides BIOS, what other libraries are you using?

    0x800ddad8:        {unknown-instance-name}   14  Blocked   262144      544

    That is a huge stack and a very high priority.

    Are you able to share your CCS project? In the thread so far I don't see much info regarding the application. Consequently I have no idea where the other Task and Semaphore objects are coming from.

    Alan

  • Alan,

    The 'Runtime support library' in Properties->General is set to '<automatic>'.  In addition to SYS/BIOS 6.73.1.01, I'm using XDC 3.50.8.24.

    Is there a place to securely upload files here?

    Kevin

  • Anything you upload to the forum will be visible to the public.

    I've sent you a private communication request.

    Alan

  • Alan,

    Your previous post referencing task at 0x800ddad8 is a good clue.  I'm not sure how I overlooked that address in the task list and got to thinking that the task running at assert time was not in our code.  That task is one that gets woken up at a periodic rate and is fairly short.  I'm going to review that code and see if I can dig out another clue.

    The task priority is high for a reason, not sure about why the stack size is that big but one thing at a time.

    Kevin Jennings

  • Alan,

    I checked the task that the assert is tagging and modified it to prevent it from calling anything before returning once it receives a message to shut down.  This task is a periodic task that wakes up every 0.5 ms.  This change had no effect, the assert still happens.  I received your friend request and will send you the project once I get your link.

    Kevin Jennings

  • Alan,

    Got it!  There is a function of ours that was registered to run at system exit via the SYS/BIOS System_atexit function.  Inside that function, our code would read a flash memory to load the next program that is to be executed after the system reboots itself.  However, access to the flash memory is semaphore controlled.  Attempting to read the flash requires waiting for the semaphore to release but this is happening within the context of BIOS_exit() still attempting to exit but after it has changed the thread type to 'main' thereby causing the assert.  The semaphore control is recently added code to address a sharing problem with the flash so it makes sense why this suddenly popped up as a 'new' problem.  The only thing unexplained at present is why the two other applications that I have did not exhibit this problem.  In any case, I've gotten to the bottom of this.  The solution of course is simply to move the flash read code to happen right before the call to BIOS_exit rather from within the handler function that BIOS_exit calls.

    Thanks for your help on this problem.

    Kevin Jennings

  • That's great news!

    You're welcome, of course!

    Alan