diff --git a/modules/wishbone/wb_spi_flash/wb_spi_flash.vhd b/modules/wishbone/wb_spi_flash/wb_spi_flash.vhd index d1f8e9142a28b8fdc055c8aecd24ce84840c548f..0ee5924f24783a4ea8e492d09cd3e344b83036b3 100644 --- a/modules/wishbone/wb_spi_flash/wb_spi_flash.vhd +++ b/modules/wishbone/wb_spi_flash/wb_spi_flash.vhd @@ -17,6 +17,7 @@ -- Revisions : -- Date Version Author Description -- 2013-04-15 1.0 terpstra Created +-- 2013-08-28 2.0 terpstra Quad-lane support ------------------------------------------------------------------------------- library ieee; @@ -31,9 +32,10 @@ use work.gencores_pkg.all; -- Memory mapped flash controller entity wb_spi_flash is generic( - g_port_width : natural := 1; -- 1 for EPCS, 4 for EPCQ - g_addr_width : natural := 24; -- 24 for EPCS, 32 for EPCQ + g_port_width : natural := 1; -- 1 for EPCS, 4 for EPCQ + g_addr_width : natural := 24; -- size dependent (EPCQ256=25, EPCS128=24, ...) g_idle_time : natural := 3; + g_dummy_time : natural := 8; -- leave these at defaults if you have: -- a) slow clock, b) valid constraints, or c) registered in/outputs g_input_latch_edge : std_logic := '1'; -- rising @@ -49,6 +51,7 @@ entity wb_spi_flash is clk_out_i : in std_logic; clk_in_i : in std_logic; ncs_o : out std_logic; + oe_o : out std_logic_vector(g_port_width-1 downto 0); asdi_o : out std_logic_vector(g_port_width-1 downto 0); data_i : in std_logic_vector(g_port_width-1 downto 0); @@ -61,6 +64,7 @@ architecture rtl of wb_spi_flash is subtype t_word is std_logic_vector(31 downto 0); subtype t_byte is std_logic_vector( 7 downto 0); subtype t_address is unsigned(g_addr_width-1 downto 2); + subtype t_status is std_logic_vector(g_port_width-1 downto 0); subtype t_count is unsigned(f_ceil_log2(t_word'length)-1 downto 0); subtype t_ack_delay is std_logic_vector(g_input_to_output_cycles-1 downto 0); @@ -73,21 +77,29 @@ architecture rtl of wb_spi_flash is constant c_write_bytes2 : t_byte := "11010010"; -- address, dataout constant c_write_bytes4 : t_byte := "00010010"; -- address, dataout constant c_erase_sector : t_byte := "11011000"; -- address + constant c_4addr : t_byte := "10110111"; -- + + function f_addr_bits(x : natural) return natural is begin + if x <= 24 then return 24; else return 32; end if; + end f_addr_bits; + constant c_addr_bits : natural := f_addr_bits(g_addr_width); constant c_low_time : t_count := to_unsigned(g_idle_time-1, t_count'length); constant c_cmd_time : t_count := to_unsigned(t_byte'length-1, t_count'length); constant c_status_time : t_count := to_unsigned(8*((g_input_to_output_cycles+14)/8), t_count'length); - constant c_addr_time : t_count := to_unsigned((g_addr_width/g_port_width)-1, t_count'length); + constant c_addr_time : t_count := to_unsigned((c_addr_bits/g_port_width)-1, t_count'length); constant c_data_time : t_count := to_unsigned((t_wishbone_data'length/g_port_width)-1, t_count'length); constant c_whatever : std_logic_vector(g_port_width-1 downto 0) := (others => '-'); constant c_magic_reg : t_address := (others => '1'); type t_state is ( - S_ERROR, S_WAIT, S_DISPATCH, S_JTAG, + S_ERROR, S_WAIT, S_INIT, S_DISPATCH, S_JTAG, S_READ, S_READ_ADDR, S_READ_DUMMY, S_READ_DATA, S_LOWER_CS_IDLE, S_ENABLE_WRITE, S_LOWER_CS_WRITE, S_WRITE, S_WRITE_ADDR, S_WRITE_DATA, - S_ENABLE_ERASE, S_LOWER_CS_ERASE, S_ERASE, S_ERASE_ADDR, + S_ENABLE_ERASE, S_LOWER_CS_ERASE, S_ERASE, + S_ERASE_ADDR3, S_ERASE_ADDR2, S_ERASE_ADDR1, S_ERASE_ADDR0, + S_ENABLE_4ADDR, S_LOWER_CS_4ADDR, S_4ADDR, S_LOWER_CS_WAIT, S_READ_STATUS, S_LOAD_STATUS, S_WAIT_READY); -- Format a command for output @@ -96,6 +108,11 @@ architecture rtl of wb_spi_flash is begin for i in t_byte'range loop result(i*g_port_width + t_word'length-g_port_width*8) := cmd(i); + + if g_port_width >= 4 then + result(i*g_port_width + t_word'length-g_port_width*8 + 2) := '1'; + result(i*g_port_width + t_word'length-g_port_width*8 + 3) := '1'; + end if; end loop; return result; end f_stripe; @@ -116,7 +133,8 @@ architecture rtl of wb_spi_flash is function f_address(address : t_address) return t_word is variable result : t_word := (others => '0'); begin - result(t_word'left downto t_word'length-t_address'length) := + result((t_word'length-c_addr_bits)+t_address'left downto + (t_word'length-c_addr_bits)+t_address'right) := std_logic_vector(address); return result; end f_address; @@ -131,8 +149,13 @@ architecture rtl of wb_spi_flash is return result; end f_increment; - signal r_state : t_state := S_LOWER_CS_WAIT; - signal r_state_n : t_state := S_LOWER_CS_WAIT; + constant c_full_oe : std_logic_vector(3 downto 0) := "1101"; + + constant c_idle : t_word := f_stripe("--------"); + constant c_oe_default : t_status := c_full_oe(t_status'range); + + signal r_state : t_state := S_INIT; + signal r_state_n : t_state := S_INIT; signal r_count : t_count := (others => '-'); signal r_stall : std_logic := '0'; signal r_stall_n : std_logic := '0'; @@ -141,6 +164,7 @@ architecture rtl of wb_spi_flash is signal r_dat : t_wishbone_data := (others => '-'); signal r_adr : t_address := (others => '-'); signal r_ncs : std_logic := '1'; + signal r_oe : t_status := (others => '0'); signal r_shift_o : t_word := (others => '-'); signal r_shift_i : t_word := (others => '-'); @@ -195,6 +219,7 @@ begin asdi_o <= r_shift_o(31 downto 32-g_port_width); ncs_o <= r_ncs; + oe_o <= r_oe; -- output is latched by SPI on rising edge => prepare it on falling edge output : process(clk_out_i, clk_out_rstn) is @@ -202,23 +227,33 @@ begin if clk_out_rstn = '0' then r_shift_o <= (others => '-'); r_ncs <= '1'; + r_oe <= (others => '0'); elsif clk_out_i'event and clk_out_i = g_output_latch_edge then case r_state is when S_ERROR => - r_shift_o <= (others => '-'); + r_shift_o <= c_idle; r_ncs <= '1'; + r_oe <= c_oe_default; when S_WAIT => r_shift_o <= r_shift_o(31-g_port_width downto 0) & c_whatever; r_ncs <= r_ncs; + r_oe <= r_oe; + + when S_INIT => + r_shift_o <= c_idle; + r_ncs <= '1'; + r_oe <= c_oe_default; when S_DISPATCH => - r_shift_o <= (others => '-'); + r_shift_o <= c_idle; r_ncs <= '1'; + r_oe <= c_oe_default; when S_JTAG => r_shift_o <= (others => '-'); r_ncs <= '1'; + r_oe <= (others => '0'); when S_READ => case g_port_width is @@ -228,30 +263,37 @@ begin when others => null; end case; r_ncs <= '0'; + r_oe <= c_oe_default; when S_READ_ADDR => r_shift_o <= f_address(r_adr); r_ncs <= '0'; + r_oe <= (others => '1'); when S_READ_DUMMY => - r_shift_o <= (others => '-'); + r_shift_o <= (others => '0'); r_ncs <= '0'; + r_oe <= (others => '1'); when S_READ_DATA => r_shift_o <= (others => '-'); r_ncs <= '0'; + r_oe <= (others => '0'); when S_LOWER_CS_IDLE => - r_shift_o <= (others => '-'); + r_shift_o <= c_idle; r_ncs <= '1'; + r_oe <= c_oe_default; when S_ENABLE_WRITE => r_shift_o <= f_stripe(c_write_enable); r_ncs <= '0'; + r_oe <= c_oe_default; when S_LOWER_CS_WRITE => - r_shift_o <= (others => '-'); + r_shift_o <= c_idle; r_ncs <= '1'; + r_oe <= c_oe_default; when S_WRITE => case g_port_width is @@ -261,48 +303,92 @@ begin when others => null; end case; r_ncs <= '0'; + r_oe <= c_oe_default; when S_WRITE_ADDR => r_shift_o <= f_address(r_adr); + r_ncs <= '0'; + r_oe <= (others => '1'); when S_WRITE_DATA => r_shift_o <= r_dat; r_ncs <= '0'; + r_oe <= (others => '1'); when S_ENABLE_ERASE => r_shift_o <= f_stripe(c_write_enable); r_ncs <= '0'; + r_oe <= c_oe_default; when S_LOWER_CS_ERASE => - r_shift_o <= (others => '-'); + r_shift_o <= c_idle; r_ncs <= '1'; + r_oe <= c_oe_default; when S_ERASE => r_shift_o <= f_stripe(c_erase_sector); r_ncs <= '0'; + r_oe <= c_oe_default; - when S_ERASE_ADDR => - r_shift_o <= f_address(r_adr); + when S_ERASE_ADDR3 => + r_shift_o <= f_stripe(f_address(r_adr)(31 downto 24)); + r_ncs <= '0'; + r_oe <= c_oe_default; + + when S_ERASE_ADDR2 => + r_shift_o <= f_stripe(f_address(r_adr)(23 downto 16)); + r_ncs <= '0'; + r_oe <= c_oe_default; + + when S_ERASE_ADDR1 => + r_shift_o <= f_stripe(f_address(r_adr)(15 downto 8)); + r_ncs <= '0'; + r_oe <= c_oe_default; + + when S_ERASE_ADDR0 => + r_shift_o <= f_stripe(f_address(r_adr)(7 downto 0)); + r_ncs <= '0'; + r_oe <= c_oe_default; + + when S_ENABLE_4ADDR => + r_shift_o <= f_stripe(c_write_enable); + r_ncs <= '0'; + r_oe <= c_oe_default; + + when S_LOWER_CS_4ADDR => + r_shift_o <= c_idle; + r_ncs <= '1'; + r_oe <= c_oe_default; + + when S_4ADDR => + r_shift_o <= f_stripe(c_4addr); + r_ncs <= '0'; + r_oe <= c_oe_default; when S_LOWER_CS_WAIT => - r_shift_o <= (others => '-'); + r_shift_o <= c_idle; r_ncs <= '1'; + r_oe <= c_oe_default; when S_READ_STATUS => r_shift_o <= f_stripe(c_read_status); r_ncs <= '0'; + r_oe <= c_oe_default; when S_LOAD_STATUS => - r_shift_o <= (others => '-'); + r_shift_o <= c_idle; r_ncs <= '0'; + r_oe <= c_oe_default; when S_WAIT_READY => if s_wip = '0' then -- not busy - r_shift_o <= (others => '-'); + r_shift_o <= c_idle; r_ncs <= '1'; + r_oe <= c_oe_default; else - r_shift_o <= (others => '-'); + r_shift_o <= c_idle; r_ncs <= '0'; + r_oe <= c_oe_default; end if; end case; @@ -310,13 +396,18 @@ begin end process; -- bit position 0 ... and correct when g_input_to_output_cycles=1 - s_wip <= r_shift_i((g_input_to_output_cycles+7) mod 8); + wip1 : if g_port_width = 1 generate + s_wip <= r_shift_i((g_input_to_output_cycles+7) mod 8); + end generate; + wipx : if g_port_width /= 1 generate + s_wip <= r_shift_i(((33-g_input_to_output_cycles) mod 8) * g_port_width + 1); + end generate; main : process(clk_out_i, clk_out_rstn) is begin if clk_out_rstn = '0' then - r_state <= S_LOWER_CS_WAIT; - r_state_n <= S_LOWER_CS_WAIT; + r_state <= S_INIT; + r_state_n <= S_WAIT; r_count <= (others => '-'); r_stall <= '0'; r_stall_n <= '0'; @@ -359,6 +450,14 @@ begin r_ack_n <= '0'; end if; + when S_INIT => + r_count <= (others => '1'); + if g_addr_width > 24 then + r_state_n <= S_ENABLE_4ADDR; + else + r_state_n <= S_LOWER_CS_WAIT; + end if; + when S_DISPATCH => r_count <= (others => '-'); r_state_n <= S_ERROR; @@ -405,7 +504,7 @@ begin r_adr <= f_increment(r_adr); when S_READ_DUMMY => - r_count <= c_cmd_time; + r_count <= to_unsigned(g_dummy_time-1, t_count'length); r_state_n <= S_READ_DATA; when S_READ_DATA => @@ -469,12 +568,40 @@ begin when S_ERASE => r_count <= c_cmd_time; - r_state_n <= S_ERASE_ADDR; + r_state_n <= S_ERASE_ADDR3; + r_ack_n <= '1'; - when S_ERASE_ADDR => - r_count <= c_addr_time; + when S_ERASE_ADDR3 => + r_count <= c_cmd_time; + r_state_n <= S_ERASE_ADDR2; + + when S_ERASE_ADDR2 => + r_count <= c_cmd_time; + r_state_n <= S_ERASE_ADDR1; + + when S_ERASE_ADDR1 => + r_count <= c_cmd_time; + if g_addr_width > 24 then + r_state_n <= S_ERASE_ADDR0; + else + r_state_n <= S_LOWER_CS_WAIT; + end if; + + when S_ERASE_ADDR0 => + r_count <= c_cmd_time; r_state_n <= S_LOWER_CS_WAIT; - r_ack_n <= '1'; + + when S_ENABLE_4ADDR => + r_count <= c_cmd_time; + r_state_n <= S_LOWER_CS_4ADDR; + + when S_LOWER_CS_4ADDR => + r_count <= c_low_time; + r_state_n <= S_4ADDR; + + when S_4ADDR => + r_count <= c_cmd_time; + r_state_n <= S_LOWER_CS_WAIT; when S_LOWER_CS_WAIT => r_count <= c_low_time; @@ -489,6 +616,14 @@ begin r_state_n <= S_WAIT_READY; when S_WAIT_READY => + -- Allow polling the magic register to detect busy + if master_o.cyc = '1' and master_o.stb = '1' and master_o.we = '0' and + unsigned(master_o.adr(t_address'range)) = c_magic_reg then + r_dat <= (others => '0'); + r_stall <= '0'; + r_ack_n <= '1'; + end if; + if s_wip = '0' then -- not busy r_count <= c_low_time; r_state_n <= S_DISPATCH; diff --git a/modules/wishbone/wishbone_pkg.vhd b/modules/wishbone/wishbone_pkg.vhd index 1861481b12df63a41c70ef987ec8debd2d10a581..591b245ee2c7d42107bcebc69c973d08b389c1b0 100644 --- a/modules/wishbone/wishbone_pkg.vhd +++ b/modules/wishbone/wishbone_pkg.vhd @@ -849,26 +849,13 @@ package wishbone_pkg is di_dat_o : out std_logic); end component; - constant c_wb_spi_flash_sdb : t_sdb_device := ( - abi_class => x"0000", -- undocumented device - abi_ver_major => x"01", - abi_ver_minor => x"00", - wbd_endian => c_sdb_endian_big, - wbd_width => x"7", -- 8/16/32-bit port granularity - sdb_component => ( - addr_first => x"0000000000000000", - addr_last => x"0000000000ffffff", - product => ( - vendor_id => x"0000000000000651", -- GSI - device_id => x"5cf12a1c", - version => x"00000001", - date => x"20130415", - name => "SPI-FLASH-16M-MMAP "))); + function f_wb_spi_flash_sdb(g_bits : natural) return t_sdb_device; component wb_spi_flash is generic( - g_port_width : natural := 1; -- 1 for EPCS, 4 for EPCQ - g_addr_width : natural := 24; -- 24 for EPCS, 32 for EPCQ + g_port_width : natural := 1; -- 1 for EPCS, 4 for EPCQ + g_addr_width : natural := 24; -- log of memory (24=16MB) g_idle_time : natural := 3; + g_dummy_time : natural := 8; -- leave these at defaults if you have: -- a) slow clock, b) valid constraints, or c) registered in/outputs g_input_latch_edge : std_logic := '1'; -- rising @@ -884,6 +871,7 @@ package wishbone_pkg is clk_out_i : in std_logic; clk_in_i : in std_logic; ncs_o : out std_logic; + oe_o : out std_logic_vector(g_port_width-1 downto 0); asdi_o : out std_logic_vector(g_port_width-1 downto 0); data_i : in std_logic_vector(g_port_width-1 downto 0); @@ -1294,4 +1282,25 @@ package body wishbone_pkg is return s; end f_slv2string; + function f_wb_spi_flash_sdb(g_bits : natural) return t_sdb_device is + variable result : t_sdb_device := ( + abi_class => x"0000", -- undocumented device + abi_ver_major => x"01", + abi_ver_minor => x"00", + wbd_endian => c_sdb_endian_big, + wbd_width => x"7", -- 8/16/32-bit port granularity + sdb_component => ( + addr_first => x"0000000000000000", + addr_last => x"0000000000ffffff", + product => ( + vendor_id => x"0000000000000651", -- GSI + device_id => x"5cf12a1c", + version => x"00000001", + date => x"20130415", + name => "SPI-FLASH-16M-MMAP "))); + begin + result.sdb_component.addr_last := std_logic_vector(to_unsigned(2**g_bits-1, 64)); + return result; + end f_wb_spi_flash_sdb; + end wishbone_pkg; diff --git a/platform/altera/flash/altera_flash_pkg.vhd b/platform/altera/flash/altera_flash_pkg.vhd index 28ad57cd4b9bbaf1c49b3632c5164aa8086627f7..70b23ff1bd504d8dd322214865afa0120209b6d4 100644 --- a/platform/altera/flash/altera_flash_pkg.vhd +++ b/platform/altera/flash/altera_flash_pkg.vhd @@ -12,6 +12,7 @@ package altera_flash_pkg is g_family : string; g_port_width : natural; g_addr_width : natural; + g_dummy_time : natural; g_input_latch_edge : std_logic; g_output_latch_edge : std_logic; g_input_to_output_cycles : natural); diff --git a/platform/altera/flash/altera_spi.vhd b/platform/altera/flash/altera_spi.vhd index eef92b5f8abf43eabbad116f94f5f547d10027b6..4cbe526ae1e8eca87db20b480947e056afdbf2b5 100644 --- a/platform/altera/flash/altera_spi.vhd +++ b/platform/altera/flash/altera_spi.vhd @@ -9,6 +9,7 @@ entity altera_spi is port( dclk_i : in std_logic; ncs_i : in std_logic; + oe_i : in std_logic_vector(g_port_width-1 downto 0); asdo_i : in std_logic_vector(g_port_width-1 downto 0); data_o : out std_logic_vector(g_port_width-1 downto 0)); end entity; @@ -186,12 +187,12 @@ begin data_o <= data(data_o'range); width1 : if g_port_width = 1 generate - oe <= (0 => '0', others => '1'); - asdo <= (0 => asdo_i(0), others => '-'); + oe <= (0 => '1', 1 => '0', others => '1'); + asdo <= (0 => asdo_i(0), others => '1'); end generate; width4 : if g_port_width = 4 generate - oe <= (others => '0'); + oe <= oe_i; asdo <= asdo_i; end generate; @@ -202,7 +203,7 @@ begin scein => ncs_i, sdoin => asdo(0), data0out => data(0), - oe => oe(0)); + oe => '0'); end generate; cycloneii : if c_block = T_CYCLONEII generate @@ -212,7 +213,7 @@ begin scein => ncs_i, sdoin => asdo(0), data0out => data(0), - oe => oe(0)); + oe => '0'); end generate; stratixii : if c_block = T_STRATIXII generate @@ -222,7 +223,7 @@ begin scein => ncs_i, sdoin => asdo(0), data0out => data(0), - oe => oe(0)); + oe => '0'); end generate; stratixiii : if c_block = T_STRATIXIII generate @@ -232,7 +233,7 @@ begin scein => ncs_i, sdoin => asdo(0), data0out => data(0), - oe => oe(0)); + oe => '0'); end generate; stratixiv : if c_block = T_STRATIXIV generate @@ -242,7 +243,7 @@ begin scein => ncs_i, sdoin => asdo(0), data0out => data(0), - oe => oe(0)); + oe => '0'); end generate; stratixv : if c_block = T_STRATIXV generate @@ -250,7 +251,7 @@ begin port map( dclk => dclk_i, sce => ncs_i, - oe => oe(0), + oe => '0', data0out => asdo(0), data1out => asdo(1), data2out => asdo(2), @@ -270,7 +271,7 @@ begin port map( dclk => dclk_i, sce => ncs_i, - oe => oe(0), + oe => '0', data0out => asdo(0), data1out => asdo(1), data2out => asdo(2), @@ -290,7 +291,7 @@ begin port map( dclk => dclk_i, sce => ncs_i, - oe => oe(0), + oe => '0', data0out => asdo(0), data1out => asdo(1), data2out => asdo(2), diff --git a/platform/altera/flash/flash_top.vhd b/platform/altera/flash/flash_top.vhd index 8dbc871797e3d9610b6dd0fc01ed8ae0d8358830..63f44f5bd99328cc341f570623e7cfbcfba973ad 100644 --- a/platform/altera/flash/flash_top.vhd +++ b/platform/altera/flash/flash_top.vhd @@ -9,6 +9,7 @@ entity flash_top is g_family : string; g_port_width : natural; g_addr_width : natural; + g_dummy_time : natural; g_input_latch_edge : std_logic; g_output_latch_edge : std_logic; g_input_to_output_cycles : natural); @@ -32,11 +33,13 @@ architecture rtl of flash_top is port( dclk_i : in std_logic; ncs_i : in std_logic; + oe_i : in std_logic_vector(g_port_width-1 downto 0); asdo_i : in std_logic_vector(g_port_width-1 downto 0); data_o : out std_logic_vector(g_port_width-1 downto 0)); end component; signal flash_ncs : std_logic; + signal flash_oe : std_logic_vector(g_port_width-1 downto 0); signal flash_asdo : std_logic_vector(g_port_width-1 downto 0); signal flash_data : std_logic_vector(g_port_width-1 downto 0); @@ -47,6 +50,7 @@ begin g_port_width => g_port_width, g_addr_width => g_addr_width, g_idle_time => 3, + g_dummy_time => g_dummy_time, g_input_latch_edge => g_input_latch_edge, g_output_latch_edge => g_output_latch_edge, g_input_to_output_cycles => g_input_to_output_cycles) @@ -58,6 +62,7 @@ begin clk_out_i => clk_out_i, clk_in_i => clk_in_i, ncs_o => flash_ncs, + oe_o => flash_oe, asdi_o => flash_asdo, data_i => flash_data, external_request_i => '0', @@ -70,6 +75,7 @@ begin port map( dclk_i => clk_out_i, ncs_i => flash_ncs, + oe_i => flash_oe, asdo_i => flash_asdo, data_o => flash_data);