It might be a good idea to include some form of conditional assembly in the assembler so that code can work when running stand-alone or with other tasks. Maybe something like the #define and #ifdef directives used with C. The NOPs would be added, or not depending on an assembler flag. So using the previous example, the code would look something like this.
Sigh. It is actually this simple : The assembler does nothing you do not ask it to do. Period.
But then we are all in agreement, aren't we? I may have misunderstood. I thought this was about if the assembler should supply spacer instructions where that is necessary for correct working of e.g. reps
If that is not the issue then I admit to being confused about what is actually discussed here and what the disagreement really is about. Maybe someone could provide a short description of exactly what the issues are. I for one would be grateful.
It might be a good idea to include some form of conditional assembly in the assembler so that code can work when running stand-alone or with other tasks. Maybe something like the #define and #ifdef directives used with C. The NOPs would be added, or not depending on an assembler flag. So using the previous example, the code would look something like this.
- Add a spacer NOP after REPS
- Add an additional NOP at the end of the loop
- Set the instruction count in REPS so that it includes the last NOP.
I think Andy has the solution.
If all REPS follow the conventions above , it covers both single task and mulri-task scenarios safely.
Then the only warnings needed in the DOCS are about time slot influences and a reminder that their is only one REPx circuit per cog.
But doesn't that make the code inefficient if you are only repeating a single instruction? It basically runs at half-speed.
Yes, there is a run time cost inside the loop, (which I missed earlier) - that makes it a context safe, but less than optimal, solution.
That run-time cost is highest on shortest loops.
Chip may yet find a way to deliver both context safe and optimal (or at least no inner loop cost) - he's done quite well so far.
Am I the only one that finds a thread like this vaguely reassuring - with regard to what we're fussing over.
I think essentially jmg's suggestion is a "user friendly" one and is really just a bit ahead of its time wrt where we are with prop2 right now. It's probably a whole lot easier to provide a kind of tool-tip that reminds the programmer or the potential trap, together with clear and complete description in the manual.
I wonder whether Chip doesn't look at a thread like this and think it would be easier to add the remaining three repeater blocks to at least remove that gotcha. I remember thinking about this in some detail and giving my response here .
I still think its fine to have to think about these resources and how they must be used carefully. May help to prevent the onset of Alzheimer's.
* Spacer instructions ARE required in 1-task applications to allow the pipeline to prime
before repeating can commence. If REPS is used by a task that uses no more than every 2nd
time slot, NO spacers are needed. If REPD is used by a task that uses no more than every
4th time slot, NO spacers are needed, as three intervening instructions will be provided
by the other task(s).
"Spacers CANNOT be used in REPx loops in multi-tasking"
"Spacers CANNOT be used in REPx loops in multi-tasking"
Almost, but not quite : your own typo-test and Ariba's code shows there is another middle case where if you multi-task and fail to meet the greater-than setting, then you do need the user-spacer.
In your case, you got two different unstable results on that setting.
I think Ariba's code also works safely in that middle case, but at the cost of some added loop overhead.
Also, REPD may need one or two or three user-provided spacers, depending on the slot settings.
The REPD has multiple middle cases, and they may prove harder to avoid - eg someone may want to tune the slots a little.
You can avoid user-spacers in Multi-tasking, provided you always keep above a certain slot space, but fall below that, and your code will become erratic.
Ariba's solution (REPS) gives two user spacers, of which one is always used within the loop, (sometime leading, sometimes trailing) for 100% predictable outcomes (any slot map tolerated, even a mixed or dynamic one)
A similar redundant REPD solution will exist, but at the cost of more code-padding, and more wasted cycles in the loop.
Almost, but not quite : your own typo-test and Ariba's code shows there is another middle case where if you multi-task and fail to meet the greater-than setting, then you do need the user-spacer.
In your case, you got two different unstable results on that setting.
I think Ariba's code also works safely in that middle case, but at the cost of some added loop overhead.
Also, REPD may need one or two or three user-provided spacers, depending on the slot settings.
The REPD has multiple middle cases, and they may prove harder to avoid - eg someone may want to tune the slots a little.
You can avoid user-spacers in Multi-tasking, provided you always keep above a certain slot space, but fall below that, and your code will become erratic.
Ariba's solution (REPS) gives two user spacers, of which one is always used within the loop, (sometime leading, sometimes trailing) for 100% predictable outcomes (any slot map tolerated, even a mixed or dynamic one)
A similar redundant REPD solution will exist, but at the cost of more code-padding, and more wasted cycles in the loop.
Simple ? No. Dangerous ? Yes.
Running my same test with a different schedule produced erratic behaviour with a spacer NOP and without.
schedule long %%2010_1010_1010_1010
changed to
schedule long %%2111_1111_0000_0000
It seems their is no guarantee when the REPS block starts in the schedule. In this schedule most of the time the
REPS instruction will need a spacer except in one case where it doesn't. A bit of a lottery.
Based on the results I have revised my statement.
"Spacers CANNOT be used reliably in REPx loops in multi-tasking"
It seems their is no guarantee when the REPS block starts in the schedule. In this schedule most of the time the
REPS instruction will need a spacer except in one case where it doesn't. A bit of a lottery.
Lotteries are what worries me.
Did you test that with Ariba's code ? ( #71) - ie with a NOP at each end and a plus 1 on the block size.
The hope is that 'covers all timing bases', and so will be stable. Be nice to confirm Y/N on his suggestion.
Did you test that with Ariba's code ? ( #71) - ie with a NOP at each end and a plus 1 on the block size.
The hope is that 'covers all timing bases', and so will be stable. Be nice to confirm Y/N on his suggestion.
Sorry I didn't make that clear, (Doing too many things at once at the moment).
I was talking about time slots < every 2nd in this test.
I tried the original way as well as Ariba's way.
Yes, Ariba's suggested convention works perfectly in all the scenarios I tested.
Yes, Ariba's suggested convention works perfectly in all the scenarios I tested.
Good - so there is a 'high tolerance' structure for tasks, (amongst the many minefields) it's just a pity is has a loop-penalty as well.
The code-overhead side is almost tolerable, if the speed hit could be avoided.
Something for Chip to think about ?
This is a hard thing to overcome in hardware. The pipeline needs spacers to implement REPS and REPD, and I don't think I can magically insert them into the pipeline, as there's too much going on.
In 4-way multitasking, a jmp (or loop) takes only one clock - same as a NOP. So, in 4-way multitasking, REPS/REPD offers no speed or code size advantage, anyway.
Maybe the documentation should just state that REPS/REPD are for single-task programs, only. That would save a lot of headaches.
This spacer thing really nailed me once on the Spin2 interpreter. I turned on multitasking and everything blew up. I had actually forgotten that spacers weren't needed in REPS cases where a task was getting half or less of the clock cycles. I just recoded everything to use DJNZ's. Problem solved. Sanity maintained.
The thing that I cannot figure out how to solve is the spacer issue. If that were solved, things would simplify and there would be strong reason to add 3 more instances of the REPS/REPD hardware so that every task could have one to its advantage.
This is a hard thing to overcome in hardware. The pipeline needs spacers to implement REPS and REPD, and I don't think I can magically insert them into the pipeline, as there's too much going on.
In 4-way multitasking, a jmp (or loop) takes only one clock - same as a NOP. So, in 4-way multitasking, REPS/REPD offers no speed or code size advantage, anyway.
Maybe the documentation should just state that REPS/REPD are for single-task programs, only. That would save a lot of headaches.
This spacer thing really nailed me once on the Spin2 interpreter. I turned on multitasking and everything blew up. I had actually forgotten that spacers weren't needed in REPS cases where a task was getting half or less of the clock cycles. I just recoded everything to use DJNZ's. Problem solved. Sanity maintained.
The thing that I cannot figure out how to solve is the spacer issue. If that were solved, things would simplify and there would be strong reason to add 3 more instances of the REPS/REPD hardware so that every task could have one to its advantage.
What about if REPS discard last previous instructions pipeline.
Then loads without pipeline active to settle
Since your post, I've been thinking about how to get around this problem by doing what you mentioned above.
I could cancel the pipeline by doing a JMP to PC+1. That would start REPx with a clean pipe. The only problem is that GETPIX requires 3 clocks in its two prior pipeline stages, so we wouldn't be able to repeat GETPIX, which is important to be able to do.
The alternative is to add a state to the repeat circuit where we don't repeat (JMP PC-n) until one (REPS) or three (REPD) instructions pass from the same task. That wouldn't require any code modification and would be fastest for single-task programs, as the pipeline wouldn't need to be cleared. I'm going to try to do it that way. If it works, I'll add three more circuits so each task can have one.
Since your post, I've been thinking about how to get around this problem by doing what you mentioned above.
I could cancel the pipeline by doing a JMP to PC+1. That would start REPx with a clean pipe. The only problem is that GETPIX requires 3 clocks in its two prior pipeline stages, so we wouldn't be able to repeat GETPIX, which is important to be able to do.
The alternative is to add a state to the repeat circuit where we don't repeat (JMP PC-n) until one (REPS) or three (REPD) instructions pass from the same task. That wouldn't require any code modification and would be fastest for single-task programs, as the pipeline wouldn't need to be cleared. I'm going to try to do it that way. If it works, I'll add three more circuits so each task can have one.
I could skip the spacers by clearing the pipeline, but this would waste three clocks in single-task mode. I've also found that it's sometimes very handy to have an instruction or three to do some pin output with, just before the repeating block executes.
Code would be easier to write without spacers, though. You just couldn't do any output right before the repeating block executes.
Would you guys like to see REPS/REPD work so that no spacers are ever required?
This would make code easy to write, but would waste three clock in single-task mode and would not allow you to abut some pin output instruction(s) right up against the start of the repeating block.
I could stall the pipeline for the two instructions before GETPIX, without needing two discrete 'WAIT #2' instructions, so GETPIX would still work.
I like the spacers for that reason (setup), rather than just burn the clocks. If it's not easily sorted in tasking mode, so be it. We've got the DNJZ option handy for that case. Deffo want to keep GETPIX optimized.
I don't understand what this means:
The alternative is to add a state to the repeat circuit where we don't repeat (JMP PC-n) until one (REPS) or three (REPD) instructions pass from the same task.
I could skip the spacers by clearing the pipeline, but this would waste three clocks in single-task mode. I've also found that it's sometimes very handy to have an instruction or three to do some pin output with, just before the repeating block executes.
Code would be easier to write without spacers, though. You just couldn't do any output right before the repeating block executes.
I like the spacers for that reason (setup), rather than just burn the clocks. If it's not easily sorted in tasking mode, so be it. We've got the DNJZ option handy for that case.
I don't understand what this means:
Is this essentially burning the cycles?
I should have said, instead of "(REPS)", "(in the case of REPS)". Same for REPD. I meant that if REPS had executed, I would ensure that one instruction from that task passed before repeating. I would wait for three instructions in the case of REPD.
I'm talking about making coding consistent in all task modes.
Comments
Close, but even that code is not user tolerant. - worse, it gives the illusion it is coded tolerant.
The code Ariba gave above IS user tolerant, for the current silicon.
If that is not the issue then I admit to being confused about what is actually discussed here and what the disagreement really is about. Maybe someone could provide a short description of exactly what the issues are. I for one would be grateful.
-Tor
It isn't quite that simple because it depends on how the multitasking slots are allocated.
I think this really is just something the user needs to handle on their own.
C.W.
The user needs to bring some intelligence to the table, trying to protect users from themselves is a losing battle.
C.W.
I think Andy has the solution.
If all REPS follow the conventions above , it covers both single task and mulri-task scenarios safely.
Then the only warnings needed in the DOCS are about time slot influences and a reminder that their is only one REPx circuit per cog.
No assembler changes required.
Brian
It all comes back to the DOCS again. Those coders who want to get the MAX out of REPS will still be able to tweak the code for performance.
Yes, there is a run time cost inside the loop, (which I missed earlier) - that makes it a context safe, but less than optimal, solution.
That run-time cost is highest on shortest loops.
Chip may yet find a way to deliver both context safe and optimal (or at least no inner loop cost) - he's done quite well so far.
I think essentially jmg's suggestion is a "user friendly" one and is really just a bit ahead of its time wrt where we are with prop2 right now. It's probably a whole lot easier to provide a kind of tool-tip that reminds the programmer or the potential trap, together with clear and complete description in the manual.
I wonder whether Chip doesn't look at a thread like this and think it would be easier to add the remaining three repeater blocks to at least remove that gotcha. I remember thinking about this in some detail and giving my response here .
I still think its fine to have to think about these resources and how they must be used carefully. May help to prevent the onset of Alzheimer's.
"Spacers CANNOT be used in REPx loops in multi-tasking"
Brian
Almost, but not quite : your own typo-test and Ariba's code shows there is another middle case where if you multi-task and fail to meet the greater-than setting, then you do need the user-spacer.
In your case, you got two different unstable results on that setting.
I think Ariba's code also works safely in that middle case, but at the cost of some added loop overhead.
Also, REPD may need one or two or three user-provided spacers, depending on the slot settings.
The REPD has multiple middle cases, and they may prove harder to avoid - eg someone may want to tune the slots a little.
You can avoid user-spacers in Multi-tasking, provided you always keep above a certain slot space, but fall below that, and your code will become erratic.
Ariba's solution (REPS) gives two user spacers, of which one is always used within the loop, (sometime leading, sometimes trailing) for 100% predictable outcomes (any slot map tolerated, even a mixed or dynamic one)
A similar redundant REPD solution will exist, but at the cost of more code-padding, and more wasted cycles in the loop.
Simple ? Not really. Dangerous ? Yes.
Correct, it is looking ahead to how users might/will stumble, and how the Obex can be made more robust.
Running my same test with a different schedule produced erratic behaviour with a spacer NOP and without.
It seems their is no guarantee when the REPS block starts in the schedule. In this schedule most of the time the
REPS instruction will need a spacer except in one case where it doesn't. A bit of a lottery.
Based on the results I have revised my statement.
"Spacers CANNOT be used reliably in REPx loops in multi-tasking"
Brian
Lotteries are what worries me.
Did you test that with Ariba's code ? ( #71) - ie with a NOP at each end and a plus 1 on the block size.
The hope is that 'covers all timing bases', and so will be stable. Be nice to confirm Y/N on his suggestion.
Sorry I didn't make that clear, (Doing too many things at once at the moment).
I was talking about time slots < every 2nd in this test.
I tried the original way as well as Ariba's way.
Yes, Ariba's suggested convention works perfectly in all the scenarios I tested.
Brian
Good - so there is a 'high tolerance' structure for tasks, (amongst the many minefields) it's just a pity is has a loop-penalty as well.
The code-overhead side is almost tolerable, if the speed hit could be avoided.
Something for Chip to think about ?
In 4-way multitasking, a jmp (or loop) takes only one clock - same as a NOP. So, in 4-way multitasking, REPS/REPD offers no speed or code size advantage, anyway.
Maybe the documentation should just state that REPS/REPD are for single-task programs, only. That would save a lot of headaches.
This spacer thing really nailed me once on the Spin2 interpreter. I turned on multitasking and everything blew up. I had actually forgotten that spacers weren't needed in REPS cases where a task was getting half or less of the clock cycles. I just recoded everything to use DJNZ's. Problem solved. Sanity maintained.
The thing that I cannot figure out how to solve is the spacer issue. If that were solved, things would simplify and there would be strong reason to add 3 more instances of the REPS/REPD hardware so that every task could have one to its advantage.
It is not possible to stall pipeline for REPS settle before it start execute --->
Give some speed missing but I thing -- will solve problems
It's not a matter of stalling the pipeline, but of stepping things through it.
Sory as I still don't fully grasp pipelining in P2.
So I post maybe dumb questions
What about if REPS discard last previous instructions pipeline.
Then loads without pipeline active to settle
Since your post, I've been thinking about how to get around this problem by doing what you mentioned above.
I could cancel the pipeline by doing a JMP to PC+1. That would start REPx with a clean pipe. The only problem is that GETPIX requires 3 clocks in its two prior pipeline stages, so we wouldn't be able to repeat GETPIX, which is important to be able to do.
The alternative is to add a state to the repeat circuit where we don't repeat (JMP PC-n) until one (REPS) or three (REPD) instructions pass from the same task. That wouldn't require any code modification and would be fastest for single-task programs, as the pipeline wouldn't need to be cleared. I'm going to try to do it that way. If it works, I'll add three more circuits so each task can have one.
Thanks.
Any solution that skip spacers will be GOOD.
Remove all confusions for users
I could skip the spacers by clearing the pipeline, but this would waste three clocks in single-task mode. I've also found that it's sometimes very handy to have an instruction or three to do some pin output with, just before the repeating block executes.
Code would be easier to write without spacers, though. You just couldn't do any output right before the repeating block executes.
This would make code easy to write, but would waste three clock in single-task mode and would not allow you to abut some pin output instruction(s) right up against the start of the repeating block.
I could stall the pipeline for the two instructions before GETPIX, without needing two discrete 'WAIT #2' instructions, so GETPIX would still work.
I don't understand what this means:
Is this essentially burning the cycles?
If it waste 1x3 clocks on entire function and still are deterministic -- That NOT BIG problem --
BUT if it waste 3 clocks every round --- BIG problem
I should have said, instead of "(REPS)", "(in the case of REPS)". Same for REPD. I meant that if REPS had executed, I would ensure that one instruction from that task passed before repeating. I would wait for three instructions in the case of REPD.
I'm talking about making coding consistent in all task modes.
It would only waste three clocks initially. The loops would be zero-overhead.
That sounds VERY good