Locked (Atomic) Registrati in lettura / scrittura


8

Sto codificando qualcosa usando il controllo diretto di GPIO, ci sono alcune buone risorse in giro per questo, come http://elinux.org/RPi_Low-level_peripherals#GPIO_hardware_hacking ; il processo prevede open ("/ dev / mem") e quindi un'operazione mmap mappa in modo efficace l'indirizzo fisico desiderato nel tuo spazio di indirizzi virtuale. Quindi leggi la sezione 6 di questo http://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf per scoprire come sono controllati gli I / O.

Per passare alla funzione di un pin (input, o output o varie funzioni speciali), modificare questi campi a 3 bit nei registri I / O GPFSELx (000 = input, 001 = output dell'istanza del nemico). Queste operazioni di modifica vengono compilate per operazioni con carico e archivio ordinari (ad esempio per modificare GPIO0 in input: * (regptr) & = ~ 7; che viene compilato in qualcosa di simile

    ldr     r2, [r3, #0]     ; r = *ptr (load r2 from I/O register)
    bic     r2, r2, #7       ; r2 &= ~7
    str     r2, [r3, #0]     ; *ptr = r2 (store r2 to I/O register)

Il problema è questo: se si verifica un interruzione tra il carico e l'archivio e un altro processo o ISR modifica lo stesso registro I / O, l'operazione di archiviazione (basata su una lettura non aggiornata in r2) ripristinerà gli effetti di quell'altra operazione. Pertanto, la modifica di questi registri I / O deve davvero essere eseguita con un'operazione di lettura / modifica / scrittura atomica (bloccata). Gli esempi che ho visto non usano un'operazione bloccata.

Poiché questi registri I / O vengono generalmente modificati solo durante l'impostazione, è improbabile che si verifichino problemi, ma "mai" è sempre meglio di "improbabile". Inoltre, se si dispone di un'applicazione in cui si sta basando i bit per emulare un output open collector, quindi (per quanto ne so), ciò comporta la programmazione dell'output su 0 e quindi il passaggio tra output (per basso) o input ( per spento / alto). Quindi in quel caso ci sarebbero frequenti modifiche a questi registri I / O e modifiche non sicure avrebbero molte più probabilità di causare un problema.

Quindi, probabilmente c'è un ARM "confronta e imposta" o un'operazione simile che può essere utilizzata qui per fare questo, qualcuno può indicarmi questo e come farlo accadere dal codice C?

[Nota, non è necessario nulla di speciale quando si è programmato un I / O come uscita e lo si sta semplicemente cambiando da 0 a 1 o viceversa; poiché è presente un registro I / O in cui si scrive, per impostare i bit selezionati su 1 e un altro per cancellare i bit selezionati su 0. Non è necessaria alcuna lettura / scrittura per questa operazione, quindi non vi sono rischi di interruzioni].


Forse non l'ho capito bene ma da quando apri /dev/memsembra che il tuo codice sia il codice userpace. Non penso che in nessun sistema operativo moderno si debba fare attenzione agli interrupt che cambiano i valori dei registri nel codice dello spazio utente. Credo che questo non sarebbe un problema anche nel codice spaziale del kernel poiché Linux ripristina tutti i registri quando il gestore di interrupt termina il suo lavoro.
Krzysztof Adamski,

1
La mia comprensione è che il carico / archivio passa a un registro fisico tramite il mapping VM creato da mmap (un registro I / O, non un registro CPU). In questo caso non vi è motivo per cui un altro processo o un driver di dispositivo non possano fare la stessa cosa contemporaneamente e modificare lo stesso registro. (Suppongo che stia modificando un diverso set di bit nel reg, o chiaramente abbiamo problemi più grandi). Non è possibile salvare / ripristinare i registri IO come per i registri del processore.
Greggo,

Ho modificato un po 'per chiarire' Registro I / O 'rispetto a r2 ecc.
Greggo

Adesso capisco il tuo punto. Tuttavia, è più una prelazione che un problema di gestione degli interrupt. L'uso di operazioni atomiche sarebbe di aiuto almeno quando due processi stanno cercando di impostare bit diversi contemporaneamente.
Krzysztof Adamski,

ldrex / strex non funziona sulla memoria non memorizzata nella cache. Il monitor esclusivo si basa sulle cache. In effetti, era possibile bloccare la CPU con difficoltà se, ad esempio, si tentava su un sistema SMP Cortex-A9.
thinkfat,

Risposte:


3

Ho esaminato questo, l'ARM ha istruzioni 'ldrex e' strex ', lo strex restituirà un risultato fallito se l'esclusività viene persa (o potrebbe essere stata persa) dal ldrex, che include un interruttore di contesto (o un altro processore che modifica lo stesso registrarsi in un ambiente multiprocessore). Quindi si può fare usando quello; se lo strex fallisce, esegui il loop e ripeti l'operazione (con un nuovo ldrex).

rif: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dht0008a/ch01s02s01.html

Le routine seguenti sembrano funzionare sul Raspberry Pi (in quanto generano l'assemblatore che mi aspettavo; e che l'effetto sui bit quando li utilizzo sono come previsto. Non ho verificato che proteggano dal problema del cambio di contesto) . Si noti che si tratta di linee incorporate anziché di funzioni, pertanto devono essere inserite in un file di intestazione.

[ EDIT : Questo non funziona per lo scopo discusso, sembra in qualche modo non consentito. Se uso queste routine in cui * addr è una variabile ordinaria, funziona benissimo. Quando lo uso dove * addr è indirizzato a un registro GPIO mappato, il processo riceve un errore del bus. (Quando cambio ldrex / strex in ldr / str e disabilito il ciclo do, allora funziona). Quindi sembra che il monitor esclusivo ARM non sia in grado, o non sia configurato, di funzionare su registri I / O mappati in memoria, e la domanda rimane aperta.]

//
// Routines to atomically modify 32-bit registers using ldrex and strex.
// 
//
//
//  locked_bic_to_reg( volatile unsigned * addr, unsigned val )
//                 *addr &= ~val
//  locked_or_to_reg( volatile unsigned * addr, unsigned val )
//                 *addr |= val
//   locked_insert_to_reg( volatile unsigned * addr, unsigned val, int width, int pos )
//           insert 'width' lsbs of 'val into *addr, with the lsb at bit 'pos'.
//           Caller must ensure 1 <= width <= 32 and 0 <= pos < 32-width
//
//
static inline void
locked_bic_to_reg( volatile unsigned * addr, unsigned val )
{
    int fail;
    do{
        asm volatile ("ldrex r0,[%1]\n"
           "   bic r0,r0,%2\n"
           "   strex %0,r0,[%1]": "=r"(fail) : "r"(addr), "r"(val): "r0" );
    }while(fail!=0);
}
static inline void
locked_or_to_reg( volatile unsigned * addr, unsigned val)
{
    int fail;
    do{
        asm volatile ("ldrex r0,[%1]\n"
           "   orr r0,r0,%2\n"
           "   strex %0,r0,[%1]": "=r"(fail) : "r"(addr), "r"(val): "r0" );
    }while(fail!=0);
}

static inline void
locked_insert_to_reg( volatile unsigned * addr, unsigned val, int width, int pos )
{
    int fail;
    if(width >=32 ) {
        *addr = val;    // assume wid = 32, pos = 0;
    }else{
        unsigned m=(1<<width)-1;
        val = (val&m) << pos;   // mask and position
        m <<= pos;

        do{
            asm volatile ("ldrex r0,[%1]\n"
               "   bic r0,r0,%2\n"   /// bic with mask
               "   orr r0,r0,%3\n"    // or result
               "   strex %0,r0,[%1]": "=r"(fail) : "r"(addr), "r"(m), "r"(val): "r0" );
        }while(fail!=0);
    }
}

Mi sembra che questo sia il tipo di cosa che dovrebbe essere nei file .h specifici del processore, ma nessun file .h in / usr / include o / usr / lib / gcc / arm-linux-gnueabihf / contiene la stringa 'ldrex '. Forse un builtin o una delle intestazioni del kernel?
Greggo,

1
ldrex / strex sono destinati alla condivisione multi-core delle risorse (ram condiviso). swp è tradizionalmente usato per il blocco single core di una risorsa single core. ldrex / strex, sembra funzionare come una soluzione single core (DIPENDENDO DAL VENDITORE DI CHIP), quindi viene utilizzato in modo improprio. sembra funzionare sul processore raspberry pi.
old_timer
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.