ARHITEKTURA NVIDIA TESLA JEZGRA (UPROSCENA)
===========================================

*) 32-bitni registri opste namene. Njihov broj zavisi od konkretne
verzije, mi mozemo pretpostaviti da ima, na primer, 128 registra.

*) 1-bitni predikatski registri: takodje, pretpostavimo da ih ima
128. Ovi registri se koriste za uslovno izvrsavanje instrukcija (vidi
dole detaljnije).

*) U pitanju je load-store arhitektura, tj. jedino instrukcije ld i st
mogu da imaju memorijske operande. Ostale instrukcije rade iskljucivo
sa registarskim operandima i konstantama.

*) Procesor razlikuje sledece tipove podataka:
   -) neoznaceni tipovi: u8, u16, u32, u64
   -) oznaceni tipovi: s8, s16, s32, s64
   -) realni tipovi: f32, f64
   -) neinterpretirani bitski tipovi: b8, b16, b32, b64
   -) predikatski tipovi (jednobitni)

Na koji ce se nacin interpretirati podatak zavisi od toga kako se
primeni odgovarajuca instrukcija (vecina instrukcija imaju sufikse
koji odredjuju interpretaciju operanada). 8-bitni tipovi se mogu samo
ucitavati u registre (uz oznaceno ili neoznaceno prosirivanje) i
smestati u memoriju. Nad njima nije moguce vrsiti aritmeticke
operacije. 

*) Skoro sve instrukcije imaju osobinu da su svi operandi iste
velicine i tipa. Izuzetak je instrukcija cvt koja vrsi konverziju iz
tipa u tip, pa, po prirodi stvari, ima razlicite tipove izvorisnog i
odredisnog operanda. Ova instrukcija ima dva sufiksa, prvi je tip
odredisnog, a druga je tip izvorisnog operanda.

*) U asemblerskom jeziku, podaci se definisu kao promenljive koje
pripadaju odgovarajucoj klasi. Za nas su najznacajnije klase .reg,
.shared i .global.  Promenljivama deklarisanim sa .reg se dodeljuju
registri (zato ne bi trebalo da ih bude vise od broja dostupnih
registara). Promenljive deklarisane kao .shared se obicno alociraju u
memoriji koja je deljena izmedju vise jezgara u okviru istog
multiprocesora, dok se .global promenljive cuvaju u memoriji koja je
dostupna na kartici i koja je dostupna svim multiprocesorima. Sam
prevodilac ove promenljive prevodi u konkretne memorijske lokacije u
odgovarajucoj memoriji. Memorije .shared i .global predstavljaju
razlicite adresne prostore (pretpostavimo da su oba
32-bitna). Primeri:

.reg .s32 r0;  // r0 je registar koji sadrzi 32-bitni oznaceni broj
.reg .b16 r1;  // r1 je registar koji sadrzi 16-bitni neinterpretirani
              // bitovski sadrzaj (kako su registri 32-bitni, samo nizi
	      // deo registra ce se koristiti)
.shared .u8 x; // jedan neoznaceni bajt u deljenoj memoriji
.global .s16 y; // 16-bitni oznaceni broj u globalnoj memoriji
.reg .pred p;  // deklarisemo predikatski registar p

*) Registarski operandi se navode tako sto se navedu nazivi
odgovarajucih registarskih varijabli. Primer:

.reg .s32 r0;
.reg .s32 r1;
.reg .s32 r2;
add.s32 r1, r2, r3   // r1 = r2 + r3

*) Neposredni operandi (konstante) se navode tako sto se zada
odgovarajuca konstanta. U asembleru su dozvoljene heksadekadne,
oktalne, dekadne i binarne konstante, a kod realnih brojeva se koristi
uobicajeni zapis. Najvise jedan operand instrukcije moze biti
konstanta. Primer:

.reg .u32 r0;
.reg .u32 r1;

mov.u32 r1, 23;
add.u32 r0, r1, 23;

