DCE Q816 – pierwszy procesor w FPGA

Artykuł ten jest wpisem archiwalnym pochodzącym z mojej pierwszej strony internetowej powstałej w okolicach 2017 roku. Jakość grafik jest nie najlepsza, ponieważ nie mam już niestety dostępu do oryginałów, ale dla zachowania pamięci o tym projekcie udostępniam związane z nim materiały.

System DCE to zaprojektowany przez mnie procesor (DCE Q816) oraz koprocesor (DCE Q817) uruchomiony na układach FPGA. Powstał on przy okazji mojej nauki języka VHDL oraz ogólnego poznawania układów FPGA. Postanowiłem, że podzielę się moim projektem z innymi, aby zaciekawić ich tematyką FPGA.
Projekt zrealizowany został na dwóch płytkach Elbert v2. Wyposażono je w układy Xilinx Spartan XC3S50A. Jak widać jedna, jak i druga płytka przeszły odpowiednie modyfikacje. Na płytce procesora DCE Q816 odłączone zostały wyświetlacze 7-segmentowe. Płytka koprocesora DCE Q817 przeszła znacznie więcej. Tutaj wyświetlacze 7-segmentowe oraz pięć przycisków zostały całkowicie usunięte a diody LED oraz przełączniki Dipswitch odłączone. Modyfikacje wykonane zostały, aby zwiększyć liczbę dostępnych pinów. 
 
Poniżej płytek z układami FPGA widzimy trzy kolejne elementy systemu DCE. Są to: wyświetlacz LCD oparty na sterowniku HD44780, sekcja zasilania doprowadzająca odpowiednie napięcie do pozostałych układów oraz cztery wyświetlacze 7-segmentowe, z czego aktywne są trzy.

Powyżej zobaczyć możemy schemat ogólny systemu DCE. Głównym elementem jest procesor DCE Q816 zawiera on jednostkę centralną (CPU) oraz pamięć operacyjną RAM. Do jego wejścia podłączonych zostało osiem przełączników Dipswitch. Kolejnym elementem jest koprocesor DCE Q817. Realizuje on funkcję pamięci ROM, w której zapisany jest wykonywany program oraz procesora graficznego.  Zajmuje się on generowaniem sygnałów dla wyświetlaczy LCD oraz LED podłączonych do wyjść koprocesora.

DCE Q816 jest 8-bitowym procesorem zrealizowanym na układzie FPGA. Współpracuje on z koprocesorem DCE Q817. Układ oparty został na architekturze MS2. Jest to moja własna koncepcja oparta na architekturze harwardzkiej (pamięć programu oraz operacyjna są fizycznie rozdzielone). Procesor taktowany jest sygnałem o częstotliwości 12MHz, ale układ obsługuję programową zmianę częstotliwości. Mamy możliwość zmniejszenia tej wartości nawet do 2,8Hz. Procesor posiada 8-bitowe złącze wejściowe oraz 16-bitowe wyjściowe. Dodatkowo układ posiada wejście na zewnętrzny sygnał RESET.

Powyżej widzimy, że CPU oparto na czterech blokach funkcyjnych. Rdzeń wykonawczy CORE MS2 to tutaj wykonywane są wszystkie operacje arytmetyczne oraz logiczne. Układ sterowania tytaj generowane są wszystkie sygnały sterujące. Pamięć RAM (pamięć operacyjna) o pojemności 2kb (256B) zrealizowana na bloku pamięci wewnątrz układu FPGA. Ostatnim elementem jest blok I/O (wejścia/wyjścia) obsługuję on porty wejścia/wyjścia.

Rdzeń logiczny (wykonawczy) CORE MS2 jest głównym elementem procesora DCE Q816. To tutaj wykonywane są wszystkie operacje arytmetyczne oraz logiczne. W jego skład wchodzi jednostka arytmetyczno-logiczna (ALU), multiplekser wejściowy oraz zespół rejestrów.

Rejestr A jest pierwszym elementem pamiętającym, to tutaj zapisywane są wszystkie dane niezależnie od dalszego przeznaczenia. Rejestr B przechowuję drugą zmienną dla ALU. W rejestrze C przechowywany jest rozkaz dla ALU. Ostatnim rejestrem jest STS. Jest to 4-bitowy rejestr przechowujący informację o tzw. bitach stanu. Potrzebne są one do wykonania rozkazów skoków warunkowych.

Bit przeniesienia

Bity stanu

Cout

A=B

A>B

A<B

