Nasobicka ARM-1 nepracovala 'paralelne' se zbytkem chipu (ARM-1 neni superskalarni). Vlastne vyuzivala jiz pritomnou ALU, tedy jeji scitacku a barrel shifter a nejaky sekvencni obvod tyto ridil tak aby realizovaly Boothuv algoritmus nasobeni (neco jako pisemne nasobeni, ale po 2 bitech naraz (vyuziva se toho ze x*3 je vlastne (x<<2)-x)) takze nasobeni trvalo az 16 taktu kdyz byly bity ve druhem ciniteli zaplnene az do pozice 31. Dokonce i novejsi jadro ARM7TDMI nasobi sekvencne ale uz ma v sobe asi nejake tabulky na nasobeni 32bitu*8bitu, takze nasobeni trva 2 az 5 taktu (podle obsazenosti druheho cinitele), MAC (multiply-and-accumulate) jeste o takt vic.
Problem je ze tato cinnost neni pipelinovana (jako na novejsich CPU), takze program nemuze do nasobicky kazdy takt nacpat nova data s tim ze je bude potrebovat az za 5 taktu -- tady se musi vzdy cekat -- scitacka je jen jedna a pouziva se u temer vsech instrukci, dokonce i pri adresovani -- load a store instrukce umoznuji pricist k bazove adrese registr a ten (volitelne) inkrementovat -- to vse se provede jedinou instrukci behem nacitani dat z pameti. I relativni skok pouziva scitacku z ALU.
Trochu je to videt z nakresu jednotek a sbernic pres ktere si muzou posilat data:
http://en.wikipedia.org/wiki/File:Diagrama_ARM7.jpg
Jejich filosofii pri navrhu bylo aby pokud mozno kazda cast chipu byla v kazdy okamzik k necemu vyuzita. To nevede k nejrychlejsimu chipu -- treba dedikovana pipelinovana nasobicka jde proti teto filosofii -- ale vede to k malemu poctu transistoru a male spotrebe.
Taky je to dusledek toho ze ARM je dilem 2 lidi (podobne jako puvodni UNIX) a ne armady inzenyru, kde jedno oddeleni dela dekoder instrukci, dalsi scitacku, dalsi adresovou jednotku, dalsi jeste neco jineho a nad tim bdi system integrator, ktery jim definuje protokoly pomoci kterych si ty jednotku budou povidat. A i kdyz se pak zjisti ze implementovali tutez vec 2*, vetsinou uz se s tim neda nic delat.
Jinak ARM ma opravdu velmi malou spotrebu, nekde na netu se da najit video kde Steve Furber vypravi jak na jednom z prvnich chipu chtel merit spotrebu, mel ho pripojeny na sbernici 8-bitoveho BBC-micro, na kterem delali vyvoj a nechal si na obrazovku v nekonecnem cyklu neco vypisovat. Pak chtel ARM odpojit od napajeni aby tam vlozil ampermetr a zjistil ze i bez napajeciho napeti pocita dal. Nakonec se to vysvetlilo --- bylo to tim ze na sbernici byly nejake bity v log. 1 a pres ochranou diodu na pinech ARMu se to napeti dostalo na Vcc a odebirany proud byl tak maly, ze sbernice 'utahla' cely procesor.
Pekny bylo taky jak to vyvijeli --- Steve Furber mel na starosti obvodovou realizaci a Sophie (tenkrat jeste Rodger) Wilson vymyslel instrukcni soubor a napsal v tom svem BASICu simulator (myslim ze na urovni hradel ale uz si to moc nepamatuju). Takze na 8mibitovych pocitachich BBC micro vyvinuli 32bitove. Krome nich na tom pracovalo tusim 30 dalsich lidi z Acornu a nejaci inzenyri z VLSI technologies, kteri meli na starosti fyzickou vyrobu (2 vrstva CMOS, 3um sirka cary). Furber taky vypravel ze meli velke problemy tak maly chip pripojit k tehdejsim standardizovanym pouzdrum a tak dlouhe bondovaci dratky pak vypadaly divne.
Uplne jim takovy vyvoj zavidim, museli si to pekne uzit (od zacatku do vyroby prototypu to stihli za 18 mesicu).
V clanku jeste nesouhlasi pocet registru. Urcite jich neni 37. Najednou je pristupno 16 registru, preruseni a ruzne vyjimky pak maji vlastni link-register a stack-pointer. Myslim ze to bude neco kolem 26 registru, protoze i ARM7 ma dohromady 31 registru.
Tady je mozne se podivat na prototyp: http://acorn.chriswhy.co.uk/Computers/A500.html
Doly za doplneni. Jen trosku opravim, ze tu nasobicku popisujes u ARMv2, ne ARM 1 ne?
Add pocet registru: ono jich je skutecne hodne, i kdyz to pri praci v user spacu programatorovi nepripadne, protoze tam jich vidi 16, vlastne 17 (i s CPSR). Jak se postupne pridavaly dalsi rezimy prace procesoru, tak nakonec mame:
r0-r15 v user rezimu
r8_fiq - r14_fiq pro fast interrupt rezim (registry navic, r0-r7 jsou klasicke z user rezimu)
r13_* - r14_* pro dalsi tri/ctyri rezimy (r0-r12 jsou klasicke z user rezimu)
+ CPSR + 5x SPSR_* pro dalsi tri/ctyri rezimy (flags)
Ano myslel jsem ARM-2, ARM-1 nasobicku nemel, nejak jsem se prepsal. Dik za opravu. Mozna dokonce ten Boothuv algoritmus meli az u ARM-3 a ARM-2 nasobil 'pisemne', uz si nejsem uplne jistej.
K tem registrum: 26bitove verze ARMu coz ARM2 byl, nemely jeste CPSR, stav byl soucasti registru 15, coz byl instruction pointer v jehoz nekterych bitech byly flagy a stav procesoru. IMHO to bylo roztomile reseni, protoze pri navratu z preruseni (i z podprogramu) se automaticky obnovil stav procesoru, jen to melo nevyhodu ze kod nemohl byt delsi nez 2^26 bytu a nedaly se pridavat nove flagy (coz se pozdeji stalo kdyz zavedli saturacni aritmetiku chteli mit flag ze doslo k saturaci).
Takze pak zavedli CPSR a SPSR registry aby mohli r15 prodlouzit, cimz to trochu zprasili, protoze pak musela instrukce rozlisovat jestli se tez obnovuje stav nebo ne, a prepnuti kontextu se zmenilo z jednoduche LDM/STM sekvence na nocni muru (ovsem v porovnani s tim co dokazal vymyslet v podobnych pripadech Intel je to stale prochazka ruzovym sadem, jen uz to postrada puvodni eleganci).
r8 a r9 ve FIQ modu maji az novejsi ARMy, napr. ARM7. ARM2 je nemel.
Takze (r0-r15) + (r13-r14)*3 + (r10-r14) = 16 + 3*2 + 5 = 27
Jasne, kdyz mluvime o ARM2, tak tam bylo registru min. Ja jsem ty tri posledni kapitoly v clanku uz psal (pro zjednoduseni) pro 32bitove ARMy obecne, proto taky zminka o Thumb, Thumb2 atd.
Kdyz o tom tak premyslim, tak v ARM rezimu (!Thumb) je vlastne PC stale nevyuzity, protoze nejnizsi dva bity musi byt nulove. Vyuziva se to nekde? (v LISPu na tagy atd.?)
Ja myslim ze ne. Kdyz bych v ARM rezimu skocil na adresu nedelitenou 4ma tak dojde k prefetch abort exception -- sice teoreticky bych se z ni mohl nejak vratit, ale na prakticke vyuziti by to bylo prilis pomale. Ja ve svem OS tyhle vyjimky akorat chytim a vypisu posmrtnou zpravu na ktere adrese k tomu doslo a resetuju CPU.
Jeste dodam, ze ten rozdil mezi vysledkem prace nekolika lidi vs. velkeho vyvojoveho tymu (jeste vetsinou rozstrkaneho po celem svete) je taky videt napriklad na navrhu ruznych protokolu nebo programovacich jazyku (asi netreba jmenovat jeden jazyk, ktery dnes "rozsiruje" velke konsorcium, v minulosti to potkalo treba COBOL a castecne i Fortran).
Přejmějšlím, jestli je možný, aby se jedna a ta samá sčítačka používala u výpočtu adres pro load/store, pro relativní skoky a i pro sčítání jako ALU operaci. Musím doma vyhrabat knížku, kde je kolik taktů která instrukce na ARM2 trvá, ale to by load/store a skoky nemohly trvat jeden takt, ne?
I blbá 8086 měla zvláštní sčítačku na sčítání segmentu a offsetu, 80186 měla další zvláštní sčítačku i na výpočet offsetu z báze nebo indexu.
U osmibitovych CPU byla jen jedna scitacka na vsechno, ale tyto procesory zase vetsinou nemely zretezenou pipeline, popr. ne do te hloubky jako klasicke RISCy :-)
Napriklad velky rozdil mezi MOS 6502 a Motorolou 6800 byl (mj.) i ten, ze MOS 6502 byl little endian, coz znamenalo, ze se u instrukci s absolutni adresou (+index) mohla zacit pocitat realna adresu jiz u druheho bajtu instrukce (=dolni bajt adresy) zatimco se nacital bajt vyssi, ktery uz se mohl lisit jen max. o 1.
U big endian procesoru, navic s 16bitovym index registrem, se ztracely takty ;-)
To by asi řešila 16bitová sčítačka. Nebo čtení instrukce v pořadí první - třetí - druhej bajt :-)
Ne, pořád po těch letech nemůžu přijít little endianu na jméno. A to všechno kvůli sériovejm pamětem u Datapointu 2200. I Stan Mazor přiznal, že to byl omyl: http://silicongenesis.stanford.edu/transcripts/mazor.htm
Ja zase little-endian povazuju za prirozene poradi. Asi je to tim ze jsem zacinal na 6502 a pokracoval na Intelu. I ten ARM je little endian (protoze jeho tvurci sami priznavaji ze chteli neco jako 6502, tak rychleho aby programy v BASICu na tom bezely tak rychle jako rucne optimalizovany assembler na 6502). Pozdeji ho upravili na bi-endian, takze se to da nejak prepnout. Uprimne jsem to ale nikde nevidel v big-endian konfiguraci pouzivat.
Jinak 16-bitova scitacka v situaci kdy stejne musim nacitat data po 8mi bitech by bylo plytvani transistory.
Já začínal na 8080 a Z80 a přišlo mi to nepříjemný. Pak jsem přešel na 68000 a big endian mi přišel skvělej. Otázkou samozřejmě je, že kdyby kvůli kontraktu s Datapointem nebyla 8080 little endian, jestli by si u 6502 ten little endian lajsli, když by dvě hlavní konkurence byly big endian. A 8086 by následovala 8080, ARM by následoval 6502. No to je fuk, dneska s tím už asi nic nenaděláme.
Samozrejme to nevim jiste, ale mam dojem, ze u osmibitovych mikroprocesoru to az tak neresili, kdyz jedine, co bylo 16bitove (tedy kde se rozlisoval endianess) byla adresa a nikoli operandy, ty si kazdej mohl ukladat jak chtel.
Proste se vybralo reseni, ktere danemu soudruhovi vyhovovalo, ale asi s tim nedelali zadny vyzkum (stejne se jeste poradne nevedelo, na co vsechno muze byt takovy CPU dobry :-)
Snadnější čtení kódu.
Snadnější odhalení chyb se špatným typem. Pokud mám v paměti 0x18 0x10 0x00 0x00 při little endian a spletu si int32 a int16, tak mi to hodí stejnou hodnotu.
Taky je votravný porovnávat nějaký signatury a vobracet kvůli tomu řetězce. Třeba 0xBEBAFECA místo CAFEBABE :-)
To uz byste rovnou mohl chtit zobrazovat cisla v BCD kodu aby se to snadneji cetlo.
Me to prijde snadnejsi presne naopak ale je to asi tim na co je clovek zvyklej.
To ze mi *(i32*)p da stejnou hodnotu jako *(i16*)p pokud je tam ulozeno male cislo
mi prijde spis jako vyhoda, i kdyz se neda moc vyuzit kdyz clovek chce aby kod pracoval i na big-endianech.
No radsi toho nechame, dnes je to uz stejne jedno.
No to s tím int16 a int32 já třeba jako výhodu vidím. Párkát se mi stalo, že sem někde měl pole int16, pak k tomu omylem přistoupil přes ukazatel jako int. Při prvních laděních to prošlo a pak později na reálnejch datech to začalo náhodně padat a chvíli trvalo než jsem zjistil, co jsem proved. U big endianu by to bylo jasný okamžitě.
load trva 3 takty, store trva 2 takty. Aritmeticke instrukce a MOV z registru do registru trva 1 takt. Tohle je az do ARM7, ARM9 uz ma cache takze si muze dovolit predstirat harwardskou architekturu, a tam LOAD i STORE zabere 1 takt, pokud to chyti cache (taky ARM9 ma 5-stage pipeline, ARM1-7 maji jen 3 stage takze pri stejne technologii nejdou taktovat tak vysokymi frekvencemi jako ARM9).
Duvod proc to trva 2 takty je ten ze ARM1 az ARM7 jsou von-Neumann stroje, takze maji jen jednu sbernici do pameti a jelikoz jsou pipelinovane tak by v kazdem taktu musely nacitat dalsi instrukci a nemely by kdy nacist/ulozit data. Proto se pipeline musi pozdrzet a udelat mimoradny pristup do pameti na jine misto. Nejaky detail navrhu komunikace s pameti (myslim ze se adresa posila o pul taktu driv nebo tak neco -- Furber to jednou nekde vysvetloval, ze to bylo takhle levnejsi udelat ale uz si nepamatuju v cem to vezelo) zpusobuje ze pri cteni to trva jeste o takt dyl nez by clovek rekl ze by muselo).
Pristup k pameti ale neni vzdy tak pomalej, procesor ma instrukce LDM a STM, ktere podle bitove masky na nejakou adresu (z nejake adresy) ulozi (nactou) registry jejihz bit v masce byl jednickovy. LDM trva 2+pocet registru, STM 1+pocet registru.
Vyborne se to hodi treba na zacatku a konci funkce kde se tak daji ulozit registry jedinou instrukci misto sekvence push r1 push r2 push r3 ...
Taky se to da pouzivat pri kopirovani bloku dat.
Mimochodem ta blba 8086 s jeji zvlastni scitackou pro scitani segmentu a offsetu mela stejne transistoru jako ARM2.