Maniac Processing Unit  (MPU)    v0.2 RC


Projekt procesora przeznaczonego do implementacji w układach FPGA.

Pewni ludzie, którzy kiedyś robili TO, z nostalgii za dawnymi czasami, postanowili połączyć przyjemne z pożytecznym i zbudować coś, co prócz frajdy przyda się w tworzonych przez nich na co dzień projektach. Założenia do projektu były następujące:
- zbudować procesor prosty, taki w starym stylu, który będzie miał tylko to co niezbędne (w odróżnieniu od dzisiejszych procesorów, które nierzadko mają  setki wyprowadzeń, z czego połowa nie wiadomo do czego służy,  mają też wiele ciekawych rozkazów, o których nigdy nie słyszeli twórcy kompilatorów ;)
- pomimo prostoty procesor powinien być w miarę wydajny, tak aby poradził sobie z typowymi zadaniami, jakich dzisiaj wymaga się od prostych systemów wbudowanych.

 Opis powstałego procesora

- 32 bity
- architektura von Neumanna (w odróżnieniu od innych spotykanych rozwiązań, procesor ma jednolity dostęp do wszystkich zasobów systemu)
- całość została narysowana schematami w edytorze Xilinx (prawdziwy elektronik używa schematów ;), co oczywiście nie wyklucza zastosowania go w projektach miłośników poezji logicznej w VHDL.
 
Wszystkie rejestry procesora znajdują się w pierwszych 64 adresach przestrzeni adresowej. Działanie opiera się głównie na operacjach typu MOV. Cykl maszynowy i praca magistral dostosowane są do współpracy z pamięcią synchroniczną o LATENCY=1, czyli taką jaka znajduje się w układach FPGA. Procesor zajmuje ok. 1500 LUTs (XST, domyślne ustawienia), testowany w układzie Xilinx Virtex5 pracuje na 150MHz i osiąga wydajność 40MIPS.

Symbol i opis końcówek

Wejścia:
- RV(31:0) - Reset Vector
- DI(31:0) - magistrala danych dla odczytu
- CLK - wejście zegara
- RST - reset, powinien trwać minimum 2 CLK
- IRQ - zgłoszenie przerwania, powinno trwać minimum 1 CLK

Wyjścia:
- A(31:0) - magistrala adresowa
- DO(31:0) - magistrala danych dla zapisu
- ENA - służy jako sygnał ENABLE dla zewnętrznego dekodera adresów (procesor znajduje się we wspólnej przestrzeni adresowej, gdy ENA jest nieaktywne oznacza to operację wewnętrzną)
- WR - Write
- RD - Read


Rejestry

Dla procesora zarezerwowane są adresy (HEX) z przedziału 0..3F. Adresy 0..1F zawierają rejestry sterujące tylko do zapisu.
----------------
00 - NULL
----------------
01 - PC - wpisanie tutaj adresu spowoduje skok bezwarunkowy (JMP)
----------------
02 - ID - sterowanie inc/dec dla rejestrów S, X, Y, Z, A:
- b1,b0 - S - 10 dec, 11 inc
- b3,b2 - X
- b5,b4 - Y
- b7,b6 - Z
- b9,b8 - A
----------------
03 - SC - set/clear, wpisanie 1 do odpowiedniego bitu spowoduje:
- b0 - CLRC - clear carry
- b1 - SETC - set carry
- b2 - CLRI - skasuje zgłoszenie przerwania (flaga I)
- b3 - SETI  - ustawi zgłoszenie przerwania
- b4 - I_OFF - wyłączy przerwania
- b5 - I_ON - włączy przerwania
- b7 - RST - autoreset procesora
----------------
04 - CALL - skok bezwarunkowy, zachowuje adres powrotu
----------------
05 - JZ - skok warunkowy, gdy flaga Z=1
----------------
06 - JNZ - skok warunkowy, gdy flaga Z=0
----------------
07 - JC
----------------
08 - JNC
----------------
09 - JS
----------------
0A - JNS
----------------
0B - JZX
----------------
0C - JNZX
----------------
0D - JZY
----------------
0E - JNZY
----------------
0F - JE
----------------
10 - JNE
----------------
11 - JG
----------------
12 - JNG
----------------
13 - JL
----------------
14 - JNL
----------------
15 - JI
----------------
16 - JNI
----------------
----------------
20 - rejestr indeksowy S  (wskaźnik stosu)
----------------
21 - rejestr indeksowy X
----------------
22 - rejestr indeksowy Y
----------------
23 - rejestr indeksowy Z
----------------
24 - rejestr A  (akumulator)
----------------
25 - rejestr B
----------------
26 - rejestr C
----------------
27 - FLAGS - rejestr znaczników
----------------
28 - RET - adres powrotu dla skoków CALL
----------------
29 - RETI - adres powrotu z przerwania
----------------
2A - IV - wektor przerwania
----------------
2E - wynik: A*B, młodsze 32 bity
----------------
2F - wynik: A*B, starsze 32 bity
----------------
30 - wynik: SWAP A (zamiana 16 bitowych połówek słowa)
----------------
31 - wynik: A AND B
----------------
32 - wynik: A OR B
----------------
33 - wynik: A XOR B
----------------
34 - wynik: SHR A (bit wychodzący przechodzi do znacznika C)
----------------
35 - wynik: SHL A (bit wychodzący przechodzi do znacznika C)
----------------
36 - wynik: ROR A (bit przechodzący jest kopiowany do znacznika C)
----------------
37 - wynik: ROL A (bit przechodzący jest kopiowany do znacznika C)
----------------
38 - wynik: A+B
----------------
39 - wynik: A+B+carry
----------------
3A - wynik: A-B
----------------
3B - wynik: A-B-not_carry
----------------
3C - wynik: SHR8 A (SHR o 8 bitów)
----------------
3D - wynik: SHL8 A (SHL o 8 bitów)
----------------
3E - wynik: SSR A (analogicznie jak SHR, ale brakujące pole jest uzupełniane bitem znaku)
----------------
3F - wynik: C+(A*B)
----------------

