SAMD10 and I2C error

Go To Last Post
45 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

When initiating the I2C driver the compiler adds ~10KB to the program memory.

After checking the "i2c_master_init​" function i found that the error occurs when SERCOMx->I2CM->BAUD.reg is written to.

Any fix for this issue would be appreciated. 

 

 

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jan_sqgaard wrote:
the error

What error??

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Writing to a 32bit register should not use 10 kilobytes of the program memory. I assume that is an error.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jan_sqgaard wrote:
Writing to a 32bit register should not use 10 kilobytes of the program memory.
This is why God invented the NM utility. There is a presumably an arm(something)-nm utility in the installation. Use it both before and after the 10K have appeared. What has actually been added to the binary? Presumably it is a "knock on" effect. As soon as you use i2c_master_init() it relies on 3 other functions that each rely on 5 others that each rely of 3/4/5 others and so on and pretty soon you have +10K. NM will reveal what all those added routines are.

 

(ASF will never win awards for the efficiency of the code!!)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jan_sqgaard wrote:
Writing to a 32bit register should not use 10 kilobytes of the program memory.

But you've added the entire I2C driver - it does a lot more than just write to one register!

 

Have you enabled the option(s) to remove unused code?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
What has actually been added to the binary?

You can also see what has been added as source ...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

awneil wrote:
it does a lot more than just write to one register!

Described in 47 pages here: http://www.atmel.com/Images/Atmel-42117-SAM-I2C-Bus-Driver-Sercom-I2C_ApplicationNote_AT03250.pdf

 

Sadly, none of those pages gives any indication of the resource usage.

 

<rolls eyes>

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes, I've added a library. If the library does not write to the baud rate register it does not use the memory.
This happens in the example project for the D11 as well so I'm certain it is the I2C driver.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jan_sqgaard wrote:
Yes, I've added a library. If the library does not write to the baud rate register it does not use the memory. This happens in the example project for the D11 as well so I'm certain it is the I2C driver.
The only reason that "unused functions" are removed from the link are because the code is compiled with -ffunction-sections and is linked with -gc-sections so have you disabled either of these?

 

Anyway NM is the way to find out what "made it through". If you find functions in the nm output that you are sure are not invoked anywhere and -ffunction-sections and -gc-sections are still enabled then there is, indeed, something very "odd" going on.

 

(back in the day, before function-sections / gc-sections proper "libraries" were employed where each function was built into a separate .c file (and hence .o file in the .a archive). The linker would then only pull in and link the .o files that were actually referenced but these days function-sections has removed the need for such shenanigans).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm not sure what unused functions have to do with writing to a physical register (or at least a pointer to a pointer to the register). This happens even when I create an example project and presses compile without adding or subtracting code. Debugging ASF functions further seems counterproductive since the code has to compile on multiple systems. I had hoped this is a known problem and some workaround is available. If not, it makes more sense to configure the sercom module manually and completely bypass the library.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The point is that as soon as you say "I want i2c_master_init()" you are probably saying "and all the other functions associated with it". Now if it is really the case that you call that one function in isolation and it has just one line of C writing to an SFR then, yes, even if 200 other functions were pulled in and compiled the use of -ffunction-sections should have put them into 200 different memory sections then -gc-sections at link time says "discard any sections that have no referral" so 199 functions should then be discarded because they aren't referenced. But I suspect i2c_master_init() calls something else that calls something else and so on and maybe 50 of the 200 are then referenced so while some of the code will be discarded it won't all be.

 

That is why I suggested NM. It will tell you what IS in the build. You can then grep the source for references to anything that has been included to understand WHY it has been included. As I say if you really find that i2c_master_init() references nothing else yet 50 functions totalling 10K are built in it would imply that -ffunction-sections and -gc-sections are not doing their job.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I think you misunderstand me. I ended up copying the source code line by line. I understand how the compiler works. I understand how that particular function works. There is one line in a function called by the init function that writes to one of the registers that control the baudrate. But this does not matter as it is integrated functions and it makes little sense modifying them.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So show the one line of C that adds 10K.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Also the versions of ASF, Atmel Studio, and GCC.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Sure,

