library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; entity cpu is port( clk: in std_logic; rst: in std_logic; code_data: in std_logic_vector(15 downto 0); code_addr: out std_logic_vector(15 downto 0); mem_in: in std_logic_vector(15 downto 0); mem_out: out std_logic_vector(15 downto 0); mem_addr: out std_logic_vector(15 downto 0); mem_write: out std_logic; mem_read: out std_logic; mem_busy: in std_logic ); end entity cpu; architecture behavior of cpu is component alu is port( a: in std_logic_vector(15 downto 0); b: in std_logic_vector(15 downto 0); sel: in std_logic_vector(3 downto 0); flag: out std_logic; q: out std_logic_vector(15 downto 0) ); end component; component reg is port( clk : in std_logic; rst : in std_logic; d : in std_logic_vector(15 downto 0); q : out std_logic_vector(15 downto 0) ); end component; signal alu_a: std_logic_vector(15 downto 0); signal alu_b: std_logic_vector(15 downto 0); signal alu_q: std_logic_vector(15 downto 0); signal alu_sel: std_logic_vector(3 downto 0); signal alu_flag: std_logic; signal load_reg_next, load_reg: std_logic_vector(15 downto 0); signal load_addr_next, load_addr: std_logic_vector(15 downto 0); signal inst_next, inst: std_logic_vector(15 downto 0); type regbank is array(0 to 15) of std_logic_vector(15 downto 0); signal reg_d: regbank; signal reg_q: regbank; type cpu_state_t is (RUN, LOAD, BRANCH, WAIT_MEM); signal cpu_state, cpu_state_next: cpu_state_t; begin cpu_alu: alu port map(a => alu_a, b => alu_b, sel => alu_sel, flag => alu_flag, q => alu_q); load_reg_r: reg port map(clk => clk, rst => rst, d => load_reg_next, q => load_reg); load_addr_r: reg port map(clk => clk, rst => rst, d => load_addr_next, q => load_addr); allregs: for i in 0 to 15 generate regx: reg port map(clk => clk, rst => rst, d => reg_d(i), q => reg_q(i)); end generate allregs; process(clk, rst) begin if rst = '1' then cpu_state <= BRANCH; -- wait a cycle at first inst <= x"0000"; elsif rising_edge(clk) then cpu_state <= cpu_state_next; inst <= inst_next; end if; end process; code_addr <= reg_q(14); process(code_data, reg_q, mem_in, mem_busy, alu_q, alu_flag, cpu_state, load_addr, load_reg, inst) is variable regn_0: natural; variable regn_1: natural; variable regn_2: natural; variable do_alu: std_logic; begin mem_write <= '0'; mem_read <= '0'; mem_addr <= x"0000"; mem_out <= x"0000"; alu_sel <= "0000"; alu_a <= x"0000"; alu_b <= x"0000"; do_alu := '0'; for i in 0 to 15 loop reg_d(i) <= reg_q(i); end loop; cpu_state_next <= RUN; load_reg_next <= load_reg; load_addr_next <= load_addr; case cpu_state is when RUN => reg_d(14) <= std_logic_vector(unsigned(reg_q(14)) + 2); inst_next <= code_data; when LOAD => inst_next <= inst; mem_addr <= load_addr; -- maintain this until we're done reading if load_reg(3 downto 0) = x"e" then cpu_state_next <= BRANCH; else reg_d(14) <= std_logic_vector(unsigned(reg_q(14)) + 2); end if; regn_0 := to_integer(unsigned(load_reg(3 downto 0))); if load_addr(0) = '1' then -- 8-bit load reg_d(regn_0) <= mem_in(7 downto 0) & x"00"; else reg_d(regn_0) <= mem_in; end if; when BRANCH => inst_next <= x"0000"; -- NOP reg_d(14) <= std_logic_vector(unsigned(reg_q(14)) + 2); when WAIT_MEM => inst_next <= inst; reg_d(14) <= std_logic_vector(unsigned(reg_q(14)) + 2); if mem_busy = '1' then reg_d(14) <= reg_q(14); -- halt the prefetcher cpu_state_next <= WAIT_MEM; end if; end case; if cpu_state = RUN then regn_0 := to_integer(unsigned(inst(11 downto 8))); regn_1 := to_integer(unsigned(inst(7 downto 4))); regn_2 := to_integer(unsigned(inst(3 downto 0))); case inst(15 downto 12) is when "0000" => -- NOP when "0001" => -- LOAD rn, [rm, imm] (imm is signed 4 bits) if mem_busy = '1' then reg_d(14) <= reg_q(14); -- halt the prefetcher inst_next <= inst; cpu_state_next <= WAIT_MEM; else mem_read <= '1'; cpu_state_next <= LOAD; reg_d(14) <= reg_q(14); -- halt the prefetcher -- inst_next <= inst; load_addr_next <= std_logic_vector(signed(reg_q(regn_1)) + signed(inst(3 downto 0) & '0')); mem_addr <= std_logic_vector(signed(reg_q(regn_1)) + signed(inst(3 downto 0) & '0')); load_reg_next(3 downto 0) <= inst(11 downto 8); end if; when "0010" => -- STORE rn, [rm, imm] if mem_busy = '1' then reg_d(14) <= reg_q(14); -- halt the prefetcher inst_next <= inst; cpu_state_next <= WAIT_MEM; else mem_write <= '1'; mem_addr <= std_logic_vector(signed(reg_q(regn_1)) + signed(inst(3 downto 0) & '0')); mem_out <= reg_q(regn_0); end if; --- ALU stuff when "0011" => do_alu := '1'; -- ADD rd, rn, rm (rd := rn + rm) when "0100" => do_alu := '1'; -- SUB rd, rn, rm (rd := rn - rm) when "0101" => do_alu := '1'; -- OR rd, rn, rm (rd := rn or rm) when "0110" => do_alu := '1'; -- AND rd, rn, rm (rd := rn and rm) when "0111" => do_alu := '1'; -- NOT rd, rn (rd := not rn) when "1000" => do_alu := '1'; -- XOR rd, rn, rm (rd := rn xor rm) when "1001" => -- SETH rd, imm reg_d(regn_0)(15 downto 8) <= inst(7 downto 0); when "1010" => -- SHR rd, rn, imm (rd := rn >> imm) alu_sel <= inst(15 downto 12); alu_a <= reg_q(regn_1); alu_b <= x"000" & inst(3 downto 0); reg_d(regn_0) <= alu_q; when "1011" => do_alu := '1'; -- MUL rd, rn, rm (rd := rn * rm) when "1100" => -- CMP rn, rm (flag := 1 if equal) alu_sel <= "1100"; alu_a <= reg_q(regn_0); alu_b <= reg_q(regn_1); reg_d(15)(0) <= alu_flag; when "1101" => -- BEQ imm (jump to [pc, imm] if flag is set, imm is signed 12 bits) if reg_q(15)(0) = '1' then reg_d(14) <= std_logic_vector(signed(reg_q(14)) + signed(inst(11 downto 0) & '0')); cpu_state_next <= BRANCH; end if; when "1110" => -- SET rd, imm (rd := imm, imm is 8 bit) reg_d(regn_0) <= x"00" & inst(7 downto 0); when "1111" => -- BNEQ imm if reg_q(15)(0) = '0' then reg_d(14) <= std_logic_vector(signed(reg_q(14)) + signed(inst(11 downto 0) & '0')); cpu_state_next <= BRANCH; end if; when others => -- do nothing end case; if do_alu = '1' then -- 1:1 mapping alu_sel <= inst(15 downto 12); alu_a <= reg_q(regn_1); alu_b <= reg_q(regn_2); reg_d(regn_0) <= alu_q; reg_d(15)(0) <= alu_flag; if inst(11 downto 8) = x"e" then cpu_state_next <= BRANCH; end if; end if; end if; end process; end behavior;