Znaczniki rejestru FLAGS

b0 - Z  (zero)
b1 - C  (carry)
b2 - S  (sign)
b3 - ZX  (Z=X)
b4 - ZY  (Z=Y)
b5 - EQ  (A=B)
b6 - GT  (A>B, dla liczb ze znakiem)
b7 - LT  (A<B, dla liczb ze znakiem)
b8 - I   (IRQ)

Flagi Z, C, S, ustawiają się podczas odczytu rejestrów wynikowych z zakresu 30..3F, nie jest więc wymagane przenoszenie wyniku do A, można go przenieść gdziekolwiek. Flagi Z, S ustawiają się również podczas zapisu lub inkrementacji/dekrementacji rejestru A. Ponadto flaga Z ustawia się podczas zapisu lub inkrementacji/dekrementacji rejestrów X,Y,Z.
Flagi ZX, ZY, EQ, GT, LT, I, pokazują zawsze stan obecny, nie można więc ich przechować, Z, C, S można przechować, np. przy przyjęciu przerwania, i potem przywrócić. Flagi  ZX, ZY, EQ, GT, LT, przywrócą się same po przywróceniu rejestrów X, Y, Z, A, B.
Flaga I oznacza, że oczekuje zgłoszenie przerwania, gdy przerwania są wyłączone.

Opis rozkazów

Rozkazów jest 8, każdy zajmuje 2 słowa. Numer rozkazu jest kodowany na 3 najstarszych bitach pierwszego słowa b31,b30,b29. W opisie użyte są następujące oznaczenia:
ADDR - adres bezpośredni,
#DATA - dana bezpośrednia,
PARAM - adres wewnętrzny procesora (adres wyniku operacji),
A, B - rejestry procesora,
(I) - adres pośredni pobierany z rejestru indeksowego S, X, Y, Z.
Numery rejestrów indeksowych, binarnie:
00 - S
01 - X
10 - Y
11 - Z
Numer ten podaje się na najmłodszych bitach (b1,b0) słowa, w którym występuje adres pośredni. Tam gdzie występuje adres pośredni można dodatkowo zrobić post inkrementację/dekrementację użytego rejestru indeksowego, (I+), (I-):
b27 - gdy 1 to post INC/DEC się wykona
b26 - gdy 0 to DEC, gdy 1 to INC