i2c_module->BAUD.reg = SERCOM_I2CM_BAUD_BAUD(tmp_baud) | SERCOM_I2CM_BAUD_HSBAUD(tmp_baud_hs) | SERCOM_I2CM_BAUD_HSBAUDLOW(tmp_baudlow_hs); 

 

from i2c_master.c line 201 in function _i2c_master_set_config. i2c_module is a pointer to SERCOMx, which is a union of structs full of pointers to registers. It is not this line alone, you will need at least some of the previous lines, but without this... and no, the latter code does not matter. 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

the latest, ASF 3.34.1, AS 7.0.1417, ARM/GNU C Compiler ​6.2.1

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

worth a try, if it does not work I guess I have to configure the registers manually tomorrow. Fingers crossed.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jan_sqgaard wrote:
 I've added a library.

To what did you add it?

 

Quote:
This happens in the (sic) example project for the D11 as well

Which example?

 

AS shows me 60 examples for D11

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I added the i2c master library to a larger code I am writing.
I assumed it was implied the example project was i2c master, either polled or callback

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jan_sqgaard wrote:
 I've added a library.

awneil wrote:
To what did you add it?

And which "library" did you add?

 

AS gives me:

  1. SERCOM I2C - Master Mode I2C (driver) - with options of 'callback' and 'polled'
  2. SERCOM I2C - Master Mode I2C (driver) - with options of 'callback' and 'polled'

 

EDIT

 

oops - one of those should say "slave"

 

blush

Last Edited: Wed. Aug 30, 2017 - 10:05 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

i added the i2c master library.

polled or callback does not matter... well the callback uses some hundred bytes more.

Not sure why you have duplicates of that example. 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jan_sqgaard wrote:
Not sure why you have duplicates of that example. 

Sorry, that's a typo - one should say "Slave".

 

blush

 

AS gives me only 2 examples for D10, so I chose LED_TOGGLE.

Building this alone gives

 

	Program Memory Usage 	:	2624 bytes   16.0 % Full
	Data Memory Usage 	:	1088 bytes   26.6 % Full

 

Just adding the I2C Master, polled - not using it - gives

	Program Memory Usage 	:	2624 bytes   16.0 % Full
	Data Memory Usage 	:	1088 bytes   26.6 % Full

ie, exactly the same.

Looking at the map file shows

Discarded input sections

:
:

 .text._i2c_master_wait_for_sync
                0x00000000        0xc src/ASF/sam0/drivers/sercom/i2c/i2c_sam0/i2c_master.o
 .text.i2c_master_init
                0x00000000      0x2c8 src/ASF/sam0/drivers/sercom/i2c/i2c_sam0/i2c_master.o
 .text.i2c_master_reset
                0x00000000       0x2c src/ASF/sam0/drivers/sercom/i2c/i2c_sam0/i2c_master.o
 .text._i2c_master_address_response
                0x00000000       0x30 src/ASF/sam0/drivers/sercom/i2c/i2c_sam0/i2c_master.o
 .text._i2c_master_wait_for_bus
                0x00000000       0x2c src/ASF/sam0/drivers/sercom/i2c/i2c_sam0/i2c_master.o
 .text._i2c_master_send_hs_master_code
                0x00000000       0x20 src/ASF/sam0/drivers/sercom/i2c/i2c_sam0/i2c_master.o
 .text._i2c_master_read_packet
                0x00000000      0x1a0 src/ASF/sam0/drivers/sercom/i2c/i2c_sam0/i2c_master.o
 .text._i2c_master_write_packet
                0x00000000       0xf4 src/ASF/sam0/drivers/sercom/i2c/i2c_sam0/i2c_master.o
 .text.i2c_master_read_packet_wait
                0x00000000       0x14 src/ASF/sam0/drivers/sercom/i2c/i2c_sam0/i2c_master.o
 .text.i2c_master_read_packet_wait_no_stop
                0x00000000       0x14 src/ASF/sam0/drivers/sercom/i2c/i2c_sam0/i2c_master.o
 .text.i2c_master_read_packet_wait_no_nack
                0x00000000       0x14 src/ASF/sam0/drivers/sercom/i2c/i2c_sam0/i2c_master.o
 .text.i2c_master_write_packet_wait
                0x00000000       0x14 src/ASF/sam0/drivers/sercom/i2c/i2c_sam0/i2c_master.o
 .text.i2c_master_write_packet_wait_no_stop
                0x00000000       0x14 src/ASF/sam0/drivers/sercom/i2c/i2c_sam0/i2c_master.o
 .text.i2c_master_send_stop
                0x00000000       0x18 src/ASF/sam0/drivers/sercom/i2c/i2c_sam0/i2c_master.o
 .text.i2c_master_send_nack
                0x00000000       0x18 src/ASF/sam0/drivers/sercom/i2c/i2c_sam0/i2c_master.o
 .text.i2c_master_read_byte
                0x00000000       0x30 src/ASF/sam0/drivers/sercom/i2c/i2c_sam0/i2c_master.o
 .text.i2c_master_write_byte
                0x00000000       0x20 src/ASF/sam0/drivers/sercom/i2c/i2c_sam0/i2c_master.o
 
 :
 :