W tabeli widzimy dokładną konstrukcję rejestru STS. Pierwsze trzy bity (od prawej strony) określają zależność między zmienną zapisaną w rejestrze A oraz B. Po prostu, jeżeli zmienna zapisana w B będzie większa niż ta zapisana w A to pierwszy bit (A<B) przyjmie wartość jeden. Ostatnim bitem rejestru STS jest Cout tzw. bit przeniesienia. Informuje on, że wynik operacji przeprowadzonej przez jednostkę arytmetyczno-logiczną (ALU) przekroczył zakres 8 bitów. Wyjście rejestru STS połączone jest z układem sterowania. 
 
MUX, czyli multiplekser wejściowy jest elementem wybierającym, który z sygnałów wejściowych ma zostać zapisany do rejestru A. Sygnał ten wybierany jest na podstawie wykonywanego rozkazu. 
 

Głównym elementem rdzenia wykonawczego CORE MS2 jest jednostka arytmetyczno-logiczne (ALU). To tutaj na zmiennych zapisanych w rejestrach A i B wykonywane są operację zgodne z rozkazem zapisanym w rejestrze C. W tabeli 2 widzimy wszystkie rozkazy obsługiwane przez ALU.

Kod binarny

Nazwa rozkazu

Opis

0000

A

Prześlij A

0001

A+1

Zwiększ A o jeden

0010

A+B

Dodawanie

0011

A-B

Odejmowanie

0100

A and B

Operacje logiczne

0101

A or B

0110

A xor B

0111

A nand B

1000

A nor B

1001

A xnor B

1010

not A

1011

PL

Przesunięcie w lewo

1100

PR

Przesunięcie w prawo

1101

RL

Rotacja w lewo

1110

RP

Rotacja w prawo

Układ I/O jest w rzeczywistości zbiorem trzech rejestrów IN, OUT1, OUT2. Jak można się domyślić jeden z nich jest wejściowy pozostałe dwa wyjściowe. Procesor zapisuje w nich wszystkie dane odebrane z zewnątrz oraz te, które na zewnątrz chce wysłać. Można powiedzieć, że pełnią one role komunikatorów ze światem zewnętrznym.  

Blok pamięci RAM składa się z dwóch elementów rejestru RAR przechowującego adres oraz samej pamięci. Do realizacji pamięci wykorzystany został blok dostępny wewnątrz układu FPGA. Adresy komórek pamięci mają szerokość 8 bitów co daje łączną pojemność 2k bitów (256 bajtów).

Układ sterowania jest najbardziej zaawansowanym elementem procesora. To tutaj na podstawie rozkazu oraz sygnału zegarowego generowane są wszystkie sygnały sterujące. Dodatkowo w skład układu sterowania wchodzą: licznik adresowy dla pamięci ROM wraz z rejestrami przechowującymi adresy do skoków, rejestr DB przechowujący zmienną bezpośrednio odebraną z pamięci ROM.

Na obrazku widzimy uproszczone przebiegi czasowe dla procesora DCE Q816. Widzimy, że wykonanie pojedynczego rozkazu bez względu na jego typ zajmuję cztery takty zegara. Na rysunku opisane zostały cztery sygnały. Pierwszy z nich to Adres ROM, czyli adres dla pamięci programu. Jak widać wartość adresu zmienia się przy czwartym takcie zegara może to być zwiększenie o jeden lub skok pod adres zapisany w rejestrach PCL i PCH. Kolejny sygnał to zmienna pojawiająca się na wejściu procesora. Zmienna pojawia się tam tuż po zmianie adresu. Dane IR/DB to określenie wyjść rejestrów IR (rejestr instrukcji) oraz DB (dana bezpośrednia). To na podstawie danych zapisanych w tych rejestrach określany jest rozkaz, jaki wykonuje procesor. Zapis do tych rejestrów odbywa się przy pierwszym takcie zegara. Ostatni sygnał określa to, co znajduje się na wyjściu układu. Aktualizacja zawartości rejestrów OUT oraz większości rejestrów procesora odbywa się przy drugim takcie zegara. 
 
Jest to dość uproszczony schemat działania procesora, ale można to ująć jeszcze prościej. Układ pobiera instrukcje z pamięci ROM. Wykonuje ją. Zwiększa lub ustawia adres do pobrania kolejnej instrukcji i wszystko wykonuje się kolejny raz.

Lp.

Kod binarny

Nazwa

Opis

1

00000

NOP

Nie rób nic

2

00001

AC

Prześlij zmienną z wyjścia CORE MS2 do A

3

00010

AIN

Prześlij zawartość z rejestru IN do A

4

00011

ADB

Prześlij zawartość z rejestru DB do A

5

00100

ARAM

Prześlij zawartość z pamięci RAM do A

