Hogyan tervezzünk processzort [pre-amb]

Egy új, a korábbinál valamivel komplexebb processzor tervezését tervezem. A processzorról, annak majdani jellemzőiről néhány mondat:

- Két munka-regiszterrel bír majd (AX,BX), valamint, nyolc bites szervezésű lesz
- Lesz két flag-je is (Előjel és Zero)
- RISC processzor lesz, tehát, ez is elég kevés utasítást fog ismerni
- Előkerül az endianness fogalma és a stack
- A korábbi VM alapjaira épül majd, de azon sok mindenben túlmutat
   Például a 256 Byte memóriaküszöb át lesz hágva. A korábbi, egy byte-os, előjel nélküli integerek  helyett ez a processzor már 16 bites előjeles integerekkel (-32768 .. 32767) lesz képes műveleteket végezni

 A processzor a saját assembly nyelve mellett egy primitív, de magas szintű nyelven is programozható lesz.  A cél ezzel az assembly és a magas szintű nyelvek kapcsolatának bemutatása.      

Ez a munka valamivel komolyabb - és persze némleg összetettebb is - lesz az eddigieknél.
A korábbi sorozatokban előfordult, hogy kissé levegősen fogalmaztam, néha csak utalást tettem  bizonyos dolgokra. Ez szándékos volt. Így próbáltam enyhén, burkoltan presszionálni az olvasót arra, hogy bátran kutakodjon, maga is nézzen utána dolgoknak, bővítse ismereteit az aktuális témára vonatkozóan. A visszajelzések hiánya okán nem tudom, hogy ez mennyire sikerült.   
Itt viszont ez a, mondjuk úgy,  hiánygazdálkodásos gyakorlat már nagy mértékben korlátozná az érthetőséget, így arra jutottam, hogy magyarázataimban ezúttal sokkal következetesebb leszek, magyarán, minden szükséges információ, részlet magából a sorozatból ki fog derülni.

A forgatókönyv:
Az első részek témája a CPU bemutatása, annak felépítése és megépítése lesz. Miértek, hogyanok. Ezt követi a processzor assemblere, ami helyenként  fel fog bukkanni már a CPU-ról szóló, első fejezetekben is. A végén sor kerül annak a szimpla, de már magas szintű nyelvnek a bemutatására és megépítésére, amellyel a processzort - immár nem csak assembly nyelven -  programozni lehet.

Még valami.

Tekintve, hogy ezzel már több munkám lesz, mint a korábbiakkal, fölöslegesen dolgozni viszont én sem szeretek, így ebbe csak akkor fogok bele, ha jelen poszt kiíratásának dátumától számítva két napon belül legalább tizenkét olvasó jelzi a poszt alatt abbéli szándékát, hogy a dolog Őt érdekli. Ha ez a feltétel nem teljesül, akkor az egésszel automatikusan  felhagyok és ez a fentebbi infó - ha úgy tetszik előzetes - úgymond, semmissé lesz.

Köszönöm a figyelmet, minden olvasónak további kellemes napot kivánok.

Hozzászólások

Milyen formátumban lesz elérhető a processzor leírása? VHDL, Verilog?

A processzor virtuális lesz.  Egy emuláció fog futni PC-s környezetben.
Sem VHDL sem verilog megvalósítást nem tervezek.  Legalábbis egyelőre.
 

Pedig hardverben az igazi.
Ez például megtetszett: egy Beaglebone Black fölé épített FPGA-s elektronikába célszerű volt beépíteni egy kisegítő CPU-t.

Verilog oldal: https://github.com/jks-prv/Beagle_SDR_GPS/blob/master/verilog/cpu.v
Szoftver oldal: https://github.com/jks-prv/Beagle_SDR_GPS/blob/master/e_cpu/asm/cpu.h

Külön érdekesség a fenti verilog állományban:

               op_dup       =  8'h81,
               op_swap      =  8'h82,
               op_swap16    =  8'h83,
               op_over      =  8'h84,
               op_drop      =  8'h85,
               op_rot       =  8'h86,

Láttam ennek a modulnak az eredetijét is, ott egyszerűen assemblyben definiáltak utasításnévvel konstansokat (op_dup, op_swap) és ezekkel zsonglőrködtek a kód hátralevő részében, így nem kellett saját assembly fordítót sem írniuk.

Nem vagyok benne biztos, hogy akik a processzorok működését szeretnék megismerni, ráadásul szoftveres megközelítésből, azok szívesen foglalkoznának hardver építéssel. Ráadásul, a korábbi posztomban ismertetett TD4 is éppen csak, hogy teljesíti a processzoroktól elvárhatót, mivel RAM-ja egyáltalán nincs is. Ennek ellenére, a megépítése, önköltségi szinten is úgy 4-5000 Ft lenne. Hányan állnának neki megépíteni, ha ott van mellette az emulátor, teljesen készen, ingyen?

