From: rs <> Date: Tue, 11 Nov 2025 21:48:15 +0000 (-0600) Subject: Add USB mux to support STM and EPP together X-Git-Url: https://git.the-white-hart.net/?a=commitdiff_plain;h=5cdeed2185e843f42977c4de2c8ec6ecd51c02e7;p=vhdl Add USB mux to support STM and EPP together --- diff --git a/libraries/nexys2/eppex_wb.vhd b/libraries/nexys2/eppex_wb.vhd new file mode 100644 index 0000000..f8a5d9d --- /dev/null +++ b/libraries/nexys2/eppex_wb.vhd @@ -0,0 +1,211 @@ +-------------------------------------------------------------------------------- +-- epp_wb - Digilent EPP to Wishbone bridge +-- +-- Connects to a Digilent EPP bus (one of Digilent's USB interface methods) and +-- converts EPP transactions into Wishbone bus transactions. +-------------------------------------------------------------------------------- +-- WISHBONE DATASHEET +-- +-- Wishbone specification used: Rev B.3 +-- Interface type: master +-- Port size: 8-bit +-- Operand sizes: 8-bit +-- Endianness: undefined (port size same as granularity) +-- Data transfer sequence: undefined +-- Clock constraints: none +-- Signals: +-- * rst_i +-- * clk_i +-- * cyc_o +-- * stb_o +-- * we_o +-- * ack_i +-- * adr_o (8-bit) +-- * dat_i (8-bit) +-- * dat_o (8-bit) +-------------------------------------------------------------------------------- + +library ieee; +use ieee.std_logic_1164.all; + + +entity eppex_wb is + port ( + -- Epp-bus signals + -- Assumed to be externally-registered, prefereably by clock domain crossing from DstmIFCLK to clk_50 + EppEN: in std_logic; + EppAstb: in std_logic; + EppDstb: in std_logic; + EppWr: in std_logic; + EppDB_i: in std_logic_vector(7 downto 0); + EppDB_o: out std_logic_vector(7 downto 0); + EppDB_w: out std_logic; -- '1' when EppDB_o should be put on the bus + EppWait: out std_logic; + + -- Wishbone bus signals + rst_i: in std_logic; + clk_i: in std_logic; + cyc_o: out std_logic; + stb_o: out std_logic; + we_o: out std_logic; + ack_i: in std_logic; + adr_o: out std_logic_vector(7 downto 0); + dat_i: in std_logic_vector(7 downto 0); + dat_o: out std_logic_vector(7 downto 0) + ); +end eppex_wb; + + +architecture behavioral of eppex_wb is + + type state_t is ( + S_READY, + S_ADDR, + S_DATA, + S_DONE + ); + + -- Registered epp bus signals + signal epp_data_out_reg: std_logic_vector(7 downto 0); + + signal epp_data_out_latch: std_logic; + signal epp_data_out_en: std_logic; + + -- Internal address register + signal addr_latch: std_logic; + signal addr_reg: std_logic_vector(7 downto 0); + + -- Registered wishbone bus signals + signal next_stb: std_logic; + signal stb_reg: std_logic; + signal next_we: std_logic; + signal we_reg: std_logic; + + -- State machine + signal cur_state_reg: state_t; + signal next_state: state_t; + +begin + + -- Interface output should be in tristate until data is available for EPP to read + --EppDB <= epp_data_out_reg when epp_data_out_en = '1' else (others => 'Z'); + EppDB_o <= epp_data_out_reg; + EppDB_w <= epp_data_out_en; + + -- Output registered wishbone signals + cyc_o <= stb_reg; + stb_o <= stb_reg; + we_o <= we_reg; + + -- Always present the latched EPP data bus and latched address to the wishbone bus + dat_o <= EppDB_i; -- epp_data_in_reg; + adr_o <= addr_reg; + + -- State register + p_regs: process (clk_i, rst_i, next_state, EppEN) + begin + if rising_edge(clk_i) then + if rst_i = '1' or EppEN = '0' then + cur_state_reg <= S_READY; + else + cur_state_reg <= next_state; + end if; + end if; + end process; + + -- Register wishbone outputs + p_wb_reg: process (clk_i, rst_i, next_stb, next_we) + begin + if rising_edge(clk_i) then + if rst_i = '1' then + stb_reg <= '0'; + we_reg <= '0'; + else + stb_reg <= next_stb; + we_reg <= next_we; + end if; + end if; + end process; + + -- Address register + p_addr: process (clk_i, addr_latch, EppDB_i) + begin + if rising_edge(clk_i) and addr_latch = '1' then + addr_reg <= EppDB_i; + end if; + end process; + + -- Data register from wishbone bus to epp bus + p_data: process (clk_i, epp_data_out_latch, dat_i) + begin + if rising_edge(clk_i) and epp_data_out_latch = '1' then + epp_data_out_reg <= dat_i; + end if; + end process; + + -- State machine transitions + p_fsm: process(cur_state_reg, + EppEN, EppAstb, EppDstb, EppWR, + ack_i) + begin + -- Default to remaining in same state + next_state <= cur_state_reg; + + -- Default to not starting a wishbone cycle + next_stb <= '0'; + next_we <= '0'; + + -- Default to not latching or outputting anything + addr_latch <= '0'; + epp_data_out_latch <= '0'; + epp_data_out_en <= '0'; + + -- Default to telling EPP-side to wait + EppWait <= '0'; + + case cur_state_reg is + when S_READY => + -- Leave state when a strobe is asserted + -- Check EppEN as well to prevent false strobes + if EppAstb = '0' and EppEN = '1' then + next_state <= S_ADDR; + elsif EppDstb = '0' and EppEN = '1' then + next_stb <= '1'; + next_we <= not EppWR; + next_state <= S_DATA; + end if; + + when S_DATA => + -- Ack may be combinational, so don't register it + if ack_i = '1' then + -- Latch data from wishbone, as EPP may take more time to read + epp_data_out_latch <= '1'; + next_state <= S_DONE; + else + -- Continue asserting bus signals next cycle if not acknowledged + next_stb <= '1'; + next_we <= not EppWR; + end if; + + when S_ADDR => + addr_latch <= '1'; + next_state <= S_DONE; -- No stretcing will occur for address access + + when S_DONE => + -- Indicate completion + EppWait <= '1'; + + -- If a read is being performed, take the EPP bus out of tristate + epp_data_out_en <= EppWR; + + -- Wait for strobes to be deasserted before returning to READY state + if EppAstb = '1' and EppDstb = '1' then + next_state <= S_READY; + end if; + + when others => + next_state <= S_READY; + end case; + end process; + +end behavioral; diff --git a/libraries/nexys2/tests/nexys2_usb.vhd b/libraries/nexys2/tests/nexys2_usb.vhd new file mode 100644 index 0000000..2d1c22c --- /dev/null +++ b/libraries/nexys2/tests/nexys2_usb.vhd @@ -0,0 +1,125 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library work; + + +entity nexys2_usb is + port ( + clk_50: in std_logic; + + EppDB_DstmDB: inout std_logic_vector(7 downto 0); + EppWRITE: in std_logic; + EppASTB_DstmFLAGA: in std_logic; + EppDSTB_DstmFLAGB: in std_logic; + EppWAIT_DstmSLRD: out std_logic; + DstmIFCLK: in std_logic; + DstmSLCS: in std_logic; + DstmADR: out std_logic_vector(1 downto 0); + DstmSLWR: out std_logic; + DstmSLOE: out std_logic; + DstmPKTEND: out std_logic; + UsbMode: in std_logic; + UsbRdy: in std_logic; + + seg: out std_logic_vector(6 downto 0); + dp: out std_logic; + an: out std_logic_vector(3 downto 0); + Led: out std_logic_vector(7 downto 0); + sw: in std_logic_vector(7 downto 0) + ); +end nexys2_usb; + + +architecture behavioral of nexys2_usb is + + signal rst: std_logic; + + signal wb_cyc: std_logic; + signal wb_stb: std_logic; + signal wb_we: std_logic; + signal wb_ack: std_logic; + signal wb_adr: std_logic_vector(7 downto 0); + signal wb_miso: std_logic_vector(7 downto 0); + signal wb_mosi: std_logic_vector(7 downto 0); + + signal stm_stb: std_logic; + signal stm_ack: std_logic; + signal stm_dat: std_logic_vector(7 downto 0); + +begin + + e_por: entity utility.power_on_reset_opt + port map ( + clk_i => clk_50, + rst_o => rst + ); + + e_usb: entity work.usb_cypress + port map ( + EppDB_DstmDB => EppDB_DstmDB, + EppWRITE => EppWRITE, + EppASTB_DstmFLAGA => EppASTB_DstmFLAGA, + EppDSTB_DstmFLAGB => EppDSTB_DstmFLAGB, + EppWAIT_DstmSLRD => EppWAIT_DstmSLRD, + DstmIFCLK => DstmIFCLK, + DstmSLCS => DstmSLCS, + DstmADR => DstmADR, + DstmSLWR => DstmSLWR, + DstmSLOE => DstmSLOE, + DstmPKTEND => DstmPKTEND, + UsbMode => UsbMode, + UsbRdy => UsbRdy, + + epp_rst_i => rst, + epp_clk_i => clk_50, + epp_cyc_o => wb_cyc, + epp_stb_o => wb_stb, + epp_we_o => wb_we, + epp_ack_i => wb_ack, + epp_adr_o => wb_adr, + epp_dat_i => wb_miso, + epp_dat_o => wb_mosi, + + stm_rst_i => rst, + stm_clk_i => clk_50, + stm_dl_stb_o => stm_stb, + stm_dl_ack_i => stm_ack, + stm_dl_dat_o => stm_dat, + stm_ul_stb_i => stm_stb, + stm_ul_ack_o => stm_ack, + stm_ul_dat_i => stm_dat + ); + + e_regs: entity work.host_regs_opt + port map ( + rst_i => rst, + clk_i => clk_50, + + h_cyc_i => wb_cyc, + h_stb_i => wb_stb, + h_we_i => wb_we, + h_ack_o => wb_ack, + h_adr_i => wb_adr(2 downto 0), + h_dat_i => wb_mosi, + h_dat_o => wb_miso, + + d_cyc_i => '0', + d_stb_i => '0', + d_we_i => '0', + d_ack_o => open, + d_adr_i => "000", + d_dat_i => x"00", + d_dat_o => open, + + ctrl => open, + flags => open, + seg => seg, + dp => dp, + an => an, + Led => Led, + sw => sw + ); + +end behavioral; diff --git a/libraries/nexys2/tests/program.sh b/libraries/nexys2/tests/program.sh index 6d2fed4..e185d2a 100755 --- a/libraries/nexys2/tests/program.sh +++ b/libraries/nexys2/tests/program.sh @@ -1,4 +1,7 @@ #!/usr/bin/env bash -djtgcfg -d Nexys2 -i 0 -f test_nexys2_mem_wb8_0.bit prog +#djtgcfg -d Nexys2 -i 0 -f test_nexys2_mem_wb8_0.bit prog +#djtgcfg -d Nexys2 -i 0 -f StreamIOvhd.bit prog +#djtgcfg -d Nexys2 -i 0 -f nexys2_stm.bit prog +djtgcfg -d Nexys2 -i 0 -f nexys2_usb.bit prog diff --git a/libraries/nexys2/usb.vhd b/libraries/nexys2/usb.vhd new file mode 100644 index 0000000..5ae0966 --- /dev/null +++ b/libraries/nexys2/usb.vhd @@ -0,0 +1,258 @@ +library ieee; +use ieee.std_logic_1164.all; + +library utility; + +library work; + + +entity usb_cypress is + port ( + -- Nexys2 pins + EppDB_DstmDB: inout std_logic_vector(7 downto 0); + EppWRITE: in std_logic; + EppASTB_DstmFLAGA: in std_logic; + EppDSTB_DstmFLAGB: in std_logic; + EppWAIT_DstmSLRD: out std_logic; + DstmIFCLK: in std_logic; + DstmSLCS: in std_logic; + DstmADR: out std_logic_vector(1 downto 0); + DstmSLWR: out std_logic; + DstmSLOE: out std_logic; + DstmPKTEND: out std_logic; + UsbMode: in std_logic; + UsbRdy: in std_logic; + + -- EPP-Wishbone interface + epp_rst_i: in std_logic; + epp_clk_i: in std_logic; + + epp_cyc_o: out std_logic; + epp_stb_o: out std_logic; + epp_we_o: out std_logic; + epp_ack_i: in std_logic; + epp_adr_o: out std_logic_vector(7 downto 0); + epp_dat_i: in std_logic_vector(7 downto 0); + epp_dat_o: out std_logic_vector(7 downto 0); + + -- STM-Pipeline interfaces + stm_rst_i: in std_logic; + stm_clk_i: in std_logic; + + stm_dl_stb_o: out std_logic; + stm_dl_ack_i: in std_logic; + stm_dl_dat_o: out std_logic_vector(7 downto 0); + + stm_ul_stb_i: in std_logic; + stm_ul_ack_o: out std_logic; + stm_ul_dat_i: in std_logic_vector(7 downto 0) + ); +end usb_cypress; + + +architecture behavioral of usb_cypress is + + -- Between USB mux and EPP interface, in DstmIFCLK domain + signal epp_en: std_logic; + signal epp_astb: std_logic; + signal epp_dstb: std_logic; + signal epp_write: std_logic; + signal epp_db_i: std_logic_vector(7 downto 0); + signal epp_db_o: std_logic_vector(7 downto 0); + signal epp_db_w: std_logic; + signal epp_wait: std_logic; + + -- Between USB mux and EPP interface, in epp_clk_i domain + signal xclk_eppen: std_logic; + signal xclk_astb: std_logic; + signal xclk_dstb: std_logic; + signal xclk_write: std_logic; + signal xclk_db_i: std_logic_vector(7 downto 0); + signal xclk_db_o: std_logic_vector(7 downto 0); + signal xclk_db_w: std_logic; + signal xclk_wait: std_logic; + + -- Between USB mux and STM interface + signal dstm_ifclk: std_logic; + signal dstm_slcs: std_logic; + signal dstm_flaga: std_logic; + signal dstm_flagb: std_logic; + signal dstm_adr: std_logic_vector(1 downto 0); + signal dstm_slrd: std_logic; + signal dstm_slwr: std_logic; + signal dstm_sloe: std_logic; + signal dstm_pktend: std_logic; + signal dstm_db_i: std_logic_vector(7 downto 0); + signal dstm_db_o: std_logic_vector(7 downto 0); + + -- Between STM interface and FIFOs + signal dl_stb: std_logic; + signal dl_rdy: std_logic; + signal dl_dat: std_logic_vector(7 downto 0); + signal dl_end: std_logic; + + signal ul_stb: std_logic; + signal ul_ack: std_logic; + signal ul_dat: std_logic_vector(7 downto 0); + signal ul_end: std_logic; + +begin + + ------------------------------------------------------------------------ + -- Sharing single USB controller interface between EPP and STM + + e_mux: entity work.usb_mux + port map ( + EppDB_DstmDB => EppDB_DstmDB, + EppWRITE => EppWRITE, + EppASTB_DstmFLAGA => EppASTB_DstmFLAGA, + EppDSTB_DstmFLAGB => EppDSTB_DstmFLAGB, + EppWAIT_DstmSLRD => EppWAIT_DstmSLRD, + DstmIFCLK => DstmIFCLK, + DstmSLCS => DstmSLCS, + DstmADR => DstmADR, + DstmSLWR => DstmSLWR, + DstmSLOE => DstmSLOE, + DstmPKTEND => DstmPKTEND, + UsbMode => UsbMode, + UsbRdy => UsbRdy, + + s0_EppEN => epp_en, + s0_EppASTB => epp_astb, + s0_EppDSTB => epp_dstb, + s0_EppWRITE => epp_write, + s0_EppDB_i => epp_db_i, + s0_EppDB_o => epp_db_o, + s0_EppDB_w => epp_db_w, + s0_EppWAIT => epp_wait, + + s1_DstmIFCLK => dstm_ifclk, + s1_DstmSLCS => dstm_slcs, + s1_DstmFLAGA => dstm_flaga, + s1_DstmFLAGB => dstm_flagb, + s1_DstmADR => dstm_adr, + s1_DstmSLRD => dstm_slrd, + s1_DstmSLWR => dstm_slwr, + s1_DstmSLOE => dstm_sloe, + s1_DstmPKTEND => dstm_pktend, + s1_DstmDB_i => dstm_db_i, + s1_DstmDB_o => dstm_db_o + ); + + ------------------------------------------------------------------------ + -- EPP Interface + + -- EPP clock domain crossing + -- Both ends are handshaken, but this end has fewer signals to cross + -- FIXME: it should only be necessary to synchronize the handshaking signals + e_xclk_eppen: entity utility.xclk_sig + generic map (INIT => '0') + port map (a_sig_i => epp_en, b_clk_i => epp_clk_i, b_sig_o => xclk_eppen); + + e_xclk_astb: entity utility.xclk_sig + generic map (INIT => '1') + port map (a_sig_i => epp_astb, b_clk_i => epp_clk_i, b_sig_o => xclk_astb); + + e_xclk_dstb: entity utility.xclk_sig + generic map (INIT => '1') + port map (a_sig_i => epp_dstb, b_clk_i => epp_clk_i, b_sig_o => xclk_dstb); + + e_xclk_write: entity utility.xclk_sig + generic map (INIT => '1') + port map (a_sig_i => epp_write, b_clk_i => epp_clk_i, b_sig_o => xclk_write); + + e_xclk_db_i: entity utility.xclk_vec + port map (a_sig_i => epp_db_i, b_clk_i => epp_clk_i, b_sig_o => xclk_db_i); + + e_xclk_db_o: entity utility.xclk_vec + port map (a_sig_i => xclk_db_o, b_clk_i => DstmIFCLK, b_sig_o => epp_db_o); + + e_xclk_db_w: entity utility.xclk_sig + generic map (INIT => '0') + port map (a_sig_i => xclk_db_w, b_clk_i => DstmIFCLK, b_sig_o => epp_db_w); + + e_xclk_wait: entity utility.xclk_sig + generic map (INIT => '0') -- FIXME: is this the best initial value for WAIT? + port map (a_sig_i => xclk_wait, b_clk_i => DstmIFCLK, b_sig_o => epp_wait); + + -- EPP interface logic + e_epp: entity work.eppex_wb + port map ( + EppEN => xclk_eppen, + EppAstb => xclk_astb, + EppDstb => xclk_dstb, + EppWr => xclk_write, + EppDB_i => xclk_db_i, + EppDB_o => xclk_db_o, + EppDB_w => xclk_db_w, + EppWait => xclk_wait, + + rst_i => epp_rst_i, + clk_i => epp_clk_i, + cyc_o => epp_cyc_o, + stb_o => epp_stb_o, + we_o => epp_we_o, + ack_i => epp_ack_i, + adr_o => epp_adr_o, + dat_i => epp_dat_i, + dat_o => epp_dat_o + ); + + ------------------------------------------------------------------------ + -- STM Interface + + -- STM interface logic + e_stm: entity work.stmex_wb + port map ( + ifclk => dstm_ifclk, + stmen => dstm_slcs, + db_i => dstm_db_i, + db_o => dstm_db_o, + flaga => dstm_flaga, + flagb => dstm_flagb, + fifoadr => dstm_adr, + slrd => dstm_slrd, + slwr => dstm_slwr, + sloe => dstm_sloe, + pktend => dstm_pktend, + + dl_stb_o => dl_stb, + dl_rdy_i => dl_rdy, + dl_dat_o => dl_dat, + dl_end_o => dl_end, + + ul_stb_i => ul_stb, + ul_ack_o => ul_ack, + ul_dat_i => ul_dat, + ul_end_i => ul_end + ); + + -- STM clock domain crossing, download side + e_dl_fifo: entity utility.fifo_xclk + port map ( + head_clk_i => dstm_ifclk, + head_stb_i => dl_stb, + head_rdy_o => dl_rdy, + head_dat_i => dl_dat, + + tail_clk_i => stm_clk_i, + tail_stb_o => stm_dl_stb_o, + tail_ack_i => stm_dl_ack_i, + tail_dat_o => stm_dl_dat_o + ); + + -- STM clock domain crossing, upload side + e_ul_fifo: entity utility.fifo_xclk + port map ( + head_clk_i => stm_clk_i, + head_stb_i => stm_ul_stb_i, + head_rdy_o => stm_ul_ack_o, + head_dat_i => stm_ul_dat_i, + + tail_clk_i => dstm_ifclk, + tail_stb_o => ul_stb, + tail_ack_i => ul_ack, + tail_dat_o => ul_dat + ); + +end behavioral; diff --git a/libraries/nexys2/usb_mux.vhd b/libraries/nexys2/usb_mux.vhd new file mode 100644 index 0000000..909a248 --- /dev/null +++ b/libraries/nexys2/usb_mux.vhd @@ -0,0 +1,80 @@ +library ieee; +use ieee.std_logic_1164.all; + + +entity usb_mux is + port ( + -- Nexys2 pins + EppDB_DstmDB: inout std_logic_vector(7 downto 0); + EppWRITE: in std_logic; + EppASTB_DstmFLAGA: in std_logic; + EppDSTB_DstmFLAGB: in std_logic; + EppWAIT_DstmSLRD: out std_logic; + DstmIFCLK: in std_logic; + DstmSLCS: in std_logic; + DstmADR: out std_logic_vector(1 downto 0); + DstmSLWR: out std_logic; + DstmSLOE: out std_logic; + DstmPKTEND: out std_logic; + UsbMode: in std_logic; + UsbRdy: in std_logic; + + -- EPP interface (sel_i = '0') + s0_EppEN: out std_logic; + s0_EppASTB: out std_logic; + s0_EppDSTB: out std_logic; + s0_EppWRITE: out std_logic; + s0_EppDB_i: out std_logic_vector(7 downto 0); -- Inbound: from host to device + s0_EppDB_o: in std_logic_vector(7 downto 0); -- Outbound: from device to host + s0_EppDB_w: in std_logic; -- '1' when EppDB_o should be on the + s0_EppWAIT: in std_logic; + + -- STM interface (sel_i = '1') + s1_DstmIFCLK: out std_logic; + s1_DstmSLCS: out std_logic; + s1_DstmFLAGA: out std_logic; + s1_DstmFLAGB: out std_logic; + s1_DstmADR: in std_logic_vector(1 downto 0); + s1_DstmSLRD: in std_logic; + s1_DstmSLWR: in std_logic; + s1_DstmSLOE: in std_logic; + s1_DstmPKTEND: in std_logic; + s1_DstmDB_i: out std_logic_vector(7 downto 0); + s1_DstmDB_o: in std_logic_vector(7 downto 0) + ); +end usb_mux; + + +architecture behavioral of usb_mux is + + signal DB_o: std_logic_vector(7 downto 0); + signal DB_w: std_logic; + +begin + + -- Outputs to Nexys2 board + DstmADR <= s1_DstmADR when UsbRdy = '1' else (others => 'Z'); + DstmSLWR <= s1_DstmSLWR when UsbRdy = '1' else 'Z'; + DstmSLOE <= s1_DstmSLOE when UsbRdy = '1' else 'Z'; + DstmPKTEND <= s1_DstmPKTEND when UsbRdy = '1' else 'Z'; + EppWAIT_DstmSLRD <= 'Z' when UsbRdy = '0' else s0_EppWAIT when DstmSLCS = '0' else s1_DstmSLRD; + DB_o <= s0_EppDB_o when DstmSLCS = '0' else s1_DstmDB_o; + DB_w <= s0_EppDB_w when DstmSLCS = '0' else s1_DstmSLOE; + + EppDB_DstmDB <= (others => 'Z') when UsbRdy = '0' else DB_o when DB_w = '1' else (others => 'Z'); + + -- Outputs to EPP interface + s0_EppEN <= UsbMode; + s0_EppASTB <= EppASTB_DstmFLAGA; + s0_EppDSTB <= EppDSTB_DstmFLAGB; + s0_EppWRITE <= EppWRITE; + s0_EppDB_i <= EppDB_DstmDB; + + -- Outputs to STM interface + s1_DstmIFCLK <= DstmIFCLK; + s1_DstmSLCS <= DstmSLCS; + s1_DstmFLAGA <= EppASTB_DstmFLAGA; + s1_DstmFLAGB <= EppDSTB_DstmFLAGB; + s1_DstmDB_i <= EppDB_DstmDB; + +end behavioral;