Problems with accessing internal registers

Status
Not open for further replies.

Welocs

Well-known member
Hello,

i am trying to access registers of the uart-controller of teensy 3.5. But if i do so, the µC get stuck.

Code:
		Serial.println("no bug here");
		// configure UARTX-Controller:
		UART3_C2 &= 0x7f;			// disable TIE;
		Serial.println("no bug here");
		UART3_C2 |= UART_C2_TCIE;
		Serial.println("no bug here");
		UART3_C5 &= 0x7f;			// disable TDMAS;
		Serial.println("no bug here");
		UART3_C5 |= UART_C5_TCDMAS;
		Serial.println("no bug here");

"no bug here" is just printed once :-\

any ideas, why i cannot access the register / why the controller get stuck?
 
Last edited:
Which Teensy? Which compiler options? I found that sometimes these direct register accesses fail when compiling with LTO.

(Könnt Ihr nicht einmal alle notwendigen Informationen im ersten Posting geben, damit man sich die Rückfragen spart... ;) )
 
Which compiler options? I found that sometimes these direct register accesses fail when compiling with LTO.

Well, i had the same idea of trying another compiler setting and tried out some others, but it doesn't work.
AND...sometimes compilers do crazy stuff, but i don't think its about the compiler options, because all the libraries used for SerialX do the same (at different compiler settings) and still work.
 
My question was less about the compiler, but LTO. The linker optimizations are sometimes unpredictable...

What happens when you try to read the UART3_C2 and ~_C5 registers and printing these with Serial.println before you try to write/modify these? I'm at work now, no access to my Teensy enironment...
 
i tried out to read from UART3_C2:

Code:
Serial.println("no bug here");
		uint8_t temp;
		temp = UART3_C2;
		Serial.printf("register: %d\r\n", temp);

"register: %d" is not being printed;
 
ok, i just created a new sketch in arudino itself ( i was working with microsoft visual studio):
Code:
#include <kinetis.h>

#define LED 45

void setup() {
  //Serial.begin(500000);
  //while (!Serial);
  pinMode(LED, OUTPUT);
  digitalWrite(LED, 1);

  uint8_t temp;
  temp = UART3_C2;

  digitalWrite(LED, 0);
}

void loop() {
  // put your main code here, to run repeatedly:

}

i tried out different compiler settings (of course also with LTO off), and all the same result: the LED at pin 45 turns on, but not off, so the CPU is stuck somewhere in the nirvana of bits and bytes?!
but if i read out e.g.
Code:
temp = GPIOA_PDIR;
the LED turns off again.

is there a prohibition for manipulating these registers in the userlevel?
 
You can do anything you want - there are side effects from those actions.

I have no idea why that operation was chosen - or what it does - other than it is referencing a piece of uninitialized hardware :: Serial4

>> #define UART3_C2 (KINETISK_UART3.C2) // UART Control Register 2

When this is added to setup before exciting that register :: Serial4.begin(500000);

The LED goes off as expected.
 
i was working with microsoft visual studio

Another bit of information which you didn't give in your first post... Seen that the default environment is the Arduino IDE with the Teensyduino plugin which takes care of automatically including the core files with the needed headers including updated register definitions, you should have mentioned that you work with an unsupported IDE.

I've also run into such issues in the past, working in Eclipse with the Sloeber plugin. From that, I learned to always copy/paste non working code into the Arduino/Teensyduino IDE and compile it there for a first check. If it then runs, it's not PJRC's fault.
 
Another bit of information which you didn't give in your first post... Seen that the default environment is the Arduino IDE with the Teensyduino plugin which takes care of automatically including the core files with the needed headers including updated register definitions, you should have mentioned that you work with an unsupported IDE.

I've also run into such issues in the past, working in Eclipse with the Sloeber plugin. From that, I learned to always copy/paste non working code into the Arduino/Teensyduino IDE and compile it there for a first check. If it then runs, it's not PJRC's fault.

as you can read in my last post, i tried both...
 
You can do anything you want - there are side effects from those actions.

I have no idea why that operation was chosen - or what it does - other than it is referencing a piece of uninitialized hardware :: Serial4

>> #define UART3_C2 (KINETISK_UART3.C2) // UART Control Register 2

When this is added to setup before exciting that register :: Serial4.begin(500000);

The LED goes off as expected.

Hello defragster,

