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.

SPI_open crashes

Other Parts Discussed in Thread: SYSBIOS, EK-TM4C1294XL

CCS: Version: 6.1.2.00015
Tiva Eval Board: EK_TM4C1294XL
TivaWare_C_Series-2.1.2.111
xdctools_3_31_01_33_core

Wanting to use a SPI serial flash chip in our project.  Using an eval board as a testbed prior to having
our initial prototype built.

I put the serial flash chip on a protoboard and connected it to the booster pack #2 bus.  The pins were
setup to use the second SPI channel (Board_SPI1) which corresponds to SSI #3 on the CPU.

First I took the 'SPI Loopback' example and modified it so I could demonstrate that I could communicate
with the device.  I both wrote data and read it back.  Well and good.

Then I wanted to take my learnings and add SPI communication to my already existing much larger application
also running on the same eval board.

I carefully examined the .cfg file for the SPI Looback example and the only thing I could find that seemed
to be different was the line:
    TIRTOS.useSPI = true;

So I added this line to my config file and included "ti/drivers/SPI.h" in my source file.

Executing the following sequence:
  SPI_Params_init(&spi_params);
  spi_params.bitRate = 10000000;                                       // 10 megabit data rate
  spi_params.frameFormat = SPI_POL1_PHA1;               // This is the mode that works with the serial flash
  spi_handle = SPI_open(Board_SPI1, &spi_params);

The program crashes during the SPI_open function.  According to the console output at the time of
the crash, it is aborting on the first assembly line of function "SSIIntDisable()" called from
"SPITivaDMA_open()".

I have verified that the working SPI Loopback derivation goes through the same line without failing.

Is there some other change I need to make to the project to enable SSI/SPI operation?

Below is the .cfg file (header comments excluded) on the larger application that fails to open the SPI channel:

var Defaults = xdc.useModule('xdc.runtime.Defaults');
var Diags = xdc.useModule('xdc.runtime.Diags');
var Error = xdc.useModule('xdc.runtime.Error');
var Log = xdc.useModule('xdc.runtime.Log');
var LoggerBuf = xdc.useModule('xdc.runtime.LoggerBuf');
var Memory = xdc.useModule('xdc.runtime.Memory');
var SysMin = xdc.useModule('xdc.runtime.SysMin');
var System = xdc.useModule('xdc.runtime.System');
var Timestamp = xdc.useModule('xdc.runtime.Timestamp');

var BIOS = xdc.useModule('ti.sysbios.BIOS');
var Hwi = xdc.useModule('ti.sysbios.hal.Hwi');
var Idle = xdc.useModule('ti.sysbios.knl.Idle');
var Task = xdc.useModule('ti.sysbios.knl.Task');
var Clock = xdc.useModule('ti.sysbios.knl.Clock');
var Timer = xdc.useModule('ti.sysbios.family.arm.lm4.Timer');
var Mailbox = xdc.useModule('ti.sysbios.knl.Mailbox');
var Semaphore = xdc.useModule('ti.sysbios.knl.Semaphore');
var Startup = xdc.useModule('xdc.runtime.Startup');
var Seconds = xdc.useModule('ti.sysbios.hal.Seconds');

/*
 * Uncomment this line to globally disable Asserts.
 * All modules inherit the default from the 'Defaults' module.  You
 * can override these defaults on a per-module basis using Module.common$.
 * Disabling Asserts will save code space and improve runtime performance.
Defaults.common$.diags_ASSERT = Diags.ALWAYS_OFF;
 */

/*
 * Uncomment this line to keep module names from being loaded on the target.
 * The module name strings are placed in the .const section. Setting this
 * parameter to false will save space in the .const section.  Error and
 * Assert messages will contain an "unknown module" prefix instead
 * of the actual module name.
Defaults.common$.namedModule = false;
 */

/*
 * Minimize exit handler array in System.  The System module includes
 * an array of functions that are registered with System_atexit() to be
 * called by System_exit().
 */
System.maxAtexitHandlers = 4;

/*
 * Uncomment this line to disable the Error print function.
 * We lose error information when this is disabled since the errors are
 * not printed.  Disabling the raiseHook will save some code space if
 * your app is not using System_printf() since the Error_print() function
 * calls System_printf().
Error.raiseHook = null;
 */

