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: 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. 1
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 parameter parameter parameter parameter ...
ADD = 4’d0; SUB = 4’d1; INC = 4’d2; DEC = 4’d3; 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 ... end else begin case ( state ) fetch : ... decode: ... execute : ... store : ... default : state <= #1 fetch; endcase end end 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 state store_temp store_alu end
Nagy Gergely (BME EET)
<= <= <= <=
#1 #1 #1 #1
instruction_memory[pc]; decode; 1’b0; 1’b0;
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; end
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 end
<= <= <= <=
#1 #1 #1 #1
‘A_ADD; registers[op1]; registers[op2]; 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 end
<= <= <= <=
#1 #1 #1 #1
‘A_INC; registers[op1]; op1; 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övelendő 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}; end end
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 <= #1 data_memory[{op1, op2}]; store_temp <= #1 1’b1; end STA: begin data_memory[{op1, op2}] <= #1 registers[op3]; end
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; end if (store_alu) begin registers [op3] <= #1 alu_res; flags [0] <= #1 (alu_res == 8’b0) ? 1’b1 : 1’b0; end state <= #1 fetch; end 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 rendszer.
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