Menu

Read-Modify-Write problem

Gigi
2025-07-30
2025-08-14
  • Gigi

    Gigi - 2025-07-30

    I discovered that there is a new method on GCB (#define Porta.0 volatile) to eliminate the problem, but it did not work correctly. The problem was especially presented to me on the piloting of Gate Mos 2N7000 or the like.
    Everything works well as if command is: porta.0= ! porta.0
    But it has no effect if checked too ASM:
    do
    porta.0=0
    porta.0=1
    loop

    To avoid the RMW problem I do as recommended by microchip with the use of a variable but a dedicated command would be convenient, or maybe I'm wrong something with GCB.
    Thank you

     
  • Anobium

    Anobium - 2025-07-30

    I can look into this. We should fix. Please give me the exact code that works, and the code that fails. I just need the shortest code. But, at the moment I do not know the chip or enough to look at this.

     
  • Gigi

    Gigi - 2025-07-31

    This example uses 12f675 but I saw that the same is obtained on other pic 16f819 etc. Practically on all the PICs who do not have the LAT register.
    The modification of the door normally takes place without preventing RMW.

     
  • jackjames

    jackjames - 2025-08-01

    In Mikrobasic the volatile word is also used during the variable declaration.
    A variable whose value can change asynchronously, out of the direct control of the flow of the main program. The compiler will not make optimizations on it, precisely because it must consider that the value could be altered by:
    Interruption routine (ISR)
    Direct accesses from hardware peripherals
    Asynchronous operations from other threads or processes (if present)

     
  • Gigi

    Gigi - 2025-08-01

    I didn't know about the volatile declaration I discovered it looking at the example code. In Help says it is used to avoid glitches on the doors, a problem I had long ago on an automation of a gate that used several Mos for piloting. I used a variable to change the door and worked well:

    dim LATA as byte
    LATA=0

    do
    LATA.0=1:PORTA=LATA
    LATA.0=0:PORTA=LATA
    loop

    On recent Pic + there is no problem as an "lat" register that avoids these problems.

     
  • Anobium

    Anobium - 2025-08-04

    I have looked into this, and not yet resolved but I am starting to understand the issue in the compiler.

    Does this resolve? When you use a BIT is set the state?

    #chip 12F675,4     
    #config INTRC_OSC_NOCLKOUT, WDT_OFF, MCLR_Off, BOD_on, PWRT_on
    #option explicit  'dichiarazione variabile obbligatoria
    
    #option volatile gpio.1
    #option volatile gpio.2
    
    Dim Offstate as Bit: Offstate = 0
    Dim Onstate as Bit:  Onstate = 1
    
    
    dir gpio out
    
    do
    
        gpio.1=Offstate:gpio.2=Onstate
    
    loop
    

    If this works then this tells me the part of the compiler that I need to look into deeper.

     
  • Anobium

    Anobium - 2025-08-04

    I have update the compiler. It is a specific change for this issue. It will need testing.

    When #option volatile is used with PIC ( with no LAT ) the ASM will read the register, cache the register, change the cache, write the cache.

    I will post a link to the new compiler if you can test and provide feedback.

     
  • Jerry Messina

    Jerry Messina - 2025-08-04

    how would that work if ports are also written to inside an interrupt?

     
  • Anobium

    Anobium - 2025-08-05

    @Jerry. What a great question. I had overlooked but this is now handled. A better explanation.

    Does this look OK? Could I just toggle GIE and not use SYSINTSTATESAVE0 ?

    Evan

    Read later post, as this has been removed from the compiler.


    🧵 Resolving RMW Issues in GPIO Control with GCBASIC on PIC12F675

    GCBASIC handles GPIO bit manipulation on the PIC12F675 with care—especially when RWM issues can occur, then the added complexity of interrupts.

    The classic Read-Modify-Write (RMW) problem can corrupt GPIO state if an interrupt fires mid-operation and touches the same register.

    1. The source GCBASIC code
    2. Generated ASM without interrupt handling
    3. Generated ASM with interrupt protection

    📘 1. Source GCBASIC Code

    Here is the minimal control loop setting gpio.1 = 1 and gpio.2 = 0:

    #chip 12F675,4     
    #config INTRC_OSC_NOCLKOUT, WDT_OFF, MCLR_Off, BOD_on, PWRT_on
    #option explicit
    
    #option volatile gpio.1
    #option volatile gpio.2
    
    dir gpio out
    
    do
        gpio.1 = 1
        gpio.2 = 0
    loop
    

    ⚙️ 2. GCBASIC-Generated ASM (Without Interrupt Handling)

    When interrupts are disabled, the compiler outputs straightforward bit instructions with direct RMW on the GPIO register:

    ;do
    SysDoLoop_S1
    ;gpio.1=1
    ;Handler for Read-Modify-Write problem
        banksel GPIO
        movf     GPIO, W
        movwf    SYSPICRWMCACHE
        bsf  SYSPICRWMCACHE,1
        movf    SYSPICRWMCACHE, W
        movwf   GPIO
    ;gpio.2=0
    ;Handler for Read-Modify-Write problem
        movwf    SYSPICRWMCACHE
        bcf  SYSPICRWMCACHE,2
        movf    SYSPICRWMCACHE, W
        movwf   GPIO
    ;loop
        goto    SysDoLoop_S1
    

    🔍 Problem: These RMW operations are vulnerable. If an interrupt modifies GPIO during or between bsf/bcf instructions, bit state loss may occur.


    🛡️ 3. GCBASIC-Generated ASM (With Interrupt Protection)

    With interrupt support enabled, GCBASIC wraps RMW operations to preserve atomicity using:

    • SYSINTSTATESAVE0: stores interrupt enable state
    • SYSPICRWMCACHE: staging register for safe bit operations
    SysDoLoop_S1
    
    ; gpio.1 = 1 — RMW with interrupt wrapping
    bcf SYSINTSTATESAVE0, 0
    btfsc   INTCON, GIE
    bsf SYSINTSTATESAVE0, 0
    bcf INTCON, GIE
    
    banksel GPIO
    movf    GPIO, W
    movwf   SYSPICRWMCACHE
    bsf SYSPICRWMCACHE, 1
    movf    SYSPICRWMCACHE, W
    movwf   GPIO
    
    btfss   SYSINTSTATESAVE0, 0
    bcf INTCON, GIE
    btfsc   SYSINTSTATESAVE0, 0
    bsf INTCON, GIE
    
    ; gpio.2 = 0 — RMW with interrupt wrapping
    bcf SYSINTSTATESAVE0, 0
    btfsc   INTCON, GIE
    bsf SYSINTSTATESAVE0, 0
    bcf INTCON, GIE
    
    movf    GPIO, W
    movwf   SYSPICRWMCACHE
    bcf SYSPICRWMCACHE, 2
    movf    SYSPICRWMCACHE, W
    movwf   GPIO
    
    btfss   SYSINTSTATESAVE0, 0
    bcf INTCON, GIE
    btfsc   SYSINTSTATESAVE0, 0
    bsf INTCON, GIE
    
    goto SysDoLoop_S1
    

    Benefit: Ensures no interrupt corrupts the GPIO state mid-operation—crucial for reliable output in multi-context systems.


    🧮 Summary

    Stage Behaviour RMW Protection
    GCBASIC Source Code High-level intent Compiler-managed
    ASM without Interrupts Direct bit set/clear ✅ Full isolation
    ASM with Interrupt Protection Uses state save + shadow register ✅ Full isolation

     
    ❤️
    1

    Last edit: Anobium 2025-08-05
  • Anobium

    Anobium - 2025-08-05

    🧵 Resolving GPIO RMW Hazards with Shadow Register Workaround

    #option shadowregister GPIO

    📌 Context

    In traditional PIC microcontroller setups, especially those lacking hardware LAT registers, modifying a GPIO pin within a Read-Modify-Write (RMW) sequence can introduce unexpected behavior when pin states are physically influenced by external loads. If a pin like GPIO.1 is overloaded and pulled low, reading the port during software staging can yield false states, leading to unintended outcomes when writing back.

    💡 Hugh's Insight

    Hugh shared, via a private email, an excellent observation about how even methodical caching into SYSPICRWMCACHE (proposed in previous post) still doesn’t prevent read corruption if the pin is pulled below logic threshold. This vulnerability persists during operations like:

    movf GPIO, W     ; reads corrupted pin state
    movwf GPIO       ; rewrites incorrect data back
    

    To address this, Hugh proposed a manual shadow register emulating LAT behavior:

    bcf LATA, n       ; clear software latch bit
    bsf LATA, n       ; set latch bit (if needed)
    movf LATA, W      ; load the full latch state
    movwf GPIO        ; apply safely to hardware port
    

    ⚠️ This still requires interrupt protection between the movf and movwf steps to ensure atomicity.

    Hugh also cited GCBASIC’s approach for 12-bit instruction PICs lacking direct bit-level access to certain registers. GCBASIC introduces shadow variables for registers like TRIS and OPTION_REG, allowing controlled manipulation:

    • Set or clear bits in the variable copy.
    • Write back using W register:
    movf shadowTRIS, W
    tris
    

    This technique mirrors the LAT workaround, substituting hardware latching with software staging.

    🛠️ Alternate Recommendations

    Hugh pointed to two mitigation paths:

    Option Description
    12F1840 or similar Has proper LAT registers eliminating RMW issues
    Load reduction Ensure GPIO switching completes in <1 cycle to avoid logic corruption

    ✅ Next Steps

    • Remove the currently implemented RWM code.
    • Implement the Shadow Register method as proposed by Hugh to ensure state integrity and eliminate RMW pitfalls.
    • This solution will be exposed as:
      #option shadowregister GPIO

    We will resolve.

     
    ❤️
    1
  • Gigi

    Gigi - 2025-08-06

    You have given a nice description of the problem, I think the solution is as written in the last posts as any reading of the door can cause errors.
    It is nice that the compiler will automatically solve this problem, great GCB.

     
    • Anobium

      Anobium - 2025-08-06

      I will update this thread when I have made the changes. :-)

       
  • Gigi

    Gigi - 2025-08-06

    Well, when I finished I take tests on my gate home that gave me many problems and I solved with lat variable.
    To test well you have to do it in the real world with the load on the doors (especially capacitive).

     
  • Anobium

    Anobium - 2025-08-14

    🛠️ New GCBASIC Capability: RMW-Compliant ASM Output creates Safer PIC Code

    🚀 Overview

    I have completed the new capability for GCBASIC that generates read-modify-write (RMW)-safe assembly for PIC microcontrollers—especially useful on chips like the PIC12F675 and may more, which lack LATx registers and are prone to subtle latch corruption when using high-level pin commands.

    This capability ensures that output operations never corrupt input latches, preserving predictable behaviour and making the generated code interrupt-safe when the user controls the interrupts.

    To use is very simple. Add a compiler directive and the RWM ASM is generated.

    This is available from build 1490 and onwards.


    🔧 What Problem Does It Solve?

    The example program is a good example of the RWM problem.

    #chip 12F675,4      
    #option shadowregister gpio
    
    #option explicit
    
    Dir GP0 In
    GP2 = 1
    

    On chips without LATx, instructions like GPIO.2 = 1 compile to:

    bsf GPIO, 2
    

    This triggers a read-modify-write on the GPIO register. If any pins are configured as inputs, their bits reflect external voltage levels during the read—so writing back the modified byte can unintentionally clear output latches for those input pins.

    This can cause:

    • Unexpected output levels when switching pin direction
    • Corrupted latch states during interrupts
    • Hard-to-trace bugs in mixed input/output setups

    ✅ What the new capability does

    Instead of relying on the register GPIO, the new capability uses a RAM-based shadow latch (e.g. LATA) to track intended output states. All pin changes update this shadow, and writes to GPIO are done using full-byte transfers from LATA.

    Example Output (RMW-Safe)

    ; Set GP2 high without affecting GP0
    ;GP2 = 1
    bsf     LATA, GP2        ; Update shadow latch
    movf    LATA, W
    movwf   GPIO             ; Safe write to port
    

    This avoids reading GPIO entirely and ensures that input pins (like GP0) do not influence the output latch state.


    🧠 Visualizing the RWM Hazard

    Pin Direction External Level GPIO Read Latch Impact
    GP0 Input 0 V 0 Cleared
    GP2 Output 0 Set to 1

    If you read GPIO and write back with GP2 set, GP0’s 0 gets written too—even though it’s just an input. That’s the trap.


    📦 Adapter Features

    • ✅ RMW-safe output for all pin operations
    • ✅ Shadow latch tracking via LATA RAM variable
    • ✅ Compatible with GCBASIC macros and inline ASM

    📚 How to Use

    #option shadowregister gpio
    

    The current implementation supports gpio, porta, portb.


    🧪 YouTube

    I posted a video with a lot more detail, see https://youtu.be/t6PvKRTl8ls

     
    • Anobium

      Anobium - 2025-08-14

      AdaptRMWIssues Function Documentation

      Overview

      The new AdaptRMWIssues function is a sub routine within the compiler designed to process assembly code lines generated by GCBASIC for Microchip PIC microcontrollers (e.g., PIC16F819). It adapts instructions that operate on I/O ports (GPIO, PORTA, PORTB) to use software cache registers (LATA, LATB) in RAM, mitigating Read-Modify-Write (RMW) issues and ensuring reliable reads of output port states.

      This is critical for this type of PIC which lacks hardware latch registers, as direct port operations can lead to bit corruption or unreliable state reads due to external loads or pin transitions.

      Function Signature

      Function AdaptRMWIssues(rmwAsmLine As String, rmwLineNumber As String) As String
      

      Parameters

      • rmwAsmLine (String): The assembly code line to process (e.g., movf PORTA, W or bcf PORTB, 1).
      • rmwLineNumber (String): The line number of the assembly code, used for debugging or logging (not utilized in the function logic).

      Return Value

      • String: The adapted assembly code line(s) with operations redirected to use LATA or LATB, or an empty string ("") if no adaptation is needed or the input is invalid.

      Purpose

      The function ensures RMW-safe and reliable port operations by:

      1. Replacing RMW-prone instructions (bcf, bsf, incf, etc.) targeting GPIO, PORTA, or PORTB with equivalent operations on LATA or LATB, followed by copying to the port.
      2. Replacing read instructions (movf PORTx, W) with movf LATx, W to use the intended output state.
      3. Replacing write instructions (movwf PORTx, clrf PORTx) with operations on LATA or LATB, followed by copying to the port.
      4. Replacing bit-test instructions (btfsc, btfss) on PORTx with LATA or LATB for reliable state checks.

      This is essential for these PICs, where LATA and LATB are user-defined RAM variables acting as software caches to track intended port output states.

      Supported Instructions

      The function processes the following instructions when they target GPIO, PORTA, or PORTB:

      • RMW-prone instructions: bcf, bsf, incf, decf, comf, rlf, iorwf, andwf, xorwf, rrf
      • Read instructions: movf (when destination is W)
      • Write instructions: movwf, clrf
      • Bit-test instructions: btfsc, btfss

      Logic Flow

      1. Input Validation

      • Returns an empty string if rmwAsmLine is empty or starts with a comment (;).
      • Removes inline comments (e.g., movf PORTA, W ; commentmovf PORTA, W).

      2. Tokenization

      • Splits the line into tokens (instruction and operands) using spaces (e.g., movf PORTA, W["movf", "PORTA", "W"]).
      • Trims tokens to remove extra spaces.
      • Normalizes the instruction to lowercase for comparison (e.g., MOVFmovf).

      3. Instruction Check

      • Checks if the instruction is in targetInstructions (bcf, bsf, ..., movf, movwf, clrf) or targetBitTestInstructions (btfsc, btfss).
      • Returns an empty string if the instruction is not supported.

      4. Operand Analysis

      • Splits the first operand to isolate the register and bit number (e.g., PORTA,1["PORTA", "1"]).
      • Identifies the target register (GPIO, PORTA, PORTB) and maps it to the corresponding shadow register (LATA for GPIO/PORTA, LATB for PORTB).
      • Verifies that the shadow register is enabled using HashMapGet(ShadowOutputs, "GPIO"|"PORTA"|"PORTB"). Returns an empty string if not enabled.

      5. Instruction Adaptation

      For movf:

      • If destination is W (e.g., movf PORTA, W), replaces with movf LATA, W.
      • Skips if destination is F or other values.

      For movwf or clrf:

      • Replaces movwf PORTA with:
        asm movwf LATA movf LATA, W movwf PORTA
      • Replaces clrf PORTA with:
        asm clrf LATA movf LATA, W movwf PORTA

      For btfsc or btfss:

      • Replaces btfsc PORTA, 5 with btfsc LATA, 5 (similarly for btfss).

      For bit operations (bcf, bsf):

      • Replaces bcf PORTA, 1 with:
        asm bcf LATA, 1 movf LATA, W movwf PORTA

      For byte operations (incf, decf, etc.):

      • Replaces operation on PORTx with LATx (e.g., incf PORTA, Fincf LATA, F), followed by copying to PORTx if destination is F.

      6. Interrupt Handling

      • Includes placeholders for disabling/enabling interrupts (INTOFF, INTON) if UserInt or SysInt is true, though these are currently commented out.

      7. Output

      • Returns the adapted assembly code as a string, with instructions separated by newlines (Chr(10)).
      • Preserves the original register casing (e.g., PORTA instead of porta) in the output.
      • Returns an empty string if no adaptation is needed or the input is invalid.
      • The string is then used to update the list of assembly.

      Example Usage - internal and not exposed to user

      Input

      movf PORTA, W
      bcf PORTB, 2
      movwf PORTA
      btfsc PORTA, 5
      clrf PORTB
      

      Output

      movf LATA, W
      bcf LATB, 2
      movf LATB, W
      movwf PORTB
      movwf LATA
      movf LATA, W
      movwf PORTA
      btfsc LATA, 5
      clrf LATB
      movf LATB, W
      movwf PORTB
      

      Explanation

      • movf PORTA, Wmovf LATA, W: Reads the intended output state from LATA.
      • bcf PORTB, 2bcf LATB, 2; movf LATB, W; movwf PORTB: Uses LATB to avoid RMW.
      • movwf PORTAmovwf LATA; movf LATA, W; movwf PORTA: Writes to LATA first.
      • btfsc PORTA, 5btfsc LATA, 5: Tests LATA for reliable state.
      • clrf PORTBclrf LATB; movf LATB, W; movwf PORTB: Clears LATB and updates PORTB.

      Assumptions

      • The legacy PICs are the target microcontroller, with LATA and LATB defined as RAM variables
      • The ShadowOutputs hash map tracks whether LATA/LATB are enabled for GPIO, PORTA, or PORTB using #option shadowregister gpio|porta|portb
      • Ports are configured as outputs (e.g., clrf TRISA), so LATA/LATB represent the intended output state.
      • No interrupts are enabled in the program, so INTOFF/INTON are optional.

      Limitations

      • Only processes instructions targeting GPIO, PORTA, or PORTB. Other registers are ignored.
      • Assumes movf with destination F (e.g., movf PORTA, F) is not adapted, as it's rare and may require different handling.
      • Does not handle mixed input/output pins on the same port (e.g., some PORTA pins as inputs). In such cases, PORTA must be read for input pins.
      • Interrupt protection (INTOFF/INTON) is commented out and may need to be enabled if interrupts are used.

      Potential future enhancements

      • Handle Mixed I/O: If ports have input pins, extend the function to check TRISA/TRISB and read PORTA/PORTB for input pins.
      • Enable Interrupt Protection: Uncomment INTOFF/INTON if interrupts are enabled to ensure atomic operations.
       
    • Anobium

      Anobium - 2025-08-14

      RMW Safety Test Cases (Legacy PICs)

      # Test Case RWM Explanation
      1 dir gpio out No RWM issue. Sets TRISA to make all GPIO pins outputs. No GPIO read/modify.
      2 dir gpio in No RWM issue. Sets TRISA to make all GPIO pins inputs. No GPIO read/modify.
      3 gpio.n = 0 or 1 Potential RWM issue. Bit-clear/set reads pin state. LATA cache stores intent.
      4 PortState = gpio No RWM issue. Reads actual pin states. LATA not updated.
      5 PortState = gpio.5 No RWM issue. Reads GP5 pin state. May differ from LATA.
      6 gpio = CONSTANT_VALUE No RWM issue. Constant written to GPIO and cached in LATA.
      7 gpio = PortState No RWM issue. PortState copied to GPIO and cached in LATA.
      8 gpio.1 = !gpio.1 Potential RWM issue. Toggles bit via LATA cache.
      9 gpio = !gpio Potential RWM issue. Inverts GPIO via LATA cache.
      10 gpio = NOT PortState No RWM if PortState is trusted. LATA stores inverted state.
      11 gpio.3 = gpio.4 RWM issue. Reads GP4, writes GP3. LATA updated conditionally.

      Note:
      Initialize LATA = 0x00 before tests to ensure predictable output.
      LATA and PortState are RAM variables.

       

Log in to post a comment.

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.