Teensy4 startup code weirdness

Status
Not open for further replies.

shoedrip

Member
Hi,

Sorry if this isn't the proper forum section to ask for advice...

I'm working on a small learning project but reached a point where I'd like to be able to trace what exception/interrupts are triggered. I obviously can't simply overwrite entries in the _VectorsRam array as library code may overwrite to it at anytime.
My idea was then to switch to another vector table, and make all the entries point to a function that reads the current interrupt number (from ICSR's VECTACTIVE field), writes somewhere that an exception occured, and called the correct entry from the _VectorsRam array.

_VectorsRam is stored in SRAM at address 0x20004c00, 1024-bytes aligned while my secondary table (I called it VRAMHooks) is at 0x20003400, also 1024-bytes aligned.

However when I write VRAMHooks address into SCB_VTOR, the teensy hangs and doesn't respond over serial to programming commands.

Code:
void debug_exception(void) {
	int excn = int_csr->vectactive;
	Serial.printf("exc: %d\n", excn);
	activated[excn] = 1;
	_VectorsRam[excn]();
}

VRAMHooks is filled with pointers to this function.
Anyway I couldn't figure out what was causing the crash, and even memcpy'ing _VectorsRam into VRAMHooks still caused the teensy to hang. I then took the ResetHandler function, and copy pasted it into my program, set the original ResetHandler to a weak symbol to override it, and the teensy still booted correctly.

I then modified that ResetHandler to fill VRAMHooks and set it to SCB_VTOR, instead of _VectorsRam and that's when things got weird.

Basically, I don't understand how to initialize th VRAMHooks array:

Code:
	for (i=0; i < NVIC_NUM_INTERRUPTS + 16; i++) {
		_VectorsRam[i] = &unused_interrupt_vector;
//		VRAMHooks[i] = [anything, function pointer or constant value];
	}

I left SCB_VTOR initialized to _VectorsRam, meaning that from this point on, I'm only writing into VRAMHooks.
I can't add a line in this loop that initializes _VectorsRam, or else the teensy doesn't hang, but the led starts blinking, which I guess is from the hardfault handler. My guess is that this optimize("no-tree-loop-distribute-patterns") attribute is related to this?

However, if I move the initialization to a separate loop right after:

Code:
	for (i=0; i < NVIC_NUM_INTERRUPTS + 16; i++) {
		_VectorsRam[i] = &unused_interrupt_vector;
        }
	for (i=0; i < NVIC_NUM_INTERRUPTS + 16; i++) {
		VRAMHooks[i] = [anything except a pointer to debug_exception];
	}

This doesn't hardfault. Except if I'm assigning debug_exception. unused_interrupt_vector works.

Why? How?

Anyway, since it stopped crashing to the hardfault handler, I tried writing VRAMHooks to SCB_VTOR but obviously no luck, and reverted back to _VectorsRam, since the rest of the initialization requires systick to be properly working, which it can't if I can't initialize VRAMHooks with pointers to debug_exception (and assuming it does what I think it does).

I did a bit of further investigating, and it does this even if I move that loop right before the call to main, but it works from main. So I deduced that the issue is because ResetHandler is in the .startup section and main is in .text.itcm, but this raises more questions than it answers:

- _VectorsRam and VRAMHooks are in the same memory region, why can't ResetHandler write arbitrary values to VRAMHooks if it's only being written to, never read, while code after main can? debug_exception and unused_interrupt_vector are in the same memory region, what makes them different? I even copy pasted unused_interrupt_vector's code and attributes into debug_exception, but it didn't do anything.

- What makes code executing in flash different from code in ITCM?

Thanks, please don't hesitate to point out any error I made.
 
I can imagine you could be running into a whole lot of problems or questions, like:
You are in an interrupt, and then you try to call Serial.print...

Suppose: you have interrupts before the Serial subsystem is initialized? Or that it is called hundreds/thousands of times per second, could it keep up?
Also suppose your Serial output is going to need interrupts while working with the USB subsystem...

There is some support within the core to help you trace out stuff at the lower level and it is setup to use Serial4 (pin 17)...
That can be enabled by going into the file teensy4\debug\printf.h

and uncomment the first line: //#define PRINT_DEBUG_STUFF
This will enable the code in the file debugprintf.c

Which will initialize Serial4 at 115200 baud rate. The code that outputs is really basic and does not use interrupts, it waits until it can output each character (no software queue).

I have used this when debugging low level stuff, especially as earlier on, during T4 beta time frame when there was no USB Serial support...

Edit: My guess is that you may need/want to do this much more targeted, as you can and probably well get a lot more interrupts/output and it will probably be difficult to find the tree you are looking for in the forest...
 
I will obviously comment Serial.print out when I feel it may be a part of the problem.
One of the issue I'm having right now is that I can't even get that function called because I can't even relocate the vector table.
 
I guess it may depend on how hard do you want to work at it?

That is there are places in code that are hard coded to write to _vectorsRam as a known array. Simply do a global search in the Teensy4 directory and you will see some of them. like:
Code:
static void configure_systick(void)
{
	_VectorsRam[14] = pendablesrvreq_isr;
	_VectorsRam[15] = systick_isr;
...

Which is also touched in delay.c
Likewise DMAChannel code has:
Code:
	void attachInterrupt(void (*isr)(void)) {
		_VectorsRam[channel + IRQ_DMA_CH0 + 16] = isr;
		NVIC_ENABLE_IRQ(IRQ_DMA_CH0 + channel);
	}

And who knows if or how many libraries may do something similar.

Now if it were me, and really wanted to try this out:

I would probably try to leave most of the code alone, setup my own vector table with my forwarders and then simply set it as the interrupt vector instead of the _VectorsRam;
i.e. the line:
Code:
	SCB_VTOR = (uint32_t)_VectorsRam;

Which I know you are trying to do.

But again this would be mucking with the timings and the like... Also not sure how your code is figuring out which ISR is triggered and then branching off to the correct real interrupt vector. That is what is (int_csr->vectactive) If this is wrong you could easily be jumping to never never land.
 
I know that my interrupt code may be wrong, that's why I mentionned that even memcpy'ing the whole table didn't work.
 
The question is when did you do a memcpy? Will it catch all of the places that currently setup their own ISR handler.

Example if somewhere in your code after this does something like: Serial1.begin(115200), or SPI.begin or setup to use any timer or...
And something then triggers that interrupt. Example for Serial1, you do something like Serial1.write(0); or a character comes in on the RX pin, an interrupt condition will be triggered.

Now if your code keeps this interrupt from being called. Either a) it will go to an unused interrupt vector an crash, or it potentially it simply returns, without doing anything to clear the interrupt condition, the system will retrigger the interrupt, which will leave you an an endless calling of ISR...
 
OK this actually works. It turns out for some reason I either, left Serial.printf in the exception handler, did an infinite recursive loop because I replaced _VectorsRam with VRAMHooks in the handler, or forgot to remove an __attribute__((naked)) while testing...

Simply assigning the table to VTOR works as expected now.
Thank you!
 
Status
Not open for further replies.
Back
Top