Jak działa adresowanie we / wy mapowane w pamięci?

29

Jak działa adresowanie we / wy mapowane w pamięci?

Próbuję zrozumieć dostarczoną próbkę I2S: Czy ktoś ją uruchomił? .

Konfigurowanie zegarów:

#define BCM2708_PERI_BASE        0x20000000
#define CLOCK_BASE               (BCM2708_PERI_BASE + 0x101000) /* Clocks */

Najpierw mapuje kod w taki sposób ...

clk_map = (unsigned char *)mmap(
      (caddr_t)clk_mem,
      MAP_BLOCK_SIZE,
      PROT_READ|PROT_WRITE,
      MAP_SHARED|MAP_FIXED,
      mem_fd,
      CLOCK_BASE
   );

Potem robi coś ...

 // Always use volatile pointer!
   clk = (volatile unsigned *)clk_map;

A kiedy się do niego odwołuje, pojawiają się te dziwne dodatki 0x26 i 0x27, o co chodzi?

 printf("Disabling I2S clock\n");
 *(clk+0x26) = 0x5A000000;
 *(clk+0x27) = 0x5A000000;

 usleep(10);

 printf("Confiure I2S clock\n");
 *(clk+0x26) = 0x5A000001;
 *(clk+0x27) = 0x5A000000 | 3<<12 | 1<<9; // divider: 3.125==0b11.001

 usleep(10);
 printf("Enabling I2S clock\n");
 *(clk+0x26) = 0x5A000011;

Patrząc na arkusz danych, widzę, gdzie mają niektóre z tych wartości, na przykład adres podstawowy, ale staram się zrozumieć inne. Gdzie to jest CLOCK_BASEokreślone i co się dzieje?

Uszy psa
źródło
1
Prawdopodobnie najlepiej nadaje się do StackOverflow. Mimo że odnosi się do RPi, chętniej znajdziesz tam odpowiedzi na pytania dotyczące programowania.
Jivings
4
Może, ale uważam, że jest to bardziej ogólne pytanie związane z programowaniem Pi, łączące interpretację arkusza danych i sprzętu Pi. zobaczmy, czy otrzyma jakieś dobre informacje.
Dog Ears
W porządku. Zobaczmy, jak leci :)
Jivings,
1
Nie sądzę, żeby to zrobiło się zbyt dobrze w przypadku przepełnienia stosu - jest dość specjalistyczny i prawdopodobnie zyskałby tutaj więcej ekspertów.
Flexo

Odpowiedzi:

18

Na komputerze piszesz pod określonym „adresem pamięci”. Ten adres jest rozpoznawany przez system jako adres sprzętowy, a odpowiedni sprzęt odbiera lub wysyła odpowiednią wartość.

Większość systemów sprzętowych ma wiele różnych rejestrów, które można ustawić lub odczytać. Niektóre mogą mieć kilka, niektóre mogą mieć wiele. Rejestry te zostaną pogrupowane w ciągły zakres. Wskaźnik bazowy wskazuje pierwszy w zakresie, a ty piszesz na przykład do drugiego portu za pomocą wskaźnika_podstawowego + 1. Nie musisz, możesz pisać bezpośrednio do wskaźnika, ale użycie offsetu ułatwia pracę.

Raspberry Pi rozpoznaje ogromny zakres rejestrów sprzętowych pod adresem 0x20000000. Dostęp do szeregu rejestrów sterujących systemami zegarowymi można uzyskać od BCM2708_PERI_BASE + 0x101000. Rejestry sterujące zegarem I2S to 38. i 39. rejestr w tym bloku, zapisany przy użyciu BCM2708_PERI_BASE + 0x101000 + 0x26 i 0x27

Nie możesz po prostu zmienić wartości zegara, musisz wyłączyć zegar, zmienić wartości i uruchomić go ponownie.

Jeśli ta odpowiedź jest zbyt prosta, przepraszam. W takim przypadku twoje pytanie jest naprawdę hardcorowe, powodzenia. Ten link może okazać się pomocny

Aktualizacja: Dlaczego warto korzystać z mmap i nie zapisywać bezpośrednio w pamięci?

Gdy program uruchamia adresy pamięci, które sądzi, że nie są to adresy rzeczywiste, menedżer pamięci mapuje je na adresy rzeczywiste. To powstrzymuje jeden program od wpływu na inny. Dwa procesy mogą doskonale odczytywać i zapisywać pod własnym adresem 1234, a menedżer pamięci zachowa dwie lokalizacje całkowicie osobno.

Porty sprzętowe znajdują się jednak pod bezwzględnymi adresami fizycznymi. Ale nie możesz do nich pisać bezpośrednio, ponieważ menedżer pamięci weźmie twój adres i zamapuje go w osobistym obszarze pamięci.

W systemie Linux / dev / mem znajduje się „ plik urządzenia znakowego, który jest obrazem głównej pamięci komputera

Jeśli otworzysz to jak plik, możesz czytać i zapisywać jak plik. W dostarczonej próbce mem_fd jest uchwytem pliku wynikającym z otwarcia / dev / mem

Innym systemem, który może znacznie ułatwić życie, jest możliwość mapowania pliku do pamięci i zapisania go jak pamięci. Jeśli więc masz plik, w którym chcesz odczytać lub zapisać różne określone bity, zamiast przesuwać wskaźnik pliku do tyłu i do przodu, możesz zmapować go do lokalizacji w pamięci, a następnie zapisać do niego bezpośrednio, tak jakby był pamięcią.

W tym przykładzie kod tworzy uchwyt pamięci fizycznej, tak jakby to był plik na dysku, a następnie prosi system, aby traktował go tak, jakby był pamięcią. Trochę skomplikowane, ale konieczne, aby obejść menedżera pamięci wirtualnej i napisać na rzeczywisty adres fizyczny. Wydaje się, że wartość 0x20000000 jest trochę czerwonym śledziem. Kod proponuje ten adres jako wskazówkę, system nie musi tutaj mapować / dev / mem, choć prawdopodobnie tak jest. Zwykle przekazywana jest wartość null, a system mapuje uchwyt pliku na dowolny adres, który uzna za najlepszy.

Teraz pamięć fizyczna jest mapowana na pamięć wirtualną procesów, a odczyty i zapisy idą tam, gdzie się tego spodziewacie.

Referencje:

http://www.kernel.org/doc/man-pages/online/pages/man2/mmap.2.html

http://www.raspberrypi.org/phpBB3/viewtopic.php?f=44&t=8496&p=104359

https://superuser.com/questions/71389/what-is-dev-mem

David Sykes
źródło
Nadal mam kilka pytań: Dlaczego mapują? Dlaczego nie uzyskać bezpośredniego dostępu do pamięci?
Alex Chamberlain
@AlexChamberlain Ponieważ kod działa w systemie Linux, więc nie można uzyskać bezpośredniego dostępu do pamięci, ponieważ każdy proces otrzymuje własną pamięć wirtualną. Można jednak otworzyć i mmap / dev / mem, aby uzyskać bezpośredni dostęp do pamięci fizycznej
nr
1

@AlexChamberlain wynika to ze struktury systemu operacyjnego. Możesz przejść bez, mmapale stronicowanie jest zadeklarowane, dlatego nie ma bezpośredniego dostępu. W trybie jądra możesz przejść bez mmap, na przykład, wstawiania sterownika jako modułu jądra bez potrzeby mmap. Ponadto w najprostszym przypadku systemu operacyjnego, w którym nie jest używana pamięć tabeli stron, można uzyskać do niej dostęp bez mmap, np. bezpośredni dostęp do adresu fizycznego.

Manoj Singh
źródło