W wielu aplikacjach procesor, którego wykonywanie instrukcji ma znaną zależność czasową z oczekiwanymi bodźcami wejściowymi, może obsłużyć zadania wymagające znacznie szybszego procesora, gdyby związek był nieznany. Na przykład w projekcie, w którym użyłem PSOC do wygenerowania wideo, użyłem kodu do wyprowadzenia jednego bajtu danych wideo co 16 taktów procesora. Ponieważ testowanie, czy urządzenie SPI jest gotowe i rozgałęzienie, jeśli nie, IIRC zajmie 13 zegarów, a ładowanie i przechowywanie danych wyjściowych zajmie 11, nie było możliwości przetestowania urządzenia pod kątem gotowości między bajtami; zamiast tego po prostu ustawiłem, aby procesor wykonał dokładnie kod o wartości 16 cykli dla każdego bajtu po pierwszym (wydaje mi się, że użyłem rzeczywistego obciążenia indeksowanego, sztucznego obciążenia indeksowanego i magazynu). Pierwszy zapis SPI każdej linii miał miejsce przed rozpoczęciem wideo, a dla każdego kolejnego zapisu było 16-cyklowe okno, w którym zapis mógł wystąpić bez przepełnienia lub niedopełnienia bufora. Pętla rozgałęziająca wygenerowała 13-cyklowe okno niepewności, ale przewidywalne wykonanie 16-cyklowe oznaczało, że niepewność dla wszystkich kolejnych bajtów mieściłaby się w tym samym oknie 13-cyklowym (które z kolei mieszczą się w 16-cyklowym oknie, w którym zapis może być akceptowalny pojawić się).
W przypadku starszych procesorów informacje o taktowaniu instrukcji były jasne, dostępne i jednoznaczne. W przypadku nowszych układów ARM informacje o taktowaniu wydają się znacznie bardziej niejasne. Rozumiem, że kiedy kod jest wykonywany z pamięci flash, zachowanie buforowania może znacznie utrudnić przewidywanie, więc spodziewałbym się, że każdy kod liczony w cyklu powinien być wykonywany z pamięci RAM. Jednak nawet podczas wykonywania kodu z pamięci RAM specyfikacje wydają się nieco niejasne. Czy stosowanie kodu liczonego w cyklu jest nadal dobrym pomysłem? Jeśli tak, jakie są najlepsze techniki, aby działał niezawodnie? W jakim stopniu można bezpiecznie założyć, że sprzedawca mikroukładów nie zamierza po cichu wsunąć „nowego ulepszonego” układu, który w niektórych przypadkach odcina cykl wykonywania niektórych instrukcji?
Zakładając, że następująca pętla zaczyna się na granicy słów, jak określić na podstawie specyfikacji dokładnie, ile to zajmie (załóżmy, że Cortex-M3 z pamięcią stanu zerowego oczekiwania; nic innego o systemie nie powinno mieć znaczenia dla tego przykładu).
myloop: mov r0, r0; Krótkie proste instrukcje, aby umożliwić pobranie większej liczby instrukcji mov r0, r0; Krótkie proste instrukcje, aby umożliwić pobranie większej liczby instrukcji mov r0, r0; Krótkie proste instrukcje, aby umożliwić pobranie większej liczby instrukcji mov r0, r0; Krótkie proste instrukcje, aby umożliwić pobranie większej liczby instrukcji mov r0, r0; Krótkie proste instrukcje, aby umożliwić pobranie większej liczby instrukcji mov r0, r0; Krótkie proste instrukcje, aby umożliwić pobranie większej liczby instrukcji dodaje r2, r1, # 0x12000000; Instrukcja 2-słowowa ; Powtórz następujące czynności, prawdopodobnie z innymi operandami ; Będzie dodawał wartości, dopóki nie pojawi się przeniesienie itcc addscc r2, r2, # 0x12000000; 2-wyrazowa instrukcja plus dodatkowe „słowo” dla itcc itcc addscc r2, r2, # 0x12000000; 2-wyrazowa instrukcja plus dodatkowe „słowo” dla itcc itcc addscc r2, r2, # 0x12000000; 2-wyrazowa instrukcja plus dodatkowe „słowo” dla itcc itcc addscc r2, r2, # 0x12000000; 2-wyrazowa instrukcja plus dodatkowe „słowo” dla itcc ; ... itd., z bardziej warunkowymi instrukcjami składającymi się z dwóch słów sub r8, r8, # 1 bpl myloop
Podczas wykonywania pierwszych sześciu instrukcji rdzeń będzie miał czas na pobranie sześciu słów, z których trzy zostaną wykonane, aby mogły zostać pobrane maksymalnie trzy słowa. Kolejne instrukcje składają się z trzech słów, więc rdzeń nie będzie mógł pobrać instrukcji tak szybko, jak są one wykonywane. Spodziewałbym się, że niektóre instrukcje „it” zajmą cykl, ale nie wiem, jak przewidzieć, które z nich.
Byłoby miło, gdyby ARM mógł określić pewne warunki, w których czas rozkazu „it” byłby deterministyczny (np. Jeśli nie ma stanów oczekiwania lub rywalizacji o magistralę kodową, a poprzednie dwie instrukcje są instrukcjami rejestru 16-bitowego itp.) ale nie widziałem żadnej takiej specyfikacji.
Przykładowa aplikacja
Załóżmy, że ktoś próbuje zaprojektować płytę główną dla Atari 2600 do generowania komponentowego wyjścia wideo w rozdzielczości 480P. 2600 ma zegar pikseli 3,579 MHz i zegar procesora 1,19 MHz (zegar punktowy / 3). W przypadku komponentowego wideo 480P, każda linia musi być wyprowadzona dwukrotnie, co oznacza wyjście z zegarem kropkowym 7,168 MHz. Ponieważ układ wideo Atari (TIA) generuje jeden z 128 kolorów, wykorzystując jako 3-bitowy sygnał luma plus sygnał fazowy o rozdzielczości około 18ns, trudno byłoby dokładnie określić kolor, patrząc tylko na wyjścia. Lepszym rozwiązaniem byłoby przechwytywanie zapisów do rejestrów kolorów, obserwowanie zapisanych wartości i wprowadzanie do każdego rejestru wartości luminancji TIA odpowiadającej numerowi rejestru.
Wszystko to można zrobić za pomocą FPGA, ale niektóre dość szybkie urządzenia ARM mogą być znacznie tańsze niż FPGA z wystarczającą ilością pamięci RAM, aby obsłużyć niezbędne buforowanie (tak, wiem, że w przypadku woluminów taka rzecz mogłaby zostać wyprodukowana koszt nie jest prawdziwy czynnik). Wymaganie od ARM monitorowania przychodzącego sygnału zegarowego znacznie zwiększy jednak wymaganą szybkość procesora. Przewidywalne liczby cykli mogłyby uczynić rzeczy czystszymi.
Stosunkowo proste podejście polegałoby na tym, aby CPLD obserwował procesor i TIA i generował 13-bitowy sygnał synchronizacji RGB +, a następnie kazałby ARM DMA pobierać 16-bitowe wartości z jednego portu i zapisywać je na drugim z odpowiednim taktowaniem. Ciekawym wyzwaniem projektowym byłoby sprawdzenie, czy tani ARM mógłby zrobić wszystko. DMA może być użytecznym aspektem podejścia typu „wszystko w jednym”, jeśli można przewidzieć jego wpływ na liczbę cykli procesora (szczególnie jeśli cykle DMA mogą się zdarzyć w cyklach, gdy szyna pamięci jest w przeciwnym razie bezczynna), ale w pewnym momencie procesu ARM musiałby wykonywać funkcje wyszukiwania tabeli i oglądania magistrali. Zauważ, że w przeciwieństwie do wielu architektur wideo, w których rejestry kolorów są zapisywane w odstępach czasu wygaszania, Atari 2600 często zapisuje rejestry kolorów podczas wyświetlanej części ramki,
Być może najlepszym rozwiązaniem byłoby użycie kilku dyskretnych układów logicznych do identyfikacji zapisów kolorów i wymuszenie niższych bitów rejestrów kolorów do odpowiednich wartości, a następnie użycie dwóch kanałów DMA do próbkowania danych wejściowych magistrali procesora i danych wyjściowych TIA oraz trzeci kanał DMA do generowania danych wyjściowych. Procesor będzie wtedy mógł przetwarzać wszystkie dane z obu źródeł dla każdej linii skanowania, wykonać niezbędne tłumaczenie i buforować je w celu uzyskania danych wyjściowych. Jedynym aspektem obowiązków adaptera, które musiałyby się zdarzyć w „czasie rzeczywistym”, byłoby zastąpienie danych zapisanych w COLUxx, i które można by załatwić za pomocą dwóch wspólnych układów logicznych.