Create SDRAM array to match size of loaded file


#1

Is it possible for code in the Init Code section to create an SDRAM array of exactly the right size to load a binary file?

I've worked out how to use FatFS' f_size function to get the size of the file, but it appears I can't then use that size as the length of the SDRAM array, since it can't be a constant.

Is there any way around this?

Could the SDRAM array be made arbitrarily large initially, then reduced in size after the binary data had been loaded into it, perhaps?

a|x


#2

simple answer , or not so simple answer : :slight_smile:

a) simple...
arrays are fixed sized determined at compile/link time, so cannot be resized.

you might want to read something on C memory allocation e.g. a random google find here. essentially we use stack (and static) memory allocation, not heap (malloc/free)

2) bit more complicated...
you could implement your own 'allocation' mechanism within a large array, to allocate bits of it out to for various uses.
(I mentioned this before, when I said, you could have an array that could store N samples... such that the length of the array determined the max size of ALL samples rather than 1)

3) complex
ChibiOS does support dynamic memory allocation BUT the firmware needs to be configured in certain ways, and also you'd have to check out how to ensure it allocate from sdram. AND using dynamic allocation is usually avoided for audio applications, as memory management is not free :wink:
(hmm, there might also be some nasty side effects, to do with dynamic patch loading... in fact generally its possibly more complex that it initially seems)

as i think was mentioned in previous threads, you have to bare in mind, that with Axoloti , the #1 priority is the audio is not disturbed due to things like memory allocation... so the general model is to know up front how much memory you need to allocate (or rather distribute, as you might as well allocate it all) and avoid things (like) loading up data that there is not enough room for.

