URL
https://opencores.org/ocsvn/pmodsf3driver/pmodsf3driver/trunk
Subversion Repositories pmodsf3driver
[/] [pmodsf3driver/] [trunk/] [hw/] [sources/] [PmodSF3SPIController.vhd] - Rev 2
Compare with Previous | Blame | View Log
------------------------------------------------------------------------ -- Engineer: Dalmasso Loic -- Create Date: 19/02/2025 -- Module Name: PmodSF3SPIController -- Description: -- Pmod SF3 SPI Controller for the 32 MB NOR Flash Memory MT25QL256ABA. -- Supports Single, Dual and Quad SPI Modes: -- | i_spi_dual_enable | i_spi_single_enable | SPI Mode -- | 0 | 1 | Single -- | 1 | 1 | Single -- | 1 | 0 | Dual -- | 0 | 1 | Quad -- -- The 'o_ready' signal indicates this module is ready to start new SPI transmission. -- The 'i_start' signal starts the SPI communication, according to the mode (Read or Write memory), command/address/data bytes. -- In Write operation, when the 'o_next_data_w' is set to '1', the MSB of the 'i_data_w' is loaded. -- In Read operation, when the 'o_data_ready', data from memory is available in 'o_data_r' signal. -- -- Ports -- Input - i_sys_clock: System Input Clock -- Input - i_sys_clock_en: System Input Clock Enable -- Input - i_reset: System Input Reset ('0': No Reset, '1': Reset) -- Input - i_start: Start SPI Transmission ('0': No Start, '1': Start) -- Input - i_spi_single_enable: Enable SPI Single Mode ('0': Disable, '1': Enable) -- Input - i_spi_dual_enable: Enable SPI Dual Mode ('0': Disable, '1': Enable) -- Input - i_mode: Set Memory Operation Mode ('0': Write, '1': Mode) -- Input - i_command: Command Byte -- Input - i_addr_bytes: Number of Address Bytes to use (0 to 3 bytes) -- Input - i_addr: Address Bytes -- Input - i_dummy_cycles: Number of Dummy Cycles (0 to 14 cycles) -- Input - i_data_bytes: Number of Data Bytes to write -- Input - i_data_w: Data Bytes to write -- Output - o_next_data_w: Next bit of Data Bytes trigger ('0': Disable, '1': Enable) -- Output - o_data_r: Data Bytes read from Memory -- Output - o_data_ready: Data Bytes read from Memory Ready ('0': NOT Ready, '1': Ready) -- Output - o_ready: System Ready for transmission -- Output - o_reset: Memory Reset ('0': Reset, '1': No Reset) -- Output - o_sclk: SPI Serial Clock -- In/Out - io_dq: SPI Serial Data -- Output - o_ss: SPI Slave Select Line ('0': Enable, '1': Disable) ------------------------------------------------------------------------ LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; USE IEEE.NUMERIC_STD.ALL; ENTITY PmodSF3SPIController is PORT( -- Module Control i_sys_clock: IN STD_LOGIC; i_sys_clock_en: IN STD_LOGIC; i_reset: IN STD_LOGIC; i_start: IN STD_LOGIC; -- SPI Mode Config (Single, Dual or Quad) i_spi_single_enable: IN STD_LOGIC; i_spi_dual_enable: IN STD_LOGIC; -- Memory Command/Addr/Data i_mode: IN STD_LOGIC; i_command: IN UNSIGNED(7 downto 0); i_addr_bytes: IN INTEGER range 0 to 3; i_addr: IN UNSIGNED(23 downto 0); i_dummy_cycles: IN INTEGER range 0 to 14; i_data_bytes: IN INTEGER; i_data_w: IN UNSIGNED(7 downto 0); o_next_data_w: OUT STD_LOGIC; o_data_r: OUT UNSIGNED(7 downto 0); o_data_ready: OUT STD_LOGIC; -- Module Outputs o_ready: OUT STD_LOGIC; o_reset: OUT STD_LOGIC; o_sclk: OUT STD_LOGIC; io_dq: INOUT STD_LOGIC_VECTOR(3 downto 0); o_ss: OUT STD_LOGIC ); END PmodSF3SPIController; ARCHITECTURE Behavioral of PmodSF3SPIController is ------------------------------------------------------------------------ -- Constant Declarations ------------------------------------------------------------------------ -- SPI Write Register Length: Command (1 Byte) + Address (3 Bytes) + Data (1 Byte) constant SPI_WRITE_REGISTER_LENGTH: INTEGER := 40; -- SPI Write Register Indexes constant SPI_WRITE_REGISTER_CMD_MSB: INTEGER := SPI_WRITE_REGISTER_LENGTH-1; constant SPI_WRITE_REGISTER_CMD_LSB: INTEGER := SPI_WRITE_REGISTER_LENGTH-8; constant SPI_WRITE_REGISTER_ADDR_DATA_MSB: INTEGER := SPI_WRITE_REGISTER_LENGTH-9; -- SPI Empty Write Bits constant EMPTY_WRITE_BITS: UNSIGNED(SPI_WRITE_REGISTER_ADDR_DATA_MSB-8 downto 0) := (others => '0'); -- SPI SCLK IDLE Bit constant SPI_SCLK_IDLE: STD_LOGIC := '1'; -- SPI DQ IDLE Bit constant SPI_DQ_IDLE: STD_LOGIC := 'Z'; -- SPI Disable Slave Select Bit constant DISABLE_SS_BIT: STD_LOGIC := '1'; -- SPI Bit Counter Increment constant SPI_BIT_COUNTER_INCREMENT_1: UNSIGNED(2 downto 0) := "001"; constant SPI_BIT_COUNTER_INCREMENT_2: UNSIGNED(2 downto 0) := "010"; constant SPI_BIT_COUNTER_INCREMENT_4: UNSIGNED(2 downto 0) := "100"; -- SPI Bit Counter Ends constant SPI_SINGLE_BIT_COUNTER_END: UNSIGNED(2 downto 0) := "111"; constant SPI_DUAL_BIT_COUNTER_END: UNSIGNED(2 downto 0) := "110"; constant SPI_QUAD_BIT_COUNTER_END: UNSIGNED(2 downto 0) := "100"; -- Memory Read Mode constant MEM_READ_MODE: STD_LOGIC := '1'; constant MEM_WRITE_MODE: STD_LOGIC := '0'; ------------------------------------------------------------------------ -- Signal Declarations ------------------------------------------------------------------------ -- SPI Controller States TYPE spiState is (IDLE, WRITE_CMD, WRITE_ADDR, DUMMY_CYCLES, BYTES_TXRX, STOP_TX); signal state: spiState := IDLE; signal next_state: spiState; -- SPI Modes signal spi_single_enable_reg: STD_LOGIC := '0'; signal spi_dual_enable_reg: STD_LOGIC := '0'; -- Memory Operation Mode signal mem_mode_reg: STD_LOGIC := '0'; -- Address & Data Byte Number signal addr_bytes_reg: INTEGER range 0 to 3 := 0; signal data_bytes_reg: INTEGER := 0; -- Data to Memory signal data_w_reg: UNSIGNED(SPI_WRITE_REGISTER_LENGTH-1 downto 0) := (others => '0'); -- Number of Dummy Cycles signal dummy_cycles_reg: INTEGER range 0 to 14 := 0; -- Data from Memory signal data_r_reg: UNSIGNED(7 downto 0) := (others => '0'); signal data_r_ready_reg: STD_LOGIC := '0'; -- SPI Transmission Bit Counter signal bit_counter: UNSIGNED(2 downto 0) := (others => '0'); signal bit_counter_increment: UNSIGNED(2 downto 0) := (others => '0'); signal bit_counter_end: STD_LOGIC := '0'; -- SPI SCLK signal sclk_reg: STD_LOGIC := '0'; signal sclk_edge_reg0: STD_LOGIC := '0'; signal sclk_edge_reg1: STD_LOGIC := '0'; signal sclk_rising_edge: STD_LOGIC := '0'; signal sclk_falling_edge: STD_LOGIC := '0'; ------------------------------------------------------------------------ -- Module Implementation ------------------------------------------------------------------------ begin ----------------------- -- SPI State Machine -- ----------------------- -- SPI State process(i_sys_clock) begin if rising_edge(i_sys_clock) then -- Reset if (i_reset = '1') then state <= IDLE; -- Clock Enable elsif (i_sys_clock_en = '1') then state <= next_state; end if; end if; end process; -- SPI Next State process(state, i_start, bit_counter_end, mem_mode_reg, addr_bytes_reg, dummy_cycles_reg, data_bytes_reg) begin case state is when IDLE => if (i_start = '1') then next_state <= WRITE_CMD; else next_state <= IDLE; end if; -- Write Command Byte when WRITE_CMD => -- End of Write Command Byte if (bit_counter_end = '1') then -- Address Bytes if (addr_bytes_reg = 0) then next_state <= BYTES_TXRX; else next_state <= WRITE_ADDR; end if; -- Continue Write Command Byte else next_state <= WRITE_CMD; end if; -- Write Address Bytes when WRITE_ADDR => -- End of Write Address Bytes if (bit_counter_end = '1') and (addr_bytes_reg <= 1) then -- Dummy Cycles if (mem_mode_reg = MEM_READ_MODE) and (dummy_cycles_reg /= 0) then next_state <= DUMMY_CYCLES; else next_state <= BYTES_TXRX; end if; -- Continue Write Address Bytes else next_state <= WRITE_ADDR; end if; -- Dummy Cycles when DUMMY_CYCLES => -- End of Dummy Cycles if (dummy_cycles_reg = 0) then next_state <= BYTES_TXRX; -- Continue Waiting Dummy Cycles else next_state <= DUMMY_CYCLES; end if; -- Read/Write Data Bytes when BYTES_TXRX => -- End of Write Data Byte if (bit_counter_end = '1') and (data_bytes_reg <= 1) then next_state <= STOP_TX; else next_state <= BYTES_TXRX; end if; -- End of Transmission when others => next_state <= IDLE; end case; end process; --------------------- -- SPI SCLK Output -- --------------------- process(i_sys_clock) begin if rising_edge(i_sys_clock) then -- Clock Enable if (i_sys_clock_en = '1') then -- Reset SCLK if (state = IDLE) or (state = STOP_TX) then sclk_reg <= SPI_SCLK_IDLE; -- Generate SCLK else sclk_reg <= not(sclk_reg); end if; end if; end if; end process; o_sclk <= sclk_reg; ----------------------------- -- SPI SCLK Edge Detection -- ----------------------------- process(i_sys_clock) begin if rising_edge(i_sys_clock) then -- Clock Enable if (i_sys_clock_en = '1') then -- SCLK Edge Detection sclk_edge_reg0 <= sclk_reg; sclk_edge_reg1 <= sclk_edge_reg0; end if; end if; end process; sclk_rising_edge <= sclk_edge_reg0 and not(sclk_edge_reg1); sclk_falling_edge <= not(sclk_edge_reg0) and sclk_edge_reg1; ---------------------- -- SPI Mode Handler -- ---------------------- process(i_sys_clock) begin if rising_edge(i_sys_clock) then -- Clock Enable if (i_sys_clock_en = '1') then -- Load SPI Mode Inputs if (state = IDLE) then spi_single_enable_reg <= i_spi_single_enable; spi_dual_enable_reg <= i_spi_dual_enable; end if; end if; end if; end process; ------------------------- -- Memory Mode Handler -- ------------------------- process(i_sys_clock) begin if rising_edge(i_sys_clock) then -- Clock Enable if (i_sys_clock_en = '1') then -- Load Memory Mode Input if (state = IDLE) then mem_mode_reg <= i_mode; end if; end if; end if; end process; ------------------------------------- -- Command, Address & Data Handler -- ------------------------------------- process(i_sys_clock) begin if rising_edge(i_sys_clock) then -- Clock Enable if (i_sys_clock_en = '1') then -- Load Inputs if (state = IDLE) then -- Command Byte data_w_reg(SPI_WRITE_REGISTER_CMD_MSB downto SPI_WRITE_REGISTER_CMD_LSB) <= i_command; -- Address & Data Bytes if (i_addr_bytes /= 0) then data_w_reg(SPI_WRITE_REGISTER_ADDR_DATA_MSB downto 0) <= i_addr & i_data_w; -- Data Byte only else data_w_reg(SPI_WRITE_REGISTER_ADDR_DATA_MSB downto 0) <= i_data_w & EMPTY_WRITE_BITS; end if; -- Handle Next Data to Write on SCLK Falling Edge (Left-Shift Data Write Register & new Data Input) elsif (sclk_falling_edge = '1') then -- Write Command, Address and Data (in Write Mode only) if (state = WRITE_CMD) or ((state = WRITE_ADDR) and (addr_bytes_reg /= 0)) or ((state = BYTES_TXRX) and (data_bytes_reg /= 0) and (mem_mode_reg = MEM_WRITE_MODE)) then -- Single SPI Mode: DQ0 if (spi_single_enable_reg = '1') then data_w_reg <= data_w_reg(SPI_WRITE_REGISTER_LENGTH-2 downto 0) & i_data_w(7); -- Dual SPI Mode: DQ[1:0] elsif (spi_dual_enable_reg = '1') then data_w_reg <= data_w_reg(SPI_WRITE_REGISTER_LENGTH-3 downto 0) & i_data_w(7 downto 6); -- Quad SPI Mode: DQ[3:0] else data_w_reg <= data_w_reg(SPI_WRITE_REGISTER_LENGTH-5 downto 0) & i_data_w(7 downto 4); end if; end if; end if; end if; end if; end process; ----------------------------- -- Next Write Data Trigger -- ----------------------------- o_next_data_w <= '1' when (mem_mode_reg = MEM_WRITE_MODE) and (sclk_falling_edge = '1') and ((state = WRITE_CMD) or (state = WRITE_ADDR) or (state = BYTES_TXRX)) else '0'; --------------------------- -- Address Bytes Handler -- ---------------------------- process(i_sys_clock) begin if rising_edge(i_sys_clock) then -- Clock Enable if (i_sys_clock_en = '1') then -- Load Address Bytes Input if (state = IDLE) then addr_bytes_reg <= i_addr_bytes; -- Address Bytes State elsif (state = WRITE_ADDR) and (bit_counter_end = '1') then -- Decrement Address Bytes addr_bytes_reg <= addr_bytes_reg -1; end if; end if; end if; end process; -------------------------- -- Dummy Cycles Handler -- -------------------------- process(i_sys_clock) begin if rising_edge(i_sys_clock) then -- Clock Enable if (i_sys_clock_en = '1') then -- Load Dummy Cycles Input if (state = IDLE) then dummy_cycles_reg <= i_dummy_cycles; -- Dummy Cycles State (only at SCLK Rising Edge) elsif (state = DUMMY_CYCLES) and (sclk_rising_edge = '1') then -- Decrement Dummy Cycles dummy_cycles_reg <= dummy_cycles_reg -1; end if; end if; end if; end process; ------------------------ -- Data Bytes Handler -- ------------------------ process(i_sys_clock) begin if rising_edge(i_sys_clock) then -- Clock Enable if (i_sys_clock_en = '1') then -- Load Data Bytes Input if (state = IDLE) then data_bytes_reg <= i_data_bytes; -- Data Bytes State elsif (state = BYTES_TXRX) and (bit_counter_end = '1') then -- Decrement Data Bytes data_bytes_reg <= data_bytes_reg -1; end if; end if; end if; end process; --------------------- -- SPI Bit Counter -- --------------------- process(i_sys_clock) begin if rising_edge(i_sys_clock) then -- Clock Enable if (i_sys_clock_en = '1') then -- Bit Counter Increment if (state = IDLE) then -- Single SPI Mode if (spi_single_enable_reg = '1') then bit_counter_increment <= SPI_BIT_COUNTER_INCREMENT_1; -- Dual SPI Mode elsif (spi_dual_enable_reg = '1') then bit_counter_increment <= SPI_BIT_COUNTER_INCREMENT_2; -- Quad SPI Mode else bit_counter_increment <= SPI_BIT_COUNTER_INCREMENT_4; end if; end if; -- Reset Bit Counter if (state = IDLE) or (state = DUMMY_CYCLES) then bit_counter <= (others => '0'); -- Increment Bit Counter (only at SCLK Falling Edge) elsif (sclk_falling_edge = '1') then bit_counter <= bit_counter +bit_counter_increment; end if; end if; end if; end process; -- Bit Counter End process(i_sys_clock) begin if rising_edge(i_sys_clock) then -- Clock Enable if (i_sys_clock_en = '1') then -- Bit Counter End Trigger (only at SCLK Rising Edge) if (sclk_rising_edge = '1') then -- Single SPI Mode if (spi_single_enable_reg = '1') and (bit_counter = SPI_SINGLE_BIT_COUNTER_END) then bit_counter_end <= '1'; -- Dual SPI Mode elsif (spi_dual_enable_reg = '1') and (bit_counter = SPI_DUAL_BIT_COUNTER_END) then bit_counter_end <= '1'; -- Quad SPI Mode elsif (spi_single_enable_reg = '0') and (spi_dual_enable_reg = '0') and (bit_counter = SPI_QUAD_BIT_COUNTER_END) then bit_counter_end <= '1'; end if; -- Reset Bit Counter End else bit_counter_end <= '0'; end if; end if; end if; end process; --------------------------- -- SPI Write Data (MOSI) -- --------------------------- process(state, mem_mode_reg, spi_single_enable_reg, spi_dual_enable_reg, data_w_reg) begin if (state = WRITE_CMD) or (state = WRITE_ADDR) or ((state = BYTES_TXRX) and (mem_mode_reg = MEM_WRITE_MODE)) then -- Single SPI Mode: DQ0 if (spi_single_enable_reg = '1') then io_dq(3) <= SPI_DQ_IDLE; io_dq(2) <= SPI_DQ_IDLE; io_dq(1) <= SPI_DQ_IDLE; io_dq(0) <= data_w_reg(SPI_WRITE_REGISTER_LENGTH-1); -- Dual SPI Mode: DQ[1:0] elsif (spi_dual_enable_reg = '1') then io_dq(3) <= SPI_DQ_IDLE; io_dq(2) <= SPI_DQ_IDLE; io_dq(1) <= data_w_reg(SPI_WRITE_REGISTER_LENGTH-1); io_dq(0) <= data_w_reg(SPI_WRITE_REGISTER_LENGTH-2); -- Quad SPI Mode: DQ[3:0] else io_dq(3) <= data_w_reg(SPI_WRITE_REGISTER_LENGTH-1); io_dq(2) <= data_w_reg(SPI_WRITE_REGISTER_LENGTH-2); io_dq(1) <= data_w_reg(SPI_WRITE_REGISTER_LENGTH-3); io_dq(0) <= data_w_reg(SPI_WRITE_REGISTER_LENGTH-4); end if; -- IDLE else io_dq <= (others => SPI_DQ_IDLE); end if; end process; -------------------------- -- SPI Read Data (MISO) -- -------------------------- process(i_sys_clock) begin if rising_edge(i_sys_clock) then -- Sampling Read Data Enable if (i_sys_clock_en = '1') then -- Next Data to Write on SCLK Rising Edge if (sclk_rising_edge = '1') and (mem_mode_reg = MEM_READ_MODE) and (state = BYTES_TXRX) then -- Check Last Byte Cycle if (data_bytes_reg /= 1) or (bit_counter_end = '0') then -- Single SPI Mode: DQ1 if (spi_single_enable_reg = '1') then data_r_reg(7 downto 1) <= data_r_reg(6 downto 0); data_r_reg(0) <= io_dq(1); -- Dual SPI Mode: DQ[1:0] elsif (spi_dual_enable_reg = '1') then data_r_reg(7 downto 2) <= data_r_reg(5 downto 0); data_r_reg(1) <= io_dq(1); data_r_reg(0) <= io_dq(0); -- Quad SPI Mode: DQ[3:0] else data_r_reg(7 downto 4) <= data_r_reg(3 downto 0); data_r_reg(3) <= io_dq(3); data_r_reg(2) <= io_dq(2); data_r_reg(1) <= io_dq(1); data_r_reg(0) <= io_dq(0); end if; end if; end if; end if; end if; end process; o_data_r <= data_r_reg; ------------------------- -- SPI Read Data Valid -- ------------------------- process(i_sys_clock) begin if rising_edge(i_sys_clock) then -- Clock Enable if (i_sys_clock_en = '1') then -- Data Read Ready if (mem_mode_reg = MEM_READ_MODE) and (state = BYTES_TXRX) and (bit_counter_end = '1') then data_r_ready_reg <= '1'; -- Data Read NOT Ready else data_r_ready_reg <= '0'; end if; end if; end if; end process; o_data_ready <= data_r_ready_reg; ---------------------- -- SPI Ready Status -- ---------------------- o_ready <= '1' when state = IDLE else '0'; ------------------ -- Reset Output -- ------------------ o_reset <= not(i_reset); --------------------------- -- SPI Slave Select Line -- --------------------------- o_ss <= DISABLE_SS_BIT when (state = IDLE) or (state = STOP_TX) else not(DISABLE_SS_BIT); end Behavioral;