Attól, hogy virtuális, még valamilyen leírónyelven lehet specifikálni a hardver működését. A processzor semmi más, mint egy szép nagy kombinációs/sorrendi hálózat, ami egy jó nagy logikai függvényt valósít meg.

Ezt logikai kapu (áramkör) szinten érdemes specifikálni, ha már tényleg processzort tervezel.

Az utasítás-dekódoló áramkört (ez definiálja egyben az utasításkészletet is - mármint magát a gépi kódot), az utasításvégrehajtó áramkört (hány elemű a pipeline, ha egyáltalán van pipeline, in-order, out-of-order stb), támogat-e 30 éve fejlettnek számító dolgokat (branch prediction stb.), a memóriával való interfészt (memória kontroller áramkör).

Attól, hogy virtuális, még valamilyen leírónyelven lehet specifikálni a hardver működését.

Lehetne, de ha azok, akiknek az anyag szánva van, nem ismerik ezeket a leírónyelveket, akkor értelme se sok lenne az egésznek.

Maga a cél is más. A dolog lényege a processzor működésének demonstrálása, de nem elemi szinten.
Kb olyan, mint, hogy assembly nyelven is lehet programot írni meg magas szintű nyelven is.

Itt, ebben az olvasatban a tervezés nem tranzisztor, vagy logikai kapuk szintjén megy végbe, hanem regiszterek, stack, stb. szinten. Ezek szerepe, egymásra hatása kerül bemutatásra. Ha kerül. 
Itt olyan, hogy predikció, meg pipeline, szóba sem kerül.  RAS meg CAS lesz, meg lesz talán fetch is és LIFO, de ennél lejjebb nem lesz említve semmi.  
 

Fontos, hogy a telepítőt ne az operációs rendszer felhasználói adatokat tartalmazó könyvtárába (pl. User, Felhasználók, Desktop, Asztal, stb.) csomagoljuk ki, mert onnan indítva nem működik! 

F@&zpalm! Ilyen komoly szoftverek gyártói miért nem tudnak soha rendes telepítőt írni?

Általában az ilyen kis processzorokat nagyon egyszerű valamilyen HDL-ben leírni, aztán meg futtathatod szimulátorban (amihez olyan körítést írsz, amilyet csak szeretnél, akár GUI-val is). És így akár igazi hardvert is meghajthat, pl. egy olcsó FPGA dev boardon keresztül.

Amúgy szerintem szemléletesebb egy HDL kód (akár VHDL, akár Verilog), mint egy emulátor valamilyen "rendes" programnyelven.

Most akkor te processzort tervezel, vagy egy számítógép-architektúrát? Ezt jó lenne az elején eldönteni.

Az, hogy hány regiszter látszik a programozó felől, mi az utasításkészlet, mi az utasításkódolás (azaz mi a gépi kód), mi a memória interfész, az még nem konkrét processzor, csak egy számítógép-architektúra, amit lehet többféle módon is implementálni, a programozó meg a külvilág felé ekvivalens lesz - az egyes implementációk maguk a konkrét processzorok, amik jól vagy rosszul valósítják meg az adott architektúrát.

A számítógép architektúra az én fogalmaim szerint nem kevéssé túlmutat egy processzor keretein, bár kétségtelen, hogy utóbbi az előbbinek abszolút alapja.

Ebben a kontextusban a processzor tervezése egy olyan művelet, ami a szoftver irányából közelít.
Tehát, ha mondjuk,  egy magas szintű programnyelvnek az abszolút minimumát meg szeretnénk alkotni, akkor ehhez milyen kiépítésű processzor szükségeltetik, mint feltétlenül szükséges. Annak a processzornak milyen belsővel kell bírnia, milyen utasításkészletet kell ismernie, mekkora memória címzését kell, hogy lehetővé tegye, stb., stb.

Na, én az ezt megvalósító (bemutató) folyamatra gondoltam.  

Szoftver irányából nézve te nem processzort látsz, hanem számítógép architektúrát.

Regisztereket, címzési módokat, utasáításokat, utasítások kódolását, támogatott adattípusokat, a végrehajtási modellt (pl. mi a kezdőcím) stb.

Ez még nem processzor. Ez egy architektúra. De nem tudod, hogy mondjuk a konkrét megvalósítása az adott architektúrának milyen.