ie, the linker has detected that the I2C functions are not being used and, therefore, has not included them - which  is why the memory usage hasn't changed.

 

Adding a call to i2c_master_init() in main() gives:

	Program Memory Usage 	:	10268 bytes   62.7 % Full
	Data Memory Usage 	:	1096 bytes   26.8 % Full

so the increase is 7644 - not 10K (though still quite a lot)

 

The I2C functions are no longer in the 'Discarded' section, and do now appear in the executable memory map.

 

Disabling the line

#if 0
		i2c_module->BAUD.reg = SERCOM_I2CM_BAUD_BAUD(tmp_baud) |
			SERCOM_I2CM_BAUD_HSBAUD(tmp_baud_hs) | SERCOM_I2CM_BAUD_HSBAUDLOW(tmp_baudlow_hs);
#endif

makes no difference.

 

 

So I think the conclusion is as originally stated: it is not the writing to the register which causes the code increase - it is the linker deciding whether or not the code has actually been used and, thus, whether or not to include it in the image.

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

As Cliff said earlier, ASF is not noted for its economy of code size.

 

Generally, that's not really an issue on ARM parts - but it probably is on the D10 with their very small memory sizes.

 

ISTR this has been mentioned before on the D1x forum - it really doesn't seem to have been thought-through properly.

 

As I said earlier, it is rather poor that Atmel don't state the memory requirements of these drivers.

 

If you can't fit with ASF, I guess your choices are:

 

  1. Ignore it altogether, and just go straight to direct register access;
     
  2. "prototype" with ASF to get stuff working, then pull out just the bits you actually need.

 

 

The 'nm' command that Cliff mentioned is in the Studio installation, under 'toolchain', as  arm-none-eabi-nm.exe

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It really does not matter, but i decided to copy the init function line by line until it used excessive memory. that happened at line 201. It also happened if i instead wrote: i2c_module->BAUD.reg = 1; so i concluded that the compiler were at fault (i2c_module is a pointer to a union of structs accessing the hardware registers).

As for this just being a part of the driver... no. the function of the init is similar to the init of all other SERCOM functions (they waste memory by the hunreds not thousands), after all it is the same configuration registers that has to be set (and no, it do it in a convoluted way, but it is basically what it does).

You are right, i will have to configure the registers manually. 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hmmm - I did 

arm-none-eabi-nm -S -n

on the two ELF files (using & not using the I2C), and the size of all the I2C stuff seems to be just 1036 bytes.

 

The outputs are attached, if anyone wants to check my working ...

 

 

 

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Not exactly easy to "diff" XLS. Just plain text might have been easier.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The XLS shows my working to get "just 1036 bytes"

 

But here are the "raw" text files - including the Map files

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It seems disabling the lines: 195 to 203 and assigning a fixed value to the register fixes the issue (yes, it has to be fixed):

