Még mindig C, beágyazva

Szó se róla, már elég régóta emlegetem, hogy kellene C-t tanulnom. Annyira azért nem kapkodom el a dolgokat, de jobb később, mint még később. A „tanulást” jelen esetben több tényező is akadályozza (a lustaságon kívül), például ilyenek, hogy a különböző típusú, nagy példányszámban használt operációs rendszerekre való programírás sose vonzott igazán. Azt szeretem, ha a vasat közvetlenül lehet „birizgálni”, nem rengeteg mindenféle rétegen keresztül.

(Erre írtam egy buta hasonlatot, de inkább mégse hagyom itt...) Aztán tanulni egy megfelelően egyszerű / bonyolult feladatot megoldva a legegyszerűbb, de ilyet nem mindig könnyű találni. Most viszont van egy megfelelőnek ítélt probléma, de ez történet (szokásomhoz híven?) nem erről fog szólni.

Kelleni fog viszont telepítenem valami fordítót. (Az eddigi(ek) nem lesz(nek) jó(k), erről hamarosan.) Csak úgy kíváncsiságból ránéztem a partíciók szabad területeire:

Hát, nem túl meggyőző a látvány. (Sőt, ha még azt is hozzáveszem, hogy ez már a /root alatti takarítás utáni állapot, letöröltem pár nem kellő install csomagot. Az Use% alatt 99% szerepelt eredetileg...) Lassan jöhet egy újrainstallálás? Akkor már talán a 7-es CentOS-t lesz célszerű megpróbálni... GRUB2? Jaj... Partíciók átméretezése? Ajjaj... Minden cucc újra felrakása / tesztelése? Ajjajjaj! :-/ Na, ez nem most lesz. :) Na de mi „zabál” itt ennyit? A fő mumust az /opt alatt kell keresni:

A „megfelelő” könyvtárat egy szomszédos partícióra átrakva (az /opt alatt egy symlinkkel), egyből lett pár szabad BYTE:

Nahát... Minő meglepetés. (Ez a program hogy lett ekkora bloatware, azt nem tudom. Az első verzió installja volt vagy 48 MBYTE.)

Na de vissza a témára... A kívánt feladatot (retró, ha elkészül, majd írok róla) – természetesen – egy mikrovezérlővel tudom megoldani, de a sokszor használt M88 készletemből kezdek kifogyni, meg az most I/O-ra kevés is. Mivel maga a feladat amúgy is „recycled material” segítségével készül (kukából előszedettek a hozzávalók, csak így szebben hangzik), legyen a µC is az. A megfelelő méretű lábasjószágot egy bontott PIC16F877 személyében találtam a fiókban, van belőle vagy két csővel. (Kísérletezhetek bátran! :) ) De vannak – természetesen – problémák. Egy AVR-hez képest a („14 bites”¹) PIC eléggé gyér architektúra terén. (Jöhetnek a beszólások a PIC fanatikusoktól. :) ) Nem épp ideális egy „magas szintű” nyelvhez, azért C fordító (erős megkötésekkel) létezik rá, de eddig ilyet még nem használtam. Mik a lehetőségek? Egyrészt ott az „eredeti”, MPLAB XC8 fordító a Microchip-től, de az „túl egyszerű” lenne. (Meg nem teljesen free, meg ilyenek.) A többi ismert C fordítóról most inkább hallgatok, amúgy is inkább Windows™-os verziójuk van csak, az meg az esetemben nem játszik.

(¹: A bitszám nem véletlenül van idézőjelben. A PIC ugyanúgy mint az AVR, Harvard architektúrás. Ez a Neumann architektúrától abban tér el, hogy a program illetve az adatmemória két külön buszon van. Ez programozás oldalról abban nyilvánul meg, hogy a futó program nem tud (azokkal az utasításokkal) olvasni a programmemóriából (mint az adatmemóriából), illetve az adatmemóriából nem tud programot futtatni. Mivel a két memória külön buszon van, így a „szélességük” sem szükségszerűen ugyanakkora. A „14 bites” PIC esetében a programmemória 14 bit szélességű „valamikből” áll (Van ennek neve? :) ), a régebbiek 12 bitesek, az újabbak meg 16 bitesek. Ettől függetlenül mindegyik core 8 bites architektúra, mert az adatok, amivel egy lépésben tudnak dolgozni, maximum ekkorák. A 16F877 14 bit széles utasításokkal dolgozik, ezért kellene most nekem a „14 bites” PIC támogatás.)

