Shop OBEX P1 Docs P2 Docs Learn Events
Question about the PINx & DIRx registers and instructions AND, ANDN, OR, XOR behavour — Parallax Forums

Question about the PINx & DIRx registers and instructions AND, ANDN, OR, XOR behavour

Cluso99Cluso99 Posts: 18,069
edited 2013-06-06 15:25 in Propeller 2
I have been converting an old P1 program that uses AND, ANDN, OR and XOR instructions to modify the I/O pins using the INA and OUTA registers on the P1.

We now have a PINA register that replaces/combines the INA and OUTA registers. From the information available, reading the PINA register reads the level appearing at the pin(s).

What we did in the past when we wanted to change a few pins was to do...
XOR OUTA,#7 'inverts the current P0,1,2 pins.

This instruction reads the output latch of OUTA, exclusive ors the lower 3 bits, and writes back to the output register.

When the P2 performs this using PINA, I presume the output pins will be read (not the output latch), exclusive ors the lower 3 bits, and writes back the result. This could result in an error in writing back the results because an output pin(s) may be overridden by an external force, resulting in incorrect operation. Is this assumption correct? Is there any simple way around this besides keeping a software set of the PINA output register?

I also presume the default map of the PINA/DIRA is to P0..P31 and I do not need to initialise this setting unless I require a different bank of pins on PINA/DIRA. Is this correct?
«13

