CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to support@ccsinfo.com

[PIC24HJ256GP210] IVT Spacing / Redirection

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
canadidan



Joined: 13 Feb 2019
Posts: 24

View user's profile Send private message

[PIC24HJ256GP210] IVT Spacing / Redirection
PostPosted: Tue Aug 13, 2019 3:03 pm     Reply with quote

Hello all, I'm hoping someone here can clarify this behaviour for me. It seems to be something compiler-specific that I can't find other reports of.

Note, this is inherited code, so when I call out the Problem below, someone discovered the workaround but it wasn't me.

Scenario

* PIC-C V4.106
* Bootloader application
* IVT redirection

Code:

Program Memory Table
+---------------------+
|  0x0000 - RESET     |
+---------------------+
|  0x0004 - IVT       |
+---------------------+
|  0x0104 - AIVT      |
+---------------------+
|  0x0504 - RDR       |
+---------------------+
|  0x0800 - APP       |
+---------------------+
|  0x26800 - BOOT     |
+---------------------+


It's pretty simple:

Application IVT Table - Generated at 0x0504
Code:

#BUILD (RESET=0x00800)
#BUILD (interrupt=0x0504)


Bootloader IVT Table - Redirection
Code:

#define RDR 0x0504
#ROM  0x00004={RDR+00, 0x0000, RDR+4,  0x0000, ....}


When the interrupt is triggered, the actual IVT will point to the RDR table redirected value, and the RDR table is generated by the linker(?) to point to the correct location for the ISR.

Problem

Notice how the IVT redirection table increments by +4, and is double-spaced by padding with 0x0000. In the generated HEX file, indeed the generated RDR table has incremented by +4.

In the datasheet, the IVT is consecutive and increments by +2.

To illustrate this, see this example:

Example - U2RX

U2RX - UART 2 Receiver (IVT: 0x0050)

PIC-C Generated Code

Redirection IVT Table (Note: instruction 0x50 is byte 0xA0)
:1000A0009C050000A0050000A4050000A8050000B4

Generated IVT Table (Note: instruction 0x059C is byte 0x0B38)
:100B3000FFFFFF00FFFFFF00C613040000000000DE

MPLAB Generated Code

If IVT starts at 0x0504, UART 2 should be at 0x0550

Generated IVT Table (Note: instruction 0x0550 is byte 0x0AA0)
:100AA000C80800001A0900001A0900001A0900000D

Question

* Why does PIC-C increment the IVT by 4, and not 2?
* How does this work given the IVT is defined by datasheet/hardcoded?
* Does this mean the IVT is called early, and NOP's its way to the correct ISR by luck?
* Is my compiler just too old?
Ttelmah



Joined: 11 Mar 2010
Posts: 19195

View user's profile Send private message

PostPosted: Wed Aug 14, 2019 1:52 am     Reply with quote

The reason for 4, is that the data sheet is talking _words_, not _bytes_.
They have a habit of switching nomenclature without warning...
The 'instruction space', always aligns on even 'byte' addresses and
goes up by two addresses in ROM, for each instruction. Each IVT entry
occupies four bytes, but two words.

Pull the Microchip reference:

"PIC24F Flash Program Memory".

Then look at figure 2-4 in this.

Then realise that the IVT, is a 24bit address, occupying 2 words each, but
as a byte address, this means it has to move by four addresses.
canadidan



Joined: 13 Feb 2019
Posts: 24

View user's profile Send private message

PostPosted: Wed Aug 14, 2019 7:27 am     Reply with quote

I think I found the answer! I have the whole byte address (hex file) and word address (flash memory) concepts clear in my head. What I didn't understand is why the PIC-C redirected IVT was different than what I got MPLAB X. Now I understand:

* Using #BUILD (interrupt=0x0504) causes PIC-C to generate a table of GOTOs
* GOTOs are two instructions wide (+4 words)
* The actual IVT points to these GOTOs
* MPLAB (XC16) is only generating a normal IVT with addresses, not GOTO
* Addresses are only one instruction wide (+2 words)

This is how I figured it out

My project has two interrupts: Timer2, and U2RX.

