Uczę się korzystać z FPGA (planszy Papilio, która ma xilinx spartan3e, używając vhdl).
Muszę podzielić przychodzący puls przez (zakodowany na stałe) numer.
Widzę 3 opcje - z grubsza jako pseudokod (na przykładzie 10 zliczeń):
- Zainicjuj na 0, przy wzroście zbocza narastającego o 1, w porównaniu do 10; jeśli są równe, zresetować do 0 i wyzwolić impuls wyjściowy
- Zainicjuj do 10, przy spadku narastającego zbocza wejściowego o 1, w porównaniu do 0; jeśli są równe, zresetuj do 10 i uruchom impuls wyjściowy
- Zainicjuj na 9, ale upewnij się, że jest co najmniej 1 wiodący bit „0”, który jest moim bitem wyjściowym. Przy narastającym zboczu narastającym zmniejsz o 1. Po zboczu narastającym bitu wyjściowego zresetuj.
Cykl pracy jest nieistotny.
Czy jeden z nich jest lepszy od innych? Czy istnieje jeszcze lepsza metoda, o której nie myślałem?
Czy istnieje „standardowy” sposób, który da kompilatorowi najlepszą szansę na optymalizację?
Odpowiedzi:
Optymalizacja do tego poziomu złamie ci serce. Wynik może się zmienić ze względu na zastosowaną technologię FPGA, inne czynniki w FPGA, ale również z powodu czynników niezależnych od ciebie, w tym losowej liczby instalatora.
Mimo to uważam, że wariant 3 będzie najlepszy. Opcje 1 i 2 mają bramkę porównawczą / OR przechodzącą między licznikami, aby mogła zasygnalizować, że liczba docelowa została osiągnięta. Opcja 2 może być nieco szybsza niż 1, ponieważ wszystko może być proste LUB połączone razem bez żadnych falowników, ale ponownie napotykasz małe różnice technologiczne, w których może być szybsze do AND lub XOR.
Opcja 3 pomija porównanie z niskim kosztem jednego dodatkowego bitu w kasie. To powinno być tego warte, chyba że masz poważne ograniczenia w klapkach.
Ciekawym faktem na temat liczników jest to, że mają one tendencję do grupowania w rozmiarach specyficznych dla urządzenia w bloku logicznym, a zobaczysz zmianę taktowania bardziej niż oczekiwano, jeśli ten dodatkowy bit wypchnie cię z tej grupy.
źródło
Jedna inna opcja byłoby zainicjować licznik do 6 (= 2 4 - 10), liczy się, a następnie zresetować kiedy uaktywnia wyjście carry (tj FFS są wszystkie te).
Zaletą tego jest to, że nie wymaga dodatkowego FF, a wiele FGPA ma dedykowaną logikę pomocniczą, aby przyspieszyć ten rodzaj operacji przenoszenia w obwodzie licznika lub sumatora.
źródło
Zależy. Na przykład: opóźnienie propagacji przerzutu dla 0 → 1 i 1 → 0 może być różne, a zatem opóźnienia przejścia licznika dla 000 → 001 i 001 → 000 mogą być nieco inne. Może być wyższa lub niższa, w zależności od technologii cmos stosowanej w FPGA. Musisz więc zsyntetyzować i dowiedzieć się, który z nich ma lepszą wydajność synchronizacji.
źródło
Z perspektywy autora kompilatora: jeśli go użyjesz
integer
, wewnętrzna reprezentacja jest niezdefiniowana, a kompilator może wybrać najbardziej efektywną implementację.Jeśli wymusisz określoną reprezentację wewnętrzną, optymalizator będzie nadal próbował ją ulepszyć, ale zacznie od nieco gorszego punktu widzenia.
Niektóre układy FPGA mają funkcje „wstępnego ładowania”, w których rejestry mogą być inicjowane do dowolnych wartości, w takim przypadku inicjowanieN.- 1 , odliczanie i użycie najwyższego bitu przenoszenia jako wyjścia i resetu (w następnym cyklu) jest bardziej wydajne niż implementacja zarówno sumatora, jak i komparatora. Bez wstępnego ładowania sumator może być lepszy.
O ile nie znasz wewnętrznej struktury, zasoby przydzielone na inną logikę (wiele układów FPGA ma dedykowaną zmiennoprzecinkową logikę wielokrotnego dodawania, której możesz również użyć do implementacji licznika, jeśli masz resztki jednostek) i masz całkowitą pewność, że nie zmienisz w innym modelu odpowiedź brzmi „nie myśl o tym”.
źródło