---------------------------------------------------------------------------- -- usbgen.vhd -- Flexible Generator for the USB 3.x Plugin Module -- Version 1.0 -- -- Copyright (C) 2019 H.Poetzl -- -- This program is free software: you can redistribute it and/or -- modify it under the terms of the GNU General Public License -- as published by the Free Software Foundation, either version -- 2 of the License, or (at your option) any later version. ---------------------------------------------------------------------------- library IEEE; use IEEE.std_logic_1164.ALL; use IEEE.numeric_std.ALL; library machxo2; use machxo2.components.ALL; /* JTAG ER1 Control Registers: (0) ... Counter Control [ 3:0 ] ... Counter Enable [11:8 ] ... Counter Reset (1) ... Data Control [ 3:0 ] ... Data Enable (2) ... FTDI Pin Control [ 3:0 ] ... Byte Enable (BE) [ 9:8 ] ... GPIO1, GPIO0 [16] ...... #OE [17] ...... #RD [18] ...... #WR [20] ...... #SIWU [24] ...... #RESET [28] ...... #WAKEUP (3) ... LED Control [ 2:0 ] ... Led Mode 000 ... Always Off 001 ... #TXE 010 ... #RXF 011 ... #WR 100 ... #RD 101 ... cnt(3)(24) 110 ... cnt(0)(24) 111 ... Led On (4) ... Interval Control [15:0 ] ... Total [31:16] ... Pause (5) ... Cycle Control [15:0 ] ... Total [16] ...... Enable [20] ...... Reset (6) ... (7) ... Config Update */ entity top is port ( clk : in std_logic; -- led : out std_logic; -- oe_n : out std_logic; rd_n : out std_logic; wr_n : out std_logic; siwu_n : out std_logic; -- rxf_n : in std_logic; txe_n : in std_logic; -- reset_n : out std_logic; wakeup_n : inout std_logic; -- gpio : inout std_logic_vector(1 downto 0); data : inout std_logic_vector(31 downto 0); be : inout std_logic_vector(3 downto 0); -- lvds : inout std_logic_vector(5 downto 0) ); end entity top; architecture RTL of top is function to_index ( val : std_logic_vector ) return natural is begin return to_integer(unsigned(val)); end function; function to_index ( val : std_logic_vector; len : natural ) return natural is constant high_c : natural := val'low + len - 1; begin assert high_c <= val'high report "to_index: vector to small" severity ERROR; return to_integer(unsigned(val(high_c downto val'low))); end function; function to_addr ( val, len : natural ) return std_logic_vector is begin return std_logic_vector(to_unsigned(val, len)); end function; /* LED signals */ signal led_o : std_logic; signal led_t : std_logic; signal led_sig : std_logic_vector(0 to 7); /* JTAG signals */ signal jtdi : std_logic; signal jtck : std_logic; signal jshift : std_logic; signal jrstn : std_logic; signal jupdate : std_logic; signal jce : std_logic_vector(2 downto 1); signal jtdo : std_logic_vector(2 downto 1); signal jrti : std_logic_vector(2 downto 1); /* ER1 constants */ constant ER1_DATA_WIDTH : integer := 32; constant ER1_ADDR_WIDTH : integer := 3; constant ER1_WIDTH : integer := ER1_DATA_WIDTH + 1 + ER1_ADDR_WIDTH; constant ER1_ADDR_SIZE : integer := 2**ER1_ADDR_WIDTH; /* jtag clock domain */ signal er1_sample : std_logic; signal er1_sample_tgl : std_logic := '0'; signal er1_update : std_logic; signal er1_update_tgl : std_logic := '0'; /* shared data registers */ type reg_array_t is array (integer range <>) of std_logic_vector(ER1_DATA_WIDTH-1 downto 0); signal er1_reg : reg_array_t(0 to ER1_ADDR_SIZE-1) := (others => (others => '0')); signal er1_din : std_logic_vector(ER1_WIDTH-1 downto 0); signal er1_dout : std_logic_vector(ER1_WIDTH-1 downto 0); alias er1_addr : std_logic_vector(ER1_ADDR_WIDTH-1 downto 0) is er1_dout(ER1_ADDR_WIDTH-1 downto 0); alias er1_flag : std_logic is er1_dout(ER1_ADDR_WIDTH); alias er1_data : std_logic_vector(ER1_DATA_WIDTH-1 downto 0) is er1_dout(ER1_WIDTH-1 downto ER1_ADDR_WIDTH+1); alias er1_read : std_logic_vector(ER1_DATA_WIDTH-1 downto 0) is er1_din(ER1_WIDTH-1 downto ER1_ADDR_WIDTH+1); /* high speed domain */ type ctrl_array_t is array (integer range <>) of std_logic_vector(ER1_DATA_WIDTH-1 downto 0); signal ctrl : ctrl_array_t(0 to ER1_ADDR_SIZE-1) := (others => (others => '0')); signal er1_ctrl : ctrl_array_t(0 to ER1_ADDR_SIZE-1) := (others => (others => '0')); signal er1_sample_sync : std_logic_vector(2 downto 0) := (others => '0'); signal er1_update_sync : std_logic_vector(2 downto 0) := (others => '0'); signal er1_sample_req : std_logic; signal er1_update_req : std_logic; /* FTDI data signals */ constant DAT_WIDTH : integer := 32; type dat_array_t is array (integer range <>) of unsigned(DAT_WIDTH-1 downto 0); signal dat : dat_array_t(0 to 7) := (others => (others => '0')); signal dat_en : std_logic_vector(3 downto 0); signal dat_rst : std_logic_vector(3 downto 0); /* FTDI stat counter signals */ constant CNT_WIDTH : integer := 64; type cnt_array_t is array (integer range <>) of unsigned(CNT_WIDTH-1 downto 0); signal cnt : cnt_array_t(0 to 3) := (others => (others => '0')); signal cnt_en : std_logic_vector(3 downto 0); signal cnt_cond : std_logic_vector(3 downto 0); signal cnt_en_d : std_logic_vector(3 downto 0); signal cnt_rst : std_logic_vector(3 downto 0); /* Interval and Cycle signals */ signal cyc_en : std_logic; signal cyc_rst : std_logic; signal int_over : std_logic := '0'; signal wr_en : std_logic; signal wr_en_d : std_logic; begin be <= ctrl(2)(3 downto 0); gpio <= ctrl(2)(9 downto 8); oe_n <= not ctrl(2)(16); rd_n <= not ctrl(2)(17); wr_n <= not wr_en_d; siwu_n <= '0' when ctrl(2)(20) else 'Z'; reset_n <= '0' when ctrl(2)(24) else 'Z'; wakeup_n <= '0' when ctrl(2)(28) else 'Z'; OBZ_inst : OBZ port map ( I => led_o, T => led_t, O => led ); JTAGF_inst: JTAGF generic map ( ER1 => "ENABLED", ER2 => "ENABLED" ) port map ( TCK => '0', TMS => '0', TDI => '0', TDO => open, -- JTDI => jtdi, JTCK => jtck, -- JSHIFT => jshift, JUPDATE => jupdate, JRSTN => jrstn, -- JRTI1 => jrti(1), JRTI2 => jrti(2), -- JTDO1 => jtdo(1), JTDO2 => jtdo(2), -- JCE1 => jce(1), JCE2 => jce(2) ); /* prepare synchronization signals for ER1 */ er1_sig_proc : process(jtck) begin if falling_edge(jtck) then if er1_sample = '1' then er1_sample_tgl <= not er1_sample_tgl; end if; if er1_update = '1' then er1_update_tgl <= not er1_update_tgl; end if; end if; end process; /* handle ER1 jtag state machine */ er1_proc : process(jtck, jce(1)) variable jreg : std_logic_vector(ER1_WIDTH-1 downto 0); variable addr : std_logic_vector(ER1_ADDR_WIDTH-1 downto 0); variable bpos : integer := 0; begin if falling_edge(jtck) then er1_sample <= '0'; er1_update <= '0'; if jrstn = '0' then -- Test Logic Reset elsif jce(1) = '1' then -- Capture/Shift DR if jshift = '1' then -- Shift DR jreg := jtdi & jreg(jreg'high downto 1); if bpos = ER1_ADDR_WIDTH then addr := jreg(jreg'high downto ER1_DATA_WIDTH+1); jreg(ER1_DATA_WIDTH-1 downto 0) := er1_reg(to_index(addr)); end if; bpos := bpos + 1; else -- Capture DR jreg := er1_din; bpos := 0; end if; elsif jupdate = '1' then er1_dout <= jreg; er1_update <= '1'; elsif jrti(1) = '1' then -- Run Test/Idle er1_sample <= '1'; else -- Last Bit jreg := jtdi & jreg(jreg'high downto 1); bpos := bpos + 1; end if; end if; jtdo(1) <= jreg(0); end process; /* High speed domain synchronizers */ er1_sync_proc : process(clk) variable last_sample : std_logic := '0'; variable last_update : std_logic := '0'; begin if rising_edge(clk) then er1_sample_req <= '0'; er1_sample_sync(2 downto 0) <= er1_sample_sync(1 downto 0) & er1_sample_tgl; if last_sample /= er1_sample_sync(2) then last_sample := er1_sample_sync(2); er1_sample_req <= '1'; end if; er1_update_req <= '0'; er1_update_sync(2 downto 0) <= er1_update_sync(1 downto 0) & er1_update_tgl; if last_update /= er1_update_sync(2) then last_update := er1_update_sync(2); er1_update_req <= '1'; end if; end if; end process; /* Clock Domain Transfer */ er1_data_proc : process(clk) constant LAST_ADDR : std_logic_vector(er1_addr'range) := (others => '1'); begin if rising_edge(clk) then if er1_sample_req = '1' then /* Sample */ er1_din <= (others => '0'); for I in 0 to 3 loop er1_reg(2*I) <= std_logic_vector(cnt(I)(31 downto 0)); er1_reg(2*I+1) <= std_logic_vector(cnt(I)(63 downto 32)); end loop; end if; if er1_update_req = '1' then /* Update */ if er1_flag = '1' then if er1_addr = LAST_ADDR then ctrl <= er1_ctrl; else er1_ctrl(to_index(er1_addr)) <= er1_data; end if; end if; end if; end if; end process; /* FTDI Data Generation */ dat_en(0) <= ctrl(0)(0); dat_en(1) <= ctrl(0)(1) and not wr_n; dat_en(2) <= ctrl(0)(2) and not txe_n; dat_en(3) <= ctrl(0)(3) and not txe_n and not wr_n; dat_rst <= ctrl(1)(11 downto 8); dat_proc : process(clk) begin if rising_edge(clk) then for I in 0 to 3 loop if dat_rst(I) then dat(I) <= (others => '0'); elsif dat_en(I) then dat(I) <= dat(I) + "1"; else dat(I) <= dat(I); end if; end loop; end if; end process; data <= dat(to_index(ctrl(1)(18 downto 16))); /* Interval and Cycles */ wr_en <= not ctrl(2)(17) and not int_over; cyc_en <= ctrl(5)(16); cyc_rst <= ctrl(5)(20); cyc_proc : process(clk) variable cyc_cnt : unsigned(15 downto 0) := (others => '0'); variable int_cnt : unsigned(15 downto 0) := (others => '0'); begin if rising_edge(clk) then if cyc_rst = '1' then cyc_cnt := (others => '0'); int_cnt := (others => '0'); int_over <= '0'; elsif cyc_en = '1' then if int_cnt = unsigned(ctrl(4)(15 downto 0)) then if cyc_cnt = unsigned(ctrl(5)(15 downto 0)) then int_cnt := int_cnt; int_over <= '1'; else cyc_cnt := cyc_cnt + "1"; int_cnt := (others => '0'); int_over <= '0'; end if; elsif int_cnt = unsigned(ctrl(4)(31 downto 16)) then int_cnt := int_cnt + "1"; int_over <= '1'; else int_cnt := int_cnt + "1"; end if; else int_cnt := int_cnt; end if; wr_en_d <= wr_en; end if; end process; /* Statistics Counters */ cnt_en <= ctrl(0)(3 downto 0); cnt_rst <= ctrl(0)(11 downto 8); cnt_cond(0) <= '1'; cnt_cond(1) <= not wr_n; cnt_cond(2) <= not txe_n; cnt_cond(3) <= not txe_n and not wr_n; cnt_proc : process(clk) begin if rising_edge(clk) then for I in 0 to 3 loop if cnt_rst(I) then cnt(I) <= (others => '0'); elsif cnt_en_d(I) then cnt(I) <= cnt(I) + "1"; else cnt(I) <= cnt(I); end if; end loop; cnt_en_d <= cnt_en and cnt_cond; end if; end process; /* LED signals */ led_sig(0) <= '0'; led_sig(1) <= txe_n; led_sig(2) <= rxf_n; led_sig(3) <= wr_n; led_sig(4) <= rd_n; led_sig(5) <= cnt(0)(24); led_sig(6) <= cnt(3)(24); led_sig(7) <= '1'; led_proc : process(clk) variable idx : integer; begin if rising_edge(clk) then idx := to_index(ctrl(3)(2 downto 0)); led_o <= led_sig(idx); led_t <= '0'; end if; end process; end RTL;