Ami fordító most érdekes lehet, az nem más mint az SDCC, ennek is van – igaz, egyelőre erősen fejlesztés alatt álló – „14 bites” PIC támogatása (sok egyéb architektúra mellett). Hogy mire lesz elég, majd kiderül.

Az SDCC ugyan megtalálható az EPEL repóban, de az a 3.2.0-ás verzió, ami „elég régi”, 2012 közepén jelent meg. (Puskázni viszont jó lesz belőle. A csomagokat installálás nélkül is le lehet tölteni. A yum-utils csomagban található a yumdownloader nevű túl, a yumdownloader sdcc paranccsal a feltelepíthető RPM csomag, a yumdownloader -- source sdcc paranccsal meg a forrás RPM tölthető le. Az utóbbihoz természetesen engedélyezni kell a forrás-repó elérését a megfelelő helyen.) A mostani aktuális a 3.4.0, de ez is 2014 áprilisi, az sem „friss”, „ropogós”. Az SDCC oldala valamerre azt írja, ha az utolsó kiadás régebbi mint két hónap, akkor célszerű a napi snapshotot megpróbálni. Ugyan nem vagyok „mindenből a legújabb” mániás, de ez esetben kivételt teszek: mivel a PIC támogatás egyelőre kezdetleges, talán azon a részen érdemes leginkább javulást várni az újabb verzióktól. Szóval a terv megszületett: fordítsunk SDCC-t! (Ez remélem nem lesz olyan kálvária, mint a GCC-s küzdés! Itt van azért egy lényeges különbség: az SDCC lefordított állapotban, „egy fordítóként” támogatja az összes általa ismert architektúrát. A GCC „forrás szinten” teszi ugyanezt, ott olyan verziót kell belőle forgatni, amilyen architektúrájú fordítót akarok.)

Első lépésben kelleni fog a forrás:

$ mkdir SDCC-svn
$ cd SDCC-svn
$ svn checkout svn://svn.code.sf.net/p/sdcc/code/trunk/sdcc sdcc

Az utolsó parancs az aktuális könyvtárban készít egy sdcc alkönyvtárat, majd oda szépen „lehúzza” az aktuális állapotot. (Checked out revision 9068.) A szintaxis az oldalról van, egyelőre a verziókezelés – úgy általánosságban – kimaradt az életemből, pedig többször jól jött volna már. (Már megint a tanulás... :) Amúgy az svn-es verzió természetesen tartogathat meglepetéseket, az sem biztos, hogy egyáltalán le lehet fordítani!) Tanulva a GCC-s küzdésből, gyorsan készítek is a könyvtárról egy másolatot:

$ cp -r sdcc sdcc-save

Majd jöhet a (szokásos) fordítás! Vagy legalábbis az előkészület:

$ cd sdcc
$ ./configure

Első körben a configure néhányszor elakadt ilyen-olyan hiányokra hivatkozva, de szerencsére most mindenből van megfelelő verzió a repókban (alap CentOS + EPEL elég, ha minden igaz). Egy

# yum install bison flex boost boost-devel

parancs rootként kiadva felrakja a szükséges csomagokat (ami még kellhet, az nálam már fenn volt), így a configure már lefut szépen. Viszont van egy szép figyelmeztetés:

configure: WARNING: 
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!!! 
!!! WARNING: The installed gputils do not support all 
!!! PIC devices currently supported by SDCC. 
!!! If you continue to build SDCC, library files for the 
!!! following devices will not be built, and you will 
!!! not be able to compile any projects for these devices: 
!!! 
 
 12f1571 12f1572 12f1612 16f1613 16f1703 16f1704 16f1705
 16f1707 16f1708 16f1709 16f1713 16f1716 16f1717 16f1718
 16f1719 16f1788 16f1789 16f753 16hv753 16lf1554 16lf1559
 16lf1704 16lf1708 
==> 23 devices are *not* supported 
    156 devices are supported 

!!! 
!!! Please update your gputils to a recent snapshot and 
!!! run configure again using the updated gputils. Make 
!!! sure to have them in PATH prior to the previously 
!!! found ones (or remove the older version completely). 
!!! 
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 

