{"id":18006994,"url":"https://github.com/yne/r3k","last_synced_at":"2026-01-18T18:32:27.844Z","repository":{"id":25628933,"uuid":"29064215","full_name":"yne/R3K","owner":"yne","description":"5 stages pipeline MIPS R3000","archived":false,"fork":false,"pushed_at":"2015-12-19T01:03:03.000Z","size":1374,"stargazers_count":6,"open_issues_count":0,"forks_count":2,"subscribers_count":3,"default_branch":"gh-pages","last_synced_at":"2025-04-04T11:47:12.174Z","etag":null,"topics":["mips","pipeline-cpu","r3000","vhdl"],"latest_commit_sha":null,"homepage":"","language":"VHDL","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/yne.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-01-10T17:07:20.000Z","updated_at":"2022-03-19T15:53:47.000Z","dependencies_parsed_at":"2022-09-09T19:10:47.285Z","dependency_job_id":null,"html_url":"https://github.com/yne/R3K","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/yne/R3K","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yne%2FR3K","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yne%2FR3K/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yne%2FR3K/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yne%2FR3K/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/yne","download_url":"https://codeload.github.com/yne/R3K/tar.gz/refs/heads/gh-pages","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yne%2FR3K/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28547222,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-18T14:59:57.589Z","status":"ssl_error","status_checked_at":"2026-01-18T14:59:46.540Z","response_time":98,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["mips","pipeline-cpu","r3000","vhdl"],"created_at":"2024-10-30T01:11:35.056Z","updated_at":"2026-01-18T18:32:27.830Z","avatar_url":"https://github.com/yne.png","language":"VHDL","funding_links":[],"categories":[],"sub_categories":[],"readme":"﻿#Introduction :\n\nPour ce projet j'ai préféré travailler seul (comme sur tout les autres projets)\ncar cela me force a être autonome et a maitriser le projet de A à Z.\nJe n'ai n'y essayé de faire au plus vite ni d'implémenter le plus de fonction possible,\nmais plutôt de faire une architecture la plus optimisée et concise possible,\nmême si cela m'a forcé forcé à recommencer plusieurs fois (parfois de zéro).\n\nPlutôt que de ré-expliquer la partie commune a tout les projet\n(Qu'est-ce qu'un pipeline, un chemin de données, un aléa ...),\nce rapport se concentrera sur les points qui le différencie des autres\nainsi que les choix d'implémentations qui ont étaient fait et leur raisons.\n\nLa présentation des différents travaux réalisés sera répartie selon leur emplacement dans le chemin de données.\nEnfin, nous terminerons, avec les travaux qui ne sont pas en rapport avec un étage spécifique.\n\n\u003e Ce projet a été synchronisé avec [github.com/yne/R3K](http://github.com/yne/R3K)\n\u003e il est donc possible d'en afficher toute les versions\n\u003e afin de voir en détail les différentes itérations que j'ai faites sur le code.\n\n![pipeline risc](doc/pipeline.jpg)\n\n# Etage EI\n\nCet étage contient que 3 entités (ei_pc_mux, reg_pc, icache).\nLeurs rôles se limitent à modifier le PC et à fetcher depuis le cache d'instruction l'instruction pointée par le PC.\n\nLe registre PC peut être modifié par plusieurs sources d'adresse :\n- Son adresse suivante\n- Adresse d'un saut par registre (JR/JALR)\n- Adresse d'un saut par valeur immédiate (J/JAL)\n- Adresse d'un branchement immédiat/par registre (B*)\n\nJ'ai décidé d'implémenter ces modifications au plus tôt dans le pipeline.\nLes valeurs sont donc disponibles à différents étages dans le pipeline :\n- CP+4 : disponible en **EI**\n- JR/JALR : disponible en **DI**\n- J/JAL : disponible en **DI**\n- BNE,BLE... : disponible en **EX**\n\n\nIl nous faut donc un mux avec plusieurs entrées et plusieurs flags en paramètre et qui prendrait l'adresse la plus avancée parmi toutes celles valides :\n\n```VHDL\nwith(ei_pc_mux) select ei_pc_next \u003c= \n\treg_PC(PC'range)+1 when PC_NEXT, -- \"+\" surchargé par l'entity adder\n\tdi_pc_branch       when PC_BRANCH,-- BEQ,BNE,BLEZ,BGTZ,BLTZ,BGEZ,BLTZAL,BGEZAL\n\tei_pc_jump         when PC_JUMP,  -- J/JAL\n\tdi_qa(PC'range)    when PC_JUMP_R;-- JR/JALR\n```\n\nVoici l'agorithme de décision du chemin à emprunter :\n\n```VHDL\nei_pc_mux \u003c= PC_BRANCH when\n   ((reg_EX_ME.me_ctrl.BRANCH=B_NE) and (not(reg_EX_ME.ual_Z='1')                                                    ))\nor ((reg_EX_ME.me_ctrl.BRANCH=B_EQ) and (   (reg_EX_ME.ual_Z='1')                                                    ))\nor ((reg_EX_ME.me_ctrl.BRANCH=B_LE) and (   (reg_EX_ME.ual_Z='1') or     (reg_EX_ME.ual_V='1' xor reg_EX_ME.ual_N='1')))\nor ((reg_EX_ME.me_ctrl.BRANCH=B_LT) and (not(reg_EX_ME.ual_Z='1') and    (reg_EX_ME.ual_V='1' xor reg_EX_ME.ual_N='1')))\nor ((reg_EX_ME.me_ctrl.BRANCH=B_GT) and (not(reg_EX_ME.ual_Z='1') and not(reg_EX_ME.ual_V='1' xor reg_EX_ME.ual_N='1')))\nor ((reg_EX_ME.me_ctrl.BRANCH=B_GE) and (   (reg_EX_ME.ual_Z='1') or  not(reg_EX_ME.ual_V='1' xor reg_EX_ME.ual_N='1')))\nelse PC_JUMP   when di_ctrl_di.JUMP_IMM\nelse PC_JUMP_R when di_ctrl_di.JUMP_REG\nelse PC_NEXT;\n```\n\nOn remarque que le cas du BRANCH est le plus prioritaire (car disponible tardivement en EX), suivi par les JUMP (disponible en DI) et en dernier le PC+4 si aucune des conditions précédentes n'est remplie.\n\n# Etage DI\n\nAu fur et à mesure que j'implémentais les différents op-codes de l'unité de contrôle,\nj’avais la sensation grandissante que cette unité pouvait être factorisable\nen fonction des bits des instructions qu'elle traitait.\nCe sont surtout les instructions SIGNED/UNSIGNED (avec seulement leurs derniers bits qui varient)\nqui m'ont mis la puce à l'oreille.\n\nA ma première tentative, l'unité de contrôle complète tenais dans (180 lignes).\nEt voici la version après réécriture complète en 40 lignes (soit 5 fois plus légère):\n\n```VHDL\ndi_ctrl_di \u003c= DI_DEFL; -- IMM_UNSIGNED,'0','0'\ndi_ctrl_ex \u003c= EX_DEFL; -- SUB,ALU_SIGNED,REGS_QA,REGS_QB,REG_RT\ndi_ctrl_me \u003c= ME_DEFL; -- MEM_32,B_NULL,MEM_READ,MEM_UNSIGNED,'0'\ndi_ctrl_er \u003c= ER_DEFL; -- REG_WRITE,ALU_S\n\nif(OP=TYPE_R)then                  -- REG ARITHMETIC\n\tdi_ctrl_di.JUMP_REG  \u003c= '1'        when F=JR  or F=JALR;\n\tdi_ctrl_ex.REG_DST   \u003c= REG_RD;-- \n\tdi_ctrl_ex.ALU_SIGNED\u003c= F(0);-- 1=unsigned\n\tdi_ctrl_ex.ALU_SRCA  \u003c= REGS_QB    when F=LSL or F=LSR;\n\tdi_ctrl_ex.ALU_SRCB  \u003c= VAL_DEC    when F=LSL or F=LSR;\n\tdi_ctrl_er.REGS_SRCD \u003c= NextPC     when F=JR  or F=JALR;\n\tdi_ctrl_er.REGS_WE   \u003c= REG_RDONLY when F=JR;\n\tdi_ctrl_ex.ALU_OP    \u003c= LSR        when F=JR  or F=JALR else F;\n\tif(f=SYNC)then report \"BREAK INSTR\" severity FAILURE;end if;\nelsif(OP=TYPE_B)then               -- REG BRANCH\n\tdi_ctrl_ex.ALU_SRCB   \u003c= IMM_0;\n\twith(B) select di_ctrl_me.branch \u003c= \n\t\tB_LT when BLTZ|BLTZAL,B_GE when BGEZ|BGEZAL,B_NULL when others;\nelsif OP(5 downto 4) = \"10\" then   -- MEMOIRE L*/S*\n\tdi_ctrl_ex.REG_DST    \u003c= REG_RD when OP(3)='1' else REG_RT;\n\tdi_ctrl_me.DC_RW      \u003c= OP(3);\n\tdi_ctrl_me.DC_DS      \u003c= OP(1 downto 0);\n\tdi_ctrl_me.DC_SIGNED  \u003c= OP(2);\n\tdi_ctrl_er.REGS_WE    \u003c= OP(3);\nelsif OP(5 downto 3) = \"001\"  then -- IMM ARITHMETIC (REG_RT=REGS_QA \u003c?\u003e IMMD)\n\tdi_ctrl_er.REGS_SRCD  \u003c= ALU_S;\n\tdi_ctrl_ex.ALU_SRCA   \u003c= IMMD   when OP=LUI;\n\tdi_ctrl_ex.ALU_SRCB   \u003c= IMM_16 when OP=LUI else IMMD;\n\tdi_ctrl_ex.ALU_SIGNED \u003c= OP(0);-- 1=unsigned\n\tdi_ctrl_di.SIGNED_EXT \u003c= IMM_SIGNED when OP=ADDI or OP=ADDIU or OP=SLTI;\n\tdi_ctrl_ex.ALU_OP \u003c= OP;\nelsif OP(5 downto 2) = \"0001\" then -- IMM BRANCH (JUMP_BRA = r[rs]\u003c?\u003er[rt/0])\n\twith(OP) select di_ctrl_me.branch \u003c=\n\t\tB_EQ when BEQ,B_NE when BNE,B_LE when BLEZ,\tB_GT when BGTZ,B_NULL when others;\nelsif OP(5 downto 1) = \"00001\" then -- JUMP (J/JAL)\n\tdi_ctrl_di.JUMP_IMM \u003c= '1';\n\tdi_ctrl_er.REGS_WE \u003c= REG_WRITE when OP = JAL else REG_RDONLY;\n\tdi_ctrl_ex.REG_DST \u003c= REG_31    when OP = JAL else REG_RD;\nend if;\n```\n\nOn remarque que :\n\n- Les attributs les plus utilisés par les différents étages sont mis dans la valeur par défaut de l'étage (valeurs qui sont assignées dès le début de la fonction).\nAinsi, même si aucun ALU_OP n'est spécifié (comme dans les étages REG BRANCH_R et IMM BRANCH) c'est l'opération SUB qui sera effectuée (car la plus utilisée par l'ensemble des étages).\n- Le choix du code opération de l'ALU est déporté dans l'ALU elle même (dont nous verrons en détail la raison dans le chapitre EX).\n- Le code des instructions contient souvent les attributs à certain offset.\nPar exemple, pour les instructions de type registre, on a les codes fonctions suivants :\n\n| Name | Code     |\n|------|----------|\n| ADD  | `10000 0` |\n| ADD**U** | `10000 1` |\n| SUB  | `10001 0` |\n| SUB**U** | `10001 1` |\n| SLT  | `10101 0` |\n| SLT**U** | `10101 1` |\n| LSL  | `00000 0` |\n| LSR  | `00001 0` |\n| JR   | `00100 0` |\n| JALR | `00100 1` |\n| iAND | `10010 0` |\n| iOR  | `10010 1` |\n| iXOR | `10011 0` |\n| iNOR | `10011 1` |\n\nOn remarque que le bit de poids faible des opérations arithmétiques contient le signe de l'opération (signed/unsigned).\nOn peut donc simplement écrire  `di_ctrl_ex.ALU_SIGNED\u003c= F(0);` pour mettre l'attribut à la bonne valeur.\n\n\u003e Cela signifie que les autres instructions (sauts, opérations booléennes et opérations décalages) auront cet attribut de signe positionné a un étant qui ne leur correspond pas.\n\u003e Mais ces derniers ne prennent pas en compte cet attribut donc ce n'est pas un problème\n\n\n\nUn exemple plus flagrant avec les instructions de type mémoires :\n\n| Name | Code     |\n|------|----------|\n|LB    | `10 0 0 00` |\n|LH    | `10 0 0 01` |\n|LW    | `10 0 0 11` |\n|LBU   | `10 0 1 00` |\n|LHU   | `10 0 1 01` |\n|SB    | `10 1 0 00` |\n|SH    | `10 1 0 01` |\n|SW    | `10 1 0 11` |\n\nOn remarque que les bits sont de la forme :\n\n| Bits | Description | valeurs |\n|----- |-------------|---------|\n| 5-4  | toujours | `10` |\n| 3    | Type  | `0`=Load, `1`=Store |\n| 2    | Signe | `0`=signed, `1`=unsigned |\n| 1-0  | Taille| nb d'octets `00`=1, `01`=2, `10`=3, `11`=4 |\n\nCette unité de contrôle est valide, et le processeur produit bien successivement les valeurs 0,1,-1,-1,2,-2,0 et 8 dans les registres.\n\n```\n00100000000000000000000000000001;ADDI  r0,0001=\u003e r0\n00100000000000010000000000000001;ADDI  r0,0001=\u003e r1\n00100000000000101111111111111111;ADDI  r0,FFFF=\u003e r2\n00100100000000111111111111111111;ADDIU r0,FFFF=\u003e r3\n00000000001000010010011111100000;ADD   r1,r1 =\u003e r4\n00000000010000100010111111100001;ADDU  r2,r2 =\u003e r5\n00000000000000000000000000000000;LSL   r0,0  =\u003e r0 (NOP)\n00000011111000010011100011000000;LSL   r1,4  =\u003e r7\n00000000000000000000000000000000;NOP\n00000000000000000000000000000000;NOP\n00000000000000000000000000000000;NOP\n00000000000000000000000000001111;SYNC\n00000000000000000000000000001000;JR    r0\n```\n\n![screen bench alu](doc/bench_alu.png)\n\nNous pouvons optimiser encore plus et directement envoyer la partie de l'instruction représentant la taille de l'accès mémoire en entrée de notre banc mémoire générique.\nC'est ce qui est fait dans la ligne `di_ctrl_me.DC_DS \u003c= OP(1 downto 0)`\n\nIl serait possible d'optimiser encore plus l'unité en déportant le switch qui assigne le ALU_OP dans l'alu en créant un `ALU_CTRL` à coté de l'alu et en faisant suivre l'instruction jusqu'à\nl'étage de l'ALU (EX) pour que l'ALU_CTRL fasse elle-même le switch pour déterminer l'opération.\n\n# Etage EX\n\nL'étage EX posede 1 ALU est 3 mux :\n\n### MUX : ALU_SRC_A\n\nQui met en entrée A de l'ALU, les signaux :\n- QA pour les opérations type registre (excepté LSL/LSR)\n- QB pour les opérations LSL/LSR\n- IMMD pour les opérations arithmétiques immédiates\n\n### MUX : ALU_SRC_B\n\nQui met en entrée B de l'ALU, les signaux :\n- QB : pour la plupart des opérations type registre\n- IMM : pour toutes les opérations de type arithmétique à valeur immédiate\n- VALDEC : pour les instructions LSL et LSR qui contiennent le nombre de décalage dans leur zone \"valdec\"\n- 16 : pour l'instruction LUI qui nécessite un décalage à gauche de 16 bits\n- 0 : pour les comparaisons type BNEZ (comparaison \"QA vs 0\")\n\n\u003e Il serait possible de diminuer le nombre d'entrée de ce mux (actuelement de 5)\n\u003e en redirigeant le mux vers le signal VALDEC qui contiendrait la valeur constante (0, 16) mise en place par l'unité de contrôle.\n\u003e Mais cela sous-entendrait que l'unité de contrôle aurait accès à ces signaux en \"out\" ce qui n'est pas le cas actuellement,\n\u003e et qui serait impossible sans ajouter une grande quantité de fil, alors que notre but est d'en économiser...\n\n### MUX : REG_DST\nCe registre est en charge de définir le registre de destination où sera stocké le résultat (qui peut-être soit une lecture mémoire, la sortie de l'ALU, le prochain emplacement du PC)\n\n### ALU\n\nComme nous l'avons vue précédemment, le contrôle de l'opération de l'ALU a été déplacé dans l'ALU elle-même.\nLa raison est simple, cela permet d'éviter 2 switch dans le code de l'unité de contrôle et de les combiner à la place du switch existant dans l'ALU.\nL'ALU ne switchera donc plus sur ALU_OP mais sur l'OPCODE ou le FCODE de l'instruction :\n\n```VHDL\nwhen iAND|ANDI =\u003e tmp_S := A and B;\nwhen iOR |ORI  =\u003e tmp_S := A  or B;\nwhen iNOR      =\u003e tmp_S := A nor B;\nwhen iXOR|XORI =\u003e tmp_S := A xor B;\nwhen LSL |LUI  =\u003e tmp_S := shl(A,B);\nwhen LSR       =\u003e tmp_S := shr(A,B);\n```\n\n\u003e Les instructions type registre `JR` et `JALR` entre respectivement en conflit avec `ADDI` et `ADDIU`.\n\u003e Donc à la réception d'un `JR` l'ALU pensera avoir à faire à un `ADDI` or le JR doit faire effectuer un `LSR` à l'ALU.\n\u003e La solution est donc de ne pas envoyer le FCODE pour le `JR` et le `JALR` mais directement l'`LSR` :\n\n```\ndi_ctrl_ex.ALU_OP    \u003c= LSR        when F=JR  or F=JALR else F;\n```\n\n# Etage ME\n\nLe registre mémoire a été l'une des premières entités sur laquelle j'ai compris qu'il y avait un potentiel d'optimisation.\nJ'ai donc décidé de la modifier pour qu'elle puisse accepter n'importe quel taille de donnée en entrée en utilisant un `natural` comme attribut du DS.\nPuis, dans une seconde itération j'ai décidé de n'utiliser que des `std_logic_vector` car cela permettait de lui mettre en entrée directement les bits des opcodes sans avoir à passer par un `conv_integer`.\n\n```VHDL\nP_CACHE: process(CLK, RST, ADR, AS, RW, DS, SIGN) begin\nif RST = '0' then \n\tREGS \u003c= (others=\u003e(others=\u003e(others=\u003e'0'))) when STRING'(FILENAME)=\"\" else LOAD_FILE(STRING'(FILENAME));\nelse\n\tif AS = '0' then\n\t\tQ \u003c= (others =\u003e 'Z');\n\telse \n\t\tif (DS/=MEM_8 and ADR(natural(log2(real(conv_integer(DS)+1)))-1 downto 0) /= ZERO)then\n\t\t\tBerr \u003c= '0';\n\t\telse\n\t\t\tBerr \u003c= '1';\n\t\t\tif RW = MEM_READ then \n\t\t\t\tQ \u003c= (others =\u003e '0');\n\t\t\t\tfor i in 0 to conv_integer(DS) loop\n\t\t\t\t\tQ((i+1)*8 - 1 downto i*8) \u003c= REGS(conv_integer(ADR(I_ADR'range)))(conv_integer(ADR(B_ADR'range))+i);\n\t\t\t\tend loop;\n\t\t\t\tif SIGN = MEM_SIGNED then\n\t\t\t\t\tQ(DBUS_WIDTH-1 downto (conv_integer(DS)+1)*8) \u003c= (others=\u003eREGS(conv_integer(ADR(I_ADR'range)))(conv_integer(ADR(B_ADR'range)) + conv_integer(DS))(0));\n\t\t\t\tend if;\n\t\t\telse -- MEM_WRITE\n\t\t\t\tif CLK'event and CLK=ACTIVE_FRONT then\n\t\t\t\t\tfor i in 0 to (conv_integer(DS)) loop\n\t\t\t\t\t\tREGS(conv_integer(ADR(I_ADR'range)))(conv_integer(ADR(B_ADR'range)) + i) \u003c= D((i+1) * 8 - 1 downto i*8);\n\t\t\t\t\tend loop;\n\t\t\t\tend if;\n\t\t\tend if;\n\t\tend if;\n\tend if;\nend if;\nend process;\n```\n\nCette dernière modification permet à l'entité memory d'obtenir directement la taille à copier à partir du code de l'instruction : `di_ctrl_me.DC_DS \u003c= OP(1 downto 0);`\n\n# Etage ER\n\nL'étage ER ne possède qu'un mux servant a rediriger la source du résultat (REGS_SRCD) vers le banc de registre.\nLes différentes sources de résultats sont :\n- PC : le prochain PC (utilisé pour stocker dans un registre, le plus souvent le 31, l'adresse de retour de la fonction)\n- Q : sortie mémoire (opération de Load)\n- S : sortie de l'UAL (la plupart des opérations en général)\n\n```VHDL\ner_adrw\u003c=reg_ME_ER.reg_dst;\nwith(reg_ME_ER.er_ctrl.REGS_SRCD) select er_regd \u003c= \n\treg_ME_ER.mem_Q        when MEM_Q,\n\treg_ME_ER.ual_S        when ALU_S,\n\tconv_std_logic_vector(conv_integer(reg_ME_ER.pc_next), DATA'length)      when NextPC;\nend behavior;\n```\n\u003e Il doit y avoir un meilleur moyen d'arriver a maper deux std_logic_vector de taille différentes\n\u003e peut être en utilisant `my_vec \u0026 \"00\"` ?\n\nLa sortie du `MUX_SRCD` est stoquée dans la banc de registre indiqué par `REG_W` a condition que `er_reg_w` autoriser l'ecriture dans le bac.\n\n\u003e Une optimisation possible serait de se passer totalement du `er_reg_w` et de laisser toujours l'ecriture possible\n\u003e et de pointer `REG_W` vers `R0` lorsque l'on ne veu pas ecrire le resultat dans un registre\n\u003e puisque le registre `R0` registre reste a 0 quoi que l'on y ecrive.\n\n# Ajout de l'instruction SYNC\n\nJe trouvais frustrant de ne pas avoir de contrôle sur l'arrêt de la simulation autre que le timeout,\nj'ai donc décidé d'ajouter une instruction SYNC (famille des instructions TYPE_R, FCODE=001111)\nCette instruction provoque le code VHDL suivant :\n\n \n```VHDL\nif(f=SYNC)then\n    report \"BREAK INSTR\" severity FAILURE;\nend if;\n```\n\n![screen synch stop](doc/sync_exec.png)\n\nCela permet de mettre un terme à la simulation proprement (à l'image d'un exit() en C).\n\n# IEEE.math_real.log2\n\nJ'ai supprimé toute trace de la fonction `cpu_package.log2` au profit de la librairie `IEEE.math_real.log2` car :\n- Si ça existe déjà, pourquoi alourdir le code avec des fonctions doublon ?\n- elle est surement bien faite (c'est l'IEEE quand même !)\n\n# JS-ASM\n\nPour m'occuper (et parce-que M Thiebolt nous avais proposé de le faire)\nj'ai décidé de créer mon propre assembleur R3000 en me basant sur un de mes anciens projets d'assembleur MIPS.\nLe résulta est un assembleur codé en langage web capable de générer du code à la volée (génération en parallèle de la saisie de l'utilisateur)\nSa force réside dans le fait qu'il est syntaxiquement beaucoup moins strict que l'assembleur *r3kasm2* mais il ne gère pas (encore) les labels.\n\n![screen synch stop](doc/js-asm.png)\n\n# VHDL 2 GRAPH\n\nPour m'aider lors de la conception du R3K j'utilisais très souvent le schémas du pipeline fourni par M Thiebolt.\nMais pour vérifier que mon code suivait bien mes dessins, je devais faire de fastidieuses vérifications de code.\nJ'ai donc décidé de faire un outil capable de régénérer le graphique des étages du pipeline directement à partir du code source `risc.vhd`\nLe résultat est assez impressionnant et plus satisfaisant que je ne l'espérais :\n\n![screen risc graph](doc/risc-graph.png)\n\n# Conclusion\n\nMême si je n'ai pas tout fait (cache,envoie,...) je me suis bien amusé sur ce projet.\nCela a été pour moi un excellent exercice qui m'a permis de bien maitriser VHDL\n(ce qui pour moi etais un des objectifs principaux de ce projet)\nainsi que l'outil ModelSim que l'on adore détester mais qui au final s'est avéré être extrêmement puissant et personnalisable.\n\n# Annexes\n\n### [cpu_package](cpu_package.vhd)\n### [registres](registres.vhd)\n### [memory](memory.vhd)\n### [risc](risc.vhd)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyne%2Fr3k","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyne%2Fr3k","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyne%2Fr3k/lists"}