Projekt SYKO (Lato 2018) Ostatnia aktualizacja: 2018.03.05 Założenia ogólne 1.Punktacja (w całości 50pkt) 20pkt - implementacja 20pkt - testowanie 10pkt - dokumentacja 2.Grupa - trzy osobowa - nie może zmieniać tematu podczas pracy (przypisanie tematu do osób). W przypadkach spornych - decyduje prowadzący zajęcia projektowe. Założenia szczegółowe 1.Implementacja wirtualnego procesora. Implementacja wirtualnego procesora powinna być zapisana w języku "C", w jednym z następujących środowisk programistycznych: GCC MinGW -działającym "pod system" Linux (Debian, Ubuntu, Fedora, Cygwin) -darmowy kompilator C (na bazie gcc), dla środowiska win32/win64: http://www.mingw.org, http://sourceforge.net/projects/mingw/files/ zalecana wersja - nie starsza niż 3.2.3, opis co trzeba pobrać i jak instalować jest na stronie: http://www.mingw.org/download.shtml Implementacja powinna być pozbawiona wszelkich konstrukcji właściwych dla C++ czy C#. Dodatkowo nie wolno używać elementów interfejsu z użytkownikiem (t.j. niedozwolona jest jakakolwiek grafika lub jakiekolwiek ramki w trybie tekstowym). Na poniższym rysunku widać przykładowy przepływ informacji wejściowych/wyjściowych oraz umiejscowienie wirtualnego procesora: Kod (file_code.bin) Dane wejściowe (file_data_in.bin) Wirtualny procesor Dane wyjściowe (file_data_out.bin) Jak widać na powyższym rysunku kod emulowanej aplikacji testowej (binarna forma) - wykonywany przez wirtualny procesor - ładowany jest z pliku: file_code.bin a dane wejściowe dla tej aplikacji testowej, wczytywane są z pliku: file_data_in.bin Po zakończeniu wykonywania aplikacji testowej przez procesor wirtualny zawartość pamięci danych powinna być zapisana do pliku: file_data_out.bin Na bazie analizy zawartości pamięci danych możliwe jest ustalenie czy wirtualny procesor wykonał poprawnie aplikację testową. W pliku po adresem http://cygnus.tele.pw.edu.pl/olek/doc/syko/syko_cpu_20180305.zip zawarto szkielet interpera (podstawowa część wirtualnego procesora) oraz przykładowe implementacje instrukcji i bloków towarzyszących na bazie których można napisać własny wirtualny procesor. 2.Implementacja - postać źródłowa Kod źródłowy implementacji wirtualnego procesora powinien być zwięzły i efektywny. Rozumie się przez to, że ma być on jak najkrótszy (np.: co liczby linii kodu, liczby znaków) a 1/7
po kompilacji działać poprawnie i jak najszybciej. Dodatkowo negatywnie o jakości utworzonej implementacji świadczyć będzie rozrzutność w użytych zasobach wymaganych dla pracy wirtualnego procesora. Zatem kody źródłowe oddawanego projektu powinny (kolejność odzwierciedla wagę): -działać poprawnie (zgodnie z opisem wirtualnego procesora), -dawać się testować (zgodnie z przygotowanymi danymi wejściowymi), -dawać się ponownie kompilować, a wyniki kompilacji powinien dawać się uruchamiać wielokrotnie (dając za każdym razem oczekiwany rezultat), -być czytelne i poprawnie skomentowane. Zalecenia odnośnie dobrego kodowania w języku C, można znaleźć na stronie: http://galaxy.uci.agh.edu.pl/~worek/praktyki/standard_kodowania_2007-07-17.pdf 3.Testowanie własnego produktu Jednym z elementów zadania projektowego jest przemyślane przygotowanie plików testowych. Pliki te mają potwierdzić prawidłowe działanie emulowanych komponentów i instrukcji poprzez wytworzony wirtualny procesor. 3.1.Przygotowanie zestawów plików testowych Zakłada się, że każdy zestaw plików testowy jest mini-aplikacją testującą wirtualny procesor pod określonym kątem (np.: generowanie przerwań, instrukcje dodawania,...) oraz plików pomagających odzwierciedlić stan otoczenia wirtualnego procesora. Zaleca się zatem przemyślenie co i w jakiej kolejności będzie testowane. W wyniku takich przemyśleń powinno powstać wiele (nieraz bardzo specyficznych) zestawów plików testowych. W podanym szkielecie wirtualnego procesora mamy zatem zestaw składający się z: file_code.bin - plik z obrazem pamięci kodu, testującym dane zagadnienie file_pc.bin - plik z licznikiem PC/IP wskazującym gdzie aktualnie znajdujemy się podczas testowania file_data.bin - plik z obrazem pamięci danych, utworzonym dla danego obrazu kodu file_counter.bin - plik z licznikiem cykli wykonanych przez wirtualny procesor Studencka grupa projektowa w ramach prac projektowych ma utworzyć pełen zestaw testów dla otrzymanego przez siebie zadania. Testy te powinny sprawdzać zarówno typowe zachowania wirtualnego procesora, jak i warunki brzegowe (poszczególnych jego instrukcji jak i emulowanych peryferii). Dla przykładu testując zachowanie instrukcji dodawania, np.: ADC R1, R2 można napisać następujący kod (zakładając że poprzednio przetestowano całościowo poprawność działania instrukcji MOV): MOV R1, CONST1 MOV R2, CONST2 MOV FLAGS,CONST3 ADC R1, R2 MOV GDZIES_DATA1, R1 MOV GDZIES_DATA2, FLAGS //R1=CONST1 //R2=CONST2 //FLAGS=0 (w tym C=0) //R1=R1+R2+C //MEM[GDZIES_DATA1]=R1 //MEM[GDZIES_DATA2]=FLAGS Dla sprawdzenia wszystkich warunków (zwykłych i brzegowych) test ten trzeba przeprowadzić dla wielu różnych wartości stałych: CONST1, CONST2 oraz CONST3 - a nie tylko dla jednego jakoś wybranego zestawu tych wartości. Dla testowania warunków zwykłych będą to wartości, np.: CONST1 CONST2 CONST3 (bit C) 1 2 0 1 2 1 2/7
Natomiast dla testowania warunków brzegowych hipotetycznego wirtualnego procesora 4 bitowego - czyli takiego w którym ALU operuje naraz na 4 bitach - należałoby wykonać testy także z wartościami: CONST1 CONST2 CONST3 (bit C) 1 14 0 1 14 1 1 15 0 1 15 1 14 1 0 14 1 1 15 1 0 15 1 1 Pliki binarne testujące wirtualny procesor można tworzyć "ręcznie", lecz jest to proces bardzo pracochłonny i podatny na błędy. Innym zalecanym podejściem jest wykorzystanie kompilatorów języka asembler dla testowanego procesora. Takie narzędzia są z reguły dostarczane łącznie z programami narzędziowymi kompilatorów języka C. Warto tutaj skorzystać z prostego w instalacji zestawu narzędzi dostarczanego wraz z środowiskiem Arduino IDE 1.8.5: https://www.arduino.cc/en/main/software Środowisko to jest przygotowane dla wielu systemów operacyjnych, co może ułatwić tworzenie testowych plików. Proszę pamiętać iż po instalacji środowisko to nie dodaje swoich narzędzi do ścieżki systemowej, co pociąga za sobą konieczność specyfikacji pełnej ścieżki podczas używania odpowiednich narzędzi bądź zmodyfikowania zmiennych systemowych, tak aby odpowiednie narzędzia były automatycznie wyszukiwane. 3.2.Jak dokumentować testy Przygotowanie plików z testami to etap wstępny, przeprowadzenie testów to etap właściwy procedury testowej a etap finalny to udokumentowanie, że dany test został przeprowadzony i wynik są zgodne z oczekiwaniami. Cały ten komplet podlega ocenianiu. Podstawowe założenie właściwego etapu testowania, to założenie, że wirtualny procesor ma działać zgodnie z jego dokumentacją. Często twórca takich testów zapomina o bardzo istotnym fakcie, że procesor wykonując to co opisuje dokumentacja nie może wykonać nic więcej (np.: zmodyfikować nieodwracalnie jakąś cząstkę pamięci na chwilę ). Jednym z elementów testowania jest zatem dowód wykazujący, że procesor wykonał zadany test i nie zmienił się jego stan w niezamierzony sposób (dotyczy to nie tylko jego rejestrów ale i reszty logiki w tym i zawartości jego pamięci). Aby udokumentować w zwięzły sposób poprawność stanu procesora po zakończeniu danej operacji (lub zestawu operacji) można posługując się narzędziami porównywania plików binarnych sprawdzić stan pamięci danych przed i po danym zestawie operacji i w odpowiedni sposób zacytować co w zawartości pamięci zostało zmienione. Jednym z podstawowych narzędzi może być CMP (narzędzie dostępne w większości dystrybucji systemu Linux, czy pod cygwin), dla dwóch plików file_data_in.bin i file_data_out.bin wynik porównania mógłby wyglądać następująco: >cmp file_data_in.bin file_data_out.bin -l 8 125 61 15 31 101 16 111 101 W powyższym przykładzie plik: file_data_in.bin, przechowuje zawartość przed uruchomieniem emulacji a plik: file_data_out.bin, stan po zakończeniu emulacji. 3/7
Widać zatem z raportu narzędzia CMP, iż różnice są na pozycjach: 8, 15 i 16 oraz widać jak zmieniła się treść. Pierwszy plik zawierał na pozycji 8 znak o kodzie 125 a drugi o kodzie 61, itd. Choć podejście te z początku jest mało przyjazne, ułatwia w efekcie dokumentowanie zmian pokazując tylko to co się zmieniło. Innym ważnym aspektem jest testowanie peryferii wirtualnego procesora. Testowanie to zasadniczo jest podobne do testowania instrukcji, różnica jednak polega na tym, iż emulowanie otoczenia procesora jest nieco trudniejsze. Zakładając konieczność przetestowania mechanizmu przerwań zewnętrznych, moduł emulacji bloku obsługi tych przerwań powinien bazować podczas swojej pracy na pliku opisu stanu wejść GPIO, np.: file_gpio_in.txt. Format danych zapisanych w tym pliku mógłby wyglądać tak: 0 4 1 0 3 1 0 2 1 10 4 0 200 3 0 201 4 1... Tak przygotowany plik opisuje w kolumnie 1 numer cyklu procesora kiedy określona zmiana ma nastąpić. W kolumnie drugiej opisana jest informacja które wejście GPIO ma być zmieniona, a o tym na jaki stan informuje nas ostatnia kolumna. Dla przykładu z pliku tego wynika, że w cyklu 0 (start emulacji) GPIO numer 4 ma przyjąć stan 1, podczas gdy w cyklu 10 nastąpi zmiana na 0, a w cyklu 201 ponownie zmieni swój stan na 1. W przypadku testowania działania GPIO podczas zmiany ich stanów wyjściowych sytuacja jest niemal identyczna tyle, że plik będzie opisywał stan zmian GPIO po określonych operacjach. I tak file_gpio_out.txt, może podawać stan wszystkich GPIO po każdej operacji wirtualnego procesora (podejście nie zalecane ze względu na ilość generowanej informacji) albo odnotowywać tylko fakt zmiany opisując kiedy nastąpiła ta zmiana, czego dotyczyła i jaki stan osiągnęła po zmianie. Dla przykładu plik: 0 5 1 11 5 0 11 6 0 13 5 1 14 6 1 Opisuje jedynie zmiany GPIO 5 i 6 informując nas, że stan pozostałych nie uległ zmianie. Format tego pliku jest podobny do formatu pliku file_gpio_in.txt, pierwsza kolumna to numer cyklu, druga numer GPIO a ostatnia stan po zmianie. 4.Procedura oddawania projektu Dla uproszczenia procedury oddawania projektu, prace wynikowe powinny być przygotowane dla środowiska komputera klasy PC. Zaleca się jednak wykorzystanie jednego z systemów wirtualizacji: VirtualBox, Qemu, choć istnieje także możliwość prezentowania zadania podczas zaliczenia na laptopie studentów. Zadania przygotowane pod systemem wirtualizacji podczas odbioru zadania projektowego powinny być przygotowane (wyeksportowane) tak aby możliwe było nagranie ich na płycie CD lub DVD (4,7G). Takie płyty należy dostarczyć dnia poprzedzającego zaliczanie projektu. Przygotowanie wirtualizacji lub zaliczanie z własnego laptopu, zapobiegnie sytuacjom w którym podczas obrony projektu, nie powiedzie się jego uruchomienie, zarówno postaci 4/7
wynikowej jak i kompilacja postaci źródłowej (np.: na skutek różnic środowisk kompilacji u studenta i u prowadzącego). W przypadku przyjścia na zaliczenie z własnym laptopem proszę skonfigurować go tak aby łączność z siecią Internet zapewniała sieć wydziałowa albo wbudowana karta sieci Ethernet pobierająca numer IP z serwera DHCP. Mimo ułatwień ze strony uczelni, ze względu na problemy licencyjne, wirtualizowany PC powinien mieć zainstalowany system operacyjny dla którego nabywanie licencji nie jest wymagane (Ubuntu/Debian/...). 5.Wirtualny procesor AVR Informacje o budowie listy rozkazów procesora AVR można znaleźć na stronie: http://ww1.microchip.com/downloads/en/devicedoc/atmel-0856-avr-instruction-set-manual.pdf Informacje o mikrokontrolerze Atemag328p (jedna linia) http://ww1.microchip.com/downloads/en/devicedoc/atmega328_p%20avr%20mcu%20with%20picopower%20technolo gy%20data%20sheet%2040001984a.pdf Informacje o mikrokontrolerze Atemag128 http://ww1.microchip.com/downloads/en/devicedoc/doc2467.pdf Informacje o mikrokontrolerze Atemag2561 (jedna linia) http://ww1.microchip.com/downloads/en/devicedoc/atmel-2549-8-bit-avr-microcontroller-atmega640-1280-1281-2560- 2561_datasheet.pdf Należy tutaj zaznaczyć, iż dla danej grupy projektowej podane do zaimplementowania instrukcje mogą występować w różnych trybach adresowania (ang. addressing mode) - obowiązuje zatem konieczność implementacji wszystkich ich kombinacji co oznacza, że liczba instrukcji do zaimplementowania może być granicznie równa: {zadana liczba instrukcji} x {wszystkie tryby adresowania dla danej instrukcji} 5.1.Testowanie własnej implementacji procesora AVR Sposób posługiwania się kompilatorem dla generowania plików testowych jest następującym: a)tworzenie z pliku asemblerowego (plik: main.s), wyniku w postaci pliku O (plik pośredni): >avr-as -mmcu=atmega328 main.s -o main.o b)linkowanie - przekształcenie do postaci wynikowej ELF >avr-gcc -nostartfiles -mmcu=atmega328 main.o -o main.elf c)przekształcanie postaci ELF w postać z surowymi binariami czyli wygenerowanie zawartości pliku: file_code.bin: >avr-objcopy --output-target binary main.elf file_code.bin d)weryfikacja procesu kompilacji (de-asemblacja wyniku kompilacji) - przydatne dla celów sprawdzenia poprawności sporządzenia pliku testowego z pliku: main.s: >avr-objdump -d main.elf > main.lst W pliku: main.lst, zawarta jest ostateczną postać wyniku kompilacji zalecane jest sprawdzenie tej postaci. 5/7
W poniższym listingu zawarto przykład pliku: main.s, testujący instrukcje call (przy założeniu, że instrukcje: jmp, push, nop, rjmp,, zostały wcześniej przetestowane):.text.global main.type main, @function my_init = 0 my_vector_1 = 4 my_vector_2 = 8 my_vector_3 = 12.section.vectors,"ax",@progbits.global my_vectors.func my_vectors my_vectors: jmp main jmp my_vector_1 jmp my_vector_2 jmp my_vector_3.endfunc main: push r29 call func loop_main: nop rjmp loop_main func: nop nop my_vector_1: my_vector_2: my_vector_3: A wygenerowany plik main.lst, wygląda tak: main.elf: file format elf32-avr Disassembly of section.text: 00000000 <my_vectors>: 0: 0c 94 08 00 jmp 0x10 ; 0x10 <main> 4: 0c 94 02 00 jmp 0x4 ; 0x4 <my_vectors+0x4> 8: 0c 94 04 00 jmp 0x8 ; 0x8 <my_vectors+0x8> c: 0c 94 06 00 jmp 0xc ; 0xc <my_vectors+0xc> 00000010 <main>: 10: df 93 push r29 12: 0e 94 0d 00 call 0x1a ; 0x1a <func> 00000016 <loop_main>: 16: 00 00 nop 18: fe cf rjmp.-4 ; 0x16 <loop_main> 0000001a <func>: 1a: 00 00 nop 1c: 00 00 nop 1e: 08 95 00000020 <my_vector_1>: 20: 08 95 00000022 <my_vector_2>: 22: 08 95 00000024 <my_vector_3>: 24: 08 95 Analizując kod w main.lst, widać, że wszystkie adresy i przesunięcia w kodzie wygenerowane zostały poprawnie. 6/7
Wyjaśnienia wymaga, istnienie zestawu czterech instrukcji JMP które wstawiono na początku pamięci kodu. Zakłada się, że wirtualny procesor będzie mógł emulować system przerwań, a zgodnie z dokumentacją procesorów AVR na początku pamięci kodu znajduje się tablica wektorów przerwań. Dla każdego z wektorów przewidziano 4B miejsca tyle aby można było dane przerwanie przekierować w miejsce z pełną obsługą tego przerwania, co wykonuje się właśnie za pomocą instrukcji JMP. W powyższym przykładzie większość tych wektorów jest zaślepiona prostą obsługą wektorów przerwań i jest zakończoną instrukcją RET. Tylko obsługa wektora RESET jest w pełni obsłużona jest to typowe dla prostych aplikacji testowych. W aplikacjach użytkowych z reguły korzysta się z bibliotek, które podczas linkowania są automatycznie dołączane do kodu. W zadaniu projektowym takie zachowanie linkera mocno utrudniłoby bądź uniemożliwiło testowanie systemu stąd należy za pomocą argumentu przekazanego podczas wywołania linkera nostartfiles, uniemożliwia się dołączanie niechcianego kodu. Na zakończenie należy zwrócić uwagę na aspekt związany z niezbędną do zaimplementowania listą instrukcji wirtualnego procesora. Mimo, iż w treści zadań przekazanych grupom projektowym podano tylko parę instrukcji a w niektórych przypadkach nie ma ich w ogóle, należy jednak pamiętać, że testowany jest procesor, dla inicjalizacji i używania emulowanych peryferii wymaga posiadania odpowiedniej listy instrukcji umożliwiającej obsługę i testowanie tych peryferii. Sugeruje się zatem aby przed przystąpieniem do procesu testowania przemyśleć jakie instrukcje procesora AVR faktycznie będą potrzebne i je zaimplementować. Warto także zaznaczyć, iż zaimplementowana lista instrukcji przez daną grupę projektową ma być wybrana wyłącznie pod kątem swoich testów. Stąd instrukcje zaimplementowane a nie używane podczas testów nie będą oceniane, a co ważniejsze mogą wpłynąć negatywnie na ocenę końcową. 7/7