*) Memorijski operandi se zadaju u zagradama [] kao i u vecini drugih
asemblera. Memorijski operand moze biti:
  -- naziv promenljive deklarisane sa .shared ili .global:

     .shared .b16 x;
     .reg .b16 r;

     ld.shared.b16 r, [x];  // ucitava promenljivu x iz .shared
                            // memoriju u registar r
  
  -- naziv registarske promenljive, u kom slucaju se sadrzaj tog registra
     tumaci kao adresa (indirektno adresiranje):

     .shared .b16 x;
     .reg .b16 r;
     .reg .b32 ra;

     mov ra, x;  // adresu podatka x u .shared memoriji kopira u registar ra
     ld.shared.b16 r, [ra];  // kopira podatak sa adrese ra u .shared
                            // memoriji u registar r


  -- naziv registarske promenljive +/- pomeraj (konstanta)

     ld.shared.b16 r, [ra + 8];

  -- apsolutna adresa zadata kao konstanta

     ld.shared.b16 r, [0xffee5524];

Memorijski operandi se koriste samo kod ld i st instrukcija. 


*) Izmedju registara podaci se mogu premestati mov instrukcijom. Ova
instrukcija moze imati kao izvorisni operand i konstantu. Takodje,
izvorisni operand moze biti i promenljiva deklarisana kao .shared ili
.global, u kom slucaju se adresa te promenljive koristi kao konstantni
operand (tj. adresa odgovarajuce promenljive se kopira u odredisni
registar). Primeri:

   .shared .s32  x;
   .reg .s32 r;
   .reg .s32 p;
   
   mov r, p; // kopira p u r
   mov r, x; // kopira adresu od x u .shared memoriji u r.

*) Instrukcije mogu imati od 1 do 3 izvorisnih operanada i najvise
jedan odredisni operand. U asembleru se odredisni operand (ako
postoji) navodi kao prvi operand instrukcije. Odredisni operand ne
moze biti konstanta.
   
*) Sve instrukcije se mogu izvrsavati uslovno, u zavisnosti od
vrednosti predikatskog registra (ako je jednak 1, izvrsava se, a ako
je jednak 0 ne izvrsava se). Sintaksa u asembleru je:

@p  <instrukcija>

ili

@!p  <instrukcija>

U ovom drugom slucaju se umesto vrednosti registra p koristi njegova
negacija. Najcesce se najpre vrednost registra p postavi pomocu
odgovarajuce instrukcije poredjenja (vidi dole), a onda se u
zavisnosti od rezultata poredjenja (tj. vrednosti registra p)
instrukcija uslovno izvrsava:

.reg .u32 x;
.reg .u32 y;
.reg .u32 z;
.reg .pred p; // predikatski registar (1-bitni)

setp.hs.u32 p, x, y;        // postavlja p na 1 akko je x >= y (neoznaceno poredjenje)
@p add.u32 z, x, y;        // ako je p == 1, tj. x >= y, tada je z = x + y;
@!p sub.u32 z, x, y;       // u suprotnom, z = x - y;


INSTRUKCIJE:
============

U opisu instrukcija a, b, c predstavljaju izvorisne operande, dok d
predstavlja odredisni operand. Najvise jedan izvorisni operand moze
biti konstanta. Sve instrukcije imaju sufiks .tip koji moze biti .s16,
.u16, .s32, .u32, .s64, .u64., .b16, .b32, .b64, .f32 i .f64, i
odredjuje tip podatka koji se nalazi u operandima. Tipovi .u8, .s8,
.b8  se mogu koristiti samo za ld i st instrukcije, kao sto je
ranije naglaseno.  Ukoliko se deklarisani tip promenljive razlikuje od
tipa koji je naveden u sufiksu instrukcije, tada ce asemblerski
prevodilac prijaviti gresku, osim ako tipovi nisu kompatibilni (tipovi
su kompatibilni ako su iste velicine i oba su celobrojna ili su oba
realna; specijalno, tipovi .b8, .b16, .b32, .b64 su kompatibilni sa
svakim tipom odgovarajuce velicine).

Sve instrukcije mogu imati i predikatski registar, tj. mogu se
izvrsavati uslovno, sto necemo eksplicitno navoditi.

*) ARITMETICKE INSTRUKCIJE

-- add.tip d, a, b    // d = a + b
-- sub.tip d, a, b    // d = a - b
-- mul.tip d, a, b    // d = a * b, a i b su tipa .tip, d je dvostruke sirine
-- mad.tip d, a, b, c  // d = a * b + c, a, b i c su tipa .tip, d je dvostruke sirine
-- div.tip d, a, b     // d = a / b, d i b su tipa .tip, a je dvostruke sirine
-- rem.tip d, a, b     // d = a % b, d i b su tipa .tip, a je dvostruke sirine
-- abs.tip d, a        // d = |a|
-- neg.tip d, a        // d = -a
-- min.tip d, a, b     // d = min(a, b)
-- max.tip d, a, b     // d = max(a, b)