// if (tmp_baud > 255 || tmp_baud < 0 || tmp_baud_hs > 255 || tmp_baud_hs < 0) {
  /* Baud rate not supported. */
//  tmp_status_code = STATUS_ERR_BAUDRATE_UNAVAILABLE;
// }
// if (tmp_status_code != STATUS_ERR_BAUDRATE_UNAVAILABLE) {
  /* Baud rate acceptable. */
//  i2c_module->BAUD.reg = SERCOM_I2CM_BAUD_BAUD(tmp_baud) |
//   SERCOM_I2CM_BAUD_HSBAUD(tmp_baud_hs) | SERCOM_I2CM_BAUD_HSBAUDLOW(tmp_baudlow_hs);
// }
i2c_module->BAUD.reg = 0x12345678;

This is not a solution but it allows me to move on for now (and hope atmel fixes whatever goes wrong). 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

That looks like it's calculating the baud rate at run time - which is probably something you'd want to optimise out anyhow.

 

Still seems a bit surprising that it adds so much code: it looks to be all integer maths - but maybe they've slipped a float in there somewhere ... ?

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Aha!

 

In the map file, the following are extra when the I2C is used:

 .text          0x00000e14      0x620 c:/program files (x86)/atmel/studio/7.0/7.0/toolchain/arm/arm-gnu-toolchain/bin/../lib/gcc/arm-none-eabi/6.2.1/thumb/v6-m\libgcc.a(adddf3.o)
                0x00000e14                __aeabi_dadd
 .text          0x00001434      0x668 c:/program files (x86)/atmel/studio/7.0/7.0/toolchain/arm/arm-gnu-toolchain/bin/../lib/gcc/arm-none-eabi/6.2.1/thumb/v6-m\libgcc.a(divdf3.o)
                0x00001434                __aeabi_ddiv
 .text          0x00001a9c      0x500 c:/program files (x86)/atmel/studio/7.0/7.0/toolchain/arm/arm-gnu-toolchain/bin/../lib/gcc/arm-none-eabi/6.2.1/thumb/v6-m\libgcc.a(muldf3.o)
                0x00001a9c                __aeabi_dmul
 .text          0x00001f9c      0x62c c:/program files (x86)/atmel/studio/7.0/7.0/toolchain/arm/arm-gnu-toolchain/bin/../lib/gcc/arm-none-eabi/6.2.1/thumb/v6-m\libgcc.a(subdf3.o)
                0x00001f9c                __aeabi_dsub
 .text          0x000025c8       0x68 c:/program files (x86)/atmel/studio/7.0/7.0/toolchain/arm/arm-gnu-toolchain/bin/../lib/gcc/arm-none-eabi/6.2.1/thumb/v6-m\libgcc.a(fixdfsi.o)
                0x000025c8                __aeabi_d2iz
 .text          0x00002630       0x70 c:/program files (x86)/atmel/studio/7.0/7.0/toolchain/arm/arm-gnu-toolchain/bin/../lib/gcc/arm-none-eabi/6.2.1/thumb/v6-m\libgcc.a(floatunsidf.o)
                0x00002630                __aeabi_ui2d
 .text          0x000026a0       0x3c c:/program files (x86)/atmel/studio/7.0/7.0/toolchain/arm/arm-gnu-toolchain/bin/../lib/gcc/arm-none-eabi/6.2.1/thumb/v6-m\libgcc.a(_clzsi2.o)
                0x000026a0                __clzsi2
 

and

 .rodata        0x00002778       0x40 c:/program files (x86)/atmel/studio/7.0/7.0/toolchain/arm/arm-gnu-toolchain/bin/../lib/gcc/arm-none-eabi/6.2.1/thumb/v6-m\libgcc.a(divdf3.o)
 .rodata        0x000027b8       0x40 c:/program files (x86)/atmel/studio/7.0/7.0/toolchain/arm/arm-gnu-toolchain/bin/../lib/gcc/arm-none-eabi/6.2.1/thumb/v6-m\libgcc.a(muldf3.o)

which comes to 6472 bytes.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jan_sqgaard wrote:
hope atmel fixes whatever goes wrong