if your finding that you need to loads lots of data, and its of different sizes, and you want to optimise memory usage , such that you can load as much as possible option 2 is the solution. (imho) ... this is pretty trivial IF it only happens at start up... doing it on the fly and deallocating memory etc, becomes non-trivial quickly (you'll start to learn quickly about memory allocation strategies :wink: )

for 2&3 you may need to have a modified version of the filesystem objects, but again, not too complicated.


#3

Thanks as ever for the detailed explanation.

Giving this a bit more thought, I think I can just get away with allocating 32kB (the maximum expected size), then keep the number of bytes actually used in a variable.

I can then use this value to make sure that any lookups into the data in SDRAM don't overrun the number of bytes loaded from the file.

I was already thinking of trying to also store some meta-information about the loaded file in a second, much smaller array on SDRAM, so I can just add the number of bytes loaded to that, along with the other information.

a|x


#4

Yes, there is plenty (8MB) space in sdram that freeing up something like 32kB is not worth the trouble. I'd use regular sram to store more frequently used things like length, sdram is slower than sram.


#5

OK. Can I allocate an array in SDRAM and one in SRAM in the same object, and access them both from another object, in the same way as you can access a single SRAM/SDRAM array/table using the table read objects?

a|x


#6

lol, and slowly you descend into the dark realms of memory management :smiling_imp:

but seriously, its not difficult if you don't need to reallocate memory.
just a set of pointers to whats allocated where, and a count of how much has been allocated of your total pool.

the thing not quite clear to me is you refer to loading one file, so you need to allocate enough for the maximum that can be (unless you start loading in blocks..) but how are you going to use the 'left over'? what ever you want to use this, will also have to use your memory allocation object.

or are you just worried about not overrunning the data in the file?

btw: I don't think table load etc keep number of bytes read, but its trivial to add if it doesnt.
(I've been thinking for a while, the objects should have this as an output)


#7

Where the tortured souls of the damned cry out in eternal torment...

I'm massively over-complicating things, I think (as I tend to do, then get discouraged when I can't work out how to do the over-complicated thing I didn't need to do, in the first place).

What I'm trying to do is basically this:

It's a binary file-loader, able to load one or (optionally) two files. In almost every case, the data I want to load comes in the form or either one or two parts, of exactly 16384 bytes (16kB).

Since it's such a small amount of data, it seems most sensible now to just allocate 32768 bytes (32kB), and either fill half or all of it with loaded data.

I might add file-size checking though, so that files larger than 16kB won't load. The files in question are ROM-dumps from vintage toys, and the ROM size was limited to 16kB, anyway, so maybe it would make most sense to just stop any files larger than that from being loaded, on the assumption that if they're larger, there must be something wrong with them, or they're the wrong type of file altogether.

a|x


#8

This seems to work:

// Allocate array in SDRAM
static int8_t _array[attr_poly][LENGTH] __attribute__ ((section (".sdram")));
// Set pointer to SDRAM array
array = &_array[parent -> polyIndex][0];

// Zero array
int i;
for(i = 0; i < LENGTH; i++)
	array[i] = 0;

// Handles to 2 binary files
FIL vsm1;
FIL vsm2;

// Length of vsm files
uint32_t vsm1Len;
uint32_t vsm2Len;

// FatFS error message
FRESULT err;
UINT bytes_read;

////////////////////////////////////
// Attempt to load first VSM file //
////////////////////////////////////

err = f_open(&vsm1, "attr_vsm1", FA_READ | FA_OPEN_EXISTING);

// Exit if 1st VSM file not found
if(err != FR_OK) {
	LogTextMessage("Unable to open first VSM file, aborting load");
	return;
}

// Get length of VSM1 file
vsm1Len = f_size(&vsm1);

// Check file exactly 16kB long, abort if not
if(vsm1Len != 16384) {
	LogTextMessage("File is wrong length, aborting load");
	return;
}

// Load file into SDRAM array
int rem_sz = sizeof(_array[0]);
int offset = 0;

while (rem_sz > 0) {
	if (rem_sz > sizeof(fbuff)) {
		err = f_read(&vsm1, fbuff, sizeof(fbuff), &bytes_read);
		if (bytes_read == 0)
			break;
		memcpy((char *)(&_array[0]) + offset,(char *)fbuff,bytes_read);
		rem_sz -= bytes_read;
		offset += bytes_read;
	} else {
		err = f_read(&vsm1, fbuff, rem_sz, &bytes_read);
		memcpy((char *)(&_array[0]) + offset,(char *)fbuff,bytes_read);
		rem_sz = 0;
	}
}

if (err != FR_OK) {
	LogTextMessage("Read failed\n");
	return; 
}

err = f_close(&vsm1);
if (err != FR_OK) {
	LogTextMessage("Close failed\n");
	return; 
}

/////////////////////////////////////
// Attempt to load second VSM file //
/////////////////////////////////////

// Attempt to open second VSM file and save feedback
err = f_open(&vsm2, "attr_vsm2", FA_READ | FA_OPEN_EXISTING);

// Exit if VSM2 file not found
if(err != FR_OK) {
	LogTextMessage("Second ROM part not found");
	return;
}

// Get length of VSM2 file
vsm2Len = f_size(&vsm2);

// Abort loading if file to large for remaining space
if(vsm2Len > rem_sz) {
	LogTextMessage("Selected VSM2 file too large for remaining memory, aborting load");
	return;
}

bytes_read = 0;

// Load file into SDRAM array, starting at previous offset
while (rem_sz > 0) {
	if (rem_sz > sizeof(fbuff)) {
		err = f_read(&vsm2, fbuff, sizeof(fbuff), &bytes_read);
		if (bytes_read == 0)
			break;
		memcpy((char *)(&_array[0]) + offset,(char *)fbuff,bytes_read);
		rem_sz -= bytes_read;
		offset += bytes_read;
	} else {
		err = f_read(&vsm2, fbuff, rem_sz, &bytes_read);
		memcpy((char *)(&_array[0]) + offset,(char *)fbuff,bytes_read);
		rem_sz = 0;
	}
}

if (err != FR_OK) {
	LogTextMessage("Read failed\n");
	return; 
}

err = f_close(&vsm2);
if (err != FR_OK) {
	LogTextMessage("Close failed\n");
	return; 
}

I should probably roll the file-loading into a function, for neatness, rather than just repeating it twice, but at least it works.

a|x


#9

There's already a variable named "bytes_read" that is set to the last byte read, when the end of the file has been reached and reading stops, so it would merely be a question of creating a positive integer outlet, and setting it to the value of this variable.

a|x


#10

yup, I know, Ive just been meaning to add it :slight_smile:

the only slight hesitation is we don't actually have much that can do access at the byte level, most is done as a % of the file... so I wasn't sure how useful it was, but probably very useful for custom objects.
I think we could probably also do with a boolean flag, where 'true' = valid/sucessfully read.
so patches can know if the file was successfully loaded or not. (as it may not be on the sdcard)


#11

That's true.

Having said that, the existing 16b SDRAM file-loader does dump error messages to the Console when it fails (albeit not very readable ones).

Table-read objects seem to simply output 0 if the table they're associated with is empty, because the allocated array in SDRAM is zeroed before the read is attempted, so nothing catastrophic happens if a file isn't loaded (except obviously the patch won't work as expected).

a|x