Komunikacja zewnętrznego CPU z FPGA Spartan-6 przez magistralę równoległą. Communication between an external CPU and the Spartan-6 FPGA via a parallel bus.
Moduł FPGA jest widziany w przestrzeni adresowej CPU jak zwykła pamięć RAM — CPU używa standardowych instrukcji zapisu i odczytu pamięci, bez potrzeby stosowania dedykowanego interfejsu I/O.
Moduł FPGA jest sterowany przez zewnętrzny CPU (np. mikrokontroler ATmega) poprzez magistralę równoległą. Komunikacja odbywa się przez wydawanie komend z odpowiednimi parametrami — CPU zapisuje numer komendy oraz jej parametry do odpowiednich rejestrów, a FPGA wykonuje operację i udostępnia wynik.
CPU może na dwa sposoby dowiedzieć się o zakończeniu operacji:
INT, a CPU obsługuje je w swojej procedurze przerwania.Oba tryby mogą być używane jednocześnie — CPU może wybrać, które zdarzenia mają generować przerwanie, a pozostałe sprawdzać przez odpytywanie.
The FPGA module is seen in the CPU address space as ordinary RAM — the CPU uses standard memory read/write instructions, without the need for a dedicated I/O interface.
The FPGA module is controlled by an external CPU (e.g. an ATmega microcontroller) via a parallel bus. Communication is performed by issuing commands with appropriate parameters — the CPU writes the command number and its parameters to the appropriate registers, and the FPGA executes the operation and provides the result.
The CPU can learn about the completion of an operation in two ways:
INT line, and the CPU handles it in its interrupt routine.Both modes can be used simultaneously — the CPU can choose which events generate interrupts and poll the rest.
Magistrala łącząca CPU z FPGA jest równoległa i składa się z:
0x00…0x1F), z których wszystkie są wykorzystane. Nie wszystkie adresy są jednak przeznaczone do zapisu — część rejestrów jest tylko do odczytu.Magistrala jest buforowana i dostosowana do poziomów TTL 5 V, dzięki czemu współpracuje bezpośrednio z typowymi mikrokontrolerami 5 V (np. ATmega) bez konieczności stosowania dodatkowych konwerterów poziomów.
Oprócz szyny danych i adresu magistrala zawiera również linie sterujące:
CE_n — wybór układu (chip enable).OE_n — zezwolenie na wyjście (output enable) — aktywne przy odczycie.WE_n — zezwolenie na zapis (write enable) — aktywne przy zapisie.Dodatkowo wyprowadzone są dwie linie specjalne:
INT — linia przerwania, zgłasza zdarzenia do CPU.LOAD — linia informująca o zakończeniu ładowania bitstreamu do FPGA przy uruchomieniu.Obie linie — INT i LOAD — są typu open collector. Oznacza to, że wymagają zewnętrznych rezystorów podciągających. Płytka ma wbudowane rezystory podciągające, które można jednak odłączyć od zasilania przez odpowiednią konfigurację jumperów — co jest przydatne, gdy linie są podciągane po stronie CPU.
The bus connecting the CPU and the FPGA is parallel and consists of:
0x00…0x1F), all of which are used. However, not all addresses are for writing — some registers are read-only.The bus is buffered and adapted to TTL 5 V levels, so it works directly with typical 5 V microcontrollers (e.g. ATmega) without the need for additional level converters.
In addition to the data and address bus, the bus also contains control lines:
CE_n — chip enable.OE_n — output enable — active during read.WE_n — write enable — active during write.Two special lines are also provided:
INT — interrupt line, signals events to the CPU.LOAD — line indicating the completion of bitstream loading into the FPGA at startup.Both lines — INT and LOAD — are open collector. This means they require external pull-up resistors. The board has built-in pull-up resistors, which can be disconnected from the power supply via appropriate jumper configuration — useful when the lines are pulled up on the CPU side.
FPGA jest widziana w przestrzeni adresowej CPU jak zwykła pamięć RAM. Dostępnych jest 32 adresy (5 bitów adresu), z których każdy odpowiada jednemu rejestrowi. Nie wszystkie rejestry są przeznaczone do zapisu — część jest tylko do odczytu, a część dwukierunkowa.
Poniższa tabela przedstawia pełny podział przestrzeni adresowej:
The FPGA is seen in the CPU address space as ordinary RAM. 32 addresses are available (5 address bits), each corresponding to one register. Not all registers are for writing — some are read-only and some are bidirectional.
The following table shows the full address space layout:
| AdresAddress | TypType | PrzeznaczeniePurpose |
|---|---|---|
0x00 | ZapisWrite | Rejestr komendCommand register |
0x01…0x09 | Zapis/odczytRead/Write | Parametry komendCommand parameters |
0x0A | OdczytRead | Odpowiedź karty SDSD card response |
0x0B | Zapis/odczytRead/Write | Konfiguracja UARTUART configuration |
0x0C | Zapis/odczytRead/Write | Dane UARTUART data |
0x0D | OdczytRead | Dane PS/2PS/2 data |
0x0E | Zapis/odczytRead/Write | Sterowanie kursoremCursor control |
0x0F | OdczytRead | Rejestr statusuStatus register |
0x10 | OdczytRead | Flagi przerwańInterrupt flags |
0x11 | ZapisWrite | Kasowanie flag przerwańClear interrupt flags |
0x12 | Zapis/odczytRead/Write | Maska zezwoleń przerwańInterrupt enable mask |
0x13 | Zapis/odczytRead/Write | Preskaler timeraTimer prescaler |
0x14 | Zapis/odczytRead/Write | Licznik timeraTimer counter |
0x15 | Zapis/odczytRead/Write | Konfiguracja timeraTimer configuration |
0x16 | Zapis/odczytRead/Write | Wartość przeładowania timeraTimer reload value |
0x17…0x1C | — | Zarezerwowane (rejestry ALU — opcjonalne, domyślnie wyłączone)Reserved (ALU registers — optional, disabled by default) |
0x1D | OdczytRead | Generator pseudolosowy 8-bit8-bit pseudo-random generator |
0x1E | OdczytRead | Generator pseudolosowy 16-bit (LSB)16-bit pseudo-random generator (LSB) |
0x1F | OdczytRead | Generator pseudolosowy 16-bit (MSB)16-bit pseudo-random generator (MSB) |
Poniżej opisano znaczenie poszczególnych rejestrów w przestrzeni adresowej FPGA.
ram[0]…ram[9])| Rejestr | Przeznaczenie | Typ |
|---|---|---|
ram[0] | Rejestr komend — zapis numeru komendy rozpoczyna jej wykonanie | Zapis |
ram[1]…ram[9] | Parametry komend — znaczenie zależy od konkretnej komendy | Zapis/odczyt |
ram[10]…ram[15])| Rejestr | Przeznaczenie | Typ |
|---|---|---|
ram[10] | Odpowiedź R1 karty SD | Odczyt |
ram[11] | Konfiguracja UART (prędkość, RX/TX enable) | Zapis/odczyt |
ram[12] | Dane UART (zapis = wyślij, odczyt = odbierz) | Zapis/odczyt |
ram[13] | Kod klawisza PS/2 | Odczyt |
ram[14] | Sterowanie kursorem tekstowym | Zapis/odczyt |
ram[15] | Rejestr statusu ogólnego | Odczyt |
ram[16]…ram[18])| Rejestr | Przeznaczenie | Typ |
|---|---|---|
ram[16] | Aktywne flagi przerwań | Odczyt |
ram[17] | Kasowanie flag przerwań (1 = kasuj) | Zapis |
ram[18] | Maska zezwoleń przerwań | Zapis/odczyt |
ram[19]…ram[22])| Rejestr | Przeznaczenie | Typ |
|---|---|---|
ram[19] | Preskaler timera (podział częstotliwości) | Zapis/odczyt |
ram[20] | Licznik timera 8-bit | Zapis/odczyt |
ram[21] | Konfiguracja timera (autoreload, compare) | Zapis/odczyt |
ram[22] | Wartość przeładowania timera | Zapis/odczyt |
Uwaga o timerze: pełna obsługa timera jest zaimplementowana tylko częściowo — działa wyłącznie tryb przepełnienia (overflow). Tryb porównania (compare match) NIE JEST zaimplementowany: flaga IRQ_TIMER8_CMP i bit TIMCFG_COMPARE istnieją w definicjach, ale nie są nigdzie używane w kodzie Verilog.
ram[23]…ram[28])Adresy ram[23]…ram[28] są zarezerwowane na przyszłe zastosowania. W domyślnej kompilacji są to rejestry ALU (REG_ALU_*) — opcjonalne i zależne od definicji USE_ALU16 w pliku config.vh. W obecnej wersji ALU jest wyłączone — odczyt z tych adresów zwraca 0x00, a bit 7 w REG_INT_FLAGS i REG_INT_ENABLE (IRQ_ALU) jest nieużywany.
ram[29]…ram[31])| Rejestr | Przeznaczenie | Typ |
|---|---|---|
ram[29] | Generator 8-bit LFSR | Odczyt |
ram[30] | Generator 16-bit LFSR (LSB) | Odczyt |
ram[31] | Generator 16-bit LFSR (MSB) | Odczyt |
The meaning of each register in the FPGA address space is described below.
ram[0]…ram[9])| Register | Purpose | Type |
|---|---|---|
ram[0] | Command register — writing the command number starts its execution | Write |
ram[1]…ram[9] | Command parameters — meaning depends on the specific command | Read/Write |
ram[10]…ram[15])| Register | Purpose | Type |
|---|---|---|
ram[10] | SD card R1 response | Read |
ram[11] | UART configuration (baud rate, RX/TX enable) | Read/Write |
ram[12] | UART data (write = send, read = receive) | Read/Write |
ram[13] | PS/2 key code | Read |
ram[14] | Text cursor control | Read/Write |
ram[15] | General status register | Read |
ram[16]…ram[18])| Register | Purpose | Type |
|---|---|---|
ram[16] | Active interrupt flags | Read |
ram[17] | Clear interrupt flags (1 = clear) | Write |
ram[18] | Interrupt enable mask | Read/Write |
ram[19]…ram[22])| Register | Purpose | Type |
|---|---|---|
ram[19] | Timer prescaler (frequency divider) | Read/Write |
ram[20] | 8-bit timer counter | Read/Write |
ram[21] | Timer configuration (autoreload, compare) | Read/Write |
ram[22] | Timer reload value | Read/Write |
Note about the timer: full timer support is implemented only partially — only the overflow mode works. The compare match mode is NOT implemented: the IRQ_TIMER8_CMP flag and the TIMCFG_COMPARE bit exist in the definitions but are not used anywhere in the Verilog code.
ram[23]…ram[28])Addresses ram[23]…ram[28] are reserved for future use. In the default build, these are the ALU registers (REG_ALU_*) — optional and dependent on the USE_ALU16 definition in the config.vh file. In the current version, the ALU is disabled — reading from these addresses returns 0x00, and bit 7 in REG_INT_FLAGS and REG_INT_ENABLE (IRQ_ALU) is unused.
ram[29]…ram[31])| Register | Purpose | Type |
|---|---|---|
ram[29] | 8-bit LFSR generator | Read |
ram[30] | 16-bit LFSR generator (LSB) | Read |
ram[31] | 16-bit LFSR generator (MSB) | Read |
Poza rejestrami interfejsu komend FPGA posiada szereg rejestrów wewnętrznych, które konfigurują pracę poszczególnych podsystemów i działają w tle — bez potrzeby wydawania komend.
Niektóre z tych mechanizmów działają autonomicznie od momentu resetu FPGA (np. licznik czasu UNIX, generatory pseudolosowe), inne wymagają wcześniejszej konfiguracji przez odpowiedni rejestr (np. UART wymaga włączenia RX w REG_CFG).
Kliknij wiersz, aby zobaczyć szczegóły rejestru.
In addition to the command interface registers, the FPGA has a number of internal registers that configure the operation of individual subsystems and work in the background — without the need to issue commands.
Some of these mechanisms operate autonomously from the moment the FPGA is reset (e.g. the UNIX time counter, pseudo-random generators), while others require prior configuration through the appropriate register (e.g. UART requires enabling RX in REG_CFG).
Click a row to see register details.
| RejestrRegister | AdresAddress | Wymaga konfiguracji?Requires configuration? |
|---|---|---|
| Rejestr komendCommand register | ram[0] | NieNo |
| Pozycja X kursoraCursor X position | ram[1] | NieNo |
| Pozycja Y kursoraCursor Y position | ram[2] | NieNo |
| Kod ASCII znakuCharacter ASCII code | ram[3] | NieNo |
| Atrybut znakuCharacter attribute | ram[4] | NieNo |
| Parametr 1 komendyCommand parameter 1 | ram[5] | NieNo |
| Parametr 2 komendyCommand parameter 2 | ram[6] | NieNo |
| Parametr 3 komendyCommand parameter 3 | ram[7] | NieNo |
| Parametr 4 komendyCommand parameter 4 | ram[8] | NieNo |
| Parametr 5 komendyCommand parameter 5 | ram[9] | NieNo |
| Odpowiedź karty SDSD card response | ram[10] | NieNo |
| Konfiguracja UARTUART configuration | ram[11] | NieNo |
| Dane UARTUART data | ram[12] | Tak — REG_CFGYes — REG_CFG |
| Dane PS/2PS/2 data | ram[13] | NieNo |
| Sterowanie kursoremCursor control | ram[14] | NieNo |
| Rejestr statusuStatus register | ram[15] | NieNo |
| Flagi przerwańInterrupt flags | ram[16] | NieNo |
| Kasowanie flag przerwańClear interrupt flags | ram[17] | NieNo |
| Maska zezwoleń przerwańInterrupt enable mask | ram[18] | NieNo |
| Preskaler timeraTimer prescaler | ram[19] | NieNo |
| Licznik timeraTimer counter | ram[20] | NieNo |
| Konfiguracja timeraTimer configuration | ram[21] | NieNo |
| Wartość przeładowania timeraTimer reload value | ram[22] | NieNo |
| Rejestry ALU (opcjonalne, wyłączone)ALU registers (optional, disabled) | ram[23]…ram[28] | Tak — USE_ALU16Yes — USE_ALU16 |
| Generator pseudolosowy 8-bit8-bit pseudo-random generator | ram[29] | NieNo |
| Generator pseudolosowy 16-bit16-bit pseudo-random generator | ram[30], ram[31] | NieNo |
| MechanizmMechanism | Rejestr / sygnałRegister / signal | Wymaga konfiguracji?Requires configuration? |
|---|---|---|
| Licznik czasu UNIXUNIX time counter | unix_time | NieNo |
| Timer 8-bit z preskalerem8-bit timer with prescaler | ram[19]…ram[22] | Tak — REG_DIV ≠ DIV_OFFYes — REG_DIV ≠ DIV_OFF |
| Timer 8-bit — pełny opis (overflow + compare)8-bit timer — full description (overflow + compare) | ram[19]…ram[22] | Tak — REG_DIV ≠ DIV_OFFYes — REG_DIV ≠ DIV_OFF |
| Synchronizacja RTS + IRQ_RTSRTS synchronization + IRQ_RTS | RTS | NieNo |
| Odbiór znaku PS/2PS/2 character reception | ram[13] | NieNo |
| Odbiór UARTUART reception | ram[12] | Tak — REG_CFG[0] = 1Yes — REG_CFG[0] = 1 |
| Przerwania UART (RX/TX)UART interrupts (RX/TX) | ram[16], ram[18] | Tak — REG_CFG + REG_INT_ENABLEYes — REG_CFG + REG_INT_ENABLE |
| Bufor magistrali (ENABLE/DIR)Bus buffer (ENABLE/DIR) | CE_n, WE_n, OE_n | NieNo |
| Latch adresu/danych magistraliBus address/data latch | addr_latch, data_latch | NieNo |
| Linia przerwania INTINT interrupt line | INT | NieNo |
| Blankowanie framebufferFramebuffer blanking | fb_blank | NieNo |
| Port debug (LED)Debug port (LED) | debug_port | NieNo |
| Zasilanie karty SDSD card power | SD_VCC | NieNo |
| Wyjścia sprite'ówSprite outputs | spr_x_*, spr_y_*, spr_ctrl_* | NieNo |
| Kursor tekstowyText cursor | cursor_ctrl, cursor_index | NieNo |
Kliknij wybrany wiersz, aby zobaczyć szczegóły rejestru.
Click a row to see register details.
Komendy wydaje się przez zapis parametrów do rejestrów ram[1]…ram[9], a następnie zapis numeru komendy do rejestru ram[0]. Zapis do ram[0] rozpoczyna wykonanie komendy.
Kliknij wiersz, aby zobaczyć szczegóły komendy.
Commands are issued by writing parameters to registers ram[1]…ram[9], and then writing the command number to register ram[0]. Writing to ram[0] starts command execution.
Click a row to see command details.
| NrNo. | NazwaName | OpisDescription |
|---|---|---|
| 0 | CMD_NONE | Brak komendyNo command |
| 1 | CMD_CLEAR_FRAMEBUFFER | Czyszczenie bufora graficznegoClear framebuffer |
| 2 | CMD_WRITE_PIXEL | Zapis pojedynczego pikselaWrite single pixel |
| 3 | CMD_DRAW_RECT | Rysowanie wypełnionego prostokątaDraw filled rectangle |
| 4 | CMD_DRAW_FRAME | Rysowanie ramki prostokątaDraw rectangle frame |
| 5 | CMD_WRITE_TEXT | Zapis znaku w określonym miejscuWrite character at specified position |
| 6 | CMD_CLEAR_TEXT | Czyszczenie ekranu tekstowegoClear text screen |
| 7 | CMD_PUT_TEXT | Dopisanie znaku w bieżącej pozycji kursoraAppend character at current cursor position |
| 8 | CMD_SET_CURSOR | Ustawienie położenia kursora tekstowegoSet text cursor position |
| 9 | CMD_SET_PIXEL | Ustawienie bieżącej pozycji kursora graficznegoSet current graphics cursor position |
| 10 | CMD_PUT_PIXEL | Zapis piksela na aktualnej pozycji graficznejWrite pixel at current graphics position |
| 11 | CMD_UART_ON | Włączenie UARTEnable UART |
| 12 | CMD_UART_OFF | Wyłączenie UARTDisable UART |
| 13 | CMD_SET_UNIX | Ustawienie czasu UNIXSet UNIX time |
| 14 | CMD_GET_UNIX | Pobranie czasu UNIXGet UNIX time |
| 15 | CMD_SET_DEBUG | Wystawienie danej na debug_portOutput data to debug_port |
| 16 | CMD_GET_DEBUG | Pobranie danej z debug_portRead data from debug_port |
| 17 | CMD_GET_RTS | Pobranie stanu pinu RTSGet RTS pin state |
| 18 | CMD_RESET_FPGA | Reset FPGAReset FPGA |
| 19 | CMD_GET_CURSOR | Pobranie położenia kursora tekstowegoGet text cursor position |
| 20 | CMD_PUT_TEXT_REPEAT_OFF | Kasowanie flagi repetycji znakuClear character repeat flag |
| 21 | CMD_FB_BLANK_OFF | Zablokowanie framebuffer (czarny)Block framebuffer (black) |
| 22 | CMD_FB_BLANK_ON | Odblokowanie framebufferUnblock framebuffer |
| NrNo. | NazwaName | OpisDescription |
|---|---|---|
| 40 | CMD_SD_SEND | Wysłanie komendy do karty SDSend command to SD card |
| 41 | CMD_SD_READ_STATUS | Odczyt statusu karty SDRead SD card status |
| 42 | CMD_SD_READ_DATA | Odczyt danych z karty SDRead data from SD card |
| 43 | CMD_SD_WRITE_DATA | Zapis danych do karty SDWrite data to SD card |
| 44 | CMD_SD_VCC_ON | Włączenie zasilania karty SDEnable SD card power |
| 45 | CMD_SD_VCC_OFF | Wyłączenie zasilania karty SDDisable SD card power |
| NrNo. | NazwaName | OpisDescription |
|---|---|---|
| 50 | CMD_SPRITE_LOAD_BEGIN | Rozpoczęcie ładowania spriteBegin sprite loading |
| 51 | CMD_SPRITE_LOAD_DATA | Ładowanie danych spriteLoad sprite data |
| 52 | CMD_SPRITE_LOAD_END | Zakończenie ładowania spriteEnd sprite loading |
| 53 | CMD_SPRITE_POS | Ustawienie pozycji spriteSet sprite position |
| 54 | CMD_SPRITE_ENABLE | Włączenie/wyłączenie spriteEnable/disable sprite |
| 55 | CMD_SPRITE_CLEAR | Wyłączenie wszystkich sprite'ówDisable all sprites |
| NrNo. | NazwaName | OpisDescription |
|---|---|---|
| 23 | CMD_DRAW_LINE | Rysowanie linii (Bresenham, grubość, dash)Draw line (Bresenham, thickness, dash) |
| 32 | CMD_SET_BUF_LB | Przełączenie na dolny bufor graficzny (LB)Switch to lower graphics buffer (LB) |
| 33 | CMD_SET_BUF_UB | Przełączenie na górny bufor graficzny (UB)Switch to upper graphics buffer (UB) |
Kliknij wybrany wiersz, aby zobaczyć szczegóły komendy.
Click a row to see command details.
Rejestr statusu ram[15] (REG_STATUS_VGA) to rejestr tylko do odczytu, w którym FPGA na bieżąco aktualizuje stan poszczególnych podsystemów. CPU może go odpytywać (polling) w celu sprawdzenia, czy dana operacja się zakończyła, czy dane są gotowe, albo czy wystąpił błąd.
| Bit | Nazwa | Opis |
|---|---|---|
| 0 | STATUS_VGA | 0 = VGA idle, 1 = VGA busy — trwa operacja graficzna |
| 1 | STATUS_PS2 | 1 = nowy kod klawisza PS/2 gotowy do odczytu |
| 2 | STATUS_UART_RXD | 1 = odebrano dane UART |
| 3 | STATUS_UART_TXD | 1 = trwa transmisja UART |
| 4 | STATUS_SD | 1 = operacja na karcie SD zakończona |
| 5 | STATUS_SD_PRESENT | 1 = karta SD włożona do gniazda |
| 6 | STATUS_SD_POWER | 1 = zasilanie karty SD włączone |
| 7 | — | zarezerwowany pod przyszłe zastosowania |
CPU może odpytywać rejestr statusu w pętli, aż wybrany bit zmieni stan. Przykład — oczekiwanie na zakończenie operacji graficznej:
while (fpga_read(15) & 0x01) {
/* czekaj, aż STATUS_VGA = 0 */
}
The status register ram[15] (REG_STATUS_VGA) is a read-only register in which the FPGA continuously updates the state of individual subsystems. The CPU can poll it to check whether a given operation has completed, whether data is ready, or whether an error has occurred.
| Bit | Name | Description |
|---|---|---|
| 0 | STATUS_VGA | 0 = VGA idle, 1 = VGA busy — graphics operation in progress |
| 1 | STATUS_PS2 | 1 = new PS/2 key code ready to read |
| 2 | STATUS_UART_RXD | 1 = UART data received |
| 3 | STATUS_UART_TXD | 1 = UART transmission in progress |
| 4 | STATUS_SD | 1 = SD card operation completed |
| 5 | STATUS_SD_PRESENT | 1 = SD card inserted in slot |
| 6 | STATUS_SD_POWER | 1 = SD card power enabled |
| 7 | — | reserved for future use |
The CPU can poll the status register in a loop until the selected bit changes state. Example — waiting for a graphics operation to complete:
while (fpga_read(15) & 0x01) {
/* wait until STATUS_VGA = 0 */
}
FPGA ma osobną linię przerwania INT, która jest typu open collector i jest wyzwalana poziomem niskim (active-low). Zgłasza ona CPU wystąpienie zdarzenia — zamiast odpytywania statusu, CPU może zostać powiadomiony natychmiast. Przerwania są konfigurowalne: każde źródło można indywidualnie włączyć lub zamaskować.
Ważne: linia INT pozostaje aktywna (w stanie niskim) do momentu skasowania wszystkich źródeł przerwania. Jeśli kilka źródeł zgłosi przerwanie jednocześnie, CPU musi skasować wszystkie aktywne flagi — dopiero wtedy INT zostanie zwolniona (przejdzie w stan wysoki).
| Rejestr | Nazwa | Typ | Przeznaczenie |
|---|---|---|---|
ram[16] | REG_INT_FLAGS | Odczyt | Aktywne flagi przerwań |
ram[17] | REG_CLEAR_FLAGS | Zapis | Kasowanie flag (zapis 1 = kasuj) |
ram[18] | REG_INT_ENABLE | Zapis/odczyt | Maska zezwoleń przerwań |
ram[16] i ram[18])| Bit | Nazwa | Opis |
|---|---|---|
| 0 | IRQ_TIMER8_OVF | Przepełnienie timera 8-bit |
| 1 | IRQ_TIMER8_CMP | Zgodność timera (compare match) — NIEZIMPLEMENTOWANE |
| 2 | IRQ_PS2 | Nowy kod klawisza PS/2 |
| 3 | IRQ_UART_RX | Odebrano dane UART |
| 4 | IRQ_UART_TX | Zakończono transmisję UART |
| 5 | IRQ_CMD_DONE | Zakończono długą komendę |
| 6 | IRQ_RTS | Zmiana stanu linii RTS |
| 7 | — | zarezerwowany pod przyszłe zastosowania (ALU — opcjonalne, wyłączone) |
Uwaga o timerze: pełna obsługa timera jest zaimplementowana tylko częściowo — działa wyłącznie tryb przepełnienia (overflow). Flaga IRQ_TIMER8_CMP i bit TIMCFG_COMPARE istnieją w definicjach, ale nie są nigdzie używane w kodzie Verilog.
Linia INT jest aktywna (stan niski), gdy którakolwiek flaga w ram[16] jest ustawiona i odpowiadający jej bit w ram[18] jest włączony:
assign INT = |(irq_flags & enable_irq);
Linia INT pozostaje w stanie niskim tak długo, jak długo w ram[16] jest ustawiona jakakolwiek flaga (przy włączonym odpowiadającym jej zezwoleniu w ram[18]). Dopiero skasowanie wszystkich aktywnych flag zwalnia linię INT — przechodzi ona w stan wysoki.
Dzięki temu, że linia jest wyzwalana poziomem niskim, CPU może obsłużyć przerwanie w dowolnym momencie — nie musi reagować natychmiast, bo sygnał nie zniknie, dopóki flaga nie zostanie skasowana.
ram[18] (1 = włączone).INT w stan niski, gdy wystąpi zdarzenie.ram[16], żeby sprawdzić, które źródła zadziałały.1 na wszystkich aktywnych bitach w ram[17] — to skasuje flagi w ram[16] i zwolni linię INT (przejdzie w stan wysoki).Uwaga: jeśli CPU skasuje tylko część flag, linia INT pozostanie w stanie niskim — bo w ram[16] wciąż są ustawione pozostałe flagi. Dopiero skasowanie wszystkich aktywnych flag zwalnia INT.
/* Włącz przerwania od PS/2 (bit 2) i UART RX (bit 3) */
fpga_write(18, 0x0C);
/* W procedurze przerwania */
void INT_ISR(void) {
uint8_t flags = fpga_read(16);
uint8_t to_clear = 0;
if (flags & 0x04) {
/* Obsłuż klawisz PS/2 */
uint8_t key = fpga_read(13);
/* ... */
to_clear |= 0x04;
}
if (flags & 0x08) {
/* Obsłuż dane UART */
uint8_t data = fpga_read(12);
/* ... */
to_clear |= 0x08;
}
/* Skasuj wszystkie obsłużone flagi naraz */
fpga_write(17, to_clear);
}
Flaga IRQ_CMD_DONE (bit 5) jest ustawiana po zakończeniu długich operacji: CMD_DRAW_RECT, CMD_DRAW_FRAME, CMD_CLEAR_FRAMEBUFFER, CMD_CLEAR_TEXT. Dzięki temu CPU nie musi odpytywać statusu — dostaje przerwanie, gdy operacja się zakończy.
Aby skasować flagę, zapisz 1 na odpowiednim bicie w ram[17]:
fpga_write(17, 0x04); /* skasuj flagę IRQ_PS2 */
Zapis 0 nie zmienia stanu flagi.
Aby zwolnić linię INT, trzeba skasować wszystkie aktywne flagi. Można to zrobić jednym zapisem — sumując bity wszystkich flag, które chcemy skasować:
fpga_write(17, 0x04 | 0x08); /* skasuj IRQ_PS2 i IRQ_UART_RX naraz */
The FPGA has a dedicated interrupt line INT, which is open collector and active-low. It signals the CPU that an event has occurred — instead of polling the status, the CPU can be notified immediately. Interrupts are configurable: each source can be individually enabled or masked.
Important: the INT line remains active (low) until all interrupt sources are cleared. If several sources trigger an interrupt simultaneously, the CPU must clear all active flags — only then will INT be released (go high).
| Register | Name | Type | Purpose |
|---|---|---|---|
ram[16] | REG_INT_FLAGS | Read | Active interrupt flags |
ram[17] | REG_CLEAR_FLAGS | Write | Clear flags (write 1 = clear) |
ram[18] | REG_INT_ENABLE | Read/Write | Interrupt enable mask |
ram[16] and ram[18])| Bit | Name | Description |
|---|---|---|
| 0 | IRQ_TIMER8_OVF | 8-bit timer overflow |
| 1 | IRQ_TIMER8_CMP | Timer compare match — NOT IMPLEMENTED |
| 2 | IRQ_PS2 | New PS/2 key code |
| 3 | IRQ_UART_RX | UART data received |
| 4 | IRQ_UART_TX | UART transmission completed |
| 5 | IRQ_CMD_DONE | Long command completed |
| 6 | IRQ_RTS | RTS line state change |
| 7 | — | reserved for future use (ALU — optional, disabled) |
Note about the timer: full timer support is implemented only partially — only the overflow mode works. The IRQ_TIMER8_CMP flag and the TIMCFG_COMPARE bit exist in the definitions but are not used anywhere in the Verilog code.
The INT line is active (low) when any flag in ram[16] is set and the corresponding bit in ram[18] is enabled:
assign INT = |(irq_flags & enable_irq);
The INT line remains low as long as any flag in ram[16] is set (with the corresponding enable in ram[18]). Only clearing all active flags releases INT — it goes high.
Because the line is active-low, the CPU can handle the interrupt at any time — it does not need to react immediately, as the signal will not disappear until the flag is cleared.
ram[18] (1 = enabled).INT line low when an event occurs.ram[16] to see which sources have triggered.1 to all active bits in ram[17] — this clears the flags in ram[16] and releases the INT line (goes high).Note: if the CPU clears only some of the flags, the INT line will remain low — because the remaining flags are still set in ram[16]. Only clearing all active flags releases INT.
/* Enable PS/2 (bit 2) and UART RX (bit 3) interrupts */
fpga_write(18, 0x0C);
/* In the interrupt routine */
void INT_ISR(void) {
uint8_t flags = fpga_read(16);
uint8_t to_clear = 0;
if (flags & 0x04) {
/* Handle PS/2 key */
uint8_t key = fpga_read(13);
/* ... */
to_clear |= 0x04;
}
if (flags & 0x08) {
/* Handle UART data */
uint8_t data = fpga_read(12);
/* ... */
to_clear |= 0x08;
}
/* Clear all handled flags at once */
fpga_write(17, to_clear);
}
The IRQ_CMD_DONE flag (bit 5) is set after the completion of long operations: CMD_DRAW_RECT, CMD_DRAW_FRAME, CMD_CLEAR_FRAMEBUFFER, CMD_CLEAR_TEXT. This way the CPU does not need to poll the status — it receives an interrupt when the operation completes.
To clear a flag, write 1 to the appropriate bit in ram[17]:
fpga_write(17, 0x04); /* clear IRQ_PS2 flag */
Writing 0 does not change the flag state.
To release the INT line, all active flags must be cleared. This can be done with a single write — summing the bits of all flags to clear:
fpga_write(17, 0x04 | 0x08); /* clear IRQ_PS2 and IRQ_UART_RX at once */
Linia LOAD informuje CPU o zakończeniu ładowania bitstreamu do FPGA. Jest typu open collector i jest wyzwalana poziomem niskim (active-low). Dzięki niej CPU może poczekać, aż FPGA będzie gotowe do pracy, zanim rozpocznie komunikację.
Po włączeniu zasilania FPGA ładuje bitstream z pamięci konfiguracji (SPI Flash). W tym czasie linia LOAD pozostaje w stanie wysokim — CPU czeka. Gdy ładowanie się zakończy, FPGA wypuszcza linię LOAD w stan niski — CPU przechodzi do dalszej części kodu.
| Stan LOAD | Znaczenie |
|---|---|
| 1 (wysoki) | Trwa ładowanie bitstreamu — CPU czeka |
| 0 (niski) | Bitstream załadowany — FPGA gotowe, CPU kontynuuje |
/* Oczekiwanie na zakończenie ładowania bitstreamu */
while ((PING & (1 << PG5)) != 0) {
/* LOAD = 1 — czekaj */
_delay_ms(10);
}
/* LOAD = 0 — FPGA gotowe, można kontynuować */
Linia LOAD — podobnie jak INT — jest typu open collector. Wymaga rezystora podciągającego. Płytka ma wbudowane rezystory podciągające, które można odłączyć przez odpowiednią konfigurację jumperów.
The LOAD line informs the CPU about the completion of bitstream loading into the FPGA. It is open collector and active-low. It allows the CPU to wait until the FPGA is ready before starting communication.
After power-up, the FPGA loads the bitstream from the configuration memory (SPI Flash). During this time, the LOAD line remains high — the CPU waits. When loading completes, the FPGA pulls the LOAD line low — the CPU continues.
| LOAD state | Meaning |
|---|---|
| 1 (high) | Bitstream loading in progress — CPU waits |
| 0 (low) | Bitstream loaded — FPGA ready, CPU continues |
/* Wait for bitstream loading to complete */
while ((PING & (1 << PG5)) != 0) {
/* LOAD = 1 — wait */
_delay_ms(10);
}
/* LOAD = 0 — FPGA ready, can continue */
The LOAD line — like INT — is open collector. It requires a pull-up resistor. The board has built-in pull-up resistors that can be disconnected via appropriate jumper configuration.
Linie INT i LOAD są typu open collector. Oznacza to, że nie mogą same wyprowadzić stanu wysokiego — wymagają zewnętrznego rezystora podciągającego, który ustali stan wysoki, gdy żadne urządzenie nie ściąga linii do masy.
Płytka FPGA ma wbudowane rezystory podciągające o wartości 4.7 kΩ, podłączone do zasilania 5 V. Napięcie to jest pobierane z układu CPU, z którym FPGA współpracuje (np. z płytki ATmegi) — dzięki temu poziomy napięć na liniach INT i LOAD są zgodne z poziomami CPU.
Każda linia ma swój własny jumper SMD:
| Linia | Jumper | Stan domyślny |
|---|---|---|
INT | SJ3 | zwarta |
LOAD | SJ5 | zwarta |
Fabrycznie zworki są zwarte, czyli pull-upy są podłączone. Aby odłączyć pull-up danej linii, należy rozciąć odpowiednią zworkę SMD (np. przecinając ścieżkę ostrym narzędziem).
Odłączenie pull-upów jest przydatne, gdy:
Odłączenie pull-upów jest trwałe — zworki SMD są jednorazowe. Jeśli chcesz je później przywrócić, trzeba będzie wlutować rezystor 0Ω albo połączyć pady kroplą cyny.
Na płytce znajduje się więcej zworek SMD — zostaną one opisane w przyszłości.
The INT and LOAD lines are open collector. This means they cannot drive a high state themselves — they require an external pull-up resistor that establishes the high state when no device is pulling the line to ground.
The FPGA board has built-in pull-up resistors of 4.7 kΩ, connected to 5 V. This voltage is drawn from the CPU with which the FPGA works (e.g. from the ATmega board) — so the voltage levels on the INT and LOAD lines match the CPU levels.
Each line has its own SMD jumper:
| Line | Jumper | Default state |
|---|---|---|
INT | SJ3 | closed |
LOAD | SJ5 | closed |
By default, the jumpers are closed, meaning the pull-ups are connected. To disconnect a pull-up, you must cut the appropriate SMD jumper (e.g. by cutting the trace with a sharp tool).
Disconnecting pull-ups is useful when:
Disconnecting pull-ups is permanent — SMD jumpers are one-time. If you want to restore them later, you will need to solder a 0Ω resistor or bridge the pads with a drop of solder.
There are more SMD jumpers on the board — they will be described in the future.
Kompletny kod demo dla mikrokontrolera ATmega2560 jest dostępny do pobrania. Kod jest napisany w czystym C/AVR (nie jest to sketch Arduino) i kompilowany w środowisku Microchip Studio (dawniej Atmel Studio) z użyciem kompilatora avr-gcc.
Wersja kodu demo: 1.0
ATmega2560 jest programowany przez interfejs JTAG — na płytce FPGA wyprowadzone jest dedykowane złącze JTAG dla ATmela. Programowanie odbywa się przez zewnętrzny programator (np. Atmel-ICE, AVR JTAGICE mkII, JTAGICE3), a nie przez bootloader Arduino ani USB.
| Element | Wartość |
|---|---|
| Środowisko | Microchip Studio (dawniej Atmel Studio) |
| Kompilator | avr-gcc |
| Mikrokontroler | ATmega2560 |
| Programowanie | JTAG (zewnętrzny programator) |
| Złącze JTAG | wyprowadzone na płytce FPGA |
| Wersja kodu demo | 1.0 |
Uwaga: piny FPGA są ściśle przyporządkowane do konkretnych pinów ATmega2560 — nie ma możliwości dowolnego wyboru pinów. Wynika to z implementacji magistrali w bibliotece FPGA.cpp, która używa bezpośrednio rejestrów portów ATmega. Poniższe tabele przedstawiają pełne mapowanie.
| Pin ATmega | Port | Funkcja |
|---|---|---|
| PD0 | PORTD bit 0 | A0 — najmłodszy bit adresu |
| PD1 | PORTD bit 1 | A1 — bit adresu |
| PD2 | PORTD bit 2 | A2 — bit adresu |
| PD3 | PORTD bit 3 | A3 — bit adresu |
| PH0 | PORTH bit 0 | A4 — najstarszy bit adresu |
| Pin ATmega | Port | Funkcja |
|---|---|---|
| PK0 | PORTK bit 0 | D0 — najmłodszy bit danych |
| PK1 | PORTK bit 1 | D1 — bit danych |
| PK2 | PORTK bit 2 | D2 — bit danych |
| PK3 | PORTK bit 3 | D3 — bit danych |
| PK4 | PORTK bit 4 | D4 — bit danych |
| PK5 | PORTK bit 5 | D5 — bit danych |
| PK6 | PORTK bit 6 | D6 — bit danych |
| PK7 | PORTK bit 7 | D7 — najstarszy bit danych |
| Pin ATmega | Port | Funkcja |
|---|---|---|
| PH1 | PORTH bit 1 | CE_n — Chip Enable (aktywny 0) |
| PJ0 | PORTJ bit 0 | WE_n — Write Enable (aktywny 0) |
| PJ1 | PORTJ bit 1 | OE_n — Output Enable (aktywny 0) |
| Pin ATmega | Port | Funkcja |
|---|---|---|
| PE4 | PORTE bit 4 | INT4 — linia przerwania od FPGA (aktywna 0, open collector). Rezystor podciągający znajduje się na płytce FPGA (zworka SJ3, domyślnie zwarta). |
| PG5 | PORTG bit 5 | LOAD — linia gotowości FPGA (aktywna 0, open collector). Rezystor podciągający znajduje się na płytce FPGA (zworka SJ5, domyślnie zwarta). |
| Pin ATmega | Port | Funkcja |
|---|---|---|
| RXD | — | Odbiór danych z FPGA (UART) |
| TXD | — | Wysyłanie danych do FPGA (UART) |
| RTS | — | Request To Send — wejście z FPGA |
| Pin ATmega | Port | Funkcja |
|---|---|---|
| PS2_CLK | — | Linia zegara klawiatury PS/2 |
| PS2_DATA | — | Linia danych klawiatury PS/2 |
FPGA.cpp (operacje na rejestrach PORTD, PORTH, PORTJ, PORTK, PORTE, PORTG). Zmiana pinów wymagałaby modyfikacji kodu biblioteki.Kod źródłowy jest podzielony na pliki:
main.cpp — menu główne i obsługa demogame.cpp — logika gry Pac-ManFPGA.cpp, FPGA.h — biblioteka komunikacji z FPGAcolor.h, Cursor.h, SDcard.h — definicje pomocniczedef_screen.h — stałe geometryczne ekranuThe complete demo code for the ATmega2560 microcontroller is available for download. The code is written in pure C/AVR (not an Arduino sketch) and compiled in Microchip Studio (formerly Atmel Studio) with the avr-gcc compiler.
Demo code version: 1.0
The ATmega2560 is programmed via the JTAG interface — a dedicated JTAG connector for Atmel is provided on the FPGA board. Programming is done with an external programmer (e.g. Atmel-ICE, AVR JTAGICE mkII, JTAGICE3), not through the Arduino bootloader or USB.
| Element | Value |
|---|---|
| Environment | Microchip Studio (formerly Atmel Studio) |
| Compiler | avr-gcc |
| Microcontroller | ATmega2560 |
| Programming | JTAG (external programmer) |
| JTAG connector | provided on the FPGA board |
| Demo code version | 1.0 |
Note: the FPGA pins are strictly assigned to specific ATmega2560 pins — there is no free choice of pins. This results from the bus implementation in the FPGA.cpp library, which uses ATmega port registers directly. The tables below show the complete mapping.
| ATmega pin | Port | Function |
|---|---|---|
| PD0 | PORTD bit 0 | A0 — least significant address bit |
| PD1 | PORTD bit 1 | A1 — address bit |
| PD2 | PORTD bit 2 | A2 — address bit |
| PD3 | PORTD bit 3 | A3 — address bit |
| PH0 | PORTH bit 0 | A4 — most significant address bit |
| ATmega pin | Port | Function |
|---|---|---|
| PK0 | PORTK bit 0 | D0 — least significant data bit |
| PK1 | PORTK bit 1 | D1 — data bit |
| PK2 | PORTK bit 2 | D2 — data bit |
| PK3 | PORTK bit 3 | D3 — data bit |
| PK4 | PORTK bit 4 | D4 — data bit |
| PK5 | PORTK bit 5 | D5 — data bit |
| PK6 | PORTK bit 6 | D6 — data bit |
| PK7 | PORTK bit 7 | D7 — most significant data bit |
| ATmega pin | Port | Function |
|---|---|---|
| PH1 | PORTH bit 1 | CE_n — Chip Enable (active low) |
| PJ0 | PORTJ bit 0 | WE_n — Write Enable (active low) |
| PJ1 | PORTJ bit 1 | OE_n — Output Enable (active low) |
| ATmega pin | Port | Function |
|---|---|---|
| PE4 | PORTE bit 4 | INT4 — interrupt line from FPGA (active low, open collector). The pull-up resistor is on the FPGA board (jumper SJ3, closed by default). |
| PG5 | PORTG bit 5 | LOAD — FPGA ready line (active low, open collector). The pull-up resistor is on the FPGA board (jumper SJ5, closed by default). |
| ATmega pin | Port | Function |
|---|---|---|
| RXD | — | Receive data from FPGA (UART) |
| TXD | — | Transmit data to FPGA (UART) |
| RTS | — | Request To Send — input from FPGA |
| ATmega pin | Port | Function |
|---|---|---|
| PS2_CLK | — | PS/2 keyboard clock line |
| PS2_DATA | — | PS/2 keyboard data line |
FPGA.cpp (operations on PORTD, PORTH, PORTJ, PORTK, PORTE, PORTG registers). Changing pins would require modifying the library code.The source code is divided into files:
main.cpp — main menu and demo handlinggame.cpp — Pac-Man game logicFPGA.cpp, FPGA.h — FPGA communication librarycolor.h, Cursor.h, SDcard.h — helper definitionsdef_screen.h — screen geometry constantsPełny kod projektu jest dostępny w repozytorium GitHub:
The complete project code is available in the GitHub repository: