Escola Politècnica Superior
Memòria del Treball de Fi de Grau
Implementació d’un processador RISC en una FPGA
Sergio Pérez Bugarín
Grau d’Enginyeria Electrònica Industrial i Automàtica
Any acadèmic 2018-19
DNI de l’alumne: 43210124R
Treball tutelat per Sebastián Antonio Bota Ferragut Departament de Física
S'autoritza la Universitat a incloure aquest treball en el Repositori Institucional per a la seva consulta en accés obert i difusió en línia, amb finalitats
exclusivament acadèmiques i d'investigació
Autor Tutor Sí No Sí No
X X
Paraules clau del treball:
FPGA, RISC, MIPS, processador, VHDL, ...
1
Índex
1. Resum ... 4
2. Introducció ... 5
3. FPGA ... 7
4. MIPS ... 10
3.1. Instruccions ... 10
3.2. Mòduls del microprocessador ... 12
3.2.1. Memòria de dades ... 13
3.2.2. Memòria d’instruccions ... 13
3.2.3. Memòria de registres ... 14
3.2.4. Dos sumadors ... 14
3.2.5. Extensió de signe ... 15
3.2.6. ALU ... 16
3.2.7. Control de la ALU ... 16
3.2.8. Comptador de programa (PC) ... 17
3.2.9. Desplaçament de 2 bits a l’esquerra ... 17
3.2.10. Unitat de control ... 17
3.3. Aportació pròpia ... 18
3.3.1. ALU ... 20
3.3.2. Unitat de control ... 21
3.3.3. Programació de la SRAM ... 23
4. Implementació del processador ... 24
4.1. FPGA ... 24
4.2. ModelSim ... 25
4.3. Quartus II ... 26
4.4. TimeQuest Timing Analyzer ... 26
4.5. PLL ... 27
4.6. Problemes i solucions ... 27
5. Verificació experimental ... 30
5.1. Programa 2 ... 30
5.2. Programa 3 ... 31
5.3. Mòdul de validació ... 32
5.4. Botons i LEDs ... 32
2
5.5. Recursos emprats ... 33
5.6. TimeQuest Timing Analyzer ... 33
6. Temps invertit ... 35
7. Conclusió ... 36
Bibliografia ... 37
Annexos ... 39
Annex A: Testbench ... 39
ALU_tb ... 39
CompareRegister_tb ... 42
ControlUnit_tb ... 44
CreateClock_tb ... 47
DataMemory_tb ... 48
InstructionMemory_tb ... 50
MuxALU_tb ... 51
MuxPC_tb ... 53
MuxUnsigned_tb ... 55
MuxWriteData_tb ... 57
MuxWriteRegister_tb ... 59
PC_tb ... 61
ProcesadorCompleto_tb ... 63
ProcesadorModulos_tb ... 65
Register32_tb ... 72
RegisterMemory_tb ... 74
Shift_tb ... 77
Sign_tb ... 78
SUM_tb... 79
SUMPC_tb ... 81
Unsigned_tb ... 82
Annex B: Programes verificació ... 83
Programa 1 ... 83
Programa 2 ... 86
Programa 3 ... 88
Annex C: Codi VHDL del processador ... 90
ALU ... 90
3
CompareRegister ... 92
ControlUnit ... 93
CreateClock ... 100
DataMemory ... 108
InstructionMemory ... 110
MuxALU ... 113
MuxPC ... 114
MuxUnsigned ... 115
MuxWriteData ... 116
MuxWriteRegister ... 117
PC ... 118
ProcesadorCompleto ... 119
ProcesadorModulos ... 121
Register32 ... 124
RegisterMemory ... 126
Shift ... 128
Sign ... 129
SUM ... 130
SUMPC ... 131
Unsigned ... 132
4
1. Resum
En aquest projecte el primer objectiu és implementar un processador RISC, i per a poder realitzar-ho s’ha utilitzat el llenguatge VHDL. El segon objectiu és carregar-lo en una FPGA i fer els canvis necessaris per al seu correcte funcionament. Per a poder configurar la FPGA s’han seguit els següents passos: definició del codi VHDL, simulació, síntesis, anàlisis temporal i carregar el fitxer de programació.
Com a punt de partida s’ha agafat el processador descrit en el llibre de Henessy i Patterson [15], que és capaç d’executar les instruccions bàsiques d’un processador MIPS. A aquest s’han afegit alguns mòduls nous i s’han modificat altres per a poder executar algunes altres instruccions més a part de les bàsiques, i també s’han realitzat aquests canvis per poder implementar funcions extres i d’aquesta manera fer possible la interactuació amb el processador emprant diferents elements d’entrada i sortida de la FPGA.
A més, s’utilitza un PLL, que permet variar la freqüència a la qual treballa per defecte la FPGA i d’aquesta manera comprovar la freqüència màxima a la qual pot treballar correctament.
S’ha aconseguit implementar un processador capaç d’executar un total de 17 instruccions, tenint en compte les bàsiques, amb el qual es pot interactuar amb alguns botons i comprovar si el resultat és correcte amb alguns LEDs de la FPGA.
En el processador implementat no es pot programar la memòria, és a dir, cada vegada que es canvien les instruccions o les dades de la memòria s’ha de repetir el procés per a configurar una FPGA i es defineixen en binari. En aquest projecte no s’ha intentat implementar una manera de programar-la, ja que no forma part dels objectius.
5
2. Introducció
Amb el nom de MIPS (Microprocessor without Interlocked Pipeline Stages) es coneix a una família de microprocessadors, que presenta una arquitectura RISC que es va desenvolupar per millorar el rendiment dels microprocessadors aplicant tècniques de segmentació (pipelining).
En 1981, en la universitat de Stanford un equip liderat per John L. Hennessy va començar a treballar en el primer processador MIPS. En 1984 Hennessy va deixar la universitat per fundar l’empresa MIPS Computer System i l’any 1985 van presentar el seu primer disseny, el processador R2000. En 1988 va estar disponible al mercat el R3000, amb un control de cache millorat, i més endavant va treure el primer processador de 64 bits, l’R4000.
En 1992 MIPS Computer System va ser adquirida per Silicon Graphics empresa dedicada a fabricar estacions de treball pel mercat de disseny gràfic en 3D. A més, varen vendre els seus dissenys a altres fabricants, i per això s’utilitzen també en sistemes encastats.
Aquests processadors utilitzen instruccions senzilles i de format fixe de manera que es facilita la fase de descodificació, ja que els camps que indiquen l’operació a realitzar estan sempre a la mateixa posició. Tot això permet reduir el temps d’execució de les instruccions, per tant, reduir el temps d’execució de la CPU i poder treballar a una major freqüència.
A causa dels aspectes anteriors les instruccions es poden executar en un cicle de rellotge, que és com s’ha implementat en aquest projecte. També s’ha de tenir en compte que existeix pipelining, però no s’implementarà en aquest projecte, ja que no forma part dels objectius.
Un dels desavantatges, que té aquest processador és que és menys potent que altres processadors, ja que utilitza instruccions simples i en algunes aplicacions, com pot ser tractament de foto i vídeo, amb instruccions més complexes algunes operacions s’executarien més ràpidament. També s’ha de tenir en compte, que a causa d’utilitzar instruccions simples els programes són més llargs.
El primer desavantatge no afecta aquest projecte, perquè l’objectiu d’aquest projecte no és aconseguir fer un processador molt potent. A més, com es fan servir programes senzills el segon desavantatge tampoc ens afecta.
L’objectiu d’aquest projecte és implementar un processador RISC en una FPGA a partir d’una descripció VHDL. Es realitzarà una versió simplificada del processador que sigui capaç d’executar el conjunt d’instruccions bàsiques dels processadors MIPS.
El segon objectiu és carregar el fitxer de programació necessari per configurar el funcionament de la FPGA, i fent els canvis necessaris per al correcte funcionament d’aquest. D’aquesta manera aconseguir familiaritzar-se amb les FPGAs i aconseguir alguns coneixements sobre com treballar amb una.
En primer lloc s’utilitzarà una descripció VHDL per poder simular el funcionament del sistema abans d’implementar-ho a la FPGA. En aquesta etapa s’introduiran les correccions pertinents per tal d’adaptar el disseny a les nostres especificacions sense perdre de vista les limitacions relacionades amb la FPGA seleccionada.
Un altre dels objectius serà el de planificar la verificació del funcionament del microprocessador dissenyat a partir dels diferents elements (interruptors, polsadors, leds...) disponibles a la placa d’avaluació de la FPGA.
Respecte al software emprat la definició del codi VHDL es pot realitzar amb un editor de text o es pot fer amb el mateix programa amb el qual es realitza la simulació del codi. En aquest projecte s’ha emprat el segon.
6
En una primera fase el funcionament del microprocessador (simulació funcional) es realitzarà amb un simulador VHDL, de manera que puguem modificar fàcilment el codi si la resposta no és l’esperada. A més, el mateix simulador ens ajuda a depurar els possibles errors de sintaxi abans de passar a l’etapa de síntesi lògica.
Una vegada completada la simulació funcional serà necessari utilitzar un nou programa per realitzar la síntesi lògica del codi VHDL, és a dir, a partir del codi funcional VHDL generarà un codi estructural utilitzant els recursos lògics disponibles a la FPGA que hem seleccionat prèviament. S’ha de tenir en compte que encara que la simulació funcional hagi estat satisfactòria, l’eina de síntesis lògica pot generar errors si hem utilitzat instruccions de codi no sintetitzables.
També s’emprarà una eina que permet realitzar una anàlisi temporal dels senyals, per comprovar que aquests puguin arribar a temps als diferents components de la FPGA. S’ha de tenir en compte que aquesta anàlisi es fa abans de carregar el fitxer de programació necessari per configurar el funcionament del processador a la FPGA.
A més s’hauran d’emprar dues eines més. La primera ens permetrà generar el codi VHDL de diferents mòduls complementaris necessaris per adaptar el nostre disseny a les característiques de la placa d’avaluació. La segona eina ens permet analitzar els senyals de la FPGA durant un cert interval de temps i d’aquesta manera podem validar el funcionament del microprocessador un cop implementat i, en el seu cas, detectar i corregir possibles errors.
Finalment, serà necessari crear alguns programes que realitzen una funció determinada, per a poder conèixer el resultat final que s’hauria d’obtenir. Aquests ens permetran comprovar si funciona correctament el processador, i en aquest projecte s’han creat dos.
7
3. FPGA
FPGA és un acrònim que significa Field programable Gate Arrays (matriu de portes programables en camp). Està formada per blocs lògics configurables (CLBs) connectats a través d’interconnexions programables, de manera que podem programar tant els CLBs com les diferents connexions per aconseguir la funció desitjada. Sovint es poden complementar amb blocs més complexos tals com memòries SRAM, blocs aritmètics, etc.
Figura 1. Floorplan d’una FPGA (Ciclone III)
També hi ha una alternativa a les FPGAs que són els ASICs, que és un acrònim que vol dir circuit integrat per aplicacions específiques. S’ha utilitzat en aquest projecte una FPGA, perquè és més flexible i de propòsit d’ús general, mentre que els ASICs són circuits integrats de propòsit particular, i si es vol fer algun canvi és més costós i no són reprogramables.
Podem trobar FPGAs que només es poden programar una vegada, mentre que n’hi ha d’altres que són reprogramables. En el mercat de les FPGAs destaquen dos grans fabricants: Altera (adquirida per Intel l’any 2015) i Xilinx. En concret, en aquest projecte s’utilitza una FPGA d’Altera reprogramable.
Les FPGAs presenten els següents avantatges:
• Es poden reprogramar moltes vegades.
• El cost és baix.
• Temps de disseny és baix.
• El temps per observar els resultats és baix.
• L’execució dels diferents mòduls és en paral·lel.
En aquest projecte és útil que es pugui reprogramar, per a poder afegir més mòduls al processador a mesura que avança el projecte i per solucionar els problemes que poden sorgir.
També és important que el disseny no sigui molt costós, i que es pugui saber ràpidament si el comportament és el desitjat.
A continuació, podem observar els desavantatges:
• No es poden implementar sistemes excessivament complexos.
• Consumeixen més potencia que altres alternatives, com els ASIC.
8
• Una vegada es desconnecten del corrent, perden els mòduls carregats.
Els desavantatges que presenten les FPGAs no afecten aquest projecte, ja que s’implementa una versió senzilla d’un processador MIPS i per tant tampoc consumeix molta potència (encara que amb un ASIC possiblement s’aconseguiria una freqüència de funcionament més elevada).
El circuit lògic que es vol dissenyar es pot implementar amb un llenguatge de descripció hardware, on es pot trobar Verilog, VHDL i Abel. En aquest cas s’utilitza VHDL per descriure els diferents elements que formen part del processador.
VHDL és un llenguatge que permet descriure a alt nivell el hardware a implementar. Aquest llenguatge va sorgir al final de la dècada dels anys 70, pel departament de defensa dels Estats Units. El propòsit d’aquest llenguatge era crear un estàndard per dissenyar, modelar i documentar circuits complexos, perquè les diferents empreses poguessin entendre els dissenys d’altres. En 1987, va ser reconeguda com un llenguatge estàndard per a la descripció de hardware.
Aquest llenguatge a més de permetre descriure diferents components hardware, també ens dóna la possibilitat de realitzar un testbench, que ens permet comprovar el funcionament dels diferents components en la simulació, per observar si hem obtingut el comportament desitjat.
Per configurar una FPGA s’han de seguir una sèrie de passos. El primer pas per programar una FPGA és definir el codi VHDL del circuit lògic que es vol implementar, en el cas d’aquest projecte, el processador RISC. Per a la definició es pot emprar un editor de text o un programa que et deixi simular el codi VHDL.
Una vegada s’ha redactat el codi VHDL es realitza una simulació. Primer s’han de corregir tots els errors que hi ha en la redacció del codi, i una vegada no hi hagi errors de compilació ja es pot simular. Gràcies a la simulació podrem observar el comportament dels senyals per comprovar si actua de la manera desitjada i si no fer els canvis pertinents.
El següent pas és realitzar la síntesi del codi, és a dir, es generen els arxius que indiquen com són les diferents interconnexions entre els diferents components de la FPGA. També s’han d’indicar els pins a on s’han de connectar les diferents entrades i sortides en cas de voler utilitzar els botons, LCD, etc. de la FPGA.
A continuació, es realitza una anàlisi temporal dels diferents senyals per assegurar-se que tots arribaran a temps. Si algun no arribes, cal solucionar l’error, per exemple, modificant la freqüència.
Finalment, ja es pot passar el circuit lògic que s’ha creat a la FPGA i comprovar el seu funcionament. Per a observar el comportament dels senyals es pot emprar una eina d’anàlisi i així és més senzill trobar on actua de manera no desitjada.
9
Figura 2. Passos a seguir per configurar una FPGA
Si es vol més informació sobre les FPGAs existeixen moltes alternatives per a informar-se d’aquest tema, en concret es poden consultar les referències [16], [17] i [18] de la bibliografia.
10
4. MIPS
El processador MIPS que es defineix en aquest projecte s’ha realitzat agafant com a referència la descripció del llibre de Henessy i Patterson [15]. En primer lloc s’explicaran el conjunt d’instruccions utilitzades i els mòduls bàsics. Després es justifica quins mòduls s’han afegit, per a poder augmentar el nombre d’instruccions inicial.
3.1. Instruccions
El processador agafat com a referència, està dissenyat per implementar les següents instruccions:
• Suma (add).
• Resta (sub).
• And (and).
• Or (or).
• Carregar una paraula (lw).
• Guardar una paraula (sw).
• Branch on equal (beq).
• Set on less than (slt).
En la següent imatge es mostren els diferents mòduls que formen el processador agafat com a referència i les connexions entre aquests. Els principals elements són una Memòria d’instrucció, un conjunt de registres, una memòria de dades, una ALU i un comptador de programa, blocs que es descriuran en major detall a l’apartat 3.2.
Figura 3. Processador de referència
11
De les instruccions anteriors s’ha de tenir en compte que només accedeixen a la memòria de dades dues instruccions: Carregar i Guardar una paraula. Mentre que la resta d’instruccions només accedeixen als registres. També s’ha de tenir en compte, que després d’executar una instrucció els registres mantenen el valor resultant de l’operació.
A continuació, es mostra un exemple de com funcionen les instruccions anteriors.
Instrucció Operació
add r1, r2, r3 r1 = r2 + r3 sub r1, r2,r 3 r1 = r2 - r3 and r1, r2 , r3 r1 = r2 and r3
or r1, r2, r3 r1 = r2 or r3 lw r1, 10(r2) r1 = Memòria [r2 + 10]
sw r1, 10(r2) Memòria [r2 + 10] = r1
beq r1, r2, 20 Si r1=r2 → salta a la posició PC+4+20*4 slt r1, r2, r3 Si r2<r3 → r1=1
Sino r1=0
Taula 1. Operacions de la ALU. R1, r2 i r3 fan referència a tres registres i PC al comptador de programa
Respecte a les instruccions anteriors cal destacar, que en la instrucció Branch on equal es multiplica per 4 l’últim paràmetre, perquè aquest fa referència al nombre d’instruccions a saltar, i s’ha de tenir en compte que cada instrucció ocupa una paraula, que són 4 bytes, i el processador fa la suma tenint en compte el nombre de bytes a saltar.
En les instruccions carregar i guardar una paraula no es multiplica per 4, perquè aquest paràmetre indica l’offset en nombre de bytes i el processador també treballa tenint en compte els bytes per accedir a una posició. A més, d’aquesta manera es pot accedir a qualsevol byte de la memòria.
Totes les instruccions són de 32 bits, però l’estructura és diferent segons la instrucció. Es poden distingir dos tipus d’instruccions segons el format: R i L. De les instruccions anteriors són de format R les següents instruccions: add, sub, and, or i slt, i la resta són de format L. La diferència entre els dos formats són els 16 bits menys significatius, ja que el format R presenta 3 camps (rd, shamt, funct) i el format L només un camp (address). A continuació, es mostren els valors en els diferents camps per a les instruccions anteriors.
12
address (16 bits) Instruccions Format
opcode (6 bits)
rs (5 bits)
rt (5 bits)
rd (5 bits)
shamt (5 bits)
function (6 bits)
add R 0 0 32
sub R 0 0 34
and R 0 0 36
or R 0 0 37
lw L 35
sw L 43
beq L 4
slt R 0 0 42
Taula 2. Camps de les instruccions del processador agafat com a punt de partida
Hi ha camps que s’han deixat en blanc, perquè els seus valors depenen dels registres que es volen utilitzar i de l’offset.
A continuació, s’expliquen els diferents camps que pertanyen al format R:
• opcode: sempre és igual a 0.
• rs: primer registre per a la realització d’una operació. Per a les instruccions de desplaçament lògic a l’esquerra i dreta, sempre és igual a 0.
• rt: segon registre per a la realització d’una operació.
• rd: registre on es guarda el resultat de l’operació.
• shamt: nombre de bits a desplaçar a l’esquerra o dreta. Aquest camp es posa a 0, excepte si s’utilitza la instrucció de desplaçament lògic a l’esquerra o dreta, ja que en aquest cas s’utilitza per a indicar el nombre de bits a desplaçar.
• function: indica quina operació s’ha de realitzar.
Ara, s’expliquen els diferents camps del format L:
• opcode: indica quina operació s’ha de realitzar.
• rs: registre per a la realització d’una operació. Per a la instrucció de carregar i guardar una paraula el seu valor més el camp de address indica la posició de memòria.
• rt: registre on es guarda el resultat de l’operació. En el cas de la instrucció de Carregar una paraula és el registre on es guarda el valor de la posició corresponent de la memòria i per a la de Guardar una paraula el valor d’aquest registre es guarda en la memòria.
• address: indica l’offset. Per a les instruccions d’immediat és el valor de la constant.
3.2. Mòduls del microprocessador
En aquest apartat es descriuen els diferents mòduls que componen el microprocessador.
13 3.2.1. Memòria de dades
Figura 4. Memòria de dades
L’entrada Write Data indica el valor que volem guardar en la memòria i Address indica en quina posició. L’entrada Address també pot fer referència de quina posició de memòria volem llegir, i el seu valor es mostra per la sortida Read Data.
Per controlar l’escriptura i lectura tenim dos senyals de control, MemWrite i MemRead. La primera s’utilitza per controlar l’escriptura a la memòria i és un senyal síncron, que s’utilitza per a la instrucció Guardar una paraula. Mentre que la segona controla la lectura i és asíncrona, i aquest senyal s’utilitza per a la instrucció Carregar una paraula. Però s’ha de tenir en compte que no es pot escriure i llegir a la vegada, i s’han implementat de manera que són actives per nivell alt.
Es pot observar com es va dir anteriorment, que només accedeixen a la memòria les instruccions Carregar i Guardar una paraula, ja que són les úniques instruccions que utilitzen els senyals per controlar l’escriptura i lectura de la memòria de dades.
3.2.2. Memòria d’instruccions
Figura 5. Memòria d'instruccions
Aquest mòdul conté les instruccions que formen part del programa i que es van executant. En la sortida Instruction es mostra la instrucció, que es troba en la posició indicada per l’entrada Read Address. D’aquesta manera podem accedir a les instruccions que van executant-se per controlar com han de comportar-se els diferents mòduls segons la instrucció en execució.
14 3.2.3. Memòria de registres
Figura 6. Memòria de registres
Aquest mòdul ens permet accedir al valor de qualsevol dels registres i en total hi ha 32. També ens permet modificar el valor dels registres, i s’ha de tenir en compte que la grandària de les dades que guarden és de 32 bits.
Les sortides, Read Data 1 i 2 mostren el valor dels registres indicats per les entrades Read Register 1 i 2 respectivament. L’entrada Write Register indica en quin registre volem guardar el resultat d’una operació i Write Data Register indica el valor que volem guardar. L’escriptura en un registre ve controlada pel senyal RegWrite que és síncrona i activa per nivell alt, i aquest senyal és activada per les instruccions Suma, Resta, And, Or, Carregar una paraula i Set on less than.
S’han d’afegir dos multiplexors, un per a l’entrada Write Register i un altre per Write Data Register. El primer s’utilitza perquè en el format R es fa ús del camp rd per indicar el registre on guardar el resultat, mentre que per al format L s’utilitza el camp rt. El segon s’utilitza perquè en les instruccions de format R es vol guardar el resultat de la ALU i amb la instrucció per Guardar una paraula el que volem guardar és el contingut d’una posició de memòria.
3.2.4. Dos sumadors
El processador disposa de dos sumadors aritmètics: SUMPC i SUM.
3.2.4.1. SUMPC
Figura 7. SUMPC
15
Aquest sumador agafa l’entrada outPC, li suma 4 i treu el resultat per la sortida outUM. S’utilitza per passar a la següent instrucció, i se suma 4 perquè cada instrucció ocupa una paraula, és a dir, 4 bytes.
3.2.4.2. SUM
Figura 8. SUM
Aquest agafa les dues entrades inAsum i inBsum, i el resultat de sumar les dues entrades ho treu per la sortida outAdd. Es fa ús per a les instruccions de salt per sumar l’offset a la posició de la instrucció actual més 4, en el cas que es compleixi la condició de salt. En aquest processador només tenim Branch on equal, com instrucció de salt.
3.2.5. Extensió de signe
Figura 9. Extensió de signe
Aquest mòdul agafa la seva entrada de 16 bits i la converteix en un número de 32 bits mantenint el signe. De manera que mira el bit més significatiu de l’entrada i posa els 16 bits més significatius de la sortida igual a aquest i els 16 menys significatius de la sortida iguals a l’entrada. Per exemple, si l’entrada val “1010101010101010” la sortida resultant és
“11111111111111111010101010101010”, mentre que si l’entrada és “0010101010101010” la sortida és igual a “00000000000000000010101010101010”.
És necessari utilitzar aquest mòdul per a les instruccions de format L, per al camp Address que té una grandària de 16 bits. Es converteix a un número de 32 bits, perquè els registres treballen amb dades d’una grandària de 32 bits.
16 3.2.6. ALU
Figura 10. ALU
La ALU realitza una operació amb el valor de les dues entrades inA i inB, i treu el resultat per outR. L’operació que realitza depèn de l’entrada ALU Operation, com es pot observar a la següent taula:
ALU Operation outR 0010 outR = inA + inB 0110 outR = inA - inB 0000 outR = inA and inB 0001 outR = inA or inB 0111 Si inA<inB → outR=1
Sino outR=0
Taula 3. Operacions afegides a la ALU
La sortida outZ és igual a 1 si outR és igual a 0, si no serà igual a 0. S’ha de tenir en compte que en la instrucció Branch on equal per comprovar si dos registres tenen el mateix valor, s’utilitza l’operació de resta i després es comprova el valor de la sortida outZ, i si val 1 vol dir que són iguals. Per a la instrucció de Carregar i Guardar una paraula s’utilitza l’operació de suma per sumar l’offset al valor del registre rs.
Com és lògic la instrucció de Suma utilitza la primera operació de la taula anterior, la instrucció de resta la segona, la And la tercera, la Or la quarta i la instrucció Set on less than utilitza l’última.
A l’entrada inB s’ha d’afegir un multiplexor, perquè si és una instrucció de format R es sumen dos registres, però si és una instrucció de format L excepte en el cas de la instrucció Branch on equal es te que sumar el valor del registre rs a l’offset.
3.2.7. Control de la ALU
Figura 11. Control de la ALU
17
Aquest mòdul rep el camp function de la instrucció que s’està executant per l’entrada, i per la sortida indica quina operació ha de realitzar la ALU.
3.2.8. Comptador de programa (PC)
Figura 12. Comptador de programa
Aquest mòdul s’utilitza per indicar a quina instrucció volem accedir. Quan hi ha un flanc de baixada del senyal de rellotge actualitza el valor de la sortida outPC, és a dir, mostra l’entrada inPC.
En l’entrada d’aquest mòdul s’ha de posar un multiplexor perquè si tenim la instrucció Branch on equal i no es relitza el salt o s’executa una altra instrucció s’ha de passar a la següent instrucció, però si es realitza el salt li haurem de sumar l’offset.
3.2.9. Desplaçament de 2 bits a l’esquerra
Figura 13. Desplaçament de 2 bits a l'esquerra
Aquest mòdul desplaça dues posicions a l’esquerra l’entrada i la mostra per la sortida, que és el mateix que multiplicar per 4 l’entrada, ja que el desplaçament a l’esquerra una posició és el mateix que multiplicar per 2. S’utilitza per a la instrucció Branch on equal per a multiplicar per 4 l’offset.
3.2.10. Unitat de control
Una vegada explicats tots el mòduls anteriors, la unitat de control s’encarrega de generar els senyals que controlen els diferents multiplexors, per tant, ha de rebre els camps opcode i function per poder controlar quins valors deixar passar pels multiplexors i quins no segons la instrucció que s’està executant i també els senyals d’escriptura i lectura de la memòria de registres i dades. A continuació, es mostren els valors dels senyals per a les diferents instruccions.
18
add sub and or lw sw beq slt MemWrite 0 0 0 0 0 1 0 0
MemRead 0 0 0 0 1 0 0 0 ALUSrc 0 0 0 0 1 1 0 0 BranchEqual 0 0 0 0 0 0 1 0 BranchNotEqual 0 0 0 0 0 0 0 0 Shift 0 0 0 0 0 0 0 0 MemtoReg 0 0 0 0 1 0 0 0 RegDst 1 1 1 1 0 0 0 1 RegWrite 1 1 1 1 1 0 0 1
Taula 4. Senyals de control del processador segons la instrucció
3.3. Aportació pròpia
Respecte al processador anterior utilitzat com a referència s’han realitzat una sèrie de millores que s’expliquen en els següents apartats.
Figura 14. Processador de punt de partida més algunes de les aportacions pròpies
Els canvis que s’han realitzat han estat per poder afegir les següents instruccions:
• Suma immediata (addi).
• And immediata (andi).
• Or immediata (ori).
• Or negada (nor).
• Desplaçament logic a l’esquerra (sll).
• Desplaçament logic a la dreta (srl).
19
• Branch on not equal (bne).
• Set on less than immediate (slti).
• Xor (xor).
A continuació, es mostra un exemple de com és el comportament de les instruccions anteriors, i també s’ha de tenir en compte que r1, r2 i r3 fa referència a tres registres, != vol dir diferent, r2 << 10 fa referència a un desplaçament a l’esquerra de 10 posicions i r2 >> 10 el mateix però un desplaçament a la dreta.
Instrucció Operació
addi r1, r2, 20 r1 = r2 + 20 andi r1, r2, 20 r1 = r2 - 20 ori r1, r2 , 20 r1 = r2 and 20 nor r1, r2, r3 r1 = not (r2 or r3)
sll r1, r2, 3 r1 = r2 << 10 srl r1, r2, 3 r1 = r2 >> 10
bne r1, r2, 20 Si r1!=r2 → salta a la posició PC+4+20*4 slti r1, r2, 20 Si r2<20 → r1=1
Sino r1=0 xor r1, r2, r3 r1 = r2 xor r3
Taula 5. Instruccions afegides al processador
De les instruccions anteriors són de format R l’operació lògica nor i xor i el desplaçament lògic a la dreta i a l’esquerra, i la resta de format L. A continuació, es mostren el valor dels diferents camps, i els que estan en blanc depenen dels registres utilitzats, de l’offset i de les constants.
address (16 bits) Instruccions Format
opcode (6 bits)
rs (5 bits)
rt (5 bits)
rd (5 bits)
shamt (5 bits)
function (6 bits)
addi L 8
andi L 12
ori L 13
nor R 0 0 39
sll R 0 0 0
srl R 0 0 2
bne L 5
slti L 10
xor R 0 0 38
Taula 6. Camps de les instruccions afegides al processador
20
Per a les instruccions nor i xor no s’ha afegit cap mòdul addicional, perquè tenen la mateixa estructura que les instruccions de suma i resta, però si que s’ha hagut de modificar la ALU, perquè pugui realitzar aquestes operacions.
Per a la instrucció Branch on not equal només s’ha posat una condició més en el multiplexor que està connectat a l’entrada inPC del comptador de programa, perquè es sumi l’offset si s’està executant aquesta instrucció i els dos registres a comparar són diferents.
Per a les instruccions de tipus immediat, no s’ha hagut d’afegir cap mòdul, perquè en les instruccions carregar i guardar una paraula ja suma a rs el camp Address. A més, ja tenim un multiplexor, que deixa guardar en un registre el resultat de la ALU i un altre, que permet guardar el resultat de l’operació en rt.
En el cas de les instruccions Desplaçament lògic a la dreta i a l’esquerra, s’ha afegit un mòdul anomenat Unsigned, que converteix un nombre de 5 bits en un de 32 bits afegint vint-i-set 0 com a bits més significatius. També s’han afegit dues operacions més a la ALU, per a poder realitzar el desplaçament i s’ha introduït un multiplexor en l’entrada inA de la ALU per saber quantes posicions s’ha de desplaçar l’entrada inB. Gràcies a aquest multiplexor podem accedir al camp shaft o rs de la instrucció en execució.
3.3.1. ALU
A la ALU se li han afegit més operacions per a poder executar algunes de les noves instruccions que s’han afegit.
ALU Operation outR 0010 outR = inA + inB 0110 outR = inA - inB 0000 outR = inA and inB 0001 outR = inA or inB 0111 Si inA<inB → outR=1
Sino outR=0 1100 outR = not (inA or inB) 0100 outR = inB << inA 0101 outR = inB >> inA 1000 outR = inA xor inB
Taula 7. Operacions de la ALU tenint en compte les instruccions afegides
S’ha de tenir en compte que la sèptima operació realitza un desplaçament a l’esquerra de inB tantes posicions com indica inA, i és utilitzada per la instrucció Desplaçament lògic a l’esquerra.
La tercera instrucció funciona de la mateixa manera que l’anterior, però realitza un desplaçament a la dreta i és emprada per la instrucció Desplaçament lògic a la dreta.
La instrucció Suma immediata utilitza la primera operació, la Resta immediata la segona, la And immediata la tercera, la Or immediata la quarta, la instrucció Set on less than immediate fa ús de la quinta, la Nor utilitza la sexta i la Xor l’última.
21
Finalment, s’ha de tenir en compte que per a la instrucció Branch on not equal es realitza l’operació de resta i es mira quin valor té la sortida outZ de la ALU, igual que en la instrucció Branch on equal, però en aquest cas es realitza el salt si outZ és igual a 0, és a dir, els dos registres són diferents.
3.3.2. Unitat de control
En la següent imatge podem observar les entrades i sortides de la unitat de control.
Figura 15. Unitat de control
Respecte al disseny inicial, s’ha ajuntat la unitat de control amb el control de la ALU, de manera que s’encarrega de controlar els senyals dels multiplexors, l’operació que realitza la ALU i els senyals d’escriptura i lectura de la memòria de registres i dades. En la següent taula es mostren els valors dels senyals per a cada instrucció, i per a identificar quina instrucció s’està executant té dues entrades, per on rep els camps opcode i function de la instrucció, que s’està executant.
add addi sub and andi or ori nor
MemWrite 0 0 0 0 0 0 0 0
MemRead 0 0 0 0 0 0 0 0
ALUSrc 0 1 0 0 1 0 1 0
BranchEqual 0 0 0 0 0 0 0 0
BranchNotEqual 0 0 0 0 0 0 0 0
Shift 0 0 0 0 0 0 0 0
MemtoReg 0 0 0 0 0 0 0 0
RegDst 1 0 1 1 0 1 0 1
RegWrite 1 1 1 1 1 1 1 1
ALU_operation 0010 0010 0110 0000 0000 0001 0001 1100
Taula 8.1. Senyals de control de les instruccions del processador, tenint en compte les noves
22
sll srl lw sw beq bne slt slti xor
MemWrite 0 0 0 1 0 0 0 0 0
MemRead 0 0 1 0 0 0 0 0 0
ALUSrc 0 0 1 1 0 0 0 1 0
BranchEqual 0 0 0 0 1 0 0 0 0
BranchNotEqual 0 0 0 0 0 1 0 0 0
Shift 1 1 0 0 0 0 0 0 0
MemtoReg 0 0 1 0 0 0 0 0 0
RegDst 1 1 0 0 0 0 1 0 1
RegWrite 1 1 1 0 0 0 1 1 1
ALU_operation 0100 0101 0010 0010 0110 0110 0111 0111 1000
Taula 8.2. Senyals de control de la resta d'instruccions
També té dues entrades: stop i start, per a controlar l’execució del programa, que es connecten a dos botons de la FPGA. Si es polsa el botó de start, provoca que el programa carregat en el processador s’executi fins que acabi o es pulsi stop. Si es polsa start després del stop, s’executa el programa carregat des del principi.
Quan es polsa start es fa un reset de manera que tots els registres es posen a zero i el comptador de programa també per anar a la primera instrucció. També s’ha creat una instrucció, que he anomenat end per indicar el final del programa, de manera que quan el processador rep aquesta instrucció aquest sap que ha arribat al final del programa.
Figura 16. Diagrama d'estats de la unitat de control
Per fer el reset s’ha afegit una entrada més al comptador de programa i a la memòria de registres. També s’ha afegit al comptador de programa dues entrades addicionals per llegir el camp opcode de la instrucció actual i la sortida execute de la unitat de control, de manera que si arriba la instrucció end o el processador deixa d’executar el programa, perquè s’ha polsat el botó stop deixi d’actualitzar el valor de sortida.
Finalment, s’ha de tenir en compte que el opcode de la instrucció end és igual a 63, i la resta de camps no els utilitza, per tant, no importa el valor que tinguin.
23 3.3.3. Programació de la SRAM
La càrrega del programa al microprocessador i la introducció de les dades a la memòria de dades es realitza definint els valors inicials de la memòria d’instruccions i de la memòria de dades en la seva descripció VHDL, per tant, cada vegada que es canvia alguna instrucció o dada, s’ha de tornar a realitzar la síntesi per carregar una altra vegada el fitxer de programació necessari per configurar el funcionament de la FPGA.
Queda fora dels objectius d’aquest projecte la implementació d’un mètode més eficient per introduir noves dades i/o instruccions, sense haver de realitzar l’etapa de síntesis lògica.
24
4. Implementació del processador
En aquest capítol s’indica quina és la FPGA utilitzada i també el software emprat. També s’explica quins problemes he tingut durant la realització del projecte i la solució, i alguns problemes soferts són aspectes importants a tenir en compte per a la implementació en VHDL d’un component per després carregar el fitxer de programació necessari per a poder configurar el funcionament de la FPGA.
4.1. FPGA
En aquest projecte s’ha utilitzat una placa de desenvolupament de Terasic, en concret una DE0, que permet treballar amb una FPGA d’Altera, la Cyclone 3 EP3C16F484. Aquest ens limitarà el nombre de blocs que es poden implementar com es mostra a continuació:
• 15408 elements lògics.
• 56 blocs de memòria encastats de tipus M9K.
• 504000 bits de memòria RAM.
• 65 multiplicadors encastats.
• 4 PLLs.
• 346 pins d’entrada/sortida per a l’usuari.
S’ha de tenir en compte que cada bloc de memòria M9K consta de 8192 bits, i si tenim en compte els bits de paritat té 9216 bits. Finalment, la DE0 genera una freqüència de 50 MHz, que per defecte és la freqüència de treball de la FPGA.
Figura 17. Placa DE0 de Terasic amb els seus components indicats
25
4.2. ModelSim
ModelSim és un programa que permet la simulació dels arxius VHDL que descriuen el processador i els diferents mòduls que conformen aquest. En aquest projecte s’ha emprat aquest programa per comprovar que els diferents mòduls funcionessin de la manera desitjada, però s’ha de tenir en compte que encara que funcioni correctament en la simulació no vol dir que una vegada carregat en la FPGA tot funcioni correctament, ja que hi ha alguns aspectes a tenir en compte.
Per a realitzar la simulació d’un arxiu VHDL, s’ha de crear un testbench, és a dir, un arxiu on es fa referència a un altre, el qual volem comprovar el seu funcionament, i descriure el valor que han de tenir les entrades durant un cert interval de temps. Després podrem observar el valor de les diferents entrades, sortides i senyals del mòdul a analitzar.
Figura 18. Testbench de MuxPC
En aquest projecte s’ha realitzat un testbench per a cada mòdul, tenint en compte que s’han hagut de repetir per solucionar alguns errors i per noves funcionalitats, que s’han afegit a mesura que avançava el projecte. Amb noves funcionalitats es fa referència a tots aquells senyals que s’han afegit com aportació pròpia respecte al processador agafat com a punt de partida.
A més d’analitzar els diferents mòduls per separat també es va comprovar el correcte funcionament de tots els mòduls junts sense la unitat de control i per últim amb la unitat de control.
En l’annex A, es poden trobar els testbench dels diferents mòduls i també de tots junts, però ja amb tots els senyals de la aportació pròpia. S’ha de tenir en compte que si se ha realitzat un testbench dels mòduls del processador agafat com a punt de partida i després s’han afegit els nous senyals i funcionalitats per realitzar un altre testbench.
Excepte en el cas on es comprova el funcionament de tots els mòduls junts sense la unitat de control ni el control de l’ALU, on es va comprovar el funcionament del processador de referència amb les noves instruccions afegides i el senyal de reset (però sense la unitat de control). Es va realitzar utilitzant el programa 1 de l’annex B.
26
4.3. Quartus II
Quartus II és un programa que permet realitzar tot el procés de programació d’una FPGA, és a dir, permet realitzar la síntesi, l’anàlisi temporal i carregar el fitxer de programació necessari per configurar el funcionament de la FPGA.
També inclou una eina anomenada MegaWizard Plug-In Manager que permet crear diferents mòduls, per exemple, una memòria i un PLL. Per a crear un mòdul s’ha d’especificar quin es vol crear, i després especificar alguns paràmetres per indicar el funcionament d’aquest. En aquest projecte només s’ha utilitzat per crear un PLL, i més endavant s’explica perquè s’ha utilitzat.
Figura 19. Creació d’un PLL amb MegaWizard Plug-In Manager
A més, hi ha una altra eina anomenada SignalTap II Logic Analyzer, que una vegada configurada la FPGA, permet visualitzar el valor dels diferents senyals durant un cert interval de temps.
Aquesta eina resulta útil per observar quins senyals no funcionen de la manera prevista. S’ha de indicar quin és el senyal de rellotge, i com de llarg és l’interval de temps que es desitja guardar.
També s’ha d’indicar a partir de quin instant es vol començar a guardar el valor dels diferents senyals, que s’indica especificant el senyal i el canvi o valor que ha de tenir.
També inclou TimeQuest Timing Analyzer, una eina que permet realitzar una anàlisi temporal de la versió estructural del microprocesador generada per l’eina de síntesis. Finalment, disposa d’una eina anomenada Programmer, que permet carregar la seqüència de bits corresponent al circuit que es vol implementar, necessària per programar la FPGA.
4.4. TimeQuest Timing Analyzer
És una eina que es troba en Quartus II, que permet realitzar una anàlisi temporal dels diferents senyals del component descrit, i es fa per poder saber si tots els senyals es propaguen complint els requisits temporals, és a dir, arriben al seu destí a temps per realitzar els càlculs necessaris amb aquests.
27
Figura 20. Anàlisi temporal del processador
S’ha de tenir en compte que és una aproximació i s’està posant en el pitjor cas, per tant, pot ser que encara que doni un error en l’anàlisi temporal, el programa bolcat sobre la FPGA funcioni correctament. Però és recomanable que no hi hagi errors en l’anàlisi temporal mentre s’està provant el correcte funcionament del programa.
Per a realitzar una anàlisi temporal s’ha de crear un arxiu amb l’extensió sdc, en el qual es defineix el període dels diferents senyals de rellotge que necessiten els components descrits, i s’assigna a cada senyal un nom i l’entrada del component on es connectarà. En l’arxiu [22] de la bibliografia s’explica com es crea aquest arxiu en detall.
Una vegada el funcionament és correcte, es pot programar la FPGA, encara que hi hagi errors en l’anàlisi temporal, per comprovar la freqüència màxima a la què poden treballar els components creats.
4.5. PLL
Un PLL és un acrònim en anglès que vol dir bucle bloquejat de fase. Es tracta d’un sistema de control, que genera un senyal per la seva sortida la qual té una freqüència i una fase relacionada amb el senyal d’entrada.
Està format per un detector de fase, un filtre, un oscil·lador i una realimentació, a més alguns tenen un divisor. Aquest divisor resulta útil, perquè permet reduir la freqüència de l’entrada. Els PLLs tenen diferents aplicacions, però per aquest projecte ens resulten útils, perquè podrem variar la freqüència de la FPGA i més endavant s’explica perquè s’han utilitzat.
4.6. Problemes i solucions
En aquest apartat s’expliquen els diferents problemes que s’han trobat en la fase d’implementació i com s’han solucionat.
El primer problema es va tenir amb la memòria de dades, en la descripció inicial d’aquest bloc, quan el senyal de lectura estava a nivell baix la sortida de la memòria quedava en alta impedància. Això no és possible en una FPGA, ja que no es poden implementar buffers triestat en una FPGA. Per tant, es va canviar la descripció de manera que quan el senyal de rellotge està en nivell baix, la sortida manté el valor anterior.
El segon problema que es va tenir va ser que en la descripció inicial, la memòria de registres realitzava algunes operacions en el flanc de baixada i altres en el de pujada, això a nivell de simulació funcional no donava cap problema. Però això no es pot implementar en una FPGA, ja que els flip flops disponibles actuen únicament per flanc de pujada (o de baixada, però no els dos al mateix temps).
28
També s’ha de tenir en compte que en cada arxiu VHDL on es descriu un component o es junten diferents mòduls, només es pot comprovar el flanc de pujada o baixada d’un senyal.
Un altre problema va ser que la unitat de control rebia el senyal de rellotge i la modificava perquè només es generés un canvi en la senyal de rellotge en aquells estats en què s’executava una instrucció de guardar o llegir dades en la memòria de dades o registres. Però s’ha de tenir en compte que el senyal de rellotge no es pot modificar d’aquesta manera i per tant, es va haver de modificar el comportament de la unitat de control, i també els registres de manera que nomes s’actualitza el valor d’aquests si es posa a 1 el senyal d’activació.
També es va tenir un altre problema, que va ser que la síntesi tardava molt de temps. En el codi VHDL es definia la memòria de dades i instruccions amb 216 posicions amb una grandària de 8 bits cada una i d’aquesta manera tardava 3 hores, ja que li costava assignar els recursos. Per tant, es va reduir de manera que es varen posar 28 posicions de 8 bits cada una i d’aquesta manera tarda tres minuts i mig aproximadament.
Van aparèixer errors quan es va realitzar l’anàlisi de temps. En conseqüència, es va dividir la instrucció de carregar d’una paraula en dos cicles, ja que és l’única instrucció que primer accedeix a la memòria de dades i després a la de registres, i podia ser que no li donés temps a executar-se. Tot i això, encara no es va solucionar el problema.
Un altre aspecte que es va tenir en compte per solucionar els errors en l’anàlisi de temps va ser posar els senyals de reset de la memòria de registres i del comptador de programa síncrones.
Però d’aquesta manera tampoc va desaparèixer l’error.
Finalment, es va fer un PLL amb l’eina MegaWizard Plug-In Manager de Quartus II, per a poder disminuir la freqüència del senyal de rellotge de la FPGA, i d’aquesta manera cada instrucció tingui més temps per executar-se, i d’aquesta manera es va aconseguir eliminar l’error.
També es vol utilitzar per comprovar quina és la freqüència màxima a què pot treballar. Al principi aquest provocava un error en l’anàlisi de temps, ja que si es llevava no donava error.
Però més endavant com igualment hi havia errors sense aquest, es va intentar tornar a introduir- ho, per comprovar si es solucionava, i d’aquesta manera es varen aconseguir solucionar els errors en l’anàlisi de temps.
El PLL creat es troba en l’arxiu VHDL anomenat CreateClock, i aquest té una entrada per on rep el senyal de rellotge de la FPGA i per la sortida treu un senyal de la qual la seva freqüència depèn de dos paràmetres. El primer paràmetre multiplica la freqüència i en l’arxiu VHDL generat té el nom de clk0_multiply_by, i el segon paràmetre la divideix i s’anomena clk0_divide_by. Per tant, la freqüència de la sortida es calcula amb la següent formula.
𝑓𝑜𝑢𝑡 = 𝑁 𝑀· 𝑓𝑖𝑛
Equació 1. Freqüència de treball del processador
S’ha de tenir en compte que el paràmetre N fa referència clk0_multiply_by y M a clk0_divide_by.
A més, fout és la freqüència del senyal de sortida i fin la de l’entrada.
Una altra dificultat que es va tenir va ser que donava un error, perquè t’avisava de què es superaven el màxim nombre de blocs de memòria M9K. Al principi, es va considerar que el problema podia estar en la memòria de dades i instruccions, per tant, es va fer ús de l’eina MegaWizard Plug-In Manager per crear un bloc de memòria RAM, i es varen compilar tots tres per separat per comprovar si algun necessitava molta memòria, però no va ser el cas per cap dels tres, per tant, aquí no estava l’error.
Més endavant es va trobar que el problema estava en l’eina SignalTap II Logic Analyzer, perquè per a poder guardar al valor dels senyals durant un interval de temps fa ús dels bits de memòria
29
i per tant, només es poden visualitzar uns pocs senyals, perquè si no utilitzarà més bits de memòria dels disponibles i donarà error.
Finalment, es varen tenir dos errors a l’hora de probar el funcionament del programa en la FPGA.
El primer va ser que el senyal de reset de la unitat de control no s’activava, i el que es va fer és controlar aquest senyal en un conjunt de condicions i no en diferents. També es va observar que la memòria de dades no carregava correctament les dades que hi havia a la memòria, i es va tornar a la instrucció carregar una paraula que tarda un cicle. D’aquesta manera funcionava correctament.
S’ha de tenir en compte que per observar el valor dels senyals en la FPGA, només podien observar unes poques i cada vegada que es canvien els senyals a observar s’ha de repetir la síntesi i carregar-lo sobre la FPGA. Per tant, va dur un cert temps trobar quins mòduls no funcionaven correctament.
30
5. Verificació experimental
En aquest projecte s’han creat tres programes, però només dos d’aquests tres s’han emprat per comprovar el correcte funcionament del processador en la FPGA. El primer programa s’ha emprat per comprovar durant la simulació que tingués la capacitat d’executar les diferents instruccions, com s’ha explicat anteriorment.
També s’ha implementat un mòdul de validació en VHDL, que s’ha afegit per a comprovar que el processador funciona correctament dins de la FPGA, sense haver d’estar mirant el valor de tots els senyals de sortida. A continuació, s’expliquen els dos programes i el mòdul.
5.1. Programa 2
Aquest programa agafa dades de la memòria i guarda el màxim en un registre i en una posició de memòria. El nombre de paraules, és a dir, de dades, que agafa depèn de l’offset de la segona instrucció, en concret en el programa carregat ho hem limitat a cinc. La posició de memòria, per on comença a agafar dades depèn de l’offset de la primera instrucció. S’ha de tenir en compte, que tots els registres comencen tenint un valor igual a cero.
També s’ha de tenir en compte, que com no tenim la instrucció de resta immediata, emprem la suma immediata amb un offset de -1. Es podria igualar un registre a 1, i després utilitzar l’operació de resta o posar un registre igual a -1 i realitzar una suma, però no s’han emprat perquè s’utilitzen dues instruccions en lloc d’una, i així és més simple el programa.
En la següent taula es mostren les dades que s’han introduït en cada posició de memòria, per trobar el màxim.
Posició Dada
0 00000000
1 00000000
2 00001010
3 00100000
4 00000101
5 00000000
6 00000100
7 00100000
8 00100000
9 00011000
10 00000000
11 00000000
12 00100000
13 00110000
14 00000000
15 00000000
31
16 00001000
17 00000000
18 10000000
19 00100000
Taula 9. Dades a cada posició de memòria
Amb aquestes dades el màxim és 00100000100000000000000000001000.
5.2. Programa 3
Aquest programa mira si una paraula que es carrega de la memòria de dades és una paraula capicua. Si és capicua, posarà a 1 el registre numero 5, i si no a 0. La paraula que s’agafa per a comprovar-ho depèn de la primera instrucció i en aquest cas s’agafa la posició 0.
Finalment, s’ha de tenir en compte que el registre numero 10, s’iguala a 65535 per a agafar tots els bits de la paraula i guardar-ho en el registre 1. S’ha de tenir en compte, que es fa una extensió de signe amb la funció de suma immediata i per això agafa tots els bits, ja que 65535 en binari els 16 bits menys significatius són igual a 0 i el camp d’offset és de 16 bits. D’aquesta manera també es podrien agafar menys bits per observar si és capicua.
Amb la quinta instrucció a partir de l’offset s’indica quants dels bits menys significatius de la paraula es volen comparar amb la resta. En aquest cas, l’offset es posa a 16, per comprovar que tota la paraula és capicua, ja que es separa per la meitat.
En aquest programa s’han posat les següents dades. El primer cas és una paraula capicua i el segon no.
Posició Dada
0 00001111
1 10000111
2 11100001
3 11110000
Taula 10.1. Dades en cada posició de la memòria per una paraula capicua
Posició Dada
0 00000000
1 00000000
2 00001010
3 00100000
Taula 10.2. Dades en cada posició de la memòria per una paraula no capicua
32
5.3. Mòdul de validació
Per a poder verificar el funcionament del processador s’ha creat un mòdul, com es pot veure en la següent imatge.
Figura 21. Mòdul per a la verificació del processador
Amb els programes anteriors ja sabem el resultat i en quin registre està, per tant, aquest mòdul rep per l’entrada RegisterX el valor guardat en un registre i el compara amb la variable Compare.
Si són iguals, posa a 1 la sortida LED, i si no són iguals la posa a 0. Actualitza el valor de la sortida, quan arriba a la instrucció end i la primera vegada que s’actualitza no és fins que s’ha executat una vegada el programa carregat. La sortida com indica el nom es connecta a un LED de la FPGA.
El valor de la variable Compare es canvia segons el programa carregat i el valor que s’hauria d’obtenir després d’executar el programa. El registre que rep també depèn del mateix que la variable Compare i per tant, s’ha creat una sortida anomenada RegisterX en la memòria de registres per poder rebre el valor guardat en un registre i amb la variable NumRegister es selecciona el registre que es vol treure per la sortida.
En el cas del programa 2 NumRegister és igual a 00100000100000000000000000001000 i Compare a 3. Mentre que en el programa 3 RegisterX és igual a 00000000000000000000000000000001 i Compare a 5.
5.4. Botons i LEDs
Els programes carregats tarden molt poc temps a executar-se, per tant, quasi no es pot veure si s’encén el LED que indica que s’està executant. Com dura molt poc tampoc es pot provar si funciona correctament el botó per a parar l’execució del programa. Per tant, per al LED es va utilitzar l’eina SignalTap II Logic Analyzer i es va poder observar que efectivament si que s’encenia.
Per a provar el botó que para l’execució del programa, es va llevar la instrucció end de manera que no es para d’executar, a no ser que es polsi el botó. D’aquesta manera es va poder comprovar que funcionava correctament, i a més d’aquesta manera també es pot observar que s’encén el LED, ja que no es para d’executar fins que es polsa el botó.
Així també podem comprovar que el botó per començar a executar el programa funciona correctament, ja que s’encén el LED, que indica que s’està executant. També es pot comprovar que funciona correctament, si s’encén el LED que indica que el resultat és l’esperat i per tant això vol dir que s’ha executat.
Gràcies a les solucions trobades als diferents problemes que han sorgit durant la implementació del processador, es va poder comprovar que funcionava correctament, ja que els LEDs i els botons funcionaven de la manera desitjada.
33
5.5. Recursos emprats
En el capítol 4 s’expliquen els diferents recursos disponibles a una FPGA, i a continuació s’indiquen quants d’aquests recursos s’han emprat per implementar el processador.
• 8925 elements lògics.
• 8591 funcions combinacionals.
• 2602 registres lògics.
• 5 pins d’entrada/sortida.
• 0 pins virtuals.
• 0 bits de memòria.
• 0 multiplicadors encastats.
• 1 PLL.
S’ha de tenir en compte que empra aquests recursos tenint cap senyal a observar en l’eina SignalTap II Logic Analyzer. Si posem els senyals dels dos botons, dels dos LEDs i de les posicions 20, 21, 22 i 23 de la memòria de dades, s’empren els següents recursos.
• 9922 elements lògics.
• 9125 funcions combinacionals.
• 3451 registres lògics.
• 5 pins d’entrada/sortida.
• 0 pins virtuals.
• 294912 bits de memòria.
• 0 multiplicadors encastats.
• 1 PLL.
Per últim, s’ha de tenir en compte que amb l’anàlisi de temps la màxima freqüència a la qual pot treballar el processador sense donar error, és de 25 MHz. La freqüència anterior no és la màxima a la qual pot treballar el processador, ja que es tracta d’un valor pessimista, perquè l’anàlisi de temps realitzat amb l’eina TimeQuest Timing Analyzer també és pessimista. Es va comprovar que podia funcionar correctament fins a una freqüència màxima de 35 MHz i per a poder variar la freqüència s’ha emprat un PLL creat amb l’eina MegaWizard Plug-In Manager.
Màxima freqüència segons TimeQuest Timing Analyzer 25 MHz Màxima freqüència experimental 35 MHz
Taula 11. Freqüència de treball del processador
La freqüència a la qual es va comprovar que el processador deixa de funcionar correctament és de 40 MHz, que és la que es va provar després d’assegurar-se que funcionava correctament a 35 MHz.
5.6. TimeQuest Timing Analyzer
Utilitzant l’eina TimeQuest Timing Analyzer, es pot observar quin és el senyal més lent coneixent quin senyal té el pitjor temps de slack. Dins d’aquesta eina s’ha d’utilitzar l’opció Report Timing per al senyal de rellotge que tingui pitjor temps de slack, i d’aquesta manera es pot trobar quin és el senyal més lent i per tant també conèixer el mòdul més lent.
34
En aquest projecte s’ha trobat que el senyal més lent és generat per la unitat de control, per tant, aquest és el mòdul més lent. Però s’ha de tenir en compte que aquesta eina realitza una aproximació i és pessimista, de manera que podria ser una vegada carregat el processador en la FPGA hi hagi un mòdul que sigui més lent, que l’indicat per TimeQuest Timing Analyzer.
35
6. Temps invertit
Com s’explica en l’apartat 2 el procés per carregar un programa en una FPGA està format pels següents passos: definició del codi VHDL, simulació, síntesis, anàlisi temporal i carregar el fitxer de programació necessari per configurar el funcionament de la FPGA.
Del temps total dedicat a carregar el processador en la FPGA, aproximadament tres mesos i mig, un 40% del temps ha estat per la definició del codi VHDL i la simulació d’aquest. La síntesi aproximadament un 10% i l’anàlisi temporal un 20%. Finalment, un 30% per carregar el fitxer i que funcioni tot correctament.
També s’ha de tenir en compte que per entendre com funciona el processador, per a després poder definir-ho en VHDL he estat aproximadament uns dos mesos i mig.
36
7. Conclusió
En aquest projecte s’ha aconseguit implementar un processador capaç d’executar les instruccions bàsiques del processador MIPS i altres més, gràcies als mòduls afegits al processador de referència, de manera que és capaç d’executar 17 instruccions en total. També s’ha de tenir en compte que el processador pot treballar fins a una frqüència màxima de 35 MHz.
S’ha de tenir en compte que no es pot programar la SRAM, per tant, cada vegada que es canvien les instruccions o dades de la memòria s’ha de repetir tot el procés per configurar la FPGA. No s’ha modificat el processador per permetre la programació de la SRAM, perquè no forma part dels objectius d’aquest projecte.
A més, he aconseguit augmentar els meus coneixements sobre la programació en VHDL, i com funcionen les FPGAs, en concret, el procés per carregar el fitxer de programació necessari per configurar el funcionament d’aquesta i algunes limitacions que s’han de tenir en compte a l’hora de la implementació.
Hi ha diferents continuacions a aquest projecte, i una podria ser implementar una manera d’introduir les instruccions en la FPGA sense haver de repetir tot el procés per a carregar el fitxer de programació cada vegada que es canvia una instrucció i el mateix per a la memòria de dades.
També que no s’hagin d’introduir les instruccions ni les dades en binari.
Una altra proposta per a una continuació seria millorar el temps d’execució de les diferents instruccions, i així tenir un processador més ràpid, i també introduir més mòduls al processador i modificar els existents perquè el processador pugui implementar més instruccions.
37
Bibliografia
[1] https://allanlopezunah.wordpress.com/2016/05/25/conjunto-de-instrucciones-de- arquitectura-mips/
[2] http://cidecame.uaeh.edu.mx/lcc/mapa/PROYECTO/libro20/13_principios_de_arquite ctura_risc.html
[3] https://allanlopezunah.wordpress.com/2016/06/04/arquitecturas-cisc-y-risc/
[4] https://www.aboutespanol.com/que-es-un-procesador-risc-841017
[5] http://rcmcomputointegrado.blogspot.com/2012/03/arquitectura-risc-y-cisc.html
[6] https://www.ecured.cu/RISC
[7] http://alanclements.org/mips_history.html
[8] https://academy.bit2me.com/que-es-fpga/
[9] https://planetachatbot.com/qu%C3%A9-es-una-fpga-y-por-qu%C3%A9- jugar%C3%A1n-un-papel-clave-en-el-futuro-e76667dbce3e
[10] https://www.xilinx.com/products/silicon-devices/fpga/what-is-an-fpga.html
[11] https://www.techopedia.com/definition/2357/application-specific-integrated-circuit- asic
[12] http://www.idc-
online.com/technical_references/pdfs/electronic_engineering/FPGA_ASIC_Design_Ad vantages_Disadvantages.pdf
[13] https://www.viewpointusa.com/industrial-embedded/when-is-an-fpga-worth-it-and- when-is-it-not-when-developing-an-industrial-embedded-system/
[14] http://hardwarebee.com/fpga-advantages-common-applications-today/
[15] D. A. Patterson and J. L. Hennessy, Computer organization and design: the hardware/software interface. Elsevier, 3a ed, 2005.
[16] R. Munden, ASIC and FPGA verification: a guide to component modeling. Elsevier, 2005.
38
[17] S. Kilts, Advanced FPGA design: architecture, implementation, and optimization.
Hoboken, N.J. : Wiley : IEEE, 2007.
[18] P. Wilson, Design recipes for FPGAs: using Verilog and VHDL. Newnes, 2a ed, 2016.
[19] https://knowledge.ni.com/KnowledgeArticleDetails?id=kA00Z000000P9T3SAK
[20] https://www.allaboutcircuits.com/technical-articles/what-exactly-is-a-phase-locked- loop-anyways/
[21] https://www.youtube.com/watch?v=5bg_3uelNM0
[22] https://fpgawiki.intel.com/uploads/3/3f/TimeQuest_User_Guide.pdf
39
Annexos
Annex A: Testbench
ALU_tb
--- --Arxiu: ALU_tb.vhd
--Descripció: Es tracta de un testbench del arxiu ALU --- LIBRARY IEEE;
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity ALU_tb is
GENERIC( NumberBitsOperation: INTEGER:=32);
end ALU_tb;
architecture Behavioral of ALU_tb is
SIGNAL inA, inB : STD_LOGIC_VECTOR (NumberBitsOperation-1 DOWNTO 0);
SIGNAL outR : STD_LOGIC_VECTOR (NumberBitsOperation-1 DOWNTO 0);
SIGNAL outZ : STD_LOGIC;
SIGNAL ALU_operation : STD_LOGIC_VECTOR (3 DOWNTO 0);
begin
dut1: entity work.ALU(DF1) generic map(NumberBitsOperation=>32) port map (inA=>inA, inB=>inB, outR=>outR, outZ=>outZ, ALU_operation=>ALU_operation);
stim_proc: process begin
inA <= "00000000000000000000000000000000";
inB <= "11111111111111111111111111101011";
ALU_operation <= "0010";
wait for 10 ns;
inA <= "00000000000000000000000000000111";
inB <= "00000000000000000000000000010000";
ALU_operation <= "0110";
wait for 10 ns;
inA <= "00000001000000000001000000001000";
inB <= "11111111111111111111111111101011";
ALU_operation <= "0000";
wait for 10 ns;
inA <= "00000000000000000000000000000000";