Assigning a float kills my program

Status
Not open for further replies.

Projectitis

Well-known member
Hi all,

I'm getting a very weird bug on T_3.6 trying to copy a byte from an array in PROGMEM to a struct in ram.

My program just simply stops executing at a certain point (at least, my Serial debug commands stop coming).
The code is relatively big, so I'll explain what I can below. You can grab the code from the repo here: https://github.com/projectitis/libxm_t3
  • Add libxm_t3 as a library
  • Run the sketch in the examples folder
The code is parsing an xm audio module in flash mem. It's stepping through a bunch of samples, but it is choking in one particular place when it tries to grab the volume (1 byte) of a sample.

Some background:

This macro reads one uint8_t from the module in flash (moddata is a const char* pointing to the module in flash mem), with overflow protection:
Code:
#define READ_U8(offset) (((offset) < moddata_length) ? (*(uint8_t*)(moddata + (offset))) : 0)

The other thing you need to know is that the 'sample' struct contains volume as a float.
Code:
float volume;

Here is the code in question (load.c, line 393):
Code:
sample->volume = (float)READ_U8(offset + 12) / (float)0x40;

There are 7 samples in the module.
  • For sample 0, the uint8_t is 64, the float is 1.0f, and the code works fine
  • For sample 1, the unit8_t is 64, the float is 1.0f, and the code dies trying to assign the float to sample->volume;

Changing the code to this has the exact same result (broken):
Code:
temp_uint8 = READ_U8(offset + 12); // This works ok
temp_float = temp_uint8 / 64.0f; // This works ok
sample->volume = temp_float; // This is where it dies

Changing the code to this (just for testing) works (but doesn't set the actual volume value):
Code:
temp_uint8 = READ_U8(offset + 12);
temp_float = temp_uint8 / 64.0f;
sample->volume = 1.0f;

Changing it to this breaks it again, even though I can verify that the value of temp_float is 1.0f;
Code:
temp_uint8 = READ_U8(offset + 12);
temp_float = temp_uint8 / 64.0f;
sample->volume = 1.0f;
sample->volume = temp_float;

It's important to highlight that when the code dies on sample 1 it has already run exactly the same code with exactly the same values on sample 0.

I thought maybe it had something to do with memory alignment, but we're only reading a single byte here, so that shouldn't be an issue?
In my case the actual memory addresses are as follows:
  • For sample 0, the volume byte is at 56943 in flash mem. This is the one that works
  • For sample 1, the volume byte is at 60180 in flash mem. This is the one that doesn't work

There are two places in the code where this program fails during execution, and both of them involve reading a byte, converting it to a float (with a division) and assigning it to a struct member.

Any help appreciated!
 
Not sure of your syntax :: sample->volume

If sample is the struct - to access the volume element it should be :: sample.volume

Replace above use in this example - and refer if needed to the link for more notes - but that doesn't seem to be what was intended - and explains why 'somewhere' you are writing data and that unintended place finds that discomforting.

http://www.cplusplus.com/doc/tutorial/structures/
The arrow operator (->) is a dereference operator that is used exclusively with pointers to objects that have members. This operator serves to access the member of an object directly from its address. For example, in the example above:

pmovie->title

is, for all purposes, equivalent to:

(*pmovie).title

This would access the value pointed by a hypothetical pointer member called title of the structure object pmovie (which is not the case, since title is not a pointer type).
 
Thanks for the comment, but that's not it unfortunately :) 'sample' is actually a pointer to a struct, so accessing with the arrow is what is needed.
This is a port of code that works perfectly on other systems. I'm just getting it to run on Teensy.

I have made progress though - it seems that sample->volume (which is a float) is not always aligned to a 4-byte boundary.
The default GCC behavior is supposed to be to align struct members to their respective boundaries, but somehow the structs seem to be packed....

  • For sample 0, the address of sample->volume is 536907452, which divides evenly in to 4
  • For sample 1, the address of sample->volume is 536910442, which has remainder of 0.5 (2-byte alignment)

So far, forcing alignment of the volume to 4 bytes hasn't helped (same result):
Code:
struct xm_sample_s {
	...
	float volume __attribute__ ((aligned (4)));
	...
};

I may have to align the entire struct (not just one member). Just testing that now.
 
Last edited:
I did say : "If sample is the struct "

Is that how that resolves? Maybe I'm not reading the linked example text right. Certainly not used my 'c' much in 20 years . . .

maybe:: (*sample).volume

Ref :: Pointer to Structure in C Programming

Way 2 : Using Indirection Operator and Pointer
Accessing Roll Number : (*ptr).roll
Accessing Name : (*ptr).name
 
Last edited:
Updated prior post - copied wrong ref ....

Way 2 : Using Indirection Operator and Pointer
Accessing Roll Number : (*ptr).roll
Accessing Name : (*ptr).name
 
Don't worry, my c isn't quite as rusty :p

I'm pretty sure I've narrowed down the issue.

The author of this library initially wrote it on x86 or another architecture that can access unaligned memory.
The xm module actually contains a great deal of different structs. Normally, as each struct is allocated, the compiler would ensure that the memory for each is aligned on the correct word boundaries.
In this implementation, however, in an effort to optimize for memory efficiency perhaps, the author allocates a single memory pool at the start, and then manually steps through the memory pool, packing the structs in as he goes.
The code is quite elegant, but unfortunately not that portable. I feel a rewrite coming up....
 
Had I read to example #3 ... indeed that is why I used to use the '->'

My first though was memory alignment - but then I also read the problem to involve "array in PROGMEM to a struct in ram" and got distracted when I didn't see it in the sample

32 Bit Teensy's store all that (const) stuff in FLASH - there is no PROGMEM given the flat memory space.

Good luck.
 
The T3.5/3.6 hardware float requires float's be word-aligned.
see https://forum.pjrc.com/threads/49442-Teensy-3-5-FlightSim-Float-problem

EDIT: well, maybe unaligned is ok. I just ran a test on T3.6 with unaligned float seemed to work ok.

I don't recall if the 3.6 has the same issue as the 3.2, but the 3.2 has two memory lines. Normally the processor handles unaligned loads and stores. However, if you are crossing the boundary between the two pages, and use a word or double word item that is not aligned, the chip causes a exception. Normally, you would not see this, because the compiler aligns all int/long/float/double items on the appropriate boundary. But if you are playing games with pointers, you might see it.
 
Yes, all pointers are manually assigned in the memory pool in this code. All the structs being stored were fine, as the size of them was all a factor of 4, but the sample data that is also stored within the memory pool (interleaved with the structs) could have random lengths, thus throwing alignment for structs that followed. I've updated the code now to pad audio sample data to 4-byte multiples, and the code seems to run fine.

So I don't know what assembler this compiles down to, but it seems that storing a float from a variable to an unaligned address causes the fault, but storing a constant float to an unaligned address does not? The address is unaligned by a half-word.
 
Status
Not open for further replies.
Back
Top