Shop OBEX P1 Docs P2 Docs Learn Events
Alternative "rep" syntax? — Parallax Forums

Alternative "rep" syntax?

SeairthSeairth Posts: 2,474
edited 2015-09-27 01:51 in Propeller 2
I realize that this topic has been discussed in the past. However, I think it's worth revisiting.

@moderators: Can you move the rep-related comment I made (and follow-up comments) in "Propr2 FPGA files!!!" to this thread?

Edit: never mind. I moved my own comment.
«13

Comments

  • As for REP, looking at MainLoader.spin, there's this snippet:
            rep     #2,#7           'ready for 8 bits
            waitx   waita           'wait for middle of 1st data bit
            testb   inb,#31	wc      'sample rx
            rcr     x,#1            'rotate bit into byte
            waitx   waitb           'wait for middle of nth data bit
    

    From that, it looks to me like:

    1. The instruction immediately following REP is executed.
    2. The first REP parameter is N-1 instructions that you want to repeat
    3. The second REP parameter is N-1 times you want to repeat the loop

    Is that correct? If so, for #2 and #3, I wish there were a better way of setting the parameters. Immediate thoughts are to change the second parameter to have #0 mean "forever" (if that's still possible) and have any positive value indicate the count.

    I know the first parameter could also be done like:
            rep     #(r_end-r_beg) >> 2, #7  'ready for 8 bits
            waitx   waita           'wait for middle of 1st data bit
    r_beg   testb   inb,#31	wc      'sample rx
            rcr     x,#1            'rotate bit into byte
    r_end   waitx   waitb           'wait for middle of nth data bit
    

    But the expression is... well... ugly. You might be able to do something like:
            rep     #(@r_end-4) >> 2, #7  'ready for 8 bits
            waitx   waita           'wait for middle of 1st data bit
            testb   inb,#31	wc      'sample rx
            rcr     x,#1            'rotate bit into byte
    r_end   waitx   waitb           'wait for middle of nth data bit
    

    which is a bit better. But what I'd really like is something like:
    bits        rep     #7              'ready for 8 bits
                waitx   waita           'wait for middle of 1st data bit
                testb   inb,#31	wc  'sample rx
                rcr     x,#1            'rotate bit into byte
    rep_bits    waitx   waitb           'wait for middle of nth data bit
    

    where the compiler will do the work for me.
  • Going one step further with your idea Seairth would be really nice.
    The repeat value is the true count value.
    bits        rep     #8              'ready for 8 bits
                waitx   waita           'wait for middle of 1st data bit
                testb   inb,#31	wc  'sample rx
                rcr     x,#1            'rotate bit into byte
    rep_bits    waitx   waitb           'wait for middle of nth data bit
    


  • Cluso99Cluso99 Posts: 18,069
    edited 2015-09-27 02:30
    All this will ultimately be the compilers job.
    Let's leave pnut.exe to whatever is easy for Chip.

    I would like to see the compiler...

    1. Determine the count based on the label ":rep_bits" because it's a local label.
    For me, I would prefer ":rep" or ":rep_end" or ":rep_last".

    2. Automatically deduct the "1" from the no. of repeats. So if we want 8 we say #8 and the compiler substitutes #7.

    3. For an endless repeat, I would prefer no repeat count. ie "rep"

    So here are the code examples...
                rep     #8              ' 8 loops (compiler will set to "#3,#7" in object code)
                nop                     'needed for rep to get ready - can be any instruction
                testb   inb,#31	wc  '\ repeated loop
                rcr     x,#1            '|
    :rep_last   waitx   waitb           '/ last repeated instruction (required label)
                .....
                rep                     ' infinite loops (compiler will set to "#3,#0" in object code)
                nop                     'needed for rep to get ready - can be any instruction
                testb   inb,#31	wc  '\ repeated loop
                rcr     x,#1            '|
    :rep_last   waitx   waitb           '/ last repeated instruction (required label)
    
    Note we may need a number after ":rep_last" ie ":rep_lastn". The compiler will just use the ":rep_last" section closest to the rep instruction.
  • ozpropdev wrote: »
    Going one step further with your idea Seairth would be really nice.
    The repeat value is the true count value.
    bits        rep     #8              'ready for 8 bits
                waitx   waita           'wait for middle of 1st data bit
                testb   inb,#31	wc  'sample rx
                rcr     x,#1            'rotate bit into byte
    rep_bits    waitx   waitb           'wait for middle of nth data bit
    


    Agreed. I made such a note it my original comment. #0 can mean "forever" (if that function still exists).
  • Cluso99 wrote: »
    All this will ultimately be the compilers job.
    Let's leave pnut.exe to whatever is easy for Chip.

    That may be true for the instruction count, but I think the proposed change for repeat count would require an FPGA change.

  • Hmm... here's a thought. Suppose"
                waitx   waita           'wait for middle of 1st data bit
                rep     #8              'ready for 8 bits
    	    rep     #@:rep_end      'number of instructions to execute in the loop
                testb   inb,#31	wc  'sample rx
                rcr     x,#1            'rotate bit into byte
    :rep_end    waitx   waitb           'wait for middle of nth data bit
    

    The first "rep" sets the loop count and the second "rep" sets the instruction count. This would be similar to instructions like AUGDS that augment the second instruction. In this case, though, the second instruction is really augmenting the first one. Of course, the mnemonics could be changed. And, of course, this could all be hidden by the assembler as a single instruction.

    Note also that this places the "waitx" back before the "rep", which is in keeping with all other branch-like instructions. Now that we no longer have delayed branches, this characteristic of "rep" sticks out a bit. An approach like the one above would take care of that!
  • cgraceycgracey Posts: 14,155
    edited 2015-09-27 05:44
    Good thinking on the REP syntax. Simple will be best. A funny thing to consider: even though it appears that the repeat value is N-1, it actually is the number of times to repeat, considering that it will flow through once, before repeating. I think it would be best not to make adjustments to constants in the assembler, because it will not apply to cases where registers are used.
  • How about...
    bits        rep     label,#8        'ready for 8 bits
                waitx   waita           'wait for middle of 1st data bit
    label       testb   inb,#31   wc    'sample rx
                rcr     x,#1            'rotate bit into byte
    label_end   waitx   waitb           'wait for middle of nth data bit
    
    we point the start, and the compiler automatically seeks for the "*_end" tag in a way that's sort of mimicking call/ret behavior ?

    Hmm... well the label is always $+2, so I guess it's a bit redundant.

    Otherwise, we only point the end explicitly, and the compiler makes the rest. Two parameters makes the rep more readable IMHO, expecially if the loop is large.
  • jmgjmg Posts: 15,173
    edited 2015-09-27 06:07
    Seairth wrote: »

    Note also that this places the "waitx" back before the "rep", which is in keeping with all other branch-like instructions.

    Interesting idea, as it saves needing the Rep_Start label to make it clear where the loop actually is.
    It does make placing labels trickier tho, as the 'pull-up' creates out-of-order ASM.

    Maybe that can be managed with more tool-support:

    Label_Valid
               waitx   waita   'out of order pulled up      
    Label_Invalid
               rep     #8             
    	   rep     #@:rep_end 
    Label_Invalid2
                testb   inb,#31	wc 
                rcr     x,#1            
    Label_Invalid3
    :rep_end    waitx   waitb     
    Label_Valid2
    

    In the above case, a jump from elsewhere to any Label_InValid would be flagged, but a jump to Label_Valid is ok.

    To avoid out-of-order ASM, code clarity would need two labels :
    The tools can now catch common by-line paste/comment code changes that may break the loop structure.
    Label_Valid
               rep     Rep_Start, Rep_End,#8             
    Label_Invalid
               waitx   waita   'done once opcode up      
    Rep_Start
                testb   inb,#31	wc 
                rcr     x,#1            
    Rep_end    
    Label_Valid2
               waitx   waitb     
    

  • Cluso99Cluso99 Posts: 18,069
    edited 2015-09-27 08:20
    cgracey wrote: »
    Good thinking on the REP syntax. Simple will be best. A funny thing to consider: even though it appears that the repeat value is N-1, it actually is the number of times to repeat, considering that it will flow through once, before repeating. I think it would be best not to make adjustments to constants in the assembler, because it will not apply to cases where registers are used.
    That count is different to repeat in spin.

    May be better to let the compiler reduce the count by 1, and disallow using a register and only allow the count to be immediate.

    BTW if the operands are swapped to rep #count,#instructions would this get rid of executing the following instruction before the rep starts? This operand order makes more sense even though the destination (count) is not decremented.

    We can always change the count using movd rep_instr,S/#.

    IMHO I would rather a restriction if it means the rep instruction is simpler to understand.
  • Cluso99Cluso99 Posts: 18,069
    Seairth wrote: »
    Hmm... here's a thought. Suppose"
                waitx   waita           'wait for middle of 1st data bit
                rep     #8              'ready for 8 bits
    	    rep     #@:rep_end      'number of instructions to execute in the loop
                testb   inb,#31	wc  'sample rx
                rcr     x,#1            'rotate bit into byte
    :rep_end    waitx   waitb           'wait for middle of nth data bit
    

    The first "rep" sets the loop count and the second "rep" sets the instruction count. This would be similar to instructions like AUGDS that augment the second instruction. In this case, though, the second instruction is really augmenting the first one. Of course, the mnemonics could be changed. And, of course, this could all be hidden by the assembler as a single instruction.

    Note also that this places the "waitx" back before the "rep", which is in keeping with all other branch-like instructions. Now that we no longer have delayed branches, this characteristic of "rep" sticks out a bit. An approach like the one above would take care of that!
    No! If we have to delay an instruction I would rather have the option of using that slot for something useful.

  • jmg wrote: »
    Interesting idea, as it saves needing the Rep_Start label to make it clear where the loop actually is.
    It does make placing labels trickier tho, as the 'pull-up' creates out-of-order ASM.

    Maybe that can be managed with more tool-support:

    Label_Valid
               waitx   waita   'out of order pulled up      
    Label_Invalid
               rep     #8             
    	   rep     #@:rep_end 
    Label_Invalid2
                testb   inb,#31	wc 
                rcr     x,#1            
    Label_Invalid3
    :rep_end    waitx   waitb     
    Label_Valid2
    

    In the above case, a jump from elsewhere to any Label_InValid would be flagged, but a jump to Label_Valid is ok.

    [/code]

    There is no out-of-order instructions in what I was proposing. Moving the "waitx" before "rep" does nothing but free up the delayed slot after "rep". The thought was to use the second instruction slot (after the rep) to augment the rep. Or, put another way, "rep" would take two instruction slots. This could be handled in number of ways:

    * Use the same "rep" syntax as we currently do, but have the assembler expand it to two instructions.
    * Have back-to-back "rep" instructions which each take a single operand, like I showed above.
    * Have two separate opcodes (and mnemonics), but otherwise it looks like what I showed above.

    As for your code above, "Label_Invalid" would still be a valid address to jump to. Similarly, "Label_Invalid2" would be valid if you are conditionally switching between "1 or n" repeats.
  • SeairthSeairth Posts: 2,474
    edited 2015-09-27 13:28
    Cluso99 wrote: »
    Seairth wrote: »
    Hmm... here's a thought. Suppose"
                waitx   waita           'wait for middle of 1st data bit
                rep     #8              'ready for 8 bits
    	    rep     #@:rep_end      'number of instructions to execute in the loop
                testb   inb,#31	wc  'sample rx
                rcr     x,#1            'rotate bit into byte
    :rep_end    waitx   waitb           'wait for middle of nth data bit
    

    The first "rep" sets the loop count and the second "rep" sets the instruction count. This would be similar to instructions like AUGDS that augment the second instruction. In this case, though, the second instruction is really augmenting the first one. Of course, the mnemonics could be changed. And, of course, this could all be hidden by the assembler as a single instruction.

    Note also that this places the "waitx" back before the "rep", which is in keeping with all other branch-like instructions. Now that we no longer have delayed branches, this characteristic of "rep" sticks out a bit. An approach like the one above would take care of that!
    No! If we have to delay an instruction I would rather have the option of using that slot for something useful.

    Actually, chip doesn't really need to delay the instruction. He could have treated it like a "JMP @1" (i.e. flush pipeline and re-fetch the next instruction). That too would simplify the instruction usage. And keep it more consistent with all other instructions. I'd take this over the recommendation I made above.

    Edit: Changed the @4 to an @1. With all of the byte vs. long addressing discussion, I can't remember if the relative operator returns bytes or longs. It should be bytes to be consistent with all other instruction addressing. But it should be longs to avoid losing two bits range. So confusing...
  • jmgjmg Posts: 15,173
    Seairth wrote: »
    There is no out-of-order instructions in what I was proposing. Moving the "waitx" before "rep" does nothing but free up the delayed slot after "rep".
    Oh, ok, it never occurred to me that making code larger was even on the table here.
    Seairth wrote: »
    The thought was to use the second instruction slot (after the rep) to augment the rep. Or, put another way, "rep" would take two instruction slots. This could be handled in number of ways:

    * Use the same "rep" syntax as we currently do, but have the assembler expand it to two instructions.
    * Have back-to-back "rep" instructions which each take a single operand, like I showed above.
    * Have two separate opcodes (and mnemonics), but otherwise it looks like what I showed above.

    Making code larger seems a significant backward step, which means stretching the opcode time is the only realistic alternative. That makes the code slower, but it does avoid the ease-of-break-on-edit, that the present direct binary form suffers from.

    To me, the two label approach has none of these larger/slower drawbacks, and is a syntax already in use by Analog Devices.
    ie Simplicity and clarity, with no down sides.
  • jmg wrote: »
    To me, the two label approach has none of these larger/slower drawbacks, and is a syntax already in use by Analog Devices.
    ie Simplicity and clarity, with no down sides.

    I see where you are coming from.

    Personally, I think it's more important to be consistent with the rest of the ISA than to save an instruction or couple of clock cycles. Treating rep as a delayed instruction made sense in the context of the P2-hot ISA. Now, however, there are no other instructions that behave this way. From my perspective, the delayed instruction slot is a relic of an old design and therefore inconsistent with the current ISA. By stalling a few clock cylces (instead of supporting a delayed slot), you get something simple like:
            waitx   waita           'wait for middle of 1st data bit
            rep     @r_end, #8      'ready for 8 bits
            testb   inb,#31	wc      'sample rx
            rcr     x,#1            'rotate bit into byte
    r_end   waitx   waitb           'wait for middle of nth data bit
    

    To me, that is about as simple and readable as you can get.
  • jmgjmg Posts: 15,173
    Seairth wrote: »
    To me, that is about as simple and readable as you can get.
    I agree that is simple, but the compromise is less than ideal.
    Thinking some more about labels, and coding styles, I think the two label version would be clearest as
            rep     Rep_Start, Rep_Exit,#8      'ready for 8 bits
            waitx   waita           'wait for middle of 1st data bit
    Rep_Start
            testb   inb,#31	wc      'sample rx
            rcr     x,#1            'rotate bit into byte
            waitx   waitb           'wait for middle of nth data bit
    Rep_Exit
            More code ...
    
    This slight Rep_Exit change makes it clear the repeated code is inside the labels, which is a very similar block-form to High level language repeats, and safely allows labels on separate lines (ie removes possible context confusion)
    ASM code pivots on labels, so this is ASM consistent.

    Two labels means this form is also safely legal
    (This may be generated by HLL compilers )
            waitx   waita           'wait for middle of 1st data bit
            rep     Rep_Start, Rep_Exit,#8      'ready for 8 bits
    ' No opcode here ?, tools can auto-insert NOP
    Rep_Start
            testb   inb,#31	wc      'sample rx
            rcr     x,#1            'rotate bit into byte
            waitx   waitb           'wait for middle of nth data bit
    Rep_Exit
            More code ...
    
  • rjo__rjo__ Posts: 2,114
    edited 2015-09-28 12:09
    To me simpler means fewer things that a novice has to remember... In the original version, I think I have to remember REPS uses N-1 notation... instead of the N that I intend. Not exactly a huge hurdle:)

    If that's it, then for all the others I have to remember more.

    Someone recommended something like:

    Repr D ... for number of Repeats (total iterations)
    Repi D ... for number of Instructions to iterate.

    etc. etc. would be fine, but I think it confuses the brain, which is expecting to be one step away from an actual opcode...not that there is anything wrong with that:)

    This would be another example of a "compound" instruction... which could be dealt with in a documentation section listing instruction variants.
  • ElectrodudeElectrodude Posts: 1,658
    edited 2015-09-28 15:14
    How about renaming rep to repd, and adding a (macro) rep instruction that only exists in the assembler that gets macro-expanded to "repd, nop"? Or, if pnut.exe isn't going to have macros, just renaming rep to repd?
  • jmgjmg Posts: 15,173
    How about renaming rep to repd, and adding a (macro) rep instruction that only exists in the assembler that gets macro-expanded to "repd, nop"?

    That's easily handled in the two label version I gave above, and I expect the non-merged form to be more commonly generated by HLL, and maybe by someone who knows they do not need to optimize size or speed.

    Let the assembler do the simple housekeeping, and there is no need for two opcodes/macros.
    The user has total control of one opcode.
  • cgraceycgracey Posts: 14,155
    edited 2015-09-28 23:01
    I changed the way REP works.

    Now, there is no spacer instruction needed.

    Also, the repeat count from S is now 1-based and 0 means infinite repeat.

    The instruction count from D is still 0-based, but I changed the assembler so that by using @address, it figures the 0-based constant to go into D.

    Here's what REP use looks like now:
    	org			'rep only allowed under org now, not orgh
    
    	rep	@end1,#10       'do this 10 times
    	xor	outa,#1
    	xor	outa,#2
    end1	xor	outa,#3
    
    	rep	@end2,#0	        'do this infinitely
    end2	xor	outa,#7
    

    I don't think we can have it any simpler than that.

    I'm recompiling for the Prop123 and the DE2-115 right now. I will put up new files as soon as they're ready.

    Thank you all for thinking about this REP syntax. This resulted in a needed improvement. The new REP circuitry is slightly larger than before, since it has to resolve more on the initial REP cycle, but this is going to be a lot easier for people to work with.
  • Nice! :)
  • jmgjmg Posts: 15,173
    edited 2015-09-28 23:37
    cgracey wrote: »
    I changed the way REP works.

    Now, there is no spacer instruction needed.

    Great :)
    Is it still 2 cycles to 'get started' ?
    cgracey wrote: »
    I don't think we can have it any simpler than that..
    I think the above can still be improved, and made more edit tolerant.

    consider this code someone may write :
    	org			'rep only allowed under org now, not orgh
    
    	rep	@end1,#10       'do this 10 times
    	xor	outa,#1
    	xor	outa,#2
    end1	
            xor	outa,#3
            xor	outa,#7
    

    Problem: Which opcodes are actually within the REP loop ?

    To me, clearer/simpler/safer is this variant
    	org			'rep only allowed under org now, not orgh
    
    	rep	@exit1,#10       'do this 10 times
    	xor	outa,#1
    	xor	outa,#2
            xor	outa,#3
    exitl	
            xor	outa,#7
    
    This follows assembler convention, which has labels as block delimiters.

    I think this is just an assembler change, not a verilog change.

  • cgraceycgracey Posts: 14,155
    edited 2015-09-28 23:42
    jmg wrote: »
    cgracey wrote: »
    I changed the way REP works.

    Now, there is no spacer instruction needed.

    Great :)
    Is it still 2 cycles to 'get started' ?
    cgracey wrote: »
    I don't think we can have it any simpler than that..
    I think the above can still be improved, and made more edit tolerant.

    consider this code someone may write :
    	org			'rep only allowed under org now, not orgh
    
    	rep	@end1,#10       'do this 10 times
    	xor	outa,#1
    	xor	outa,#2
    end1	
            xor	outa,#3
            xor	outa,#7
    

    Problem: Which opcodes are actually within the REP loop ?

    To me, clearer/simpler/safer is this variant
    	org			'rep only allowed under org now, not orgh
    
    	rep	@exit1,#10       'do this 10 times
    	xor	outa,#1
    	xor	outa,#2
            xor	outa,#3
    exitl	
            xor	outa,#7
    
    This follows assembler convention, which has labels as block delimiters.

    I think this is just an assembler change, not a verilog change.

    You're right. That would be easier to deal with when making edits. Okay, I'll make it work that way.

    The REP instruction takes two clocks.
  • evanhevanh Posts: 15,923
    Nice indeed. So comfy I won't want to venture into HubExec. :D
  • Cluso99Cluso99 Posts: 18,069
    Agreed. Much better!
  • Let's talk about this just a bit more:

    jmg said:

    I think so too. It is really encouraging
    jmg wrote: »
    mindrobots wrote: »
    A label always refers to the adress of the line it is on if it generates something occupying memory or the next line that generates something that takes up space. It can't refer to something that comes before it. That's just wrong!

    Agreed, which is why this is edit safe, context tolerant, and correct (and will confuse no one used to HLL blocks)
    ToggleClkSub   'toggle clock n times and return
    		rep	@ClkExit,n  'repeat next two lines n times (?)
    		setb	outa,#PinPclk
    		clrb	outa,#PinPclk
    ClkExit
    		RET
    
    Two lines are repeated, and the REP exits to CLKExit Label when done . RET is done only once.

    Now, we have a label that works differently from all other labels. Seems to me, that's a thing a user needs to know, not unlike say, understanding that rep starts from 0 on it's line count.

    Worse, given the label has to have the word exit in it, there is this special complication in the assembler, a backward reference where no other label is a backward reference.

    So a user learns how labels work, understands that putting them on lines by themselves is ambiguous practice, except for jmg style labels. Of course, they will ask, "who is jmg?" among many other things.

    If we are going to support a label based use, which I agree with as an alternative to simply including a line count, then the label shouldn't add any other complexity.
    		rep	@.loop, #10
    		nop
    		nop
    .loop	        nop
    		add
    		etc...
    
    

    The above use example does that. And a user doesn't have to remember a specific word, nor understand that a rep label is different from every other label ever written for a Propeller either.

    @cgracey, can we consider the above use instead? It's consistent with all past behavior.

    Secondly, we got rid of special labels with the elimination of call / ret, with the P2 stack. Why add them back? A label should be a label, unless we've got a really sweet case for making that not true, and this one just doesn't seem like that kind of case.



  • cgraceycgracey Posts: 14,155
    edited 2015-10-19 02:13
    For...

    REP @label,repeatcount

    'label' is just a label. It needn't be local or have any special name. It's just like any other label. That label needs to come after the code to be repeated. The @ could become some other character for this use case.
  • Ok, I'll just call those the jmg labels when I teach PASM again. :)

  • cgraceycgracey Posts: 14,155
    At first I made it where the label had to be on the last instruction, but jmg pointed out that it made editing difficult, so we made it work with a label after the last instruction. I think if you are just reading code, the first way is better. If you are writing code, though, the second way is better. To resolve this dilemma, we need to develop new bungee labels which automatically go up one line when changes are done.
  • jmgjmg Posts: 15,173
    edited 2015-10-19 02:47
    potatohead wrote: »
    Ok, I'll just call those the jmg labels when I teach PASM again. :)
    I'm quite confused here.
    There is nothing strange or special about this label, you seem to have locked into some unusual interpretation ?

    If you think of the label as an exit destination, the code is simple to follow.
    ie the label is where the code goes, when done, just like any other exit or destination label, or block delimiter in HLL.

    Code that comes after the label, is not within the block.

    It is edit safe, and tolerates conditional, and will tolerate 64b opcodes.
    potatohead wrote: »
    Worse, given the label has to have the word exit in it, there is this special complication in the assembler, a backward reference where no other label is a backward reference.

    ???? Who said the label has to have the word exit in it ?
    It is just a label, the root exit was used to try and make the flow clear to a reader. Use Rep_Done if you prefer, or Post_rep or Do_After_Rep or anything you like.
Sign In or Register to comment.