It could be useful for finding hub data or buffer addresses if you don't know where in hub the code was (for position independent code). For example, you could do something like:
org 0
entry
add ptrb, ##(@data - @entry)
rdlong a, ptrb[0]
rdlong b, ptrb[1]
...
orgh
data
long initial_a, initial_b
That snippet of code could be loaded off disk into any part of RAM and still work if launched with coginit, whereas traditionally code gets compiled to run from a specific HUB address (usually 0).
That may end up being important for micropython, where the COG code gets loaded at run time and we may not be able to select where in memory it's going.
ptrb could be used as a base register for the beginning of code. This might be useful for position-independent-code (PIC) that could be executed from anywhere in memory.
EDIT: I see that Eric had already posted the same thing. I should have read the next page of comments before posting. PIC is useful for operating systems where there might be multiple applications running at the same time.
It just occurred to me that a cog could use ptrb to modify it's own code in the hub and then restart itself...
Still, wouldn't it be more useful to have 3 arguments in cognew with the third being what goes in PTRB?
What goes into ptrb is controlled by the hardware coginit instruction, which automatically puts the hub address of the code being executed into ptrb. We can't change that without changing the silicon.
Is there an easy way to find the end of program in HUB memory with Fastspin?
Say I want to create a heap that starts at the end of the code...
How can I find that point?
At first I was going to say that there's no way to do that. But actually there is, because the stack comes after the end of the program. So you can get the value in the stack pointer (ptra in P2) and add the size of stack you think you'll need to that.
I'm in the process of porting some bit-banged I2C code from the P1 to the P2. Except the P2 just blasts this stuff out so fast that the I2C bus never even has a chance to swing rail-to-rail. So as a quick-and-dirty fix I wanted to kill some time between bits. Simple, right? So I stuffed some NOPs into the Spin code figuring I'm good to go... except that while NOP is indeed a reserved word in FastSPIN, there apparently isn't actually a NOP in Spin? It throws a "error: Unknown symbol NOP".
Assuming the above assumption is correct, what then is the preferred instruction/method to just to kill a few clock cycles? (I'm really hoping I don't have to call a separate timing routine between bits. Ugh).
BTW: Slowing the clock down isn't an option. I've got a VGA driver running on two COGs that needs to be banging along at 160 mhz, minimum.
Assuming the above assumption is correct, what then is the preferred instruction/method to just to kill a few clock cycles? (I'm really hoping I don't have to call a separate timing routine between bits. Ugh).
BTW: Slowing the clock down isn't an option. I've got a VGA driver running on two COGs that needs to be banging along at 160 mhz, minimum.
There's nothing wrong with showing your intent in code by calling a wait function. If you pass a variable (or constant) into the wait, it's much easier to change each instance in the future by changing one value. Alternative is let's say you have 10 nops. Still too fast? Have to seek out all of those and add more.
The optimizer may even flatten the function call if it's just a waitx being passed a constant.
I was really looking for “quick and dirty” delays, but you guys are right: do it right the first time. So I did. But I was surprised that Spin2 doesnt have a NOP equivalent. Seems like for slash-and-burn coding it would be handy.
I think I've decided that inline assembly is the way to go for things like I2C, where you don't want to dedicate a cog, or maybe even if you do.
Inline assembly may not even be necessary. Some famous computer scientist (I think it was Donald Knuth) once said that "Premature optimization is the root of all evil". For many purposes pure Spin code will work fine, particularly if it's compiled by fastspin to PASM. And Chip's Spin2 interpreter will be significantly faster than Spin1. So I would suggest first coding the driver in pure Spin (or C, or BASIC, or some other convenient high level language) and then only if there is in fact a performance issue worrying about optimizing inner loops with inline assembly. PASM is nice as assembly goes, but it's still not as nice as a high level language .
The other BIG difference between the P2 and the P1is that everyone is going to be able to select their favorite system clock... So, you will want your i2c waitx ##_constant to be defined in terms of the clock frequency.
Yes. The other thing that helps a lot too, is good listing files from the compilers.
Users can scan the created ASM code, and in many cases avoid needing in-line assembly.
If they do need to in-line, they can cut/paste/tune the assembler bit that matters most.
P2 is going to run mixed/multiple languages much more often than other MCUs, due to the isolated nature of each COG.
When using FlexGUI 4.0.5, under Win10 and coding in BASIC for the P2-EVAL rev B board:
@ersmith It looks like there is a GUI issue when attempting to do a search (Edit->Find). Entering any search criteria gives this error:
bad command "tag": must be configure, cget, invoke, instate, state, or identify
bad command "tag": must be configure, cget, invoke, instate, state, or identify
while executing
"$w tag ranges hilite"
(procedure "searchrep'next" line 3)
invoked from within
"searchrep'next .toolbar.compile"
invoked from within
".sr.bn invoke "
invoked from within
".sr.bn instate !disabled { .sr.bn invoke } "
invoked from within
".sr.bn instate pressed { .sr.bn state !pressed; .sr.bn instate !disabled { .sr.bn invoke } } "
(command bound to event)
Is there an easy way to find the end of program in HUB memory with Fastspin?
Say I want to create a heap that starts at the end of the code...
How can I find that point?
If I make a DAT label at the bottom of the main .spin2 file, will that wind up on the bottom of the code?
I've been searching the forum and my question seems to overlap with Ray's above, hence the quote.
In my situation, I've got a bunch of font data in the hub. And *after* that, I've got some more (unrelated) data that is labeled at the beginning, like this:
My question is: How do I get the starting address for the data labeled by Tiles? I can and am manually calculating the address of that data, such that I'm able to access it (and I just store the fixed, calculated address in a cog register for the cog that needs it). But to a PASM noobie like me, it's tempting to think that the assembler at compile time (or less likely, the language at run time) should be able to help with that, you know, to automatically provide the address. I did admittedly blindly try using single, double and triple @ prefixes with the Tiles label with various compile errors, but I didn't learn much from that.
Perhaps I can handle it programmatically by making use of ptrb as mentioned above, but I haven't thought that through yet (sorry). If there's no (convenient) way to get the address, does anyone have an explanation to help me understand the "why" of it. I suppose that there's only so much that the compiler can do at compile time, and run time depends on actual code (that I supply) to do things, so maybe there's no mechanism to "bridge the gap," so to speak. Anyway, it just seems odd that, for example, if I add a third kind of data between my font data and the tiles data, that I'd need to recalculate the Tiles address. But perhaps I'm missing something or not designing things properly, hence I thought it was best to ask (to hopefully gain some insight).
Comments
It could be useful for finding hub data or buffer addresses if you don't know where in hub the code was (for position independent code). For example, you could do something like: That snippet of code could be loaded off disk into any part of RAM and still work if launched with coginit, whereas traditionally code gets compiled to run from a specific HUB address (usually 0).
That may end up being important for micropython, where the COG code gets loaded at run time and we may not be able to select where in memory it's going.
ptrb could be used as a base register for the beginning of code. This might be useful for position-independent-code (PIC) that could be executed from anywhere in memory.
EDIT: I see that Eric had already posted the same thing. I should have read the next page of comments before posting. PIC is useful for operating systems where there might be multiple applications running at the same time.
It just occurred to me that a cog could use ptrb to modify it's own code in the hub and then restart itself...
Still, wouldn't it be more useful to have 3 arguments in cognew with the third being what goes in PTRB?
What goes into ptrb is controlled by the hardware coginit instruction, which automatically puts the hub address of the code being executed into ptrb. We can't change that without changing the silicon.
Say I want to create a heap that starts at the end of the code...
How can I find that point?
If I make a DAT label at the bottom of the main .spin2 file, will that wind up on the bottom of the code?
I'm in the process of porting some bit-banged I2C code from the P1 to the P2. Except the P2 just blasts this stuff out so fast that the I2C bus never even has a chance to swing rail-to-rail. So as a quick-and-dirty fix I wanted to kill some time between bits. Simple, right? So I stuffed some NOPs into the Spin code figuring I'm good to go... except that while NOP is indeed a reserved word in FastSPIN, there apparently isn't actually a NOP in Spin? It throws a "error: Unknown symbol NOP".
Assuming the above assumption is correct, what then is the preferred instruction/method to just to kill a few clock cycles? (I'm really hoping I don't have to call a separate timing routine between bits. Ugh).
BTW: Slowing the clock down isn't an option. I've got a VGA driver running on two COGs that needs to be banging along at 160 mhz, minimum.
In Spin, use waitcnt for 1 second delay
There's nothing wrong with showing your intent in code by calling a wait function. If you pass a variable (or constant) into the wait, it's much easier to change each instance in the future by changing one value. Alternative is let's say you have 10 nops. Still too fast? Have to seek out all of those and add more.
The optimizer may even flatten the function call if it's just a waitx being passed a constant.
Thanks to all for the suggestions and guidance!
I think it’s like this:
(-Bangs head on desk-)
THAT was it! LOL!!! Now I know. And like darned near everything else in the Flex suite, it was just that simple. And I missed it.
Methinks I over-think these things...
You can and a REP instruction in there to do as many NOPs as you like (I think).
Now I wonder... Why didn't Spin1 have inline assembly?
I think Chip said that his Spin2 will also have inline assembly, hopefully compatible with FastSpin...
But, needs wrpin, which I don't think is part of Spin2.
Also, want to be compatible with Chip's Spin2 (?).
The other BIG difference between the P2 and the P1is that everyone is going to be able to select their favorite system clock... So, you will want your i2c waitx ##_constant to be defined in terms of the clock frequency.
something like: i2c_wait = ##_sysclock/100000.
waitx i2c_wait
Looks the same as Fastspin, but starts with "org" and ends with "end".
BTW: Looks like Chip's now has multiple return values. Thinking of doing that too?
fastspin has had multiple return values for a long time now...
Will really help people learn assembly.
So easy in Spin2
Yes. The other thing that helps a lot too, is good listing files from the compilers.
Users can scan the created ASM code, and in many cases avoid needing in-line assembly.
If they do need to in-line, they can cut/paste/tune the assembler bit that matters most.
P2 is going to run mixed/multiple languages much more often than other MCUs, due to the isolated nature of each COG.
@ersmith It looks like there is a GUI issue when attempting to do a search (Edit->Find). Entering any search criteria gives this error:
In my situation, I've got a bunch of font data in the hub. And *after* that, I've got some more (unrelated) data that is labeled at the beginning, like this:
My question is: How do I get the starting address for the data labeled by Tiles? I can and am manually calculating the address of that data, such that I'm able to access it (and I just store the fixed, calculated address in a cog register for the cog that needs it). But to a PASM noobie like me, it's tempting to think that the assembler at compile time (or less likely, the language at run time) should be able to help with that, you know, to automatically provide the address. I did admittedly blindly try using single, double and triple @ prefixes with the Tiles label with various compile errors, but I didn't learn much from that.
Perhaps I can handle it programmatically by making use of ptrb as mentioned above, but I haven't thought that through yet (sorry). If there's no (convenient) way to get the address, does anyone have an explanation to help me understand the "why" of it. I suppose that there's only so much that the compiler can do at compile time, and run time depends on actual code (that I supply) to do things, so maybe there's no mechanism to "bridge the gap," so to speak. Anyway, it just seems odd that, for example, if I add a third kind of data between my font data and the tiles data, that I'd need to recalculate the Tiles address. But perhaps I'm missing something or not designing things properly, hence I thought it was best to ask (to hopefully gain some insight).