Shop OBEX P1 Docs P2 Docs Learn Events
The PAR register — Parallax Forums

The PAR register

LoopyBytelooseLoopyByteloose Posts: 12,537
edited 2013-11-10 14:02 in Propeller 1
I keep wondering, does the PAR register never change once the Propeller starts a Cog (unless the Cog is stopped and restarted with a different PAR value)?

Comments

  • Mark_TMark_T Posts: 1,981
    edited 2013-11-10 06:56
    I keep wondering, does the PAR register never change once the Propeller starts a Cog (unless the Cog is stopped and restarted with a different PAR value)?

    Which PAR register? It has a shadow in COG ram as I understand it, so when you access it as
    a source register you get the true PAR register, when as a dest register you get the shadow. The
    true register is read-only from cog code, only the hub can change it (via cognew/coginit). The
    true register is 14 bits (two trailing bits are hardwired as 0), if I understand things.
  • Mike GreenMike Green Posts: 23,101
    edited 2013-11-10 07:23
    The PAR register changes only when a COGINIT / COGNEW instruction is executed. As Mark_T mentioned, PAR, like other "read-only" special locations in cog memory, has a corresponding "shadow" memory location that can be accessed through the destination field of an instruction.
  • Duane DegnDuane Degn Posts: 10,588
    edited 2013-11-10 07:55
    So if you use the "shadow" PAR register in a destination field what gets read the next time PAR is used as a source?

    Does one need to worry about these shadow registers when writing code? I don't understand this aspect of the Propeller but I feel like I can usually get the Propeller to do what I want (which is very fun).

    How helpful would it be to understand this aspect of the Propeller and if it's important to know where do I learn about it? Okay, even if it's not very important where do I learn about it? Is it in the datasheet?
  • LoopyBytelooseLoopyByteloose Posts: 12,537
    edited 2013-11-10 09:31
    I think I got the answer I required. I do understand the why and the wherefore of only 14 bits. But the concept of 'shadow' register is a bit distracting.

    A. The PAR register I refer to is one of 16 in each cog.
    B. Its contents seem to point to a starting point for loading the compiled assembly language into a cog.

    Not sure where this 'shadow' register would exist, or why. But Mike G does say it does exist in the destination field of PASM.

    The compiler might also use it plus and additional offset to locate variables, constants, routines, and so on in hubram (aka main ram) after the code actually starts to run. I am a bit uncertain if the second purpose would over-write the the code that was loaded into cog ram.

    Does the 'shadow register' have any unusually clever alternative purpose, or is this just a factoid to be kicked about?
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2013-11-10 09:34
    Re B: The contents of the PAR register can be whatever you want it to be when you start the cog, so long as the bottom two bits are zero. PAR is typically used to pass a parameter -- or the address of a block of parameters -- to the newly started cog.

    Also, writing to PAR's shadow register does not change the contents of PAR when read as a source register.

    -Phil
  • LoopyBytelooseLoopyByteloose Posts: 12,537
    edited 2013-11-10 09:42
    And so, it just a starting point for all addresses that the active cog code might use to reach hub ram.
  • Duane DegnDuane Degn Posts: 10,588
    edited 2013-11-10 10:35
    And so, it just a starting point for all addresses that the active cog code might use to reach hub ram.

    You can also "poke" the values (or addresses) into the longs before launching the cog.

    Apparently poking the values in before launching is bad form since it makes the object harder to convert for use with C.

    But code like that below always bugs me since it uses up so much of the cog RAM.
    mov     t1, par
                            add     t1, #4
                            rdlong  highTime, t1 'pulseHighTicks
    
    
                            mov     t1, par
                            add     t1, #8   'pulseLowTicks
                            rdlong  lowTime, t1
    
    
                            mov     t1, par
                            add     t1, #12   'delayTicks
                            rdlong  delayTime, t1
    
    
                            mov     t1, par
                            add     t1, #16  'delayRequestTicks
                            rdlong  delayRequestTime, t1
    

    But I just had a brilliant idea. Why not reuse this cog RAM for variables normally reserved with the "res" statement?
    highTime                mov     t1, par
    data0                   add     t1, #4
    data1                   rdlong  highTime, t1 'pulseHighTicks
    
    
    data2                   mov     t1, par
    lowTime                 add     t1, #8   'pulseLowTicks
    andSoOn                 rdlong  lowTime, t1
    
    
                            mov     t1, par
                            add     t1, #12   'delayTicks
                            rdlong  delayTime, t1
    

    Once a line of code has been executed (and assuming it's only executed once), it could then be used for variable storage.

    The main downside to doing this would be the code would be harder to read.

    (Why does the second line of code get bumped up with the first in code blocks? (I've fixed it here, but it's annoying.))
  • tonyp12tonyp12 Posts: 1,951
    edited 2013-11-10 11:15
    As t1 does not get destroyed during a rdlong, no need to reset it with original PAR value.
    I try no to use cog longs as buffers and instead read hub and insert in to self-modifying code in the first place.
    But could be a wash that any longs are saved depending if you have to reset t1 with PAR more often.
                    mov     t1, par
                    add     t1, #4                  
                    rdlong  highTime, t1            'pulseHighTicks
                    add     t1, #4                  'pulseLowTicks
                    rdlong  lowTime, t1
                    add     t1, #4                  'delayTicks
                    rdlong  delayTime, t1
    
  • Duane DegnDuane Degn Posts: 10,588
    edited 2013-11-10 11:25
    tonyp12 wrote: »
    As t1 does not get destroyed during a rdlong, no need to reset it with original PAR value.

    Thanks Tony, I hadn't noticed that.

    It's not my code. I was just looking for an example of reading in a list of variables from hub and it was the first one I saw.

    I am using the object in a project which is starting to get tight on space so I'll clean up the object a bit to save some room. Thanks for pointing it out.
  • tonyp12tonyp12 Posts: 1,951
    edited 2013-11-10 11:52
    If you have more than 6 longs in a sequential array to copy, a loop would do it.
                    mov     t1, par
                    movd    :long, #highTime        'location of first long cog array
                    mov     t2, #8                  '8 longs to copy
    :loop           rdlong  0-0, t1                 'pulseHighTicks first
                    add     t1, #4                  'switch with rdlong if PAR+4 is the first hub array location
                    add     :loop, my_bit9          'cogs  are always longs, so next space +1
                    djnz    t2, #:loop
                   
    
    my_bit9         long 1<<9                       'equ 512, as direct add's can only do 0-511
    t1              res 1
    t2              res 1   
    hightime        res 1
    lowTime         res 1
    delayTime       res 1
    NextTime1       res 1
    NextTime2       res 1
    NextTime3       res 1
    NextTime4       res 1
    NextTime5       res 1
    
    
  • Duane DegnDuane Degn Posts: 10,588
    edited 2013-11-10 12:44
    tonyp12 wrote: »
    If you have more than 6 longs in a sequential array to copy, a loop would do it.
                    mov     t1, par
                    movd    :long, #highTime        'location of first long cog array
                    mov     t2, #8                  '8 longs to copy
    :loop           rdlong  0-0, t1                 'pulseHighTicks first
                    add     t1, #4                  'switch with rdlong if PAR+4 is the first hub array location
                    add     :loop, my_bit9          'cogs  are always longs, so next space +1
                    djnz    t2, #:loop
                   
    
    my_bit9         long 1<<9                       'equ 512, as direct add's can only do 0-511
    t1              res 1
    t2              res 1   
    hightime        res 1
    lowTime         res 1
    delayTime       res 1
    NextTime1       res 1
    NextTime2       res 1
    NextTime3       res 1
    NextTime4       res 1
    NextTime5       res 1
    
    

    Thanks Tony. I knew there was a way to do that but the self-modifying code still makes my head swim.

    It seems like your code would be useful even with 5 longs needing to be copied since I could reuse t1 and t2 as temp variable elsewhere.

    As long as you got my head swimming, could you reduce the above code by one line? What if you add to both the source and the destination at once?
                    mov     t1, par
                    movd    :long, #highTime        'location of first long cog array
                    mov     t2, #8                  '8 longs to copy
    :loop           rdlong  0-0, t1                 'pulseHighTicks first
                    'add     t1, #4                  'switch with rdlong if PAR+4 is the first hub array location
                    add     :loop, my_modify          'cogs  are always longs, so next space +1 
                    djnz    t2, #:loop
                   
    
    my_modify         long (1<<9) + 4                       'equ 516, as direct add's can only do 0-511
    t1              res 1
    t2              res 1   
    hightime        res 1
    lowTime         res 1
    delayTime       res 1
    NextTime1       res 1
    NextTime2       res 1
    NextTime3       res 1
    NextTime4       res 1
    NextTime5       res 1
    
    

    Of could you even do away with t1?
                    movs    :long, par
                    movd    :long, #highTime        'location of first long cog array
                    mov     t2, #8                  '8 longs to copy
    :loop           rdlong  0-0, 0-0                 'pulseHighTicks first
                    'add     t1, #4                  'switch with rdlong if PAR+4 is the first hub array location
                    add     :loop, my_modify          'cogs  are always longs, so next space +1 
                    djnz    t2, #:loop
                   
    
    my_modify         long (1<<9) + 4                       'equ 516, as direct add's can only do 0-511
    't1              res 1
    t2              res 1   
    hightime        res 1
    lowTime         res 1
    delayTime       res 1
    NextTime1       res 1
    NextTime2       res 1
    NextTime3       res 1
    NextTime4       res 1
    NextTime5       res 1
    

    I'm going to stop before my head explodes.

    Thanks Tony.
  • tonyp12tonyp12 Posts: 1,951
    edited 2013-11-10 13:12
    >Of could you even do away with t1?
    >:loop rdlong 0-0, 0-0 'pulseHighTicks first

    No, though rdlong allows direct addressing :loop rdlong 0-0, #0-0
    You would be limited to the first 512bytes (128longs) of hub ram
    And with modular code, you should not count on your reserved hub variables to be located at a specific block.
    So the source must point to a cog variable that holds the whole 32bit address.
  • Duane DegnDuane Degn Posts: 10,588
    edited 2013-11-10 13:23
    tonyp12 wrote: »
    >Of could you even do away with t1?
    >:loop rdlong 0-0, 0-0 'pulseHighTicks first

    No, though rdlong allows direct addressing :loop rdlong 0-0, #0-0
    You would be limited to the first 512bytes (128longs) of hub ram
    So the source must point to a cog variable that holds the whole 32bit address.

    You're right. I knew that.

    Did I miss anything with my thought about adding to both the destination and source at once?

    BTW, The code is from a PlayStation 2 driver. I modified the driver to read the analog button values (among other things).

    I'm going to post a more recent version (I'm using the object in my robot remote project) but I think I'll add your loop idea to the object before posting an updated version.

    Thanks for the code example.
  • tonyp12tonyp12 Posts: 1,951
    edited 2013-11-10 13:32
    >Did I miss anything with my thought about adding to both the destination and source at once?
    >my_modify long (1<<9) + 4 'equ 516, as direct add's can only do 0-511

    That part would have worked.
    Some people would feel worried that rollover from the lower 9bits will corrupt the upper 9bits.
    But as you loop count take in to account your allotted hub ram and cog ram windows,
    S will not corrupt D, and D will not corrupt I.
  • Duane DegnDuane Degn Posts: 10,588
    edited 2013-11-10 13:42
    tonyp12 wrote: »
    >Did I miss anything with my thought about adding to both the destination and source at once?
    >my_modify long (1<<9) + 4 'equ 516, as direct add's can only do 0-511

    That part would have worked.
    Some people would feel worried that rollover from the lower 9bits will corrupt the upper 9bits.
    But as you loop count take in to account your allotted hub ram and cog ram windows,
    S will not corrupt D, and D will not corrupt I.

    On second thought I don't think it would work. I'm treating the source as immediate.

    It wouldn't add 4 to t1 it would pretty much just mess it up. My guess is it would use the number stored in delayTime (whatever that might be) as the new hub address to read from.

    I'll stick with your original version.

    Did I mention how I feel about self-modifying code?
  • tonyp12tonyp12 Posts: 1,951
    edited 2013-11-10 13:44
    >I'm treating the source as immediate.

    That is why I added # in front of 0-0
    >>rdlong allows direct addressing :loop rdlong 0-0, #0-0

    So it will work if you knew the hub array is located in the lower 128longs (bad programming though)

    >Did I mention how I feel about self-modifying code?
    Think of it as merging opcode and a reserved variable in to one.
    With a label in front of a opcode you will know its location and the D and S field
    can now be changed on the fly (with a least another line of code before it's reached)
  • Duane DegnDuane Degn Posts: 10,588
    edited 2013-11-10 13:50
    tonyp12 wrote: »
    That is why I added # in front of 0-0

    Okay. Thanks again.

    So still a bad idea.
  • Mike GreenMike Green Posts: 23,101
    edited 2013-11-10 14:02
    To answer about "shadow RAM": It's easier to build a 512 long memory than a 496 long memory, so the cogs really have 512 longs of memory each. Certain addresses in the range $1F0-$1FF actually refer to hardware registers and there are multiplexors that select either these registers or the memory. For registers that are read / write, all works great. For registers that are read-only, the multiplexor driven by the source address selects the appropriate register instead of the memory. The destination multiplexor is a problem though since it doesn[t make sense to write to a read-only register and the multiplexor defaults to the actual memory. For those instructions that use the destination field as a read / write location, the read-only addresses were left so they select the actual memory rather than the read-only register. Instruction fetches also go directly to actual memory rather than through the multiplexor. Note that, when a read / write location is used in the destination field, the result of any operation is written to both the register and the underlying RAM location, hence the use of the term "shadow".
Sign In or Register to comment.