Shop OBEX P1 Docs P2 Docs Learn Events
How to use setq in the right way? — Parallax Forums

How to use setq in the right way?

Hello everyone!

It has been quite a while since I last posted here in this forum.
Back then I was using the P1, now I got a P2 Eval Board for Christmas and I am trying to port programs and drivers from Spin1 / PASM to Spin2 / PASM2.

While doing this, I came across something, which don't really understand.

This code does not work correctly, as the "setq" instruction seems to be applied only to the "rdlong", but not to the "wrlong":
hub_cog_transfer
        rdlong buf_ptr,bufAdr
        cmp transfermode, #1    wz
        setq  #128-1                    ' setq to 128 longs to transfer
if_nz   rdlong speed_buf,buf_ptr
if_z    wrlong speed_buf,buf_ptr
hub_cog_transfer_ret
        ret

This code seems to work, but looks a little odd:
hub_cog_transfer
        rdlong buf_ptr,bufAdr
        cmp transfermode, #1    wz
if_z    setq  #128-1                    ' setq to 128 longs to transfer
if_z    wrlong speed_buf,buf_ptr
if_nz   setq  #128-1                    ' setq to 128 longs to transfer
if_nz   rdlong speed_buf,buf_ptr
hub_cog_transfer_ret
        ret

Is there a chance to do this in a more elegant way?
Why does the first mentioned code fail? Is the setq only valid for the very next instruction?


Wishing you all the best for 2021!

Patrick