Comments

  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2013-05-27 20:40
    My assumption would be that any read/modify/write instruction would act with and upon the destination register exclusively which, in your example, is the output latch, not the pin level.

    I think the rule is simply this: destination PINA == OUTA, source PINA == INA.

    -Phil
  • Cluso99Cluso99 Posts: 18,069
    edited 2013-05-27 21:47
    Phil: I thought that a long time ago it was discussed that read-modify-write would not work as expected. So I was wondering if this held true for the final silicon, or in fact I was mistaken.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2013-05-27 21:59
    Cluso99 wrote:
    I thought that a long time ago it was discussed that read-modify-write would not work as expected.

    I don't recall that discussion. (In the P1, that's only true of certain counter and read-only registers.)

    Anyway, quite awhile back, I wrote a script to examine several hundred Spin/PASM programs for INA/OUTA usage and sent the results to Chip. INA, of course, was never used as a destination register and OUTA was never (with maybe one or two exceptions) used as a source register in any of those programs. So it made sense to assign them -- with no loss of functionality -- to a single logical address, PINA, even though they're two separate registers. That notwithstanding, there's no reason to expect that
    xor pina,#7

    won't be equivalent to the former
    xor outa,#7

    -Phil
  • BeanBean Posts: 8,129
    edited 2013-05-28 05:06
    I think Chip was asked this and he said that back-to-back xor operations on the same pin(s) would NOT work. You had to have an instruction between them.

    Bean
  • cgraceycgracey Posts: 14,206
    edited 2013-05-28 09:07
    When PINA is used in an instruction, either INA or OUTA is accessed. OUTA is accessed for writes (D) or read-modify-writes (D). For non-writing D accesses and all S accesses, INA is read:

    MOV x,PINA 'INA is read
    XOR PINA,x 'OUTA is read and written
    TEST PINA,x 'INA is read since there is no write

    Back-to-back XORs are fine, because they are accessing OUTA, and are not dependent upon INA feedback.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2013-05-28 09:27
    cgracey wrote:
    TEST PINA,x 'INA is read since there is no write
    That's an odd one, Chip. I would have expected that since test is an and with nr, it would use the same register as and (i.e. outa), as opposed to
    test x,pina

    which clearly reads ina, as in the P1.

    This is especially vexing as it leaves two ways to test ina but none to test outa.

    -Phil
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2013-05-28 09:37
    Chip, could I make a suggestion? Instead of pina being allowed in PASM programs, keep ina and outa and flag an error if they're used improperly. The resulting programs will be easier to understand, and people will be spared from making wrong assumptions about which register is being referenced.

    Thanks,
    -Phil
  • SeairthSeairth Posts: 2,474
    edited 2013-05-28 09:47
    Here's the post I think everyone is referring to:

    [post]1145045[/post]
  • cgraceycgracey Posts: 14,206
    edited 2013-05-28 09:53
    Chip, could I make a suggestion? Instead of pina being allowed in PASM programs, keep ina and outa and flag an error if they're used improperly. The resulting programs will be easier to understand, and people will be spared from making wrong assumptions about which register is being referenced.

    Thanks,
    -Phil

    That would be a good solution to the dilemma, but I don't know if I'd make it mandatory, because I like PINA and it doesn't look like two registers to me. In my experience, so far, PINA works intuitively and I haven't had to stop and think about what was happening.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2013-05-28 10:04
    cgracey wrote:
    ... and I haven't had to stop and think about what was happening.

    I guess it helps that you designed it. :) However, I think the mere existence of this thread speaks to the confusion that others might suffer.

    -Phil
  • BeanBean Posts: 8,129
    edited 2013-05-28 10:07
    cgracey wrote: »
    When PINA is used in an instruction, either INA or OUTA is accessed. OUTA is accessed for writes (D) or read-modify-writes (D). For non-writing D accesses and all S accesses, INA is read:

    MOV x,PINA 'INA is read
    XOR PINA,x 'OUTA is read and written
    TEST PINA,x 'INA is read since there is no write

    Back-to-back XORs are fine, because they are accessing OUTA, and are not dependent upon INA feedback.

    Sorry Chip, I must have been thinking of something else....

    Bean
  • cgraceycgracey Posts: 14,206
    edited 2013-05-28 14:28
    Cluso99 wrote: »
    ...I also presume the default map of the PINA/DIRA is to P0..P31 and I do not need to initialise this setting unless I require a different bank of pins on PINA/DIRA. Is this correct?

    That's right.
  • cgraceycgracey Posts: 14,206
    edited 2013-05-28 14:45
    I guess it helps that you designed it. :) However, I think the mere existence of this thread speaks to the confusion that others might suffer.

    -Phil

    Perhaps, but it seems to me that people are supposing there's some trap awaiting them, when there really isn't:

    - When you are reading PINS, you read the inputs.
    - When you are writing PINS, you write the outputs.
    - Read-modify-writes to PINS work on the outputs, as this is most useful and probably expected.

    (I'm just restating those rules for general consumption on the forum.)

    I think the worries are much more theoretical than practical, especially since there are bidirectional clock delays between PINS and the actual I/O pins, which would make a mess of read-modify-write operations that involved both inputs AND outputs. That would cause some major headaches and require a lot of splainin'.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2013-05-28 15:59
    Chip,

    What happens in Spin, then, with these two statements?
    pina &= 7
    pina := pina & 7
    

    Would it not be clearer -- especially in the second case -- to specify which register (ina vs. outa) is being read? (Or is it not possible to read outa?) I'm just afraid that pina's appearance of ambiguity is going to trip up a lot of people.

    -Phil
  • SeairthSeairth Posts: 2,474
    edited 2013-05-28 16:43
    Chip,

    What happens in Spin, then, with these two statements?
    pina &= 7
    pina := pina & 7
    

    Would it not be clearer -- especially in the second case -- to specify which register (ina vs. outa) is being read? (Or is it not possible to read outa?) I'm just afraid that pina's appearance of ambiguity is going to trip up a lot of people.

    -Phil

    I think you are correct that there isn't a direct way to simply read from OUTA, since source is always INA. As a result, I would think the interpreter must implement those as a single instruction (AND PINA, #7), as it just doesn't make sense to read from INA (one instruction) and then write to OUTA (the next instruction).

    So if you need to "read" OUTA, you would need to use another register to hold the current value. I wonder if you could use INDA with the shadow register at the PINA address. But even if you could, it's probably not worth the extra confusion, complexity, etc.
  • cgraceycgracey Posts: 14,206
    edited 2013-05-28 16:51
    Chip,

    What happens in Spin, then, with these two statements?
    pina &= 7
    pina := pina & 7
    

    Would it not be clearer -- especially in the second case -- to specify which register (ina vs. outa) is being read? (Or is it not possible to read outa?) I'm just afraid that pina's appearance of ambiguity is going to trip up a lot of people.

    -Phil

    Now, that's a problem!

    Not only will we need different names, we'll need to maintain separate variables for the OUTs. This can be hidden in the interpreter, but it makes me wish I had left the INs and OUTs separate, like on Prop1. The problem we can't get around is when you have multitasking code running in the same cog as Spin. Spin can keep proper track of its OUTs, but the concurrent PASM code will not play well with Spin, as it's wont to do operations directly on PINx registers.

    Maybe the solution to this is treating Spin assignments like 'PINA &= 7' as atomic operations in the interpreter. That would better prime people for PASM, anyway. Only writes and read-modify-writes would be allowed to affect PINx registers. I think that might be better. What do you think?
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2013-05-28 18:29
    cgracey wrote:
    This can be hidden in the interpreter, but it makes me wish I had left the INs and OUTs separate, like on Prop1.
    I still think you did the right thing, except that I might not have made the distinction between whether pina gets written or not -- just whether it appears as D or S (or in a bit statement maybe -- I guess it can get complicated). I'd still be inclined to dispense with pina as a symbol and use ina and outa. That way, each of these would mean exactly what they say:
    outa &= 7    'Executed as read/modify/write.
    outa := ina & 7    'Executed as load/AND/store
    outa := outa & 7    'Executed as load/AND/store (assuming [b]outa[/b] can be read) or optimized to read/modify/write where it's possible to do so.
    outa := (outa + 2) & 7    'Executed as load/ADD/AND/store (assuming [b]outa[/b] can be read)
    outa := (ina + 2) << 4 'Executed as load/ADD/SHIFT/store
    ina &= 7    'Flagged as an error.
    

    I prefer the above, because it's perfectly clear what the programmer's intent is, and the Spin programmer doesn't have to know that ina and outa share the same address -- just that certain usages are invalid.

    Here's another pathological example that could be cleared up by using outa:
    x := (pina := 7) + 5
    

    BTW, it's still not clear to me whether outa can be read directly. If not, the interpreter could always transfer data to it via a dedicated mirror register and never use the read/modify/write form.

    -Phil
  • David BetzDavid Betz Posts: 14,516
    edited 2013-05-28 19:11
    cgracey wrote: »
    Maybe the solution to this is treating Spin assignments like 'PINA &= 7' as atomic operations in the interpreter. That would better prime people for PASM, anyway. Only writes and read-modify-writes would be allowed to affect PINx registers. I think that might be better. What do you think?
    That seems like it will be confusing to people. In all other cases the following two statements have the same effect and they will probably generate the identical code if compiled with PropGCC.
    foo &= 7
    foo := foo & 7
    

    or in C:
    foo &= 7;
    foo = foo & 7;
    

    Handling the assignment operator as a special case breaks this.
  • jmgjmg Posts: 15,175
    edited 2013-05-28 20:23
    outa &= 7    'Executed as read/modify/write.
    outa := ina & 7    'Executed as load/AND/store
    outa := outa & 7    'Executed as load/AND/store (assuming [b]outa[/b] can be read) or optimized to read/modify/write where it's possible to do so.
    outa := (outa + 2) & 7    'Executed as load/ADD/AND/store (assuming [b]outa[/b] can be read)
    outa := (ina + 2) << 4 'Executed as load/ADD/SHIFT/store
    ina &= 7    'Flagged as an error.
    

    I prefer the above, because it's perfectly clear what the programmer's intent is, and the Spin programmer doesn't have to know that ina and outa share the same address -- just that certain usages are invalid.

    I like the format above, as it makes it clear what will happen, and also gives any compiler writer, some leverage to check what the user intended.
    The objective is to have 'no surprises' in any language, and to make sure the eventual ASM code created does what was intended.

    cgracey wrote:
    When PINA is used in an instruction, either INA or OUTA is accessed. OUTA is accessed for writes (D) or read-modify-writes (D). For non-writing D accesses and all S accesses, INA is read:

    MOV x,PINA 'INA is read
    XOR PINA,x 'OUTA is read and written
    TEST PINA,x 'INA is read since there is no write

    Back-to-back XORs are fine, because they are accessing OUTA, and are not dependent upon INA feedback.

    and DJNZ/IJNZ/DJZ/IJZ ?
  • Cluso99Cluso99 Posts: 18,069
    edited 2013-05-28 20:27
    Thanks Chip for clearing that one up for pasm. It works exactly as I would expect (and hoped). I was reading more into it than was necessary due to a vague recollection of some problems. No further explanations should be necessary.

    I see the issue with spin and perhaps other high level languages. If you have to rev the silicon, perhaps a separate new instruction for reading the outx register may be in order just to cover this specific situation.
  • David BetzDavid Betz Posts: 14,516
    edited 2013-05-28 20:34
    Cluso99 wrote: »
    Thanks Chip for clearing that one up for pasm. It works exactly as I would expect (and hoped). I was reading more into it than was necessary due to a vague recollection of some problems. No further explanations should be necessary.

    I see the issue with spin and perhaps other high level languages. If you have to rev the silicon, perhaps a separate new instruction for reading the outx register may be in order just to cover this specific situation.
    It seems like it would be better to just separate INA and OUTA again.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2013-05-28 20:49
    David Betz wrote:
    It seems like it would be better to just separate INA and OUTA again.
    Ooh, it's late; let's not go there, m'kay? :) As long as outx can be read somehow, I don't see a major problem with the way the hardware is designed -- only with the way things are named in the dev tools (please no pinx). Even if outx can't be read, the problem is easily solved in the interpreter via regular registers from which all writes to outx take place.

    -Phil
  • SeairthSeairth Posts: 2,474
    edited 2013-05-28 21:01
    outa := ina & 7    'Executed as load/AND/store
    

    What does INA contain when the pin direction is set to OUT?
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2013-05-28 21:24
    Seairth wrote:
    What does INA contain when the pin direction is set to OUT?
    The actual logic levels extant on the pins, 'same as if they were driven externally.

    -Phil
  • cgraceycgracey Posts: 14,206
    edited 2013-05-28 22:25
    Well, there is no way to read OUTA. You can only write it or read-modify-write it using atomic operations like OR, XOR, AND, NOT, etc.

    We could easily establish mirror registers in high-level languages that get copied to PINx, but this leaves on big problem: concurrent, multi-tasking, user-written PASM code that will be able to run along with the Spin interpreter will tend to do operations on PINx directly, blowing up the mirror-register concept. That's why it might be necessary to break PINx up into OUTx and INx within high-level languages so that OUTx operations are always atomic. In other words, OUTx can only appear on the left-hand side of an assigment and INx can only appear on the right-hand side. Then, we have compatibility between multi-tasking PASM code and high-level code that is either interpreted or compiled.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2013-05-28 22:58
    cgracey wrote:
    In other words, OUTx can only appear on the left-hand side of an assigment and INx can only appear on the right-hand side.
    I don't have a problem with that, Chip. You could even relax that a little in the case of
    x := (outa := 7) << 4

    since the value of the parenthesized subexpression already exists on the stack without having to read outa.

    -Phil

    Addendum: But this would be a problem:
    x := (outa &= 7) << 4
  • SeairthSeairth Posts: 2,474
    edited 2013-05-29 03:14
    The actual logic levels extant on the pins, 'same as if they were driven externally.

    -Phil

    So INA would mirror OUTA for those pins? Would there be a delay from when OUTA is set and INA reflects the new output level? And it would still be possible for an external circuit to override that value (e.g. Pull to a logic zero even though the output is set to a logic one)?
  • David BetzDavid Betz Posts: 14,516
    edited 2013-05-29 04:36
    cgracey wrote: »
    Well, there is no way to read OUTA. You can only write it or read-modify-write it using atomic operations like OR, XOR, AND, NOT, etc.

    We could easily establish mirror registers in high-level languages that get copied to PINx, but this leaves on big problem: concurrent, multi-tasking, user-written PASM code that will be able to run along with the Spin interpreter will tend to do operations on PINx directly, blowing up the mirror-register concept. That's why it might be necessary to break PINx up into OUTx and INx within high-level languages so that OUTx operations are always atomic. In other words, OUTx can only appear on the left-hand side of an assigment and INx can only appear on the right-hand side. Then, we have compatibility between multi-tasking PASM code and high-level code that is either interpreted or compiled.
    I think the problem with using mirror registers is that it requires special handling in the compiler/interpreter. This is probably fine for Spin because it is specifically for the Propeller but it may be a problem for generic compilers like GCC or maybe even LCC (used by Catalina). It is also a problem for the user since those registers don't obey normal assignment operator semantics. Maybe that's okay though since hardware registers often require special treatment. It's just a little disappointing because P1 didn't require any special handling of the pin registers.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2013-05-29 09:00
    I think common usage will dictate that we don't have a real problem here. outa virtually never has to be read, except in r/m/w atomic operations, and we've got that covered. We've always had the rule that ina not be used on the left side of a Spin assignment. Adding the rule that outx not be used on the right does not seem burdensome to me. Pathological examples aside, such use would almost never occur in real life.

    -Phil
  • cgraceycgracey Posts: 14,206
    edited 2013-05-29 09:19
    I think common usage will dictate that we don't have a real problem here. outa virtually never has to be read, except in r/m/w atomic operations, and we've got that covered. We've always had the rule that ina not be used on the left side of a Spin assignment. Adding the rule that outx not be used on the right does not seem burdensome to me. Pathological examples aside, such use would almost never occur in real life.

    -Phil

    And we'd have to apply the same rules for DIRx, in order to keep all operations atomic so that concurrent PASM code runs properly.
Sign In or Register to comment.