PRZERWANIA, DMA, UART Orkiszewski Marcin Koło Naukowe Układów Cyfrowych DEMAIN
Altera NIOS II Wymagane oprogramowanie: Quartus II v. 9.0 Nios IDE v. 9.0 Do pobrania na stronie www.altera.com
Uwaga! Katalogi, w których instalujemy oprogramowanie oraz w których zakładamy projekty: ! NIE MOGĄ zawierać spacji !
Uwaga! Unikać powtarzania nazw: - block diagram: <..>_project - SOPC: <..>_sopc - Nios IDE <..>_soft W razie problemów www.google.com
Założenie projektu
1. Otworzenie projektu Quartus II Project Restore Archived Project… Otworzenie archiwum projektu: Quartus II Project Restore Archived Project…
[CPU] – bez Data Cache
[Buttons] – generują przerwanie
[UART] – 8 bitowy
[SDRAM] – 16 bitowy !!
[PLL] 50 MHz 100 MHz (-65˚ dla SDRAM)
[RAM] – dwuportowa pamięć !
2. Kompilacja projektu Processing Start compilation - Wykorzystanie komórek logicznych - Wykorzystanie bloków pamięci
3. Załadowanie obrazu do układu Tools Programmer - Hardware Setup USB-Blaster Program/Configure Start - Czasami przydatne bywa włączenie zasilania -
Programowanie
7. Założenie projektu (1/2) File New Project… Nios II C/C++ Application
7. Założenie projektu (2/2) Wskazanie obrazu systemu (.ptf): Select project template: Blank Project Name: szkolenie2_soft SOPC Builder System PTF File: ../szkolenie2_sopc.ptf Finish
8. Kompilacja biblioteki Prawy klawisz myszki na szkolenie2_soft_syslib Properties System Library Reduced device drivers Small C library itp.. Build Project
9. Dodanie plików źródłowych (C/C++) Prawy klawisz myszki na szkolenie2_soft Import… General File System szkolenie2.h szkolenie2_1.c szkolenie2_2.c szkolenie2_3.c szkolenie2_4.c szkolenie2_5.c szkolenie2_6.c
10. Uruchomienie projektu Run Run… Nios II Hardware Main Project: szkolenie2_soft Target Connection: USB-Blaster Run
Program 1 (Buttons)
Program 1 int main() { // init while(1) ; // ? } return 0;
Przerwanie Asynchroniczne sprzętowe zgłoszenie się komponentu. Przerywa obecnie wykonywany kod (skok do funkcji przerwania + zachowanie stanu procesora na stosie). Wymaga odblokowania dla każdego z komponentów (oraz potwierdzenia !). Przerwanie o wysokim priorytecie przerywa przerwanie o niskim priorytecie (możliwość zagnieżdżania przerwań).
vs. Pooling Przegląd urządzeń (czy nie potrzebują obsługi) „Wyżeramy” 100% mocy procesora nic nie robiąc (tzw. aktywne oczekiwanie), nieprzerywalność operacji obsługi (np. nie możemy odbierać danych, bo akurat coś liczymy, przegapiamy wciśnięcie przycisku, itp..), nie możemy ustalić priorytetów. „Technicznie chałowe w najwyższym stopniu”. Dr inż. Jerzy Chrząszcz (Techniki Mikroprocesorowe)
Jak używać ? Wykonywać tylko to, co musi być zrobione natychmiast ! Resztę operacji dokończyć w pętli głównej programu (poprzez ustawienie odpowiednich znaczników – zmiennych globalnych). Pamiętać o kasowaniu znaczników przerwania ! Unikać zagnieżdżania przerwań. Unikać tworzenia zmiennych lokalnych, nie wywoływać funkcji printf. Używać rozsądnie – zbyt częste lub zbyt rozbudowane przerwania skutecznie paraliżują procesor.
Program 1 #include <sys/alt_irq.h> void BUTTONS_ISR( void *context, unsigned long id ) { //… } int main() { alt_irq_register( BUTTONS_IRQ, 0, BUTTONS_ISR ); } // Zarejestrowanie funkcji przerwania
Komponent PIO – mapa rejestrów IOWR_ALTERA_AVALON_PIO_IRQ_MASK( BUTTONS_BASE, 0xFF );
Program 1 volatile alt_u8 buttons; // zmienna globalna volatile void BUTTONS_ISR( void *context, unsigned long id ) { buttons = IORD_ALTERA_AVALON_PIO_DATA( BUTTONS_BASE ); IOWR_ALTERA_AVALON_PIO_DATA( LED_G_BASE, buttons ); // potwierdzenie przerwania IOWR_ALTERA_AVALON_PIO_EDGE_CAP( BUTTONS_BASE, 0 ); }
Program 2 (Timer)
Program 2 volatile alt_u8 irq_flag = 0; void TIMER_ISR( void *context, unsigned long id ) { irq_flag = 1; IOWR_ALTERA_AVALON_TIMER_STATUS( TIMER_BASE, 0 ); // potwierdzenie } int main() alt_irq_register( TIMER_IRQ, 0, TIMER_ISR );
Program 2 // Obsługa w pętli głównej programu int main() { IOWR_ALTERA_AVALON_TIMER_PERIODH( TIMER_BASE, 0x05F5 ); // 100 MHz IOWR_ALTERA_AVALON_TIMER_PERIODL( TIMER_BASE, 0xE100 ); IOWR_ALTERA_AVALON_TIMER_CONTROL( TIMER_BASE, 0x01 | 0x02 | 0x04 ); while(1) if( irq_flag ) // obsługa przerwania irq_flag = 0; }
Program 3 (DMA)
alt_u16 * pointer = SDRAM_BASE; #include ”system.h” alt_u16 * pointer = SDRAM_BASE; Odczyt i zapis do SDRAM realizowany jest przez sprzętowy kontroler pamięci. Aby operować na tej pamięci wystarczy posłużyć się zwykłym wskaźnikiem!
DMA (Direct Memory Access) Idea – szybkie kopiowanie danych (o rozmiarze LENGTH) spod adresu READADDRESS pod adres WRITEADDRESS
DMA - inicjalizacja #include ”altera_avalon_dma_regs.h” // alt_u16 data[10] – tablica danych IOWR_ALTERA_AVALON_DMA_RADDRESS( DMA_BASE, data ); // adres docelowy IOWR_ALTERA_AVALON_DMA_WADDRESS( DMA_BASE, SDRAM_BASE ); // 16-bit, DMA aktywne, kopiuj określoną liczbę danych IOWR_ALTERA_AVALON_DMA_CONTROL( DMA_BASE, 0x02 | 0x08 | 0x80 ); // start IOWR_ALTERA_AVALON_DMA_LENGTH( DMA_BASE, 20 ); DMA skopiuje 20 BAJTÓW !
DMA - koniec transferu // oczekiwanie na koniec transferu danych while( IORD_ALTERA_AVALON_DMA_STATUS( DMA_BASE ) & 0x11 != 0x11 ) ; 0x11 = 10001
DMA - ustawienia główne 0x0001 0x0002 0x0004 0x0008 - włączenie DMA 0x0010 - przerwanie 0x0020 0x0040 0x0080 - warunek stopu 0x0100 - stały adres odczytu 0x0200 - stały adres zapisu 0x0400 0x0800 0x1000
Program 3 Kopiowanie tablicy alt_u16 data[10] do SDRAM (10 x 16 bitów = 20 bajtów) IOWR_ALTERA_AVALON_DMA_STATUS( DMA_SDRAM_BASE, 0 ); IOWR_ALTERA_AVALON_DMA_RADDRESS( DMA_SDRAM_BASE, data ); IOWR_ALTERA_AVALON_DMA_WADDRESS( DMA_SDRAM_BASE, SDRAM_BASE ); IOWR_ALTERA_AVALON_DMA_CONTROL( DMA_SDRAM_BASE, (0x02 | 0x08 | 0x80 ) ); IOWR_ALTERA_AVALON_DMA_LENGTH( DMA_SDRAM_BASE, 20 ); while ( (IORD_ALTERA_AVALON_DMA_STATUS( DMA_SDRAM_BASE ) & 0x11 ) != 0x11 ) ;
Program 3 Kopiowanie wartości do SDRAM IOWR_ALTERA_AVALON_DMA_STATUS( DMA_SDRAM_BASE, 0 ); IOWR_ALTERA_AVALON_DMA_RADDRESS( DMA_SDRAM_BASE, &data[6] ); IOWR_ALTERA_AVALON_DMA_WADDRESS( DMA_SDRAM_BASE, SDRAM_BASE ); IOWR_ALTERA_AVALON_DMA_CONTROL( DMA_SDRAM_BASE, 0x02 | 0x08 | 0x80 |0x100 ); IOWR_ALTERA_AVALON_DMA_LENGTH( DMA_SDRAM_BASE, 20 ); while ( (IORD_ALTERA_AVALON_DMA_STATUS( DMA_SDRAM_BASE ) & 0x11 ) != 0x11 ) ;
DMA - informacje Maksymalną liczbę danych jaką może kopiować DMA określamy w SoPC Wartość w rejestrze LENGTH musi być wielokrotnością danego typu (np. dla słów 32 bitowych możemy ustawiać tylko wielokrotność 4) Przed ponownym uruchomieniem DMA musimy ponownie określić adresy docelowe, chyba że zostały one oznaczone jako niezmienne W każdej chwili można sprawdzić, ile bajtów DMA już skopiowało (poprzez odczyt rejestru LENGTH) Należy znać adresy komponentów podłączanych do DMA, które nie znajdują się w przestrzeni adresowej mikroprocesora (nie ma ich w ”system.h”).
DMA - konfiguracja
Program 4 (test DMA)
Program 4 - wydajność DMA Ile danych w ciągu 1 sekundy skopiuje do SDRAM: Mikroprocesor ? DMA ? Timer po 1 sekundzie przerywa zapis danych i sprawdza ile MB udało się skopiować DMA, a ile mikroprocesorowi.
Program 5 (UART)
UART (Universal Asynchronous Receiver and Transmitter) Idea – zarządzanie transmisją danych np. RS-232, RS-485 lub między komponentami !
Program 5 - echo #include ”altera_avalon_uart_regs.h” int main() { alt_u8 data; while(1) if( IORD_ALTERA_AVALON_UART_STATUS( UART_BASE ) & 0x80 ) data = IORD_ALTERA_AVALON_UART_RXDATA( UART_BASE ); IOWR_ALTERA_AVALON_UART_TXDATA( UART_BASE, data ); } return 0; }
UART - koniec transferu // czy otrzymano dane ? if( IORD_ALTERA_AVALON_UART_STATUS( UART_BASE ) & 0x80 ) // czy dane wysłane ? while( ! (IORD_ALTERA_AVALON_UART_STATUS( DMA_BASE ) & 0x40) ) ; 0x40 0x80
Program 6 (UART + DMA)
Program 6 Podłączenie UART pod DMA – nasłuch: volatile alt_u8 data_recv[5]; IOWR_ALTERA_AVALON_DMA_STATUS( DMA_RS_READ_BASE, 0 ); IOWR_ALTERA_AVALON_DMA_RADDRESS( DMA_RS_READ_BASE, UART_BASE ); IOWR_ALTERA_AVALON_DMA_WADDRESS( DMA_RS_READ_BASE, data_recv ); IOWR_ALTERA_AVALON_DMA_CONTROL( DMA_RS_READ_BASE, (0x01 | 0x08 | 0x10 | 0x80 | 0x100) ); // 8bit & GO & IRQ & LEEN & RCONST // nasłuch 5 bajtów IOWR_ALTERA_AVALON_DMA_LENGTH( DMA_RS_READ_BASE, 5 );
Program 6 Podłączenie UART pod DMA – nadawanie: volatile alt_u8 data_recv[5]; IOWR_ALTERA_AVALON_DMA_STATUS( DMA_RS_WRITE_BASE, 0 ); IOWR_ALTERA_AVALON_DMA_RADDRESS( DMA_RS_WRITE_BASE, data_recv ); IOWR_ALTERA_AVALON_DMA_WADDRESS( DMA_RS_WRITE_BASE, (UART_BASE+1) ); IOWR_ALTERA_AVALON_DMA_CONTROL( DMA_RS_WRITE_BASE, (0x01 | 0x08 | 0x80 | 0x200) ); // 8bit & GO & LEEN & WCONST
Program 6 - echo DMA_READ ustawiane na odbiór 5 bajtów. Po odebraniu 5 bajtów, DMA_READ zgłasza przerwanie. W funkcji przerwania uruchamiane jest DMA_WRITE, które odpowiada 5 bajtami odebranymi. W funkcji przerwania ustawiana jest flaga do obsłużenia w pętli głównej programu. W pętli głównej sprawdzane jest, czy DMA_WRITE zakończyło nadawanie. W pętli głównej DMA_READ jest ponownie ustawiane odbiór 5 bajtów.
Materiały dostępne na stronie Koła http://demain.zpt.tele.pw.edu.pl/ Publikacje 24 XI 2009