URL
https://opencores.org/ocsvn/wishbone_spi_flash_interface/wishbone_spi_flash_interface/trunk
Subversion Repositories wishbone_spi_flash_interface
Compare Revisions
- This comparison shows the changes necessary to convert path
/
- from Rev 1 to Rev 2
- ↔ Reverse comparison
Rev 1 → Rev 2
/wishbone_spi_flash_interface/trunk/spi_flash_interface_used_in_larger_FPGA_setting_for_register_initialization.zip
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
wishbone_spi_flash_interface/trunk/spi_flash_interface_used_in_larger_FPGA_setting_for_register_initialization.zip
Property changes :
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: wishbone_spi_flash_interface/trunk/spi_pack.vhd
===================================================================
--- wishbone_spi_flash_interface/trunk/spi_pack.vhd (nonexistent)
+++ wishbone_spi_flash_interface/trunk/spi_pack.vhd (revision 2)
@@ -0,0 +1,1620 @@
+--------------------------------------------------------------------------
+-- Package of bit sync and DPLL components
+--
+--
+--
+
+library IEEE;
+use IEEE.STD_LOGIC_1164.ALL;
+use IEEE.NUMERIC_STD.ALL;
+use IEEE.MATH_REAL.ALL;
+
+library work;
+use work.convert_pack.all;
+
+package spi_pack is
+
+ component spi_flash_interface
+ generic(
+ NUM_CS : natural; -- Number of SPI device selects
+ DEF_R_0 : unsigned(31 downto 0); -- SPI Chip Selects
+ DEF_R_1 : unsigned(31 downto 0); -- SPI Command byte
+ DEF_R_2 : unsigned(31 downto 0) -- SPI Address (24 bits)
+ );
+ port (
+ -- System Clock and Clock Enable
+ sys_rst_n : in std_logic;
+ sys_clk : in std_logic;
+ sys_clk_en : in std_logic;
+
+ -- Bus interface
+ adr_i : in unsigned(3 downto 0);
+ sel_i : in std_logic;
+ we_i : in std_logic;
+ dat_i : in unsigned(31 downto 0);
+ dat_o : out unsigned(31 downto 0);
+ ack_o : out std_logic;
+
+ -- SPI interface
+ -- "hold" and "WP" are not implemented.
+ spi_cs_o : out unsigned(NUM_CS-1 downto 0);
+ spi_sck_o : out std_logic;
+ spi_so_o : out std_logic;
+ spi_si_i : in std_logic
+
+ );
+ end component;
+
+ component spi_flash_sys_init
+ generic(
+ SYS_CLK_RATE : real;
+ FLASH_IDLE : natural; -- Number of ms idle before RAM-mapped FLASH operation closeout
+ DECODE_BITS : natural; -- Number of init address upper-bits decoded to select individual SPI devices.
+ DEF_R_0 : unsigned(31 downto 0) := str2u("00000001",32); -- low-level SPI Chip Select control
+ DEF_R_1 : unsigned(31 downto 0) := str2u("00000003",32); -- low-level SPI Command byte
+ DEF_R_2 : unsigned(31 downto 0) := str2u("007F0000",32); -- low-level SPI Address
+ DEF_R_3 : unsigned(31 downto 0) := str2u("007F0000",32); -- Init Address
+ DEF_R_4 : unsigned(31 downto 0) := str2u("00000100",32); -- Init Bytes
+ DEF_R_5 : unsigned(31 downto 0) := str2u("00000001",32) -- Init Settings
+ );
+ port (
+ -- System Clock and Clock Enable
+ sys_rst_n : in std_logic;
+ sys_clk : in std_logic;
+ sys_clk_en : in std_logic;
+
+ -- Bus interface
+ adr_i : in unsigned(3 downto 0);
+ sel_i : in std_logic;
+ we_i : in std_logic;
+ dat_i : in unsigned(31 downto 0);
+ dat_o : out unsigned(31 downto 0);
+ ack_o : out std_logic;
+
+ -- RAM mapped SPI FLASH access port
+ -- (fl_ack_o may take ~5ms during page program)
+ fl_adr_i : in unsigned(31 downto 0);
+ fl_sel_i : in std_logic;
+ fl_we_i : in std_logic;
+ fl_dat_i : in unsigned(7 downto 0);
+ fl_dat_o : out unsigned(7 downto 0);
+ fl_ack_o : out std_logic;
+
+ -- Init data output port
+ init_adr_o : out unsigned(31 downto 0);
+ init_dat_o : out unsigned(7 downto 0);
+ init_cyc_o : out std_logic;
+ init_ack_i : in std_logic;
+ init_fin_o : out std_logic; -- Stays high when init is finished
+
+ -- SPI interface
+ -- "hold" and "WP" are not implemented.
+ spi_adr_o : out unsigned(DECODE_BITS-1 downto 0);
+ spi_cs_o : out std_logic;
+ spi_sck_o : out std_logic;
+ spi_so_o : out std_logic;
+ spi_si_i : in std_logic
+
+ );
+ end component;
+
+ component spi_flash_simulator
+ generic(
+ SYS_CLK_RATE : real;
+ FLASH_ADR_BITS : natural; -- Flash memory size is based on this
+ FLASH_INIT : string -- Default RX packet digestion settings
+ );
+ port (
+ -- System Clock and Clock Enable
+ sys_rst_n : in std_logic;
+ sys_clk : in std_logic;
+ sys_clk_en : in std_logic;
+
+ -- SPI interface
+ -- "hold" and "WP" are not implemented.
+ spi_cs_i : in std_logic;
+ spi_sck_i : in std_logic;
+ spi_si_i : in std_logic;
+ spi_so_o : out std_logic
+
+ );
+ end component;
+
+end spi_pack;
+
+-------------------------------------------------------------------------------
+-- SPI Flash Interface
+-------------------------------------------------------------------------------
+--
+-- Author: John Clayton
+-- Date : July 26, 2013 Created this module starting with code copied from
+-- another module.
+-- July 31, 2013 Modified this module in simulation, until it looked
+-- correct. Tested in hardware. It works!
+-- Aug. 10, 2013 Refined the design so that it enforces a minimum
+-- spi_cs_o high time between assertions, thus allowing
+-- the design to actually program SPI FLASH chips using
+-- a 50 MHz clock, instead of just at 25 MHz.
+--
+-- Description
+-------------------------------------------------------------------------------
+-- This module is an interface to a SPI serial FLASH memory. The code was
+-- written in order to keep it as generic as possible, but without many
+-- extra frills. The module was written by reading the Adesto Technologies
+-- AT25DF641 and ST Micro M25P64 datasheets.
+--
+-- There are three registers involved in its use. There is one for the
+-- SPI command byte, one for the 24-bit SPI address, and one for data to
+-- be read from, or written to, the SPI serial FLASH device.
+--
+-- The SPI serial FLASH interface is implemented using four signals:
+-- spi_cs_o = SPI chip select outputs
+-- spi_sck_o = Serial Clock output
+-- spi_si_i = Serial Data input
+-- spi_so_o = Serial Data output
+--
+-- For multiple SPI devices, the spi_cs_o output is implemented as a bus
+-- of register driven outputs. The size of the bus is controlled by generics,
+-- allowing any number of SPI devices to be selected. Because these outputs
+-- are not decoded as a 1-active-of-N type signal, it is possible to select
+-- multiple devices simultaneously, which may work well for writing and
+-- erasing, but not for reading. You have been warned!
+--
+-- The spi_sck_o output is generated at half the sys_clk rate, and it is
+-- not adjustable. To obtain operation at lower clock rates without
+-- using a lower sys_clk frequency, consider slowing down the operation
+-- of this module by the use of the sys_clk_en signal.
+--
+-- This module takes the approach that the serial communication with the
+-- SPI device is not continuous. Instead, the command is completed in
+-- segments or phases. To begin a command, a bus write to the SPI chip
+-- select register asserts '0' on the desired chip select bits. Then, a write
+-- to the command register causes the command to be sent out, generating a
+-- burst of eight clock pulses. Another separate bus cycle is then required
+-- in order to send out the address. After that, bus cycles can be used to
+-- either read or write data bytes. At the conclusion of those activities,
+-- the command can be terminated or executed by simply writing to the SPI
+-- chip select register to raise the chip select bit back to a '1'.
+--
+-- This approach works because the spi_sck_o output is under control of
+-- this module.
+--
+-- There are no provisions for use of the "hold" or "WP" (write protect)
+-- signals. If desired, it might be possible to use sys_clk_en as a hold
+-- signal, although this has not been tested or investigated in any detail.
+--
+-- The registers are summarized as follows:
+--
+-- Address Structure Function
+-- ------- --------- -----------------------------------------------------
+-- 0x0 (N:0) SPI chip Selects, where N=NUM_CS-1.
+-- 0x1 (7:0) SPI Command Byte
+-- 0x2 (23:0) SPI Address Bytes
+-- 0x3+ (7:0) SPI Data
+--
+-- Notes on Registers:
+--
+-- (0x0) SPI Chip Selects
+--
+-- Writing to this register selects which associated spi_cs_o outputs are
+-- active. The spi_cs_o outputs are driven directly from the register,
+-- and can be changed at any time. SPI chip selects are low asserted,
+-- so the bits in this register are loaded with all ones initially.
+-- To initiate a command, the appropriate spi_cs_o bit must be cleared
+-- to zero. To conclude and execute a command, the spi_cs_o bit must
+-- be set back to one.
+-- Reading this register returns the contents, but does not affect the
+-- SPI interface.
+-- Care should be exercised when using this register with multiple SPI
+-- devices, as it is possible to activate more than one device. This
+-- may work well for writing to the devices, but will result in data bit
+-- collisions if multiple devices are read simultaneously.
+--
+-- (0x1) SPI Command Byte
+--
+-- Writing to this register causes the contents to be updated, and also
+-- sends out the new contents in serial form, most significant bit first.
+-- Reading this register returns the contents, but does not affect the
+-- SPI interface.
+--
+-- (0x2) SPI Address Bytes
+--
+-- Writing to this register causes the contents to be updated, and also
+-- sends out the new contents in serial form, most significant bit first.
+-- Reading this register returns the contents, but does not affect the
+-- SPI interface.
+--
+-- (0x3+) SPI Data Byte (Addresses in the range [0x3..0xF] access this.)
+--
+-- There is no local storage register inside the module for this address.
+-- Writing to this address causes the data to be sent out in serial form,
+-- most significant bit first.
+-- Reading this address causes a burst of eight clock pulses to be issued
+-- from the SPI interface, effectively reading in eight bits and returning
+-- them in parallel over the bus interface.
+-- Successive reads or writes therefore program or read successive locations
+-- in the SPI device.
+-- It should be noted that the SPI serial FLASH devices may allow continuous
+-- reads which go on indefinitely, cycling through the entire contents of
+-- the FLASH array. On the other hand, writes are buffered inside the
+-- FLASH device, so that a "Byte/Page Program" instruction may allow from
+-- 1 to 256 sequential bytes to be programmed. If more bytes are sent to
+-- the FLASH device, it may simply cause the FIFO buffer inside the FLASH
+-- device to hold the last 256 bytes for programming, discarding the bytes
+-- which were written at first.
+--
+-- The sys_rst_n input is an asynchronous reset.
+
+library IEEE;
+use IEEE.STD_LOGIC_1164.ALL;
+use IEEE.NUMERIC_STD.ALL;
+use IEEE.MATH_REAL.ALL;
+
+library work;
+use work.convert_pack.all;
+
+ entity spi_flash_interface is
+ generic(
+ NUM_CS : natural := 1; -- Number of SPI device selects
+ DEF_R_0 : unsigned(31 downto 0) := str2u("00000001",32); -- SPI Chip Selects
+ DEF_R_1 : unsigned(31 downto 0) := str2u("00000003",32); -- SPI Command byte
+ DEF_R_2 : unsigned(31 downto 0) := str2u("007F0000",32) -- SPI Address (24 bits)
+ );
+ port (
+ -- System Clock and Clock Enable
+ sys_rst_n : in std_logic;
+ sys_clk : in std_logic;
+ sys_clk_en : in std_logic;
+
+ -- Bus interface
+ adr_i : in unsigned(3 downto 0);
+ sel_i : in std_logic;
+ we_i : in std_logic;
+ dat_i : in unsigned(31 downto 0);
+ dat_o : out unsigned(31 downto 0);
+ ack_o : out std_logic;
+
+ -- SPI interface
+ -- "hold" and "WP" are not implemented.
+ spi_cs_o : out unsigned(NUM_CS-1 downto 0);
+ spi_sck_o : out std_logic;
+ spi_so_o : out std_logic;
+ spi_si_i : in std_logic
+
+ );
+end spi_flash_interface;
+
+architecture beh of spi_flash_interface is
+
+-- Constants
+constant DAT_SIZE : natural := 32;
+
+-- Internal signal declarations
+signal reg_cs : unsigned(NUM_CS-1 downto 0);
+signal reg_cmd : unsigned(7 downto 0);
+signal reg_adr : unsigned(23 downto 0);
+signal sr : unsigned(7 downto 0);
+signal clk_count : unsigned(4 downto 0);
+signal ack : std_logic;
+ -- For the state machine
+type FSM_STATE_TYPE is (IDLE, SEND_CMD, SEND_ADR, SHIFT_A2, SHIFT_A1, SHIFT_A0, GET_BYTE, GIVE_ACK);
+signal fsm_state : FSM_STATE_TYPE;
+
+
+-----------------------------------------------------------------------------
+begin
+
+ -- Register read mux
+ with (adr_i) select
+ dat_o <=
+ u_resize(reg_cs,DAT_SIZE) when "0000",
+ u_resize(reg_cmd,DAT_SIZE) when "0001",
+ u_resize(reg_adr,DAT_SIZE) when "0010",
+ u_resize(sr,DAT_SIZE) when others;
+
+ -- Create acknowledge signal
+ ack <= '1' when sel_i='1' and fsm_state=GIVE_ACK else '0';
+
+ ack_o <= ack;
+
+ -- Create SPI signals
+ spi_cs_o <= reg_cs;
+ spi_so_o <= sr(7);
+ spi_sck_o <= clk_count(0);
+
+ -- The main process
+ main_proc: process(sys_clk, sys_rst_n)
+ begin
+ if (sys_rst_n='0') then
+ reg_cs <= DEF_R_0(NUM_CS-1 downto 0);
+ reg_cmd <= DEF_R_1(reg_cmd'length-1 downto 0);
+ reg_adr <= DEF_R_2(reg_adr'length-1 downto 0);
+ sr <= (others=>'0');
+ clk_count <= to_unsigned(0,clk_count'length);
+ fsm_state <= IDLE;
+ elsif (sys_clk'event and sys_clk='1') then
+ if (sys_clk_en='1') then
+ -- Decrement the clock counter
+ if (clk_count>0) then
+ clk_count <= clk_count-1;
+ -- Handle the shift register
+ if (fsm_state=GET_BYTE and clk_count(0)='0') or (fsm_state/=GET_BYTE and clk_count(0)='1') then
+ sr <= sr(6 downto 0) & spi_si_i;
+ end if;
+ end if;
+ -- Handle state transitions
+ case (fsm_state) is
+
+ when IDLE =>
+ null;
+
+ when SEND_CMD =>
+ sr <= reg_cmd;
+ clk_count <= to_unsigned(16,clk_count'length);
+ fsm_state <= SHIFT_A0;
+
+ when SEND_ADR =>
+ sr <= reg_adr(23 downto 16);
+ clk_count <= to_unsigned(16,clk_count'length);
+ fsm_state <= SHIFT_A2;
+
+ when SHIFT_A2 =>
+ if (clk_count=0) then
+ sr <= reg_adr(15 downto 8);
+ clk_count <= to_unsigned(16,clk_count'length);
+ fsm_state <= SHIFT_A1;
+ end if;
+
+ when SHIFT_A1 =>
+ if (clk_count=0) then
+ sr <= reg_adr(7 downto 0);
+ clk_count <= to_unsigned(16,clk_count'length);
+ fsm_state <= SHIFT_A0;
+ end if;
+
+ when SHIFT_A0 =>
+ if (clk_count=0) then
+ fsm_state <= GIVE_ACK;
+ end if;
+
+ when GET_BYTE =>
+ if (clk_count=0) then
+ fsm_state <= GIVE_ACK;
+ end if;
+
+ when GIVE_ACK =>
+ fsm_state <= IDLE;
+
+ --when others =>
+ -- fsm_state <= IDLE;
+ end case;
+
+ -- Handle bus writes to registers
+ if (fsm_state=IDLE and sel_i='1' and we_i='1') then
+ case (adr_i) is
+ when "0000" =>
+ reg_cs <= dat_i(NUM_CS-1 downto 0);
+ fsm_state <= GIVE_ACK;
+ when "0001" =>
+ reg_cmd <= dat_i(reg_cmd'length-1 downto 0);
+ fsm_state <= SEND_CMD;
+ when "0010" =>
+ reg_adr <= dat_i(reg_adr'length-1 downto 0);
+ fsm_state <= SEND_ADR;
+ when others => null;
+ sr <= dat_i(sr'length-1 downto 0);
+ clk_count <= to_unsigned(16,clk_count'length);
+ fsm_state <= SHIFT_A0;
+ end case;
+ end if;
+ -- Handle SPI read cycle
+ if (fsm_state=IDLE and sel_i='1' and we_i='0') then
+ case (adr_i) is
+ when "0000" =>
+ fsm_state <= GIVE_ACK;
+ when "0001" =>
+ fsm_state <= GIVE_ACK;
+ when "0010" =>
+ fsm_state <= GIVE_ACK;
+ when others =>
+ clk_count <= to_unsigned(16,clk_count'length);
+ fsm_state <= GET_BYTE;
+ end case;
+ end if;
+
+ end if; -- sys_clk_en
+ end if; -- sys_clk
+ end process;
+
+
+end beh;
+
+-------------------------------------------------------------------------------
+-- SPI Flash System Initializer
+-------------------------------------------------------------------------------
+--
+-- Author: John Clayton
+-- Date : July 31, 2013 Created this module starting with code copied from
+-- another module. Began writing description.
+-- Aug. 5, 2013 Simulated the design. Revamped the register
+-- structure, added init_chain feature. Combined
+-- states in fl_state FSM.
+-- Aug. 7, 2013 Added explicit reset bit.
+--
+-- Description
+-------------------------------------------------------------------------------
+-- This module uses an interface to SPI serial FLASH memory devices to allow
+-- reading/writing/erasing of the FLASH. It includes a state machine that
+-- coordinates many of the required commands automatically, to make the
+-- process of reading and writing SPI FLASH appear as though a simple RAM
+-- is being used. Moreover, the state machine has an initialization mode
+-- which can read bytes out of the selected SPI FLASH device and present
+-- them on an 8-bit parallel output port.
+--
+-- The actual rate of outputting "initialization bytes" is determined by
+-- the init_ack_i input, up to the maximum available rate Fmax of approx.
+-- 2.7 MBytes/second. This maximum byte transfer rate is calculated
+-- according to the following formula:
+--
+-- Fmax = (Fsys_clk / N)
+--
+-- Where N=18 sys_clks required per byte obtained, and Fsys_clk is
+-- currently 50 MHz. Other sys_clk rates would also work.
+--
+-- The init_adr_o output allows the outgoing bytes to be treated in
+-- different ways, or stored into different places, depending on the address
+-- setting being used. Note that the init_adr_o output is taken directly
+-- from the init address register, which refers to the addresses within
+-- the SPI devices themselves. This signal therefore not required to be
+-- used as-is during initialization. Feel free to substitute any addresses
+-- you want, or re-map the initialization addresses as needed.
+--
+-- The idea is that the parallel output bytes can be used to send commands
+-- on a system bus. One way to do this is to attach a UART, which would
+-- inject ASCII characters into an ASCII command interpreter, such as
+-- "async_syscon" or "udp_ip_syscon".
+--
+-- Another way would be to send the bytes into a binary command interpreter.
+--
+-- Another way would be to attach the init port as a requester on the arbiter
+-- for the target bus.
+--
+-- In addition to creating initialization commands at power on, the output
+-- bytes can also be treated as DSP samples of a waveform. Thus, the output
+-- could be used to generate audio "sound bytes" [pun intended] at a set
+-- sample rate and bits per sample.
+--
+-- In fact, it is conceivable that through the use of a command interpreter,
+-- a final initialization command could reprogram the initialization address
+-- and quantity, in order to begin a whole new initialization sequence! Oh,
+-- the sheer joy of it! If there is no command interpreter being used, there
+-- is an alternative method of achieving this serial-init-sequencing joy.
+-- A chain enable bit is provided in the initialization settings register
+-- which causes the initialization sequencer to "re-up" by using the last
+-- eight bytes of the current initialization sequence as a new address and
+-- quantity for the next one. Isn't that simply grand?
+--
+-- Think of what you could do... You could set up registers in the first
+-- initialization sequence, and then invoke another sequence which would
+-- play some kind of audio greeting. It could be music, or perhaps a voice.
+-- Like the audio might say "System is now fully functional, and ready!"
+-- Like that. You know, it isn't bad to have a healthy imagination.
+-- At least, that's what I imagine.
+--
+-- As a corollary benefit of implementing the initialization function, an
+-- additional SPI FLASH memory mapping "feature" is provided. This means
+-- that the entire contents of the SPI FLASH device is memory mapped, with a
+-- separate address for each byte within the device. Sequential reads and
+-- writes are allowed within this address space, and the needed WREN and
+-- other commands to perform the reads/writes are performed automatically.
+-- This also means that the bus cycle acknowledge signal when using the
+-- memory mapped region can take quite a long time to appear, on the order
+-- of 5 ms. Also note that the key word when using the memory mapped
+-- region is "sequential." This is so important that this module actually
+-- enforces sequential accesses, by keeping a copy of the initial address
+-- and incrementing that during the session. The "session" begins when
+-- an access is requested within the memory mapped region, and ends when
+-- an "access idle" timeout is reached. Therefore, temporally-sequential
+-- accesses which attempt to decrement the address, or jump around in
+-- other ways, will actually result in sequential bytes being read/written
+-- during the session.
+--
+-- The system initializer is an extension of a much simpler register-based
+-- "low-level" SPI Flash interface module. The "low-level" features are
+-- preserved in this module.
+--
+-- The original interface was written in order to be as generic as possible,
+-- without many extra frills. The module was written by and tested using
+-- the Adesto Technologies AT25DF641 and ST Micro M25P64 datasheets. Also,
+-- hardware testing was initially done on the M25P64 mounted on a Lattice
+-- Semiconductor "Versa" FPGA development board. Nevertheless, this design
+-- should work quite easily with other SPI FLASH devices and other FPGAs.
+--
+-- For the basic SPI interface, there are three registers involved.
+-- There is one for the SPI command byte, one for the 24-bit SPI address,
+-- and one for data to be read from, or written to, the SPI serial FLASH
+-- device. The basic interface was preserved in order to allow for any
+-- desired command to be explicitly executed via register accesses.
+--
+-- The SPI serial FLASH interface is implemented using four signals:
+-- spi_cs_o = SPI chip select outputs
+-- spi_sck_o = Serial Clock output
+-- spi_si_i = Serial Data input
+-- spi_so_o = Serial Data output
+--
+-- For multiple SPI devices, the spi_cs_o output is implemented as a bus
+-- of register driven outputs. The size of the bus is controlled by generics,
+-- allowing any number of SPI devices to be selected. Because these outputs
+-- are not decoded as a 1-active-of-N type signal, it is possible to select
+-- multiple devices simultaneously, which may work well for writing and
+-- erasing, but not for reading. You have been warned!
+--
+-- The spi_sck_o output is generated at half the sys_clk rate, and it is
+-- not adjustable. To obtain operation at lower clock rates without
+-- using a lower sys_clk frequency, consider slowing down the operation
+-- of this module by the use of the sys_clk_en signal.
+--
+-- This module takes the approach that the serial communication with the
+-- SPI device is not continuous. Instead, the command is completed in
+-- segments or phases. To begin a command, a bus write to the SPI chip
+-- select register asserts '0' on the desired chip select bits. Then, a write
+-- to the command register causes the command to be sent out, generating a
+-- burst of eight clock pulses. Another separate bus cycle is then required
+-- in order to send out the address. After that, bus cycles can be used to
+-- either read or write data bytes. At the conclusion of those activities,
+-- the command can be terminated or executed by simply writing to the SPI
+-- chip select register to raise the chip select bit back to a '1'.
+--
+-- This approach works because the spi_sck_o output is under control of
+-- this module.
+--
+-- There are no provisions for use of the "hold" or "WP" (write protect)
+-- signals. If desired, it might be possible to use sys_clk_en as a hold
+-- signal, although this has not been tested or investigated in any detail.
+--
+-- The registers are summarized as follows:
+--
+-- Address Structure Function
+-- ------- -------------- -----------------------------------------------------
+-- 0x0 (N+16:16,N:0) SPI chip Selects, where N=NUM_CS-1.
+-- 0x1 (7:0) SPI Command Byte
+-- 0x2 (23:0) SPI Address Bytes
+-- 0x3 (31:0) Init Address
+-- 0x4 (31:0) Init Quantity (bytes)
+-- 0x5 (3:0) Init Settings
+-- 0x6+ (7:0) SPI Data
+--
+-- Notes on Registers:
+--
+-- (0x0) low-level SPI Chip Select control
+--
+-- This register contains a single bit, which is directly mapped to the
+-- spi_cs_o output during low-level SPI operations. During ram mapped
+-- operations and initialization sequences, the spi_cs_o output is
+-- controlled automatically by a state machine so that during those
+-- operations, this bit has no effect.
+--
+-- (0x1) low-level SPI Command Byte
+--
+-- This is part of the low-level SPI interface, and is not involved in
+-- initialization or RAM mapping of the SPI devices.
+--
+-- Writing to this register causes the contents to be updated, and also
+-- sends out the new contents in serial form, most significant bit first.
+-- Reading this register returns the contents, but does not affect the
+-- SPI interface.
+--
+-- (0x2) low-level SPI Address
+--
+-- This is part of the low-level SPI interface, and is not involved in
+-- initialization or RAM mapping of the SPI devices.
+--
+-- Writing to this register causes the contents to be updated, and also
+-- sends out the new contents in serial form, most significant bits first.
+-- Reading this register returns the contents, but does not affect the
+-- SPI interface.
+--
+-- The number of bytes used for SPI addresses is set by the constant
+-- SPI_ADR_BYTES.
+--
+-- (0x3) Init Address
+--
+-- Bits (31:0) contain the initialization address, which consists of
+-- four bytes: [decode][sector][page][byte]
+-- This grouping of fields is actually based on the size of the
+-- SPI FLASH devices being tested. If larger devices are used,
+-- for example, then the [decode] field may use fewer bits, and
+-- the [sector] field may grow. Similarly, the [page] field might
+-- change if the devices being used have pages larger than 256
+-- bytes. So, it's a matter of interpretation. The main idea is
+-- for form a flat address space which maps all the attached SPI
+-- devices.
+--
+-- (0x4) Initialization Quantity (Bytes)
+-- This register contains the number of bytes to read from the SPI
+-- FLASH device during an initialization operation.
+-- The number can be quite large, up to 0x800000 (8 Mbytes) for the
+-- AT25DF641 and M25P64. In anticipation of larger future devices,
+-- this field was made a full 32-bits, allowing up to 4 gigabytes.
+-- Operation of the initialization cycle begins immediately once
+-- this field is set to anything greater than zero. Also, the
+-- quantity is "live" and it decrements during initialization.
+--
+-- (0x5) Init Settings
+--
+-- Bit (0) is the R/W Access Enable bit. Set this bit in order to
+-- freely read and write to the SPI FLASH via the memory mapped
+-- interface. When it is cleared, the memory mapped interface does
+-- not respond with a bus cycle acknowledge, since it is not
+-- operating.
+-- Bit (1) is the Initialization chain enable bit. Setting this bit
+-- causes the last eight bytes of the initialization to be withheld.
+-- Instead of being sent out as bus cycles on the parallel init bus,
+-- the bytes are used to reset the initializer for another run
+-- using a new address and a new quantity. Since the init_ack_i
+-- input is used to throttle initialization cycles when delays are
+-- required, no delay is provided within the eight "re-up" bytes.
+-- The re-up bytes are composed of four address bytes, followed by
+-- four quantity bytes. The fields are stored least significant
+-- byte first.
+-- Bit (2) is the Erase bit. Set this bit to erase the sector
+-- currently selected by the [sector] field in the address register.
+-- Note that sector erase times can be on the order of seconds.
+-- (Typically 1s, but up to 3s for M25P64. Typically 400ms, but
+-- up to 950ms for AT25DF641.) Since the erase operation is
+-- self-timed inside the device, this module polls the device status
+-- register to see when the erase operation is completed. When it
+-- is, the erase bit is then cleared. No other operations are
+-- possible while this is occurring. Because the sector erase can
+-- take so long, there is a way to break out by using the explicit
+-- reset bit. Also, there is no automatic timeout from the sector
+-- erase operation.
+-- Bit (3) is an explicit reset bit. Setting it resets both state
+-- machines. It is a write only bit, and returns a '0' when read.
+-- It can be used to "break out" of an operation, such as a long
+-- sector erase.
+--
+-- (0x6+) SPI Data Byte (System cycles in the range [0x6..0xF] access this.)
+--
+-- This is part of the low-level SPI interface, and is not involved in
+-- initialization or RAM mapping of the SPI devices.
+--
+-- There is no local storage register inside the module for this address.
+-- Writing to this address causes the data to be sent out in serial form,
+-- most significant bit first.
+-- Reading this address causes a burst of eight clock pulses to be issued
+-- from the SPI interface, effectively reading in eight bits and returning
+-- them in parallel over the bus interface.
+-- Successive reads or writes therefore program or read successive locations
+-- in the SPI device.
+-- It should be noted that the SPI serial FLASH devices may allow continuous
+-- reads which go on indefinitely, cycling through the entire contents of
+-- the FLASH array. On the other hand, writes are buffered inside the
+-- FLASH device, so that a "Byte/Page Program" instruction may allow from
+-- 1 to 256 sequential bytes to be programmed. If more bytes are sent to
+-- the FLASH device, it may simply cause the FIFO buffer inside the FLASH
+-- device to hold the last 256 bytes for programming, discarding the bytes
+-- which were written at first.
+--
+-- The sys_rst_n input is an asynchronous reset.
+
+library IEEE;
+use IEEE.STD_LOGIC_1164.ALL;
+use IEEE.NUMERIC_STD.ALL;
+use IEEE.MATH_REAL.ALL;
+
+library work;
+use work.dds_pack.all;
+use work.convert_pack.all;
+
+ entity spi_flash_sys_init is
+ generic(
+ SYS_CLK_RATE : real := 50000000.0;
+ FLASH_IDLE : natural := 2; -- Number of ms idle before RAM-mapped FLASH operation closeout
+ DECODE_BITS : natural := 8; -- Number of init address upper-bits decoded to select individual SPI devices.
+ DEF_R_0 : unsigned(31 downto 0) := str2u("00000001",32); -- low-level SPI Chip Select control
+ DEF_R_1 : unsigned(31 downto 0) := str2u("00000003",32); -- low-level SPI Command byte
+ DEF_R_2 : unsigned(31 downto 0) := str2u("007F0000",32); -- low-level SPI Address
+ DEF_R_3 : unsigned(31 downto 0) := str2u("007F0000",32); -- Init Address
+ DEF_R_4 : unsigned(31 downto 0) := str2u("00000100",32); -- Init Bytes
+ DEF_R_5 : unsigned(31 downto 0) := str2u("00000001",32) -- Init Settings
+ );
+ port (
+ -- System Clock and Clock Enable
+ sys_rst_n : in std_logic;
+ sys_clk : in std_logic;
+ sys_clk_en : in std_logic;
+
+ -- Bus interface
+ adr_i : in unsigned(3 downto 0);
+ sel_i : in std_logic;
+ we_i : in std_logic;
+ dat_i : in unsigned(31 downto 0);
+ dat_o : out unsigned(31 downto 0);
+ ack_o : out std_logic;
+
+ -- RAM mapped SPI FLASH access port
+ -- (fl_ack_o may take ~5ms during page program)
+ fl_adr_i : in unsigned(31 downto 0);
+ fl_sel_i : in std_logic;
+ fl_we_i : in std_logic;
+ fl_dat_i : in unsigned(7 downto 0);
+ fl_dat_o : out unsigned(7 downto 0);
+ fl_ack_o : out std_logic;
+
+ -- Init data output port
+ init_adr_o : out unsigned(31 downto 0);
+ init_dat_o : out unsigned(7 downto 0);
+ init_cyc_o : out std_logic;
+ init_ack_i : in std_logic;
+ init_fin_o : out std_logic; -- Stays high when init is finished
+
+ -- SPI interface
+ -- "hold" and "WP" are not implemented.
+ spi_adr_o : out unsigned(DECODE_BITS-1 downto 0);
+ spi_cs_o : out std_logic;
+ spi_sck_o : out std_logic;
+ spi_so_o : out std_logic;
+ spi_si_i : in std_logic
+
+ );
+end spi_flash_sys_init;
+
+architecture beh of spi_flash_sys_init is
+
+-- Constants
+constant DAT_SIZE : natural := 32;
+constant IDLE_TB_FREQ : real := 1000.0; -- Timebase is millisecond resolution
+constant IDLE_TB_ACC_BITS : natural := 24;
+constant IDLE_TIMER_WIDTH : natural := timer_width(FLASH_IDLE);
+
+constant SPI_ADR_BYTES : natural := 3; -- Increase for larger size devices
+constant SPI_ACNT_WIDTH : natural := timer_width(SPI_ADR_BYTES);
+
+constant SPI_CMD_PP : natural := 16#02#;
+constant SPI_CMD_READ : natural := 16#03#;
+constant SPI_CMD_RDSR : natural := 16#05#;
+constant SPI_CMD_WREN : natural := 16#06#;
+constant SPI_CMD_SE : natural := 16#D8#;
+
+constant CS_HIGH_CYCLES : natural := 8; -- minimum # of sysclks spi_cs_o will be driven high.
+constant CLK_COUNT_1BYTE : natural := 16;
+constant CLK_COUNT_DESEL : natural := CLK_COUNT_1BYTE+CS_HIGH_CYCLES;
+
+-- Internal signal declarations
+signal reg_cs : std_logic;
+signal reg_cmd : unsigned(7 downto 0);
+signal reg_adr : unsigned(31 downto 0);
+signal reg_ispi_adr : unsigned(31 downto 0);
+signal adr_count : unsigned(SPI_ACNT_WIDTH-1 downto 0);
+signal adr_word : unsigned(31 downto 0);
+signal adr_byte : unsigned(7 downto 0);
+signal sr : unsigned(7 downto 0);
+signal clk_count : unsigned(4 downto 0);
+signal spi_ack : std_logic;
+signal reg_we : std_logic;
+signal reg_rw : std_logic;
+signal reg_chain : std_logic;
+signal reg_erasing : std_logic;
+signal reg_init_len : unsigned(31 downto 0);
+signal reg_new_len : unsigned(31 downto 0);
+signal idle_timer : unsigned(IDLE_TIMER_WIDTH-1 downto 0);
+signal idle_tb_pulse : std_logic;
+signal idle_tb_ftw : unsigned(IDLE_TB_ACC_BITS-1 downto 0);
+signal idle_tb_hold : std_logic;
+
+ -- For the SPI state machine
+type SPI_STATE_TYPE is (IDLE, SEND_CMD, SEND_ADR, SHIFT_ADR, SHIFT_BYTE,
+ GET_BYTE, GIVE_ACK);
+signal spi_state : SPI_STATE_TYPE;
+
+ -- For the FLASH state machine
+type FLASH_STATE_TYPE is (IDLE, R_WREN, R_CMD, R_ADR, R_DAT, R_WAIT,
+ R_STAT_1, R_STAT_2, INIT_CYCLE, INIT_CHAIN, INIT_RESTART);
+signal fl_state : FLASH_STATE_TYPE;
+signal fl_ack : std_logic;
+
+
+-----------------------------------------------------------------------------
+begin
+
+ -- Register read mux
+ with (adr_i) select
+ dat_o <=
+ to_unsigned(0,31) & reg_cs when "0000",
+ u_resize(reg_cmd,DAT_SIZE) when "0001",
+ reg_adr when "0010",
+ reg_ispi_adr when "0011",
+ u_resize(reg_init_len,DAT_SIZE) when "0100",
+ to_unsigned(0,29) & reg_erasing & reg_chain & reg_rw when "0101",
+ u_resize(sr,DAT_SIZE) when others;
+
+ -- Create acknowledge signals
+ spi_ack <= '1' when spi_state=GIVE_ACK else '0';
+ ack_o <= '1' when spi_ack='1' and sel_i='1' else '0';
+ fl_ack_o <= fl_ack;
+
+ -- Provide FLASH data output
+ fl_dat_o <= sr;
+
+ -- Provide initialization outputs
+ init_adr_o <= reg_ispi_adr;
+ init_cyc_o <= '1' when fl_state=INIT_CYCLE else '0';
+
+ -- Create SPI signals
+ spi_adr_o <= adr_word(adr_word'length-1 downto adr_word'length-DECODE_BITS);
+ spi_cs_o <= reg_cs when fl_state=IDLE else
+ '0' when clk_count<=CLK_COUNT_1BYTE else
+ '1';
+ spi_so_o <= sr(7);
+ spi_sck_o <= clk_count(0) when clk_count'0') when 0,
+-- adr_word(8*to_integer(adr_count)-1 downto 8*(to_integer(adr_count)-1)) when others;
+ with (to_integer(adr_count)) select
+ adr_byte <=
+ (others=>'0') when 0,
+ adr_word(7 downto 0) when 1,
+ adr_word(15 downto 8) when 2,
+ adr_word(23 downto 16) when 3,
+ adr_word(31 downto 24) when 4,
+ (others=>'0') when others;
+
+ -- Create timebase for idle timer
+ idle_tb_ftw <= to_unsigned(integer(IDLE_TB_FREQ*(2**real(IDLE_TB_ACC_BITS))/SYS_CLK_RATE),idle_tb_ftw'length);
+ idle_tb_hold <= '0' when (fl_state=IDLE or fl_state=R_WAIT or fl_state=INIT_CYCLE) else '1';
+ timer_0 : dds_squarewave_phase_load
+ generic map(
+ ACC_BITS => IDLE_TB_ACC_BITS
+ )
+ port map(
+
+ sys_rst_n => sys_rst_n,
+ sys_clk => sys_clk,
+ sys_clk_en => sys_clk_en,
+
+ -- Frequency setting
+ freq_i => idle_tb_ftw,
+
+ -- Synchronous load
+ phase_i => to_unsigned(2**(IDLE_TB_ACC_BITS-1),IDLE_TB_ACC_BITS),
+ phase_ld_i => idle_tb_hold,
+
+ -- Output
+ pulse_o => idle_tb_pulse,
+ squarewave_o => open
+ );
+
+ -- Handle state machines and writes to registers
+ reg_proc: process(sys_clk, sys_rst_n)
+ begin
+ if (sys_rst_n='0') then
+ reg_cs <= DEF_R_0(0);
+ reg_cmd <= DEF_R_1(reg_cmd'length-1 downto 0);
+ reg_adr <= DEF_R_2;
+ reg_ispi_adr <= DEF_R_3;
+ reg_rw <= DEF_R_5(0);
+ reg_chain <= DEF_R_5(1);
+ reg_init_len <= DEF_R_4(reg_init_len'length-1 downto 0);
+ reg_new_len <= (others=>'0');
+ sr <= (others=>'0');
+ adr_count <= to_unsigned(1,adr_count'length);
+ clk_count <= to_unsigned(CLK_COUNT_DESEL,clk_count'length);
+ spi_state <= IDLE;
+ fl_state <= IDLE;
+ fl_ack <= '0';
+ idle_timer <= to_unsigned(FLASH_IDLE,idle_timer'length);
+ init_dat_o <= (others=>'0');
+ reg_we <= '0';
+ elsif (sys_clk'event and sys_clk='1') then
+ if (sys_clk_en='1') then
+ -- Decrement the SPI clock counter
+ if (clk_count>0) then
+ clk_count <= clk_count-1;
+ -- Handle the SPI shift register
+ if (spi_state=GET_BYTE and clk_count(0)='0' and clk_count0 and idle_tb_pulse='1') then
+ idle_timer <= idle_timer-1;
+ end if;
+ ----------------------------------
+ -- Handle SPI state transitions --
+ ----------------------------------
+ case (spi_state) is
+
+ when IDLE =>
+ null;
+
+ when SEND_CMD =>
+ sr <= reg_cmd;
+ clk_count <= to_unsigned(CLK_COUNT_1BYTE,clk_count'length);
+ spi_state <= SHIFT_BYTE;
+
+ -- adr_count must have already been set when going into this state,
+ -- to ensure that sr is loaded with the correct bytes
+ when SEND_ADR =>
+ sr <= adr_byte;
+ clk_count <= to_unsigned(CLK_COUNT_1BYTE,clk_count'length);
+ spi_state <= SHIFT_ADR;
+
+ when SHIFT_ADR =>
+ if (clk_count=0) then
+ if (adr_count=1) then
+ spi_state <= GIVE_ACK;
+ else
+ adr_count <= adr_count-1;
+ spi_state <= SEND_ADR;
+ end if;
+ end if;
+
+ when SHIFT_BYTE =>
+ if (clk_count=0) then
+ spi_state <= GIVE_ACK;
+ end if;
+
+ when GET_BYTE =>
+ if (clk_count=0) then
+ spi_state <= GIVE_ACK;
+ end if;
+
+ when GIVE_ACK =>
+ spi_state <= IDLE;
+
+ --when others =>
+ -- spi_state <= IDLE;
+ end case;
+
+ ------------------------------------
+ -- Handle FLASH state transitions --
+ ------------------------------------
+ -- Default Values
+ fl_ack <= '0';
+ case (fl_state) is
+
+ when IDLE =>
+ if (reg_rw='1' and spi_state=IDLE) then
+ if (fl_sel_i='1') then
+ -- Store local copy of requested address
+ -- All subsequenct accesses within the SPI command session will be sequential
+ -- The external address does not guarantee this, but the internal
+ -- one does.
+ reg_ispi_adr <= fl_adr_i;
+ -- Store write/read indication
+ reg_we <= fl_we_i;
+ idle_timer <= to_unsigned(FLASH_IDLE,idle_timer'length);
+ if (fl_we_i='1') then
+ reg_cmd <= to_unsigned(SPI_CMD_PP,reg_cmd'length);
+ fl_state <= R_WREN;
+ sr <= to_unsigned(SPI_CMD_WREN,sr'length);
+ clk_count <= to_unsigned(CLK_COUNT_1BYTE,clk_count'length);
+ spi_state <= SHIFT_BYTE;
+ else
+ reg_cmd <= to_unsigned(SPI_CMD_READ,reg_cmd'length);
+ fl_state <= R_CMD;
+ sr <= to_unsigned(SPI_CMD_READ,sr'length);
+ clk_count <= to_unsigned(CLK_COUNT_1BYTE,clk_count'length);
+ spi_state <= SHIFT_BYTE;
+ end if;
+ end if;
+ end if;
+ -- If there is initialization to do, get it done.
+ -- This has priority over other operations.
+ if (reg_init_len>0) then
+ reg_cmd <= to_unsigned(SPI_CMD_READ,reg_cmd'length);
+ fl_state <= R_CMD;
+ sr <= to_unsigned(SPI_CMD_READ,sr'length);
+ clk_count <= to_unsigned(CLK_COUNT_1BYTE,clk_count'length);
+ spi_state <= SHIFT_BYTE;
+ end if;
+
+ when R_WREN =>
+ if (spi_ack='1') then
+ sr <= reg_cmd;
+ -- Deselect SPI device to execute SPI command
+ clk_count <= to_unsigned(CLK_COUNT_DESEL,clk_count'length);
+ spi_state <= SHIFT_BYTE;
+ fl_state <= R_CMD;
+ end if;
+
+ when R_CMD =>
+ if (spi_ack='1') then
+ adr_count <= to_unsigned(SPI_ADR_BYTES,adr_count'length);
+ spi_state <= SEND_ADR;
+ fl_state <= R_ADR;
+ end if;
+
+ when R_ADR =>
+ if (spi_ack='1') then
+ if (reg_cmd=SPI_CMD_SE) then
+ sr <= to_unsigned(SPI_CMD_RDSR,sr'length);
+ -- Deselect SPI device to execute SPI command
+ clk_count <= to_unsigned(CLK_COUNT_DESEL,clk_count'length);
+ spi_state <= SHIFT_BYTE;
+ fl_state <= R_STAT_1;
+ else
+ clk_count <= to_unsigned(CLK_COUNT_1BYTE,clk_count'length);
+ spi_state <= SHIFT_BYTE;
+ fl_state <= R_DAT;
+ sr <= fl_dat_i; -- Needed for writes, and does not affect reads.
+ end if;
+ end if;
+
+ when R_DAT =>
+ if (reg_init_len>0) then
+ -- Handle init case
+ if (spi_ack='1') then
+ if (reg_chain='1' and reg_init_len<=8) then
+ fl_state <= INIT_CHAIN;
+ else
+ init_dat_o <= sr;
+ idle_timer <= to_unsigned(FLASH_IDLE,idle_timer'length);
+ fl_state <= INIT_CYCLE;
+ end if;
+ end if;
+ else
+ -- Handle memory mapped I/O case
+ -- Await timeout, in case current cycle is not de-asserted in time.
+ if (idle_timer=0) then
+ if (reg_cmd=SPI_CMD_READ) then
+ fl_state <= IDLE;
+ else
+ -- Deselect SPI device to execute SPI command
+ clk_count <= to_unsigned(CLK_COUNT_DESEL,clk_count'length);
+ idle_timer <= to_unsigned(FLASH_IDLE,idle_timer'length);
+ sr <= to_unsigned(SPI_CMD_RDSR,sr'length);
+ spi_state <= SHIFT_BYTE;
+ fl_state <= R_STAT_1;
+ end if;
+ end if;
+ -- Acknowledge the memory mapped cycle
+ if (spi_ack='1') then
+ fl_ack <= '1';
+ end if;
+ -- Handle de-assertion of current cycle
+ if (fl_sel_i='0') then
+ idle_timer <= to_unsigned(FLASH_IDLE,idle_timer'length);
+ fl_state <= R_WAIT;
+ end if;
+ end if;
+
+ -- This is a "waiting state" to see if the current SPI session is
+ -- going to continue. It continues if another write is requested.
+ -- within the allowed timeout window.
+ when R_WAIT =>
+ -- On timeout, terminate the SPI PP command.
+ if (idle_timer=0) then
+ if (reg_cmd=SPI_CMD_READ) then
+ fl_state <= IDLE;
+ else
+ -- Deselect SPI device to execute SPI command
+ clk_count <= to_unsigned(CLK_COUNT_DESEL,clk_count'length);
+ idle_timer <= to_unsigned(FLASH_IDLE,idle_timer'length);
+ sr <= to_unsigned(SPI_CMD_RDSR,sr'length);
+ spi_state <= SHIFT_BYTE;
+ fl_state <= R_STAT_1;
+ end if;
+ end if;
+ -- If another similar cycle is requested, continue
+ if (fl_sel_i='1' and fl_we_i=reg_we) then
+ idle_timer <= to_unsigned(FLASH_IDLE,idle_timer'length);
+ sr <= fl_dat_i;
+ clk_count <= to_unsigned(CLK_COUNT_1BYTE,clk_count'length);
+ spi_state <= SHIFT_BYTE;
+ fl_state <= R_DAT;
+ end if;
+ -- If a different cycle is requested, terminate the SPI command
+ if (fl_sel_i='1' and fl_we_i/=reg_we) then
+ if (reg_cmd=SPI_CMD_READ) then
+ fl_state <= IDLE;
+ else
+ -- Deselect SPI device to execute SPI command
+ clk_count <= to_unsigned(CLK_COUNT_DESEL,clk_count'length);
+ idle_timer <= to_unsigned(FLASH_IDLE,idle_timer'length);
+ sr <= to_unsigned(SPI_CMD_RDSR,sr'length);
+ spi_state <= SHIFT_BYTE;
+ fl_state <= R_STAT_1;
+ end if;
+ end if;
+
+ -- Timeout is disabled for sector erase.
+ -- The erase register bit can be cleared to reset the state machine.
+ -- (It can take a veeeerrrryy long time to erase a sector...)
+ -- (Like up to a second!)
+ when R_STAT_1 =>
+ if (spi_ack='1') then
+ clk_count <= to_unsigned(CLK_COUNT_1BYTE,clk_count'length);
+ spi_state <= SHIFT_BYTE;
+ fl_state <= R_STAT_2;
+ end if;
+
+ when R_STAT_2 =>
+ if (idle_timer=0 and reg_cmd/=SPI_CMD_SE) then
+ fl_state <= IDLE;
+ elsif (spi_ack='1') then
+ if (sr(0)='1') then -- If WIP is set, keep checking status.
+ clk_count <= to_unsigned(CLK_COUNT_1BYTE,clk_count'length);
+ spi_state <= SHIFT_BYTE;
+ else
+ fl_state <= IDLE;
+ end if;
+ end if;
+
+ -- This state creates an init cycle
+ when INIT_CYCLE =>
+ -- Await timeout, in case current cycle is not acknowledged in time.
+ if (idle_timer=0) then
+ fl_state <= IDLE;
+ end if;
+ -- Await acknowledge of current cycle
+ if (init_ack_i='1') then
+ reg_ispi_adr <= reg_ispi_adr+1;
+ reg_init_len <= reg_init_len-1;
+ if (reg_init_len=1) then
+ fl_state <= IDLE;
+ else
+ idle_timer <= to_unsigned(FLASH_IDLE,idle_timer'length);
+ clk_count <= to_unsigned(CLK_COUNT_1BYTE,clk_count'length);
+ spi_state <= SHIFT_BYTE;
+ fl_state <= R_DAT;
+ end if;
+ end if;
+
+ when INIT_CHAIN =>
+ if (reg_init_len>4) then
+ reg_ispi_adr(7 downto 0) <= sr;
+ reg_ispi_adr(31 downto 8) <= reg_ispi_adr(23 downto 0);
+ else
+ reg_new_len(7 downto 0) <= sr;
+ reg_new_len(31 downto 8) <= reg_new_len(23 downto 0);
+ end if;
+ reg_init_len <= reg_init_len-1;
+ if (reg_init_len=1) then
+ fl_state <= INIT_RESTART;
+ else
+ idle_timer <= to_unsigned(FLASH_IDLE,idle_timer'length);
+ clk_count <= to_unsigned(CLK_COUNT_1BYTE,clk_count'length);
+ spi_state <= SHIFT_BYTE;
+ fl_state <= R_DAT;
+ end if;
+
+ when INIT_RESTART =>
+ if (reg_new_len>0) then
+ reg_init_len <= reg_new_len;
+ -- Deselect SPI device to execute SPI command
+ clk_count <= to_unsigned(CLK_COUNT_DESEL,clk_count'length);
+ idle_timer <= to_unsigned(FLASH_IDLE,idle_timer'length);
+ sr <= to_unsigned(SPI_CMD_RDSR,sr'length);
+ spi_state <= SHIFT_BYTE;
+ fl_state <= R_STAT_1;
+ else
+ fl_state <= IDLE;
+ end if;
+
+ when others =>
+ spi_state <= IDLE;
+ end case;
+
+ -- Handle bus writes to registers
+ if (spi_state=IDLE and fl_state=IDLE and sel_i='1' and we_i='1') then
+ case (adr_i) is
+ when "0000" =>
+ reg_cs <= dat_i(0);
+ spi_state <= GIVE_ACK;
+ when "0001" =>
+ reg_cmd <= dat_i(reg_cmd'length-1 downto 0);
+ spi_state <= SEND_CMD;
+ when "0010" =>
+ reg_adr <= dat_i;
+ adr_count <= to_unsigned(SPI_ADR_BYTES,adr_count'length);
+ spi_state <= SEND_ADR;
+ when "0011" =>
+ reg_ispi_adr <= dat_i(reg_ispi_adr'length-1 downto 0);
+ spi_state <= GIVE_ACK; -- A quick acknowledgment
+ when "0100" =>
+ reg_init_len <= dat_i(reg_init_len'length-1 downto 0);
+ spi_state <= GIVE_ACK; -- A quick acknowledgment
+ when "0101" =>
+ reg_rw <= dat_i(0);
+ reg_chain <= dat_i(1);
+ if (dat_i(2)='1') then
+ if (reg_erasing='0') then
+ reg_cmd <= to_unsigned(SPI_CMD_SE,reg_cmd'length);
+ fl_state <= R_WREN;
+ sr <= to_unsigned(SPI_CMD_WREN,sr'length);
+ clk_count <= to_unsigned(CLK_COUNT_1BYTE,clk_count'length);
+ spi_state <= SHIFT_BYTE;
+ end if;
+ else
+ spi_state <= GIVE_ACK; -- A quick acknowledgment
+ end if;
+ if (dat_i(3)='1') then
+ spi_state <= IDLE;
+ fl_state <= IDLE;
+ reg_init_len <= (others=>'0');
+ end if;
+ when others =>
+ sr <= dat_i(sr'length-1 downto 0);
+ clk_count <= to_unsigned(CLK_COUNT_1BYTE,clk_count'length);
+ spi_state <= SHIFT_BYTE;
+ end case;
+ end if;
+ -- Handle SPI read cycle
+ if (spi_state=IDLE and fl_state=IDLE and sel_i='1' and we_i='0') then
+ case (adr_i) is
+ when "0000" =>
+ spi_state <= GIVE_ACK;
+ when "0001" =>
+ spi_state <= GIVE_ACK;
+ when "0010" =>
+ spi_state <= GIVE_ACK;
+ when "0011" =>
+ spi_state <= GIVE_ACK;
+ when "0100" =>
+ spi_state <= GIVE_ACK;
+ when "0101" =>
+ spi_state <= GIVE_ACK;
+ when others =>
+ clk_count <= to_unsigned(CLK_COUNT_1BYTE,clk_count'length);
+ spi_state <= GET_BYTE;
+ end case;
+ end if;
+
+ end if; -- sys_clk_en
+ end if; -- sys_clk
+ end process;
+
+ -- Create signal that is high during sector erase activity
+ reg_erasing <= '1' when (fl_state/=IDLE and reg_cmd=SPI_CMD_SE) else '0';
+
+ -- Create signal that is high whenever initialization is not active.
+ init_fin_o <= '1' when reg_init_len=0 else '0';
+
+end beh;
+
+-------------------------------------------------------------------------------
+-- SPI Flash Simulator
+-------------------------------------------------------------------------------
+--
+-- Author: John Clayton
+-- Date : Aug. 7, 2013 Created this module starting with code copied from
+-- another module.
+-- Aug. 10, 2013 Added and simulated page programming and sector erase
+-- with concurrent status register reads. Only the
+-- WIP bit is implemented within the status register.
+--
+-- Description
+-------------------------------------------------------------------------------
+-- This module is a very simplistic SPI FLASH simulator. It was created with
+-- the intent of running a simulation, so that real data can be returned from
+-- this module to a device which is accessing a SPI FLASH device.
+--
+-- Because of its simple intent, this module does not implement all the
+-- functions of a typical SPI FLASH, but it does do reading and writing.
+-- It implements the "write enable latch" instruction, and it can be erased
+-- too.
+--
+-- The kind of device being simulated has 24 address bits, although the size
+-- of the simulated FLASH memory is much smaller. It will simply wrap around
+-- to the beginning of the array if the end is passed.
+--
+-- Because the unit is for simulation only, a separate clock domain is created
+-- for the spi_sclk_i input
+--
+-- The sys_rst_n input is an asynchronous reset.
+
+library IEEE;
+use IEEE.STD_LOGIC_1164.ALL;
+use IEEE.NUMERIC_STD.ALL;
+use IEEE.MATH_REAL.ALL;
+
+library work;
+use work.dds_pack.all;
+use work.convert_pack.all;
+use work.block_ram_pack.all;
+
+ entity spi_flash_simulator is
+ generic(
+ SYS_CLK_RATE : real := 50000000.0;
+ FLASH_ADR_BITS : natural := 8; -- Flash memory size is based on this
+ FLASH_INIT : string := ".\flash_sim_init.txt" -- Default RX packet digestion settings
+ );
+ port (
+ -- System Clock and Clock Enable
+ sys_rst_n : in std_logic;
+ sys_clk : in std_logic;
+ sys_clk_en : in std_logic;
+
+ -- SPI interface
+ -- "hold" and "WP" are not implemented.
+ spi_cs_i : in std_logic;
+ spi_sck_i : in std_logic;
+ spi_si_i : in std_logic;
+ spi_so_o : out std_logic
+
+ );
+ end spi_flash_simulator;
+
+architecture beh of spi_flash_simulator is
+
+-- Constants
+constant SPI_CMD_NOP : natural := 16#00#;
+constant SPI_CMD_PP : natural := 16#02#;
+constant SPI_CMD_READ : natural := 16#03#;
+constant SPI_CMD_RDSR : natural := 16#05#;
+constant SPI_CMD_WREN : natural := 16#06#;
+constant SPI_CMD_SE : natural := 16#D8#;
+
+constant WIP_PP_TIMEOUT : natural := 12; -- 1.2 milliseconds
+--constant WIP_SE_TIMEOUT : natural := 12000; -- 1.2 seconds (realistic number)
+constant WIP_SE_TIMEOUT : natural := 24; -- 2.4 milliseconds (for faster simulation)
+constant WIP_TB_FREQ : real := 10000.0; -- Timebase is 100 microsecond resolution
+constant WIP_TB_ACC_BITS : natural := 24;
+constant WIP_TIMER_WIDTH : natural := timer_width(WIP_SE_TIMEOUT);
+constant SECTOR_BITS : natural := 16; -- Determines # of bytes to erase
+
+-- Internal signal declarations
+signal reg_cmd : unsigned(7 downto 0);
+signal reg_adr : unsigned(23 downto 0);
+signal flash_adr : unsigned(FLASH_ADR_BITS-1 downto 0);
+signal array_adr : unsigned(FLASH_ADR_BITS-1 downto 0);
+signal flash_we : std_logic;
+signal flash_dat : unsigned(7 downto 0);
+signal sr : unsigned(7 downto 0);
+signal sr_full : std_logic;
+signal reg_wel : std_logic; -- Write Enable Latch bit
+signal reg_wip : std_logic; -- Write In Progress bit
+signal clk_count : unsigned(2 downto 0);
+signal clk_count_r1 : unsigned(2 downto 0);
+signal clk_count_r2 : unsigned(2 downto 0);
+signal wip_timer : unsigned(WIP_TIMER_WIDTH-1 downto 0);
+signal wip_tb_pulse : std_logic;
+signal wip_tb_ftw : unsigned(WIP_TB_ACC_BITS-1 downto 0);
+signal wip_tb_hold : std_logic;
+signal sector_adr : unsigned(SECTOR_BITS-1 downto 0);
+signal sector_erase : std_logic;
+signal erase_adr : unsigned(23 downto 0);
+signal array_dat_wr : unsigned(7 downto 0);
+
+ -- For the state machine
+type FSM_STATE_TYPE is (AWAIT_CMD, AWAIT_ADR_2, AWAIT_ADR_1, AWAIT_ADR_0,
+ AWAIT_CS_HI, STORE_BYTE);
+signal fsm_state : FSM_STATE_TYPE;
+
+
+-----------------------------------------------------------------------------
+begin
+
+ -- Create SPI signals
+ spi_so_o <= sr(7);
+
+ -- The shift register process
+ -- Nothing is ever synchronously cleared, loaded or reset here.
+ -- The shift register and counter simply "roll over" in a cyclical
+ -- way, forever after the asynchronous reset.
+ sr_proc: process(spi_sck_i, sys_rst_n)
+ begin
+ if (sys_rst_n='0') then
+ sr <= (others=>'0');
+ clk_count <= (others=>'0');
+ elsif (spi_sck_i'event and spi_sck_i='1') then
+ if (spi_cs_i='0') then
+ sr <= sr(6 downto 0) & spi_si_i;
+ clk_count <= clk_count+1;
+ -- Here assign the shift register
+ -- Action depends on command type
+ case (to_integer(reg_cmd)) is
+ when SPI_CMD_READ =>
+ if (fsm_state=AWAIT_CS_HI and clk_count=0) then
+ sr <= flash_dat;
+ end if;
+ when SPI_CMD_PP =>
+ null;
+ when SPI_CMD_SE =>
+ null;
+ when SPI_CMD_RDSR =>
+ if (fsm_state=AWAIT_CS_HI and clk_count=0) then
+ sr <= "0000000" & reg_wip;
+ end if;
+ when others =>
+ null;
+ end case;
+ else
+ clk_count <= (others=>'0');
+ end if;
+ end if; -- spi_sck_i
+ end process;
+
+ -- Create timebase for WIP timer
+ wip_tb_ftw <= to_unsigned(integer(WIP_TB_FREQ*(2**real(WIP_TB_ACC_BITS))/SYS_CLK_RATE),wip_tb_ftw'length);
+ wip_tb_hold <= '0' when reg_wip='1' else '1';
+ timer_0 : dds_squarewave_phase_load
+ generic map(
+ ACC_BITS => WIP_TB_ACC_BITS
+ )
+ port map(
+
+ sys_rst_n => sys_rst_n,
+ sys_clk => sys_clk,
+ sys_clk_en => sys_clk_en,
+
+ -- Frequency setting
+ freq_i => wip_tb_ftw,
+
+ -- Synchronous load
+ phase_i => to_unsigned(2**(WIP_TB_ACC_BITS-1),WIP_TB_ACC_BITS),
+ phase_ld_i => wip_tb_hold,
+
+ -- Output
+ pulse_o => wip_tb_pulse,
+ squarewave_o => open
+ );
+ reg_wip <= '1' when (wip_timer>0) else '0';
+
+ -- Create signal which indicates when the shift register is done shifting a byte
+ sr_full <= '1' when (clk_count_r1=0 and clk_count_r2=7) else '0';
+ -- Since the simulated FLASH memory array may be much smaller than the address,
+ -- select the salient part for use in addressing the array.
+ flash_adr <= u_resize(reg_adr,flash_adr'length);
+
+ -- The main process
+ main_proc: process(sys_clk, sys_rst_n)
+ begin
+ if (sys_rst_n='0') then
+ reg_cmd <= to_unsigned(SPI_CMD_NOP,reg_cmd'length);
+ reg_adr <= (others=>'0');
+ reg_wel <= '0';
+ fsm_state <= AWAIT_CMD;
+ wip_timer <= to_unsigned(0,wip_timer'length);
+ clk_count_r1 <= (others=>'0');
+ clk_count_r2 <= (others=>'0');
+ sector_adr <= (others=>'0');
+ sector_erase <= '0';
+ elsif (sys_clk'event and sys_clk='1') then
+ if (sys_clk_en='1') then
+ -- default values
+
+ -- Synchronize and capture the clk_count value, in order to
+ -- detect changes to it.
+ clk_count_r1 <= clk_count;
+ clk_count_r2 <= clk_count_r1;
+
+ -- Handle WIP timer count down
+ if (wip_tb_pulse='1' and wip_timer>0) then
+ wip_timer <= wip_timer-1;
+ end if;
+
+ -- Handle actual sector erase array storage operations
+ if (sector_erase='1') then
+ sector_adr <= sector_adr+1;
+ if (sector_adr=(2**sector_adr'length)-1) then
+ sector_erase <= '0';
+ end if;
+ end if;
+
+ -- Handle state transitions
+ case (fsm_state) is
+
+ when AWAIT_CMD =>
+ if (sr_full='1' and spi_cs_i='0') then
+ if (reg_wip='0') then
+ reg_cmd <= sr;
+ if (sr=SPI_CMD_WREN) then
+ reg_wel <= '1';
+ fsm_state <= AWAIT_CS_HI;
+ else
+ fsm_state <= AWAIT_ADR_2;
+ end if;
+ else
+ fsm_state <= AWAIT_CS_HI;
+ -- Only RDSR is allowed when there is a write in progress (WIP)
+ if (sr=SPI_CMD_RDSR) then
+ reg_cmd <= sr;
+ else
+ reg_cmd <= to_unsigned(SPI_CMD_NOP,reg_cmd'length);
+ end if;
+ end if;
+ end if;
+
+ when AWAIT_ADR_2 =>
+ if (sr_full='1') then
+ reg_adr(23 downto 16) <= sr;
+ fsm_state <= AWAIT_ADR_1;
+ end if;
+
+ when AWAIT_ADR_1 =>
+ if (sr_full='1') then
+ reg_adr(15 downto 8) <= sr;
+ fsm_state <= AWAIT_ADR_0;
+ end if;
+
+ when AWAIT_ADR_0 =>
+ if (sr_full='1') then
+ reg_adr(7 downto 0) <= sr;
+ fsm_state <= AWAIT_CS_HI;
+ end if;
+
+ when AWAIT_CS_HI =>
+ if (sr_full='1') then
+ -- Action depends on command type
+ case (to_integer(reg_cmd)) is
+ when SPI_CMD_READ =>
+ reg_adr <= reg_adr+1;
+ when SPI_CMD_PP =>
+ if (reg_wel='1') then
+ fsm_state <= STORE_BYTE;
+ end if;
+ when SPI_CMD_SE =>
+ null;
+ when SPI_CMD_RDSR =>
+ null;
+ when others =>
+ null;
+ end case;
+ end if;
+ if (spi_cs_i='1') then
+ fsm_state <= AWAIT_CMD;
+ if (reg_cmd/=SPI_CMD_WREN) then
+ reg_wel <= '0';
+ end if;
+ if (reg_cmd=SPI_CMD_PP) then
+ wip_timer <= to_unsigned(WIP_PP_TIMEOUT,wip_timer'length);
+ end if;
+ if (reg_cmd=SPI_CMD_SE) then
+ wip_timer <= to_unsigned(WIP_SE_TIMEOUT,wip_timer'length);
+ sector_erase <= '1';
+ end if;
+ end if;
+
+ -- Being in this state asserts the flash_we signal
+ when STORE_BYTE =>
+ reg_adr <= reg_adr+1;
+ fsm_state <= AWAIT_CS_HI;
+
+ --when others =>
+ -- fsm_state <= IDLE;
+ end case;
+
+ end if; -- sys_clk_en
+ end if; -- sys_clk
+ end process;
+
+ -- This BRAM contains the simulated FLASH contents
+ flash_ram : block_ram_dp_writethrough_hexfile_init
+ generic map(
+ init_file => FLASH_INIT,
+ fil_width => 8,
+ adr_width => FLASH_ADR_BITS,
+ dat_width => 8
+ )
+ port map (
+ clk_a => sys_clk,
+ clk_b => sys_clk,
+
+ adr_a_i => array_adr,
+ adr_b_i => to_unsigned(0,FLASH_ADR_BITS),
+
+ we_a_i => flash_we,
+ en_a_i => '1',
+ dat_a_i => array_dat_wr,
+ dat_a_o => flash_dat,
+
+ we_b_i => '0',
+ en_b_i => '0',
+ dat_b_i => to_unsigned(0,8),
+ dat_b_o => open
+ );
+ flash_we <= '1' when (fsm_state=STORE_BYTE or sector_erase='1') else '0';
+ array_adr <= flash_adr when sector_erase='0' else u_resize(erase_adr,array_adr'length);
+ erase_adr <= reg_adr(reg_adr'length-1 downto SECTOR_BITS) & sector_adr;
+ array_dat_wr <= sr when sector_erase='0' else str2u("FF",8);
+
+end beh;
+
Index: wishbone_spi_flash_interface/trunk/README.txt
===================================================================
--- wishbone_spi_flash_interface/trunk/README.txt (nonexistent)
+++ wishbone_spi_flash_interface/trunk/README.txt (revision 2)
@@ -0,0 +1,27 @@
+1. All of the SPI FLASH interface modules are in "spi_pack.vhd"
+2. The file "spi_flash_sim.txt" is used to define the contents of the
+ simulated SPI FLASH device which is used in the testbench.
+3. The testbench is part of the larger design
+ "spi_flash_interface_used_in_larger_FPGA_setting_for_register_initialization"
+ which is a .zip file containing many VHDL source files.
+
+ The SPI FLASH interface is used in the larger design to automatically
+ read the SPI FLASH, and provide ASCII command characters through a UART
+ which feeds a serial command interface.
+
+ The testbench essentially emulates a UART by reading a file. The serial
+ characters are used to issue commands which can exercise the various
+ parts of the design. The file which feeds the commands to the design
+ under test is called "rs232_test_in.txt" and the results file of
+ serial response characters is "rs232_test_out.txt" The files are
+ contained within the /testbench subfolder.
+
+ The operation of the larger design is too complicated to explain here,
+ but please read the code in spi_pack.vhd, it has comments that explain
+ many aspects of the unit's operation.
+
+ Enjoy!
+
+ John Clayton
+ September 6th, 2013.
+
\ No newline at end of file
Index: wishbone_spi_flash_interface/trunk/spi_flash_sim.txt
===================================================================
--- wishbone_spi_flash_interface/trunk/spi_flash_sim.txt (nonexistent)
+++ wishbone_spi_flash_interface/trunk/spi_flash_sim.txt (revision 2)
@@ -0,0 +1,256 @@
+0D ; Carriage return for autobaud
+0A ;
+77 ; Write MAC address
+20 ;
+46 ;
+36 ;
+20 ;
+30 ;
+30 ;
+20 ;
+30 ;
+44 ;
+20 ;
+39 ;
+39 ;
+20 ;
+30 ;
+31 ;
+20 ;
+30 ;
+30 ;
+20 ;
+30 ;
+34 ;
+0D ;
+0A ;
+77 ; Wrote IP Address
+20 ;
+46 ;
+43 ;
+20 ;
+41 ;
+43 ;
+20 ;
+31 ;
+37 ;
+20 ;
+35 ;
+30 ;
+20 ;
+31 ;
+41 ;
+0D ;
+0A ;
+23 ; Comment
+20 ;
+53 ;
+65 ;
+6C ;
+66 ;
+20 ;
+4D ;
+41 ;
+43 ;
+20 ;
+26 ;
+20 ;
+49 ;
+50 ;
+20 ;
+41 ;
+64 ;
+64 ;
+72 ;
+65 ;
+73 ;
+73 ;
+65 ;
+73 ;
+20 ;
+69 ;
+6E ;
+69 ;
+74 ;
+69 ;
+61 ;
+6C ;
+69 ;
+7A ;
+65 ;
+64 ;
+2E ;
+0D ;
+0A ;
+00 ;
+00 ;
+00 ;
+00 ;
+06 ;
+07 ;
+08 ;
+09 ;
+0A ;
+0B ;
+0C ;
+0D ;
+0E ;
+0F ;
+00 ;
+01 ;
+02 ;
+03 ;
+04 ;
+05 ;
+06 ;
+07 ;
+08 ;
+09 ;
+0A ;
+0B ;
+0C ;
+0D ;
+0E ;
+0F ;
+00 ;
+01 ;
+02 ;
+03 ;
+04 ;
+05 ;
+06 ;
+07 ;
+08 ;
+09 ;
+0A ;
+0B ;
+0C ;
+0D ;
+0E ;
+0F ;
+00 ;
+01 ;
+02 ;
+03 ;
+04 ;
+05 ;
+06 ;
+07 ;
+08 ;
+09 ;
+0A ;
+0B ;
+0C ;
+0D ;
+0E ;
+0F ;
+00 ;
+01 ;
+02 ;
+03 ;
+04 ;
+05 ;
+06 ;
+07 ;
+08 ;
+09 ;
+0A ;
+0B ;
+0C ;
+0D ;
+0E ;
+0F ;
+00 ;
+01 ;
+02 ;
+03 ;
+04 ;
+05 ;
+06 ;
+07 ;
+08 ;
+09 ;
+0A ;
+0B ;
+0C ;
+0D ;
+0E ;
+0F ;
+00 ;
+01 ;
+02 ;
+03 ;
+04 ;
+05 ;
+06 ;
+07 ;
+08 ;
+09 ;
+0A ;
+0B ;
+0C ;
+0D ;
+0E ;
+0F ;
+00 ;
+01 ;
+02 ;
+03 ;
+04 ;
+05 ;
+06 ;
+07 ;
+08 ;
+09 ;
+0A ;
+0B ;
+0C ;
+0D ;
+0E ;
+0F ;
+00 ;
+01 ;
+02 ;
+03 ;
+04 ;
+05 ;
+06 ;
+07 ;
+08 ;
+09 ;
+0A ;
+0B ;
+0C ;
+0D ;
+0E ;
+0F ;
+00 ;
+01 ;
+02 ;
+03 ;
+04 ;
+05 ;
+06 ;
+07 ;
+08 ;
+09 ;
+0A ;
+0B ;
+0C ;
+0D ;
+0E ;
+0F ;
+00 ;
+01 ;
+02 ;
+03 ;
+04 ;
+05 ;
+06 ;
+07 ;
+08 ;
+09 ;
+0A ;
+0B ;
+0C ;
+0D ;