/*
 * Uncomment this line to keep Error, Assert, and Log strings from being
 * loaded on the target.  These strings are placed in the .const section.
 * Setting this parameter to false will save space in the .const section.
 * Error, Assert and Log message will print raw ids and args instead of
 * a formatted message.
Text.isLoaded = false;
 */

/*
 * Uncomment this line to disable the output of characters by SysMin
 * when the program exits.  SysMin writes characters to a circular buffer.
 * This buffer can be viewed using the SysMin Output view in ROV.
SysMin.flushAtExit = false;
 */

/*
 * The BIOS module will create the default heap for the system.
 * Specify the size of this default heap.
 */
BIOS.heapSize = 65536;

/*
 * Build a custom SYS/BIOS library from sources.
 */
BIOS.libType = BIOS.LibType_NonInstrumented;

/*
 *  Program.stack is ignored with IAR. Use the project options in
 *  IAR Embedded Workbench to alter the system stack size.
 */
if (!Program.build.target.$name.match(/iar/)) {
    /*
     *  Reducing the system stack size (used by ISRs and Swis) to reduce
     *  RAM usage.
     */
Program.stack = 2048;
}

/* Enable Semihosting for GNU targets to print to CCS console */
if (Program.build.target.$name.match(/gnu/)) {
}

/* Circular buffer size for System_printf() */
SysMin.bufSize = 0x400;

/* ================ Driver configuration ================ */
var TIRTOS = xdc.useModule('ti.tirtos.TIRTOS');
TIRTOS.useGPIO = true;
TIRTOS.useSPI = true;

var task0Params = new Task.Params();
task0Params.instance.name = "UartTaskHandle";
task0Params.arg0 = 0;
task0Params.priority = 8;
task0Params.stackSize = 1024;
Program.global.UartTaskHandle = Task.create("&Uart_Task", task0Params);

System.SupportProxy = SysMin;

/*
var log0Params = new LoggerBuf.Params();
log0Params.instance.name = "systemLog";
log0Params.exitFlush = true;
log0Params.numEntries = 16;
Program.global.systemLog = LoggerBuf.create(log0Params);

Defaults.common$.logger = Program.global.systemLog;
Defaults.common$.diags_INFO = Diags.ALWAYS_ON;
Defaults.common$.diags_USER1 = Diags.ALWAYS_OFF;
*/
Idle.addFunc("&clockIdle");


