IC és MEMS tervezés laboratórium BMEVIEEM314 Budapesti Műszaki és Gazdaságtudományi Egyetem Egyszerű RISC CPU tervezése Nagy Gergely Elektronikus Eszközök Tanszéke (BME) 2013. február 14. Nagy Gergely (BME EET) Egyszerű RISC CPU tervezése 2013. február 14. 1 / 26
IC és MEMS tervezés laboratórium BMEVIEEM314 Tartalom Bevezetés A kód szerkezete A működés tesztelése Nagy Gergely (BME EET) Egyszerű RISC CPU tervezése 2013. február 14. 2 / 26
Bevezetés Bevezetés Nagy Gergely (BME EET) Egyszerű RISC CPU tervezése 2013. február 14. 3 / 26
Bevezetés Áttekintés Áttekintés Egy nagyon egyszerű CPU Verilog kódja alapján áttekintjük egy egyszerű CPU megvalósításának lehetőségét: belső regiszterek, utasításciklus (instruction cycle) megvalósítása állapotgéppel, utasítások megvalósítása. A választott architektúra: Harvard külön utasítás-, és adatmemória. Az utasításkészlet RISC, vagyis kevés utasításból áll, az utasítások hossza (műveletkód, operandusok) megegyezik, az utasítások végrehajtási ideje is azonos. Ez a megoldás nagyon messze áll a valós CPU-k megoldásaitól, csak a legalapvetőbb vonásokat mutatja be. Nagy Gergely (BME EET) Egyszerű RISC CPU tervezése 2013. február 14. 4 / 26
Bevezetés Az architektúra Az architektúra I. Nagyon egyszerű, és sok szempontból nem reális: a két memória (utasítás-, és adat-) a CPU-n belül van megvalósítva, nincsenek különböző címzési módok, az utasításmemória 16 bites, az adatmemória 8 bites, van 16 db általános célú regiszter, a vezérlő regiszterek: PC: program counter (az aktuális utasítás kezdőcíme), flags: az utasításokkal kapcsolatos jellemzők logikai bitjei (csak zero flag van benne), egyéb segéd-regiszterek, amelyek a működéshez kellenek, programozói oldalról nem láthatóak. Nagy Gergely (BME EET) Egyszerű RISC CPU tervezése 2013. február 14. 5 / 26
Bevezetés Az architektúra Az architektúra II. Nagy Gergely (BME EET) Egyszerű RISC CPU tervezése 2013. február 14. 6 / 26
Bevezetés A működés körvonalai A működés körvonalai Az CPU tulajdonképpen egy állapotgép, amely az órajel ütemére váltja az állapotait. Az utasításciklus (instruction cycle) 4 állapotból áll: 1 fetch: ebben az állapotban olvassa be a CPU a memóriából a soronkövetkező utasítást (16 bit: négybites utasításkód és 3 db négybites operandus), 2 decode: az utasítást szétválasztja utasításkódra és operandusokra, valamint lépteti a PC-t, 3 execute: az aktuális utasítás végrehajtása és az eredmények eltárolása ideiglenes regiszterekben, 4 store: az ideiglenes tárolók kiírása szükség esetén a megfelelő regiszterekbe, valamint a flag(ek) beállítása. Nagy Gergely (BME EET) Egyszerű RISC CPU tervezése 2013. február 14. 7 / 26
Bevezetés Az utasításkészlet Az utasításkészlet I. Egyszerű logikai és aritmetikai utasítások: ADD, SUB, AND, NOT... Egyszerű feltételes és feltétel nélküli ugró utasítások: JMP, JZ, JNZ. Memória író/olvasó utasítások: LDA, STA, LDI. Kommunikáció külső áramkörökkel: OUT. Az legtöbb utasítás közvetlenül az általános célú regiszterekkel dolgozik ezek az argumentumokkal megcímezhetőek. Memóriából olvasni és oda írni is csak regiszteren keresztül lehet. Nagy Gergely (BME EET) Egyszerű RISC CPU tervezése 2013. február 14. 8 / 26
Bevezetés Az utasításkészlet Az utasításkészlet II. ADD reg[op3] = reg[op1] + reg[op2] SUB reg[op3] = reg[op1] - reg[op2] INC reg[op1]++ DEC reg[op1]-- NEG reg[op3] = ~reg[op1] AND reg[op3] = reg[op1] & reg[op2] OR reg[op3] = reg[op1] reg[op2] XOR reg[op3] = reg[op1] ^ reg[op2] Nagy Gergely (BME EET) Egyszerű RISC CPU tervezése 2013. február 14. 9 / 26
Bevezetés Az utasításkészlet Az utasításkészlet III. JMP PC = {OP1, OP2} JZ if (flags[0] == 0) PC = {OP1, OP2} JNZ if (flags[0]!= 0) PC = {OP1, OP2} LDI reg[op3] = {OP1, OP2} (LoaD Immediate) OUT output_port = reg[op1] LDA reg[op3] = data[{op1, OP2}] (LoaD Accumulator) STA data[{op1, OP2}] = reg[op3] (STore Accumulator) Nagy Gergely (BME EET) Egyszerű RISC CPU tervezése 2013. február 14. 10 / 26
A kód szerkezete A kód szerkezete Nagy Gergely (BME EET) Egyszerű RISC CPU tervezése 2013. február 14. 11 / 26
A kód szerkezete A modul fejrésze és a konstansok A modul fejrésze és a konstansok module simple_risc(input clk, input reset, output reg [7:0] output_port); parameter fetch = 4 d0; parameter decode = 4 d1; parameter execute = 4 d2; parameter store = 4 d3; parameter ADD = 4 d0; parameter SUB = 4 d1; parameter INC = 4 d2; parameter DEC = 4 d3; parameter NEG = 4 d4;... // reg[op3] = reg[op1] + reg[op2] // reg[op3] = reg[op1] reg[op2] // reg[op1]++ // reg[op1] // reg[op3] = ~reg[op1] A fetch, decode, execute, és store konstansok az utasításciklus állapotai. Az ADD, SUB, stb. konstansok az utasításkódok. Nagy Gergely (BME EET) Egyszerű RISC CPU tervezése 2013. február 14. 12 / 26
A kód szerkezete A memória-jellegű tárolók megvalósítása A memória-jellegű tárolók megvalósítása Tulajdonképpen kétdimenziós tömbök, ahol a register kulcsszó előtti méret a szavak szélességét adja meg, míg az utána következő az elemek számát: Általános célú regiszterbank: reg [7:0] registers [0:16]; Utasítás-memória: reg [15:0] instruction_memory [0:255]; Adat-memória: reg [7:0] data_memory [0:255]; Nagy Gergely (BME EET) Egyszerű RISC CPU tervezése 2013. február 14. 13 / 26
A kód szerkezete Az állapotgép megvalósítása Az állapotgép megvalósítása always @(posedge clk) begin if ( reset ) begin... else begin case ( state ) fetch :... decode:... execute:... store :... default : state <= #1 fetch; case Nagy Gergely (BME EET) Egyszerű RISC CPU tervezése 2013. február 14. 14 / 26
A kód szerkezete A reset hatása A reset hatása Minden regisztert 0 értékre állít és az állapotváltozóba a fetch állapotot írja. A store_temp és store_alu flagek funkciójáról később lesz szó. state <= #1 fetch; pc <= #1 8 d0; flags <= #1 8 d0; output_port <= #1 8 d0; store_temp <= #1 1 b0; store_alu <= #1 1 b0; Nagy Gergely (BME EET) Egyszerű RISC CPU tervezése 2013. február 14. 15 / 26
A kód szerkezete A fetch állapot A fetch állapot A PC által mutatott utsítást az instruction regiszterbe másolja. Az állapotváltozót átállítja dekódolásra (decode). Kinullázza a tárolást mutató flageket (később...). fetch : begin instruction <= #1 instruction_memory[pc]; state <= #1 decode; store_temp <= #1 1 b0; store_alu <= #1 1 b0; Nagy Gergely (BME EET) Egyszerű RISC CPU tervezése 2013. február 14. 16 / 26
A kód szerkezete A decode állapot A decode állapot Megnöveli a PC regiszter értékét. Az utasítást felbontja utasításkódra (opcode) és három paraméterre (op1-op3). Az állapotváltozót végrehajtás (execute) állapotba állítja. decode: begin pc <= #1 pc + 1; opcode <= #1 instruction[15:12]; op1 <= #1 instruction[11: 8]; op2 <= #1 instruction[ 7: 4]; op3 <= #1 instruction[ 3: 0]; state <= #1 execute; Nagy Gergely (BME EET) Egyszerű RISC CPU tervezése 2013. február 14. 17 / 26
A kód szerkezete Az utasítások megvalósítása Az utasítások megvalósítása I. Az utasítások egy jó része az ALU-t vezérli: a műveletek operandusai: reg[op1] és reg[op2], a műveletek célja: reg[op3], az ALU kimenetét a store állapotban mentjük el, ha ALU művelet történt, ebben az esetben be kell állítani a store_alu flaget 1-be. A memóriakezelő utasítások: ilyenkor a memóriabeli címet az {OP1,OP2} érték adja meg, a műveletben szereplő regisztert pedig az OP3, a memóriából való olvasásnál egy ideiglenes regiszterbe (temporary) írunk, a store állapotban ezt a regisztert kell kiírni a megadott általános célú regiszterbe, ilyenkor a store_temp flaget kell beállítani 1-be. Nagy Gergely (BME EET) Egyszerű RISC CPU tervezése 2013. február 14. 18 / 26
A kód szerkezete Az utasítások megvalósítása Az utasítások megvalósítása II. Az összeadás (ADD): ADD: begin alu_inst alu_op1 alu_op2 store_alu <= #1 A_ADD; <= #1 registers[op1]; <= #1 registers[op2]; <= #1 1 b1; Az OP1 és OP2 által megcímzett regisztereket bemásoljuk az ALU bemenetein lévő regiszterekbe. Az ALU kimenetét a store állapotban írjuk be az OP3 által címzett regiszterbe (ezért kell beállítani a store_alu értékét 1-be. Nagy Gergely (BME EET) Egyszerű RISC CPU tervezése 2013. február 14. 19 / 26
A kód szerkezete Az utasítások megvalósítása Az utasítások megvalósítása III. A növelés (INC): INC: begin alu_inst alu_op1 op3 store_alu <= #1 A_INC; <= #1 registers[op1]; <= #1 op1; <= #1 1 b1; Csak egy operandusunk van (reg[op1]). Az op3 regisztert itt mi töltjük ki, mert nem várjuk el, hogy az assembly kódban két helyen szerepeljen a megnövelő regiszter címe, ugyanakkor a célregiszter címének mindig az op3 regiszterben kell lennie. Nagy Gergely (BME EET) Egyszerű RISC CPU tervezése 2013. február 14. 20 / 26
A kód szerkezete Az utasítások megvalósítása Az utasítások megvalósítása IV. Feltételes ugrás (JZ): JZ: begin if ( flags [0] == 1 b1) begin pc <= #1 {op1, op2}; A feltétel teljesülése esetén közvetlenül a PC-t írjuk az operandusok értékével. Nagy Gergely (BME EET) Egyszerű RISC CPU tervezése 2013. február 14. 21 / 26
A kód szerkezete Az utasítások megvalósítása Az utasítások megvalósítása V. Memória műveletek (LDA, STA): LDA: begin temporary store_temp <= #1 data_memory[{op1, op2}]; <= #1 1 b1; STA: begin data_memory[{op1, op2}] <= #1 registers[op3]; Az írás közvetlenül megtörténik, az olvasásnál ideiglenes tárolóba írunk, ahonnan a store állapotban mentünk (store_temp beállítása). Nagy Gergely (BME EET) Egyszerű RISC CPU tervezése 2013. február 14. 22 / 26
A kód szerkezete A store állapot A store állapot A két jelző flag (store_temp, store_alu) függvényében a megfelelő forrást (temporary regiszter, ALU kimenete) az op3 által címzett regiszterbe másolja. Beállítja a flag(ek)et és az állapotváltozót a következő utasítást beolvasó fetch állapotba. store : begin if (store_temp) begin registers [op3] <= #1 temporary; flags [0] <= #1 (temporary == 8 b0)? 1 b1 : 1 b0; if (store_alu) begin registers [op3] <= #1 alu_res; flags [0] <= #1 (alu_res == 8 b0)? 1 b1 : 1 b0; state <= #1 fetch; Nagy Gergely (BME EET) Egyszerű RISC CPU tervezése 2013. február 14. 23 / 26
A működés tesztelése A működés tesztelése Nagy Gergely (BME EET) Egyszerű RISC CPU tervezése 2013. február 14. 24 / 26
A működés tesztelése Egy egyszerű program futtatása A CPU-t egy egyszerű program futtatásával tesztelhetjük. A mellékelt kódban ez úgy lett megoldva, hogy a CPU modul initial blokkjában (nem szintetizálható rész) feltöltjük az utasításmemóriát. A szintén mellékelt tesztkörnyezet segítségével futtatható és tesztelhető a rszer. Nagy Gergely (BME EET) Egyszerű RISC CPU tervezése 2013. február 14. 25 / 26
Források Az előadás és a forrásfájlok Az előadás PDF formátumban és a tárgyalt CPU Verilog nyelvű forrásfájljai letölthetőek innen. Nagy Gergely (BME EET) Egyszerű RISC CPU tervezése 2013. február 14. 26 / 26