Sterowanie CPUCPU Control

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.

Parallel Bus 8-bit Data 5-bit Address TTL 5V

WprowadzenieIntroduction

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:

  • Odpytywanie statusu (polling) — CPU cyklicznie odczytuje rejestr statusu i sprawdza, czy operacja się zakończyła.
  • Przerwania — FPGA zgłasza zdarzenie linią 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:

  • Status polling — the CPU periodically reads the status register and checks whether the operation has completed.
  • Interrupts — the FPGA signals an event via the 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.

MagistralaBus

Magistrala łącząca CPU z FPGA jest równoległa i składa się z:

  • 8 bitów danych — szyna dwukierunkowa, używana zarówno do zapisu, jak i odczytu.
  • 5 bitów adresu — co daje 32 adresy (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:

  • 8 data bits — bidirectional bus used for both write and read.
  • 5 address bits — giving 32 addresses (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.

Mapa pamięciMemory Map

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
0x00ZapisWriteRejestr komendCommand register
0x01…0x09Zapis/odczytRead/WriteParametry komendCommand parameters
0x0AOdczytReadOdpowiedź karty SDSD card response
0x0BZapis/odczytRead/WriteKonfiguracja UARTUART configuration
0x0CZapis/odczytRead/WriteDane UARTUART data
0x0DOdczytReadDane PS/2PS/2 data
0x0EZapis/odczytRead/WriteSterowanie kursoremCursor control
0x0FOdczytReadRejestr statusuStatus register
0x10OdczytReadFlagi przerwańInterrupt flags
0x11ZapisWriteKasowanie flag przerwańClear interrupt flags
0x12Zapis/odczytRead/WriteMaska zezwoleń przerwańInterrupt enable mask
0x13Zapis/odczytRead/WritePreskaler timeraTimer prescaler
0x14Zapis/odczytRead/WriteLicznik timeraTimer counter
0x15Zapis/odczytRead/WriteKonfiguracja timeraTimer configuration
0x16Zapis/odczytRead/WriteWartość przeładowania timeraTimer reload value
0x17…0x1C—Zarezerwowane (rejestry ALU — opcjonalne, domyślnie wyłączone)Reserved (ALU registers — optional, disabled by default)
0x1DOdczytReadGenerator pseudolosowy 8-bit8-bit pseudo-random generator
0x1EOdczytReadGenerator pseudolosowy 16-bit (LSB)16-bit pseudo-random generator (LSB)
0x1FOdczytReadGenerator pseudolosowy 16-bit (MSB)16-bit pseudo-random generator (MSB)

RejestryRegisters

Poniżej opisano znaczenie poszczególnych rejestrów w przestrzeni adresowej FPGA.

Rejestry komend i parametrów (ram[0]…ram[9])

Rejestr Przeznaczenie Typ
ram[0]Rejestr komend — zapis numeru komendy rozpoczyna jej wykonanieZapis
ram[1]…ram[9]Parametry komend — znaczenie zależy od konkretnej komendyZapis/odczyt

Rejestry sprzętowe (ram[10]…ram[15])

Rejestr Przeznaczenie Typ
ram[10]Odpowiedź R1 karty SDOdczyt
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/2Odczyt
ram[14]Sterowanie kursorem tekstowymZapis/odczyt
ram[15]Rejestr statusu ogólnegoOdczyt

Rejestry przerwań (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

Rejestry timera (ram[19]…ram[22])

Rejestr Przeznaczenie Typ
ram[19]Preskaler timera (podział częstotliwości)Zapis/odczyt
ram[20]Licznik timera 8-bitZapis/odczyt
ram[21]Konfiguracja timera (autoreload, compare)Zapis/odczyt
ram[22]Wartość przeładowania timeraZapis/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.

Rejestry zarezerwowane (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.

Generatory pseudolosowe (ram[29]…ram[31])

Rejestr Przeznaczenie Typ
ram[29]Generator 8-bit LFSROdczyt
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.

Command and parameter registers (ram[0]…ram[9])

Register Purpose Type
ram[0]Command register — writing the command number starts its executionWrite
ram[1]…ram[9]Command parameters — meaning depends on the specific commandRead/Write

Hardware registers (ram[10]…ram[15])

Register Purpose Type
ram[10]SD card R1 responseRead
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 codeRead
ram[14]Text cursor controlRead/Write
ram[15]General status registerRead

Interrupt registers (ram[16]…ram[18])

Register Purpose Type
ram[16]Active interrupt flagsRead
ram[17]Clear interrupt flags (1 = clear)Write
ram[18]Interrupt enable maskRead/Write

Timer registers (ram[19]…ram[22])

Register Purpose Type
ram[19]Timer prescaler (frequency divider)Read/Write
ram[20]8-bit timer counterRead/Write
ram[21]Timer configuration (autoreload, compare)Read/Write
ram[22]Timer reload valueRead/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.

Reserved registers (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.

Pseudo-random generators (ram[29]…ram[31])

Register Purpose Type
ram[29]8-bit LFSR generatorRead
ram[30]16-bit LFSR generator (LSB)Read
ram[31]16-bit LFSR generator (MSB)Read

Rejestry wewnętrzneInternal Registers

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.

Rejestry interfejsu (0x00–0x16)Interface registers (0x00–0x16)

RejestrRegister AdresAddress Wymaga konfiguracji?Requires configuration?
Rejestr komendCommand registerram[0]NieNo
Pozycja X kursoraCursor X positionram[1]NieNo
Pozycja Y kursoraCursor Y positionram[2]NieNo
Kod ASCII znakuCharacter ASCII coderam[3]NieNo
Atrybut znakuCharacter attributeram[4]NieNo
Parametr 1 komendyCommand parameter 1ram[5]NieNo
Parametr 2 komendyCommand parameter 2ram[6]NieNo
Parametr 3 komendyCommand parameter 3ram[7]NieNo
Parametr 4 komendyCommand parameter 4ram[8]NieNo
Parametr 5 komendyCommand parameter 5ram[9]NieNo
Odpowiedź karty SDSD card responseram[10]NieNo
Konfiguracja UARTUART configurationram[11]NieNo
Dane UARTUART dataram[12]Tak — REG_CFGYes — REG_CFG
Dane PS/2PS/2 dataram[13]NieNo
Sterowanie kursoremCursor controlram[14]NieNo
Rejestr statusuStatus registerram[15]NieNo
Flagi przerwańInterrupt flagsram[16]NieNo
Kasowanie flag przerwańClear interrupt flagsram[17]NieNo
Maska zezwoleń przerwańInterrupt enable maskram[18]NieNo
Preskaler timeraTimer prescalerram[19]NieNo
Licznik timeraTimer counterram[20]NieNo
Konfiguracja timeraTimer configurationram[21]NieNo
Wartość przeładowania timeraTimer reload valueram[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 generatorram[29]NieNo
Generator pseudolosowy 16-bit16-bit pseudo-random generatorram[30], ram[31]NieNo

Mechanizmy działające autonomicznieAutonomous mechanisms

MechanizmMechanism Rejestr / sygnałRegister / signal Wymaga konfiguracji?Requires configuration?
Licznik czasu UNIXUNIX time counterunix_timeNieNo
Timer 8-bit z preskalerem8-bit timer with prescalerram[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_RTSRTSNieNo
Odbiór znaku PS/2PS/2 character receptionram[13]NieNo
Odbiór UARTUART receptionram[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_nNieNo
Latch adresu/danych magistraliBus address/data latchaddr_latch, data_latchNieNo
Linia przerwania INTINT interrupt lineINTNieNo
Blankowanie framebufferFramebuffer blankingfb_blankNieNo
Port debug (LED)Debug port (LED)debug_portNieNo
Zasilanie karty SDSD card powerSD_VCCNieNo
Wyjścia sprite'ówSprite outputsspr_x_*, spr_y_*, spr_ctrl_*NieNo
Kursor tekstowyText cursorcursor_ctrl, cursor_indexNieNo

Kliknij wybrany wiersz, aby zobaczyć szczegóły rejestru.

Click a row to see register details.

KomendyCommands

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.

Komendy systemowe i graficzno-tekstoweSystem and graphics/text commands

NrNo. NazwaName OpisDescription
0CMD_NONEBrak komendyNo command
1CMD_CLEAR_FRAMEBUFFERCzyszczenie bufora graficznegoClear framebuffer
2CMD_WRITE_PIXELZapis pojedynczego pikselaWrite single pixel
3CMD_DRAW_RECTRysowanie wypełnionego prostokątaDraw filled rectangle
4CMD_DRAW_FRAMERysowanie ramki prostokątaDraw rectangle frame
5CMD_WRITE_TEXTZapis znaku w określonym miejscuWrite character at specified position
6CMD_CLEAR_TEXTCzyszczenie ekranu tekstowegoClear text screen
7CMD_PUT_TEXTDopisanie znaku w bieżącej pozycji kursoraAppend character at current cursor position
8CMD_SET_CURSORUstawienie położenia kursora tekstowegoSet text cursor position
9CMD_SET_PIXELUstawienie bieżącej pozycji kursora graficznegoSet current graphics cursor position
10CMD_PUT_PIXELZapis piksela na aktualnej pozycji graficznejWrite pixel at current graphics position
11CMD_UART_ONWłączenie UARTEnable UART
12CMD_UART_OFFWyłączenie UARTDisable UART
13CMD_SET_UNIXUstawienie czasu UNIXSet UNIX time
14CMD_GET_UNIXPobranie czasu UNIXGet UNIX time
15CMD_SET_DEBUGWystawienie danej na debug_portOutput data to debug_port
16CMD_GET_DEBUGPobranie danej z debug_portRead data from debug_port
17CMD_GET_RTSPobranie stanu pinu RTSGet RTS pin state
18CMD_RESET_FPGAReset FPGAReset FPGA
19CMD_GET_CURSORPobranie położenia kursora tekstowegoGet text cursor position
20CMD_PUT_TEXT_REPEAT_OFFKasowanie flagi repetycji znakuClear character repeat flag
21CMD_FB_BLANK_OFFZablokowanie framebuffer (czarny)Block framebuffer (black)
22CMD_FB_BLANK_ONOdblokowanie framebufferUnblock framebuffer

Komendy karty SDSD card commands

NrNo. NazwaName OpisDescription
40CMD_SD_SENDWysłanie komendy do karty SDSend command to SD card
41CMD_SD_READ_STATUSOdczyt statusu karty SDRead SD card status
42CMD_SD_READ_DATAOdczyt danych z karty SDRead data from SD card
43CMD_SD_WRITE_DATAZapis danych do karty SDWrite data to SD card
44CMD_SD_VCC_ONWłączenie zasilania karty SDEnable SD card power
45CMD_SD_VCC_OFFWyłączenie zasilania karty SDDisable SD card power

Komendy spriteSprite commands

NrNo. NazwaName OpisDescription
50CMD_SPRITE_LOAD_BEGINRozpoczęcie ładowania spriteBegin sprite loading
51CMD_SPRITE_LOAD_DATAŁadowanie danych spriteLoad sprite data
52CMD_SPRITE_LOAD_ENDZakończenie ładowania spriteEnd sprite loading
53CMD_SPRITE_POSUstawienie pozycji spriteSet sprite position
54CMD_SPRITE_ENABLEWłączenie/wyłączenie spriteEnable/disable sprite
55CMD_SPRITE_CLEARWyłączenie wszystkich sprite'ówDisable all sprites

Komendy dodane w wersji 1.1Commands added in version 1.1

NrNo. NazwaName OpisDescription
23CMD_DRAW_LINERysowanie linii (Bresenham, grubość, dash)Draw line (Bresenham, thickness, dash)
32CMD_SET_BUF_LBPrzełączenie na dolny bufor graficzny (LB)Switch to lower graphics buffer (LB)
33CMD_SET_BUF_UBPrzełą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 statusuStatus Register

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.

Bity rejestru statusu

Bit Nazwa Opis
0STATUS_VGA0 = VGA idle, 1 = VGA busy — trwa operacja graficzna
1STATUS_PS21 = nowy kod klawisza PS/2 gotowy do odczytu
2STATUS_UART_RXD1 = odebrano dane UART
3STATUS_UART_TXD1 = trwa transmisja UART
4STATUS_SD1 = operacja na karcie SD zakończona
5STATUS_SD_PRESENT1 = karta SD włożona do gniazda
6STATUS_SD_POWER1 = zasilanie karty SD włączone
7—zarezerwowany pod przyszłe zastosowania

Jak używać

CPU może odpytywać rejestr statusu w pętli, aż wybrany bit zmieni stan. Przykład — oczekiwanie na zakończenie operacji graficznej:

polling.c
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.

Status register bits

Bit Name Description
0STATUS_VGA0 = VGA idle, 1 = VGA busy — graphics operation in progress
1STATUS_PS21 = new PS/2 key code ready to read
2STATUS_UART_RXD1 = UART data received
3STATUS_UART_TXD1 = UART transmission in progress
4STATUS_SD1 = SD card operation completed
5STATUS_SD_PRESENT1 = SD card inserted in slot
6STATUS_SD_POWER1 = SD card power enabled
7—reserved for future use

How to 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:

polling.c
while (fpga_read(15) & 0x01) {
    /* wait until STATUS_VGA = 0 */
}

PrzerwaniaInterrupts

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).

Rejestry przerwań

Rejestr Nazwa Typ Przeznaczenie
ram[16]REG_INT_FLAGSOdczytAktywne flagi przerwań
ram[17]REG_CLEAR_FLAGSZapisKasowanie flag (zapis 1 = kasuj)
ram[18]REG_INT_ENABLEZapis/odczytMaska zezwoleń przerwań

Źródła przerwań (bity ram[16] i ram[18])

Bit Nazwa Opis
0IRQ_TIMER8_OVFPrzepełnienie timera 8-bit
1IRQ_TIMER8_CMPZgodność timera (compare match) — NIEZIMPLEMENTOWANE
2IRQ_PS2Nowy kod klawisza PS/2
3IRQ_UART_RXOdebrano dane UART
4IRQ_UART_TXZakończono transmisję UART
5IRQ_CMD_DONEZakończono długą komendę
6IRQ_RTSZmiana 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.

Jak to działa

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:

INT — logic
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.

Obsługa przerwania — krok po kroku

  1. Włącz przerwania — zapisz maskę do ram[18] (1 = włączone).
  2. Czekaj na INT — FPGA ustawi linię INT w stan niski, gdy wystąpi zdarzenie.
  3. Odczytaj flagi — odczytaj ram[16], żeby sprawdzić, które źródła zadziałały.
  4. Obsłuż zdarzenia — wykonaj odpowiednie akcje dla wszystkich aktywnych flag.
  5. Wyczyść wszystkie flagi — zapisz 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.

Przykład — włączenie przerwania od PS/2 i UART RX

isr_example.c
/* 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);
}

Przerwanie od zakończenia długiej komendy

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.

Kasowanie flag

Aby skasować flagę, zapisz 1 na odpowiednim bicie w ram[17]:

clear_flags.c
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ć:

clear_multiple.c
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).

Interrupt registers

Register Name Type Purpose
ram[16]REG_INT_FLAGSReadActive interrupt flags
ram[17]REG_CLEAR_FLAGSWriteClear flags (write 1 = clear)
ram[18]REG_INT_ENABLERead/WriteInterrupt enable mask

Interrupt sources (bits of ram[16] and ram[18])

Bit Name Description
0IRQ_TIMER8_OVF8-bit timer overflow
1IRQ_TIMER8_CMPTimer compare match — NOT IMPLEMENTED
2IRQ_PS2New PS/2 key code
3IRQ_UART_RXUART data received
4IRQ_UART_TXUART transmission completed
5IRQ_CMD_DONELong command completed
6IRQ_RTSRTS 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.

How it works

The INT line is active (low) when any flag in ram[16] is set and the corresponding bit in ram[18] is enabled:

INT — logic
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.

Interrupt handling — step by step

  1. Enable interrupts — write the mask to ram[18] (1 = enabled).
  2. Wait for INT — the FPGA will pull the INT line low when an event occurs.
  3. Read flags — read ram[16] to see which sources have triggered.
  4. Handle events — perform the appropriate actions for all active flags.
  5. Clear all flags — write 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.

Example — enabling PS/2 and UART RX interrupts

isr_example.c
/* 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);
}

Long command completion interrupt

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.

Clearing flags

To clear a flag, write 1 to the appropriate bit in ram[17]:

clear_flags.c
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:

clear_multiple.c
fpga_write(17, 0x04 | 0x08);  /* clear IRQ_PS2 and IRQ_UART_RX at once */

Linia LOADLOAD Line

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ę.

Jak to działa

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.

Stany linii LOAD

Stan LOAD Znaczenie
1 (wysoki)Trwa ładowanie bitstreamu — CPU czeka
0 (niski)Bitstream załadowany — FPGA gotowe, CPU kontynuuje

Przykład kodu — oczekiwanie na LOAD

wait_for_load.c
/* Oczekiwanie na zakończenie ładowania bitstreamu */
while ((PING & (1 << PG5)) != 0) {
    /* LOAD = 1 — czekaj */
    _delay_ms(10);
}

/* LOAD = 0 — FPGA gotowe, można kontynuować */

Pull-upy

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.

How it works

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 line states

LOAD state Meaning
1 (high)Bitstream loading in progress — CPU waits
0 (low)Bitstream loaded — FPGA ready, CPU continues

Code example — waiting for LOAD

wait_for_load.c
/* Wait for bitstream loading to complete */
while ((PING & (1 << PG5)) != 0) {
    /* LOAD = 1 — wait */
    _delay_ms(10);
}

/* LOAD = 0 — FPGA ready, can continue */

Pull-ups

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.

Pull-upy i jumperyPull-ups and Jumpers

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.

Wbudowane pull-upy

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.

Konfiguracja

Każda linia ma swój własny jumper SMD:

Linia Jumper Stan domyślny
INTSJ3zwarta
LOADSJ5zwarta

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).

Kiedy odłączyć pull-upy

Odłączenie pull-upów jest przydatne, gdy:

  • CPU ma własne rezystory podciągające,
  • linie są podciągane po stronie CPU,
  • CPU pracuje na innym napięciu niż 5 V,
  • występuje konflikt napięć między CPU a FPGA.

Uwaga

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.

Built-in pull-ups

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.

Configuration

Each line has its own SMD jumper:

Line Jumper Default state
INTSJ3closed
LOADSJ5closed

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).

When to disconnect pull-ups

Disconnecting pull-ups is useful when:

  • the CPU has its own pull-up resistors,
  • the lines are pulled up on the CPU side,
  • the CPU operates at a different voltage than 5 V,
  • there is a voltage conflict between the CPU and the FPGA.

Note

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.

Przykład kodu — ATmegaCode Example — ATmega

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

Programowanie ATmega2560

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.

Środowisko i kompilacja

Element Wartość
ŚrodowiskoMicrochip Studio (dawniej Atmel Studio)
Kompilatoravr-gcc
MikrokontrolerATmega2560
ProgramowanieJTAG (zewnętrzny programator)
Złącze JTAGwyprowadzone na płytce FPGA
Wersja kodu demo1.0

Połączenie ATmega2560 ↔ FPGA

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.

Magistrala adresowa (A0–A4)

Pin ATmega Port Funkcja
PD0PORTD bit 0A0 — najmłodszy bit adresu
PD1PORTD bit 1A1 — bit adresu
PD2PORTD bit 2A2 — bit adresu
PD3PORTD bit 3A3 — bit adresu
PH0PORTH bit 0A4 — najstarszy bit adresu

Magistrala danych (D0–D7)

Pin ATmega Port Funkcja
PK0PORTK bit 0D0 — najmłodszy bit danych
PK1PORTK bit 1D1 — bit danych
PK2PORTK bit 2D2 — bit danych
PK3PORTK bit 3D3 — bit danych
PK4PORTK bit 4D4 — bit danych
PK5PORTK bit 5D5 — bit danych
PK6PORTK bit 6D6 — bit danych
PK7PORTK bit 7D7 — najstarszy bit danych

Linie sterujące magistrali

Pin ATmega Port Funkcja
PH1PORTH bit 1CE_n — Chip Enable (aktywny 0)
PJ0PORTJ bit 0WE_n — Write Enable (aktywny 0)
PJ1PORTJ bit 1OE_n — Output Enable (aktywny 0)

Linie specjalne

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).

UART

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

PS/2

Pin ATmega Port Funkcja
PS2_CLK—Linia zegara klawiatury PS/2
PS2_DATA—Linia danych klawiatury PS/2

Uwagi do połączenia

  • Ścisłe przypisanie pinów: mapowanie pinów wynika bezpośrednio z implementacji magistrali w FPGA.cpp (operacje na rejestrach PORTD, PORTH, PORTJ, PORTK, PORTE, PORTG). Zmiana pinów wymagałaby modyfikacji kodu biblioteki.
  • Poziomy napięć: magistrala pracuje w standardzie TTL 5 V — ATmega2560 (5 V) współpracuje bezpośrednio, bez konwerterów poziomów.
  • Linie INT i LOAD: są typu open collector — wymagają rezystorów podciągających. Płytka FPGA ma wbudowane pull-upy 4.7 kΩ (zworki SJ3 i SJ5), które można odłączyć, jeśli pull-upy są po stronie ATmega.
  • Bufory magistrali: linie CE_n, WE_n, OE_n sterują buforem magistrali — kolejność ich przełączeń ma znaczenie dla poprawnej komunikacji.
  • Timer5: wykorzystywany do migania Pac-Mana (~4,19 s).

Zawartość paczki

Kod źródłowy jest podzielony na pliki:

  • main.cpp — menu główne i obsługa demo
  • game.cpp — logika gry Pac-Man
  • FPGA.cpp, FPGA.h — biblioteka komunikacji z FPGA
  • color.h, Cursor.h, SDcard.h — definicje pomocnicze
  • def_screen.h — stałe geometryczne ekranu

Pobierz kod demo v1.0 (ZIP)

The 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

Programming the ATmega2560

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.

Environment and compilation

Element Value
EnvironmentMicrochip Studio (formerly Atmel Studio)
Compileravr-gcc
MicrocontrollerATmega2560
ProgrammingJTAG (external programmer)
JTAG connectorprovided on the FPGA board
Demo code version1.0

ATmega2560 ↔ FPGA connection

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.

Address bus (A0–A4)

ATmega pin Port Function
PD0PORTD bit 0A0 — least significant address bit
PD1PORTD bit 1A1 — address bit
PD2PORTD bit 2A2 — address bit
PD3PORTD bit 3A3 — address bit
PH0PORTH bit 0A4 — most significant address bit

Data bus (D0–D7)

ATmega pin Port Function
PK0PORTK bit 0D0 — least significant data bit
PK1PORTK bit 1D1 — data bit
PK2PORTK bit 2D2 — data bit
PK3PORTK bit 3D3 — data bit
PK4PORTK bit 4D4 — data bit
PK5PORTK bit 5D5 — data bit
PK6PORTK bit 6D6 — data bit
PK7PORTK bit 7D7 — most significant data bit

Bus control lines

ATmega pin Port Function
PH1PORTH bit 1CE_n — Chip Enable (active low)
PJ0PORTJ bit 0WE_n — Write Enable (active low)
PJ1PORTJ bit 1OE_n — Output Enable (active low)

Special lines

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).

UART

ATmega pin Port Function
RXD—Receive data from FPGA (UART)
TXD—Transmit data to FPGA (UART)
RTS—Request To Send — input from FPGA

PS/2

ATmega pin Port Function
PS2_CLK—PS/2 keyboard clock line
PS2_DATA—PS/2 keyboard data line

Connection notes

  • Strict pin assignment: the pin mapping results directly from the bus implementation in FPGA.cpp (operations on PORTD, PORTH, PORTJ, PORTK, PORTE, PORTG registers). Changing pins would require modifying the library code.
  • Voltage levels: the bus operates at TTL 5 V — the ATmega2560 (5 V) works directly, without level converters.
  • INT and LOAD lines: are open collector — they require pull-up resistors. The FPGA board has built-in 4.7 kΩ pull-ups (jumpers SJ3 and SJ5), which can be disconnected if the pull-ups are on the ATmega side.
  • Bus buffers: the CE_n, WE_n, OE_n lines control the bus buffer — the order of their switching matters for correct communication.
  • Timer5: used for Pac-Man blinking (~4.19 s).

Package contents

The source code is divided into files:

  • main.cpp — main menu and demo handling
  • game.cpp — Pac-Man game logic
  • FPGA.cpp, FPGA.h — FPGA communication library
  • color.h, Cursor.h, SDcard.h — helper definitions
  • def_screen.h — screen geometry constants

Download demo code v1.0 (ZIP)

Kod źródłowySource Code

Pełny kod projektu jest dostępny w repozytorium GitHub:

Zobacz na GitHubie

The complete project code is available in the GitHub repository:

View on GitHub