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.

Compiler/TMS320C5535: How do I associate interrupt qualified functions with an interrupt vector?

Part Number: TMS320C5535

Tool/software: TI C/C++ Compiler

I have read the CPU manual and compiler manual for the C5535 and the associated C compiler. The first tells me how the CPU handles interrupts, and that's clear. The second tells me how to write and declare interrupts, and that's clear. But I can't figure out how to tell the C compiler that a particular interrupt function is for eg. a TIMER or UART interrupt. I had expected there to be a pragma to do it, or some way to do it via the linker script, but I can't figure it out. Is there a simple way to associate an interrupt function with its vector?

  • Hi,

    I've notified the sw team. Their feedback will be posted here.

    Best Regards,
    Yordan
  • Hi Jason,

    Have you had a look at the "Setting Up TMS320 DSP Interrupts in C" Application report or similar ?
    I suggest you use DSP/BIOS as it is simpler to setup interrupts even if you don't use it for much else.
    There in the configuration tool, in the "Hardware Interrupt Service Manager" you have all interrupts available software and hardware sorted by priority, and you can define the entry function for the interrupt for each interrupt used, by simply adding an underscore before the actual iterrupt function name. So your DMA function in your C-code, say DMA_isr() { ... } in DSPI/BIOS will be HWI_INT8 on a particular DSP, and will have a function defined as _DMA_isr to tell it where the interrupt executes the code etc. Some cannot be modified as are already defines by the software as defaults (say the HWI_RESET for instance or the CLK_dispatch etc).

    Hope this helps.

    Cheers, Mike
  • Thanks for that reference, I hadn't seen it. If I understand right, I need to implement the interrupt vector table in assembly and use a linker script to make sure it's placed in the correct location?

    Maybe it's just the way TI does things, but it seems like a convoluted process compared to eg. ARM (where there's a naming convention and the linker takes care of it) or Microchip (where you can annotate functions using compiler extensions). Are there fully-populated linker or assembly files that I can just modify, perhaps in the chip support library or elsewhere?
  • I was wrong about ARM in my comment, at least for the LPC17xx using Mbed libraries — it uses pre-compiled startup code based on an assembly table/linker script, just like how TI does it. But my point still stands — I hadn't remembered this because I could just obtain the necessary config pre-baked and ready to go.

    If that's not the way it's done with TI then that's just the way it is, and I'll go through the process outlined in that reference you posted. But I don't want to reinvent the wheel, so let me know if there's a quicker way to get started.
  • Oh, and what did you mean by "I suggest you use DSP/BIOS"? Is this a tool I can use?
  • Jason Heeris82 said:
    Thanks for that reference, I hadn't seen it. If I understand right, I need to implement the interrupt vector table in assembly and use a linker script to make sure it's placed in the correct location?

    Maybe it's just the way TI does things, but it seems like a convoluted process compared to eg. ARM (where there's a naming convention and the linker takes care of it) or Microchip (where you can annotate functions using compiler extensions). Are there fully-populated linker or assembly files that I can just modify, perhaps in the chip support library or elsewhere?

    Hi, no need to do any assembly. The easiest way (and since you are going to have to do some setting up anyway) is to set-up DSP/BIOS (or better known these days as SYS/BIOS) which is an O/S for the TMSC320 DSPs. You must be using Code Composer already, if version Platinum its gona be version 3,3 the last version realeased (works on Windows XP only, or the most recent version 6 point something. The O/S has a graphical configuration interface to let you pretty much set-up the O/S as you need, including DMA, GPIOs, TIMERS, memory, tasks etc. and especially easy for interrupts. Once set-up and working you'll never look back or even mention the word "assembly". If you prefer doing the vector table way then you may have to look into it, but the O/S way is more usefull in the long term.

    Good luck,

    MM

  • Sounds neat, although I'd worry about resource usage if there was an extra layer running on the chip. The MIPS budget is pretty tight, but I won't dismiss anything out of hand before profiling.

    The configuration tool sounds useful too, but can it be compiled via command line tools instead of CCS? Testing is important to me, so a fully automated build chain is essential. At the moment I'm looking at setting one up with the TI command line tools and Make.

  • Hi Jason,

    the O/S add very little overhead as its tigthly written in assembly from what I know. Some messaging functions, (mailbox or ques etc) may add a little more than others, but from my experience the semaphores add very little, and context switching is very tight. You may eventually need to do some profiling, but we never did really ... (besides there is the option of adding another 50MHz to the CPU clock if required critically).
    On the CCS that I use the graphical user interface settings are pre-compiled to somekind of java Textual Configuration script, with a listing generated that implements the eventual O/S configuration for the project. You can manually edit it if you wish and it reflects the changes made to the graphical configuration and vice versa. It's a little different in CCS 6 & 7 but the idea is the same I would assume ... you may wanna check first.

    As an asside, I cam accross some legacy code, from wayback when the project was started that includes these commented-out lines.

    /* Reference the start of the interrupt vector table */
    //extern void VECSTART(void);
    /* Protype declaration for ISR function */
    //interrupt void DMA_Isr(void);

    It looks like we were trying something like that too (but don't know exactly what we did, it was over seven years ago), but then gave up because of other reasons (including ones mentioned above) and just commented them out of the project, but you may wanna go this way if it works out easier. It may give you some clues...

    Hope this helps.
    Cheers, MM
  • I'm still at a loss as to how to do this the assembly way. The application note has very little detail, especially for the C55x, and I can't find any examples. Are there any example projects using the assembly/linker method for configuring interrupts?
  • Here's where I'm up to:

    In my CCS project, I have main.c, interrupts.asm and C5515.cmd.

    My main.c contains setup code and a function "__interrupt void gpio_isr(void)".

    My interrupts.asm looks like this:

            .ref _gpio_isr
            .ref _c_int00
            .def RST
            .sect "vectors"
            .align 256
    
    RST:        .ivec    _c_int00, USE_RETA   ; Reset / Software Interrupt #0
    NMI:        .ivec    no_isr               ; Nonmaskable Interrupt
    INT0:       .ivec    no_isr               ; External User Interrupt #0
    INT1:       .ivec    no_isr               ; External User Interrupt #1
    TINT:       .ivec    no_isr               ; Timer #0 / Software Interrupt #4
    PROG0:      .ivec    no_isr               ; Programmable 0 Interrupt
    UART:       .ivec    no_isr               ; IIS #1 Receive Interrupt
    PROG1:      .ivec    no_isr               ; Programmable 1 Interrupt
    DMA:        .ivec    no_isr               ; DMA Interrupt
    PROG2:      .ivec    no_isr               ; Programmable 2 Interrupt
    COPROCFFT:  .ivec    no_isr               ; Coprocessor FFT Module Interrupt
    PROG3:      .ivec    no_isr               ; Programmable 3 Interrupt
    LCD:        .ivec    no_isr               ; LCD Interrupt
    SARADC:     .ivec    no_isr               ; SAR ADC Interrupt
    XMT2:       .ivec    no_isr               ; I2S2 Tx Interrupt
    RCV2:       .ivec    no_isr               ; I2S2 Rx Interrupt
    XMT3:       .ivec    no_isr               ; I2S3 Tx Interrupt
    RCV3:       .ivec    no_isr               ; I2S3 Rx Interrupt
    RTC:        .ivec    no_isr               ; RTC interrupt
    SPI:        .ivec    no_isr               ; SPI Receive Interrupt
    USB:        .ivec    no_isr               ; USB Transmit Interrupt
    GPIO:       .ivec    _gpio_isr            ; GPIO Interrupt
    EMIF:       .ivec    no_isr               ; EMIF Error Interrupt
    I2C:        .ivec    no_isr               ; IIC interrupt
    BERR:       .ivec    no_isr               ; Bus Error Interrupt
    DLOG:       .ivec    no_isr               ; Emulation Interrupt - DLOG
    RTOS:       .ivec    no_isr               ; Emulation Interrupt - RTOS
    RTDXRCV:    .ivec    no_isr               ; Emulation Interrupt - RTDX receive
    RTDXXMT:    .ivec    no_isr               ; Emulation Interrupt - RTDX transmit
    EMUINT:     .ivec    no_isr               ; Emulation monitor mode interrupt
    SINT30:     .ivec    no_isr               ; Software Interrupt #30
    SINT31:     .ivec    no_isr               ; Software Interrupt #31
    
          .text
    no_isr: b no_isr
    

    Note that only the GPIO interrupt does anything.

    According to the Assembly Language Tools User's Guide, the label for the .ivec directive should be assigned a byte (not word) address and .ivec aligns the SPC to 8 byte boundaries. So all up this should be 32 interrupts * 8 bytes = 256 bytes, right? Or 0x100.

    The linker script looks like this (except I've put the "..."s in to condense it a bit):

    MEMORY
    {
        MMR:     o = 0x000000  l = 0x0000c0  /* 192B Memory Mapped Registers */
        DARAM0:  o = 0x0000C0  l = 0x001F40  /* 8kB Dual Access RAM 0 */
    ...
        DARAM7:  o = 0x00E000  l = 0x002000  /* 8kB Dual Access RAM 7 */
    
        SARAM0:   o = 0x010000  l = 0x002000  /* 8kB Single Access RAM 0 */
    ...
        SARAM31:  o = 0x04E000  l = 0x002000  /* 8kB Single Access RAM 31 */
    
        CS0:     o = 0x050000  l = 0x7B0000  /* 8MB CS0 external memory space */
    ...
        CS5:     o = 0xF00000  l = 0x0E0000  /* 1MB CS5 external memory space */
        ROM:     o = 0xFE0000  l = 0x01FF00  /* 128kB ROM (MPNMC=0) or CS5 (MPNMC=1) */
        VECS:    o = 0xFFFF00  l = 0x000100  /* reset vector */
    }
    
    SECTIONS
    {
        vectors        >  VECS   ALIGN = 256
        .cinit         >  DARAM0
        .text          >  DARAM1
        .stack         >  DARAM0
        .sysstack      >  DARAM0
        .sysmem        >  DARAM4
        .data          >  DARAM4
        .cio           >  DARAM0
        .bss           >  DARAM5
        .const         >  DARAM0
    }
    

    When I compile and link it, I get this error:

    "../C5515.cmd", line 72: error #10099-D: program will not fit into available
    memory. placement with alignment/blocking fails for section "vectors" size
    0x200 page 0. Available memory ranges:
    VECS size: 0x100 unused: 0x100 max hole: 0x100

    Why is it 0x200 (512 bytes) instead of 0x100 (256 bytes)? Am I even on the right track?

  • Jason Heeris82 said:

    ....

    ....

    The linker script looks like this (except I've put the "..."s in to condense it a bit):

    MEMORY
    {
        MMR:     o = 0x000000  l = 0x0000c0  /* 192B Memory Mapped Registers */
        DARAM0:  o = 0x0000C0  l = 0x001F40  /* 8kB Dual Access RAM 0 */
    ...
        DARAM7:  o = 0x00E000  l = 0x002000  /* 8kB Dual Access RAM 7 */
    
        SARAM0:   o = 0x010000  l = 0x002000  /* 8kB Single Access RAM 0 */
    ...
        SARAM31:  o = 0x04E000  l = 0x002000  /* 8kB Single Access RAM 31 */
    
        CS0:     o = 0x050000  l = 0x7B0000  /* 8MB CS0 external memory space */
    ...
        CS5:     o = 0xF00000  l = 0x0E0000  /* 1MB CS5 external memory space */
        ROM:     o = 0xFE0000  l = 0x01FF00  /* 128kB ROM (MPNMC=0) or CS5 (MPNMC=1) */
        VECS:    o = 0xFFFF00  l = 0x000100  /* reset vector */
    }
    
    SECTIONS
    {
        vectors        >  VECS   ALIGN = 256
        .cinit         >  DARAM0
        .text          >  DARAM1
        .stack         >  DARAM0
        .sysstack      >  DARAM0
        .sysmem        >  DARAM4
        .data          >  DARAM4
        .cio           >  DARAM0
        .bss           >  DARAM5
        .const         >  DARAM0
    }
    

    When I compile and link it, I get this error:

    "../C5515.cmd", line 72: error #10099-D: program will not fit into available
    memory. placement with alignment/blocking fails for section "vectors" size
    0x200 page 0. Available memory ranges:
    VECS size: 0x100 unused: 0x100 max hole: 0x100

    Why is it 0x200 (512 bytes) instead of 0x100 (256 bytes)? Am I even on the right track?

    For some reason I have this on my C5515 platform:

    VECT:     origin = 0xff00, len = 0x100

    You might wanna check the origin for the vectors table pertinent to you ...

    Cheers, MM

  • Looking at the CPU reference guide (p2-24), I see:

    Two 16-bit interrupt vector pointers IVPD and IVPH (see Figure 2−11) point to up to 32 interrupt vectors in program space. IVPD points to the 256-byte program page for interrupt vectors 0–15 and 24–31. IVPH points to the 256-byte program page for interrupt vectors 16–23.

    If IVPD and IVPH have the same value, all of the interrupt vectors are in the same 256-byte program page. A DSP hardware reset loads both IVPs with FFFFh. The IVPs are not affected by a software reset instruction.

    A table on the next page shows that IVPH gives the bits 24-8 of the interrupt vector addresses ie. 0xFFFF00. I'll give it a shot, but it seems like this will just put the vectors in DARAM7.

  • Jason Heeris82 said:

    I'll give it a shot, but it seems like this will just put the vectors in DARAM7.

    Yep:
    <Linking>
    "../C5515.cmd", line 67: error #10264: VECS memory range overlaps existing
       memory range DARAM7
    

  • Okay, the problem seems to be that the runtime library for this chip rts55x.lib (as selected by CCS), already contains a vectors section. I can't find documentation for what it does. There is a file RTS_4_4_1_Manifest.pdf that contains this line:

    vectors.asm TI BSD Texas Instruments, Inc.

    But the only vectors.asm I can find is in the chip support library, which is the one I based mine off.

    Is it really the case that the runtime support library reserves the interrupt vector space and I have to hack around that 

    Come on TI, I wouldn't cast myself as a master of embedded programming here, but I have used a variety of chips for interrupt-driven designs. You are making interrupts far, FAR harder than they have to be. Can you throw me a couple of clues at least, if you're not going to document this?

  • Okay, I found the RTS lib source at least, it was in the same directory as the library. I suspect I have to just not use it if I want to use my own interrupts, but that means writing my own _c_int00 and other things... which I don't hugely want to do.
  • Oh, and setting "Runtime support library" to "<none>" in CCS still makes the project link with rts55x.lib. So even if I wanted to disable it, I can't, it seems?
  • Jason Heeris82 said:
    Oh, and setting "Runtime support library" to "<none>" in CCS still makes the project link with rts55x.lib. So even if I wanted to disable it, I can't, it seems?

    Turns out there's an extra option under Build > C5500 Linker > File Search Path > Disable automatic RTS selection which need to be checked. Why you'd have a "<none>" option and not mention this is beyond me. Of course, if I remove it, I have no _c_int00 and therefore no entry point. So... the TI C5515 doesn't do freestanding operation without a lot of work, as far as I can tell.

    There's another thing too: the C5515 has a single-access ROM section mapped to exactly the memory range that the interrupts sit.

    The zero-wait-state ROM is located at the CPU byte address range FE 0000h - FF FFFFh. The ROM is
    composed of four 16K-word blocks, for a total of 128K-bytes of ROM. Each ROM block can perform one
    access per cycle (one read or one write). ROM can be accessed by the internal program or data buses,
    but not the DMA buses. The ROM address space can be mapped by software to the external memory or
    to the internal ROM via the MPNMC bit in the ST3 status register.

    The standard device includes a bootloader program resident in the ROM and the bootloader code is
    executed immediately after hardware reset. When the MPNMC bit field of the ST3 status register is set
    through software, the on-chip ROM is disabled and not present in the memory map, and byte address
    range FE 0000h - FF FFFFh is unmapped. A hardware reset always clears the MPNMC bit, so it is not
    possible to disable the ROM at hardware reset. However, the software reset instruction does not affect the
    MPNMC bit. The ROM can be accessed by the program and data buses. Each SAROM block can perform
    one word read access per cycle.

    If I'm reading this right, and maybe I'm not, it's simply impossible to have a static configuration for interrupts. Because I can't even write to this memory, via CCS or other means, without setting this MPNMC bit, which I cannot do except via software?

    C55xx: Loader: One or more sections of your program falls into a memory region that is not writable.  These regions will not actually be written to the target.  Check your linker configuration and/or memory map.

    This chip is starting to be a lot more time consuming and expensive than I first thought.

  • I realised you probably weren't aware that I'm still stuck on this, because it was marked as resolved. I've un-marked it, because I'd really appreciate some more help with this (statically configured interrupts). I need to make a decision soon about whether to continue with the C55x or not, and this is a make-or-break feature. Cheers.
  • Yes, the RTS provides its own version of the interrupt vector table, because we need it to perform compiler testing. If you look in vectors.asm, you will see that it defines a symbol named _Reset. There is some reference somewhere in your program to this symbol, which causes the linker to drag in vectors.obj from the RTS. (In fact, there's a .ref in the RTS boot.asm, so you will get this by default!) If you write your own interrupt vector table, you need to make sure that it defines the symbol _Reset, in addition to whatever other names you want to get the interrupt vector table. Then, even though you pull in _c_int00 from the RTS library, the linker will find that the symbol _Reset is satisfied by your own interrupt vector table, and won't need to pull in the RTS file vectors.obj.
  • I converted my "interrupts.asm" to look like:

        .ref _gpio_isr
        .ref _c_int00
        .sect "vectors"
        .align 256
    
        .def    _Reset
    
        .if __TMS320C55X_PLUS_BYTE__
    _Reset: .ivec _c_int00, STK_LINEAR | RET_FAST | DATA_PTR_BYTE
        .else
    _Reset: .ivec _c_int00, USE_RETA
        .endif
    
    NMI:        .ivec    no_isr               ; Nonmaskable Interrupt
    INT0:       .ivec    no_isr               ; External User Interrupt #0
    INT1:       .ivec    no_isr               ; External User Interrupt #1
    TINT:       .ivec    no_isr               ; Timer #0 / Software Interrupt #4
    PROG0:      .ivec    no_isr               ; Programmable 0 Interrupt
    UART:       .ivec    no_isr               ; IIS #1 Receive Interrupt
    PROG1:      .ivec    no_isr               ; Programmable 1 Interrupt
    DMA:        .ivec    no_isr               ; DMA Interrupt
    PROG2:      .ivec    no_isr               ; Programmable 2 Interrupt
    COPROCFFT:  .ivec    no_isr               ; Coprocessor FFT Module Interrupt
    PROG3:      .ivec    no_isr               ; Programmable 3 Interrupt
    LCD:        .ivec    no_isr               ; LCD Interrupt
    SARADC:     .ivec    no_isr               ; SAR ADC Interrupt
    XMT2:       .ivec    no_isr               ; I2S2 Tx Interrupt
    RCV2:       .ivec    no_isr               ; I2S2 Rx Interrupt
    XMT3:       .ivec    no_isr               ; I2S3 Tx Interrupt
    RCV3:       .ivec    no_isr               ; I2S3 Rx Interrupt
    RTC:        .ivec    no_isr               ; RTC interrupt
    SPI:        .ivec    no_isr               ; SPI Receive Interrupt
    USB:        .ivec    no_isr               ; USB Transmit Interrupt
    GPIO:       .ivec    _gpio_isr            ; GPIO Interrupt
    EMIF:       .ivec    no_isr               ; EMIF Error Interrupt
    I2C:        .ivec    no_isr               ; IIC interrupt
    BERR:       .ivec    no_isr               ; Bus Error Interrupt
    DLOG:       .ivec    no_isr               ; Emulation Interrupt - DLOG
    RTOS:       .ivec    no_isr               ; Emulation Interrupt - RTOS
    RTDXRCV:    .ivec    no_isr               ; Emulation Interrupt - RTDX receive
    RTDXXMT:    .ivec    no_isr               ; Emulation Interrupt - RTDX transmit
    EMUINT:     .ivec    no_isr               ; Emulation monitor mode interrupt
    SINT30:     .ivec    no_isr               ; Software Interrupt #30
    SINT31:     .ivec    no_isr               ; Software Interrupt #31
    
          .text
    no_isr: b no_isr
    

    Sure enough, this fixed the problem of having two interrupt tables competing for the "vectors" section.

    However, it's still not actually writing the table. When debugging, I see this during the programming phase:

    C55xx: Loader: One or more sections of your program falls into a memory region that is not writable.  These regions will not actually be written to the target.  Check your linker configuration and/or memory map.
    

    Upon the interrupt triggering, the CPU's PC ends up at 0xFFF061, and looks like a straight loop:

    fff061:   4a7e                     B #0xfff061

    Meanwhile the disassembly at the GPIO interrupt address 0xFFFFA8 looks like:

            GPIO:
    ffffa8:   eafff0                   MOV HI(AC3 << #0xfffffff0),*AR7(short(#7))
    ffffab:   615e                     BCC #0xffffaf,AR6 >= #0
    ffffad:   805f80                   MOV dbl(*AR2(T0)),dbl(*AR7)

    To make it even more confusing, the IVPD register contains 0x027F.

    I don't understand what's going on here.

  • The IVPD register appears to be set by the default GEL file for the ezDSP C5515 board. The GEL file contains

    #define IVPD     0x0049

    ...and then later...

    Peripheral_Reset()
    {
        int i;
    
        *(short *)PSRCR@IO = 0x0020;
        *(short *)PRCR@IO  = 0x00BB;
    
        for(i=0;i<0xff;i++);
        *(short *)IVPD@data = 0x027F; // Load interrupt vector pointer
        GEL_TextOut("Reset Peripherals is complete.\n");
    }

    ????

    There's also a function in the GEL file c5515_MapInit() that calls a function GEL_MapAdd()  a bunch of times, and there's this comment:

    /* Memory map based on MP/MC value (assume MP/MC = 0).    */

    So to recap, my questions are:

    1. Is it possible to have a static interrupt table?
    2. If so, how?
    3. How can I have it at 0xFFFF00 on the C5515?

    Extra question just for my own curiosity: What's the rationale behind having the default location of the ISR table be mapped within the same memory range as the on-chip boot ROM?

  • At this point I suspect I'm just having this conversation with myself, but I'd like to bump this one last time before I start looking at other microprocessors. It's a huge hassle to change chips at this stage of my project, but if I can't figure out how to use interrupts on this chip, I'm just going to have to bite the bullet.

    If anyone has any insight into how I can do what I need to do (see previous post), please chime in.

  • So I eventually got my interrupts working, but I had to forget about putting them at the end of memory. I think the last 256 bytes of the C5515's memory space are just not to be used? What I ended up doing was putting them at the end of DARAM like this:

    MEMORY
    {
    ...
        DARAM7:  o = 0x00E000  l = 0x001F00  /* 8kB Dual Access RAM 7 */
        VECS:    o = 0x00FF00  l = 0x000100  /* Interrupt vector table */
    
        SARAM0:   o = 0x010000  l = 0x002000  /* 8kB Single Access RAM 0 */
    ...
    }

    ...and in my setup changing the IPVD/H:

    #define INTERRUPT_BASE (0x00FF)
    
    ...
    
    CSL_CPU_REGS->IVPD = INTERRUPT_BASE;
    CSL_CPU_REGS->IVPH = INTERRUPT_BASE;

    And presto, I can use interrupts now.

    Too easy.