Ezek szerint a gputils csomag kell a stuffnak, ami föl is van telepítve. (Minő meglepetés...) Viszont „régi” verzió az is (Ez is minő meglepetés...), a fenti 23 eszközt még nem támogatja. Forgassak abból is újat? Végignézve a listát, azt hiszem ezt ki is hagyhatom, egyik fajtára sem akarok MOST fejleszteni. De azért ezt nem árt nem elfelejteni... :)

A Makefile létrejött, jöjjön a fordítás! Most majd kiderül, hogy az aktuális állapot egyáltalán le tud-e fordulni:

$ make

Hát, elvan egy darabig... (Úgy nagyjából már van sejtésem arról, hogy minek is kell egy-egy nagyobb disztribúciónak komplett build-szerver park, azért ~sok ezer csomagot lefordítani nem két perc.) De lefordul. A bin könyvtárba bekerült egy halom program, köztük az sdcc is:

$ bin/sdcc --version 
SDCC : mcs51/z80/z180/r2k/r3ka/gbz80/tlcs90/ds390/pic16/pic14/
TININative/ds400/hc08/s08/stm8 3.4.1 #9068 (Sep  8 2014) (Linux)
published under GNU General Public License (GPL)

Kiváló! Már „csak” az install maradt hátra, de ez még megér egy kis gondolkodást. Természetesen – ha már itt tartunk – jó lenne belőle saját csomag. Az EPEL-es verzió elég érdekes; az /usr/libexec/sdcc alá kerülnek a futtatható fájlok, az /usr/bin alá meg symlinkek kerülnek sdcc- prefixszel. Ott a magyarázat annyi, hogy az SDCC-nek „túl általános” programnevei vannak. Hm... A saját /usr/bin könyvtáramban nem találtam még csak hasonlóakat se, emiatt most nem aggódok, maradok az „eredeti” struktúránál. Aztán a cucc használja a libiberty-t, de úgy tűnik, hogy statikusan linkeli. Viszont a make install felrakná az /usr/lib64-be a libiberty.a-t. Ez egy archív fájl, belecsomagolva sok „ojjektum”. Ha jól sejtem ez nem fog kelleni a statikus linkelésnek hála, az EPEL-es csomagban sincs benn. (De egyik libbel sem kellene a rendszerben levőket felülcsapni, erre oda kell figyelni.)

Jöhet a „csomagolás”:

$ cd ..
$ mv sdcc-save sdcc-3.4.1svn9068
$ tar -cvjf sdcc-3.4.1svn9068.tar.bz2 sdcc-3.4.1svn9068
$ mv sdcc-3.4.1svn9068.tar.bz2 ~/RPMsandbox/SOURCES

Az elsőként létrehozott „mentés” átnevezve az aktuális verziószámnak megfelelően, betömörítve, majd áthelyezve az RPM „homokozójába”. Jöhet a csomagkezelőnek szánt spec fájl létrehozása:

$ cd ~/RPMsandbox/SPECS
$ rpmdev-newspec sdcc.spec

A létrejött sdcc.spec fájt a kedvenc editorral megnyitva, a megfelelő részeket ki kell tölteni. Ami fontos:

Name: sdcc
Version: 3.4.1svn9068
Release: 1%{?dist}
Summary: SDCC is a retargettable, optimizing ANSI - C compiler suite
Group: Development Tools 
License: GPL 
URL: http://sdcc.sourceforge.net/ 
Source0: sdcc-3.4.1svn9068.tar.bz2 
BuildRequires:  bison flex boost boost-devel 
Requires:       gputils >= 0.14.3

Név, verziószám (hozzáadva az svn-es „sorszám”), „kiadási” szám, összegzés, csoport (csomagcsoport, mihez tartozik), licenc, honlap. Eddig semmi extra. Aztán kell a forrás, ez az a név, amilyen néven össze lett tömörítve a letöltött stuff, ezen a néven keresi a ~/RPMsandbox/SOURCES könyvtárban. (Most legalábbis... Itt meg lehetne adni URL-t is, akkor onnan töltené (?) automatikusan, de ezt még nem próbáltam sose.) Utána jön két függőségi sor, a build-hoz illetve magához a telepítéshez. Ilyeneket se adtam meg eddig... :) (Némi puskázás itt is van az EPEL-es csomagból. A gputils minimum verziószáma a jelenleginek van beállítva, ebből csak azon eszközök támogatása hiányzik, amik most nem is fordultak be.)

