-------------------------------------------------------------------------- -- Project: JCPU, a portable 8-bit RISC CPU written in VHDL -- This file: JCPU top file -- -- Copyright (C) 2007 J. Ahrensfeld -- -- This program is free software: you can redistribute it and/or modify -- it under the terms of the GNU General Public License as published by -- the Free Software Foundation, either version 3 of the License, or -- (at your option) any later version. -- -- This program is distributed in the hope that it will be useful, -- but WITHOUT ANY WARRANTY; without even the implied warranty of -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -- GNU General Public License for more details. -- -- You should have received a copy of the GNU General Public License -- along with this program. If not, see . -- -- For questions and ideas, please contact the author at jens@jayfield.org -- -------------------------------------------------------------------------- library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.numeric_std.ALL; use work.cpu_pkg.all; entity cpu is Generic ( use_instr_register : boolean := false; use_ctrl_rom : boolean := true ); Port ( rst : in STD_LOGIC; clk : in STD_LOGIC; ce : in STD_LOGIC; int_in : in STD_LOGIC; int_ack : out STD_LOGIC; xmem_wait : in STD_LOGIC; xmem_we : out STD_LOGIC; xmem_re : out STD_LOGIC; instr_din : in unsigned (IMEM_DATA_WIDTH-1 downto 0); instr_addr : out unsigned (IMEM_ADDR_WIDTH-1 downto 0); xmem_din : in unsigned (DMEM_DATA_WIDTH-1 downto 0); xmem_dout : out unsigned (DMEM_DATA_WIDTH-1 downto 0); xmem_addr : out unsigned (DMEM_ADDR_WIDTH-1 downto 0); io_sel : out std_logic ); end cpu; architecture rtl of cpu is COMPONENT stack_ctrl GENERIC ( addr_width : integer ); Port ( rst : in STD_LOGIC; clk : in STD_LOGIC; push : in STD_LOGIC; pop : in STD_LOGIC; din : in inst_addr_t; dout : out inst_addr_t; mem_we : out STD_LOGIC; ptr_out : out unsigned (addr_width-1 downto 0) ); END COMPONENT; COMPONENT pc Port ( rst : in STD_LOGIC; clk : in STD_LOGIC; inc : in STD_LOGIC; load : in STD_LOGIC; pc_in : in inst_addr_t; pc_out : out inst_addr_t; pc_next : out inst_addr_t ); END COMPONENT; COMPONENT dpath_ctrl GENERIC ( use_rom : boolean ); Port ( inst_in : in inst_t; iphase_in : in iphase_t; status_in : in cpu_status_t; ctrl_out : out dpath_ctrl_out_t; idout : out inst_addr_t; ddout : out dmem_data_t ); END COMPONENT; COMPONENT int_ctrl Port ( rst : in STD_LOGIC; clk : in STD_LOGIC; ctrl_in : in int_ctrl_in_t; int_in : in STD_LOGIC; int_exit : in STD_LOGIC; iphase_in : in iphase_t; int_ack_out : out STD_LOGIC; stat_save_out : out STD_LOGIC; stat_rest_out : out STD_LOGIC; pc_addr_out : out inst_addr_t; pc_load_out : out STD_LOGIC; stk_push_out : out STD_LOGIC; stk_pop_out : out STD_LOGIC ); END COMPONENT; COMPONENT reg_dual GENERIC ( addr_width : integer; data_width : integer ); Port ( clk : in STD_LOGIC; we_a : in STD_LOGIC; ptr_a : in reg_ptr_t; ptr_b : in reg_ptr_t; din_a : in unsigned (data_width-1 downto 0); dout_a : out unsigned (data_width-1 downto 0); dout_b : out unsigned (data_width-1 downto 0) ); END COMPONENT; COMPONENT chipram GENERIC ( addr_width : integer; data_width : integer ); Port ( clka : in STD_LOGIC; clkb : in STD_LOGIC; en_a : in STD_LOGIC; en_b : in STD_LOGIC; we_a : in STD_LOGIC; we_b : in STD_LOGIC; addr_a : in unsigned (addr_width-1 downto 0); addr_b : in unsigned (addr_width-1 downto 0); din_a : in unsigned (data_width-1 downto 0); din_b : in unsigned (data_width-1 downto 0); dout_a : out unsigned (data_width-1 downto 0); dout_b : out unsigned (data_width-1 downto 0) ); END COMPONENT; COMPONENT chipreg Port ( rst : in STD_LOGIC; clk : in STD_LOGIC; we : in STD_LOGIC; addr : in dmem_addr_t; din : in dmem_data_t; dout : out dmem_data_t; ctrl_in : in creg_ctrl_in_t; ctrl_out : out creg_ctrl_out_t ); END COMPONENT; COMPONENT alu GENERIC ( data_width : integer := 8 ); Port ( rst : in STD_LOGIC; clk : in STD_LOGIC; op_en : in STD_LOGIC; cy_in : in STD_LOGIC; op1_in : in unsigned (data_width-1 downto 0); op2_in : in unsigned (data_width-1 downto 0); opsel : in alu_op_t; res_out : out unsigned (data_width-1 downto 0); stat_load : in STD_LOGIC; stat_in : in alu_status_t; stat_out : out alu_status_t ); END COMPONENT; signal cpu_status, status_reg : cpu_status_t; -- := cpu_status_t(others => '0'); signal ctrl : dpath_ctrl_out_t; signal stk_out : inst_addr_t; signal stk_in : inst_addr_t; signal stk_reg : inst_addr_t; signal pc_in, pc_out, pc_next, idata, int_pc_addr : inst_addr_t; signal iphase : iphase_t; signal const_data, reg_a_dout, reg_b_dout : dmem_data_t; signal reg_din : dmem_data_t; signal alu_op1, alu_op2, alu_result : dmem_data_t; signal alu_status : alu_status_t; signal pc_inc, pc_load, cpu_active, stk_we, cmem_we, creg_we : STD_LOGIC; signal ctrl_inst : inst_t; signal ctrl_iphase : iphase_t; signal was_pcld, was_pop2pc, int_pc_load, int_stk_push, int_stk_pop, stk_push, stk_pop : STD_LOGIC; signal status_save, status_restore : STD_LOGIC; signal creg_dout : dmem_data_t; signal cmem_din, cmem_dout : inst_addr_t; signal stk_addr, cmem_addr : unsigned(CHIPRAM_SIZE_BITS-1 downto 0); signal stk_ptr : unsigned(STACK_SIZE_BITS-1 downto 0); signal creg_ctrl_out : creg_ctrl_out_t; signal creg_ctrl_in : creg_ctrl_in_t; signal irq_out : std_logic; signal xio_sel, cio_sel : std_logic; signal mem_data : dmem_data_t; signal mem_addr : dmem_data_t; begin io_sel <= xio_sel; instr_addr <= pc_out; pc_inc <= cpu_active and ctrl.lines.pc_inc; stk_addr <= '0' & stk_ptr; cpu_status.alu <= alu_status; cpu_status.xmem_wait <= xmem_wait; creg_ctrl_in.alu <= alu_status; xmem_dout <= mem_data; xmem_addr <= mem_addr; cmem_din <= creg_ctrl_out.cmem_high & mem_data; cmem_addr <= '1' & creg_ctrl_out.bank_sel & mem_addr(MIN(cmem_addr'length-creg_ctrl_out.bank_sel'length-1, mem_addr'length)-1 downto 0); -------------------------------------------------------------------- mem_ctrl_lines: process(ctrl.lines) begin xio_sel <= '0'; xmem_re <= '0'; xmem_we <= '0'; cio_sel <= '0'; cmem_we <= '0'; creg_we <= '0'; case ctrl.lines.mem_access is when xmem_access => xmem_re <= ctrl.lines.mem_read; xmem_we <= ctrl.lines.mem_write; when xio_access => xio_sel <= '1'; xmem_re <= ctrl.lines.mem_read; xmem_we <= ctrl.lines.mem_write; when cmem_access => cmem_we <= ctrl.lines.mem_write; when cio_access => cio_sel <= '1'; creg_we <= ctrl.lines.mem_write; when others => null; end case; end process; iphase_counter: process(rst, clk, ce) variable pos : integer; begin if rst = '1' then iphase <= iphase_t'low; cpu_active <= '0'; elsif rising_edge(clk) then cpu_active <= '0'; if ce = '1' then cpu_active <= '1'; if iphase = iphase_t'high then iphase <= iphase_t'low; else iphase <= iphase + 1; end if; end if; end if; end process; gen_instr_reg: if use_instr_register = true generate instr_reg: process (rst, clk, ce) begin if rst = '1' then ctrl_inst <= (others => '0'); ctrl_iphase <= 0; elsif rising_edge(clk) and ce = '1' then ctrl_inst <= instr_din; ctrl_iphase <= iphase; end if; end process; end generate; gen_instr_direct: if use_instr_register = false generate ctrl_inst <= instr_din; ctrl_iphase <= iphase; end generate; status_register: process(clk, status_save) begin if rising_edge(clk) and status_save = '1' then status_reg.alu <= alu_status; end if; end process; pc_pushpop: process(ctrl.lines, int_pc_load, int_stk_push, int_stk_pop, was_pop2pc) begin pc_load <= ctrl.lines.pc_load or int_pc_load; stk_push <= (ctrl.lines.stk_pushd or ctrl.lines.stk_push) or (int_stk_push and not was_pop2pc); stk_pop <= ctrl.lines.stk_popd or int_stk_pop or (ctrl.lines.stk_pop and not int_pc_load); end process; lines_delay: process(clk) begin if rising_edge(clk) then was_pcld <= ctrl.lines.pc_load; was_pop2pc <= ctrl.lines.stk_pop and ctrl.lines.pc_load; end if; end process; stk_mux: process(ctrl.lines, was_pcld, int_stk_push, pc_next, idata, reg_a_dout, creg_ctrl_out) begin if was_pcld = '1' and int_stk_push = '1' then stk_in <= idata; elsif ctrl.lines.stk_pushd = '1' then stk_in <= creg_ctrl_out.stk_high & reg_a_dout; else stk_in <= pc_next; end if; end process; pc_mux: process(ctrl.lines, int_pc_load, int_stk_pop, idata, int_pc_addr, stk_out) begin pc_in <= stk_out; if int_pc_load = '1' then if int_stk_pop = '0' then pc_in <= int_pc_addr; end if; elsif ctrl.lines.pc_load = '1' then if ctrl.lines.stk_pop = '0' then pc_in <= idata; end if; end if; end process; mem_data_mux: process(ctrl.lines.ddata_src_sel, reg_a_dout, reg_b_dout, const_data, creg_ctrl_out) begin case ctrl.lines.ddata_src_sel is when reg_a => mem_data <= reg_a_dout; when reg_b => mem_data <= reg_b_dout; when const => mem_data <= const_data; when others => mem_data <= const_data; end case; end process; mem_addr_mux: process(ctrl.lines.daddr_src_sel, reg_a_dout, reg_b_dout, const_data, creg_ctrl_out.bank_sel) begin case ctrl.lines.daddr_src_sel is when reg_a => mem_addr <= reg_a_dout; when reg_b => mem_addr <= reg_b_dout; when const => mem_addr <= const_data; when others => mem_addr <= const_data; end case; end process; reg_in_mux: process(ctrl.lines.reg_src_sel, cio_sel, xmem_din, alu_result, stk_out, cmem_dout, creg_dout) variable data : dmem_data_t; begin case ctrl.lines.reg_src_sel is when xmem_src => data := xmem_din; when cmem_src => if cio_sel = '0' then data := cmem_dout(dmem_data_t'range); else data := creg_dout(dmem_data_t'range); end if; when stk_src => data := stk_out(dmem_data_t'range); when others => data := alu_result; end case; reg_din <= data; end process; alu_op1_mux: process(ctrl.lines.alu_op1_src_sel, reg_a_dout, reg_b_dout, const_data) variable data : dmem_data_t; begin case ctrl.lines.alu_op1_src_sel is when reg_a => data := reg_a_dout; when reg_b => data := reg_b_dout; when others => data := const_data; end case; alu_op1 <= data; end process; alu_op2_mux: process(ctrl.lines.alu_op2_src_sel, reg_a_dout, reg_b_dout, const_data) variable data : dmem_data_t; begin case ctrl.lines.alu_op2_src_sel is when reg_a => data := reg_a_dout; when reg_b => data := reg_b_dout; when others => data := const_data; end case; alu_op2 <= data; end process; -------------------------------------------------------------------- inst_stack_ctrl: stack_ctrl GENERIC MAP ( addr_width => STACK_SIZE_BITS ) PORT MAP( rst => rst, clk => clk, push => stk_push, pop => stk_pop, dout => stk_reg, din => stk_in, ptr_out => stk_ptr, mem_we => stk_we ); inst_pc: pc PORT MAP( rst => rst, clk => clk, inc => pc_inc, load => pc_load, pc_in => pc_in, pc_out => pc_out, pc_next => pc_next ); inst_dpath_ctrl: dpath_ctrl GENERIC MAP ( use_rom => use_ctrl_rom ) PORT MAP( inst_in => ctrl_inst, iphase_in => ctrl_iphase, status_in => cpu_status, ctrl_out => ctrl, ddout => const_data, idout => idata ); inst_int_ctrl: int_ctrl PORT MAP( rst => rst, clk => clk, ctrl_in => creg_ctrl_out.int_ctrl, int_in => int_in, int_ack_out => int_ack, int_exit => ctrl.lines.int_exit, iphase_in => ctrl_iphase, stat_save_out => status_save, stat_rest_out => status_restore, pc_addr_out => int_pc_addr, pc_load_out => int_pc_load, stk_push_out => int_stk_push, stk_pop_out => int_stk_pop ); inst_reg_ab: reg_dual GENERIC MAP ( addr_width => REG_SIZE_BITS, data_width => DMEM_DATA_WIDTH ) PORT MAP( clk => clk, we_a => ctrl.lines.reg_we, ptr_a => ctrl.reg_a_ptr, ptr_b => ctrl.reg_b_ptr, din_a => reg_din, dout_a => reg_a_dout, dout_b => reg_b_dout ); inst_chipram: chipram GENERIC MAP ( addr_width => CHIPRAM_SIZE_BITS, data_width => IMEM_ADDR_WIDTH ) PORT MAP( clka => clk, clkb => clk, en_a => cpu_active, en_b => cpu_active, we_a => stk_we, we_b => cmem_we, addr_a => stk_addr, addr_b => cmem_addr, din_a => stk_reg, din_b => cmem_din, dout_a => stk_out, dout_b => cmem_dout ); inst_chipreg: chipreg PORT MAP( rst => rst, clk => clk, we => creg_we, addr => mem_addr, din => mem_data, dout => creg_dout, ctrl_in => creg_ctrl_in, ctrl_out => creg_ctrl_out ); inst_alu: alu GENERIC MAP ( data_width => DMEM_DATA_WIDTH ) PORT MAP( rst => rst, clk => clk, op_en => ctrl.lines.alu_load, cy_in => ctrl.lines.alu_cy_in, op1_in => alu_op1, op2_in => alu_op2, opsel => ctrl.lines.alu_opsel, res_out => alu_result, stat_load => status_restore, stat_in => status_reg.alu, stat_out => alu_status ); end rtl;