Comments

  • Yes, SETQ only affects the next instruction, even if it's a dud.
  • YanomaniYanomani Posts: 1,524
    edited 2021-01-01 04:56
    I can be simply wrong (as usual), since I'm not sure about it; moreover (and, unfortunately), I can't test it ATM (thus someone would need to do it, in order to ensure it'll work, or not), but, if you are using "transfermode" to hold a value of "1" or "any other value, but 1", and your code is executing in COG/LUT memory, then I believe you could use the same "transfermode" to hold a skip pattern, and run two out of the three instructions in the sequence, under SKIPF/EXECF rules.
    That way, there would be not any "cancelled", nor decision-based-skipped instructions, in between SETQ and the RDLONG/WRLONG relying on Q's value, since the instruction pipeline would not be disturbed in any way.

    It would look like the following:
    hub_cog_transfer
            rdlong buf_ptr,bufAdr
            skipf transfermode           ' transfermode would hold #%100 for a wrlong transfer, or #%010 for a rdlong one.
            setq  #128-1                 ' setq to 128 longs to transfer
            wrlong speed_buf,buf_ptr
            rdlong speed_buf,buf_ptr
    hub_cog_transfer_ret
            ret
    

    Just in time: Happy New Year to you all! :smile:
  • evanhevanh Posts: 15,187
    edited 2021-01-01 09:31
    Yanomani wrote: »
    ... in between SETQ and the RDLONG/WRLONG relying on Q's value, since the instruction pipeline would not be disturbed in any way.
    Correct.

    An aside: The hidden Q register set by SETQ can be accessed independent of SETQ as well. MUXQ can read Q at any time. XORO32, RDLUT and GETXACC all write to Q. Some cordic ops and COGINIT clear Q by default. CRCNIB uses Q as a work register so needs IRQ shielded to prevent corruption.

  • Yanomani wrote: »
    It would look like the following:
    hub_cog_transfer
            rdlong buf_ptr,bufAdr
            skipf transfermode           ' transfermode would hold #%100 for a wrlong transfer, or #%010 for a rdlong one.
            setq  #128-1                 ' setq to 128 longs to transfer
            wrlong speed_buf,buf_ptr
            rdlong speed_buf,buf_ptr
    hub_cog_transfer_ret
            ret
    

    or
    hub_cog_transfer
            rdlong buf_ptr,bufAdr
            skipf transfermode           ' transfermode = 0 for read, %10 for write
            setq  #128-1                 ' setq to 128 longs to transfer
    _ret_   rdlong speed_buf,buf_ptr
    _ret_   wrlong speed_buf,buf_ptr
    
  • Thank you very much everyone!

    I tested both proposed variants and they work just as expected.

    There is still a lot for me to learn and discover regarding the P2 programming, but I must admit, that the P2 is a very powerful chip.

    To give you an example:

    The above code replaces all this of the orginal P1 "mb_rawb_spi" code by Jonathan "lonesock" Dummer, which for the P1 was already an incrediby fast solution for the hub <-> cog transfer.
    hub_cog_transfer
    ' setup for all 4 passes
            mov ctrb,clockXferMode
            mov frqb,#1
            rdlong buf_ptr,bufAdr
            mov ops_left,#4
            movd transfer_long,#speed_buf
    four_transfer_passes
            ' sync to the Hub RAM access
            rdlong tmp1,tmp1
            ' how many long to move on this pass? (512 bytes / 4)longs / 4 passes
            mov tmp1,#(512 / 4 / 4)
            ' get my starting address right (phsb is incremented 1 per clock, so 16 each Hub access)
            mov phsb,buf_ptr
            ' write the longs, stride 4...low 2 bits of phsb are ignored
    transfer_long
            rdlong 0-0,phsb
            add transfer_long,incDest4
            djnz tmp1,#transfer_long
            ' go back to where I started, but advanced 1 long
            sub transfer_long,decDestNminus1
            ' offset my Hub pointer by one long per pass
            add buf_ptr,#4
            ' do all 4 passes
            djnz ops_left,#four_transfer_passes
            ' restore the counter mode
            mov frqb,#0
            mov ctrb,clockInMode
    hub_cog_transfer_ret
            ret
    
  • TonyB_ wrote: »

    or
    hub_cog_transfer
            rdlong buf_ptr,bufAdr
            skipf transfermode           ' transfermode = 0 for read, %10 for write
            setq  #128-1                 ' setq to 128 longs to transfer
    _ret_   rdlong speed_buf,buf_ptr
    _ret_   wrlong speed_buf,buf_ptr
    

    There's Occam's Razor, shaving again!

    I'm kind of anticipating (crystal ball lits on...): someday, somewhere, someone will be asking for just a few MORE instructions to be ADDED to any given snippet, in order to justify starting another COG... :LOL:
  • TonyB_TonyB_ Posts: 2,125
    edited 2021-01-01 15:26
    Yanomani wrote: »
    TonyB_ wrote: »

    or
    hub_cog_transfer
            rdlong buf_ptr,bufAdr
            skipf transfermode           ' transfermode = 0 for read, %10 for write
            setq  #128-1                 ' setq to 128 longs to transfer
    _ret_   rdlong speed_buf,buf_ptr
    _ret_   wrlong speed_buf,buf_ptr
    

    There's Occam's Razor, shaving again!

    I'm kind of anticipating (crystal ball lits on...): someday, somewhere, someone will be asking for just a few MORE instructions to be ADDED to any given snippet, in order to justify starting another COG... :LOL:

    If there is a fixed difference between [bufAdr] and [buf_ptr], then the time between RDLONG and RDLONG/WRLONG can be determined and one or both of SKIPF and SETQ might be effectively "zero-cycle" instructions.

    I don't like to see a RET if _RET_ will do. Sometimes, however, adding a RET can save code overall:
    
    'skipping active
    
    	<instrA>
    	<instrB>
    	<instrC>
    routine
    	<instrD>
    	<instrE>
    	<instrF>
    	<instrG>
    	ret		'RET skipped by all skip patterns!
     			'Put here to make <instrD>..<instrG>
    			'a subroutine callable from elsewhere
    	<instrH>
    	<instrI>
    	...
    
  • evanhevanh Posts: 15,187
    I use RET WCZ quite often because I like to use the flags across calls.

Sign In or Register to comment.