How is programDataPageAddress calculated?

Just playing around with the external flash and came to the situation where I hit a performance wall :face_with_hand_over_mouth:
No issue I think but I wanted to know how the programDataPage address is created. Obviously it is aligned to a page boundary, but is it maybe aligned to something more that that? E.g. aligned to a 4k, 8k or the like boundary?
I am asking because if there is such, data can be placed in the external flash in a way that for critical sections the address calculation and the seekData can be done more efficiently.
Say if I know that an address x + y will not cross the alignment of x then I can send the address >> 16 before adding y. And while waiting I can do additional address calculations.

I know it is very theoretical at the moment but maybe worth a thougth. :sweat_smile:

At least in my case when reading the map from external flash, there is quite some performance hit.

The 16 bit programDataPage is added to the 24 bit address. This is done in assembly to save a few cycles (the adding part takes 6 cycles) there are no page boundaries to worry about for reading.

To improve performance, try to read data in sequence as much as possible.

how do you read the map and how often do you need to read the map? maybe if I saw the code I could give some tip.

1 Like

It is a typical raycaster. Each ray (64 of them) marches over the map with it’s stepx/stepy value. So sequencial reading is hard to implement.
The code is a mess right now and not in a shape to be shared. There will be too much noise.

@Mr.Blinky can you give me a quick hint how to check at which frequency the SPI runs? Today I thought I waited for around 18 to 20 cycles but the SPI transfer was not ok. Only a wait() could fix the issue.

The SPI configuration is part of bootSPI of Arduboy2 core. So unless you ommit arduboy.boot() or arduboy.begin() it’s running at 8MHz

To see how many cycles code takes. you can add some assembly labels to your code like adding a line:

asm volatile("debug:\n");

Then when you reverse the .elf file to a dissasembly you can search for <debug> and do some cycle counting.

2 Likes

This is starting to sound like a hard drive. :P

When it is, let me know.

I’d like to have a skim over it, see if I can find any improvements and then wait patiently for my feature improvements to be accepted and my style change suggestions to be rejected. :P

omit’, one ‘m’.

(Normally I wouldn’t bother bringing it up,
but you use it a lot and I’d feel more mean if I didn’t tell you.)


By the way, migrated because this has nothing to do with ‘Using Python’.

Sure I will and I will also appreciate all your comments. I took this project for relaxation from any rule that I am usually bound to. So … I just saw I warned you already in the past:

So after all this time, I might additionally request you to bring your sunglasses because I don’t want you to get hurt.

Well it’s basically a solid state drive without the controller and cache :wink:

Thanks for letting me know. I have a bad habit of using double m’s maybe because of enjoying m&m’s too much :stuck_out_tongue:

1 Like

Btw, I thought a little bit about my initial request. Actually I can align the address myself. Say I have the following scenario:

map size is 4096 bytes

So in order to be able to send flashaddress[2] and do something else (e.g. map position calculation) while flashaddress goes out I could just put my leveldata at 4096 byte aligned addresses in flash. That way the upper byte of the flash address will never change and I can send it right away and do something else while the SPI block clocks it out.

Having said that, for repetitive random accesses to the flash it might be useful to have a seekData function that takes an address that does not need to be added to programDataPage. So the user can do all this calculation itself where it is not critical and then just seek to it.

damn it, no I can’t align the address myself because it is the sum of my local offset from the binary file I create and a random address aligned to the page boundary which is created by the flash-builder script…

Is there a way to tell the flash-builder script about any alignment preferences?

Ok I found the issue. I added like 50 nops without any effect, then read the datasheet about the SPI and found it is actually required to read the SPSR before accessing the SPDR. So simple nops won’t help. A single read of SPSR did the trick.
Seems like clearing the SPIF flag either by reading it or executing an interrupt routine is required.

2 Likes

Good you’ve resolved the issue. I know that in certain cases there can be odd behaviour that requires SPSR to be read.

2 Likes

Thanks for the macro. I used it a lot recently but in some situations I wanted to add it multiple times which cause compilation errors as the label was multiply defined. You probably have a solution for this :wink: but in case you are interested, here is what I came up with:

asm volatile("lookAtMe%=:\n"::"r" (__LINE__));

It adds the line number to the label name, so all of them will be unique and you can spill them out in your code.

It’s a statement, not a macro.

I’m going to cut in ahead of time and say that this should work (and these actually are macros):

#define STRINGIFY(x) #x
#define STRING(x) STRINGIFY(x)
#define LINE_LABEL() "Line_" STRING(__LINE__) ":\n"
#define ASM_LABEL() asm volatile(LINE_LABEL())

Then you can use it as:

ASM_LABEL();

And you’ll get a label like Line_50:.


Edit:
You may also be able to do something like:

#define STRINGIFY(x) #x
#define STRING(x) STRINGIFY(x)
#define LINE_LABEL() __FUNCTION__ "_" STRING(__LINE__) ":\n"
#define ASM_LABEL() asm volatile(LINE_LABEL())

Which would give a label like main_50.

Yes. My fault, I wrapped it in a macro in my code and somehow my finger and brain did something different.

I like your macro. What about passing the label name as parameter to ASM_LABEL?
Like

ASM_LABEL(lookAtMe);

That would be awesome.

Wouldn’t that defeat the point of generating a unique name from the function name and line number?

You can if you want of course, e.g.:

#define STRINGIFY(x) #x
#define STRING(x) STRINGIFY(x)
#define LINE_LABEL(name) #name "_" __FUNCTION__ "_" STRING(__LINE__) ":\n"
#define ASM_LABEL(name) asm volatile(LINE_LABEL(name))

Then ASM_LABEL(lookAtMe) would give lookAtMe_main_50.

Or you could do:

#define STRINGIFY(x) #x
#define STRING(x) STRINGIFY(x)
#define LINE_LABEL(name) name "_" __FUNCTION__ "_" STRING(__LINE__) ":\n"
#define ASM_LABEL(name) asm volatile(LINE_LABEL(name))

Which would need to be used as ASM_LABEL("lookAtMe") instead.

If you want I can explain how it all works.

1 Like

In case of multiple usage, I would add a number or name to the label:

asm volatile("debug1:\n");
asm volatile("debug2:\n");

Nice to know that’s possible. For me it’s easier to pick and remember my own simple number rather than the actual line number which probably uses multiple digits.

1 Like

Depends on the use-case. I use it to find these in the disassembly of the .elf file in order to see how the code was translated :crazy_face:
So if I can additionally define the name, that would be great and the line number makes it unique. For instance if I use your macro in another macros within the same scope.

Thanks. Although I know it already it might be good to explain here for other users that read this. I was just too lazy to do it myself because the preprocessor always gives me the creeps and you’ve been already in a flow :blush:.

I don’t see what advantage choosing a name has over just using the function name.

Unless you’ve got ridiculously large functions and you’re going to have about 10 of these per function,
in which case the real question is “why are your functions that big?”.

Macros have no concept of scope, they just poison the whole program. :P

(Unless you #undef them after you’ve used them. Sort of.
Not really scoping, but stopping the poison from spreading.)

If you already know then I probably won’t bother unless someone specifically asks.

That’s a good thing, macros are bad for your health.


(P.S. In case I didn’t make it clear enough: macros are evil, avoid them. :P)

1 Like

Your points are all valid. Just sometimes I cannot resist. Btw, adding the function name to the label does not work on my side. I think the FUNCTION is expaned into a char array, so you cannot stringify it I guess. At least I did not find a way other than giving the macro a new parameter but that would make the thing odd.