RMW and solutions for it

Author
Stefan Uhlemayr
Super Member
  • Total Posts : 4292
  • Reward points : 0
  • Joined: 2005/05/12 12:25:46
  • Location: Germany
  • Status: offline
2010/02/11 12:42:49 (permalink)
5 (5)

RMW and solutions for it

This is a compiliation of working software-solutions for the read-modify-write-effect (rmw) with its pro's and con's (I'm aware of...):

Before having a closer look for the software-solutions you should have a look into the datasheet for your controller. If it has specific output-latch-registers (like the LATx-registers in the PIC18-series for example), then you should always use these registers for any write to an output, as this is the simplest way for avoiding any rmw-effects on the outputs.

The rmw-effect itself is affecting all instructions, which are reading a register, modifying it and writing it back. The classic examples for this are the BSF/BCF-instructions (but others are also concerned, a look into the datasheet will help here). The effect will cause problems, when the output-state still hasn't reached a voltage-level, which would read it according to its present logical state. More informations about you may find here from Michael Rigby-Jones, here from John Oldenkamp, here from Danish Ali and here from myself.



How to solve now this issue:

Solution 1: Keep the load on the output-pin small, so that you don't exceed the output-current with guaranteed voltage-level.

They are mentioned as "output-high voltage, param. D090", which gives a min. output-voltage of Vdd-0.7V for driving an output-current of 3.0 mA and as "output-low voltage, param. D080", which gives a max. output-voltage of 0.6V for sinking an output-current of 8.5 mA. These voltage-levels will ensure a safe read of the appropriate logic-level, independent on the input-buffer (ttl or st) of the pin. Have a look into the datasheet of the PIC you are using for getting the exact values. Additional, the max. allowed capacitance, which may connected to an output-pin, is very limited. Param. D101(a) specifies a max. capacitance of 50pF to meet the ac-timing-specifications. Note, that all these parameters are temperatur-depending, too...

Example:

  bsf PORTB,RB0       ;set PortB,0
  bcf PORTB,RB3       ;clear PortB,3

Pro:
  • This is the fastest solution, as there are no additional instructions required in the code.

Cons:
  • Max. output-current is pretty limited
  • If pin is still in analog-mode, it can't work
  • No capacitance (bigger then the above mentioned 50pF) should be connected to the output



Solution 2: Usage of a shadow-register

For every output-register you keep a software-latch-register (=shadow-register of the output). Any modifications to the output you do to this shadow-register, and then the shadow-register is simply copyied to the output.

Example:

  bsf PB_Shadow,RB0   ;set bit 0 in the shadow-register for PortB
  movf PB_Shadow,W    ;move the Shadow-register into W
  movwf PORTB         ;move W into PORTB
  bcf PB_Shadow,RB3   ;clear bit 3 in the shadow-register for PortB
  movf PB_Shadow,W    ;move the Shadow-register into W
  movwf PORTB         ;move W into PORTB

Pro:
  •  Works in all cases!

Cons:
  •  1 RAM-register for every output-register necessary
  •  additional software-effort
  •  slower then solution 1



Solution 3: Usage of a software-delay

After every write to the outputs you place a delay, which ensures, that the output will have reached the logical level according to your output, before you read it back again.

Example:

  bsf PORTB,RB0       ;set PortB,0
  call WAIT_100US     ;call a delay of some time (here 0.1 msec)
  bcf PORTB,RB3       ;clear PortB,3
  call WAIT_100US     ;call a delay of some time (here 0.1 msec)

Pro:
  •  An existing code can be easily modified.

Cons:
  • slowest solution (after every write to an output you need some delay)
  • additional software-effort
  • if pin is still in analog-mode, it can't work
  • if output-current is too high, it can't work (guaranteed output-level at the necessary current is outside the necessary input-level for the input-buffer (take care, if your pin has a st-input-buffer!)
  • delay-time depending on the load, which is connected to the output
  • delay depending on the pic-oscillator-frequency



Solution 4: Wait, until the output has reached the desired level

After writing to an output you check this state before you continue with your program

Example:

  bsf PORTB,RB0       ;set PortB,0
WAIT_B0
  btfss PORTB,RB0     ;PortB,0 is high?
  goto WAIT_B0        ;no - wait more
  bcf PORTB,RB3       ;clear PortB,3
WAIT_B3
  btfsc PORTB,RB3     ;PortB,0 is low?
  goto WAIT_B3        ;no - wait more

Pro:
  •  faster then solution 3

Cons:
  • additional software-effort
  • if pin is still in analog-mode, it can't work (the program will even hang in this loop!)
  • if output-current is too high, it can't work (the program will even hang in this loop!)



Conclusion:

If solution 1 is not possible (because of the load connected to the pin), then use solution 2. Solutions 3 and 4 are just mentioned because of being complete, but I don't recommend them (see the con's). Solution 4 is even pretty bad, as it may cause a hanging program (endless loop...).

