Your components can be in separate files and simply instantiated into your other files
Here is the instantiation of the Coldstart gadget as it appears in my top level file. These instantiations are placed at the bottom of my source file.
Inst_Coldstart: entity work.Coldstart PORT MAP(
Reset => Reset,
Clk => Clk,
ColdstartReq => ColdstartReq,
ColdstartAck => ColdstartAck,
ColdstartAddr => ColdstartAddr,
ColdstartData => ColdstartData,
ColdstartWr => ColdstartWr,
ColdstartHold => ColdstartHold
);
The coldstart code itself is in another file (Coldstart.vhd) and looks like:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
use work.IBM1130_PACK.ALL;
-- Uncomment the following library declaration if instantiating
-- any Xilinx primitives in this code.
--library UNISIM;
--use UNISIM.VComponents.all;
entity Coldstart is
Port ( Reset : in STD_LOGIC;
Clk : in STD_LOGIC;
ColdstartReq : out STD_LOGIC;
ColdstartAck : in STD_LOGIC;
ColdstartAddr : out STD_LOGIC_VECTOR (15 downto 0);
ColdstartData : out STD_LOGIC_VECTOR (15 downto 0);
ColdstartWr : inout STD_LOGIC;
ColdstartHold : out STD_LOGIC);
end Coldstart;
architecture Behavioral of Coldstart is
signal AddressCounter : std_logic_vector(15 downto 0);
type STATE_TYPE is (START, REQ, ACK, DONE);
signal State, NextState : STATE_TYPE;
type ADDR_COUNTER_CMD is (CMD_NOP, CMD_INC);
signal AddressCounterControl : ADDR_COUNTER_CMD;
begin
ColdstartAddr <= AddressCounter;
ColdstartWr <= '1';
ColdstartHold <= not AddressCounter(7); -- we're done when AddressCounter == 128
process(Clk, Reset, NextState)
begin
if Reset = '1' then
State <= START;
elsif rising_edge(Clk) then
State <= NextState;
end if;
end process;
process(Clk, Reset, AddressCounterControl, AddressCounter)
begin
if Reset = '1' then
AddressCounter <= (others => '0');
elsif rising_edge(Clk) then
case AddressCounterControl is
when CMD_NOP => null;
when CMD_INC => AddressCounter <= AddressCounter + 1;
when others => null;
end case;
end if;
end process;
process(State, AddressCounter)
begin
ColdstartReq <= '0';
AddressCounterControl <= CMD_NOP;
case State is
when START => NextState <= REQ;
when REQ => ColdstartReq <= '1';
NextState <= ACK;
when ACK => ColdstartReq <= '1';
if ColdstartAck = '1' then
AddressCounterControl <= CMD_INC;
if AddressCounter(7 downto 0) = x"7F" then
NextState <= DONE;
else
NextState <= START;
end if;
else
NextState <= ACK;
end if;
when DONE => NextState <= DONE;
when others => NextState <= START;
end case;
end process;
Inst_ColdstartROM: entity work.ColdstartROM PORT MAP(
Addr => AddressCounter(6 downto 0),
Data => ColdstartData
);
end Behavioral;
If you look, you will see where there is an instantiation of the ColdstartROM inside the Coldstart.vhd file.
The file ColdstartROM.vhd looks like this (snipped for brevity)
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
library UNISIM;
use UNISIM.VComponents.all;
entity ColdstartROM is port (
Addr : in STD_LOGIC_VECTOR ( 6 downto 0);
Data : out STD_LOGIC_VECTOR (15 downto 0));
end ColdstartROM;
architecture Behavioral of ColdstartROM is
begin
ROM128X1_inst_F : ROM128X1
generic map (
INIT => X"0000000000008000D1FD77C93FF4097D")
port map (
O => Data(15),
A0 => Addr(0),
A1 => Addr(1),
A2 => Addr(2),
A3 => Addr(3),
A4 => Addr(4),
A5 => Addr(5),
A6 => Addr(6)
);
<snipped>
ROM128X1_inst_0 : ROM128X1
generic map (
INIT => X"0000000000001255A6B1E64D00008378")
port map (
O => Data(0),
A0 => Addr(0),
A1 => Addr(1),
A2 => Addr(2),
A3 => Addr(3),
A4 => Addr(4),
A5 => Addr(5),
A6 => Addr(6)
);
end Behavioral;
Note that all of my instantiations specify that the files are in 'work'. In other words, they are in the project tree and, in my case, are all in the only source directory.
The problem with most tutorials is that they don't hit the core issues. They nibble away at one detail or another without ever building a multi-file project that is totally complete. It's a 'learn-as-you-go' kind of deal.
So, this is the way I learned to do it! I'm sure there are others.
BTW, investigate .PACK files if you want to store all your parameters and constants in the 'package' file. I did that for my CPU project and I'm not convinced it was a good idea. It does help when more than one source file needs access to the constants but I included constants that are only used in one file and I shouldn't have done that.
Here's how you add a .PACK file:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
use work.IBM1130_PACK.ALL;
Here's what the .PACK file looks like:
library IEEE;
use IEEE.STD_LOGIC_1164.all;
package IBM1130_PACK is
type AFR_CtrlType is (AFR_NOP, AFR_LOAD, AFR_COMP);
type ACC_CtrlType is (ACC_NOP, ACC_LOAD, ACC_AND, ACC_OR, ACC_EOR,
ACC_SHIFT_LEFT, ACC_SHIFT_RIGHT, ACC_XCHG);
type A_BUS_CtrlType is (A_BUS_NOP, A_BUS_SAR, A_BUS_SBR, A_BUS_ACC,
A_BUS_EXT, A_BUS_TAR, A_BUS_IAR, A_BUS_AFR,
A_BUS_RamData, A_BUS_TAG, A_BUS_DISP,
<snip>
-- register addresses for UART
constant RXR : std_logic_vector(2 downto 0) := "000";
constant TXR : std_logic_vector(2 downto 0) := "000";
constant IER : std_logic_vector(2 downto 0) := "001";
constant LCR : std_logic_vector(2 downto 0) := "011";
constant MCR : std_logic_vector(2 downto 0) := "100";
constant LSR : std_logic_vector(2 downto 0) := "101";
end IBM1130_PACK;