where is (PASM) coginit executed?
ags
Posts: 386
Back to grasping at straws debugging again. I have an unreliable bootloader (which was once reliable). It functions by stopping all (other) cogs, loading RAM from an EEPROM, then calling coginit (launching the SPIN interpreter in cog0) and finally terminating the cog that does all the bootloading. So the sequence is:
Where is all the work being done to complete the coginit instruction? In the code snippet above, you see that as soon as coginit returns, I stop the cog in which the instruction was executed. By this time I've already stopped every other cog. If the "target" cog (the one that is being launched by the coginit instruction, not the one in which the coginit instruction was executed) is responsible to do all the work to load itself then I'm OK. If it's the cog in which coginit was executed then I'm in trouble, because I stop that cog immediately after execution of the coginit instruction. That doesn't seem like it would ever work so I doubt it.
I'm wondering if ROM contains the SPIN interpreter and *two* bootloaders: one to load RAM from EEPROM or a host, and another to load all 512 cog longs and then begin executing at $0. It could load the SPIN interpreter from ROM and execute it just as well as loading PASM code from RAM and executing it.
But the fundamental (if not now esoteric and not really demanding full understanding to get the job done - but interesting nonetheless) question remains: how is the loading of cog memory from hub RAM/ROM accomplished if no cog is running yet?
BTW, thanks to Mike Green for sharing how to load the SPIN interpreter - years ago.
'stop all cogs other than "me" 'load program from EEPROM into hub RAM 'do stuff to setup clock correctly, letting the PLL and Oscillator settle (20ms delay @20MHz RCFAST clock mode) coginit interpreter 'interpreter is $0001 << 18 | $3C01 << 4 cogid tmp cogstop tmpDocumentation shows that cogid, cogstop, and coginit all take 8..23 clocks - typical for a hub instruction. I can see how cogid and cogstop will complete in 8..23 clocks, but not coginit. 512 longs need to be loaded into a cog, at least. So that leads me to the question:
Where is all the work being done to complete the coginit instruction? In the code snippet above, you see that as soon as coginit returns, I stop the cog in which the instruction was executed. By this time I've already stopped every other cog. If the "target" cog (the one that is being launched by the coginit instruction, not the one in which the coginit instruction was executed) is responsible to do all the work to load itself then I'm OK. If it's the cog in which coginit was executed then I'm in trouble, because I stop that cog immediately after execution of the coginit instruction. That doesn't seem like it would ever work so I doubt it.
I'm wondering if ROM contains the SPIN interpreter and *two* bootloaders: one to load RAM from EEPROM or a host, and another to load all 512 cog longs and then begin executing at $0. It could load the SPIN interpreter from ROM and execute it just as well as loading PASM code from RAM and executing it.
But the fundamental (if not now esoteric and not really demanding full understanding to get the job done - but interesting nonetheless) question remains: how is the loading of cog memory from hub RAM/ROM accomplished if no cog is running yet?
BTW, thanks to Mike Green for sharing how to load the SPIN interpreter - years ago.
Comments
The complexity came when I wondered if the "me" cog had any part in the process of loading the target cog. If so, killing it before the process is complete would be a problem. But the more I think of that, it just doesn't make sense. One cog can't do more than one thing at a time. When the coginit instruction is complete, it's done. Yet I'm still curious about the "hardware loader".
That also implies there is separate machinery that at initial boot time (after coming out of reset) that is responsible for polling to find a host or EEPROM. After some checks/handshakes, that will either do nothing or loading RAM then initiating the cog process you outline above to load a SPIN interpreter instance.
This is handled in the bootloader software. After a Reset the bootloader is loaded to cog 0 from ROM. That is all that the hardware does.
All further tests for serial and EEPROM are done per software. The booter code is encrypted in ROM, but was later released by Chip in Spin source form.
Andy
In any case, this isn't where my defect lies... but it was good to learn how it works. Thanks.
Is it that after a reset, a long value will be at put in address location 0 in cog0
This long value represents: cognew (@bootcode,0)
Or does HUB have a simple state machine, that moves 512longs and no cog have to run for this to happen?
Which came first? The chicken or the egg? The hub or the cog? I think there's a little more to the hub then just memory..
Sandy
Only 496 longs get transfered, not 512. The last 16 locations are cleared to zero, at least that's what the manual states. If that's the case, how does the PAR register at $1F0 get set?
Agreed. And that is precisely how I ended up on this subject. My previous implementation of a bootloader was starting a cog that ran PASM code; that cog would load hub RAM with contents from an EEPROM; when that was finished, it would launch cog0 as is done during a normal startup sequence. That is, load the SPIN interpreter and run the code in hub RAM. Back in the "initiating cog" - the one running the SPIN code that launched the cog to run the PASM bootloader code - once the bootloader cog was started, it would stop all cogs other than itself and the bootloader cog. Then it would stop it's own cog. I realized that although unlikely, if the SPIN code that was executed to stop all cogs modified any hub RAM that had already been loaded (in parallel) by the bootloader cog, I would end up with problems. So I moved the code to stop all other cogs from the initiating SPIN code into the bootloader PASM code. Now the first thing I do in the bootloader is stop all other cogs, then load hub RAM, then launch cog to run the SPIN interpreter and execute the program in hub RAM.
Unfortunately, that wasn't the cause of my problem - but it is a more robust implementation.
The first thing the interpreter does is to copy the values of PBASE, VBASE, DBASE, PCURR and DCURR from the header in hub RAM to registers in cog RAM. The PBASE value is stored at location $06 in hub RAM, but the copy loop in the interpreter adds a value of 2 to the hub address before it reads it. This is why PAR is set to $04 instead of $06.
The spinix loader then reads the binary file from the SD card into hub RAM starting at location 0. It assumes that all of the sectors are contiguous in the file, which is valid for cluster size of 32K, but could be a problem if the SD card uses 16K clusters. So spinix requires a cluster size of 32K to ensure correct operation. Is it possible that your loader has the same restriction?
After the spinix loader reads the binary file into memory it issues a stop command to the SD card, and then stops the SPI driver cog. BTW, you need to make sure that your SPI mailbox is at the high end of memory so it doesn't get overwritten by the binary file. Maybe that's your problem.
Yes, I recall that (14-bit address for PAR and code address) and thinking this though before. I should have made that clear (and made an outright mistake in stating that the PAR address was $01). With PAR being hub RAM location $04, I think of that as the "base mailbox address". Adding an offset to that to get to other parameters is not unusual. The question I had was why not use hub RAM location $00 as the base? I presume something else (important) is stored at that location.
Dave Hein is a genius. You have pointed me at what I am (almost) certain is the problem. I don't release any locks after stopping all the cogs. Now that you mention it, that's an obvious thing that must be done. (I checked the manual to see if there was any indication that locks are cleared, but see they can't be as they are a shared resource (like hub RAM) so can't be modified by any one cog being stopped. The only thing that clears them is a reset). This explains why I am able to run my bootloader four times, then it fails. I'm running out of locks and in a loop waiting for one to become available (which will never happen). Thanks Dave!
Now I have to figure out why I'm consuming 2x the number of locks I expect. I should be hanging on the 8th bootloader call, not the 4th.
As to the spinix bootloader, what's the advantage of having it in SPIN instead of PASM?
One thing that I forgot to mention is that you need to clear the VAR memory. Some programs may depend on their VAR variables being initialized to zero.
Lesson: stopping all cogs does not result in the same state as resetting the Prop.