Note, that the given code-examples are pretty simple ones. They are posted just for showing the idea of a solution, things like banking, paging etc. are not considered.



That's all? Unfortunately, no. Here's another rmw-problem:

It may arise with the TRIS-register. This may happen, if you use the MSSP-port in i2c-mode and you want to modify the TRIS-bits of the same output-register. If this is really necessary (its recommend to place the pin's, which needs a change from in- to output and backwards to another port), then read the tris-register, modify it according to your needs and always set the bits of the i2c-pins (so that they are in input-mode), before you write the tris-register back (or use a shadow-register, where the i2c-bits are always set).
Additional info you'll find here MSSP-errata-sheet (note: I'm not sure, if the list of affected pic's is complete in this errata).

And the last one:

If you do proper bit-banging your I²C-master (emulating the open-collector-behaviour in software using the TRIS-register and getting the high-signal only from the external pull-up-resistors) in the PIC16-series you should be careful, too. Let's have an example:

;Initialization of the port-pin's (i2c-pins are inputs, appropriate port-bits are cleared)
  BANKSEL TRISC       ;select Bank for accessing the tris-register
  bsf TRISC,RC3       ;C,3 is input (SCL)
  bsf TRISC,RC4       ;C,4 is input (SDA)
  BANKSEL PORTC       ;select Bank for accessing the port-register
  movlw b'11100111'
  andwf PORTC,F       ;clear the i2c-port-register-bits
  ...
;Generate Start-Bit
  BANKSEL TRISC
  bcf TRISC,RC4       ;C,4 (SDA) is output-now; as port-register-bit is zero, it will go low
  call Delay_4_us     ;short delay of 4 usec (required for 100 kHz-bus)
  bcf TRISC,RC3       ;C,3 (SCL) is output-now; as port-register-bit is zero, it will go low

If you've used now anywhere a rmw-instruction on PortC between the initialization and the start-bit-generation, then you can't be sure, that the PORTC-bits (3/4) are still zero (as the SDA- and SCL-lines are high, if no communciation is active). My solution for this case is to include the initialization of the i2c-pins into the start-bit-routine and to avoid any rmw-instructions while the i2c-communication is going on (including the interrupt-routines, which might be still active).

Now, that's all I'm aware of this topic. If anyone has some comments, I would be glad to read them here, too.

Hope this helps, Smile
Stefan



Edit: As ICwiki was getting retired, I've replaced its explanation for the rmw-effect with the one from Michael Rigby-Jones (piclist).
post edited by Stefan Uhlemayr - 2010/04/21 08:51:15
#1

16 Replies Related Threads

    DarioG
    humans, die, please
    • Total Posts : 53198
    • Reward points : 0
    • Joined: 2006/02/25 08:58:22
    • Location: porcodioland
    • Status: offline
    RE: RMW and solutions for it 2010/02/13 08:17:55 (permalink)
    0
    grin good!

    if only every single human would die...

    toronto made us dream!!!
    #2
    dhenry
    Super Member
    • Total Posts : 4994
    • Reward points : 0
    • Joined: 2003/11/07 12:35:12
    • Location: Colorado
    • Status: offline
    RE: RMW and solutions for it 2010/02/13 10:07:30 (permalink)
    0 (1)
    Our banned friend millwood (qili) has taught us that RMW issues are the work of con artists.

    http://forum.htsoft.com/all/showthreaded.php/Cat/0/Number/145512
    #3
    Stefan Uhlemayr
    Super Member
    • Total Posts : 4292
    • Reward points : 0
    • Joined: 2005/05/12 12:25:46
    • Location: Germany
    • Status: offline
    RE: RMW and solutions for it 2010/02/13 13:48:56 (permalink)
    0
    ORIGINAL: dhenry

    Our banned friend millwood (qili) has taught us that RMW issues are the work of con artists.
    Well, I'm not surprised from his statement. Instead of working only with a simulator he may want to have a look into the real world sometimes, too. While his simulator seems not to be very bad (at least, it simulates analog-functions...), I would not bet on the 4.23 V with a load of 110R connected to a 5V-pic. grin

    Greetings,
    Stefan
    #4
    Ian.M
    Super Member
    • Total Posts : 13110
    • Reward points : 0
    • Joined: 2009/07/23 07:02:40
    • Location: UK
    • Status: offline
    Re: RE: RMW and solutions for it 2010/08/20 15:02:08 (permalink)
    +2 (1)
    Solution 2: Usage of a shadow-register - C version
    Should be read with Stefan's version above
    Example in C:

    PORTB=PB_Shadow|=1<<0; // set bit 0 in the shadow-register for PortB, then copy it to the port.
    PORTB=PB_Shadow&=~(1<<3); // clear bit 3 in the shadow-register for PortB, then copy it to the port.

    Notes: Any reasonably good compiler will probably generate the exact same assembler code as Stephan's Solution 2.  PB_Shadow must be an unsigned char and must be globally accessible across the whole project.  If you find the above C obscure, *PLEASE* read up on operators in the K&R book 'The C Programming Language' or other good C reference and look at the verbose version below before starting a new topic if you still have questions.

    A verbose version that is exactly equivalent is:

    PB_Shadow=PB_Shadow | 0b00000001; // set bit 0 in the shadow-register for PortB.
    PORTB=PB_Shadow; // copy it to the port.

    PB_Shadow=PB_Shadow & 0b11110111; // clear bit 3 in the shadow-register for PortB.
    PORTB=PB_Shadow; // copy it to the port.

    N.B. I have not bothered with C versions of Solutions 3 and 4 as I agree with Stefan who does NOT recommend them
    #5
    john
    Starting Member
    • Total Posts : 40
    • Reward points : 0
    • Joined: 2003/11/07 12:35:09
    • Status: offline
    Re: RE: RMW and solutions for it 2010/09/16 19:48:14 (permalink)
    +2 (1)
    Stefan,

    Thanks for taking the time to collect all of this and for the citation! (solution 2). Considering the number of posts on the subject over the years I'm convinced that your picture and a link should be on MPLAB's splash page. 

    Despite the fact that I'm an unapologetic supporter of shadowing always (for the reason you indicated under Pros) you also indicated valid Cons, but not all of them. Here are some more:

    Since r-m-w operations are no longer a single instruction, if you use them in normal as well as interrupt-service code, you must disable any IRQ that also writes the port to avoid the problem of normal code flipping a bit in the shadow register, loading W and then getting interrupted before completing the write of W to the port.  If the IRQ modifies the port, the interrupted routine will clobber the just-written value.

    Some early 16xx parts compound the issue by requiring polling of GIE to ensure that IRQs have really been disabled.  Early 17's can vector to reset if IRQs are disabled when certain ones are pending.  These issues are mostly corrected in current parts but are worthy of note.

    Finding out that you should have shadowed the ports late in the game can be.. problematic.  Code that skips over a single byte r-m-w will have to be rewritten. The additional code can possibly... oh, who are we kidding here?  will inevitably bust you out of a ROM bank causing more rewrites, code busts because you missed some jump or call... Timing loops?

    So, should folks still be on board with shadowing? Yep. And do it early in the project.

    For the RAM requirements, allocate them early. You're the programmer. Its way easier to tell the project manager that your display buffer can only say INC instead of Galactic Incorporated than get them ALL assigned and have to tell her later on that the I/O does not work. Ask me how I know this.

    Arrrgh! Sorry. A little burned out on mid-range.. Use it if you must but recognize its faults and limitations. There are many posts on the forum about mysterious outputs. Most if not all trace back to this very issue.  If you can avoid mid-range, do it.  My thoughts, burns, flames etc can be found at John Oldenkamp above.  Find the passage about 50ns blankets and see what you think. 

    Finally Stefan, you deserve kudos from SSP users for posting the problems with r-m-w on TRISC while using I2C. We ran across this in the late '90s.  Never published, we found and confirmed it with uCHIP tech support (back in the phone days) and tossed it up to the RBBS.  Its what got us thinking about full shadowing on all the ports.

    For a real solution, and since I sign the checks, we not only advocate 18F but turn down all sub-18F business these days; for the noted IO reasons and the fact that 18F is just a better platform all around.  We'll do 10F, 12F etc if necessary but if the client insists on 16xx for something that can be satisfied with 18F, we send him elsewhere. Refer to the examples above and that's why we don't do 'C' on mid-range either. I've done it. Really. Just don't want any more of it. Nuff said.

    Have fun.




    post edited by john - 2010/09/16 22:23:37

    John
    #6
    NealR
    Super Member
    • Total Posts : 277
    • Reward points : 0
    • Joined: 2008/03/28 10:58:46
    • Location: Hollywood, FL
    • Status: offline
    Re: RE: RMW and solutions for it 2010/10/08 05:50:02 (permalink)
    0
    Ian.M

    Solution 2: Usage of a shadow-register - C version
    Should be read with Stefan's version above
    Example in C:
     
    PORTB=PB_Shadow|=1<<0; // set bit 0 in the shadow-register for PortB, then copy it to the port.
    PORTB=PB_Shadow&=~(1<<3); // clear bit 3 in the shadow-register for PortB, then copy it to the port.

    Notes: Any reasonably good compiler will probably generate the exact same assembler code as Stephan's Solution 2.  PB_Shadow must be an unsigned char and must be globally accessible across the whole project.  If you find the above C obscure, *PLEASE* read up on operators in the K&R book 'The C Programming Language' or other good C reference and look at the verbose version below before starting a new topic if you still have questions.

    A verbose version that is exactly equivalent is:
     
    PB_Shadow=PB_Shadow | 0b00000001; // set bit 0 in the shadow-register for PortB.
    PORTB=PB_Shadow; // copy it to the port.

    PB_Shadow=PB_Shadow & 0b11110111; // clear bit 3 in the shadow-register for PortB.
    PORTB=PB_Shadow; // copy it to the port.

    N.B. I have not bothered with C versions of Solutions 3 and 4 as I agree with Stefan who does NOT recommend them

     
    Dear Ian,
     
    I was having difficulty understanding port shadowing until you provided the "verbose version" of the code in C.  It is much more clear now how you go about doing this.  My only question is this: When you set bit 0 in the shadow-register for PortB , it looks like all of the other bits in the shadow register are set low (0b00000001).  This makes perfect sense to me.  Then when you cleared bit 3 in the shadow-register for PortB, it looks like all of the other bits in the port shadow are set high (0b11110111).  This is the part that does not make sense to me.  Will I have a problems with all of the other ouput pins going to a high state?
     
    Thank you.

     

    Sincerely,
    Neal Rosenblum (KB8PFV) 
    MPLAB X IDE v2.35
    ICD3
    X16 Compiler
    #7
    Ian.M
    Super Member
    • Total Posts : 13110
    • Reward points : 0
    • Joined: 2009/07/23 07:02:40
    • Location: UK
    • Status: offline
    Re: RE: RMW and solutions for it 2010/10/08 07:27:17 (permalink)
    +2 (1)
    No. You need to study bitwise Boolean arithmatic and specifically the 'C' bitwise operators.  If you work through that you will soon see that only the bits I named were altered.  If in doubt, you could always set it up in a loop from 0 to 255 copying the loop variable to PB_Shadow then using the EXACT code from my example, then step through it using the MPLAB Simulator.
    #8
    Ian.M
    Super Member
    • Total Posts : 13110
    • Reward points : 0
    • Joined: 2009/07/23 07:02:40
    • Location: UK
    • Status: offline
    Re: RE: RMW and solutions for it 2010/10/25 05:54:59 (permalink)
    0
    Textbook case of R-M-W on an enhanced midrange part caused by failure to use LATx registers with clear waveform diagrams showing the effect:
    Solutions #1 and #2 (above) tested successfully but finally properly handled in hardware by accessing LATx (via FSR to avoid additional bank switching code causing 'code bloat' problems).


    #9
    dhenry
    Super Member
    • Total Posts : 4994
    • Reward points : 0
    • Joined: 2003/11/07 12:35:12
    • Location: Colorado
    • Status: offline
    RE: RMW and solutions for it 2011/10/26 17:24:30 (permalink)
    0
    dhenry
    Our banned friend millwood (qili) has taught us that RMW issues are the work of con artists.

    http://forum.htsoft.com/all/showthreaded.php/Cat/0/Number/145512

    Another poster recently provided this old thread as a reference for RMW issues.  For completeness, an update is justified.

    Our banned friend "millwood" re-registered as currently active forum member "fdan00".  He was "qili" on the HI-TECH forum.  The above "work of con artists" link to a post by qili (fdan00/millwood), if clicked, comes up blank and with all qili's posts missing, which is an artifact of how he was dispatched on the HI-TECH forum.  Since it's not available to read there, I'll provide it here.

    fdan00/qili/millwood is known for brow-beating other members ad-nauseum on just about any topic, but RMW is his favorite.  The below is the referenced excerpt from a 143-post! thread about RMW:

    qili (fdan00/millwood) unlike the "experts" view, there is no issue with read-modify-write here.

    jtemples replies:
    Surely your advice to the OP isn't "ignore RMW, then rewrite the code later when some minor change to the hardware is made, like the clock speed?"

    The reply to which is this classic:
    qili (fdan00/millwood)my advice to the OP is that my experience, typified by this example, is that 99% of the people talking about RMW has zero idea what it is. they talk up RMW as a mystery "thing" to just sound sophisticated. and when you probe a little bit further, they quickly run out of any intelligence and you see them as what they are: con artist.

    #10
    DarioG
    humans, die, please
    • Total Posts : 53198
    • Reward points : 0
    • Joined: 2006/02/25 08:58:22
    • Location: porcodioland
    • Status: offline
    RE: RMW and solutions for it 2011/10/27 02:56:18 (permalink)
    0
    Hey, that answer was a "pearl" indeed Smile

    if only every single human would die...

    toronto made us dream!!!
    #11
    dhenry
    Super Member
    • Total Posts : 4994
    • Reward points : 0
    • Joined: 2003/11/07 12:35:12
    • Location: Colorado
    • Status: offline
    RE: RMW and solutions for it 2011/10/27 05:15:25 (permalink)
    0 (1)
    DarioG
    Hey, that answer was a "pearl" indeed Smile

    With him having such contempt for Microchip engineers and technical writers, one wonders why he uses Microchip products at all.
    #12
    DarioG
    humans, die, please
    • Total Posts : 53198
    • Reward points : 0
    • Joined: 2006/02/25 08:58:22
    • Location: porcodioland
    • Status: offline
    RE: RMW and solutions for it 2011/10/27 13:31:34 (permalink)
    0
    This is also true Smile

    if only every single human would die...

    toronto made us dream!!!
    #13
    Ian.M
    Super Member
    • Total Posts : 13110
    • Reward points : 0
    • Joined: 2009/07/23 07:02:40
    • Location: UK
    • Status: offline
    RE: RMW and solutions for it 2014/02/01 06:19:56 (permalink)
    +2 (1)
    XC8 + recent Hitech C for PIC10/12/16: Using PORTAbits_t to create a shadow variable for the port A?
    Access to the shadow register bits by the same structure member bitfield names as the actual port.
     
    N.B. The final form of the Macro is: #define sPORTAbits (*(PORTAbits_t * volatile)&sPORTA)
    to match the type of the shadow register sPORTA  which needs to be a volatile uint8_t (i.e. volatile unsigned char).


    #14
    DarioG
    humans, die, please
    • Total Posts : 53198
    • Reward points : 0
    • Joined: 2006/02/25 08:58:22
    • Location: porcodioland
    • Status: offline
    RE: RMW and solutions for it 2015/03/28 08:09:41 (permalink)
    0
    Updated link since piclist.org seems to be down:
    http://www.piclist.com/techref/readmodwrite.htm

    if only every single human would die...

    toronto made us dream!!!
    #15
    Ian.M
    Super Member
    • Total Posts : 13110
    • Reward points : 0
    • Joined: 2009/07/23 07:02:40
    • Location: UK
    • Status: offline
    RE: RMW and solutions for it 2016/01/21 13:40:28 (permalink)
    0 (1)
    I was looking for an easier way of reusing the sPORTX / sPORTXbits.RXn shadowing implementation that uses the PORTXbits_t struct from the standard headers to provide individual pin access to the shadow variables. (See topic Using PORTAbits_t to create a shadow variable for the port A?) and got the idea of writing a universal port shadowing header for standard midrange PICs, that automatically determines which ports exist.  If you don't want to shadow the whole shebang, (e.g. if some ports are all input or have only one output line, or are written 'full width' without RMW operations) you can exclude individual ports.   See the comment block for details:

    /* Simple PORT/PORTbit shadowing for XC8 - v1.0
     *
     * PROCESSOR : PIC16Fxxx standard midrange
     *
     * Copyright Ian.M 2016. Released under the LGPLv3 licence:
     *    https://www.gnu.org/licenses/lgpl.html
     *
     * Creates fake shadow port structures by pointer casting to avoid RMW effect
     * when writing to individual pins, all conditionally compiled only if that
     * port exists.
     *
     * Diddle with the shadow (s in front of port name or portbits name, e.g.
     * sPORTA or sPORTAbits.RA0) then use:
     *    UpdPorts();
     * to make the changes take effect. N.B there will be a slight timing skew
     * across multiple ports. You can also update individual ports manually.
     *
     * Use:
     *    #define NO_SHADOW_X
     *    #include "shadow.h"
     *
     * (where X is a port letter) to exclude that port from shadowing.
     *
     * Use:
     *    CreateShadowRegs;
     *
     * in file scope in *one* C file to define the port shadowimg variables.
     *
     * Notes:
     * It misses /MCLR as an input on 28 pin parts, but inputs dont need shadowing.
     * I *ASSUME* all other ports either have a bit 0 or bit 7 pin.  It also builds
     * an update list and a definition list for the shadow variables.
    */

     
    The preprocessor conditionals required to implement this are quite fugly, but hopefully, I wont have to revisit them!
     
    Feedback (apart from "You are crazy to use a PIC16!") is appreciated.

    --
    NEW USERS: Posting images, links and code - workaround for restrictions.
    I also support http://picforum.ric323.com because this forum is sometimes too broken to use!
    #16
    ric
    Super Member
    • Total Posts : 22101
    • Reward points : 0
    • Joined: 2003/11/07 12:41:26
    • Location: Australia, Melbourne
    • Status: offline
    RE: RMW and solutions for it 2016/01/21 14:23:43 (permalink)
    +2 (2)
    I'd qualify that to:
    "You are crazy to use a non-enhanced PIC16!" :)
     

    I also post at: PicForum
    Links to useful PIC information: http://picforum.ric323.co...opic.php?f=59&t=15
    NEW USERS: Posting images, links and code - workaround for restrictions.
    To get a useful answer, always state which PIC you are using!
    #17
    Jump to:
    © 2018 APG vNext Commercial Version 4.5