-- yasep/VHDL/microYASEP.vhdl -- créé le 19 février 2012 par Yann Guidon / whygee@f-cpu.org -- version jeu. févr. 23 03:01:31 CET 2012 -- version mar. mars 13 12:22:20 CET 2012 : ajout du flag INV -- version jeudi 15 mars 2012, 01:02:24 : found that damned Field_SI4/condition bug !!! -- version dim. mars 18 16:39:34 CET 2012 : EQ OK, manquent MIN/MAX -- version mar. mars 20 17:10:09 CET 2012 : reboot, now can execute slower -- version ven. août 10 13:16:43 CEST 2012 : FlagChangeZero added Library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; library work; use work.yasep_definitions.all; use work.yasep_utils.all; entity microYASEP is port( -- predivider ExecPrediv: in SLV4 := "0000"; -- increase to slow down the core -- invalid opcode flag : INV : inout std_logic; -- sticky flag of invalid instruction ProgRdEn: inout std_logic; -- ='0' when read is active RAM_out: in SLV16; -- read port of the instruction memory PC, -- address of the currently executed instruction NPC: inout SLVAI; -- nouvelle valeur du compteur d'instructions /!\ LSB=0 -- so we can spy on the core : Condition2, condition, WR_en, WB_en, phase, Carry, FlagZero : inout std_logic; R1, R2, R3, R4, R5, D1, D5, -- some of the registers Result: inout SLVY; -- data to write back DST, Field_cond, Field_SI4, Field_SND : inout SLV4; -- GET, PUT : GetReadPort: in SLVY; PutWritePort, SRaddress: out SLVY; GET_en, PUT_en: out std_logic := '0'; -- E/S spécifiques flag_addr: in SLV4; flags_in: in std_logic; -- could be ORed to R1 at any cycle A1_en : out std_logic; clk, reset : in std_logic); -- reset must be resynchronised end microYASEP; architecture reboot of microYASEP is signal ROP2_X2, Addsub, Carry_out, compare_signed, zero_out, CondZero : std_logic := '0'; signal opcode: SLV6 := "000000"; signal cond_func, form: SLV2 := "00"; signal PC1 : SLVAI := (SLVAI'range=>'0'); -- speculatively incremented PC signal CPU_prediv : SLV4; -- counter for the wait states signal SI4, RSI4_out, Reg_SI4, SND, RSND_out, -- registers and operands ActualA, ActualB, ROP2_RS1, ROP2_RS2, ROP2_xor, -- temporary results ASU, ROP2, -- outputs of execution units A1, A2, D2, A3, D3, A4, D4, A5 : -- other registers SLVY := (SLVY'range=>'0'); signal sumAux : unsigned(YASEP_SIZE+1 downto 0); signal int_opcode: integer := 0; function opcode_defined(o:SLV6; p:SLV6; i:integer) return boolean is begin if o=p and FlagValidInstruction(i)='1' then return true; end if; return false; end; begin ProgRdEn <= '0' when CPU_prediv="0000" and (RAM_out(0)='1' or phase='1') else '1'; int_opcode<=safe_to_integer(opcode); -- pre-decodes the opcode -- read the registers RSND_out <= SLVAI_to_SLVY(PC) when RAM_out(11 downto 8)=REG_PC else R1 when RAM_out(11 downto 8)=REG_R1 else R2 when RAM_out(11 downto 8)=REG_R2 else R3 when RAM_out(11 downto 8)=REG_R3 else R4 when RAM_out(11 downto 8)=REG_R4 else R5 when RAM_out(11 downto 8)=REG_R5 else D1 when RAM_out(11 downto 8)=REG_D1 else A1 when RAM_out(11 downto 8)=REG_A1 else D2 when RAM_out(11 downto 8)=REG_D2 else A2 when RAM_out(11 downto 8)=REG_A2 else D3 when RAM_out(11 downto 8)=REG_D3 else A3 when RAM_out(11 downto 8)=REG_A3 else D4 when RAM_out(11 downto 8)=REG_D4 else A4 when RAM_out(11 downto 8)=REG_A4 else D5 when RAM_out(11 downto 8)=REG_D5 else A5;--when RAM_out(11 downto 8)=REG_A5 RSI4_out <= SLVAI_to_SLVY(PC) when RAM_out(15 downto 12)=REG_PC else R1 when RAM_out(15 downto 12)=REG_R1 else R2 when RAM_out(15 downto 12)=REG_R2 else R3 when RAM_out(15 downto 12)=REG_R3 else R4 when RAM_out(15 downto 12)=REG_R4 else R5 when RAM_out(15 downto 12)=REG_R5 else D1 when RAM_out(15 downto 12)=REG_D1 else A1 when RAM_out(15 downto 12)=REG_A1 else D2 when RAM_out(15 downto 12)=REG_D2 else A2 when RAM_out(15 downto 12)=REG_A2 else D3 when RAM_out(15 downto 12)=REG_D3 else A3 when RAM_out(15 downto 12)=REG_A3 else D4 when RAM_out(15 downto 12)=REG_D4 else A4 when RAM_out(15 downto 12)=REG_A4 else D5 when RAM_out(15 downto 12)=REG_D5 else A5;--when RAM_out(15 downto 12)=REG_A5 -- Control path : control the write back -- the instruction can write but the opcode is not checked. WB_en <= '1' when FlagValidInstruction(int_opcode)='1' and condition='1' and phase='1' and CPU_prediv="0000" else '0'; -- enable the write back of registers WR_en <= '1' when WB_en='1' and FlagNoWriteBack(int_opcode)='0' else '0'; condition <= FlagNoCondition(int_opcode) -- is this instruction abortable ? or (RAM_out(5) xor Condition2) -- condition (computed below) when form = FORM_EXT -- extended / conditional instruction else '1'; -- true otherwise Condition2 <= R1(safe_to_integer(Field_cond)) when cond_func=COND_BIT else '1' when cond_func=COND_ZERO and Field_cond="0000" -- always else FlagZero when cond_func=COND_MSB and Field_cond="0000" -- EQ else Carry when cond_func=COND_LSB and Field_cond="0000" -- carry else CondZero when cond_func=COND_ZERO else RSI4_out(0) when cond_func=COND_LSB else RSI4_out(YASEP_SIZE-1); -- when cond_func=COND_MSB Field_cond <= RAM_out(15 downto 12); -- not to be mistaken with Field_SI4 ! cond_func <= RAM_out(7 downto 6); CondZero <= '1' when RSI4_out=(RSI4_out'range=>'0') else '0'; -- Address path : DST <= Field_SND when form(0) = '0' -- Short/Long else Field_SI4 when form(1) = '0' -- R/I4 else RAM_out(R_SND'range); -- matches DST2 because it's the 2nd halfword -- create the address of the next instruction PC1 <= std_logic_vector(unsigned(PC)+1); NPC <= Result(NPC'range) when WR_en='1' and DST=REG_PC else SI4(NPC'range) when WB_en='1' and opcode_defined(Op_CALL,opcode,int_opcode) -- should be simplified and routed through result? else ASU(NPC'range) when WB_en='1' and opcode_defined(Op_CALL2,opcode,int_opcode) else PC1; -- Datapath : select the data to write to a register Result <= ASU when opcode(2 downto 0)=GROUP_ASU else ROP2 when opcode(2 downto 0)=GROUP_ROP2 else SLVAI_to_SLVY(PC1) when opcode_defined(Op_CALL,opcode,int_opcode) or opcode_defined(Op_CALL2,opcode,int_opcode) else GetReadPort when opcode_defined(Op_GET,opcode,int_opcode) else SI4; -- the remaining instructions, such as MOV -- Datapath : Select SI4 SI4 <= RAM_out when form = FORM_RRI16 -- long immédiat else (YASEP_SIZE-5 downto 0 => Field_SI4(3)) & Field_SI4 -- sign extension when (form = FORM_RI4) -- short Imm4 or (form = FORM_EXT and RAM_out(1)='1') -- extended else Reg_SI4; -- short RR -- SR access : SRaddress <= SI4; PutWritePort <= SND; PUT_en <= '1' when opcode_defined(Op_PUT,opcode,int_opcode) and phase='1' and CPU_prediv="0000" else '0'; GET_en <= '1' when opcode_defined(Op_GET,opcode,int_opcode) and phase='1' else '0'; A1_en <= '1' when WR_en='1' and DST=REG_A1 else '0'; ----------------- ALU/ROP2 ----------------- -- ROP2_S1 ROP2_S2 ROP2_X2 Addsub compare_signed -- ADD X X X 0 0 -- SUB X X X 1 0 -- compare (== SUB, only carry-out used) -- signed X X X 1 1 -- unsigned X X X 1 0 -- AND 1 0 0 0 0 -- ANDN 1 0 0 1 0 -- NAND 1 0 1 0 0 -- OR X 1 0 0 0 -- ORN X 1 0 1 0 -- NOR X 1 1 0 0 -- XOR 0 0 0 0 0 -- XORN 0 0 0 1 0 ROP2_X2 <= '1' when opcode_defined(Op_NOR ,opcode,int_opcode) or opcode_defined(Op_NAND,opcode,int_opcode) else '0'; Addsub <= '1' when opcode_defined(Op_SUB ,opcode,int_opcode) or opcode_defined(Op_CMPS,opcode,int_opcode) or opcode_defined(Op_CMPU,opcode,int_opcode) or opcode_defined(Op_ANDN,opcode,int_opcode) or opcode_defined(Op_ORN ,opcode,int_opcode) or opcode_defined(Op_XORN,opcode,int_opcode) else '0'; compare_signed <='1' when opcode_defined(Op_CMPS,opcode,int_opcode) or opcode_defined(Op_SMAX,opcode,int_opcode) else '0'; -- XOR préliminaire et ajustement du signe : ActualB <= ( SND(YASEP_SIZE-1) xor (Addsub and (not compare_signed)) ) & ( SND(YASEP_SIZE-2 downto 0) xor (YASEP_SIZE-2 downto 0 =>Addsub)); -- XOR the MSB here too for the signed comparisons : ActualA <= ( SI4(YASEP_SIZE-1) xor compare_signed ) & SI4(YASEP_SIZE-2 downto 0); -- Add/Sub sumAux <= unsigned('0' & ActualA & '1') + unsigned('0' & ActualB & Addsub); Carry_out <= std_logic(sumAux(YASEP_SIZE+1)); ASU <= std_logic_vector(sumAux(YASEP_SIZE downto 1)); -- ROP2 : -- ROP2_xor <= ActualA xor ActualB; -- XOR toujours implémenté pour CLEAR ? zero_out <= '1' when ROP2_xor=(ROP2_xor'range=>'1') else '0'; -- First MUX ROP2_RS1 <= ActualA and ActualB when opcode_defined(Op_AND,opcode,int_opcode) or opcode_defined(Op_ANDN,opcode,int_opcode) or opcode_defined(Op_NAND,opcode,int_opcode) else ROP2_xor; ROP2_RS2 <= ActualA or ActualB when opcode_defined(Op_OR,opcode,int_opcode) or opcode_defined(Op_ORN,opcode,int_opcode) or opcode_defined(Op_NOR,opcode,int_opcode) else ROP2_RS1; -- last XOR ROP2 <= ROP2_RS2 xor (ROP2'range => ROP2_X2); process (clk, reset, ExecPrediv) begin if reset='0' then CPU_prediv <= ExecPrediv; phase <= '1'; INV <='0'; R1 <= (R1'range => '0'); PC <= (PC'range => '1'); -- loop around to 0 through PC1 D5 <= (D5'range => '0'); -- mask -- emulate a "NOP" : opcode <= "000000"; form <= "00"; Field_SND <= "0000"; Field_SI4 <= "0000"; -- because Synplify complains BUT they are not necessary for the stability Carry <= '0'; FlagZero <= '0'; R2 <= (R1'range => '0'); R3 <= (R1'range => '0'); R4 <= (R1'range => '0'); R5 <= (R1'range => '0'); A1 <= (R1'range => '0'); A2 <= (R1'range => '0'); A3 <= (R1'range => '0'); A4 <= (R1'range => '0'); A5 <= (R1'range => '0'); D1 <= (R1'range => '0'); D2 <= (R1'range => '0'); D3 <= (R1'range => '0'); D4 <= (R1'range => '0'); Reg_SI4 <= (R1'range => '0'); SND <= (R1'range => '0'); else if rising_edge(clk) and INV='0' then if flags_in='1' then R1(safe_to_integer(flag_addr)) <= '1'; end if; if ProgRdEn='0' then PC <= NPC; end if; if CPU_prediv/="0000" then CPU_prediv <= std_logic_vector(unsigned(CPU_prediv)-1); else CPU_prediv <= ExecPrediv; if phase='0' then -------------------Phase 0----------------------- phase<='1'; -- latch the proper operands : form <= RAM_out(1 downto 0); opcode <= RAM_out(R_OPCODE'range); Field_SND <= RAM_out(R_SND'range); SND <= RSND_out; if RAM_out(0)='1' then -- long instruction Field_SI4 <= RAM_out(R_SI4'range); -- destination for IMM16, source for EXT if RAM_out(1)='1' then -- latch if EXT Reg_SI4 <= RSI4_out; -- unused field for IMM16 end if; else -- short instruction if RAM_out(1)='1' then Field_SI4 <= RAM_out(R_SI4'range); -- FORM_RI4 else Reg_SI4 <= RSI4_out; -- FORM_RR end if; end if; -- lock the CPU if the opcode is invalid if RAM_out(R_OPCODE'range) = Op_INV or FlagValidInstruction(safe_to_integer(RAM_out(R_OPCODE'range)))='0' then INV <= '1'; end if; else -------------------Phase 1----------------------- phase<='0'; if WB_en='1' and FlagChangeCarry(int_opcode)='1' then Carry <= Carry_out; end if; if WB_en='1' and FlagChangeZero(int_opcode)='1' then FlagZero <= zero_out; end if; if WR_en='1' then case DST is when REG_R1 => R1 <= Result; when REG_R2 => R2 <= Result; when REG_R3 => R3 <= Result; when REG_R4 => R4 <= Result; when REG_R5 => R5 <= Result; when REG_A1 => A1 <= Result; when REG_D1 => D1 <= Result; when REG_A2 => A2 <= Result; when REG_D2 => D2 <= Result; when REG_A3 => A3 <= Result; when REG_D3 => D3 <= Result; when REG_A4 => A4 <= Result; when REG_D4 => D4 <= Result; when REG_A5 => A5 <= Result; when REG_D5 => D5 <= Result; when others => -- NPC et PC sont gérés autre part end case; end if; end if; end if; end if; end if; end process; end reboot;