if you have no idea, what this register is for: it is to configure a UART controller (in the example above case #3);

This definition, you found, is just a definition in the "kinetis.h" file. If you search deeper, you will find a definition of a pointer, starting at KINETISK_UART3 (---> (*(KINETISK_UART_t *)0x4006D000)) which adds the value of the struct KINETISK_UART_t at position "C2".
When knowing this, you also could write:
Code:
temp = (*(volatile uint8_t *) 0x4006A003);
this is the hardware address of the register mentioned above.

So thinking of this, coding "Serial4.begin(myBaud)" should have no effect to the possibilty to reading out any hardware register. But reading out any hardware register of UART might have effect to Serial4 - but if you don't use it, why these things should happen?
 
hello there,

i found the problem:
The chip is designed so that you can turn any hardware module on and off via the "SIM" module by simply turning the clock on or off. So if you do not turn on the uartx module, you want to access, the CPU will get stuck, because the module won't return any value to the internal bus system (i'd read this manual from the chip, but it has 1789 pages -.-, and i don't really like the datasheet structure, i more like the ones from microchip :p [*excuse, cough*]).
Why the chip manufacturer does implement something like "SIM"? Because, if you do not need any of the UARTs (or any other hardware modules), let them have no clock. No clock means less transistors switching, which means less power consumption.

so the simple line:
Code:
SIM_SCGC4 |= SIM_SCGC4_UART2;
solves the problem.
 
the SIM_SCGC clock register bit for the peripheral must always be enabled, otherwise accessing the remaining registers will indeed lock up, this is normal
 
Hello defragster,

if you have no idea, what this register is for: it is to configure a UART controller (in the example above case #3);

This definition, you found, is just a definition in the "kinetis.h" file. If you search deeper, you will find a definition of a pointer, starting at KINETISK_UART3 (---> (*(KINETISK_UART_t *)0x4006D000)) which adds the value of the struct KINETISK_UART_t at position "C2".
When knowing this, you also could write:
Code:
temp = (*(volatile uint8_t *) 0x4006A003);
this is the hardware address of the register mentioned above.

So thinking of this, coding "Serial4.begin(myBaud)" should have no effect to the possibilty to reading out any hardware register. But reading out any hardware register of UART might have effect to Serial4 - but if you don't use it, why these things should happen?

I wasn't looking for a lesson - given I found all I needed to in one simple search to get an idea why executing that single statement could be fatal. And then given that presented a demonstration to answer the question at hand:

Welocs said:
is there a prohibition for manipulating these registers in the userlevel?

defragster said:
You can do anything you want - there are side effects from those actions.

Apparently tonton81 actually read the manual or something


Welocs said:
did you mean by this the "not turned on clock"?

Yes … In general when the Teensy ARM powers up it is a blob of potential and everything is disabled to save power and because the way it is configured with multipoint MUXes - there is no DEFAULT behavior. Until proper setup and enabling.
 
I was also going to mention, that you needed to enable the clock otherwise accessing any memory location associated with the device would cause the processor to fault.

Yes reading the manual helps....

But I found it out on the T3.6 when I was adding support for the new USB ports and if you made a mistake and did not enable the right one, well...

First time through, I debugged with similar code like above, but warning the debug code may not catch it...

That is if you do something like:
Code:
		Serial.println("no bug here");
		// configure UARTX-Controller:
		UART3_C2 &= 0x7f;			// disable TIE;
		Serial.println("no bug here");
		UART3_C2 |= UART_C2_TCIE;
		Serial.println("no bug here");
		UART3_C5 &= 0x7f;			// disable TDMAS;
		Serial.println("no bug here");
		UART3_C5 |= UART_C5_TCDMAS;
		Serial.println("no bug here");
The first println, will probably actually print as the code will put it into the queue and the next line will fault the processor.

So when I was doing this, I ended up doing it more like:
Code:
		Serial.println("no bug here"); Serial.flush();
		// configure UARTX-Controller:
		UART3_C2 &= 0x7f;			// disable TIE;
		Serial.println("no bug here"); Serial.flush();
		UART3_C2 |= UART_C2_TCIE;
...
So the flush will force the message to be printed before it actually tries the next line...

I also found it using tracing using digitalWrite... And logic analyzer.

The other hint is to look at existing code:

For example if you look in ...\teensy3\serial4.c

At all of the functions that are likely to be called at the start of using Serial4, like serail4_begin,
You will see they all start off with a line like: SIM_SCGC4 |= SIM_SCGC4_UART3;
 
Thanks for the added info KurtE …
enable the clock otherwise accessing any memory location associated with the device would cause the processor to fault.

I just opened the library I started to work on 2 months back :: #include "debug_t3.h"

Indeed doing this where __LINE__ is #20:
Code:
  t3deb( 9, __LINE__ );
  temp = UART3_C2;

triggers a fault with this info:
>>>> debug_fault >>>> debug_fault >>>> Calling _isr#:2
Debug Info:
9 => 20 0x14 [L#20_C#2

fault:
??: 1000000
??: 60C
??: 609
psr:40072080
adr:4006D000
lr: 14
r12:14
r3: 9
r2: FFFFFFF9
r1: 0
r0: 0
r4: 1FFF1510
lr: 328
---


The first 'debug_fault' line I generated where the '9' is param one in " t3deb( 9, __LINE__ );" showing it was line 20 last set in that location, and the 'C#' printed shows I had called it once before with 9 as Param #1.

And it is "void hard_fault_isr(void)" that maps to ">>>> Calling _isr#:2"

The 'register dump' info after that is borrowed from the inbuilt FAULT code Paul has in place but disabled in :: from: ...\hardware\teensy\avr\cores\teensy3\mk20dx128.c

Using that code the code continues to run and display output - in this case it triggers a recurring FAULT on that same line and does not progress. I only tested before on other induced memory faults, which generally stepped over and kept running.

I wanted to get that "#include "debug_t3.h"" cleaned up and usable as a Debug helper library. It allows the USB system ( thanks to the borrowed PJRC code ) to keep running. Some faults can be jumped over - but that 'hard_fault' cannot be. However the code as I have it at least allows a documented crash. It has a counter for FAULTS seen and stops after 5 entries, so in this case the recurring fault displays that info 5 times then halts.

I may have made some local updates to the code posted above early June before I moved on as I didn't get any feedback to keep me interested.

In posted code it may still be DebSet() instead of t3deb( 0,1 );
/* FOR USE:: 1st Param is array index 0-9 , 2nd Param is uint32_t value.
t3deb( 0,1 );
t3deb( 9,__LINE__ );
Use as appropriate to track location or data

*/

It seems my last change was to make a macro called t3deb() to always include the LINE# like :: #define t3deb( a, b ) DebSet( a, b, __LINE__ );
This removed the need to always update the line. I also put a call counter on each update so the LAST item called would have the highest index number so where it dies would be obvious … the C#2 value.
 
Status
Not open for further replies.
Back
Top