You've already forked gitified_old_clb_svn
360 lines
14 KiB
VHDL
360 lines
14 KiB
VHDL
|
|
library IEEE;
|
|
use IEEE.STD_LOGIC_1164.all;
|
|
use ieee.numeric_std.all;
|
|
use work.wishbone_pkg.all;
|
|
|
|
library UNISIM;
|
|
use UNISIM.vcomponents.all;
|
|
|
|
entity multiboot is
|
|
port (
|
|
wb_clk_i : in std_logic;
|
|
wb_rst_i : in std_logic;
|
|
wb_adr_i : in t_wishbone_address;
|
|
wb_dat_i : in t_wishbone_data;
|
|
wb_dat_o : out t_wishbone_data;
|
|
wb_sel_i : in t_wishbone_byte_select;
|
|
wb_stb_i : in std_logic;
|
|
wb_cyc_i : in std_logic;
|
|
wb_we_i : in std_logic;
|
|
wb_ack_o : out std_logic;
|
|
wb_stall_o : out std_logic;
|
|
wb_int_o : out std_logic;
|
|
wb_err_o : out std_logic;
|
|
wb_rty_o : out std_logic
|
|
);
|
|
end multiboot;
|
|
|
|
architecture Behavioral of multiboot is
|
|
|
|
COMPONENT ICAPE2_wrapper
|
|
PORT (
|
|
CLK : IN std_ulogic;
|
|
CSIB : IN std_ulogic;
|
|
I : IN std_logic_vector (31 DOWNTO 0);
|
|
RDWRB : IN std_ulogic;
|
|
O : OUT std_logic_vector (31 DOWNTO 0)
|
|
);
|
|
END COMPONENT;
|
|
|
|
attribute NOOPT : boolean;
|
|
attribute NOOPT of ICAPE2_wrapper : component is TRUE;
|
|
|
|
type FSM_STATE is (st_abort, st_abort_1, st_abort_2,
|
|
st_idle, st_ack, st_dummy, st_bus_width_sync, st_bus_width_detect, st_dummy_2, st_sync, st_noop_1,
|
|
|
|
-- states used in the read-branch of the state machine
|
|
st_wr_addr, st_wr_data, st_wr_noop_2, st_wr_noop_3,
|
|
|
|
-- states used in the read-branch of the state machine
|
|
st_rd_addr, st_rd_noop_2, st_rd_noop_3, st_rd_deselect, st_rd_read, st_rd_read_1, st_rd_terminate,
|
|
|
|
st_cmd, st_cmd_ce, st_cmd_desync, st_noop_4, st_noop_5, st_cmd_deselect
|
|
);
|
|
signal NEXT_STATE : FSM_STATE;
|
|
signal CE : std_logic;
|
|
signal I : std_logic_vector(31 downto 0);
|
|
signal ICAP_WRITE : std_logic;
|
|
signal O : std_logic_vector(31 downto 0);
|
|
|
|
signal wb_write : std_logic; -- internal write
|
|
signal wb_read : std_logic; -- internal read
|
|
|
|
type std_logic_v32array is array ( natural range <> ) of std_logic_vector(31 downto 0);
|
|
|
|
-- see Xilinx UG470 table 5-18 for opcodes:
|
|
constant OPCODE_NOP : std_logic_vector(1 downto 0) := "00";
|
|
constant OPCODE_RD : std_logic_vector(1 downto 0) := "01";
|
|
constant OPCODE_WR : std_logic_vector(1 downto 0) := "10";
|
|
-- see Xilinx UG470 table 5-17:
|
|
constant HDR_TYPE : std_logic_vector(2 downto 0) := "001";
|
|
constant WRD_CNT_WIDTH :integer := 11;
|
|
begin
|
|
|
|
ICAPE2_wrapper_inst : ICAPE2_wrapper
|
|
-- generic map (
|
|
-- ICAP_WIDTH => "X32",
|
|
-- SIM_CFG_FILE_NAME => "NONE"
|
|
-- )
|
|
port map (
|
|
CLK => wb_clk_i, -- 1-bit Clock Input
|
|
CSIB => CE, -- 1-bit Active-Low ICAP Enable
|
|
I => I, -- 32-bit iConfiguration data input bus
|
|
RDWRB => ICAP_WRITE, -- 1-bit input: Read/Write Select input
|
|
O => O -- 32-bit iConfiguration data output bus
|
|
);
|
|
|
|
-- generate other wishbone signals
|
|
wb_err_o <= '0';
|
|
wb_rty_o <= '0';
|
|
wb_stall_o <= '0';
|
|
wb_int_o <= '0';
|
|
|
|
-- generate wishbone write and read access signal
|
|
wb_write <= wb_cyc_i and wb_stb_i and wb_we_i;
|
|
wb_read <= wb_cyc_i and wb_stb_i and not wb_we_i;
|
|
|
|
p: process(wb_clk_i, wb_rst_i)
|
|
variable cnt: unsigned (31 downto 0);
|
|
variable preload: unsigned (31 downto 0);
|
|
begin
|
|
if (wb_rst_i='1') then
|
|
ICAP_WRITE <= '1';
|
|
CE <= '1';
|
|
I <= x"20000000";
|
|
wb_ack_o <= '0';
|
|
cnt := (others => '0');
|
|
NEXT_STATE <= st_abort; -- Start with an Abort Sequence after reset.
|
|
-- It has been empirically determined that without this Abort
|
|
-- the very first ICAPE2 read cycle fails
|
|
elsif (rising_edge(wb_clk_i)) then
|
|
case NEXT_STATE is
|
|
when st_idle =>
|
|
CE <= '1';
|
|
I <= x"20000000";
|
|
wb_ack_o <= '0';
|
|
cnt := (others => '0');
|
|
if wb_write = '1' and wb_adr_i(5) = '1' then
|
|
ICAP_WRITE <= '1';
|
|
NEXT_STATE <= st_ack;
|
|
elsif wb_write = '1' or wb_read = '1' then
|
|
ICAP_WRITE <= '0';
|
|
NEXT_STATE <= st_dummy;
|
|
else
|
|
ICAP_WRITE <= '1';
|
|
NEXT_STATE <= st_idle;
|
|
end if;
|
|
|
|
when st_abort =>
|
|
ICAP_WRITE <= '0';
|
|
CE <= '1';
|
|
I <= x"20000000";
|
|
wb_ack_o <= '1';
|
|
preload := unsigned(wb_dat_i);
|
|
cnt := (others => '0');
|
|
NEXT_STATE <= st_abort_1;
|
|
when st_abort_1 =>
|
|
ICAP_WRITE <= '0';
|
|
CE <= '0';
|
|
I <= x"20000000";
|
|
wb_ack_o <= '1';
|
|
preload := unsigned(wb_dat_i);
|
|
cnt := (others => '0');
|
|
NEXT_STATE <= st_abort_2;
|
|
when st_abort_2 =>
|
|
ICAP_WRITE <= '1'; -- The actual Abort: change ICAP_WRITE while CE is asserted (see also UG470 figure 2-6 and text)
|
|
CE <= '0';
|
|
I <= x"20000000";
|
|
wb_ack_o <= '1';
|
|
preload := unsigned(wb_dat_i);
|
|
cnt := (others => '0');
|
|
NEXT_STATE <= st_idle;
|
|
|
|
-- Just acknowledge the wishbone bus when the address is out of the ICAPE2 range
|
|
when st_ack =>
|
|
ICAP_WRITE <= '1';
|
|
CE <= '1';
|
|
I <= x"20000000";
|
|
wb_ack_o <= '1';
|
|
preload := unsigned(wb_dat_i);
|
|
cnt := (others => '0');
|
|
NEXT_STATE <= st_idle;
|
|
|
|
-- Bus Width and Sync word sequence to access ICAPE2 registers (see also (part of) UG470 Table 6-1)
|
|
when st_dummy =>
|
|
ICAP_WRITE <= '0';
|
|
CE <= '0';
|
|
I <= x"FFFFFFFF"; -- step 1: dummy word
|
|
wb_ack_o <= '0';
|
|
cnt := (others => '0');
|
|
NEXT_STATE <= st_bus_width_sync;
|
|
when st_bus_width_sync =>
|
|
ICAP_WRITE <= '0';
|
|
CE <= '0';
|
|
I <= x"000000BB"; -- step 2: bus width sync word
|
|
wb_ack_o <= '0';
|
|
cnt := (others => '0');
|
|
NEXT_STATE <= st_bus_width_detect;
|
|
when st_bus_width_detect =>
|
|
ICAP_WRITE <= '0';
|
|
CE <= '0';
|
|
I <= x"11220044"; -- step 3: bus width detect
|
|
wb_ack_o <= '0';
|
|
cnt := (others => '0');
|
|
NEXT_STATE <= st_dummy_2;
|
|
when st_dummy_2 =>
|
|
ICAP_WRITE <= '0';
|
|
CE <= '0';
|
|
I <= x"FFFFFFFF"; -- step 4: dummy word
|
|
wb_ack_o <= '0';
|
|
cnt := (others => '0');
|
|
NEXT_STATE <= st_sync;
|
|
when st_sync =>
|
|
ICAP_WRITE <= '0';
|
|
CE <= '0';
|
|
I <= x"AA995566"; -- step 5: sync word
|
|
wb_ack_o <= '0';
|
|
cnt := (others => '0');
|
|
NEXT_STATE <= st_noop_1;
|
|
when st_noop_1 =>
|
|
ICAP_WRITE <= '0';
|
|
CE <= '0';
|
|
I <= x"20000000"; -- step 6: Type 1 NO OP
|
|
wb_ack_o <= '0';
|
|
cnt := (others => '0');
|
|
if wb_write = '1' then
|
|
NEXT_STATE <= st_wr_addr; -- note that write can go straight on without deselecting CE
|
|
else
|
|
NEXT_STATE <= st_rd_addr;
|
|
end if;
|
|
|
|
-- Writing ICAPE2 registers (see also (part of) UG470 Table 7-1)
|
|
when st_wr_addr =>
|
|
ICAP_WRITE <= '0';
|
|
CE <= '0'; -- step 7
|
|
I (31 downto 29) <= HDR_TYPE; -- Header type (UG470 table 5-17)
|
|
I (28 downto 27) <= OPCODE_WR;
|
|
I (26 downto 18) <= (others => '0'); -- reserved (UG470 table 5-17)
|
|
I (17 downto 13) <= wb_adr_i(4 downto 0); -- register address (UG470 table 5-17)
|
|
I (12 downto 11) <= (others => '0'); -- reserved (UG470 table 5-17)
|
|
I (10 downto 0) <= std_logic_vector(to_unsigned(1, WRD_CNT_WIDTH)); -- word count = 1 (UG470 table 5-17)
|
|
wb_ack_o <= '0';
|
|
cnt := (others => '0');
|
|
NEXT_STATE <= st_wr_data;
|
|
when st_wr_data =>
|
|
ICAP_WRITE <= '0';
|
|
CE <= '0';
|
|
I <= wb_dat_i; -- step 8: data
|
|
wb_ack_o <= '1';
|
|
cnt := (others => '0');
|
|
NEXT_STATE <= st_wr_noop_2;
|
|
when st_wr_noop_2 =>
|
|
ICAP_WRITE <= '0';
|
|
CE <= '0';
|
|
I <= x"20000000"; -- step 9: Type 1 NO OP
|
|
wb_ack_o <= '0';
|
|
cnt := (others => '0');
|
|
NEXT_STATE <= st_cmd_ce;
|
|
|
|
-- Reading ICAPE2 registers (see also UG470 Table 6-1)
|
|
-- http://forums.xilinx.com/t5/7-Series-FPGAs/ICAPE2-Configuration-Register-Read-Procedure/td-p/356861
|
|
-- http://www.xilinx.com/support/answers/44942.htm
|
|
when st_rd_addr =>
|
|
ICAP_WRITE <= '0';
|
|
CE <= '0'; -- step 7
|
|
I (31 downto 29) <= HDR_TYPE; -- Header type (UG470 table 5-17)
|
|
I (28 downto 27) <= OPCODE_RD;
|
|
I (26 downto 18) <= (others => '0'); -- reserved (UG470 table 5-17)
|
|
I (17 downto 13) <= wb_adr_i(4 downto 0); -- register address (UG470 table 5-17)
|
|
I (12 downto 11) <= (others => '0'); -- reserved (UG470 table 5-17)
|
|
I (10 downto 0) <= std_logic_vector(to_unsigned(1, WRD_CNT_WIDTH)); -- word count = 1 (UG470 table 5-17)
|
|
wb_ack_o <= '0';
|
|
cnt := (others => '0');
|
|
NEXT_STATE <= st_rd_noop_2;
|
|
when st_rd_noop_2 =>
|
|
ICAP_WRITE <= '0';
|
|
CE <= '0';
|
|
I <= x"20000000"; -- step 8: Type 1 NO OP
|
|
cnt := (others => '0');
|
|
wb_ack_o <= '0';
|
|
NEXT_STATE <= st_rd_noop_3;
|
|
when st_rd_noop_3 =>
|
|
ICAP_WRITE <= '0';
|
|
CE <= '0';
|
|
I <= x"20000000"; -- step 9: Type 1 NO OP
|
|
wb_ack_o <= '0';
|
|
cnt := (others => '0');
|
|
NEXT_STATE <= st_rd_deselect;
|
|
when st_rd_deselect => -- UG470 below Table 6-1: "The user must change the SelectMAP interface from write
|
|
-- to read control between steps 8 and 9, and back to write control after step 9.
|
|
-- (UG191 figure 7-2 takes back CE http://www.xilinx.com/support/documentation/user_guides/ug191.pdf)
|
|
ICAP_WRITE <= '0';
|
|
CE <= '1';
|
|
I <= x"20000000"; -- de assert CE without changing ICAP_WRITE (avoid Abort)
|
|
cnt := (others => '0');
|
|
wb_ack_o <= '0';
|
|
NEXT_STATE <= st_rd_read;
|
|
when st_rd_read =>
|
|
ICAP_WRITE <= '1'; -- change to "Read" while CE de-asserted
|
|
CE <= '1';
|
|
I <= x"20000000";
|
|
cnt := (others => '0');
|
|
wb_ack_o <= '0';
|
|
NEXT_STATE <= st_rd_read_1;
|
|
when st_rd_read_1 =>
|
|
ICAP_WRITE <= '1'; -- assert CE again and wait for valid data to read
|
|
CE <= '0';
|
|
I <= x"20000000"; -- step 10: Type 1 NO OP
|
|
cnt := cnt + 1;
|
|
if cnt /= 4 then -- 3 is not enough... 4 is! Empirically tested on real hardware
|
|
NEXT_STATE <= st_rd_read_1;
|
|
wb_ack_o <= '0';
|
|
else
|
|
NEXT_STATE <= st_rd_terminate;
|
|
wb_ack_o <= '1'; -- acknowledge the presented data, the actual data transfer takes place in this cycle
|
|
end if;
|
|
when st_rd_terminate =>
|
|
ICAP_WRITE <= '1';
|
|
CE <= '1'; -- de assert CE without changing ICAP_WRITE (avoid Abort)
|
|
I <= x"20000000";
|
|
wb_ack_o <= '0';
|
|
cnt := (others => '0');
|
|
NEXT_STATE <= st_cmd;
|
|
|
|
when st_cmd => -- change to "Write" while CE de-asserted
|
|
ICAP_WRITE <= '0';
|
|
CE <= '1';
|
|
I <= x"20000000";
|
|
wb_ack_o <= '0';
|
|
cnt := (others => '0');
|
|
NEXT_STATE <= st_cmd_ce;
|
|
when st_cmd_ce =>
|
|
ICAP_WRITE <= '0';
|
|
CE <= '0';
|
|
I <= x"30008001"; -- step 11: Type 1 write 1 word to CMD
|
|
wb_ack_o <= '0';
|
|
cnt := (others => '0');
|
|
NEXT_STATE <= st_cmd_desync;
|
|
when st_cmd_desync =>
|
|
ICAP_WRITE <= '0';
|
|
CE <= '0';
|
|
I <= x"0000000D"; -- step 12: DESYNC
|
|
wb_ack_o <= '0';
|
|
cnt := (others => '0');
|
|
NEXT_STATE <= st_noop_4;
|
|
when st_noop_4 =>
|
|
ICAP_WRITE <= '0';
|
|
CE <= '0';
|
|
I <= x"20000000"; -- step 13: Type 1 NO OP
|
|
wb_ack_o <= '0';
|
|
cnt := (others => '0');
|
|
NEXT_STATE <= st_noop_5;
|
|
when st_noop_5 =>
|
|
ICAP_WRITE <= '0';
|
|
CE <= '0';
|
|
I <= x"20000000"; -- step 14: Type 1 NO OP
|
|
wb_ack_o <= '0';
|
|
cnt := (others => '0');
|
|
NEXT_STATE <= st_cmd_deselect;
|
|
when st_cmd_deselect =>
|
|
ICAP_WRITE <= '0';
|
|
CE <= '1'; -- de assert CE without changing ICAP_WRITE (avoid Abort)
|
|
I <= x"20000000";
|
|
wb_ack_o <= '0';
|
|
cnt := (others => '0');
|
|
NEXT_STATE <= st_idle;
|
|
|
|
when others =>
|
|
ICAP_WRITE <= '1';
|
|
CE <= '1';
|
|
I <= x"20000000";
|
|
wb_ack_o <= '0';
|
|
NEXT_STATE <= st_idle;
|
|
end case;
|
|
end if;
|
|
end process;
|
|
|
|
wb_dat_o <= O;
|
|
|
|
end Behavioral; |