SysCVideo: fiktív grafikus kártya SystemC modulként, SDL alapú megjelenítéssel Czirkos Zoltán 2015. augusztus 26. Kivonat Az ismertetett SystemC modul egy mikroprocesszoros rendszerhez illeszthető megjelenítő perifériaként működik szimulációban. 40 25-ös szöveges és ugyanekkora felbontású grafikus módot támogat (tetrishez, kukacos játékhoz ez épp elég.) Egy mikroprocesszoros rendszerhez könnyen illeszthető: 12 bites címbusszal, 8 bites adatbusszal rendelkezik, és a szokásos chip select, read enable, write enable és reset bemenetekkel. A megjelenítéshez az SDL grafikus könyvtárat használja. A modul a billentyűzet kezelését is segíti: az egyes billentyűk lenyomott állapota ellenőrizhető, illetve a gépelt szöveg pufferelődik, és karakterenként visszaolvasható. A modul használható önállóan is, függvényhívásokon keresztül. 1. Bevezetés A bemutatott SystemC modul arra való, hogy egy szimulált mikroprocesszoros rendszer perifériájaként működjön, lehetővé téve azt, hogy videókimenetet produkáljon a rendszer a szimuláció közben. Nem egy valós, létező perifériát utánoz, hanem kitalált vezérléssel rendelkezik. Szintetizálni nem lehet. A modul a következő képességekkel rendelkezik: 40 25-ös szöveges képernyő, amely 256 beépített karakterrel rendelkezik, és a VGA kártyákról ismert az IBM437-es kódlapot használja. Fehér alapon fekete betűkkel rajzol. A szöveges képernyő tartalma bájtonként elérhető, írható és olvasható. Kurzor is használható, amelynek pozíciója tetszőlegesen írható és olvasható. A szöveges képernyőre szöveg folytonosan, bájtonként írható. Ebben a módban a modul a kurzor pozícióját automatikusan változtatja, és görgeti is a képernyőt, ha az elérte annak alját. 1
1. ábra. Képernyőkép egy futó programról A 40 25-ös grafikus képernyő mérete megegyezik a szöveges képernyőével. A nagyított pixelek 256 különböző színnel rendelkezhetnek, amelyek a VGA palettát használják. A modul néhány paranccsal is rendelkezik (pl. képernyőtörlés.) A billentyűk lenyomva tartott állapota egyesével lekérdezhető. Rendelkezik továbbá egy karakter pufferrel, amelybe a lenyomott billentyűk kerülnek. Innen a lenyomott billentyűk kódjai egyesével visszaolvashatóak. A kártya a szimulációs idő szerinti 20 ms-onként, azaz szimulációs időbeli másodpercenként 50-szer rajzolja ki a képet. 2. A kártya illesztése A kártya vezérlő jelei a 2. ábrán láthatóak. Az egyes jelek magyarázata a következő: ADDRESS Címbusz, 12 bites bemenet. DATA Adatbusz, 8 bites ki- és bemenet. CSNEG Chip select bemenet, amellyel íráskor és olvasáskor a chip engedélyezhető. Aktív L szintű ez is. READNEG Read bemenet, olvasás engedélyezése. Ennek aktív szintjével a kártya regiszterei és memóriája olvashatók. WRITENEG Chip select bemenet, aktív L szinttel. Engedélyezésével a regiszterek és memória írhatóak. A kártya 12 bites címtartománnyal rendelkezik, azaz 4096 címezhető regisztere van. Illesztésére példát a 2. ábra mutat. 2
SysCVideo ADDRESS[0..11] DATA[0..7] CSNEG READNEG WRITENEG 2. ábra. A periféria vezérlő jelei CPU DATA[0..7] ADDRESS[0..15] A[12..15] SysCVideo DATA[0..7] ADDRESS[0..11] READNEG WRITENEG CSNEG READNEG WRITENEG 3. ábra. A periféria illesztése az 0xE000-0xEFFF címre. A SystemC programban a modult nem feltétlenül szükséges ilyen módon illeszteni; a regiszterir() és a regiszterolvas() metódusain keresztül közvetlenül is vezérelhető. 3
2.1. Reset jel A reset jel hatására szöveges módba vált az eszköz. Más inicializáció nem történik, és egyébként ez is elhagyható. Ha a jel aktív, akkor más jeleknek nincsen hatása. 2.2. Regiszter írása portműveletekkel Regiszterek írásához ezeket a lépéseket kell tenni: 1. A címbuszra kell tenni a kiválasztott regiszter vagy memóriaterület címét. 2. Engedélyezni, vagyis L szintbe kell állítani a chip select (CSNEG) jelet. (Ez általában a perifériaillesztés módja miatt automatikusan megtörténik.) 3. Az adatbuszra kell tenni az írandó értéket. 4. Engedélyezni kell a write enable (WRITENEG) jelet. A regiszterbe írás a WRITENEG jel lefutó élére történik meg. Ilyenkorra a CSNEG jelnek már aktívvá kell válnia, vagyis a két lefutás nem történhet időben egyszerre. Ezek után a vezérlőjelek visszavehetőek, és a buszokra tett értékekre már nincsen szükség. Például a 0x400 címre az 57 érték a következő SystemC kódrészlettel írható: address. write (0 x400 ) ; csneg. write ( 0 ) ; // chip s e l e c t = aktiv data. write ( 5 7 ) ; writeneg. write ( 0 ) ; // write enable aktiv writeneg. write ( 1 ) ; // write enable i n a k t i v csneg. write ( 1 ) ; 2.3. Regiszter olvasása portműveletekkel Az olvasás lépései: 1. A címbuszra kell tenni a kiválasztott regiszter vagy memóriaterület címét. 2. Engedélyezni kell a chip select (CSNEG) jelet. 3. Engedélyezni kell a read enable (READNEG) jelet, a CSNEG után nem nulla idővel. Amíg a READNEG jel aktív, a regiszterből kapott érték olvasható. Itt is figyelni kell arra, hogy a CSNEG és a READNEG aktívvá válása között kell valamekkora időnek eltelnie. Példaként a 0x400 cím olvasása: 4
address. write (0 x400 ) ; csneg. write ( 0 ) ; // chip s e l e c t = aktiv readneg. write ( 0 ) ; // read enable std : : cout << data. read ( ). to_uint ( ) ; // kepernyore readneg. write ( 1 ) ; csneg. write ( 1 ) ; 3. A kártya illesztés nélküli használata A modul használható önállóan is, szignálók bekötése nélkül. Ebben az esetben a SysCVideoStandalone osztályt kell használni, és a perifériaillesztési művelet teljes egészében elhagyható. A kommunikáció a regiszterir() és regiszterolvas() függvények segítségével működik: int sc_main ( int argc, char argv [ ] ) { SysCVideoStandalone scvs ; scvs. r e g i s z t e r i r ( SysCVideo : : ParancsRegCim, 0 ) ; / sz ö veges mód s e t / } /... / A SystemC-t ebben az esetben is a programhoz kell linkelni, de semmi teendő nincsen vele, mindent elintéz a SysCVideoStandalone osztály. 4. A regiszterek címei és használatuk A regiszterek címei a 4. táblázatban láthatóak. Használatuk: Parancs regiszter. Az ide írt bájtok parancsokként működnek, amelyeket a modul végrehajt. Ez a regiszter nem olvasható. Kurzor regiszterek. A kurzor pozíciója állítható be vagy olvasható vissza ezeken keresztül. Lehetséges értékeik X=0... 39 és Y=0... 24; a (0;0) pont a bal felső sarokban van. Karakter írás regiszter. Az ide írt karaktereket a modul az aktuális kurzorpozícióba írja, és a pozíciót balra, illetve szükség esetén lefelé növeli (mint egy C-s printf.) A képernyő aljára érve a görgetés is automatikusan történik. Az egyetlen értelmezett karakter a 10-es kódú sörtörés (C-ben \n ), amelynek hatására a modul új sort kezd. 5
cím név a kódban regiszter olv./írh. 0x000 ParancsRegCim parancs regiszter R 0x001 KurzorXRegCim kurzor x pozíció R/W 0x002 KurzorYRegCim kurzor y pozíció R/W 0x003 KarakterIrCim karakter írás regiszter W 0x004 BillentyuPufCim billentyűzet puffer LSB R 0x005 BillentyuPuf256Cim billentyűzet puffer MSB R 0x100-0x128 BillentyuMatrCim billentyű nyomvatartás R 0x400-0x7E8 SzovegesCim szöveges memória R/W 0x800-0xBE8 GrafikusCim grafikus memória R/W 1. táblázat. A regiszterek címei 4. ábra. Az IBM 437-es kódtábla és a VGA paletta 6
Billentyűzet puffer. Az LSB-t olvasva a billentyűzet pufferben eltárolt következő karaktert kapjuk meg. Ha nem volt lenyomott karakter, akkor 0-t ad a regiszter. A billentyűk kódolása az SDL keysym-eknek megfelelő ez Linux rendszereken általában az /usr/include/sdl/sdl_keysym.h fájlban megtalálható, de a legtöbb kód úgy van megválasztva, hogy az ASCII kódoláshoz illeszkedjen. Mivel a vezérlőgombok (nyilak stb.) kódolása 255 feletti értékeket is használ, azok nem férnek el egy bájtban: az LSB regiszter olvasása után a felső bitek az MSB regiszterbe kerülnek, és onnan olvashatóak ki. Billentyű nyomvatartás. A 40 bájtos memóriaterület bitjei az egyes billentyűk lenyomva tartott állapotát mutatják, ahol 1-es bit jelenti a nyomva tartott gombot. Egy adott gombhoz a KEYSYM/8. bájt KEYSYM%8. bitje tartozik, pl. az A billentyűhöz tartozó SDLK_a kódja 93, így a 11. bájt alulról 5. bitje tárolja a hozzá tartozó állapotot. Szöveges memória. A karakteres képernyőn tárolt jelek kódjai, a a 4. ábrának megfelelően. Grafikus memória. A grafikus memória pixeljei a 4. ábra színeinek megfelelően. 4.1. A parancsregiszter A 0x000 (ParancsRegCim) című regiszterbe írt bájtokkal különféle parancsok adhatók az eszköznek: 0 Váltás szöveges módba. 1 Váltás grafikus módba. 2 A szöveges képernyő törlése (szóközökkel feltöltése). 3 A kurzor bal felső sarokba állítása, a (0;0) koordinátába. 4 Grafikus képernyő törlése (feketével feltöltése). 4.2. Szöveg kiírása: helló világ Az alábbi, hosszabb példában egy szöveget írunk ki a képernyőre, a bal felső sarokba. A szöveg kiírása a karakter író regiszteren keresztül történik, így nincsen szükség a képernyő koordináták számítására. A regiszter minden egyes írásakor egy új karakter jelenik meg a képernyőn. csneg. write ( 0 ) ; // c r s r home parancs address. write ( SysCVideo : : ParancsRegCim ) ; data. write ( 3 ) ; 7
writeneg. write ( 0 ) ; writeneg. write ( 1 ) ; char const s t r =" Hello v i l a g! \ nez egy uj sor. " ; for ( int i =0; s t r [ i ]! = 0 ; ++ i ) { address. write ( SysCVideo : : KarakterIrCim ) ; data. write ( s t r [ i ] ) ; writeneg. write ( 0 ) ; writeneg. write ( 1 ) ; } 4.3. Regiszter írása és olvasása metódushívással A regisztereket nem feltétlenül kell portműveleteken keresztül elérni. Ha nem szükséges ilyen alacsony szintű modell, akkor a SyscVideo modul regiszterir() és regiszterolvas() tagfüggvényei közvetlenül is hívhatóak: SC_MODULE( SysCVideo ) { unsigned char r e g i s z t e r o l v a s ( unsigned address ) ; void r e g i s z t e r i r ( unsigned address, unsigned char data ) ; } ; SysCVideo video ( " video " ) ; video. r e g i s z t e r i r (0 x004, 0x12 ) ; Arra figyelni kell, hogy hasonlóan ahhoz, mint amikor portműveletekkel ér el adatot a processzor, ezek a metódushívások sem idempotensek. Több egymás utáni írás vagy olvasás más eredményt adhat, pl. a karaktert író vagy a billentyűt olvasó parancsregiszter esetén. 8