Commit cc68c76a authored by Tomasz Wlostowski's avatar Tomasz Wlostowski

hdl: flash-to-AFPGA and host-to-flash boot modes

parent ce0783b1
files = ["mini_vme.vhd"];
files = [ "flash_boot.vhd",
"m25p_flash.vhd",
"mini_vme.vhd",
"spi_master.vhd",
"xilinx_loader.vhd",
"sxldr_wbgen2_pkg.vhd",
"svec_xloader_wb.vhd",
"sfpga_bootloader.vhd",
"svec_bootloader_pkg.vhd"
]
-----------------------------------------------------------------------------
-- Title : Flash-to-Xilinx FPGA bitstream loader
-- Project : Simple VME64x FMC Carrier (SVEC)
-------------------------------------------------------------------------------
-- File : flash_boot.vhd
-- Author : Tomasz Wlostowski
-- Company : CERN
-- Created : 2011-01-24
-- Last update: 2013-01-25
-- Platform : FPGA-generic
-- Standard : VHDL'93
-------------------------------------------------------------------------------
-- Description: Searches for an Application FPGA bitstream in the flash memory
-- and uploads it to the FPGA through external xilinx_loader module. The bitstream
-- resides at a fixed location (defined in svec_bootloader_pkg) and the flash
-- is assumed to be formatted with SDB filesystem.
-------------------------------------------------------------------------------
--
-- Copyright (c) 2013 CERN / BE-CO-HT
--
-- This source file is free software; you can redistribute it
-- and/or modify it under the terms of the GNU Lesser General
-- Public License as published by the Free Software Foundation;
-- either version 2.1 of the License, or (at your option) any
-- later version.
--
-- This source 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 Lesser General Public License for more
-- details.
--
-- You should have received a copy of the GNU Lesser General
-- Public License along with this source; if not, download it
-- from http://www.gnu.org/licenses/lgpl-2.1.html
--
-------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.sxldr_wbgen2_pkg.all;
use work.svec_bootloader_pkg.all;
entity flash_boot is
port (
clk_sys_i : in std_logic;
rst_n_i : in std_logic;
-- Wbgen2 registers (for host access to the flash SPI controller via the
-- FAR register)
regs_i : in t_sxldr_out_registers;
regs_o : out t_sxldr_in_registers;
-- 1: boot process is enabled (transition from 0 to 1 starts bootup sequence).
-- It can be performed only once, re-booting AFPGA from the flash memory
-- requires a reset of the System FPGA
enable_i : in std_logic;
-- Xilinx loader module interface (see xilinx_loader.vhd) for descriptions.
xldr_start_o : out std_logic;
xldr_mode_o : out std_logic;
xldr_data_o : out std_logic_vector(31 downto 0);
xldr_dsize_o : out std_logic_vector(1 downto 0);
xldr_dlast_o : out std_logic;
xldr_msbf_o : out std_logic;
xldr_done_i : in std_logic;
xldr_rd_i : in std_logic;
xldr_empty_o : out std_logic;
xldr_startup_o : out std_logic;
xldr_clk_div_o : out std_logic_vector(6 downto 0);
-- SPI bus to the flash memory
spi_cs_n_o : out std_logic;
spi_sclk_o : out std_logic;
spi_mosi_o : out std_logic;
spi_miso_i : in std_logic
);
end flash_boot;
architecture behavioral of flash_boot is
component m25p_flash
port (
clk_sys_i : in std_logic;
rst_n_i : in std_logic;
regs_i : in t_sxldr_out_registers;
regs_o : out t_sxldr_in_registers;
set_addr_i : in std_logic;
addr_i : in std_logic_vector(23 downto 0);
read_i : in std_logic;
data_o : out std_logic_vector(7 downto 0);
ready_o : out std_logic;
spi_cs_n_o : out std_logic;
spi_sclk_o : out std_logic;
spi_mosi_o : out std_logic;
spi_miso_i : in std_logic);
end component;
type t_boot_state is (STARTUP, SELECT_SDB, WAIT_SELECT_SDB, CHECK_SIG0, CHECK_SIG1, CHECK_SIG2, CHECK_SIG3, SELECT_BITSTREAM, WAIT_SELECT_BITSTREAM, FETCH_BS_BYTE, LOAD_BS_BYTE, NO_BITSTREAM, BOOT_DONE);
-- helper procedure to eliminate redundant code in the main FSM. Compares
-- subsequent bytes of the SDB filesystem magic ID and advances the FSM if it
-- matches.
procedure f_check_signature (
signal ready : in std_logic;
signal data : in std_logic_vector(7 downto 0);
byte_id : integer;
signal state : out t_boot_state;
next_state : t_boot_state;
signal read : out std_logic;
read_next : std_logic) is
begin
if ready = '1' then
if data = c_SDB_SIGNATURE(byte_id) then
state <= next_state;
else
state <= NO_BITSTREAM;
end if;
read <= read_next;
else
read <= '0';
end if;
end f_check_signature;
signal flash_set_addr : std_logic;
signal flash_addr : std_logic_vector(23 downto 0);
signal flash_read : std_logic;
signal flash_data : std_logic_vector(7 downto 0);
signal flash_ready : std_logic;
signal byte_count : unsigned(23 downto 0);
signal state : t_boot_state;
begin -- rtl
U_Flash_Controller : m25p_flash
port map (
clk_sys_i => clk_sys_i,
rst_n_i => rst_n_i,
regs_i => regs_i,
regs_o => regs_o,
set_addr_i => flash_set_addr,
addr_i => flash_addr,
read_i => flash_read,
data_o => flash_data,
ready_o => flash_ready,
spi_cs_n_o => spi_cs_n_o,
spi_sclk_o => spi_sclk_o,
spi_mosi_o => spi_mosi_o,
spi_miso_i => spi_miso_i);
-- We know our endian
xldr_msbf_o <= '0';
-- We startup the FPGA immediately after loading the bitstream if
-- booting from the flash (no need to mess around with VME buffer switching,
-- since while we boot up from flash, the VME is in passive mode)
xldr_startup_o <= '1';
-- 32 MHz should be just fine.
xldr_clk_div_o <= "0000001";
process(clk_sys_i)
begin
if rising_edge(clk_sys_i) then
if rst_n_i = '0' or enable_i = '0' then
state <= STARTUP;
flash_set_addr <= '0';
flash_read <= '0';
xldr_start_o <= '0';
xldr_empty_o <= '1';
else
case state is
-- Wait until we are allowed to start flash boot sequence
when STARTUP =>
if enable_i = '1' then
state <= SELECT_SDB;
byte_count <= (others => '0');
end if;
-- Go to the SDB record location
when SELECT_SDB =>
flash_set_addr <= '1';
flash_addr <= c_SDB_ROOT_OFFSET;
state <= WAIT_SELECT_SDB;
-- Wait until the address is set
when WAIT_SELECT_SDB =>
if flash_ready = '1' then
flash_read <= '1';
state <= CHECK_SIG0;
else
flash_set_addr <= '0';
end if;
-- Read and check 4 subsequent bytes of the signature 'SDB-'. If OK, proceed
-- with loading the bitstream
when CHECK_SIG0 =>
f_check_signature(flash_ready, flash_data, 0, state, CHECK_SIG1, flash_read, '1');
when CHECK_SIG1 =>
f_check_signature(flash_ready, flash_data, 1, state, CHECK_SIG2, flash_read, '1');
when CHECK_SIG2 =>
f_check_signature(flash_ready, flash_data, 2, state, CHECK_SIG3, flash_read, '1');
when CHECK_SIG3 =>
f_check_signature(flash_ready, flash_data, 3, state, SELECT_BITSTREAM, flash_read, '0');
-- Go to the beginning of the 'afpga.bin' file in the filesystem (fixed location)
when SELECT_BITSTREAM =>
xldr_start_o <= '1';
flash_set_addr <= '1';
flash_addr <= c_SDB_BITSTREAM_OFFSET;
state <= WAIT_SELECT_BITSTREAM;
-- ... and wait until the flash address is set
when WAIT_SELECT_BITSTREAM =>
xldr_start_o <= '0';
flash_set_addr <= '0';
if(flash_ready = '1') then
state <= FETCH_BS_BYTE;
flash_read <= '1';
else
flash_read <= '0';
end if;
-- Fetch another byte of the bitstream
when FETCH_BS_BYTE =>
if(flash_ready = '1') then
xldr_empty_o <= '0';
xldr_data_o(7 downto 0) <= flash_data;
xldr_dsize_o <= "00";
xldr_dlast_o <= '0';
state <= LOAD_BS_BYTE;
else
flash_read <= '0';
end if;
-- And push it to the Xilinx Loader module
when LOAD_BS_BYTE =>
if(xldr_rd_i = '1') then
flash_read <= '1';
xldr_empty_o <= '1';
-- AFPGA indicated finish of bitstream download?
if(xldr_done_i = '1') then
state <= BOOT_DONE;
-- ... or we exceeded maximum bitstream size (something is seriously wrong on board
-- or the BS is invalid)
elsif byte_count = unsigned(c_BITSTREAM_SIZE) then
state <= NO_BITSTREAM;
-- otherwise, just proceed with another byte of the BS
else
state <= FETCH_BS_BYTE;
end if;
byte_count <= byte_count + 1;
else
flash_read <= '0';
end if;
-- We have no (or invalid) bitstream. Wait until reset
when NO_BITSTREAM =>
flash_read <= '0';
if enable_i = '0' then
state <= STARTUP;
end if;
-- Bitstream was correctly loaded. Wait forever (or until reset).
when BOOT_DONE =>
flash_read <= '0';
end case;
end if;
end if;
end process;
end behavioral;
-----------------------------------------------------------------------------
-- Title : M25Pxxx Flash Controller
-- Project : Simple VME64x FMC Carrier (SVEC)
-------------------------------------------------------------------------------
-- File : m25p_flash.vhd
-- Author : Tomasz Wlostowski
-- Company : CERN
-- Created : 2013-01-24
-- Last update: 2013-01-25
-- Platform : FPGA-generic
-- Standard : VHDL'93
-------------------------------------------------------------------------------
-- Description: Simple controller for M25Pxxx series of SPI flash memories.
-- Provides two interfaces: host interface (accessible via FAR register), which
-- can execute any kind of operations, and a simple memory bus which can only read
-- blocks of bytes starting at a given address.
-------------------------------------------------------------------------------
--
-- Copyright (c) 2013 CERN / BE-CO-HT
--
-- This source file is free software; you can redistribute it
-- and/or modify it under the terms of the GNU Lesser General
-- Public License as published by the Free Software Foundation;
-- either version 2.1 of the License, or (at your option) any
-- later version.
--
-- This source 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 Lesser General Public License for more
-- details.
--
-- You should have received a copy of the GNU Lesser General
-- Public License along with this source; if not, download it
-- from http://www.gnu.org/licenses/lgpl-2.1.html
--
-------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.sxldr_wbgen2_pkg.all;
entity m25p_flash is
port (
clk_sys_i : in std_logic;
rst_n_i : in std_logic;
-- Wishbone registers (FAR register access)
regs_i : in t_sxldr_out_registers;
regs_o : out t_sxldr_in_registers;
-- Data readout interface.
-- 1: sets flash read address to addr_i
set_addr_i : in std_logic;
-- start address for read operations
addr_i : in std_logic_vector(23 downto 0);
-- data request: when 1, the controller reads subsequent bytes from
-- the flash, starting from addr_i address.
read_i : in std_logic;
-- read data output
data_o : out std_logic_vector(7 downto 0);
-- when 1, data_o contains a valid byte and the controller is ready to accept
-- another command
ready_o : out std_logic;
-- SPI bus, connect to the flash memory.
spi_cs_n_o : out std_logic;
spi_sclk_o : out std_logic;
spi_mosi_o : out std_logic;
spi_miso_i : in std_logic
);
end m25p_flash;
architecture behavioral of m25p_flash is
component spi_master
generic (
g_div_ratio_log2 : integer;
g_num_data_bits : integer);
port (
clk_sys_i : in std_logic;
rst_n_i : in std_logic;
cs_i : in std_logic;
start_i : in std_logic;
cpol_i : in std_logic;
data_i : in std_logic_vector(g_num_data_bits - 1 downto 0);
ready_o : out std_logic;
data_o : out std_logic_vector(g_num_data_bits - 1 downto 0);
spi_cs_n_o : out std_logic;
spi_sclk_o : out std_logic;
spi_mosi_o : out std_logic;
spi_miso_i : in std_logic);
end component;
signal spi_cs, spi_cs_muxed : std_logic;
signal spi_start, spi_start_host, spi_start_muxed : std_logic;
signal spi_wdata, spi_wdata_host, spi_wdata_muxed : std_logic_vector(7 downto 0);
signal spi_rdata : std_logic_vector(7 downto 0);
signal spi_ready : std_logic;
type t_read_state is (IDLE, CSEL, COMMAND, ADDR0, ADDR1, ADDR2, DUMMY_XFER, DATA);
signal state : t_read_state;
signal ready_int : std_logic;
begin -- rtl
-- Host flash data register (bidirectional), updated by writing to FAR.DATA
p_host_spi_registers : process(clk_sys_i)
begin
if rising_edge(clk_sys_i) then
if rst_n_i = '0' then
spi_start_host <= '0';
spi_wdata_host <= (others => '0');
elsif regs_i.far_data_load_o = '1' then
spi_wdata_host <= regs_i.far_data_o;
spi_start_host <= regs_i.far_xfer_o;
else
spi_start_host <= '0';
end if;
end if;
end process;
-- Multplexes the access between to the flash SPI controller between
-- the bootloader host (through FAR register) and the flash readout
-- FSM.
p_mux_spi_access : process(spi_cs, spi_start, spi_wdata, spi_start_host, spi_wdata, spi_ready, regs_i, state)
begin
spi_cs_muxed <= regs_i.far_cs_o or spi_cs;
spi_wdata_muxed <= spi_wdata_host or spi_wdata;
spi_start_muxed <= spi_start_host or spi_start;
end process;
regs_o.far_ready_i <= spi_ready;
regs_o.far_data_i <= spi_rdata;
-- SPI Master: executes SPI read/write transactions.
U_SPI_Master : spi_master
generic map (
g_div_ratio_log2 => 0,
g_num_data_bits => 8)
port map (
clk_sys_i => clk_sys_i,
rst_n_i => rst_n_i,
cs_i => spi_cs_muxed,
start_i => spi_start_muxed,
cpol_i => '0',
data_i => spi_wdata_muxed,
ready_o => spi_ready,
data_o => spi_rdata,
spi_cs_n_o => spi_cs_n_o,
spi_sclk_o => spi_sclk_o,
spi_mosi_o => spi_mosi_o,
spi_miso_i => spi_miso_i);
-- Main State machine
p_main_fsm : process(clk_sys_i)
begin
if rising_edge(clk_sys_i) then
if rst_n_i = '0' then
state <= IDLE;
spi_start <= '0';
spi_cs <= '0';
spi_wdata <= (others => '0');
ready_int <= '1';
-- any access to FAR register stops internal bus request
elsif(regs_i.far_data_load_o = '1') then
spi_start <= '0';
spi_cs <= '0';
spi_wdata <= (others => '0');
state <= IDLE;
else
case state is
-- Idle: wait for "Set Address" or "Read" commands
when IDLE =>
if set_addr_i = '1' then
spi_cs <= '0';
spi_start <= '1';
ready_int <= '0';
state <= CSEL;
elsif read_i = '1' then
spi_start <= '1';
ready_int <= '0';
state <= DATA;
else
spi_start <= '0';
ready_int <= '1';
end if;
-- executes a dummy SPI cycle with the SPI chip disabled (CS = 0), to
-- make sure it will correctly interpret the next transfer as a READ
-- command
when CSEL =>
if(spi_ready = '1') then
state <= COMMAND;
spi_wdata <= x"0b";
spi_cs <= '1';
spi_start <= '1';
else
spi_start <= '0';
end if;
-- Send command 0x3 (FAST READ DATA)
when COMMAND =>
if(spi_ready = '1') then
state <= ADDR0;
spi_wdata <= addr_i(23 downto 16);
spi_start <= '1';
else
spi_start <= '0';
end if;
-- Send 1st byte of read address
when ADDR0 =>
if(spi_ready = '1') then
state <= ADDR1;
spi_wdata <= addr_i(15 downto 8);
spi_start <= '1';
else
spi_start <= '0';
end if;
-- Send 2nd byte of read address
when ADDR1 =>
if(spi_ready = '1') then
state <= ADDR2;
spi_wdata <= addr_i(7 downto 0);
spi_start <= '1';
else
spi_start <= '0';
end if;
-- Send 3nd byte of read address
when ADDR2 =>
if(spi_ready = '1') then
state <= DUMMY_XFER;
spi_wdata <= "XXXXXXXX";
spi_start <= '1';
else
spi_start <= '0';
end if;
-- dummy transfer (necessary for fast read mode)
when DUMMY_XFER =>
spi_start <= '0';
if(spi_ready = '1') then
state <= IDLE;
end if;
-- Data readout: waits for completion of read transaction initiated
-- upon assertion of read_i and returns the byte read data_o.
when DATA =>
spi_start <= '0';
if(spi_ready = '1')then
data_o <= spi_rdata;
ready_int <= '1';
state <= IDLE;
else
ready_int <= '0';
end if;
end case;
end if;
end if;
end process;
-- De-assert ready flag early
ready_o <= ready_int and not (set_addr_i or read_i);
end behavioral;
-- minimalistic VME core providing only CR/CSR accesses. For SVEC AFPGA bootup
-- purposes.
-------------------------------------------------------------------------------
-- Title : Minimalistic VME64x Core
-- Project : Simple VME64x FMC Carrier (SVEC)
-------------------------------------------------------------------------------
-- File : mini_vme.vhd
-- Author : Tomasz Wlostowski
-- Company : CERN
-- Created : 2012-01-20
-- Last update: 2013-01-25
-- Platform : FPGA-generic
-- Standard : VHDL'93
-------------------------------------------------------------------------------
-- Description: A stripped-down version of VME64x core. Supports only CR/CSR/D32
-- accesses to a range of addresses specified in g_user_csr_start/end. Matching
-- transactions are executed through a Wishbone master.
-------------------------------------------------------------------------------
--
-- Copyright (c) 2012 - 2013 CERN / BE-CO-HT
--
-- This source file is free software; you can redistribute it
-- and/or modify it under the terms of the GNU Lesser General
-- Public License as published by the Free Software Foundation;
-- either version 2.1 of the License, or (at your option) any
-- later version.
--
-- This source 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 Lesser General Public License for more
-- details.
--
-- You should have received a copy of the GNU Lesser General
-- Public License along with this source; if not, download it
-- from http://www.gnu.org/licenses/lgpl-2.1.html
--
-------------------------------------------------------------------------------
library ieee;
use ieee.STD_LOGIC_1164.all;
......@@ -10,33 +45,35 @@ use work.wishbone_pkg.all;
entity xmini_vme is
generic (
-- Start/end of our CSR space
g_user_csr_start : unsigned(20 downto 0);
g_user_csr_end : unsigned(20 downto 0));
port (
clk_sys_i : in std_logic;
rst_n_i : in std_logic;
-- "Passive" mode enable: when '1', the core never touches the bus
passive_i : in std_logic;
-- stripped-down VME I/O
-- Stripped-down VME bus.
VME_RST_n_i : in std_logic;
VME_AS_n_i : in std_logic;
VME_LWORD_n_i : in std_logic;
VME_WRITE_n_i : in std_logic;
VME_DS_n_i : in std_logic_vector(1 downto 0);
VME_GA_i : in std_logic_vector(5 downto 0); -- Geographical Address and GA parity
VME_DTACK_n_o : out std_logic;
VME_DTACK_OE_o : out std_logic;
-- Geographical Address. Bit 5 is GA parity.
VME_GA_i : in std_logic_vector(5 downto 0);
VME_AM_i : in std_logic_vector(5 downto 0);
VME_ADDR_i : in std_logic_vector(31 downto 1);
-- Bidirectional/tristate driver signals: please put the tristates in the
-- top level entity of your design.
VME_DTACK_n_o : out std_logic;
VME_DTACK_OE_o : out std_logic;
VME_DATA_b_i : in std_logic_vector(31 downto 0);
VME_DATA_b_o : out std_logic_vector(31 downto 0);
VME_DATA_DIR_o : out std_logic;
VME_DATA_OE_N_o : out std_logic;
-- Wishbone master
master_o : out t_wishbone_master_out;
master_i : in t_wishbone_master_in
);
......@@ -45,7 +82,10 @@ end xmini_vme;
architecture rtl of xmini_vme is
-- We are only interested in CR/CSR transfers (AM = 0x2f)
constant c_AM_CS_CSR : std_logic_vector(5 downto 0) := "101111";
-- How long (in clock cycles) is our DTACK. Useful for slower VME controllers.
constant c_DTACK_LENGTH : integer := 20;
signal as_synced, ds_synced : std_logic;
......@@ -143,8 +183,8 @@ begin -- rtl
else
am_match <= '0';
end if;
-- ... D32 data type
-- ... D32 data type
if(ds_latched = "00" and lword_latched = '0' and addr_latched(1) = '0') then
dtype_match <= '1';
else
......@@ -222,15 +262,9 @@ begin -- rtl
when DTACK =>
VME_DATA_b_o <= readback_data;
if(passive_i = '1') then
VME_DATA_DIR_o <= '0';
VME_DATA_DIR_o <= '0';
VME_DTACK_OE_o <= '0';
else
VME_DTACK_n_o <= '0';
VME_DTACK_OE_o <= '1';
VME_DATA_DIR_o <= not is_write;
end if;
VME_DTACK_n_o <= '0';
VME_DTACK_OE_o <= '1';
VME_DATA_DIR_o <= not is_write;
dtack_counter <= dtack_counter + 1;
......
This diff is collapsed.
-----------------------------------------------------------------------------
-- Title : SPI Bus Master
-- Project : Simple VME64x FMC Carrier (SVEC)
-------------------------------------------------------------------------------
-- File : spi_master.vhd
-- Author : Tomasz Wlostowski
-- Company : CERN
-- Created : 2011-08-24
-- Last update: 2013-01-25
-- Platform : FPGA-generic
-- Standard : VHDL'93
-------------------------------------------------------------------------------
-- Description: Just a simple SPI master (bus-less).
-------------------------------------------------------------------------------
--
-- Copyright (c) 2011-2013 CERN / BE-CO-HT
--
-- This source file is free software; you can redistribute it
-- and/or modify it under the terms of the GNU Lesser General