var timer0Params = new Timer.Params();
timer0Params.instance.name = "SensorTimerHandle";
timer0Params.period = 30719;
timer0Params.startMode = xdc.module("ti.sysbios.interfaces.ITimer").StartMode_USER;
timer0Params.runMode = xdc.module("ti.sysbios.interfaces.ITimer").RunMode_CONTINUOUS;
timer0Params.altclk = false;
timer0Params.periodType = xdc.module("ti.sysbios.interfaces.ITimer").PeriodType_COUNTS;
Program.global.SensorTimerHandle = Timer.create(null, "&ReadLastSensorConversion", timer0Params);
Task.idleTaskStackSize = 1024;
var task1Params = new Task.Params();
task1Params.instance.name = "MainLoopTaskHandle";
task1Params.stackSize = 16384;
Program.global.MainLoopTaskHandle = Task.create("&main_loop_task", task1Params);
var clock0Params = new Clock.Params();
clock0Params.instance.name = "KernelClock";
clock0Params.startFlag = true;
clock0Params.period = 100;
Program.global.KernelClock = Clock.create("&KernelTick", 1000, clock0Params);
Clock.swiPriority = 8;
var mailbox0Params = new Mailbox.Params();
mailbox0Params.instance.name = "KickBox";
Program.global.KickBox = Mailbox.create(8, 50, mailbox0Params);
Clock.tickPeriod = 1000;
var task2Params = new Task.Params();
task2Params.instance.name = "DiscriminatorTaskHandle";
task2Params.priority = 13;
task2Params.stackSize = 4096;
Program.global.DiscriminatorTaskHandle = Task.create("&DiscriminatorTask", task2Params);
var task3Params = new Task.Params();
task3Params.instance.name = "KickTaskHandle";
task3Params.priority = 14;
task3Params.stackSize = 1024;
Program.global.KickTaskHandle = Task.create("&KickTask", task3Params);
var timer1Params = new Timer.Params();
timer1Params.instance.name = "StartKickHandle";
timer1Params.runMode = xdc.module("ti.sysbios.interfaces.ITimer").RunMode_CONTINUOUS;
timer1Params.startMode = xdc.module("ti.sysbios.interfaces.ITimer").StartMode_USER;
timer1Params.period = 100;
Program.global.StartKickHandle = Timer.create(1, "&StartKick", timer1Params);
var timer2Params = new Timer.Params();
timer2Params.instance.name = "StopKickHandle";
timer2Params.startMode = xdc.module("ti.sysbios.interfaces.ITimer").StartMode_USER;
timer2Params.runMode = xdc.module("ti.sysbios.interfaces.ITimer").RunMode_CONTINUOUS;
timer2Params.period = 100;
timer2Params.altclk = false;
Program.global.StopKickHandle = Timer.create(2, "&StopKick", timer2Params);
var task4Params = new Task.Params();
task4Params.instance.name = "SolenoidPulseOffTaskHandle";
task4Params.priority = 6;
task4Params.stackSize = 1024;
Program.global.SolenoidPulseOffTaskHandle = Task.create("&SolenoidPulseOffTask", task4Params);
var semaphore0Params = new Semaphore.Params();
semaphore0Params.instance.name = "KickCompleteSemHandle";
semaphore0Params.mode = Semaphore.Mode_BINARY;
Program.global.KickCompleteSemHandle = Semaphore.create(1, semaphore0Params);
var semaphore1Params = new Semaphore.Params();
semaphore1Params.instance.name = "SolenoidPulseOffSemHandle";
semaphore1Params.mode = Semaphore.Mode_BINARY;
Program.global.SolenoidPulseOffSemHandle = Semaphore.create(null, semaphore1Params);
var clock1Params = new Clock.Params();
clock1Params.instance.name = "DiscriminatorClockHandle";
clock1Params.period = 5;
Program.global.DiscriminatorClockHandle = Clock.create("&DiscriminatorClockTick", 5, clock1Params);
var semaphore2Params = new Semaphore.Params();
semaphore2Params.instance.name = "RunDiscriminatorTask";
semaphore2Params.mode = Semaphore.Mode_BINARY;
Program.global.RunDiscriminatorTask = Semaphore.create(null, semaphore2Params);
var clock2Params = new Clock.Params();
clock2Params.instance.name = "RailRakeMonitorHandle";
clock2Params.period = 1;
Program.global.RailRakeMonitorHandle = Clock.create("&RailRakeMonitor", 1, clock2Params);
BIOS.customCCOpts = "--endian=little -mv7M4 --abi=eabi --float_support=fpv4spd16 -q -ms --opt_for_speed=2  --program_level_compile -o3 -g --optimize_with_debug";
BIOS.logsEnabled = false;
BIOS.assertsEnabled = false;
Defaults.common$.diags_INTERNAL = Diags.ALWAYS_OFF;
Defaults.common$.diags_ASSERT = Diags.ALWAYS_OFF;
Hwi.dispatcherIrpTrackingSupport = true;
var task5Params = new Task.Params();
task5Params.instance.name = "UserButtonTaskHandle";
task5Params.priority = 4;
task5Params.stackSize = 1024;
Program.global.UserButtonTaskHandle = Task.create("&UserButtonTask", task5Params);
var task6Params = new Task.Params();
task6Params.instance.name = "DeadmanTaskHandle";
task6Params.priority = 9;
task6Params.stackSize = 1024;
Program.global.DeadmanTaskHandle = Task.create("&DeadmanTask", task6Params);
var task7Params = new Task.Params();
task7Params.instance.name = "SerialFlashTaskHandle";
task7Params.priority = 2;
Program.global.SerialFlashTaskHandle = Task.create("&SerialFlashTask", task7Params);

  • Hi Greg,

    Which TI-RTOS version are you using?

    The useSPI only brings in the handling for the SPI in ROV. It does not impact the target code at all. In 2.16, we are actually removing this from the .cfg.

    Where are you calling SPI_open from (e.g. main or a task)? What are the fields in the hwAttrs (most importantly baseAddr) when it crashes?

    Have you seen this?

    Todd

  • RTOS version: tirtos_tivac_2_14_04_31

    SPI_open called from task context.

    Fields in hwAttrs: since SPI_open never returns (it crashes as indicated in previous post), I don't have an address for the handle it is creating.
    Is there some way I can find this out from the debugger at the point it is calling SSIIntDisable(). Note: it doesn't find source for the file, I can only see it in Disassembly mode.

    As far as the example: As indicated, I got it working fine with the SPI Loopback example which succeeds on the SPI_open call. I need to know why the open call is failing. Once that succeeds and SPI_transfer commands work, I am good to go.
  • In CCS, when you step into a file that it does not know the location, you can browse to the file. This occurs because we build the libraries on Linux servers, so the path in the debug info is wrong.

    SSIntDisable will be in <tirtos_install_dir>/products/<TivaWare_ver>/driverlib.

    You can step into SPI.c and then SPITivaDMA.c (they are in <tirtos_install_dir>/packages/ti/drivers and <tirtos_install_dir>/packages/ti/drivers/spi in your version).

    I'm thinking the base address is garbage.

    Todd
  • Thanks for the hint on finding the file.

    The line it's about to execute that causes the crash:
        HWREG(ui32Base + SSI_O_IM) &= ~(ui32IntFlags);

    Here's the contents of the base register:
    Name : ui32Base
        Default:1073786880
        Hex:0x4000B000
        Decimal:1073786880
        Octal:010000130000
        Binary:01000000000000001011000000000000

    Disassembly of the instruction about to be executed:
    6942                ldr        r2, [r0, #0x14]

    Registers involved:
      R0    0x4000B000    General Purpose Register 0 [Core]    
      R2    0x0002F345    General Purpose Register 2 [Core]    

    If I perform an 'Assembly step into' It immediately branches to:
      ti_sysbios_family_arm_m3_Hwi_excHandlerAsm__I:

    ------

    Switching to the program based on the SPI Loopback example that successfully opens and communicates
    with the serial flash:

    If I set a breakpoint at the same instruction, the base register (R0) has the same value, but R2 contains:
      R2    0x00001D05    General Purpose Register 2 [Core]    

    Does this help in figuring this out?  Is there something wrong in my project settings?

    My thinking is that 'SSI_O_IM' ought to be a constant?? Should it be different between the two?
    The debugger is unable to find the definition of SSI_O_IM ('F3' does nothing).

    I would be happy to set breakpoints at other places and report values to you as needed.

  • Greg Winters45 said:
    If I perform an 'Assembly step into' It immediately branches to:
      ti_sysbios_family_arm_m3_Hwi_excHandlerAsm__I:

    Are you seeing exception information on the console?

    Does ROV tell you anything about the exception?

    Please provide all exception information that you can find.

    Greg Winters45 said:
    Switching to the program based on the SPI Loopback example that successfully opens and communicates
    with the serial flash:

    If I set a breakpoint at the same instruction, the base register (R0) has the same value, but R2 contains:
      R2    0x00001D05    General Purpose Register 2 [Core]    

    Does this help in figuring this out?  Is there something wrong in my project settings?

    Unfortunately this doesn't help much.  R2 is the destination register of the asm instruction you showed:
        Disassembly of the instruction about to be executed:
        6942                ldr        r2, [r0, #0x14]
    and since it's writing into R2 then that means it doesn't care about the contents of R2 at this time.

    Greg Winters45 said:
    My thinking is that 'SSI_O_IM' ought to be a constant?? Should it be different between the two?
    The debugger is unable to find the definition of SSI_O_IM ('F3' does nothing).

    That's defined in the TivaWare 'inc' subdirectory, in hw_ssi.h:
        hw_ssi.h:#define SSI_O_IM                0x00000014  // SSI Interrupt Mask

    This explains the 0x14 in the ldr asm instruction, and we can see that it is a constant and should not be different.

    I haven't really given any new insight except to answer your questions, but I'm wondering if there are some MPU settings that are different between the working and non-working code.  If you can read from 0x4000B000 in one but not in the other, that's the only hardware that I know of that can make a difference in that respect

    Regards,

    - Rob

  • Yeah, I figured out the destination register thing after I replied and went  "DOH!"

    Here's the exception info:

    ti.sysbios.family.arm.m3.Hwi: line 1087: E_hardFault: FORCED
    ti.sysbios.family.arm.m3.Hwi: line 1164: E_busFault: PRECISERR: Immediate Bus Fault, exact addr known, address: 4000b014
    Exception occurred in background thread at PC = 0x00058662.
    Core 0: Exception occurred in ThreadType_Task.
    Task name: {unknown-instance-name}, handle: 0x2002f1f8.
    Task stack base: 0x20012b60.
    Task stack size: 0x800.
    R0 = 0x4000b000  R8  = 0xffffffff
    R1 = 0x0000000f  R9  = 0x00000001
    R2 = 0x0002f379  R10 = 0xffffffff
    R3 = 0x00068844  R11 = 0xffffffff
    R4 = 0x2001d628  R12 = 0x200122f0
    R5 = 0x00068850  SP(R13) = 0x200132b0
    R6 = 0x000669dc  LR(R14) = 0x0002f3c3
    R7 = 0x2001330c  PC(R15) = 0x00058662
    PSR = 0x01000000
    ICSR = 0x00433803
    MMFSR = 0x00
    BFSR = 0x82
    UFSR = 0x0000
    HFSR = 0x40000000
    DFSR = 0x0000000a
    MMAR = 0x4000b014
    BFAR = 0x4000b014
    AFSR = 0x00000000
    Terminating execution...

  • Because we are working with a C++ code base (porting code from another processor), when I brought
    in my other sources, I added them to an example project that I had been experimenting with.

    The example project was 'Bigtime' which is the only C++ example that was present for our eval board.

    Since we have the case where the 'SPI Loopback' example works with no issues, just now I tried the following:
      I imported a fresh copy of the Bigtime example.
      I added the line 'TIRTOS.useSPI = true;' to the config file to enable SPI.
      I added a new task to the configuration that calls the function 'spiTask'.
      I added the following lines to the bigtime.cpp file:
        #include <ti/drivers/SPI.h>

        extern "C" void spiTask(UArg arg1, UArg arg2)
          {
          SPI_Params spi_params;

          SPI_Params_init(&spi_params);
          SPI_open(Board_SPI1, &spi_params);
          }

    I ran this and it crashed the same way on the SPI_open call.

    It is not necessary to have a SPI chip connected to the board in order for the open call to succeed.
    No interaction with the device takes place during an open call.

    So I have been able to duplicate the issue with minimal code that you should be able to test if you
    load it onto an 'EK-TM4C1294XL'.

    Is it reasonable to ask you to make this test and try to figure it out?

  • Okay, I feel silly and ashamed...

    The above test fails for the exact same reason that it fails in my application.

    I neglected to call 'Board_initSPI()'

    Sorry for all the trouble.

    Thanks,
    Greg Winters
  • Been there. Done that:)

    We actually added a check in the driver open calls to make sure the index is ok (and return NULL if it not). We did this in a 2.15 release. Unfortunately the check was not robust enough to catch the forgetting to call the driver init call. We just missed the boat for the 2.16.00 release this week (or early next week). We'll fix the check for the next one though.

    Todd
  • Hello Greg,

    I encounter the same issue on a project of mine.

    In the main context :
    1. create some tasks

    2. call Board_initSPI()
    3. call Bios_Start()

    Info : I manage a timer to tick my kernel abstraction layer to trig periodically my tasks

    When I receive a byte on UART (USB emulated), I call my application that :
    - decodes the request
    - call spi to send a byte

    But it crashes as soon as I try SPI_open. It works just fine with the spiloopback example but as soon as I put it in my project, it does not work.

    DMA is initialized, SPI_Config structure is filled perfectly but I still get a bus fault.

    Could it be because the callback is called in the usbcd interrupt and nested interrupts are a bit messy ?

    Thank you for your reply
  • Not the expert, but a couple things I can think of:

    * Perhaps the open should be called in a task context rather than interrupt context.

    * I do know that a SPI call has to be in task context unless you configure it to use a callback rather than blocking to wait until complete.  You can't block while in interrupt context.

    Hope this helps.  If it's not the answer, hopefully the TI experts will get it right.

    - Cheers, Greg

  • Thank you for your answer,

    1st problem : I moved Spi_open to main context and it worked. I get the handle ! Here is the call stack (lighted on purpose)

    main()

    - Board_initSPI()

    -- SPI_open()

    2nd problem : it seems SSIDataGetNonBlocking cannot be called from task context. I get the bus fault whereas it is in a task context, even using DMA.

    Decoded exception,

    Decoded,Hard Fault: FORCED: BUSFAULT: PRECISERR.Data Access Error. Address = 0x4000800c

    Exception context,

    $addr,0x20000ff0

    $type,ti.sysbios.family.arm.m3.Hwi.ExcContext

    threadType,ti.sysbios.BIOS.ThreadType_Task

    threadHandle,0x20001d34

    threadStack,0x20000f00

    threadStackSize,760

    r0,0x40008000

    r1,0x200010cc

    r2,0x3ff

    r3,0xa

    r4,0xdd84

    r5,0x20001c00

    r6,0x20001114

    r7,0x2000260c

    r8,0xffffffff

    r9,0xffffffff

    r10,0xffffffff

    r11,0xffffffff

    r12,0x0

    sp,0x200010c8

    lr,0x4b5f

    pc,0xbaf2

    psr,0x21000000

    ICSR,0x43c803

    MMFSR,0x0

    BFSR,0x82

    UFSR,0x0

    HFSR,0x40000000

    DFSR,0xb

    MMAR,0x4000800c

    BFAR,0x4000800c

    AFSR,0x0

    Exception call stack,

    0 SSIDataGetNonBlocking(unsigned int, unsigned int *) at ssi.c:718,PC = 0x0000BAF2 FP = 0x200010C8

    1 SPITivaDMA_configDMA(struct SPI_Config *, struct SPI_Transaction *) at SPITivaDMA.c:247,PC = 0x00004B5E FP = 0x200010C8

    2 SPITivaDMA_transfer(struct SPI_Config *, struct SPI_Transaction *) at SPITivaDMA.c:520,PC = 0x00007E04 FP = 0x200010E8

    3 SPI_transfer(struct SPI_Config *, struct SPI_Transaction *) at SPI.c:131,PC = 0x0000BFCA FP = 0x20001100

    4 HWI_bSPIWrite(unsigned char *, unsigned char *, unsigned char) at HWISpi.c:102,PC = 0x000093EA FP = 0x20001108

    5 vidSsiSendReceive(unsigned char, unsigned char * *) at SSHell_m.c:210,PC = 0x00001618 FP = 0x20001130

    6 vidManageReceiptStm(unsigned short, void *) at SSHell_engine.c:1332,PC = 0x0000180C FP = 0x200011B0

    7 vidTaskManage(void *) at kal002.c:1288,PC = 0x00003292 FP = 0x200011C8

    8 ti_sysbios_knl_Task_exit__E() at Task.c:354,PC = 0x000075A8 FP = 0x200011E0

    I snaped and went through spma043 to know what is happening and fault is coming from 0x4000800c ==> SSI0: 0x4000.8000, 0x00C SSISR RO SSI Status
    The error comes from polling the status register in the task context ? Why would not I be able to poll this register ?

  • SPI_transfer can be called from a Task context. What is the hwAttrs->baseAddr value in the call to SSIDataGetNonBlocking? I'm guessing it is 0x40008000. If it is, did you enable/setup the SSI0 channel in EK_TM4C1294XL_initSPI() in the EK_TM4C1294XL.c file?

    Also, we try to keep threads short to help with searches. If you have a new question, please start a new thread.

    Todd
  • Sorry, I thought my issue was related. SysCtl enable peripheral ils called for SSI0, SSI2 and SSI3 un the init fonction from the spiloopback in the template.

    Tell me if i should open a new thread.

  • It would be great if you start a new thread. Make sure to include the version of TI-RTOS and device you are using. Can you attach the EK_TM4C1294XL.c and .h files also? I want to see the pinmux code in the init function. Also can you give the code snippet for the SPI_open call you are making.

    Part of why we ask this is because the forum GUI gets cumbersome to use when the threads get long (especially the searches).

    Thanks,
    Todd
  • Thank you Todd, you can follow the new thread here : e2e.ti.com/.../499599