Kody rozkazów, binarnie:
----------------
000 - MOV ADDR, #DATA
- rozkaz trwa 3 cykle zegara
- w pierwszym słowie znajduje się kod rozkazu oraz adres 29 bitów (cel)
- w drugim słowie znajduje się dana 32 bity
----------------
001 - MOV (I), #DATA
- rozkaz trwa 3 cykle zegara
- w pierwszym słowie znajduje się kod rozkazu oraz numer rejestru indeksowego (cel)
- w drugim słowie znajduje się dana 32 bity
----------------
100 - MOV ADDR, ADDR
- rozkaz trwa 4 cykle zegara
- w pierwszym słowie znajduje się kod rozkazu oraz adres 29 bitów (źródło)
- w drugim słowie znajduje się adres 32 bity (cel)
----------------
101 - MOV (I), ADDR
- rozkaz trwa 4 cykle zegara
- w pierwszym słowie znajduje się kod rozkazu oraz adres 29 bitów (źródło)
- w drugim słowie znajduje się numer rejestru indeksowego (cel)
----------------
110 - MOV ADDR, (I)
- rozkaz trwa 4 cykle zegara
- w pierwszym słowie znajduje się kod rozkazu oraz numer rejestru indeksowego (źródło)
- w drugim słowie znajduje się adres 32 bity (cel)
----------------
111 - MOV (I), (I)
- rozkaz trwa 4 cykle zegara
- w pierwszym słowie znajduje się kod rozkazu oraz numer rejestru indeksowego (źródło)
- w drugim słowie znajduje się numer rejestru indeksowego (cel)
----------------
010 - OP PARAM, #DATA - rozkaz złożony, realizuje sekwencję:
MOV B, #DATA
MOV A, PARAM
- rozkaz trwa 4 cykle zegara
- w pierwszym słowie znajduje się kod rozkazu oraz parametr (adres wyniku)
- w drugim słowie znajduje się dana 32 bity
----------------
011 - OP PARAM, ADDR - rozkaz złożony, realizuje sekwencję:
MOV B, ADDR
MOV A, PARAM
- rozkaz trwa 5 cykli zegara
- w pierwszym słowie znajduje się kod rozkazu oraz adres 29 bitów (źródło)
- w drugim słowie znajduje się parametr (adres wyniku)
----------------

W praktyce asembler, wykorzystując mechanizm makr, może tłumaczyć surowe rozkazy własne procesora na typowe mnemoniki.


Uwagi

Rejestry A, B, C, są rejestrami służącymi do operacji arytmetycznych i logicznych.  Rejestr A pełni rolę akumulatora, rejestr B służy do operacji dwuargumentowych (np. A+B), rejestr C bierze udział tylko w operacji mnożenia z akumulacją. Celowo jest on wydzielony, tak aby można było swobodnie używać rejestrów A i B pomiędzy kolejnymi iteracjami pętli MAC.  Gdy nie używamy w programie mnożenia z akumulacją, możemy ten rejestr wykorzystać dowolnie, np. do tymczasowego przechowania jakiejś danej.

Rejestry indeksowe S, X, Y, Z biorą udział w adresowaniu pośrednim. Rejestr S działa analogicznie jak pozostałe, z tym że operacje na nim nie wpływają na flagi. Rejestr Z oprócz swojej podstawowej funkcji może służyć do porównania z rejestrami X i Y (flagi ZX i ZY).  Przyśpiesza to operacje przepisywania bloków pamięci. Przykładowa szybka pętla wygląda tak:

            mov x, #src
            mov y, #dest
            mov z, #src + count
loop:     mov (y+), (x+)
            jnzx (loop)


To że wynik operacji znajduje się w akumulatorze jest zgodne z tradycją, jednakże w tym procesorze nie jest to wymagane. Odpowiednie flagi ustawiają się podczas odczytu rejestru z wynikiem, można więc wynik przenieść gdziekolwiek, np. od razu do komórki pamięci. Ponadto można wtedy korzystać z wielu wyników bez ponownego ładowania  argumentów (A pozostaje niezmieniony po takiej operacji).

Cykl maszynowy jest dostosowany do współpracy z pamięcią synchroniczną o  LATENCY=1. Oznacza to, że gdy procesor zapisuje daną, robi to pod adres jaki obecnie znajduje się na magistrali adresowej, jednocześnie aktywny jest sygnał WR. Natomiast gdy procesor czyta daną, czyta ją spod adresu, który znajdował się na magistrali 1 CLK wcześniej, również sygnał RD był wtedy aktywny. Gdy jedynym źródłem danych dla procesora jest pamięć, nie ma to większego znaczenia. Gdy tych źródeł jest więcej (np. urządzenia wejścia), dana musi przejść przez multiplekser wybierający właściwe źródło. Aby multiplekser ten był poprawnie adresowany, adres musi pochodzić z poprzedniego cyklu CLK. Uzyskuje się to w prosty sposób, zatrzaskując potrzebne bity magistrali adresowej procesora w rejestrze przy aktywnym sygnale RD (sygnał RD podłączony do wejścia CE rejestru typu flip-flop).

cdn...