Zadanie 3: Kalkulator

Data ogłoszenia: 01.12.2020

Termin oddania: 12.01.2021

Archiwum z plikami: z3_calc.tar.gz, zawiera:

  • calc_template.py: szablon rozwiązania

  • calc_sw.py: wzorcowa implementacja w Pythonie

  • calc_gentest.py: generator testów (używany przez oba testy)

  • calc_test_sim.py: testuje rozwiązanie w symulacji

  • calc_wrapper.py: syntezuje rozwiązanie na Zynq (uruchamiamy bez argumentów, otrzymujemy bitstream w build/top.bin)

  • calc_run_hw.py: uruchamia zsyntezowane rozwiązanie na serwerze, pozwalając na interakcję z portem szeregowym (podajemy ścieżkę do pliku .bin jako argument)

  • calc_test_hw.py: testuje zsyntezowane rozwiązanie na serwerze (podajemy ścieżkę do pliku .bin jako argument)

Napisać układ implementujący kalkulator z interfejsem przez port szeregowy.

Jedynym interfejsem układu jest port szeregowy używający wyłącznie sygnałów rxd i txd. Układ powinien odbierać wyrażenia do obliczenia na sygnale rxd i wypisywać wyniki (lub oznaczenia błędów) na sygnale txd.

Wejściem do układu są wyrażenia arytmetyczne, zapisane w formacie ASCII. Każde wyrażenie jest zakończone znakiem nowej linii (0x0a). Kalkulator powinien na każdą linię wejścia (niezależnie od tego, czy jest to poprawne wyrażenie czy błąd) odpowiedzieć jedną linią wyjścia.

Wyrażenia mogą zawierać następujące tokeny:

  • liczby, zapisane dziesiętnie

  • binarne operatory dodawania (+), odejmowania (-), mnożenia (*), dzielenia (/), liczenia reszty z dzielenia (%)

  • unarny operator negacji (-)

  • nawiasy ((, ))

Tokeny mogą (ale nie muszą) być oddzielone od siebie białymi znakami (spacjami i/lub znakami tabulacji).

Kalkulator powinien poprawnie implementować kolejność wykonywania działań. Wszystkie wyniki pośrednie powinny być przechowywane jako liczby 32-bitowe bez znaku, a ewentualne przepełnienia zawijane modulo 2**32.

Kalkulator nie musi obsługiwać arbitralnie długich wyrażeń — wymagane jest jedynie, by działał poprawnie na wejściu długości max. 1023 znaków. Dłuższe wyrażenia mogą (ale nie muszą) być odrzucane.

Implementacja logiki kalkulatora napisana w języku Python jest dostępna wyżej. Można ją wykorzystać jako bazę do swojego rozwiązania.

Jako odpowiedź na linię wejścia kalkulator powinien wypisać jedną z następujących rzeczy i znak nowej linii (0x0a):

  • wynik obliczenia (liczbę zapisaną dziesiętnie, bez wiodących zer)

  • napis ERR LEX, jeśli nie udało mu się podzielić linii wejścia na tokeny (nierozpoznany znak itp)

  • napis ERR PARSE, jeśli wyrażenie było niepoprawne składniowo

  • napis ERR DIVIDE, jeśli nastąpiło dzielenie przez zero

  • napis ERR OVERFLOW, jeśli wyrażenie jest zbyt duże bądź zbyt skomplikowane dla kalkulatora (nie powinno wystąpić przy wyrażeniach o długości ≤1023 znaków)

  • napis ERR SERIAL, jeśli wystąpił błąd odbierania danych z portu szeregowego (framing, overflow, itp)

Nie trzeba pisać własnej obsługi portu szeregowego — można wykorzystać tą z laboratorium bądź z biblioteki nmigen-stdio.