A processzor már maga az architektúra egy konkrét megvalósítása.

Pl. milyen memóriát támogat (milyen memóriakontrollere van - DDR RAM? EDO RAM stb.)
Nem tudod, hogy belül milyen az utasítás-végrehajtás (sorrendi, out-of-order, pipeline-os stb.).
Nem tudod, hogy belül milyen ideiglenes, a programozó felé nem látható regisztereket, belső állapottárat használ - ez az implementáció része. Az implementáció része maga az órajel is például, nincs köze az architektúrához.

Nem tudhatod, hogy egyáltalán belül milyen saját utasításkészlettel dolgozik - a modern Core processzorok, (hiába CISC az Intel IA64 számítógéparchitektúra), RISC processzorok belül, a végrehajtás során.

Nagyon nagy különbség van egy számítógép-architektúra meg annak egy konkrét implementációja között.

Eleve, ha nem így lenne, akkor 1-1 architektúrát (pl. a Power-t, a RISC-V-t, az Intel IA64-et) nem is tudná, csak 1-1 processzor implementálni.

 

Most akkor te processzort, vagy architektúrát tervezel? A kérdésem még mindig áll.

Az egy dolog, hogy adsz egy adott architektúrára egy emulált processzort. Fogalmilag nagyon eltérő dolog a kettő.

Értem én, hogy te mit értesz számítógép-architektúra alatt, de én nem azt értem alatta.
Ez szemléleti, felfogásbeli kérdés.

Amikor ez a fogalom napvilágot látott, akkor még tokba integrált processzorok sem léteztek, egészen más iránya volt a számítógép, a számítógép-architektúra tervezésnek is mint ma.  Akkor még felhasználtak egy fél talicska tokozott és tokozatkan alkatrészt,  a számítógép megépítéséhez, nem különült el a processzor egység a többitől úgy, mint ma.

Amit én értettem ez alatt a fogalom alatt, hogy tervezés, azt már leírtam. Érd be ennyivel.
Számítógép-architektúra körüli fogalmi viták, mivel ezek szemléleti kérdések, ráadásul,  erősen perifériálisak, nem érdekelnek. 

 

Szakmai kíváncsiság okán növelem az érdeklődők számát :)

Sok sikert hozzá!

Én ugye, nem csak terveztem, de építettem is procit (meg gépet is hozzá).
Bár én meg emulátort nem csináltam hozzá, sem hardware leíró nyelvet.

BASIC értelmezőn gondolkodtam, de még lusta voltam belefogni. meg hát optimálisabban tudom kódolni assemlyben.

Viszont ebből a pár mondatodból megítélve, szerintem a tiédet is meglehetne valósítani TTL IC-kből!
Bár ez lehet csak az én perverzióm :)

szerintem a tiédet is meglehetne valósítani TTL IC-kből!

Igen, meg lehetne építeni, aki akarja, az meg is építheti. Én egyelőre nem érzek hozzá késztetést. Szeretnék egy négy bites gépet megépíteni, előtte megtervezni, de azt, a ROM és RAM kivételével, kizárólag SMD tranzisztorokból.  Valamikor karácsony környékén neki is állok. Ha kész leszek vele, majd én is publikálom az eredményeket, ahogy te is tetted. 

Köszönöm a biztatást!

+1

Normális ember már nem kommentel sehol.

Bár az előző sorozattal is le vagyok maradva, érdekel. Már csak azért is, mert nagyon jó valami igazi szakmai tartalmat látni itt!

Eddig is, eztán is szeretnék tanulni a blogodból. Köszi az eddigit és köszi, ha folytatod!

Lesz két flag-je is (Előjel és Zero)

Carry jó dolog, ezt szerintem ne hagyd ki. Láncolt shift, több byte-os összeadás/kivonás ... kell.

+1! Sőt, ne csak carry legyen hanem overflow is. Valojaban ezzel a ket plusszal (carry, overflow, meg ugye a zero ill negative, ami a kollega eredeti felveteseben is benne van) lesz teljes az aritmetika: igy nemcsak elojeles hanem elojelnelkuli, többszavas aritmetikat es vezerlest is tudsz csinalni. 

Illetve van egy 5ik bit, amit erdemes lehet hozzavenni, az a negative ^ overflow. Kicsit felrevezeto modon ezt hivjak nehol (pl az AVR-nel) előjelnek (sign): lasd pl itt: http://www.rjhcoding.com/avr-asm-sreg.php. Vagyis ketfajta megkozelites lehet a teljes aritmetikara:

  - csak 4 flag bit (zero, negative. carry, overflow) + szofisztikalt felteteles ugrasok (lasd: ARM)

  - több (5+) flag bit + faék egyszeru felteteles ugrasok (lasd: AVR) 

