... and maybe even a round description of each and every opcode. I'm just wondering where the best place is to help work on such a thing.
The main document is suitable for that. Jon Titus has done some additions of that type already. You'll have to sign up for a Google account though I think.
I do have some ideas, but it will take me a few evenings to do some experiments to see if they are worth sharing. I certainly don't want to distract you Chip, but was looking for a light touch way to get more hands on docs refinements in parallel if that was not already going. I'll share what I learn in a few days.
So this doesn't seem overly vague, I'll describe my line of exploration here. I'm investigating tools that might make shared docs writing/reviewing/publishing/hosting much more streamlined as well as easy to customize. It may yield nothing, or maybe we'll find something amazing.
The P1 manual is very good -- I think Chip and Jeff Martin created it. A similar document for the P2 will be helpful (and a lot thicker given the number of P2 PASM instructions).
Peter did a lot of work on the instruction set docs (google docs?) but the instruction set changed so often it was out of date and needs correcting. It was a nice summary and would be worthwhile updating.
The Graphic debug demos in Version 35 (documentation/software) work great!!! Well worth your time and effort on creating and improving them.
I've been having fun playing around with them.
These Graphic debug commands will be very useful not only for just debugging, but for producing program output in general. There is enough power/usability with these suite of commands to warrant something other than just being called "debug". But of course having them designated "debug" does give you the ability to include them in the final program or not depending your selection when you compile your program.
Just for the sake of completeness, is the set of "bird_lut*.bmp" image files (used in one of the demos) available anywhere?
I would like to get a copy of them.
The Graphic debug demos in Version 35 (documentation/software) work great!!! Well worth your time and effort on creating and improving them.
I've been having fun playing around with them.
These Graphic debug commands will be very useful not only for just debugging, but for producing program output in general. There is enough power/usability with these suite of commands to warrant something other than just being called "debug". But of course having them designated "debug" does give you the ability to include them in the final program or not depending your selection when you compile your program.
Just for the sake of completeness, is the set of "bird_lut*.bmp" image files (used in one of the demos) available anywhere?
I would like to get a copy of them.
Ah, I meant to include them, but was rushed because that Zoom meeting was starting. I will update that file tomorrow with those images. Would you believe that Zoom meeting carried on for 7 hours?
I'm glad you like all the DEBUG stuff. I still need to cover the MIDI display. I also need to document in line assembly for Spin2 Programs. Should have that done tomorrow.
Chip,
I think getms() is back in V35.
But I can't find it in the docu.
I converted the blink.spin2 example to a non-blocking variation using getms(). The getms() function returns the lower 32 bits of milliseconds since reset. I tend to run at 200MHz which means that value will rollover in about 49 days. Using it in this [differential] manner is not affected by the rollover.
con
DELAY = 125
pub go() | t
t := getms()
repeat
if ((getms() - t) >= DELAY)
pinwrite(56 addpins 7, getrnd())
t += DELAY
' do other things for about 125 ms
...
Just for the sake of completeness, is the set of "bird_lut*.bmp" image files (used in one of the demos) available anywhere?
I would like to get a copy of them.
I just added them into the .zip file at the top of this thread.
Resident interrupts in SPIN2 inline asm looks interesting and may help save a COG at times.
Yes, the Spin2 interpreter was developed with all kinds of sporadic interrupts executing in the background, just to be sure it was able to tolerate them. It only does a STALLI/ALLOWI to protect single CORDIC operations. Multiple CORDIC operations are coded as STALLI/cordic_operation/ALLOWI/NOP + STALLI/cordic_operation/ALLOWI/NOP, etc. The NOP is there so interrupts can be processed quickly.
STALLI and ALLOWI are good to control interrupts, though personally I really like the REP method discussed in a prior thread to disable interrupts with the CORDIC because in theory it blocks interrupts while we use it and still leaves things in the same state at the end, and even saves an instruction. If instead we have STALLI/ALLOWI to protect the CORDIC state it would automatically then re-enable interrupts each time we use the CORDIC wouldn't it? So if we wanted interrupts disabled for some controlled period of time in a portion of our code by calling STALLI and ALLOWI ourselves then we'd just have to be sure not to make use of the CORDIC which would have an unwanted side effect of also enabling interrupts, potentially when we wanted them to remain disabled until our own ALLOWI executes. At least that is what I think it would do, but I haven't tried it so can't be 100% sure there.
STALLI and ALLOWI are good to control interrupts, though personally I really like the REP method discussed in a prior thread to disable interrupts with the CORDIC because in theory it blocks interrupts while we use it and still leaves things in the same state at the end, and even saves an instruction. If instead we have STALLI/ALLOWI to protect the CORDIC state it would automatically then re-enable interrupts each time we use the CORDIC wouldn't it? So if we wanted interrupts disabled for some controlled period of time in a portion of our code by calling STALLI and ALLOWI ourselves then we'd just have to be sure not to make use of the CORDIC which would have an unwanted side effect of also enabling interrupts, potentially when we wanted them to remain disabled until our own ALLOWI executes. At least that is what I think it would do, but I haven't tried it so can't be 100% sure there.
I never thought about using REP as an interrupt shield during CORDIC operations. That's a great idea.
In the interpreter, I don't want to stall things for too long, so I allow interrupts for one NOP, so that's there an opportunity for any pending interrupt to execute. Maybe two or three NOPs could be needed to get around a multi-interrupt bottleneck.
What do you mean by a CORDIC operation re-enabling interrupts? I don't think I'm understanding all you said.
STALLI and ALLOWI are good to control interrupts, though personally I really like the REP method discussed in a prior thread to disable interrupts with the CORDIC because in theory it blocks interrupts while we use it and still leaves things in the same state at the end, and even saves an instruction. If instead we have STALLI/ALLOWI to protect the CORDIC state it would automatically then re-enable interrupts each time we use the CORDIC wouldn't it? So if we wanted interrupts disabled for some controlled period of time in a portion of our code by calling STALLI and ALLOWI ourselves then we'd just have to be sure not to make use of the CORDIC which would have an unwanted side effect of also enabling interrupts, potentially when we wanted them to remain disabled until our own ALLOWI executes. At least that is what I think it would do, but I haven't tried it so can't be 100% sure there.
I never thought about using REP as an interrupt shield during CORDIC operations. That's a great idea.
In the interpreter, I don't want to stall things for too long, so I allow interrupts for one NOP, so that's there an opportunity for any pending interrupt to execute. Maybe two or three NOPs could be needed to get around a multi-interrupt bottleneck.
What do you mean by a CORDIC operation re-enabling interrupts? I don't think I'm understanding all you said.
ALLOWI explicitly enables interrupts which might have been disabled before the STALLI, in other words the previous interrupt state could be lost, whereas REP has the advantage of blocking interrupts without changing the state.
Yes, by always putting in that ALLOWI after the CORDIC work it has this side effect of enabling interrupts (which may be undesirable in some cases). The REP helps that out. You can still have your NOP(s) at the end of the protected REP portion where you could then respond to interrupts in a timely way after each CORDIC operation is done in a sequence (assuming they were globally enabled before the CORDIC was accessed in the first place).
It would just be weird to have this side effect where if you do a division operation in SPIN2 then interrupts forcibly get enabled as well (assuming you are using the CORDIC for integer division, as I'd imagine you would be).
When using rep-blocks as a means to control whether or not interrupts are allowed to hit during certain periods of time (without having to resort to STALLI/ALLOWI), you can attain the same effect of momentarily enabling interrupts to occur within preciselly-defined windows, by factoring each long-range rep block in smaller units.
I'm only not sure if the use an interveinning NOP between any two consecutive rep-blocks is mandatory, or the simple fact that a natural "gap" exists between the rep-blocks, which would, perhaps, ensure a sane "hit spot" for any pending interrupt to occur.
It would just be weird to have this side effect where if you do a division operation in SPIN2 then interrupts forcibly get enabled as well (assuming you are using the CORDIC for integer division, as I'd imagine you would be).
Yes, it would be better if the Spin2 interpreter used REP instead of STALLI/ALLOWI. Some of the changes are simple, others might need consecutive REPs due to unequal skipping.
Thanks for the explanation. I understand what you mean now.
A NOP is needed after an ALLOWI, though, because an interrupt can't begin when a STALLI instruction is next. There needs to be some other intervening instruction.
Using REP is way better, for all the reasons you listed. This will save a bunch of instructions. Thanks.
This makes my head hurt. Do any of you guys have any idea if there will be trouble if I change this:
rotxy_ setq #2-1 'a b
rdlong y,--ptra 'a b
_ret_ tjnz y,#.true 'a |
.true _ret_ mov x,z 'a |
polxy_ popa y ' | c d e
stalli ' b c d |
setq z ' b | | |
qrotate y,x ' b c | |
qvector y,x ' | | d |
getqx x ' b c d |
pusha x ' b c d e
getqy x ' b c d |
_ret_ allowi ' b c d |
_ret_ subr x,y ' e
...to this...
rotxy_ setq #2-1 'a b
rdlong y,--ptra 'a b
_ret_ tjnz y,#.true 'a |
.true _ret_ mov x,z 'a |
polxy_ popa y ' | c d e
rep #6,#1 ' b c d |
setq z ' b | | |
qrotate y,x ' b c | |
qvector y,x ' | | d |
getqx x ' b c d |
pusha x ' b c d e
_ret_ getqy x ' b c d |
_ret_ subr x,y ' e
A NOP is needed after an ALLOWI, though, because an interrupt can't begin when a STALLI instruction is next. There needs to be some other intervening instruction.
Does the same applies to REP (in the sense of chances for an interrupt to begin when a REP instruction is next)?
A NOP is needed after an ALLOWI, though, because an interrupt can't begin when a STALLI instruction is next. There needs to be some other intervening instruction.
Does the same applies to REP (in the sense of chances for an interrupt to begin when a REP instruction is next)?
The silicon will not start an interrupt branch if REP is in the pipeline.
I finished documenting all the ways PASM can be called from Spin2.
Here is the final example, in which a terminate-stay-resident program is installed into the top of free register memory in the cog and it services a timer interrupt in the background, toggling some pins every 500ms, while the Spin2 executes, undeterred.
PUB go()
regexec(@chunk) 'load self-defined chunk and execute it
'chunk starts timer interrupt and returns
repeat
pinwrite(60 addpins 3, getrnd()) 'randomize pins 60..63
waitms(100) 'pins 56..59 toggle via interrupt
DAT
chunk word start,finish-start-1 'define chunk start and size-1
org $128 'org can be $000..$130-size
start mov ijmp1,#isr 'set int1 vector
setint1 #1 'set int1 to ct-passed-ct1 event
getct pr0 'get ct
_ret_ addct1 pr0,bigwait 'set initial ct1 target, return to Spin2
isr drvnot #56 addpins 3 'interrupt service routine, toggle 56..59
addct1 pr0,bigwait 'set next ct1 target
reti1 'return from interrupt
bigwait long 20_000_000 / 2 '500ms second on RCFAST
finish
This makes my head hurt. Do any of you guys have any idea if there will be trouble if I change this:
I think it should work, as long as you adjust your execf/skipf masks accordingly. I think you can still do a ret from the end of a REP loop using the HW stack, though I know that evanh has issues with absolute branches at the end of REP loops (I do know indirect branches via registers are okay - I use them in my HyperRAM driver). That would be the only potential issue I could imagine.
Worst case situation if the P2 fails to allow _ret_ at the end of the REP loops, is you place a normal RET where the ALLOWI currently is, and don't make it part of the REP loop.
In this case, since it isn't actually intended to loop, having the REP block length intentionally longer will be a good thing. It just has to hold interrupts until the branch. The branch will cancel the REP and therefore take the hold off any IRQ. I'd use REP @label instead of a hardcoded length. It prevents silly mistakes when later editing.
And the branch due to _RET_ will be an absolute address so it won't trigger the flaw with relative branching from a REP block.
Comments
The main document is suitable for that. Jon Titus has done some additions of that type already. You'll have to sign up for a Google account though I think.
So this doesn't seem overly vague, I'll describe my line of exploration here. I'm investigating tools that might make shared docs writing/reviewing/publishing/hosting much more streamlined as well as easy to customize. It may yield nothing, or maybe we'll find something amazing.
Very soon. I'm just getting the documentation done now. Jeff will get it into PropellerTool.exe soon after. He may already be integrating it.
Chip,
The Graphic debug demos in Version 35 (documentation/software) work great!!! Well worth your time and effort on creating and improving them.
I've been having fun playing around with them.
These Graphic debug commands will be very useful not only for just debugging, but for producing program output in general. There is enough power/usability with these suite of commands to warrant something other than just being called "debug". But of course having them designated "debug" does give you the ability to include them in the final program or not depending your selection when you compile your program.
Just for the sake of completeness, is the set of "bird_lut*.bmp" image files (used in one of the demos) available anywhere?
I would like to get a copy of them.
Ah, I meant to include them, but was rushed because that Zoom meeting was starting. I will update that file tomorrow with those images. Would you believe that Zoom meeting carried on for 7 hours?
I'm glad you like all the DEBUG stuff. I still need to cover the MIDI display. I also need to document in line assembly for Spin2 Programs. Should have that done tomorrow.
I think getms() is back in V35.
But I can't find it in the docu.
I converted the blink.spin2 example to a non-blocking variation using getms(). The getms() function returns the lower 32 bits of milliseconds since reset. I tend to run at 200MHz which means that value will rollover in about 49 days. Using it in this [differential] manner is not affected by the rollover.
Sorry, it WAS missing. I just put it back into the doc.
I just added them into the .zip file at the top of this thread.
Yes, the Spin2 interpreter was developed with all kinds of sporadic interrupts executing in the background, just to be sure it was able to tolerate them. It only does a STALLI/ALLOWI to protect single CORDIC operations. Multiple CORDIC operations are coded as STALLI/cordic_operation/ALLOWI/NOP + STALLI/cordic_operation/ALLOWI/NOP, etc. The NOP is there so interrupts can be processed quickly.
I never thought about using REP as an interrupt shield during CORDIC operations. That's a great idea.
In the interpreter, I don't want to stall things for too long, so I allow interrupts for one NOP, so that's there an opportunity for any pending interrupt to execute. Maybe two or three NOPs could be needed to get around a multi-interrupt bottleneck.
What do you mean by a CORDIC operation re-enabling interrupts? I don't think I'm understanding all you said.
ALLOWI explicitly enables interrupts which might have been disabled before the STALLI, in other words the previous interrupt state could be lost, whereas REP has the advantage of blocking interrupts without changing the state.
It would just be weird to have this side effect where if you do a division operation in SPIN2 then interrupts forcibly get enabled as well (assuming you are using the CORDIC for integer division, as I'd imagine you would be).
I'm only not sure if the use an interveinning NOP between any two consecutive rep-blocks is mandatory, or the simple fact that a natural "gap" exists between the rep-blocks, which would, perhaps, ensure a sane "hit spot" for any pending interrupt to occur.
Yes, it would be better if the Spin2 interpreter used REP instead of STALLI/ALLOWI. Some of the changes are simple, others might need consecutive REPs due to unequal skipping.
Thanks for the explanation. I understand what you mean now.
A NOP is needed after an ALLOWI, though, because an interrupt can't begin when a STALLI instruction is next. There needs to be some other intervening instruction.
Using REP is way better, for all the reasons you listed. This will save a bunch of instructions. Thanks.
...to this...
Does the same applies to REP (in the sense of chances for an interrupt to begin when a REP instruction is next)?
The silicon will not start an interrupt branch if REP is in the pipeline.
Here is the final example, in which a terminate-stay-resident program is installed into the top of free register memory in the cog and it services a timer interrupt in the background, toggling some pins every 500ms, while the Spin2 executes, undeterred.
Worst case situation if the P2 fails to allow _ret_ at the end of the REP loops, is you place a normal RET where the ALLOWI currently is, and don't make it part of the REP loop.
And the branch due to _RET_ will be an absolute address so it won't trigger the flaw with relative branching from a REP block.