Ezeken felül a %description alá jöhet egy „hosszabb” leírás (a honlapról idemásolva), majd a „maradék”:

%files 
%defattr(-,root,root,-) 
%doc 
%{_bindir}/* 
%{_datadir}/* 
%exclude %{_libdir}/ 

%changelog 
* Mon Sep 8 2014 balagesz 
- Initial RPM release 

A %files szekció is némi „puska” eredménye, a %{_bindir}/* illetve %{_datadir}/* részek berakják a csomagba a lényeget. Az %exclude %{_libdir}/ sor meg kihagyja a libeket a csomagból, hogy azokkal ne írja majd felül az installáció a rendszerben levőket. (Ezek természetesen NEM azok, amik majd a használat során a „kis eszközökhöz” kellenek.) Egy kissé „kellemesebb” megoldás ez így, mint amit az asl csomagolásánál sikerült elkövetni... :)

A maradék (%changelog ) rész egyértelmű, ezenfelül a spec fájlban más módosítás nem is kell. Jöhet a csomaggenerálás:

$ rpmbuild -bb sdcc.spec

Ez némi meló (nálam úgy 6-7 perc...) után el is készíti a „kedvenc” RPM-ünket a ~/RPMsandbox/RPMS/x86_64 alá. Ezt célszerű leellenőrizni, hogy mi került bele, ehhez az

$ rpm2cpio sdcc-3.4.1svn9068-1.el6.x86_64.rpm | cpio -idmv

parancs jól is fog jönni. És igen, a kívánt fájlokat tartalmazza a csomag, „csak” az installálás van hátra root-ként:

# yum install sdcc-3.4.1svn9068-1.el6.x86_64.rpm

„Szép” végeredmény, 160 MBYTE egy fordítónak? (Mennnyiiii??? :) Az első saját HDD-m 40 MBYTE volt. :) Na, erre kellett az a hely.)

No, jöhet a próba! Egy egyszerű Hello World példa, aminek a neve legyen most test-16f877.c:

#include <pic16f877.h>

// Config Bits:
static unsigned int __at(_CONFIG) _conf = _FOSC_HS & _WDT_OFF & _LVP_OFF & _BOREN_OFF & _CP_OFF;

void main(void) {
  ADCON1 = 0x06;	// PORTA = digital I/O, analog inputs disabled
  TRISB = 0xfe;		// PB0 = OUTPUT
  while (1) {
    if ((PORTB & 0x02) != 0) {
      PORTB &= 0xfe;
    } else {
      PORTB |= 0x01;
    }
  }
}

Ez annyit csinál, hogy a PB0-ás vonalat kimenetre állítja, majd a PB1 állapotát megfordítva kirakja rá egy végtelen ciklus keretében. (A kimenetre egy LED kell előtét-ellenállással. A bemenet meg ha magas majd alacsony lesz, akkor a LED-nek kell kapcsolódnia szépen. Minimalista HW alkalmazás, de tesztelhető vele, hogy jól sikerül-e a tok programozása, például.) Már csak le kell fordítani:

$ sdcc -mpic14 -p16f877 --use-non-free test-16f877.c

Nem magyarázza túl, hogy mi történt, egy -V kapcsoló jóval beszédesebbé teszi a fordítás folyamatát, de a lényeg, hogy fordul! Azaz az esetünkben inkább: fordít! :) A végeredménybe „belenézve” akár még működhet is. (Mondjuk „furcsán hosszú” a kapott .hex fájl... Debug infó van benne? Ennek majd érdemes lesz utánajárni.)

Van még egy „feladat”, ami ehhez kapcsolódik. A Microchip-nek van egy fejlesztő környezete, az MPLAB® X. Ez egy NetBeans IDE-re épülő eszköz, ami szerencsére használható Linux alatt is. Az SDCC-t az „alá” is be lehet / kellene üzemelni, mert csak kényelmesebb az alól kezelni az eszközöket. Első lépésként meg kell „mutatni” neki, hogy van SDCC. Tools/Plugins/Available Plugins menüpont:

Kijelölés, „Install” majd némi licenc elfogadás után meg is van, már csak egy újraindítás van hátra. :) (Szerencsére csak az IDE részéről.) Utána már lehet is próbálni... Egy projekt létrehozva + belemásolva a fenti tesztkód, majd fordítás:

Egyrészt az editor rész „sír”, mert nem látja az inklúdot (De hogy miért... A Header files alatt „kézzel” megmutattam neki, így már csak pár dolgot mond hibásnak, de ha nagyon akarja, azokról is van eredmény, szóval...), másrészt az SDCC is nyavalyog egy kicsit holmi ismeretlen paraméterek miatt, de a lényeg: lefordul! (A tok felprogramozása még hátra van, remélem működik is, nem csak forog...) Ebből akár még valami kódolásféle is kisülhet... (Hogy mennyire „experimental” az SDCC PIC támogatása, az fogja eldönteni, hogy össze áll-e majd a C-ben készülendő verzió. Assemblyben nem akarnék újra PIC-et programozni, de ha „muszáj” lesz, azért nem szaladok világgá... Remélem a legjobbakat.)

Linkek:

balagesz

---

2014.09.08.
2019.12.14. D8.

Hozzászólások

Szerintem a gputils csomagban van PIC-re C fordító. A 14 bites valamit nyugodtan nevezd 14 bites szónak. Én még úgy tanultam, hogy szűkebb értelemben a szó 16 bitet takar, de általánosságban a bárhány bites izét szónak nevezzük. (Olyan ez, mint a Linux. Szűkebb értelemben a kernel, de tágabb értelmezésben egy jellemzően GNU/Linux operációs rendszert, terjesztést is jelent.) Ezek a PIC-ek 8 bitesek, hiszen 8 bites fileregisztereken operálnak az utasítások. Az egy dolog, hogy az op. kód és az operandus ugyanazzal a ciklussal fetch-elődik, így 14 bites szón van tárolva. Ugyanakkor ez Neumann architektúra esetén két ciklus lenne byte-os szervezésben.

Mondtad, C gyakorlás volt a cél, arra viszont maga a Linux szerintem alkalmasabb lett volna. PIC-re eddig kizárólag assembly-ben programoztam, eszembe nem jutott másként.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

A gputils oldala nem említ C fordítót, legalábbis nem látom. A 14 bites "valamit" a MC doksik is "szónak" mondják, csak az számomra olyan - hogy is mondjam - furcsa. :) A szervezés mögötti logikát értem, azzal nincs is bajom. (Mással sincs... A PIC architektúrája maga, na az más téma. :) )

Gyakorlás volna a cél, igen. (Csak jussak el odáig...) Viszont a Linuxot itt nem értem, milyen összefüggésben került ide. Magán a Linux kernelen gyakoroljak C-t? (Az azért elég nagy falat lenne...) Vagy Linux alatt próbálkozzak valami C program készítésével? (Az meg igazából nem mozgat.) A mikrovezérlős feladat azért jó, mert az asm-jét ismerem, a fordító által generált assembly forrást "elemezve" meg legalább látom, hogy milyen C kód mire is fordul.

Megnéztem, s valóban rosszul emlékeztem, a gputils-ban nincs C fordító. Mondjuk nekem nem is hiányzik, én assembly-ben szeretem piszkálni a PIC-eket. :)

Amikor a C tanulására utaltam, nem a kernelre gondoltam, hanem valami programra GNU/Linuxra. Nekem egyszer volt olyan problémám, hogy PIC-hez illesztett 1 MiB memóriába kellett egyszerűen adatot tölteni. Úgy oldottam meg, hogy a PIC-be írtam FSK vevőt, Linuxra C-ben pedig egy programot, ami az adatfile-ból előállított egy FSK hangfile-t, amit aztán lejátszottam a PIC-nek. Szóval nekem itt volt motivációm arra, hogy előállítsak egy kb. 100 MB-os wav file-t. Bash-ben, de még awk-ban is ez már igen lassú volt.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Viszonylag régen kódoltam kifejezetten PIC-re, de én is assembly-ben toltam, természetesen. Magát az architektúrát igazából nem sikerült megkedvelni. (Azt azért a védelmében megjegyezném, hogy a "14 bites" verzió ezerszer jobb mint az ős "12 bites", ahhoz - egy kis kerülővel ugyan, de - bőven volt szerencsém.)

A számítógépre, OS alá kódolási igényeimet eddig tökéletesen kielégítette (régebben) a Pascal, illetve (újabban) a Python. Meg amúgy is rengeteg kiváló (...) kóder van "ide", inkább maradok a hardverközelibb oldalon. :)