Alábbiakat mindkettőtöknek írom:

Jó az, amit javasoltok, csak úgy vélem, nem ide. Akik azért olvassák a blogomat, hogy ismereteket nyerjenek, azok tudásszintjéről, legalábbis ebben a témakörben, fogalmam sincs. Feltételezem, hogy vannak köztük, akik olyan fogalmak jelentésével, mint carry, overflow, stack, zero flag, stb. még nincsenek tisztában. Amit meg kell érteniük, az eddig sem volt kevés. A befogadó sem terhelhető vég nélkül, ha az a cél, hogy meg is értse az olvasottakat. Úgy gondolom, a kevesebb most is több lenne. A célom nem egy szuper processzor összerakása, hanem az elméleti részt modellező, azt igazoló, annak életképességét alátámasztó processzor megépítése. 

 A célom nem egy szuper processzor összerakása, hanem az elméleti részt modellező, azt igazoló, annak életképességét alátámasztó processzor megépítése. 

Ebben tokeletesen igazad van, a minimalizmus az tenyleg nem baj ebben az esetben. Azt en is latom hogy sok fizikailag is letezo architektura egeszen jo lenne hasonlo celokra, ha nehany ponton nem lott volna nagyon tul a RISC filozofian... Ugyanakkor azt is irod meg a nyitoban, hogy:

processzor már 16 bites előjeles integerekkel (-32768 .. 32767) lesz képes műveleteket végezni

Na, ezt pl overflow bit nelkul nem tudod megcsinalni, ha a minimalis "hasonlitsunk ossze ket elojeles egesz szamot" funkcionalitast is hozzaveszed. Ha meg a "hasonlitsunk ossze ket nemnegativ egesz szamot" nezzuk (azaz az unsigned aritmetika egyik probemajat nezzuk), akkor meg a carry bitre van szukseged. Szoval nem veletlenul irtuk ezt :) 

Persze az is egy mas kerdes hogy a "bevezetes a processzorok vilagaba" teman belul lehet egy "bevezetes az egesz-aritmetika vilagaba" vagy az "ALU-k vilagaba" vagy hasonlo resz is. Ami mar onmagaban is megallja a helyet, lehet egy kerek tortenet es nagyban segiti+konnyiti a processzorok allapotgepenek a megerteset is.

Még anno oktató koromban volt egy hallgatói beszámoló, ahol a srác tervezett egy processzorkát, és lelkesen ecsetelte, hogy milyen szuper, milyen jó.

Csak pl. regisztereket nem tudott menteni, és ezzel mondjuk a megszakításvezérlőket ki is lőtte szépen (és nem értette, hogy ez miért baj...)

Hát ebben lehet majd regisztereket "menteni", elvileg, mert lesz benne HW stack,  persze a mentés módjával mehet csak, mert az egyszerűség, az érthetőség is szempont. Megszakítás viszont nem lesz benne, tehát, olyan nagyon a regiszterek mentésére sem lesz égető szükség. 

Na, ezt pl overflow bit nelkul nem tudod megcsinalni, ha a minimalis "hasonlitsunk ossze ket elojeles egesz szamot" funkcionalitast is hozzaveszed. Ha meg a "hasonlitsunk ossze ket nemnegativ egesz szamot" nezzuk (azaz az unsigned aritmetika egyik probemajat nezzuk), akkor meg a carry bitre van szukseged. Szoval nem veletlenul irtuk ezt :) 

Igen, a kiírásban az szerepel, hogy 8 bites lesz, de ott hibáztam. A cél valóban ez volt, de később átgondoltam, és úgy döntöttem, hogy 16 bites regiszterek lesznek benne, legalábbis a munkaregiszterek és a címreg, mivel az előző processzor egy byte-os korlátja mellett a 256 Byte-os memóriát éreztem kissé komolytalannak,  legalábbis a userek szemszögéből. 
Már elmondtam, pontosabban, leírtam, hogy mi ezzel a cél, és azt is, hogy mindenképpen kell legyen egy határ. Még akkor is, ha ez azzal jár, hogy az értőbbek, a profibbak hiányolni fognak ezt-azt. Ez a dolog, bár véleményezhetik, de akkor sem nekik készül.
A carry bit szükségességének feloldásán pedig épp most dolgozok.

 