You'd better report it, then.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

awneil wrote:
maybe they've slipped a float in there somewhere ... ?

 

and here it is:

	/* For High speed mode, set the SCL ratio of high:low to 1:2. */
	if (config->transfer_speed == I2C_MASTER_SPEED_HIGH_SPEED) {
		tmp_baudlow_hs = (int32_t)((fgclk * 2.0) / (3.0 * fscl_hs) - 1);
		if (tmp_baudlow_hs) {
			tmp_baud_hs = (int32_t)(fgclk / fscl_hs) - 2 - tmp_baudlow_hs;
		} else {
			tmp_baud_hs = (int32_t)(div_ceil(fgclk, 2 * fscl_hs)) - 1;
		}
	}

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

How I wish I had suggested diffing "nm" output in post #4...

 

 

which shows that this added __aeabi_dadd (0x620 bytes),  __aeabi_ddiv (0x628 bytes) etc.

 

So if the original suggestion of using nm had been followed this thread would have been solved in five minutes. Ho hum.

 

PS tip for the future - addresses don't matter, sizes do so nm with --size-sort would have produced output that was even easier to compare.

Last Edited: Thu. Aug 31, 2017 - 12:57 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I was diffing the map files, but initially missed the added __aeabi_... stuff.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You are right, i should have checked it with nm earlier. I do not understand why that is such a problem, floating point numbers is not that difficult to operate with (a few extra operations for each instructions at most).

I cannot see any better solution than discarding the calculation and manually setting the register?

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jan_sqgaard wrote:
You are right, i should have checked it with nm earlier.

Or, as I did, just checked the map files. They are generated as a standard part of the build anyhow.

 

I do not understand why that is such a problem, floating point numbers is not that difficult to operate with

Seriously?!?!

 

Doing FP on a CPU with no FPU is a significant burden - the ~6K seen here should be no surprise at all.

 

What this does mean, of course, is that any other FP operations you may want in your own code are essentially "free".

 

(a few extra operations for each instructions at most).

That appears to be the attitude of the ASF developers - so you can hardly criticise them for that!

 

 

I cannot see any better solution than discarding the calculation and manually setting the register?

It's the runtime calculation that needs removing.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

awneil wrote:
the ~6K seen here should be no surprise at all.
I don't know - avr-gcc does the basic operations (+-*/) in about 700 bytes. I know Cortex may be "larger" but it's almost 10 times larger. Suggests their soft-FP may be quite sub-optimal.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
 avr-gcc does the basic operations (+-*/) in about 700 bytes

the ones here seem to be using about 0x600 bytes for each operator!

 

surprise

 

Suggests their soft-FP may be quite sub-optimal.

It does, doesn't it.

 

I wonder if the "nano" library would help ... ?

 

https://github.com/32bitmicro/ne...

 

But now I think this should be in the Cortex forums ... 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm sorry, i'm better at hardware design (give me an FPGA any day), but an addition with two 24.8 floating point numbers should not be much more than a shift and an addition. A multiplication should be a multiplication and an addition. That is an significant increase in execution time if you are running a linear solver or similar algorithms, but except for division (and multiplication if the IC has no multiplier) it should be less than 10 operations for each. 700 bytes is plausible, that is less than 200 operations total, but yes, 6K is a surprise, you are not going to tell me old AVRs are not capable of calculating with floats.

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi All.

 

  I have the same issue. The only solution that I have is putting the temporally VAR tmp_baud = 5; after calculating the value manually, and commenting the line next line the driver works fine:

/*tmp_baud = (int32_t)(div_ceil(
            fgclk - fscl * (10 + (fgclk * 0.000000001)* trise), 2 * fscl));*/

 

Best Regards.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Again.

 

  Is there any idea if the ASF driver will be updated?

 

Best Regards.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Not unless one of you reports it to Atmel/Microchip ... 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I want to report this Bug, How can I report to Atmel/microchip directly??.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

This has just come up again - in the SAM "Community": 

 

https://community.atmel.com/forum/asf-i2c-library-samd-does-floating-point-causing-bloat