PIC-C Code
Code:

// IVT at 0x004
:1000380034050000380500003C05000040050000BC // Addr: 0x44 bytes, 0x22 words
:1000980094050000980500009C050000A0050000DC // Addr: 0xA0 bytes, 0x50 words
// Table at 0x504
:080A8000E01104000000000079 // Addr: 0xA80 bytes, 0x540 words
:080B3800F214040000000000AB // Addr: 0xB38 bytes, 0x59C words


The IVT points to GOTOs in the GOTO redirect table.

MPLAB X Code
Code:

// Table at 0x504
:100A38001A0900001A0900001A090000FA08000043 // Addr: 0xA44 bytes, 0x522 words
:100A98001A0900001A090000C80800001A09000015 // Addr: 0xAA0 bytes, 0x550 words


This table is straight from the linker, and is not GOTOs. It is the actual IVT but placed later in memory. This won't work Razz

Thanks for helping me dig deeper Ttelmah!
jeremiah



Joined: 20 Jul 2010
Posts: 1314

View user's profile Send private message

PostPosted: Wed Aug 14, 2019 11:24 am     Reply with quote

One note: In place of your manual ROM relocations of the ISRs, you can instead use a somewhat undocumented feature for PIC24s:

In your bootloader, instead of the #build() call to move your interrupts you can do:

Code:

#INT_DEFAULT FAST
void isr(void)
{
   jump_to_isr(0x0504);
}


And it will auto map the IVT for you. Then if you need an interrupt for the bootloader you can do something like:

Code:

#INT_TIMER1 ALT
void timer1_isr(){}


After that, you just need to handle switching between alternate and normal interrupts using enable_interrupts(INTR_ALTERNATE) and enabled_interrupts(INTR_NORMAL). EDIT: Some current versions appear to have a bug where this doesn't work, so you may have to manually set the alternate bit and clear it instead.

In your application code, you still want the #build() call to map your ISRs though. The above is just for the bootloader

also note, for the application (not the bootloader), if you use #build(reset=XXX) it will also use a goto, so you are better off mapping your reset to right before your IVT: #build(reset=0x0500, interrupt=0x0504) and it should be at the start of a FLASH page.

CCS will handle placing updating that reset goto for you

Example bootloader for a PIC24:
Code:

#include <24fj256ga406.h>

/* SET FUSES HERE, THE APPLICATION CANNOT SET THEM */

// User defined bootloader stuff
#define APP_START 0x0800
#define APP_IVT   0x0804

// Remaps IVT
#INT_DEFAULT FAST
void isr(void)
{
   jump_to_isr(APP_IVT);
}

// Reserve application space so bootloader doesn't use it
#org APP_START, (getenv("PROGRAM_MEMORY")-1) {}


// Bootloader ISRS
#INT_TIMER1 ALT
void timer1_isr(){}

// compiler bug workaround
#BIT ALTIVT = getenv("BIT:ALTIVT")

// Bootloader main
void main(void)
{
   // Setup interrupts for bootloader
   ALTIVT = TRUE;
   enable_interrupts(INT_TIMER1);
   enable_interrupts(GLOBAL);
   
   /* DO BOOTLOADER STUFF */
   
   // Reset interrupts for application
   disable_interrupts(GLOBAL);
   disable_interrupts(INT_TIMER1);
   ALTIVT = FALSE;
   
   // Start application
   goto_address(APP_START);
   while(TRUE);
   
}


sample PIC24 application:
Code:

#include <24fj256ga406.h>

#FUSES NONE

#define APP_START 0x0800
#define APP_IVT   0x0804

#build (reset = APP_START, interrupt = APP_IVT)

// reserve bootloader space so application doesn't use it
#org 0, (APP_START-1){}
// need another #org here for the last page in memory since the
// fuses reside here and you don't want to write to this page.  I didn't
// take the time to work out the calculation...exercise for the reader

#INT_TIMER1
void timer1_isr(){}

void main(){

   enable_interrupts(INT_TIMER1);
   enable_interrupts(GLOBAL);
   
   /* DO APPLICATION STUFF */
   while(TRUE);
}
canadidan



