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