Ezen bitek (Z, N, V, C, S, ...) meglete nem igazan fugg a bitszelessegtol ;) Ugyanugy kell 8, 16, 32, ... barmennyi bithez, ha egesz aritmetikaban dolgozunk. Sot, sok bit ugyanilyen ertelemben megmarad lebegopontos aritmetikaval is, csak... csak ott nehezebb hardverbol kiszamolni. A carry meg mar az 1 bites felosszeadonal is megjelenik mint alapfogalom. 

Persze ki lehet hagyni, de asszem most nagyon sokaig kene agyalnom hogy C nelkul hogyan is adnek ossze ket 16 bites szamot egy 8 bites architekturan (vagy ket 32 biteset egy 16 bitesen, vagy ket 64 biteset egy 32 bitesen, stb). Ha egyatalan meg lehet csinalni.

Ó, azok az egyszerű Forth processzorok ... semmi flag nem kellett neki, ment minden adatként a stackbe és az UNTIL meg döntött, hasonlóképpen ahogy a Brainfuck-nál A ] jel. :)

$ gforth
: teszt 1 BEGIN DUP . 1 + DUP 10 > UNTIL DROP ;
teszt

Itt a flag helyett a stack-be tolja az eredményt a '>' bármiféle flag helyett. Az UNTIL meg kiveszi a stackből az értéket és ha 0, akkor a PC-t visszaírja a BEGIN címére.
Brainfucknál éppen inverz, ott a nem 0 a visszaugrás előidézője.

Ha egy előjel nélküli számhoz hozzáadsz egy másikat és a végeredmény kisebb lesz, mint az eredeti szám, akkor átvitel történt, ehhez még nem feltétlen kell Carry flag, max. több lépcsőből rakod össze. Viszont, ahhoz, hogy megállapítsd valamiről, hogy kisebb-e, vagy nagyobb-e, mint valami más, ahhoz viszont már kelleni fog a Carry, hiszen a komparáció is lényegében kivonás, csak tárolás nélkül. A Zero flaggel csak az egyenlő, nem egyenlő vizsgálat végezhető el, a Carry nélkül egy CPU rettenetesen limitált lesz. Én nem is tudok olyat, amiben nincs; a két alap flag, aminek egy generikus processzorban lennie kell, az a Carry és a Zero. Nélkülük rémálom lesz programozni, mert nincs lehetőség a vizsgálatokra.

Hat, igen, akkor kb ugy tudsz osszehasonlitani csak hogy A-bol es B-bol elkezdesz levonni ciklusonkent egyet, amig mondjuk B el nem eri a 0-t (zero flag-gel). Es ha A nem erte el ezalatt a nullat, akkor A nagyobb mint B. Igy akkor mondjuk egy CMP 254,253 eltarthat mondjuk 1020+ oraciklusig is :) Valahogy igy:

    mov a, ...
    mov b, ...
    mov c, 0
cmp_loop:
    sub a, 1
    b.nz cmp_skip_set
    mov c, 1
cmp_skip_set:
    sub b, 1
    b.z cmp_out
    sub a, 0
    b.nz cmp_loop
cmp_out:
     ... 
; itt c az 1 ha a>b, egyebkent 0. vagy forditva :]

de meg az is lehet hogy egyszerubben is meg lehet csinalni, nem tudom :) 

Hogyne lehetne "egyszerűbben": bitshifttel és maszkokkal; először lecsekkolod, hogy nem egyenlő-e, ha igen, kiszállsz, különben pedig fentről lefelé elkezded kimaszkolni a biteket és ahol először különbséget találsz, ott az lesz a nagyobb szám, amelyikben az egyes volt. Hogy ez egyszerűbb-e? Az embernek megírni nem, de a CPU-nak futtatni igen, lévén így nem 1000 ciklusról beszélünk, csak pár tucatról. :)

Szóval, igen: Carry nélkül szopás az élet; a többi flag nélkül lehet élni (max nem lesznek előjeles változók/tiltható megszakítások, vagy nem egyszerűen), de a Zero és a Carry az musthave.

Hat, az azert mar furcsa lenne ha lenne bitshift de nem lenne carry ;) Bar ha az "onmagahoz hozzaadjuk" megoldasokat tekintjuk (mint ahogy pl AVR-en is van ugyan formalisan letezik az LSL rd, de ugye az ugyanaz mint az ADD rd, rd), akkor talan bitshift nelkul is megoldhato. Na, igy mar akkor csak bitmaszking kell!

Már önmagában a Carry hiánya is kb. "furcsa". :)
De igen, a balra bitshift az pótítható duplázással, csak egy itt a gond...itt jobbra kéne forgatni, hiszen fentről lefele megyünk. :)
Bár itt csak a bitmaszkot kell tologatni, azt pedig akár táblából is lehet olvasni.