Joined: 13 Feb 2019
Posts: 24

View user's profile Send private message

PostPosted: Wed Aug 14, 2019 1:00 pm     Reply with quote

Great example, thank you! Aside from the jump_to_isr() approach, the code I'm working with is similar, and uses ALTIVT for the bootloader and IVT for application.

However, I can clean up the organization a bit to map things at the start of a page.

Bootloader
Code:

#include <ALT_24HJ256GP210.h>
#BUILD (ALT_INTERRUPT)
#DEFINE FIRMWARE_RESET_ADDRESS 0x400

// Fuses //

#org default
#org 0x26800, 0x2A7FF  auto=0 default

void main(){
  // stuff
}


Application
Code:

#include <ALT_24HJ256GP210.h>
#BUILD (RESET=0x00400)
#BUILD (interrupt=0x0404)
#ORG 0x00000,0x003FF {}

void main(){
  // stuff
}
jeremiah



Joined: 20 Jul 2010
Posts: 1314

View user's profile Send private message

PostPosted: Wed Aug 14, 2019 1:45 pm     Reply with quote

No problem at all

canadidan wrote:

Application
Code:

#include <ALT_24HJ256GP210.h>
#BUILD (RESET=0x00400)
#BUILD (interrupt=0x0404)
#ORG 0x00000,0x003FF {}

void main(){
  // stuff
}


Don't forget you want

Code:
#FUSES NONE


In the application header so it doesn't generate any code for the FUSES. Additionally, if you have a #use delay() statement, it should only have a "clock=" statement and not "internal=", etc. so that it doesn't generate any fuses.

EX:
Code:
#use delay(clock=8MHz)


You also want a blank #org for the the last page in memory so that the application doesn't try to erase that page (it has the FUSES in it). A lot of bootloaders will throw that code out and error, but you don't want to rely on that for a couple of reasons:

1. Some bootloaders only ignore that page and don't generate an error or warning, which means you will potentially be referencing code that isn't there
2. Some bootloaders don't protect that page at all, so you would erase the fuses and the next time you cycle power things will go crazy.
canadidan



Joined: 13 Feb 2019
Posts: 24

View user's profile Send private message

PostPosted: Wed Aug 14, 2019 2:57 pm     Reply with quote

My ignorance will show here, but aren't fuses located outside of the "usable memory" range?

0x00000 - RESET (GOTO 0x26800)
0x00004 - IVT
0x00104 - AIVT
0x00400 - APP GOTO
0x00404 - APP IVT
0x26800 - BOOTLOADER
0x2ABFF - END OF MEMORY

My understanding is that fuses are located in the 0xF00000 range, way outside (or is this a variable location the compiler sets?)

I suppose this is implementation specific, but my application "upgrade" code won't write or erase outside of 0x400 - 0x267FF (to protect bootloader and page 0).
jeremiah



Joined: 20 Jul 2010
Posts: 1314

View user's profile Send private message

PostPosted: Wed Aug 14, 2019 6:42 pm     Reply with quote

canadidan wrote:
My ignorance will show here, but aren't fuses located outside of the "usable memory" range?

0x00000 - RESET (GOTO 0x26800)
0x00004 - IVT
0x00104 - AIVT
0x00400 - APP GOTO
0x00404 - APP IVT
0x26800 - BOOTLOADER
0x2ABFF - END OF MEMORY

My understanding is that fuses are located in the 0xF00000 range, way outside (or is this a variable location the compiler sets?)

I suppose this is implementation specific, but my application "upgrade" code won't write or erase outside of 0x400 - 0x267FF (to protect bootloader and page 0).


IIRC, the fuses have two locations. You don't write your code to 0xF00000 or whatever your chips address is. You write them to the end of writable memory and the micro reads them at bootup and loads the other address with them (I think).

Either way, look at the bottom of your LST file and you'll see they are written to the bottom of writeable memory. As a matter of fact if you copy the address of

Code:

getenv("PROGRAM_MEMORY")


and print it, that will be the location of the FUSES. And that location is on the last writeable page of memory, so you don't want to erase that by writing application code to the beginning of that page (which would require an erase to the page first)
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group