--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
--| @file core.vhd
|
--| @file core.vhd
|
--| @brief This contains the CPU, memory and interrupt handler instances
|
--| @brief This contains the CPU, memory and interrupt handler instances
|
--|
|
--|
|
--| @author Richard James Howe.
|
--| @author Richard James Howe.
|
--| @copyright Copyright 2013,2017 Richard James Howe.
|
--| @copyright Copyright 2013,2017 Richard James Howe.
|
--| @license MIT
|
--| @license MIT
|
--| @email howe.r.j.89@gmail.com
|
--| @email howe.r.j.89@gmail.com
|
--|
|
--|
|
--| @todo Make a test bench for the H2 core for executing small sections of
|
|
--| code, in succession, and testing the response.
|
|
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
|
|
library ieee,work;
|
library ieee, work;
|
use ieee.std_logic_1164.all;
|
use ieee.std_logic_1164.all;
|
use work.util.n_bits;
|
use work.util.n_bits;
|
|
use work.util.common_generics;
|
use work.h2_pkg.all;
|
use work.h2_pkg.all;
|
|
|
package core_pkg is
|
package core_pkg is
|
type cpu_debug_interface is record
|
type cpu_debug_interface is record
|
pc: address;
|
pc: address;
|
insn: word;
|
insn: word;
|
dwe: std_ulogic;
|
dwe: std_ulogic;
|
dre: std_ulogic;
|
dre: std_ulogic;
|
din: word;
|
din: word;
|
dout: word;
|
dout: word;
|
daddr: address;
|
daddr: address;
|
end record;
|
end record;
|
|
|
component core is
|
component core is
|
generic(number_of_interrupts: positive := 8);
|
generic(g: common_generics; number_of_interrupts: positive := 8);
|
port(
|
port(
|
-- synthesis translate_off
|
-- synthesis translate_off
|
debug: out cpu_debug_interface;
|
debug: out cpu_debug_interface;
|
-- synthesis translate_on
|
-- synthesis translate_on
|
|
|
clk: in std_ulogic;
|
clk: in std_ulogic;
|
rst: in std_ulogic;
|
rst: in std_ulogic;
|
|
|
stop: in std_ulogic; -- Halts the CPU
|
stop: in std_ulogic; -- Halts the CPU
|
|
|
io_wr: out std_ulogic; -- I/O Write enable
|
io_wr: out std_ulogic; -- I/O Write enable
|
io_re: out std_ulogic; -- hardware *READS* can have side effects
|
io_re: out std_ulogic; -- hardware *READS* can have side effects
|
io_din: in word;
|
io_din: in word;
|
io_dout: out word:= (others => 'X');
|
io_dout: out word:= (others => 'X');
|
io_daddr: out word:= (others => 'X');
|
io_daddr: out word:= (others => 'X');
|
|
|
-- Interrupts
|
-- Interrupts
|
cpu_irq: in std_ulogic;
|
|
cpu_irc: in std_ulogic_vector(number_of_interrupts - 1 downto 0);
|
cpu_irc: in std_ulogic_vector(number_of_interrupts - 1 downto 0);
|
cpu_irc_mask: in std_ulogic_vector(number_of_interrupts - 1 downto 0);
|
cpu_irc_mask: in std_ulogic_vector(number_of_interrupts - 1 downto 0);
|
cpu_irc_mask_we: in std_ulogic);
|
cpu_irc_mask_we: in std_ulogic);
|
end component;
|
end component;
|
|
|
component interrupt_request_handler is
|
component interrupt_request_handler is
|
generic(
|
generic(
|
|
g: common_generics;
|
number_of_interrupts: positive := 8;
|
number_of_interrupts: positive := 8;
|
lowest_interrupt_first: boolean := true);
|
lowest_interrupt_first: boolean := true);
|
port(
|
port(
|
clk: in std_ulogic;
|
clk: in std_ulogic;
|
rst: in std_ulogic;
|
rst: in std_ulogic;
|
|
|
irq_i: in std_ulogic;
|
irq_i: in std_ulogic;
|
irc_i: in std_ulogic_vector(number_of_interrupts - 1 downto 0);
|
irc_i: in std_ulogic_vector(number_of_interrupts - 1 downto 0);
|
|
|
mask: in std_ulogic_vector(number_of_interrupts - 1 downto 0);
|
mask: in std_ulogic_vector(number_of_interrupts - 1 downto 0);
|
mask_we: in std_ulogic;
|
mask_we: in std_ulogic;
|
|
|
irq_o: out std_ulogic;
|
irq_o: out std_ulogic;
|
addr_o: out std_ulogic_vector(n_bits(number_of_interrupts) - 1 downto 0));
|
addr_o: out std_ulogic_vector(n_bits(number_of_interrupts) - 1 downto 0));
|
end component;
|
end component;
|
end package;
|
end package;
|
|
|
----- CPU ----------------------------------------------------------------------
|
----- CPU ----------------------------------------------------------------------
|
|
|
library ieee,work;
|
library ieee,work;
|
use ieee.std_logic_1164.all;
|
use ieee.std_logic_1164.all;
|
use work.util.n_bits;
|
|
use work.core_pkg.all;
|
use work.core_pkg.all;
|
use work.h2_pkg.all;
|
use work.h2_pkg.all;
|
|
use work.util.n_bits;
|
|
use work.util.common_generics;
|
use work.util.file_format;
|
use work.util.file_format;
|
use work.util.FILE_HEX;
|
use work.util.file_hex;
|
|
use work.util.or_reduce;
|
|
|
entity core is
|
entity core is
|
generic(number_of_interrupts: positive := 8);
|
generic(g: common_generics; number_of_interrupts: positive := 8);
|
port(
|
port(
|
-- synthesis translate_off
|
-- synthesis translate_off
|
debug: out cpu_debug_interface;
|
debug: out cpu_debug_interface;
|
-- synthesis translate_on
|
-- synthesis translate_on
|
|
|
clk: in std_ulogic;
|
clk: in std_ulogic;
|
rst: in std_ulogic;
|
rst: in std_ulogic;
|
|
|
stop: in std_ulogic; -- Halts the CPU
|
stop: in std_ulogic; -- Halts the CPU
|
|
|
io_wr: out std_ulogic; -- I/O Write enable
|
io_wr: out std_ulogic; -- I/O Write enable
|
io_re: out std_ulogic; -- hardware *READS* can have side effects
|
io_re: out std_ulogic; -- hardware *READS* can have side effects
|
io_din: in word;
|
io_din: in word;
|
io_dout: out word := (others => 'X');
|
io_dout: out word := (others => 'X');
|
io_daddr: out word := (others => 'X');
|
io_daddr: out word := (others => 'X');
|
|
|
-- Interrupts
|
-- Interrupts
|
cpu_irq: in std_ulogic;
|
|
cpu_irc: in std_ulogic_vector(number_of_interrupts - 1 downto 0);
|
cpu_irc: in std_ulogic_vector(number_of_interrupts - 1 downto 0);
|
cpu_irc_mask: in std_ulogic_vector(number_of_interrupts - 1 downto 0);
|
cpu_irc_mask: in std_ulogic_vector(number_of_interrupts - 1 downto 0);
|
cpu_irc_mask_we: in std_ulogic);
|
cpu_irc_mask_we: in std_ulogic);
|
end;
|
end;
|
|
|
architecture structural of core is
|
architecture structural of core is
|
constant interrupt_address_length: natural := n_bits(number_of_interrupts);
|
constant interrupt_address_length: natural := n_bits(number_of_interrupts);
|
constant file_name: string := "h2.hex";
|
constant file_name: string := "h2.hex";
|
constant file_type: file_format := FILE_HEX;
|
constant file_type: file_format := file_hex;
|
|
|
signal pc: address := (others => '0'); -- Program counter
|
signal pc: address := (others => '0'); -- Program counter
|
signal insn: word := (others => '0'); -- Instruction issued by program counter
|
signal insn: word := (others => '0'); -- Instruction issued by program counter
|
signal dwe: std_ulogic := '0'; -- Write enable
|
signal dwe: std_ulogic := '0'; -- Write enable
|
signal dre: std_ulogic := '0'; -- Read enable
|
signal dre: std_ulogic := '0'; -- Read enable
|
signal din: word := (others => '0');
|
signal din: word := (others => '0');
|
signal dout: word := (others => '0');
|
signal dout: word := (others => '0');
|
signal daddr: address := (others => '0');
|
signal daddr: address := (others => '0');
|
|
signal irc_edges: std_ulogic_vector(cpu_irc'range) := (others => '0');
|
|
signal irq_edges: std_ulogic := '0';
|
signal h2_irq: std_ulogic := '0';
|
signal h2_irq: std_ulogic := '0';
|
signal h2_irq_addr: std_ulogic_vector(interrupt_address_length - 1 downto 0) := (others=>'0');
|
signal h2_irq_addr: std_ulogic_vector(interrupt_address_length - 1 downto 0) := (others=>'0');
|
begin
|
begin
|
-- synthesis translate_off
|
-- synthesis translate_off
|
debug.pc <= pc;
|
debug.pc <= pc;
|
debug.insn <= insn;
|
debug.insn <= insn;
|
debug.dwe <= dwe;
|
debug.dwe <= dwe;
|
debug.dre <= dre;
|
debug.dre <= dre;
|
debug.din <= din;
|
debug.din <= din;
|
debug.dout <= dout;
|
debug.dout <= dout;
|
debug.daddr <= daddr;
|
debug.daddr <= daddr;
|
-- synthesis translate_on
|
-- synthesis translate_on
|
|
|
|
-- Ensure all interrupts occur are rising edge triggered
|
|
edges: work.util.rising_edge_detectors
|
|
generic map(g => g, N => cpu_irc'length)
|
|
port map(
|
|
clk => clk,
|
|
rst => rst,
|
|
di => cpu_irc,
|
|
do => irc_edges);
|
|
|
|
irq_edges <= or_reduce(irc_edges);
|
|
|
irqh_0: work.core_pkg.interrupt_request_handler
|
irqh_0: work.core_pkg.interrupt_request_handler
|
generic map(number_of_interrupts => number_of_interrupts)
|
generic map(g => g, number_of_interrupts => number_of_interrupts)
|
port map(
|
port map(
|
clk => clk,
|
clk => clk,
|
rst => rst,
|
rst => rst,
|
|
|
irq_i => cpu_irq,
|
irq_i => irq_edges,
|
irc_i => cpu_irc,
|
irc_i => irc_edges,
|
|
|
irq_o => h2_irq,
|
irq_o => h2_irq,
|
addr_o => h2_irq_addr,
|
addr_o => h2_irq_addr,
|
|
|
mask => cpu_irc_mask,
|
mask => cpu_irc_mask,
|
mask_we => cpu_irc_mask_we);
|
mask_we => cpu_irc_mask_we);
|
|
|
h2_0: work.h2_pkg.h2 -- The actual CPU instance (H2)
|
h2_0: work.h2_pkg.h2 -- The actual CPU instance (H2)
|
generic map(interrupt_address_length => interrupt_address_length)
|
generic map(asynchronous_reset => g.asynchronous_reset, delay => g.delay, interrupt_address_length => interrupt_address_length)
|
port map(
|
port map(
|
clk => clk,
|
clk => clk,
|
rst => rst,
|
rst => rst,
|
|
|
-- External interface with the 'outside world'
|
-- External interface with the 'outside world'
|
stop => stop,
|
stop => stop,
|
io_wr => io_wr,
|
io_wr => io_wr,
|
io_re => io_re,
|
io_re => io_re,
|
io_din => io_din,
|
io_din => io_din,
|
io_dout => io_dout,
|
io_dout => io_dout,
|
io_daddr => io_daddr,
|
io_daddr => io_daddr,
|
|
|
irq => h2_irq,
|
irq => h2_irq,
|
irq_addr => h2_irq_addr,
|
irq_addr => h2_irq_addr,
|
|
|
-- Instruction and instruction address to CPU
|
-- Instruction and instruction address to CPU
|
pc => pc,
|
pc => pc,
|
insn => insn,
|
insn => insn,
|
-- Fetch/Store
|
-- Fetch/Store
|
dwe => dwe,
|
dwe => dwe,
|
dre => dre,
|
dre => dre,
|
din => din,
|
din => din,
|
dout => dout,
|
dout => dout,
|
daddr => daddr);
|
daddr => daddr);
|
|
|
mem_h2_0: entity work.dual_port_block_ram
|
mem_h2_0: entity work.dual_port_block_ram
|
generic map(
|
generic map(
|
|
g => g,
|
addr_length => address'length,
|
addr_length => address'length,
|
data_length => word'length,
|
data_length => word'length,
|
file_name => file_name,
|
file_name => file_name,
|
file_type => file_type)
|
file_type => file_type)
|
port map(
|
port map(
|
-- Port A, Read only, CPU instruction/address
|
-- Port A, Read only, CPU instruction/address
|
a_clk => clk,
|
a_clk => clk,
|
a_dwe => '0',
|
a_dwe => '0',
|
a_dre => '1',
|
a_dre => '1',
|
a_addr => pc,
|
a_addr => pc,
|
a_din => (others => '0'),
|
a_din => (others => '0'),
|
a_dout => insn,
|
a_dout => insn,
|
-- Port B, Read/Write controlled by CPU instructions
|
-- Port B, Read/Write controlled by CPU instructions
|
b_clk => clk,
|
b_clk => clk,
|
b_dwe => dwe,
|
b_dwe => dwe,
|
b_dre => dre,
|
b_dre => dre,
|
b_addr => daddr,
|
b_addr => daddr,
|
b_din => dout,
|
b_din => dout,
|
b_dout => din);
|
b_dout => din);
|
|
|
end architecture;
|
end architecture;
|
|
|
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
--| @brief Interrupt request handler, while the CPU can handle interrupts
|
--| @brief Interrupt request handler, while the CPU can handle interrupts
|
--| it does not to a good job of it. This allows customization of
|
--| it does not to a good job of it. This allows customization of
|
--| priority.
|
--| priority.
|
--|
|
--|
|
--| @author Richard James Howe.
|
--| @author Richard James Howe.
|
--| @copyright Copyright 2017 Richard James Howe.
|
--| @copyright Copyright 2017, 2019 Richard James Howe.
|
--| @license MIT
|
--| @license MIT
|
--| @email howe.r.j.89@gmail.com
|
--| @email howe.r.j.89@gmail.com
|
--|
|
--|
|
--| This is a simple interrupt handler, interrupts are decoded in priority
|
--| This is a simple interrupt handler, interrupts are decoded in priority
|
--| order which can be set by a generic. If an interrupt occurs and then
|
--| order which can be set by a generic. If an interrupt occurs and then
|
--| another interrupt of the same type occurs before it has been processed
|
--| another interrupt of occurs before it has been processed the second
|
--| the second interrupt will be lost.
|
--| interrupt will be lost.
|
--|
|
--|
|
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
|
|
library ieee,work;
|
library ieee,work;
|
use ieee.std_logic_1164.all;
|
use ieee.std_logic_1164.all;
|
use ieee.numeric_std.all;
|
use ieee.numeric_std.all;
|
use ieee.math_real.all; -- only needed for calculations relating to generics
|
use ieee.math_real.all; -- only needed for calculations relating to generics
|
use work.util.reg;
|
use work.util.reg;
|
use work.util.n_bits;
|
use work.util.n_bits;
|
use work.util.select_bit;
|
use work.util.select_bit;
|
use work.util.priority;
|
use work.util.priority;
|
|
use work.util.common_generics;
|
|
|
entity interrupt_request_handler is
|
entity interrupt_request_handler is
|
generic(
|
generic(
|
|
g: common_generics;
|
number_of_interrupts: positive := 8;
|
number_of_interrupts: positive := 8;
|
lowest_interrupt_first: boolean := true);
|
lowest_interrupt_first: boolean := true);
|
port(
|
port(
|
clk: in std_ulogic;
|
clk: in std_ulogic;
|
rst: in std_ulogic;
|
rst: in std_ulogic;
|
|
|
irq_i: in std_ulogic;
|
irq_i: in std_ulogic;
|
irc_i: in std_ulogic_vector(number_of_interrupts - 1 downto 0);
|
irc_i: in std_ulogic_vector(number_of_interrupts - 1 downto 0);
|
|
|
mask: in std_ulogic_vector(number_of_interrupts - 1 downto 0);
|
mask: in std_ulogic_vector(number_of_interrupts - 1 downto 0);
|
mask_we: in std_ulogic;
|
mask_we: in std_ulogic;
|
|
|
irq_o: out std_ulogic;
|
irq_o: out std_ulogic;
|
addr_o: out std_ulogic_vector(n_bits(number_of_interrupts) - 1 downto 0));
|
addr_o: out std_ulogic_vector(n_bits(number_of_interrupts) - 1 downto 0));
|
end;
|
end;
|
|
|
architecture rtl of interrupt_request_handler is
|
architecture rtl of interrupt_request_handler is
|
constant addr_length: natural := n_bits(number_of_interrupts);
|
constant addr_length: natural := n_bits(number_of_interrupts);
|
signal irq_n: std_ulogic := '0';
|
signal irq_n: std_ulogic := '0';
|
signal irc_n: std_ulogic_vector(irc_i'range) := (others => '0');
|
signal irc_n: std_ulogic_vector(irc_i'range) := (others => '0');
|
|
|
signal addr: std_ulogic_vector(addr_length - 1 downto 0) := (others => '0');
|
signal addr: std_ulogic_vector(addr_length - 1 downto 0) := (others => '0');
|
signal irq: std_ulogic := '0';
|
signal irq: std_ulogic := '0';
|
|
|
signal mask_n: std_ulogic_vector(mask'range) := (others => '0');
|
signal mask_n: std_ulogic_vector(mask'range) := (others => '0');
|
begin
|
begin
|
|
|
irq_in: entity work.reg
|
irq_in: entity work.reg
|
generic map(
|
generic map(g => g, N => 1)
|
N => 1)
|
|
port map(
|
port map(
|
clk => clk,
|
clk => clk,
|
rst => rst,
|
rst => rst,
|
we => '1',
|
we => '1',
|
di(0) => irq_i,
|
di(0) => irq_i,
|
do(0) => irq_n);
|
do(0) => irq_n);
|
|
|
irc_in: entity work.reg
|
irc_in: entity work.reg
|
generic map(
|
generic map(g => g, N => number_of_interrupts)
|
N => number_of_interrupts)
|
|
port map(
|
port map(
|
clk => clk,
|
clk => clk,
|
rst => rst,
|
rst => rst,
|
we => '1',
|
we => '1',
|
di => irc_i,
|
di => irc_i,
|
do => irc_n);
|
do => irc_n);
|
|
|
irc_mask: entity work.reg generic map(
|
irc_mask: entity work.reg generic map(g => g, N => number_of_interrupts)
|
N => number_of_interrupts)
|
|
port map(
|
port map(
|
clk => clk,
|
clk => clk,
|
rst => rst,
|
rst => rst,
|
we => mask_we,
|
we => mask_we,
|
di => mask,
|
di => mask,
|
do => mask_n);
|
do => mask_n);
|
|
|
process(irc_n, irq_n, mask_n)
|
process(irc_n, irq_n, mask_n)
|
variable addr_n: std_ulogic_vector(addr'range) := (others => '0');
|
variable addr_n: std_ulogic_vector(addr'range) := (others => '0');
|
begin
|
begin
|
addr_n := priority(irc_n, not lowest_interrupt_first);
|
addr_n := priority(irc_n, not lowest_interrupt_first);
|
addr <= addr_n;
|
addr_o <= addr_n after g.delay;
|
if select_bit(mask_n, addr_n) = '1' then
|
if select_bit(mask_n, addr_n) = '1' then
|
irq <= irq_n;
|
irq_o <= irq_n after g.delay;
|
else
|
else
|
irq <= '0';
|
irq_o <= '0' after g.delay;
|
end if;
|
end if;
|
end process;
|
end process;
|
|
|
irq_out: entity work.reg
|
|
generic map(
|
|
N => 1)
|
|
port map(
|
|
clk => clk,
|
|
rst => rst,
|
|
we => '1',
|
|
di(0) => irq,
|
|
do(0) => irq_o);
|
|
|
|
addr_out: entity work.reg
|
|
generic map(
|
|
N => addr_length)
|
|
port map(
|
|
clk => clk,
|
|
rst => rst,
|
|
we => '1',
|
|
di => addr,
|
|
do => addr_o);
|
|
|
|
end architecture;
|
end architecture;
|
|
|