567 lines
13 KiB
VHDL
567 lines
13 KiB
VHDL
--------------------------------------------------------------------------
|
|
-- 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 <http://www.gnu.org/licenses/>.
|
|
--
|
|
-- 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.page_sel & mem_addr(MIN(cmem_addr'length-creg_ctrl_out.page_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.page_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;
|