*) INSTRUKCIJA POREDJENJA

-- setp.op.tip p, a, b;

op moze biti nesto od { eq, ne, lt, le, gt, ge, lo, ls, hi, hs }. lt, le, gt, ge se 
koriste za oznaceno poredjenje, a lo, ls, hi, hs se koriste za neoznaceno poredjenje.
p je predikatski odredisni operand u koji se upisuje 1 ako je rezultat odgovarajuceg
poredjenja a i b tacan, a 0 u suprotnom. Na primer:

.shared .b32 x, y;
.reg .pred p;

setp.lo.u32 p, x, y; // ispituje x < y (neoznaceno poredjenje) i
                     // postavlja p na 1 akko je uslov ispunjen

@p bra L             // skace na labelu L ako je uslov ispujen


*) LOGICKE INSTRUKCIJE

-- and.tip d, a, b    // d = a & b
-- or.tip d, a, b     // d = a | b
-- xor.tip d, a, b    // d = a ^ b
-- not.tip d, a       // d = ~a
-- shl.tip d, a, b    // d = a << b
-- shr.tip d, a, b    // d = a >> b, u zavisnosti od tipa, logicki ili aritmeticko


*) INSTRUKCIJE TRANSFERA

-- mov.tip d, a   

d mora biti registar, a moze biti konstanta ili registar (u kom slucaju se vrednost kopira
u d) ili promenljiva .shared ili .global klase, u kom slucaju se adresa te promenljive kopira
u registar d. 

-- ld.klasa.tip d, [a] 

klasa moze biti shared ili global.

d mora biti registar. dok a moze biti:
   -- konstanta: u tom slucaju se vrednost konstante uzima kao adresa i iz memorije (deljene ili globalne)
   se sa te adrese uzima vrednost i ucitava u registar d.
   -- registar: u tom slucaju se vrednost registra uzima kao adresa sa koje se uzima vrednost
   -- registar + pomeraj: tada se pomeraj dodaje na registar i sa te adrese se uzima vrednost
   -- promenljiva .shared ili .global klase: tada se vrednost te promenljive ucitava u registar d. 

Primeri:

.shared .s32  x;
.reg .s32 r, ra;

ld.shared.s32 r, [x]   // ucitava x u r
ld.shared.s32 r, [ra]  // ucitava podatak sa adrese ra iz .shared memorije u r
ld.shared.s32 r, [ra - 8] // ucitava podatak sa adrese ra - 8 iz .shared memorije u r
ld.shared.s32 r, [0x0ff0f0f0] // ucitava podatak sa apsolutno zadate adrese

-- st.klasa.tip [d], a

a mora biti registar cija se vrednost cuva u memoriji, dok d moze biti sve kao i u slucaju ld instrukcije.

-- cvt.dtip.stip d, a

d i a su registri. a treba da bude tipa stip, dok d treba da bude tipa
dtip. Instrukcija vrsi konverziju vrednosti registra a iz stip u dtip
i dobijenu vrednost cuva u d. Na primer:

.reg .s32 x;
.reg .f32 y;

cvt.f32.s32 y, x;  // konvertuje x iz .s32 u .f32 tip i smesta ga u y.


*) INSTRUKCIJE KONTROLE TOKA

-- bra labela

Ova instrukcija vrsi skok na datu labelu. Ako zelimo da imamo uslovni
skok, tada treba uslovno izvrsiti ovu instrukciju. Na primer:

setp.lt.s32 p, x, y   // ispituje da li je x < y i upisuje rezultat u predikatski registar p 
@p bra L1   // skace na labelu L1 akko je p == 1, tj. akko je x < y

-- exit   // prekida izvrsavanje programa

*) INSTRUKCIJE ZA RAD SA REALNIM BROJEVIMA

-- rcp.tip d, a // d = 1 / a, tip treba da bude f32 ili f64
-- sqrt.tip d, a // d = sqrt(a)
-- rsqrt.tip d, a // d = 1/sqrt(a)
-- sin.tip d, a  // d = sin(a), ugao je u radijanima
-- cos.tip d, a  // d = cos(a)
-- lg2.tip d, a  // d = log_2(a)
-- ex2.tip d, a  // d = 2^a



