From: Raptor Engineering Development Team Date: Wed, 23 Feb 2022 00:35:12 +0000 (-0600) Subject: Add initial Tercel support for Arctic Tern X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=e04d79005b2d9da70dba49e54845c8fca2421ea6;p=microwatt.git Add initial Tercel support for Arctic Tern --- diff --git a/LICENSE.tercel b/LICENSE.tercel new file mode 100644 index 0000000..d645c69 --- /dev/null +++ b/LICENSE.tercel @@ -0,0 +1 @@ +Tercel is licensed under the terms of the GPU LGPLv3 diff --git a/Makefile b/Makefile index 9eeeee0..4623300 100644 --- a/Makefile +++ b/Makefile @@ -186,6 +186,8 @@ soc_files += litedram/extras/litedram-wrapper-l2.vhdl \ litedram/generated/rcs-arctic-tern-bmc-card/litedram-initmem.vhdl soc_extra_v += litedram/generated/rcs-arctic-tern-bmc-card/litedram_core.v soc_extra_v += liteeth/generated/rcs-arctic-tern-bmc-card/liteeth_core.v +soc_extra_v += tercel/phy.v +soc_extra_v += tercel/wishbone_spi_master.v endif GHDL_IMAGE_GENERICS=-gMEMORY_SIZE=$(MEMORY_SIZE) -gRAM_INIT_FILE=$(RAM_INIT_FILE) \ diff --git a/README.tercel.md b/README.tercel.md new file mode 100644 index 0000000..874f352 --- /dev/null +++ b/README.tercel.md @@ -0,0 +1,172 @@ +# ARCHITECTURE + +Tercel is a Wishbone-compatible, 32-bit, single+quad SPI Flash controller with XIP support. Both 3BA and 4BA devices are supported. + +Tercel provides two interfaces to the host CPU: +1. A direct MMIO read/write region for the flash device +2. A configuration space where the core can be reconfigured on-line for operation beyond single SPI 3BA mode with fallback clocks (default) + +# USAGE + +## General Usage + +On reset, the Tercel core provides read-only access in single SPI, 3BA mode to any attached Flash device. It uses the generic well-known single byte access instructions to provide full XIP support. Host software is responsible for reading the Flash ID of the attached Flash device and reconfiguring the Tercel core for faster and more advanced operating modes. This reconfiguration can take place online, with no interruption to the concurrent read operations in progress on the main MMIO Flash window. + +By default, with Microwatt, the two bus regions are available at: +Flash MMIO (XIP base): 0xf0000000 +Core configuration: 0xc8050000 + +## Read Flash ID + + - Enable user mode + Set bit 0 of "Core control register 1" + + - Send Flash ID command + Write 0x9e to SPI MMIO base address (offset 0x0) + + - Read response + Read four bytes from SPI MMIO base address (offset 0x0) and assemble into 32-bit device ID + Read sequence is big endian per Flash device convention + + - Disable user mode + Clear bit 0 of "Core control register 1" + +# REGISTER MAP + +## [0x00 - 0x07) Device ID + + Device make/model unique identifier for PnP functionality + Fixed value: 0x7c5250545350494d + +## [0x08 - 0x0b) Device version + + Device revision (stepping) + + | Bits | Description | + |-------|---------------| + | 31:16 | Major version | + | 15:8 | Minor version | + | 7:0 | Patch level | + +## [0x0c - 0x0f) + + System clock frequency + + Can be used to set divisor to meet specific SPI Flash clock frequency requirements + +## [0x10 - 0x13) + + PHY configuration register 1 + Default: 0x00000a10 + + | Bits | Description | + |-------|--------------------------------------------------------------------------------------------------------| + | 31:24 | Insert idle cycles with CS deasserted between SPI operations (cycle count to insert, 0 for none) | + | 23:22 | Reserved | + | 21 | Enable quad I/O data write in QSPI mode | + | 20 | Enable quad I/O data read in QSPI mode | + | 19 | Enable fast reads (1 == use fast read commands and cycles, 0 == use standard read commands and cycles) | + | 18 | Enable 4BA addressing mode (1 == 4BA, 0 == 3BA) | + | 17:16 | PHY I/O type (0 == single, 2 == quad, others invalid) | + | 15:8 | Dummy cycle count | + | 7:0 | SPI clock divisor | + + Clock divisor works as follows: + Clock frequency calculation: + spi_clock_frequency = peripheral_bus_clock_frequency / ((spi_clock_divisor - 1) * 2) + + | Clock divisor value | Actual division | + |---------------------|------------------------------------| + | 0 | override to standard divide by two | + | 1 | divide by 1 | + | 2 | divide by 2 | + | 3 | divide by 4 | + | 4 | divide by 6 | + | 5 | divide by 8 | + | 6 | divide by 10 | + | 7 | divide by 12 | + | ... | ... | + +## [0x14 - 0x17) + + Flash configuration register 1 + Default: 0x13031303 + + | Bits | Description | + |-------|-----------------------| + | 31:24 | Reserved | + | 23:16 | QSPI 3BA read command | + | 15:8 | SPI 4BA read command | + | 7:0 | SPI 3BA read command | + +## [0x18 - 0x1b) + + Flash configuration register 2 + Default: 0xeceb0c0b + + | Bits | Description | + |-------|----------------------------| + | 31:24 | QSPI 4BA fast read command | + | 23:16 | QSPI 3BA fast read command | + | 15:8 | SPI 4BA fast read command | + | 7:0 | SPI 3BA fast read command | + +## [0x1c - 0x1f) + + Flash configuration register 3 + Default: 0x34321202 + + | Bits | Description | + |-------|--------------------------| + | 31:24 | QSPI 4BA program command | + | 23:16 | QSPI 3BA program command | + | 15:8 | SPI 4BA program command | + | 7:0 | SPI 3BA program command | + +## [0x20 - 0x23) + + Flash configuration register 4 + Default: 0x00000000 + + Cycles to keep CS asserted after operation completion. Used to support high-throughput multi-cycle transfers with specific Flash devices. + + See also "Flash configuration register 5" + +## [0x24 - 0x27) + + Flash configuration register 5 + Default: 0x00000000 + + | Bits | Description | + |------|-------------------------| + | 31:2 | Reserved | + | 1 | Allow multicycle writes | + | 0 | Allow multicycle reads | + +## [0x28 - 0x2b) + + Core control register 1 + Default: 0x00000000 + + | Bits | Description | | + |------|-------------|--------------------------| + | | 31:1 | Reserved | + | | 0 | User command mode enable | + + User command mode operates in conjunction with "Core data register 1" to support custom (non-data-I/O) SPI commands. + +## [0x2c - 0x2f) + + Core data register 1 + + Data transfer to/from SPI device in user command mode + + See also "Core control register 1" + +# LICENSE + +Tercel is licensed under the terms of the GNU LGPLv3. See LICENSE.tercel for details. + +# DOCUMENTATION CREDITS + +(c) 2022 Raptor Engineering, LLC diff --git a/fpga/top-rcs-arctic-tern-bmc-card.vhdl b/fpga/top-rcs-arctic-tern-bmc-card.vhdl index 919c8d7..f441cac 100644 --- a/fpga/top-rcs-arctic-tern-bmc-card.vhdl +++ b/fpga/top-rcs-arctic-tern-bmc-card.vhdl @@ -22,6 +22,7 @@ entity toplevel is SPI_FLASH_DEF_QUAD : boolean := true; LOG_LENGTH : natural := 0; USE_LITEETH : boolean := true; + USE_TERCEL : boolean := true; UART_IS_16550 : boolean := true; HAS_UART1 : boolean := false; ICACHE_NUM_LINES : natural := 64 @@ -87,6 +88,7 @@ architecture behaviour of toplevel is signal wb_ext_is_dram_csr : std_ulogic; signal wb_ext_is_dram_init : std_ulogic; signal wb_ext_is_eth : std_ulogic; + signal wb_ext_is_tercel : std_ulogic; -- DRAM main data wishbone connection signal wb_dram_in : wishbone_master_out; @@ -99,6 +101,9 @@ architecture behaviour of toplevel is signal ext_irq_eth : std_ulogic; signal wb_eth_out : wb_io_slave_out := wb_io_slave_out_init; + -- Tercel connection + signal wb_tercel_out : wb_io_slave_out := wb_io_slave_out_init; + -- Control/status signal core_alt_reset : std_ulogic; @@ -109,6 +114,10 @@ architecture behaviour of toplevel is signal spi_sdat_oe : std_ulogic_vector(3 downto 0); signal spi_sdat_i : std_ulogic_vector(3 downto 0); + -- SPI main data wishbone connection + signal wb_spiflash_in : wb_io_master_out; + signal wb_spiflash_out : wb_io_slave_out; + -- Fixup various memory sizes based on generics function get_bram_size return natural is begin @@ -173,13 +182,6 @@ begin uart0_txd => uart0_txd, uart0_rxd => uart0_rxd, - -- SPI signals - spi_flash_sck => spi_sck, - spi_flash_cs_n => spi_cs_n, - spi_flash_sdat_o => spi_sdat_o, - spi_flash_sdat_oe => spi_sdat_oe, - spi_flash_sdat_i => spi_sdat_i, - -- DRAM wishbone wb_dram_in => wb_dram_in, wb_dram_out => wb_dram_out, @@ -192,28 +194,15 @@ begin wb_ext_io_out => wb_ext_io_out, wb_ext_is_dram_csr => wb_ext_is_dram_csr, wb_ext_is_dram_init => wb_ext_is_dram_init, - wb_ext_is_eth => wb_ext_is_eth, + wb_ext_is_eth => wb_ext_is_eth, + wb_ext_is_tercel => wb_ext_is_tercel, + + wb_spiflash_in => wb_spiflash_in, + wb_spiflash_out => wb_spiflash_out, alt_reset => core_alt_reset ); - -- SPI Flash - -- - spi_flash_cs_n <= spi_cs_n; - spi_flash_mosi <= spi_sdat_o(0) when spi_sdat_oe(0) = '1' else 'Z'; - spi_flash_miso <= spi_sdat_o(1) when spi_sdat_oe(1) = '1' else 'Z'; - spi_flash_wp_n <= spi_sdat_o(2) when spi_sdat_oe(2) = '1' else 'Z'; - spi_flash_hold_n <= spi_sdat_o(3) when spi_sdat_oe(3) = '1' else 'Z'; - spi_sdat_i(0) <= spi_flash_mosi; - spi_sdat_i(1) <= spi_flash_miso; - spi_sdat_i(2) <= spi_flash_wp_n; - spi_sdat_i(3) <= spi_flash_hold_n; - - uclk: USRMCLK port map ( - USRMCLKI => spi_sck, - USRMCLKTS => '0' - ); - nodram: if not USE_LITEDRAM generate signal ddram_clk_dummy : std_ulogic; begin @@ -406,8 +395,107 @@ begin ext_irq_eth <= '0'; end generate; + -- SPI Flash + -- + spi_flash_cs_n <= spi_cs_n; + spi_flash_mosi <= spi_sdat_o(0) when spi_sdat_oe(0) = '1' else 'Z'; + spi_flash_miso <= spi_sdat_o(1) when spi_sdat_oe(1) = '1' else 'Z'; + spi_flash_wp_n <= spi_sdat_o(2) when spi_sdat_oe(2) = '1' else 'Z'; + spi_flash_hold_n <= spi_sdat_o(3) when spi_sdat_oe(3) = '1' else 'Z'; + spi_sdat_i(0) <= spi_flash_mosi; + spi_sdat_i(1) <= spi_flash_miso; + spi_sdat_i(2) <= spi_flash_wp_n; + spi_sdat_i(3) <= spi_flash_hold_n; + + uclk: USRMCLK port map ( + USRMCLKI => spi_sck, + USRMCLKTS => '0' + ); + + has_tercel : if USE_TERCEL generate + + component tercel_core port ( + sys_clk_freq : in std_ulogic_vector(31 downto 0); + + peripheral_clock : in std_ulogic; + peripheral_reset : in std_ulogic; + + spi_clock : out std_ulogic; + spi_d_out : out std_ulogic_vector(3 downto 0); + spi_d_direction : out std_ulogic_vector(3 downto 0); + spi_d_in : in std_ulogic_vector(3 downto 0); + spi_ss_n : out std_ulogic; + + wishbone_adr : in std_ulogic_vector(29 downto 0); + wishbone_dat_w : in std_ulogic_vector(31 downto 0); + wishbone_dat_r : out std_ulogic_vector(31 downto 0); + wishbone_sel : in std_ulogic_vector(3 downto 0); + wishbone_cyc : in std_ulogic; + wishbone_stb : in std_ulogic; + wishbone_ack : out std_ulogic; + wishbone_we : in std_ulogic; + wishbone_err : out std_ulogic; + + cfg_wishbone_adr : in std_ulogic_vector(29 downto 0); + cfg_wishbone_dat_w : in std_ulogic_vector(31 downto 0); + cfg_wishbone_dat_r : out std_ulogic_vector(31 downto 0); + cfg_wishbone_sel : in std_ulogic_vector(3 downto 0); + cfg_wishbone_cyc : in std_ulogic; + cfg_wishbone_stb : in std_ulogic; + cfg_wishbone_ack : out std_ulogic; + cfg_wishbone_we : in std_ulogic; + cfg_wishbone_err : out std_ulogic + ); + end component; + + signal wb_tercel_cyc : std_ulogic; + + begin + tercel : tercel_core + port map( + sys_clk_freq => std_logic_vector(to_unsigned(CLK_FREQUENCY, 32)), + + peripheral_clock => system_clk, + peripheral_reset => soc_rst, + + spi_clock => spi_sck, + spi_d_out => spi_sdat_o, + spi_d_direction => spi_sdat_oe, + spi_d_in => spi_sdat_i, + spi_ss_n => spi_cs_n, + + wishbone_adr => wb_spiflash_in.adr, + wishbone_dat_w => wb_spiflash_in.dat, + wishbone_dat_r => wb_spiflash_out.dat, + wishbone_sel => wb_spiflash_in.sel, + wishbone_cyc => wb_spiflash_in.cyc, + wishbone_stb => wb_spiflash_in.stb, + wishbone_ack => wb_spiflash_out.ack, + wishbone_we => wb_spiflash_in.we, + wishbone_err => open, + + cfg_wishbone_adr => wb_ext_io_in.adr, + cfg_wishbone_dat_w => wb_ext_io_in.dat, + cfg_wishbone_dat_r => wb_tercel_out.dat, + cfg_wishbone_sel => wb_ext_io_in.sel, + cfg_wishbone_cyc => wb_tercel_cyc, + cfg_wishbone_stb => wb_ext_io_in.stb, + cfg_wishbone_ack => wb_tercel_out.ack, + cfg_wishbone_we => wb_ext_io_in.we, + cfg_wishbone_err => open + ); + + -- Gate cyc with "chip select" from soc + wb_tercel_cyc <= wb_ext_io_in.cyc and wb_ext_is_tercel; + + -- Tercel isn't pipelined + wb_tercel_out.stall <= not wb_tercel_out.ack; + + end generate; + -- Mux WB response on the IO bus wb_ext_io_out <= wb_eth_out when wb_ext_is_eth = '1' else + wb_tercel_out when wb_ext_is_tercel = '1' else wb_dram_ctrl_out; end architecture behaviour; diff --git a/soc.vhdl b/soc.vhdl index d03f114..507a146 100644 --- a/soc.vhdl +++ b/soc.vhdl @@ -66,6 +66,7 @@ entity soc is SPI_BOOT_CLOCKS : boolean := true; LOG_LENGTH : natural := 512; HAS_LITEETH : boolean := false; + HAS_TERCEL : boolean := false; UART0_IS_16550 : boolean := true; HAS_UART1 : boolean := false; ICACHE_NUM_LINES : natural := 64; @@ -90,6 +91,7 @@ entity soc is wb_ext_is_dram_csr : out std_ulogic; wb_ext_is_dram_init : out std_ulogic; wb_ext_is_eth : out std_ulogic; + wb_ext_is_tercel : out std_ulogic; -- External interrupts ext_irq_eth : in std_ulogic := '0'; @@ -102,12 +104,9 @@ entity soc is uart1_txd : out std_ulogic; uart1_rxd : in std_ulogic := '0'; - -- SPI Flash signals - spi_flash_sck : out std_ulogic; - spi_flash_cs_n : out std_ulogic; - spi_flash_sdat_o : out std_ulogic_vector(SPI_FLASH_DLINES-1 downto 0); - spi_flash_sdat_oe : out std_ulogic_vector(SPI_FLASH_DLINES-1 downto 0); - spi_flash_sdat_i : in std_ulogic_vector(SPI_FLASH_DLINES-1 downto 0) := (others => '1'); + -- SPI Flash controller signals: + wb_spiflash_in : out wb_io_master_out; + wb_spiflash_out : in wb_io_slave_out := wb_io_slave_out_init; -- DRAM controller signals alt_reset : in std_ulogic := '0' @@ -160,12 +159,6 @@ architecture behaviour of soc is signal uart1_dat8 : std_ulogic_vector(7 downto 0); signal uart1_irq : std_ulogic; - -- SPI Flash controller signals: - signal wb_spiflash_in : wb_io_master_out; - signal wb_spiflash_out : wb_io_slave_out; - signal wb_spiflash_is_reg : std_ulogic; - signal wb_spiflash_is_map : std_ulogic; - -- XICS signals: signal wb_xics_icp_in : wb_io_master_out; signal wb_xics_icp_out : wb_io_slave_out; @@ -212,7 +205,6 @@ architecture behaviour of soc is SLAVE_IO_ICP, SLAVE_IO_ICS, SLAVE_IO_UART1, - SLAVE_IO_SPI_FLASH_REG, SLAVE_IO_SPI_FLASH_MAP, SLAVE_IO_EXTERNAL, SLAVE_IO_NONE); @@ -543,8 +535,6 @@ begin slave_io := SLAVE_IO_ICP; elsif std_match(match, x"C0005") then slave_io := SLAVE_IO_ICS; - elsif std_match(match, x"C0006") then - slave_io := SLAVE_IO_SPI_FLASH_REG; end if; slave_io_dbg <= slave_io; wb_uart0_in <= wb_sio_out; @@ -553,8 +543,6 @@ begin wb_uart1_in.cyc <= '0'; wb_spiflash_in <= wb_sio_out; wb_spiflash_in.cyc <= '0'; - wb_spiflash_is_reg <= '0'; - wb_spiflash_is_map <= '0'; -- Only give xics 8 bits of wb addr (for now...) wb_xics_icp_in <= wb_sio_out; @@ -602,6 +590,9 @@ begin elsif wb_sio_out.adr(23 downto 16) = x"03" and HAS_LITEETH then wb_ext_is_eth <= '1'; ext_valid := true; + elsif wb_sio_out.adr(21 downto 14) = x"05" and HAS_TERCEL then + wb_ext_is_tercel <= '1'; + ext_valid := true; end if; if ext_valid then wb_ext_io_in.cyc <= wb_sio_out.cyc; @@ -629,11 +620,6 @@ begin wb_spiflash_in.adr(29 downto 28) <= "00"; wb_spiflash_in.cyc <= wb_sio_out.cyc; wb_sio_in <= wb_spiflash_out; - wb_spiflash_is_map <= '1'; - when SLAVE_IO_SPI_FLASH_REG => - wb_spiflash_in.cyc <= wb_sio_out.cyc; - wb_sio_in <= wb_spiflash_out; - wb_spiflash_is_reg <= '1'; when others => end case; @@ -774,35 +760,6 @@ begin uart1_irq <= '0'; end generate; - spiflash_gen: if HAS_SPI_FLASH generate - spiflash: entity work.spi_flash_ctrl - generic map ( - DATA_LINES => SPI_FLASH_DLINES, - DEF_CLK_DIV => SPI_FLASH_DEF_CKDV, - DEF_QUAD_READ => SPI_FLASH_DEF_QUAD, - BOOT_CLOCKS => SPI_BOOT_CLOCKS - ) - port map( - rst => rst_spi, - clk => system_clk, - wb_in => wb_spiflash_in, - wb_out => wb_spiflash_out, - wb_sel_reg => wb_spiflash_is_reg, - wb_sel_map => wb_spiflash_is_map, - sck => spi_flash_sck, - cs_n => spi_flash_cs_n, - sdat_o => spi_flash_sdat_o, - sdat_oe => spi_flash_sdat_oe, - sdat_i => spi_flash_sdat_i - ); - end generate; - - no_spi0_gen: if not HAS_SPI_FLASH generate - wb_spiflash_out.dat <= (others => '1'); - wb_spiflash_out.ack <= wb_spiflash_in.cyc and wb_spiflash_in.stb; - wb_spiflash_out.stall <= wb_spiflash_in.cyc and not wb_spiflash_out.ack; - end generate; - xics_icp: entity work.xics_icp port map( clk => system_clk, diff --git a/tercel/phy.v b/tercel/phy.v new file mode 100644 index 0000000..57cbe2c --- /dev/null +++ b/tercel/phy.v @@ -0,0 +1,222 @@ +// © 2017 - 2021 Raptor Engineering, LLC +// +// Released under the terms of the GPL v3 +// See the LICENSE file for full details + +// SPI mode 3 transfer +// Single data rate, quad transfer +// This module assumes it is on the same clock domain as the external control logic +module spi_master_phy_quad( + input wire platform_clock, + input wire reset, + input wire [31:0] tx_data, + output reg [31:0] rx_data, + input wire [7:0] dummy_cycle_count, + input wire hold_ss_active, + input wire qspi_mode_active, + input wire qspi_transfer_mode, // 0 == byte transfer, 1 == word transfer + input wire qspi_transfer_direction, // 0 == read (input), 1 == write (output) + input wire cycle_start, + output reg transaction_complete, + + output reg spi_clock, + output reg spi_d0_out, + input wire spi_d0_in, + output reg spi_d1_out, + input wire spi_d1_in, + output reg spi_d2_out, + input wire spi_d2_in, + output reg spi_d3_out, + input wire spi_d3_in, + output reg spi_ss_n, + output reg spi_data_direction, // 0 == tristate (input), 1 == driven (output) + output reg spi_quad_mode_pin_enable + ); + + reg [3:0] transfer_state = 0; + reg [31:0] data_shift_out = 0; + + reg [7:0] state_iteration = 0; + reg ss_state_at_idle = 1'b1; + + reg [7:0] dummy_cycle_count_reg = 0; + reg [7:0] dummy_cycle_ctr = 0; + + reg qspi_transfer_mode_reg = 0; + reg [7:0] sspi_transfer_cycle_stop_value = 0; + reg [3:0] qspi_transfer_cycle_stop_value = 0; + + always @(posedge platform_clock) begin + if (reset) begin + transfer_state <= 0; + state_iteration <= 0; + transaction_complete <= 1; + dummy_cycle_count_reg <= 0; + spi_clock <= 1'b1; + spi_d0_out <= 1'b0; + spi_d1_out <= 1'b1; + spi_d2_out <= 1'b1; + spi_d3_out <= 1'b1; + spi_ss_n <= 1'b1; + ss_state_at_idle <= 1'b1; + spi_data_direction <= 1'b0; + spi_quad_mode_pin_enable <= 1'b0; + end else begin + case (transfer_state) + 0: begin + // Idle state + spi_clock <= 1'b1; + spi_d0_out <= 1'b0; + spi_ss_n <= ss_state_at_idle; + transaction_complete <= 0; + state_iteration <= 0; + if (cycle_start) begin + // Set up transfer + rx_data <= 0; + dummy_cycle_count_reg <= dummy_cycle_count; + data_shift_out <= tx_data; + spi_quad_mode_pin_enable <= qspi_mode_active; + spi_data_direction <= qspi_transfer_direction; + qspi_transfer_mode_reg <= qspi_transfer_mode; + if (qspi_transfer_mode == 0) begin + // Byte transfer (2 nibbles per word) + qspi_transfer_cycle_stop_value <= 1; + sspi_transfer_cycle_stop_value <= 7; + end else begin + // Word transfer (4 bytes / 8 nibbles per word) + qspi_transfer_cycle_stop_value <= 7; + sspi_transfer_cycle_stop_value <= 31; + end + + // Drive frame start + spi_clock <= 1'b1; + spi_ss_n <= 1'b0; + + transfer_state <= 1; + end else begin + if (!hold_ss_active) begin + ss_state_at_idle <= 1'b1; + end + spi_quad_mode_pin_enable <= 0; + transfer_state <= 0; + end + end + 1: begin + // Shift out TX byte / toggle clock + spi_clock <= 1'b0; + spi_ss_n <= 1'b0; + if (spi_quad_mode_pin_enable) begin + if (qspi_transfer_mode_reg) begin + spi_d3_out <= data_shift_out[31]; + spi_d2_out <= data_shift_out[30]; + spi_d1_out <= data_shift_out[29]; + spi_d0_out <= data_shift_out[28]; + end else begin + spi_d3_out <= data_shift_out[7]; + spi_d2_out <= data_shift_out[6]; + spi_d1_out <= data_shift_out[5]; + spi_d0_out <= data_shift_out[4]; + end + data_shift_out <= data_shift_out << 4; + end else begin + if (qspi_transfer_mode_reg) begin + spi_d0_out <= data_shift_out[31]; + end else begin + spi_d0_out <= data_shift_out[7]; + end + data_shift_out <= data_shift_out << 1; + end + transfer_state <= 2; + end + 2: begin + // Shift in RX byte / toggle clock + spi_clock <= 1'b1; + spi_ss_n <= 1'b0; + state_iteration <= state_iteration + 1; + if (spi_quad_mode_pin_enable) begin + if (qspi_transfer_mode_reg) begin + rx_data <= {rx_data[27:0], spi_d3_in, spi_d2_in, spi_d1_in, spi_d0_in}; + end else begin + rx_data <= {rx_data[3:0], spi_d3_in, spi_d2_in, spi_d1_in, spi_d0_in}; + end + if (state_iteration >= qspi_transfer_cycle_stop_value) begin + if (hold_ss_active) begin + ss_state_at_idle <= 1'b0; + end else begin + ss_state_at_idle <= 1'b1; + end + if (dummy_cycle_count_reg == 0) begin + transaction_complete <= 1; + transfer_state <= 3; + end else begin + dummy_cycle_ctr <= 0; + transfer_state <= 4; + end + end else begin + transfer_state <= 1; + end + end else begin + if (qspi_transfer_mode_reg) begin + rx_data <= {rx_data[30:0], spi_d1_in}; + end else begin + rx_data <= {rx_data[6:0], spi_d1_in}; + end + if (state_iteration >= sspi_transfer_cycle_stop_value) begin + if (hold_ss_active) begin + ss_state_at_idle <= 1'b0; + end else begin + ss_state_at_idle <= 1'b1; + end + transaction_complete <= 1; + if (dummy_cycle_count_reg == 0) begin + transfer_state <= 3; + end else begin + dummy_cycle_ctr <= 0; + transfer_state <= 4; + end + end else begin + transfer_state <= 1; + end + end + end + 3: begin + // Wait for host to deassert transaction request + if (!cycle_start) begin + transaction_complete <= 0; + transfer_state <= 0; + end + spi_clock <= 1'b1; + spi_d0_out <= 1'b0; + spi_d1_out <= 1'b1; + spi_d2_out <= 1'b1; + spi_d3_out <= 1'b1; + spi_ss_n <= ss_state_at_idle; + spi_data_direction <= 0; + spi_quad_mode_pin_enable <= 0; + end + 4: begin + // Increment counter / toggle clock + spi_clock <= 1'b0; + dummy_cycle_ctr <= dummy_cycle_ctr + 1; + + transfer_state <= 5; + end + 5: begin + if (dummy_cycle_ctr < dummy_cycle_count_reg) begin + transfer_state <= 4; + end else begin + transaction_complete <= 1; + transfer_state <= 3; + end + + // Toggle clock + spi_clock <= 1'b1; + end + default: begin + transfer_state <= 0; + end + endcase + end + end + +endmodule diff --git a/tercel/wishbone_spi_master.v b/tercel/wishbone_spi_master.v new file mode 100644 index 0000000..4cf64fa --- /dev/null +++ b/tercel/wishbone_spi_master.v @@ -0,0 +1,1094 @@ +// © 2017 - 2022 Raptor Engineering, LLC +// +// Released under the terms of the GNU LGPL v3 +// See the LICENSE file for full details + +// ============================================================================================= +// Memory Map: +// ============================================================================================= +// Device ID string (8 bytes) +// Device version (4 bytes) +// Base clock frequency (4 bytes) +// PHY configuration register 1 (4 bytes): {cs_extra_idle_cycles, 2'b0, qspi_write_quad_io_en, qspi_read_quad_io_en, fast_read_mode, four_byte_address_mode, phy_io_type, dummy_cycle_count, spi_clock_divisor} +// Flash configuration register 1 (4 bytes): {4BA QSPI read command, 3BA QSPI read command, 4BA SPI read command, 3BA SPI read command} +// Flash configuration register 2 (4 bytes): {4BA QSPI fast read command, 3BA QSPI fast read command, 4BA SPI fast read command, 3BA SPI fast read command} +// Flash configuration register 3 (4 bytes): {4BA QSPI program command, 3BA QSPI program command, 4BA SPI program command, 3BA SPI program command} +// Flash configuration register 4 (4 bytes): {spi_cs_active_hold_cycles} +// Flash configuration register 5 (4 bytes): {30'b0, allow_multicycle_writes, allow_multicycle_reads} +// Control register 1 (4 bytes): {31'b0, user_command_mode} +// Data register 1 (4 bytes): {24'b0, user_command_mode_read_during_write} + +// Stop LiteX silently ignoring net naming / missing register errors +`default_nettype none + +module tercel_core( + // Configuration registers + input wire [31:0] sys_clk_freq, + + // Wishbone signals + input wire wishbone_cyc, + input wire wishbone_stb, + input wire wishbone_we, + input wire [29:0] wishbone_adr, + input wire [31:0] wishbone_dat_w, + output wire [31:0] wishbone_dat_r, + input wire [3:0] wishbone_sel, + output wire wishbone_ack, + output wire wishbone_err, + + // Wishbone configuration port signals + input wire cfg_wishbone_cyc, + input wire cfg_wishbone_stb, + input wire cfg_wishbone_we, + input wire [29:0] cfg_wishbone_adr, + input wire [31:0] cfg_wishbone_dat_w, + output wire [31:0] cfg_wishbone_dat_r, + input wire [3:0] cfg_wishbone_sel, + output wire cfg_wishbone_ack, + output wire cfg_wishbone_err, + + // SPI bus signals + output wire spi_clock, + output wire [3:0] spi_d_out, + output wire [3:0] spi_d_direction, // 0 == tristate (input), 1 == driven (output) + input wire [3:0] spi_d_in, + output wire spi_ss_n, + + output wire [7:0] debug_port, + + input wire peripheral_reset, + input wire peripheral_clock + ); + + // Control and status registers + wire [63:0] device_id; + wire [31:0] device_version; + + // PHY configuration register 1 + // Defaults to standard SPI mode, 3BA, non-extended read/write (qspi_[read|write]_quad_io_en = 0), cs_extra_idle_cycles = 0, dummy cycle cont = 10, clock divisor 16 + reg [31:0] phy_cfg1 = 32'h00000a10; + + // Defaults to compatibility with Micron N25Q/512MB and similar 3BA/4BA capable devices, with multicycle and write disabled + // Note that the N25Q does not support normal reads in QSPI mode, so we leave the QSPI normal read commands equal + // to the normal SPI commands for safety -- no corruption is possible if the device is latched into read mode... + reg [31:0] flash_cfg1 = 32'h13031303; + reg [31:0] flash_cfg2 = 32'heceb0c0b; + reg [31:0] flash_cfg3 = 32'h34321202; + reg [31:0] flash_cfg4 = 32'h00000000; + reg [31:0] flash_cfg5 = 32'h00000000; + + // Defaults to read mode + reg [31:0] core_ctl1 = 32'h00000000; + wire [31:0] core_data1; + + // Device identifier + assign device_id = 64'h7c5250545350494d; + assign device_version = 32'h00010000; + + reg cfg_wishbone_ack_reg = 0; + reg [31:0] cfg_wishbone_dat_r_reg = 0; + + assign cfg_wishbone_ack = cfg_wishbone_ack_reg; + assign cfg_wishbone_dat_r = cfg_wishbone_dat_r_reg; + + parameter WB_CFG_TRANSFER_STATE_IDLE = 0; + parameter WB_CFG_TRANSFER_STATE_TR01 = 1; + + reg [31:0] wishbone_config_buffer_address_reg = 0; + reg [7:0] wishbone_config_transfer_state = 0; + reg [31:0] wishbone_cfg_space_tx_buffer = 0; + reg [31:0] wishbone_cfg_space_rx_buffer = 0; + + // Wishbone configuration space connector + always @(posedge peripheral_clock) begin + if (peripheral_reset) begin + // Reset Wishbone interface / control state machine + cfg_wishbone_ack_reg <= 0; + + wishbone_config_transfer_state <= WB_CFG_TRANSFER_STATE_IDLE; + end else begin + case (wishbone_config_transfer_state) + WB_CFG_TRANSFER_STATE_IDLE: begin + // Compute effective address + wishbone_config_buffer_address_reg[31:2] = cfg_wishbone_adr; + case (cfg_wishbone_sel) + 4'b0001: wishbone_config_buffer_address_reg[1:0] = 0; + 4'b0010: wishbone_config_buffer_address_reg[1:0] = 1; + 4'b0100: wishbone_config_buffer_address_reg[1:0] = 2; + 4'b1000: wishbone_config_buffer_address_reg[1:0] = 3; + 4'b1111: wishbone_config_buffer_address_reg[1:0] = 0; + default: wishbone_config_buffer_address_reg[1:0] = 0; + endcase + + if (cfg_wishbone_cyc && cfg_wishbone_stb) begin + // Configuration register space access + // Single clock pulse signals in deasserted state...process incoming request! + if (!cfg_wishbone_we) begin + // Read requested + case ({wishbone_config_buffer_address_reg[7:2], 2'b00}) + 0: wishbone_cfg_space_tx_buffer = device_id[63:32]; + 4: wishbone_cfg_space_tx_buffer = device_id[31:0]; + 8: wishbone_cfg_space_tx_buffer = device_version; + 12: wishbone_cfg_space_tx_buffer = sys_clk_freq; + 16: wishbone_cfg_space_tx_buffer = phy_cfg1; + 20: wishbone_cfg_space_tx_buffer = flash_cfg1; + 24: wishbone_cfg_space_tx_buffer = flash_cfg2; + 28: wishbone_cfg_space_tx_buffer = flash_cfg3; + 32: wishbone_cfg_space_tx_buffer = flash_cfg4; + 36: wishbone_cfg_space_tx_buffer = flash_cfg5; + 40: wishbone_cfg_space_tx_buffer = core_ctl1; + 44: wishbone_cfg_space_tx_buffer = core_data1; + default: wishbone_cfg_space_tx_buffer = 0; + endcase + + // Endian swap + cfg_wishbone_dat_r_reg[31:24] <= wishbone_cfg_space_tx_buffer[7:0]; + cfg_wishbone_dat_r_reg[23:16] <= wishbone_cfg_space_tx_buffer[15:8]; + cfg_wishbone_dat_r_reg[15:8] <= wishbone_cfg_space_tx_buffer[23:16]; + cfg_wishbone_dat_r_reg[7:0] <= wishbone_cfg_space_tx_buffer[31:24]; + + // Signal transfer complete + cfg_wishbone_ack_reg <= 1; + + wishbone_config_transfer_state <= WB_CFG_TRANSFER_STATE_TR01; + end else begin + // Write requested + case ({wishbone_config_buffer_address_reg[7:2], 2'b00}) + // Device ID / version registers cannot be written, don't even try... + 16: wishbone_cfg_space_rx_buffer = phy_cfg1; + 20: wishbone_cfg_space_rx_buffer = flash_cfg1; + 24: wishbone_cfg_space_rx_buffer = flash_cfg2; + 28: wishbone_cfg_space_rx_buffer = flash_cfg3; + 32: wishbone_cfg_space_rx_buffer = flash_cfg4; + 36: wishbone_cfg_space_rx_buffer = flash_cfg5; + 40: wishbone_cfg_space_rx_buffer = core_ctl1; + // Status registers cannot be written, don't even try... + default: wishbone_cfg_space_rx_buffer = 0; + endcase + + if (cfg_wishbone_sel[0]) begin + wishbone_cfg_space_rx_buffer[7:0] = cfg_wishbone_dat_w[31:24]; + end + if (cfg_wishbone_sel[1]) begin + wishbone_cfg_space_rx_buffer[15:8] = cfg_wishbone_dat_w[23:16]; + end + if (cfg_wishbone_sel[2]) begin + wishbone_cfg_space_rx_buffer[23:16] = cfg_wishbone_dat_w[15:8]; + end + if (cfg_wishbone_sel[3]) begin + wishbone_cfg_space_rx_buffer[31:24] = cfg_wishbone_dat_w[7:0]; + end + + case ({wishbone_config_buffer_address_reg[7:2], 2'b00}) + 16: phy_cfg1 <= wishbone_cfg_space_rx_buffer; + 20: flash_cfg1 <= wishbone_cfg_space_rx_buffer; + 24: flash_cfg2 <= wishbone_cfg_space_rx_buffer; + 28: flash_cfg3 <= wishbone_cfg_space_rx_buffer; + 32: flash_cfg4 <= wishbone_cfg_space_rx_buffer; + 36: flash_cfg5 <= wishbone_cfg_space_rx_buffer; + 40: core_ctl1 <= wishbone_cfg_space_rx_buffer; + endcase + + // Signal transfer complete + cfg_wishbone_ack_reg <= 1; + + wishbone_config_transfer_state <= WB_CFG_TRANSFER_STATE_TR01; + end + end + end + WB_CFG_TRANSFER_STATE_TR01: begin + // Cycle complete + cfg_wishbone_ack_reg <= 0; + wishbone_config_transfer_state <= WB_CFG_TRANSFER_STATE_IDLE; + end + default: begin + // Should never reach this state + wishbone_config_transfer_state <= WB_CFG_TRANSFER_STATE_IDLE; + end + endcase + end + end + + // SPI bus signals + wire spi_data_direction; + wire spi_quad_mode_pin_enable; + wire drive_host_interfaces; + assign drive_host_interfaces = 1; + assign spi_d_direction[0] = !spi_quad_mode_pin_enable || (spi_data_direction & drive_host_interfaces); + assign spi_d_direction[1] = spi_quad_mode_pin_enable && (spi_data_direction & drive_host_interfaces); + assign spi_d_direction[2] = !spi_quad_mode_pin_enable || (spi_data_direction & drive_host_interfaces); + assign spi_d_direction[3] = !spi_quad_mode_pin_enable || (spi_data_direction & drive_host_interfaces); + + // Interface registers + wire [7:0] spi_clock_divisor; + wire [7:0] dummy_cycle_count; + wire [1:0] phy_io_type; + wire [7:0] cs_extra_idle_cycles; + wire four_byte_address_mode; + wire fast_read_mode; + wire qspi_read_quad_io_en; + wire qspi_write_quad_io_en; + wire [7:0] qspi_read_4ba_command_code; + wire [7:0] qspi_read_3ba_command_code; + wire [7:0] spi_read_4ba_command_code; + wire [7:0] spi_read_3ba_command_code; + wire [7:0] qspi_fast_read_4ba_command_code; + wire [7:0] qspi_fast_read_3ba_command_code; + wire [7:0] spi_fast_read_4ba_command_code; + wire [7:0] spi_fast_read_3ba_command_code; + wire [7:0] qspi_program_4ba_command_code; + wire [7:0] qspi_program_3ba_command_code; + wire [7:0] spi_program_4ba_command_code; + wire [7:0] spi_program_3ba_command_code; + wire [31:0] spi_cs_active_hold_cycles; + wire allow_multicycle_writes; + wire allow_multicycle_reads; + wire user_command_mode; + reg [7:0] user_command_mode_read_during_write = 0; + assign spi_clock_divisor = phy_cfg1[7:0]; + assign dummy_cycle_count = phy_cfg1[15:8]; + assign phy_io_type = phy_cfg1[17:16]; + assign cs_extra_idle_cycles = phy_cfg1[31:24]; + assign four_byte_address_mode = phy_cfg1[18]; + assign fast_read_mode = phy_cfg1[19]; + assign qspi_read_quad_io_en = phy_cfg1[20]; + assign qspi_write_quad_io_en = phy_cfg1[21]; + assign qspi_read_4ba_command_code = flash_cfg1[31:24]; + assign qspi_read_3ba_command_code = flash_cfg1[23:16]; + assign spi_read_4ba_command_code = flash_cfg1[15:8]; + assign spi_read_3ba_command_code = flash_cfg1[7:0]; + assign qspi_fast_read_4ba_command_code = flash_cfg2[31:24]; + assign qspi_fast_read_3ba_command_code = flash_cfg2[23:16]; + assign spi_fast_read_4ba_command_code = flash_cfg2[15:8]; + assign spi_fast_read_3ba_command_code = flash_cfg2[7:0]; + assign qspi_program_4ba_command_code = flash_cfg3[31:24]; + assign qspi_program_3ba_command_code = flash_cfg3[23:16]; + assign spi_program_4ba_command_code = flash_cfg3[15:8]; + assign spi_program_3ba_command_code = flash_cfg3[7:0]; + assign spi_cs_active_hold_cycles = flash_cfg4[31:0]; + assign allow_multicycle_writes = flash_cfg5[1]; + assign allow_multicycle_reads = flash_cfg5[0]; + assign user_command_mode = core_ctl1[0]; + assign core_data1 = {24'h000000, user_command_mode_read_during_write}; + + parameter PHY_IO_TYPE_SINGLE = 0; + parameter PHY_IO_TYPE_QUAD = 2; + + // PHY clock generator + // Divisor: + // (spi_clock_divisor - 1) * 2 + // 0 == undefined (actually divide by two) + // 1 == divide by 1 + // 2 == divide by 2 + // 3 == divide by 4 + // 4 == divide by 6 + // 5 == divide by 8 + // 6 == divide by 10 + // 7 == divide by 12 + // etc. + wire spi_phy_clock; + reg spi_phy_clock_gen_reg = 0; + reg [7:0] spi_phy_clock_counter = 0; + assign spi_phy_clock = (spi_clock_divisor == 1)?peripheral_clock:spi_phy_clock_gen_reg; + always @(posedge peripheral_clock) begin + if (spi_phy_clock_counter >= (spi_clock_divisor - 2)) begin + spi_phy_clock_gen_reg <= ~spi_phy_clock_gen_reg; + spi_phy_clock_counter <= 0; + end else begin + spi_phy_clock_counter <= spi_phy_clock_counter + 1; + end + end + + reg [31:0] phy_tx_data = 0; + wire [31:0] phy_rx_data; + reg phy_hold_ss_active = 0; + reg phy_qspi_mode_active = 0; + reg phy_qspi_transfer_mode = 0; + reg phy_qspi_transfer_direction = 0; + reg [7:0] phy_dummy_cycle_count = 0; + reg [3:0] single_cycle_read_counter = 0; + reg [3:0] single_cycle_write_counter = 0; + reg phy_cycle_start = 0; + wire phy_transaction_complete; + + spi_master_phy_quad spi_master_phy_quad( + .platform_clock(spi_phy_clock), + .reset(peripheral_reset), + .tx_data(phy_tx_data), + .rx_data(phy_rx_data), + .dummy_cycle_count(phy_dummy_cycle_count), + .hold_ss_active(phy_hold_ss_active), + .qspi_mode_active(phy_qspi_mode_active), + .qspi_transfer_mode(phy_qspi_transfer_mode), + .qspi_transfer_direction(phy_qspi_transfer_direction), + .cycle_start(phy_cycle_start), + .transaction_complete(phy_transaction_complete), + + .spi_clock(spi_clock), + .spi_d0_out(spi_d_out[0]), + .spi_d0_in(spi_d_in[0]), + .spi_d1_out(spi_d_out[1]), + .spi_d1_in(spi_d_in[1]), + .spi_d2_out(spi_d_out[2]), + .spi_d2_in(spi_d_in[2]), + .spi_d3_out(spi_d_out[3]), + .spi_d3_in(spi_d_in[3]), + .spi_ss_n(spi_ss_n), + .spi_data_direction(spi_data_direction), + .spi_quad_mode_pin_enable(spi_quad_mode_pin_enable) + ); + + reg [3:0] wishbone_sel_reg = 0; + reg wishbone_ack_reg = 0; + assign wishbone_ack = wishbone_ack_reg; + reg [31:0] wishbone_dat_r_reg = 0; + assign wishbone_dat_r = wishbone_dat_r_reg; + + parameter SPI_MASTER_TRANSFER_STATE_IDLE = 0; + parameter SPI_MASTER_TRANSFER_STATE_TR01 = 1; + parameter SPI_MASTER_TRANSFER_STATE_TR02 = 2; + parameter SPI_MASTER_TRANSFER_STATE_TR03 = 16; + parameter SPI_MASTER_TRANSFER_STATE_TR04 = 17; + parameter SPI_MASTER_TRANSFER_STATE_TR05 = 18; + parameter SPI_MASTER_TRANSFER_STATE_TR06 = 19; + parameter SPI_MASTER_TRANSFER_STATE_TR07 = 20; + parameter SPI_MASTER_TRANSFER_STATE_TR08 = 21; + parameter SPI_MASTER_TRANSFER_STATE_TR09 = 22; + parameter SPI_MASTER_TRANSFER_STATE_TR10 = 23; + parameter SPI_MASTER_TRANSFER_STATE_TR11 = 24; + parameter SPI_MASTER_TRANSFER_STATE_TR12 = 25; + parameter SPI_MASTER_TRANSFER_STATE_RD01 = 32; + parameter SPI_MASTER_TRANSFER_STATE_RD02 = 33; + parameter SPI_MASTER_TRANSFER_STATE_RD03 = 34; + parameter SPI_MASTER_TRANSFER_STATE_WR01 = 48; + parameter SPI_MASTER_TRANSFER_STATE_WR02 = 49; + parameter SPI_MASTER_TRANSFER_STATE_WR03 = 50; + parameter SPI_MASTER_TRANSFER_STATE_UC01 = 64; + parameter SPI_MASTER_TRANSFER_STATE_UC02 = 65; + parameter SPI_MASTER_TRANSFER_STATE_UC03 = 66; + parameter SPI_MASTER_TRANSFER_STATE_UC04 = 67; + + reg [7:0] byte_out = 0; + reg [7:0] spi_transfer_state = 0; + reg spi_data_cycle_type = 0; + reg [31:0] spi_address_reg = 0; + reg [1:0] phy_io_type_reg; + reg [31:0] spi_byte_read_count = 0; + reg wishbone_data_cycle_type = 0; + reg wishbone_access_is_32_bits = 0; + reg user_command_mode_active = 0; + reg multicycle_read_in_progress = 0; + reg multicycle_write_in_progress = 0; + reg [31:0] multicycle_transaction_address = 0; + reg [7:0] cs_extra_cycle_counter = 0; + reg [31:0] spi_cs_active_counter = 0; + + assign debug_port = spi_transfer_state; + + always @(posedge peripheral_clock) begin + if (peripheral_reset) begin + // Reset PHY + phy_hold_ss_active <= 0; + phy_qspi_mode_active <= 0; + phy_qspi_transfer_mode <= 0; + phy_qspi_transfer_direction <= 0; + phy_dummy_cycle_count <= 0; + phy_cycle_start <= 0; + + // Reset Wishbone interface / control state machine + wishbone_ack_reg <= 0; + wishbone_data_cycle_type <= 0; + wishbone_access_is_32_bits <= 0; + user_command_mode_read_during_write <= 0; + user_command_mode_active <= 0; + multicycle_read_in_progress <= 0; + multicycle_write_in_progress <= 0; + multicycle_transaction_address <= 0; + spi_cs_active_counter <= 0; + spi_transfer_state <= SPI_MASTER_TRANSFER_STATE_IDLE; + end else begin + case (spi_transfer_state) + SPI_MASTER_TRANSFER_STATE_IDLE: begin + // Compute effective address + spi_address_reg[31:2] = wishbone_adr; + case (wishbone_sel) + 4'b0001: spi_address_reg[1:0] = 0; + 4'b0010: spi_address_reg[1:0] = 1; + 4'b0100: spi_address_reg[1:0] = 2; + 4'b1000: spi_address_reg[1:0] = 3; + 4'b1111: spi_address_reg[1:0] = 0; + default: spi_address_reg[1:0] = 0; + endcase + + // Process command + if ((spi_cs_active_hold_cycles > 0) && (spi_cs_active_counter >= spi_cs_active_hold_cycles)) begin + // Stop multicycle transfer + spi_transfer_state <= SPI_MASTER_TRANSFER_STATE_UC02; + end else if (!user_command_mode && user_command_mode_active) begin + // Exit user command mode + spi_transfer_state <= SPI_MASTER_TRANSFER_STATE_UC02; + end else if (multicycle_read_in_progress) begin + if (!allow_multicycle_reads) begin + // Stop multicycle transfer + spi_transfer_state <= SPI_MASTER_TRANSFER_STATE_UC02; + end else if (wishbone_cyc && wishbone_stb) begin + if (user_command_mode) begin + // User command mode requested + // Stop multicycle transfer in preparation to enable user command mode + spi_transfer_state <= SPI_MASTER_TRANSFER_STATE_UC02; + end else if (wishbone_data_cycle_type) begin + // Write requested + // Stop multicycle transfer in preparation to enable write mode + spi_transfer_state <= SPI_MASTER_TRANSFER_STATE_UC02; + end else begin + // Select 8-bit/32-bit transfer size via Wishbone access mode + if (wishbone_sel == 4'b1111) begin + wishbone_access_is_32_bits <= 1; + end else begin + wishbone_access_is_32_bits <= 0; + end + wishbone_sel_reg <= wishbone_sel; + + // Verify next address matches current address + if (four_byte_address_mode) begin + if (multicycle_transaction_address == spi_address_reg) begin + // Transfer next data chunk + spi_transfer_state <= SPI_MASTER_TRANSFER_STATE_RD01; + end else begin + // Stop multicycle transfer + spi_transfer_state <= SPI_MASTER_TRANSFER_STATE_UC02; + end + end else begin + if (multicycle_transaction_address[23:0] == spi_address_reg[23:0]) begin + // Transfer next data chunk + spi_transfer_state <= SPI_MASTER_TRANSFER_STATE_RD01; + end else begin + // Stop multicycle transfer + spi_transfer_state <= SPI_MASTER_TRANSFER_STATE_UC02; + end + end + end + end + end else if (multicycle_write_in_progress) begin + if (!allow_multicycle_writes) begin + // Stop multicycle transfer + spi_transfer_state <= SPI_MASTER_TRANSFER_STATE_UC02; + end else if (wishbone_cyc && wishbone_stb) begin + if (user_command_mode) begin + // User command mode requested + // Stop multicycle transfer in preparation to enable user command mode + spi_transfer_state <= SPI_MASTER_TRANSFER_STATE_UC02; + end else if (!wishbone_data_cycle_type) begin + // Read requested + // Stop multicycle transfer in preparation to enable read mode + spi_transfer_state <= SPI_MASTER_TRANSFER_STATE_UC02; + end else begin + // Select 8-bit/32-bit transfer size via Wishbone access mode + if (wishbone_sel == 4'b1111) begin + wishbone_access_is_32_bits <= 1; + end else begin + wishbone_access_is_32_bits <= 0; + end + wishbone_sel_reg <= wishbone_sel; + + // Verify next address matches current address + if (four_byte_address_mode) begin + if (multicycle_transaction_address == spi_address_reg) begin + // Transfer next data chunk + spi_transfer_state <= SPI_MASTER_TRANSFER_STATE_WR01; + end else begin + // Stop multicycle transfer + spi_transfer_state <= SPI_MASTER_TRANSFER_STATE_UC02; + end + end else begin + if (multicycle_transaction_address[23:0] == spi_address_reg[23:0]) begin + // Transfer next data chunk + spi_transfer_state <= SPI_MASTER_TRANSFER_STATE_WR01; + end else begin + // Stop multicycle transfer + spi_transfer_state <= SPI_MASTER_TRANSFER_STATE_UC02; + end + end + end + end + end else begin + if (wishbone_cyc && wishbone_stb) begin + if (wishbone_sel == 4'b1111) begin + wishbone_access_is_32_bits <= 1; + end else begin + wishbone_access_is_32_bits <= 0; + end + wishbone_sel_reg <= wishbone_sel; + wishbone_data_cycle_type <= wishbone_we; + phy_io_type_reg <= phy_io_type; + if (!phy_transaction_complete) begin + if (user_command_mode) begin + // Read the data byte to write from the active lane + if (wishbone_sel[0]) begin + phy_tx_data[7:0] <= wishbone_dat_w[31:24]; + end else if (wishbone_sel[1]) begin + phy_tx_data[7:0] <= wishbone_dat_w[23:16]; + end else if (wishbone_sel[2]) begin + phy_tx_data[7:0] <= wishbone_dat_w[15:8]; + end else if (wishbone_sel[3]) begin + phy_tx_data[7:0] <= wishbone_dat_w[7:0]; + end else begin + phy_tx_data[7:0] = 8'hff; // Safe default -- will not send any useful commands / data + end + + // Set up SPI access + phy_tx_data[31:8] = 0; + phy_qspi_mode_active <= 0; + phy_qspi_transfer_mode <= 0; + phy_dummy_cycle_count <= 0; + phy_hold_ss_active <= 1; + phy_cycle_start <= 1; + + // Set user command mode active + user_command_mode_active <= 1; + spi_transfer_state <= SPI_MASTER_TRANSFER_STATE_UC01; + end else begin + if (!wishbone_we) begin + // Set up SPI read access + if (phy_io_type == PHY_IO_TYPE_QUAD) begin + if (four_byte_address_mode) begin + if (fast_read_mode) begin + phy_tx_data <= qspi_fast_read_4ba_command_code; + end else begin + phy_tx_data <= qspi_read_4ba_command_code; + end + end else begin + if (fast_read_mode) begin + phy_tx_data <= qspi_fast_read_3ba_command_code; + end else begin + phy_tx_data <= qspi_read_3ba_command_code; + end + end + end else begin + if (four_byte_address_mode) begin + if (fast_read_mode) begin + phy_tx_data <= spi_fast_read_4ba_command_code; + end else begin + phy_tx_data <= spi_read_4ba_command_code; + end + end else begin + if (fast_read_mode) begin + phy_tx_data <= spi_fast_read_3ba_command_code; + end else begin + phy_tx_data <= spi_read_3ba_command_code; + end + end + end + if (allow_multicycle_reads) begin + multicycle_read_in_progress <= 1; + end else begin + single_cycle_read_counter <= 0; + end + end else begin + // Set up SPI write access + if (phy_io_type == PHY_IO_TYPE_QUAD) begin + if (four_byte_address_mode) begin + phy_tx_data <= qspi_program_4ba_command_code; + end else begin + phy_tx_data <= qspi_program_3ba_command_code; + end + end else begin + if (four_byte_address_mode) begin + phy_tx_data <= spi_program_4ba_command_code; + end else begin + phy_tx_data <= spi_program_3ba_command_code; + end + end + if (allow_multicycle_writes) begin + multicycle_write_in_progress <= 1; + end else begin + single_cycle_write_counter <= 0; + end + end + phy_qspi_mode_active <= 0; + phy_qspi_transfer_mode <= 0; + phy_dummy_cycle_count <= 0; + phy_hold_ss_active <= 1; + phy_cycle_start <= 1; + + if (four_byte_address_mode) begin + multicycle_transaction_address <= spi_address_reg; + end else begin + multicycle_transaction_address <= {8'h00, spi_address_reg[23:0]}; + end + + spi_transfer_state <= SPI_MASTER_TRANSFER_STATE_TR03; + end + end + end + end + + if ((spi_cs_active_hold_cycles > 0) && (multicycle_read_in_progress || multicycle_write_in_progress)) begin + spi_cs_active_counter <= spi_cs_active_counter + 1; + end else begin + spi_cs_active_counter <= 0; + end + end + SPI_MASTER_TRANSFER_STATE_UC01: begin + if (phy_transaction_complete) begin + phy_cycle_start <= 0; + + user_command_mode_read_during_write <= phy_rx_data[7:0]; + if (!wishbone_data_cycle_type) begin + // Read cycle + // Replicate the output bytes to all active lanes + if (wishbone_sel_reg[0]) begin + wishbone_dat_r_reg[31:24] <= phy_rx_data[7:0]; + end + if (wishbone_sel_reg[1]) begin + wishbone_dat_r_reg[23:16] <= phy_rx_data[7:0]; + end + if (wishbone_sel_reg[2]) begin + wishbone_dat_r_reg[15:8] <= phy_rx_data[7:0]; + end + if (wishbone_sel_reg[3]) begin + wishbone_dat_r_reg[7:0] <= phy_rx_data[7:0]; + end + end + + spi_transfer_state <= SPI_MASTER_TRANSFER_STATE_TR01; + end + end + SPI_MASTER_TRANSFER_STATE_UC02: begin + // Release CS and prepare the SPI device for the next command + phy_hold_ss_active <= 0; + phy_qspi_transfer_mode <= 0; + phy_qspi_mode_active <= 0; + + // Terminate user command mode / multicycle transfers + user_command_mode_active <= 0; + multicycle_read_in_progress <= 0; + multicycle_write_in_progress <= 0; + spi_transfer_state <= SPI_MASTER_TRANSFER_STATE_UC03; + end + SPI_MASTER_TRANSFER_STATE_UC03: begin + // Wait for CS to return to idle, since this state machine is running + // significantly faster than the SPI PHY state machine. This avoids + // potentially short cycling the PHY and not allowing CS to return + // to idle, thus inadvertently chaining the next command onto the + // previous one! + if (spi_ss_n) begin + if (cs_extra_idle_cycles > 0) begin + cs_extra_cycle_counter <= 1; + spi_transfer_state <= SPI_MASTER_TRANSFER_STATE_UC04; + end else begin + // Return to idle + spi_transfer_state <= SPI_MASTER_TRANSFER_STATE_IDLE; + end + end + end + SPI_MASTER_TRANSFER_STATE_UC04: begin + if (cs_extra_cycle_counter >= cs_extra_idle_cycles) begin + // Return to idle + spi_transfer_state <= SPI_MASTER_TRANSFER_STATE_IDLE; + end else begin + cs_extra_cycle_counter <= cs_extra_cycle_counter + 1; + end + end + SPI_MASTER_TRANSFER_STATE_TR01: begin + // Wait for CS to return to idle, since this state machine is running + // significantly faster than the SPI PHY state machine. This avoids + // potentially short cycling the PHY and not allowing CS to return + // to idle, thus inadvertently chaining the next command onto the + // previous one! + // If user command mode is active, CS will be asserted by design, + // so ignore... + if (spi_ss_n || user_command_mode_active + || multicycle_read_in_progress + || multicycle_write_in_progress) begin + if (!wishbone_data_cycle_type + && wishbone_access_is_32_bits + && !allow_multicycle_reads + && (single_cycle_read_counter < 4)) begin + // Set up SPI read access for next byte + if (phy_io_type == PHY_IO_TYPE_QUAD) begin + if (four_byte_address_mode) begin + if (fast_read_mode) begin + phy_tx_data <= qspi_fast_read_4ba_command_code; + end else begin + phy_tx_data <= qspi_read_4ba_command_code; + end + end else begin + if (fast_read_mode) begin + phy_tx_data <= qspi_fast_read_3ba_command_code; + end else begin + phy_tx_data <= qspi_read_3ba_command_code; + end + end + end else begin + if (four_byte_address_mode) begin + if (fast_read_mode) begin + phy_tx_data <= spi_fast_read_4ba_command_code; + end else begin + phy_tx_data <= spi_read_4ba_command_code; + end + end else begin + if (fast_read_mode) begin + phy_tx_data <= spi_fast_read_3ba_command_code; + end else begin + phy_tx_data <= spi_read_3ba_command_code; + end + end + end + + phy_qspi_mode_active <= 0; + phy_qspi_transfer_mode <= 0; + phy_dummy_cycle_count <= 0; + phy_hold_ss_active <= 1; + phy_cycle_start <= 1; + + spi_transfer_state <= SPI_MASTER_TRANSFER_STATE_TR03; + end else if (wishbone_data_cycle_type + && wishbone_access_is_32_bits + && !allow_multicycle_writes + && (single_cycle_write_counter < 4)) begin + // Set up SPI write access for next byte + if (phy_io_type == PHY_IO_TYPE_QUAD) begin + if (four_byte_address_mode) begin + phy_tx_data <= qspi_program_4ba_command_code; + end else begin + phy_tx_data <= qspi_program_3ba_command_code; + end + end else begin + if (four_byte_address_mode) begin + phy_tx_data <= spi_program_4ba_command_code; + end else begin + phy_tx_data <= spi_program_3ba_command_code; + end + end + + phy_qspi_mode_active <= 0; + phy_qspi_transfer_mode <= 0; + phy_dummy_cycle_count <= 0; + phy_hold_ss_active <= 1; + phy_cycle_start <= 1; + + spi_transfer_state <= SPI_MASTER_TRANSFER_STATE_TR03; + end else begin + // Signal transfer complete + wishbone_ack_reg <= 1; + + spi_transfer_state <= SPI_MASTER_TRANSFER_STATE_TR02; + end + end + end + SPI_MASTER_TRANSFER_STATE_TR02: begin + // Cycle complete + wishbone_ack_reg <= 0; + spi_cs_active_counter <= 0; + spi_transfer_state <= SPI_MASTER_TRANSFER_STATE_IDLE; + end + SPI_MASTER_TRANSFER_STATE_TR03: begin + if (phy_transaction_complete) begin + phy_cycle_start <= 0; + spi_transfer_state <= SPI_MASTER_TRANSFER_STATE_TR04; + end + end + SPI_MASTER_TRANSFER_STATE_TR04: begin + if (phy_io_type_reg == PHY_IO_TYPE_QUAD) begin + if (!wishbone_data_cycle_type) begin + // Read + phy_qspi_mode_active <= qspi_read_quad_io_en; + end else begin + // Write + phy_qspi_mode_active <= qspi_write_quad_io_en; + end + end else begin + phy_qspi_mode_active <= 0; + end + if (four_byte_address_mode) begin + spi_transfer_state <= SPI_MASTER_TRANSFER_STATE_TR05; + end else begin + spi_transfer_state <= SPI_MASTER_TRANSFER_STATE_TR07; + end + end + SPI_MASTER_TRANSFER_STATE_TR05: begin + // Address (4 bytes / 1 word) + if (!phy_transaction_complete) begin + if (wishbone_access_is_32_bits && !allow_multicycle_reads) begin + phy_tx_data <= multicycle_transaction_address; + end else begin + phy_tx_data <= spi_address_reg; + end + phy_qspi_transfer_mode <= 1; + phy_qspi_transfer_direction <= 1; + if (!wishbone_data_cycle_type) begin + // Read + if (fast_read_mode) begin + phy_dummy_cycle_count <= dummy_cycle_count; + end else begin + phy_dummy_cycle_count <= 0; + end + end else begin + // Write + phy_dummy_cycle_count <= 0; + end + phy_cycle_start <= 1; + + spi_byte_read_count <= 0; + spi_transfer_state <= SPI_MASTER_TRANSFER_STATE_TR06; + end + end + SPI_MASTER_TRANSFER_STATE_TR06: begin + if (phy_transaction_complete) begin + phy_cycle_start <= 0; + + if (phy_io_type_reg == PHY_IO_TYPE_QUAD) begin + phy_qspi_mode_active <= 1; + end else begin + phy_qspi_mode_active <= 0; + end + + if (wishbone_data_cycle_type) begin + spi_transfer_state <= SPI_MASTER_TRANSFER_STATE_WR01; + end else begin + spi_transfer_state <= SPI_MASTER_TRANSFER_STATE_RD01; + end + end + end + SPI_MASTER_TRANSFER_STATE_RD01: begin + if (!phy_transaction_complete) begin + // Data words + phy_tx_data <= 0; // Not used + if (phy_io_type_reg == PHY_IO_TYPE_QUAD) begin + phy_qspi_mode_active <= 1; + end else begin + phy_qspi_mode_active <= 0; + end + if (wishbone_access_is_32_bits && allow_multicycle_reads) begin + phy_qspi_transfer_mode <= 1; + end else begin + phy_qspi_transfer_mode <= 0; + end + phy_qspi_transfer_direction <= 0; + phy_dummy_cycle_count <= 0; + phy_cycle_start <= 1; + spi_transfer_state <= SPI_MASTER_TRANSFER_STATE_RD02; + end + end + SPI_MASTER_TRANSFER_STATE_RD02: begin + if (phy_transaction_complete) begin + // Set Wishbone output data + if (wishbone_access_is_32_bits) begin + if (allow_multicycle_reads) begin + wishbone_dat_r_reg <= phy_rx_data; + multicycle_transaction_address <= multicycle_transaction_address + 4; + end else begin + if (single_cycle_read_counter == 0) begin + wishbone_dat_r_reg[31:24] <= phy_rx_data[7:0]; + end else if (single_cycle_read_counter == 1) begin + wishbone_dat_r_reg[23:16] <= phy_rx_data[7:0]; + end else if (single_cycle_read_counter == 2) begin + wishbone_dat_r_reg[15:8] <= phy_rx_data[7:0]; + end else if (single_cycle_read_counter == 3) begin + wishbone_dat_r_reg[7:0] <= phy_rx_data[7:0]; + end + single_cycle_read_counter <= single_cycle_read_counter + 1; + multicycle_transaction_address <= multicycle_transaction_address + 1; + end + end else begin + // Replicate the data bytes to all active lanes + if (wishbone_sel_reg[0]) begin + wishbone_dat_r_reg[31:24] <= phy_rx_data[7:0]; + end + if (wishbone_sel_reg[1]) begin + wishbone_dat_r_reg[23:16] <= phy_rx_data[7:0]; + end + if (wishbone_sel_reg[2]) begin + wishbone_dat_r_reg[15:8] <= phy_rx_data[7:0]; + end + if (wishbone_sel_reg[3]) begin + wishbone_dat_r_reg[7:0] <= phy_rx_data[7:0]; + end + multicycle_transaction_address <= multicycle_transaction_address + 1; + end + + phy_cycle_start <= 0; + spi_transfer_state <= SPI_MASTER_TRANSFER_STATE_RD03; + end + end + SPI_MASTER_TRANSFER_STATE_RD03: begin + if (!phy_transaction_complete) begin + // Release CS (if required) + if (!multicycle_read_in_progress) begin + phy_hold_ss_active <= 0; + end + + spi_transfer_state <= SPI_MASTER_TRANSFER_STATE_TR01; + end + end + SPI_MASTER_TRANSFER_STATE_WR01: begin + if (!phy_transaction_complete) begin + // Data words + if (wishbone_access_is_32_bits) begin + if (allow_multicycle_writes) begin + phy_tx_data <= wishbone_dat_w; + phy_qspi_transfer_mode <= 1; + multicycle_transaction_address <= multicycle_transaction_address + 4; + end else begin + if (single_cycle_write_counter == 0) begin + phy_tx_data[7:0] <= wishbone_dat_w[7:0]; + end else if (single_cycle_write_counter == 1) begin + phy_tx_data[7:0] <= wishbone_dat_w[15:8]; + end else if (single_cycle_write_counter == 2) begin + phy_tx_data[7:0] <= wishbone_dat_w[23:16]; + end else if (single_cycle_write_counter == 3) begin + phy_tx_data[7:0] <= wishbone_dat_w[31:24]; + end + phy_qspi_transfer_mode <= 0; + single_cycle_write_counter <= single_cycle_write_counter + 1; + multicycle_transaction_address <= multicycle_transaction_address + 1; + end + end else begin + // Write cycle + // Read the data byte to write from the active lane + if (wishbone_sel_reg[0]) begin + phy_tx_data[7:0] <= wishbone_dat_w[31:24]; + end else if (wishbone_sel_reg[1]) begin + phy_tx_data[7:0] <= wishbone_dat_w[23:16]; + end else if (wishbone_sel_reg[2]) begin + phy_tx_data[7:0] <= wishbone_dat_w[15:8]; + end else if (wishbone_sel_reg[3]) begin + phy_tx_data[7:0] <= wishbone_dat_w[7:0]; + end else begin + phy_tx_data[7:0] = 8'hff; // Safe default -- will not overwrite any bits + end + phy_tx_data[31:8] = 0; + phy_qspi_transfer_mode <= 0; + multicycle_transaction_address <= multicycle_transaction_address + 1; + end + if (phy_io_type_reg == PHY_IO_TYPE_QUAD) begin + phy_qspi_mode_active <= 1; + end else begin + phy_qspi_mode_active <= 0; + end + phy_qspi_transfer_direction <= 1; + phy_dummy_cycle_count <= 0; + phy_cycle_start <= 1; + spi_transfer_state <= SPI_MASTER_TRANSFER_STATE_WR02; + end + end + SPI_MASTER_TRANSFER_STATE_WR02: begin + if (phy_transaction_complete) begin + phy_cycle_start <= 0; + + spi_transfer_state <= SPI_MASTER_TRANSFER_STATE_WR03; + end + end + SPI_MASTER_TRANSFER_STATE_WR03: begin + if (!phy_transaction_complete) begin + // Release CS (if required) + if (!multicycle_write_in_progress) begin + phy_hold_ss_active <= 0; + end + + spi_transfer_state <= SPI_MASTER_TRANSFER_STATE_TR01; + end + end + SPI_MASTER_TRANSFER_STATE_TR07: begin + // Send the three address bytes one at a time... + if (!phy_transaction_complete) begin + if (wishbone_access_is_32_bits && !allow_multicycle_reads) begin + phy_tx_data <= multicycle_transaction_address[23:16]; + end else begin + phy_tx_data <= spi_address_reg[23:16]; + end + if (phy_io_type_reg == PHY_IO_TYPE_QUAD) begin + phy_qspi_mode_active <= 1; + end else begin + phy_qspi_mode_active <= 0; + end + phy_qspi_transfer_mode <= 0; + phy_qspi_transfer_direction <= 1; + phy_dummy_cycle_count <= 0; + phy_cycle_start <= 1; + + spi_byte_read_count <= 0; + spi_transfer_state <= SPI_MASTER_TRANSFER_STATE_TR08; + end + end + SPI_MASTER_TRANSFER_STATE_TR08: begin + if (phy_transaction_complete) begin + phy_cycle_start <= 0; + spi_transfer_state <= SPI_MASTER_TRANSFER_STATE_TR09; + end + end + SPI_MASTER_TRANSFER_STATE_TR09: begin + if (!phy_transaction_complete) begin + if (wishbone_access_is_32_bits && !allow_multicycle_reads) begin + phy_tx_data <= multicycle_transaction_address[15:8]; + end else begin + phy_tx_data <= spi_address_reg[15:8]; + end + if (phy_io_type_reg == PHY_IO_TYPE_QUAD) begin + phy_qspi_mode_active <= 1; + end else begin + phy_qspi_mode_active <= 0; + end + phy_qspi_transfer_mode <= 0; + phy_qspi_transfer_direction <= 1; + phy_dummy_cycle_count <= 0; + phy_cycle_start <= 1; + + spi_byte_read_count <= 0; + spi_transfer_state <= SPI_MASTER_TRANSFER_STATE_TR10; + end + end + SPI_MASTER_TRANSFER_STATE_TR10: begin + if (phy_transaction_complete) begin + phy_cycle_start <= 0; + spi_transfer_state <= SPI_MASTER_TRANSFER_STATE_TR11; + end + end + SPI_MASTER_TRANSFER_STATE_TR11: begin + if (!phy_transaction_complete) begin + if (wishbone_access_is_32_bits && !allow_multicycle_reads) begin + phy_tx_data <= multicycle_transaction_address[7:0]; + end else begin + phy_tx_data <= spi_address_reg[7:0]; + end + if (phy_io_type_reg == PHY_IO_TYPE_QUAD) begin + phy_qspi_mode_active <= 1; + end else begin + phy_qspi_mode_active <= 0; + end + phy_qspi_transfer_mode <= 0; + phy_qspi_transfer_direction <= 1; + if (!wishbone_data_cycle_type) begin + // Read + if (fast_read_mode) begin + phy_dummy_cycle_count <= dummy_cycle_count; + end else begin + phy_dummy_cycle_count <= 0; + end + end else begin + phy_dummy_cycle_count <= 0; + end + phy_cycle_start <= 1; + + spi_byte_read_count <= 0; + spi_transfer_state <= SPI_MASTER_TRANSFER_STATE_TR12; + end + end + SPI_MASTER_TRANSFER_STATE_TR12: begin + if (phy_transaction_complete) begin + phy_cycle_start <= 0; + if (wishbone_data_cycle_type) begin + spi_transfer_state <= SPI_MASTER_TRANSFER_STATE_WR01; + end else begin + spi_transfer_state <= SPI_MASTER_TRANSFER_STATE_RD01; + end + end + end + default: begin + spi_transfer_state <= SPI_MASTER_TRANSFER_STATE_IDLE; + end + endcase + end + end +endmodule