6

00101

B

Prześlij zawartość z rejestru A do B

7

00110

C

Prześlij zawartość z rejestru A do C

8

00111

RAMA

Zapisz do rejestru adresowego pamięci RAM wartość z wyjścia CORE MS2

9

01000

RAMD

Zapisz do pamięci RAM wartość z wyjścia CORE MS2

10

01001

OUT1

Zapisz do rejestru OUT1 wartość z wyjścia CORE MS2

11

01010

OUT2

Zapisz do rejestru OUT2 wartość z wyjścia CORE MS2

12

01011

PCL

Zapisz do rejestru PCL wartość z wyjścia CORE MS2

13

01100

PCH

Zapisz do rejestru PCH wartość z wyjścia CORE MS2

14

01101

IN

Zapisz wartość do rejestru IN

15

01110

CLRA

Zeruj wartość rejestru A

16

01111

JMP

Skok bezwarunkowy

17

10000

JMP>

Skok jeżeli A>B

18

10001

JMP<

Skok jeżeli A<B

19

10010

JMP=

Skok jeżeli A=B

20

10011

JMPC

Skok jeżeli Cout=1

21

10100

JMPNC

Skok jeżeli Cout=0

22

10101

RESET

Ogólny reset procesora

23

10110

CF

Zmiana częstotliwości taktowania

W tabeli 3 widzimy listę instrukcji wykonywanych przez procesor DCE Q816. Jest to również dobry moment do omówienia struktury wykonywanych rozkazów. Pojedyncza instrukcja składa się z dwóch części pierwsze pięć bitów to ogólny rozkaz (wartość z tabeli 3) pozostałe osiem bitów to tzw. dana bezpośrednia.

Dane bezpośrednie

instrukcja

D12

D11

D10

D9

D8

D7

D6

D5

D4

D3

D2

D1

D0

Dana bezpośrednia posiada trzy funkcje. Pierwsza to po prostu stała zmienna zapisana w programie, na której to później wykonywane są odpowiednie operacje. Kolejne znaczenie jest powiązane z pierwszym. Początkowo traktujemy ją jak stałą zapisaną w programie. Jednak później zostaje ona zapisana w rejestrze C i staje się rozkazem dla ALU. Wartości odpowiadające odpowiednim rozkazom w tabeli 2. Ostatnią funkcją zmiennej bezpośredniej jest określenie częstotliwości taktowania procesora. Jak już wspominałem procesor posiada programową możliwość zmiany częstotliwości taktowania. Po wpisaniu rozkazu 23 (tabela 3) w część odpowiadającą instrukcji dana bezpośrednia staje się określeniem częstotliwości taktowania procesora. W tabeli 6 możemy zobaczyć wszystkie dostępne wartości i odpowiadające im dane bezpośrednie. 

Kod binarny

Częstotliwość taktowania

00000

2,8Hz

00001

5,7Hz

00010

11Hz

00011

22Hz

00100

45Hz

00101

91Hz

00110

183Hz

00111

366Hz

01000

732Hz

01001

1,5kHz

01010

3kHz

01011

5kHz

01100

11kHz

01101

23kHz

01110

46kHz

01111

93kHz

10000

187kHz

10001

375kHz

10010

750kHz

10011

1,5MHz

10100

3MHz

10101

6MHz

10110

12MHz

Koprocesor DCE Q817 jest drugim elementem systemu DCE. Zrealizowany został na identycznej płytce jak procesor. Koprocesor realizuje funkcję pamięci ROM, w której zapisany jest program wykonywany przez procesor. Dodatkowo zajmuje się on generowaniem sygnałów dla wyświetlaczy 7 segmentowych na podstawie danych otrzymanych od procesora. Na ten moment nie ingeruje on w zmienne sterujące wyświetlaczem LCD po prostu je przesyła. Generowaniem sygnału zajmuje się procesor.
 
Pamięć ROM zrealizowana na układzie FPGA jest w rzeczywistości pamięcią RAM z przypisanymi wartościami początkowymi. Właśnie te wartości są programem wykonywanym przez procesor. Pojemność pamięci ROM to 26kb (około 3kB).

Jak już wspomniałem jest to archiwalny opis projektu, który zbudowałem na potrzeby nauki języka VHDL. Ostatecznie powstały cztery wersje tego projektu zbudowane w programie ISE, każdą z nich umieściłem na moim profilu GitHub.

Chcesz być na bieżąco?
Dołącz do newslettera

Otrzymywać będziesz powiadomienia o nowych artykułach oraz informacje o projektach, nad którymi pracuję.

Leave a Comment

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *

Przewiń do góry