BlinkenArea - GitList
Repositories
Blog
Wiki
mips_sys
Code
Commits
Branches
Tags
Search
Tree:
160b4d9
Branches
Tags
master
mips_sys
io
eth
eth.vhd
improve ethernet busmaster (TX) critical path
Stefan Schuermans
commited
160b4d9
at 2012-04-03 20:23:25
eth.vhd
Blame
History
Raw
LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; USE IEEE.NUMERIC_STD.ALL; ENTITY e_io_eth IS PORT ( rst: IN std_logic; clk: IN std_logic; i_addr: IN std_logic_vector( 3 DOWNTO 0); o_rd_data: OUT std_logic_vector(31 DOWNTO 0); i_rd_en: IN std_logic_vector( 3 DOWNTO 0); i_wr_data: IN std_logic_vector(31 DOWNTO 0); i_wr_en: IN std_logic_vector( 3 DOWNTO 0); o_bm_req: OUT std_logic; i_bm_grant: IN std_logic; o_bm_addr: OUT std_logic_vector(31 DOWNTO 0); i_bm_rd_data: IN std_logic_vector(31 DOWNTO 0); o_bm_rd_en: OUT std_logic_vector( 3 DOWNTO 0); o_bm_wr_data: OUT std_logic_vector(31 DOWNTO 0); o_bm_wr_en: OUT std_logic_vector( 3 DOWNTO 0); pin_o_nrst: OUT std_logic; pin_i_rx_clk: IN std_logic; pin_i_rxd: IN std_logic_vector(4 DOWNTO 0); pin_i_rx_dv: IN std_logic; pin_i_crs: IN std_logic; pin_i_col: IN std_logic; pin_i_tx_clk: IN std_logic; pin_o_txd: OUT std_logic_vector(3 DOWNTO 0); pin_o_tx_en: OUT std_logic ); END ENTITY e_io_eth; ARCHITECTURE a_io_eth OF e_io_eth IS SIGNAL s_rxif_data: std_logic_vector(7 DOWNTO 0); SIGNAL s_rxif_data_en: std_logic; SIGNAL s_rxif_done: std_logic; SIGNAL s_rxif_err: std_logic; SIGNAL s_rxframe_data: std_logic_vector(31 DOWNTO 0); SIGNAL s_rxframe_data_en: std_logic; SIGNAL s_rxframe_done: std_logic; SIGNAL s_rxframe_err: std_logic; SIGNAL s_rx_new: std_logic; SIGNAL s_txif_data: std_logic_vector(7 DOWNTO 0); SIGNAL s_txif_data_en: std_logic; SIGNAL s_txif_data_ack: std_logic; SIGNAL s_txframe_en: std_logic; SIGNAL s_txframe_done: std_logic; SIGNAL s_txbuf_wr_rdy: std_logic; SIGNAL s_txbuf_wr_data: std_logic_vector(31 DOWNTO 0); SIGNAL s_txbuf_wr_en: std_logic; SIGNAL s_txbuf_rd_rdy: std_logic; SIGNAL s_txbuf_rd_data: std_logic_vector(31 DOWNTO 0); SIGNAL s_txbuf_rd_en: std_logic; -- RX buffer registers -- start: current buffer begin -- cur: address of next data write -- size: size of data received -- end: address just behind buffer -- data is written to buffer like this: <size><data 0>...<data size-1> -- size is written last -- next packet begins directly afterwards -- all addresses/sizes are word-aligned SIGNAL r_rx_start: std_logic_vector(31 DOWNTO 0) := X"00000000"; SIGNAL n_rx_start: std_logic_vector(31 DOWNTO 0); SIGNAL r_rx_cur: std_logic_vector(31 DOWNTO 0) := X"00000004"; SIGNAL n_rx_cur: std_logic_vector(31 DOWNTO 0); SIGNAL r_rx_size: std_logic_vector(31 DOWNTO 0) := X"00000000"; SIGNAL n_rx_size: std_logic_vector(31 DOWNTO 0); SIGNAL r_rx_end: std_logic_vector(31 DOWNTO 0) := X"00000000"; SIGNAL n_rx_end: std_logic_vector(31 DOWNTO 0); -- RX new buffer registers -- new_start: begin of new buffer -- new_end: address just behind new buffer -- new_en: if a new buffer is available -- all addresses are word-aligned SIGNAL r_rx_new_start: std_logic_vector(31 DOWNTO 0) := X"00000000"; SIGNAL n_rx_new_start: std_logic_vector(31 DOWNTO 0); SIGNAL r_rx_new_end: std_logic_vector(31 DOWNTO 0) := X"00000000"; SIGNAL n_rx_new_end: std_logic_vector(31 DOWNTO 0); SIGNAL r_rx_new_en: std_logic := '0'; SIGNAL n_rx_new_en: std_logic; -- TX buffer registers -- start: begin of buffer (word-aligned) -- end: address just behind buffer (NOT word-aligned) -- en: write 1 to start transmission, returns to zero if complete SIGNAL r_tx_start: std_logic_vector(31 DOWNTO 0) := X"00000000"; SIGNAL n_tx_start: std_logic_vector(31 DOWNTO 0); SIGNAL r_tx_end: std_logic_vector(31 DOWNTO 0) := X"00000000"; SIGNAL n_tx_end: std_logic_vector(31 DOWNTO 0); SIGNAL r_tx_en: std_logic := '0'; SIGNAL n_tx_en: std_logic; -- MAC address register SIGNAL r_mac: std_logic_vector(47 DOWNTO 0) := X"FFFFFFFFFFFF"; SIGNAL n_mac: std_logic_vector(47 DOWNTO 0); -- TX busmaster read state machine -- pos: next address to read (NOT word-aligned) -- rem: number of bytes remaining (NOT multiple of words size) SIGNAL r_tx_pos: std_logic_vector(31 DOWNTO 0) := X"00000000"; SIGNAL n_tx_pos: std_logic_vector(31 DOWNTO 0); SIGNAL r_tx_rem: signed (31 DOWNTO 0) := X"00000000"; SIGNAL n_tx_rem: signed (31 DOWNTO 0); -- busmaster write buffer SIGNAL s_wrbuf_wr_rdy: std_logic; SIGNAL s_wrbuf_wr_data: std_logic_vector(63 DOWNTO 0); SIGNAL s_wrbuf_wr_en: std_logic; SIGNAL s_wrbuf_rd_rdy: std_logic; SIGNAL s_wrbuf_rd_data: std_logic_vector(63 DOWNTO 0); SIGNAL s_wrbuf_rd_en: std_logic; -- busmaster read flag SIGNAL r_bm_rd: std_logic := '0'; SIGNAL n_bm_rd: std_logic; COMPONENT e_io_eth_rst IS PORT ( rst: IN std_logic; clk: IN std_logic; pin_o_nrst: OUT std_logic ); END COMPONENT e_io_eth_rst; COMPONENT e_io_eth_rxif IS PORT ( rst: IN std_logic; clk: IN std_logic; o_data: OUT std_logic_vector(7 DOWNTO 0); o_data_en: OUT std_logic; o_done: OUT std_logic; o_err: OUT std_logic; pin_i_rx_clk: IN std_logic; pin_i_rxd: IN std_logic_vector(4 DOWNTO 0); pin_i_rx_dv: IN std_logic; pin_i_crs: IN std_logic; pin_i_col: IN std_logic ); END COMPONENT e_io_eth_rxif; COMPONENT e_io_eth_rxframe IS PORT ( rst: IN std_logic; clk: IN std_logic; i_data: IN std_logic_vector( 7 DOWNTO 0); i_data_en: IN std_logic; i_done: IN std_logic; i_err: IN std_logic; i_mac: IN std_logic_vector(47 DOWNTO 0); o_data: OUT std_logic_vector(31 DOWNTO 0); o_data_en: OUT std_logic; o_done: OUT std_logic; o_err: OUT std_logic ); END COMPONENT e_io_eth_rxframe; COMPONENT e_io_eth_txif IS PORT ( rst: IN std_logic; clk: IN std_logic; i_data: IN std_logic_vector(7 DOWNTO 0); i_data_en: IN std_logic; o_data_ack: OUT std_logic; pin_i_tx_clk: IN std_logic; pin_o_txd: OUT std_logic_vector(3 DOWNTO 0); pin_o_tx_en: OUT std_logic ); END COMPONENT e_io_eth_txif; COMPONENT e_io_eth_txframe IS PORT ( rst: IN std_logic; clk: IN std_logic; o_if_data: OUT std_logic_vector(7 DOWNTO 0); o_if_data_en: OUT std_logic; i_if_data_ack: IN std_logic; i_frame_en: IN std_logic; i_frame_data: IN std_logic_vector(31 DOWNTO 0); i_frame_data_en: IN std_logic; o_frame_data_ack: OUT std_logic; o_frame_done: OUT std_logic ); END COMPONENT e_io_eth_txframe; COMPONENT e_block_fifo IS GENERIC ( addr_width: natural; data_width: natural ); PORT ( rst: IN std_logic; clk: IN std_logic; o_wr_rdy: OUT std_logic; i_wr_data: IN std_logic_vector(data_width - 1 DOWNTO 0); i_wr_en: IN std_logic; o_rd_rdy: OUT std_logic; o_rd_data: OUT std_logic_vector(data_width - 1 DOWNTO 0); i_rd_en: IN std_logic ); END COMPONENT e_block_fifo; BEGIN reset: e_io_eth_rst PORT MAP ( rst => rst, clk => clk, pin_o_nrst => pin_o_nrst ); rxif: e_io_eth_rxif PORT MAP ( rst => rst, clk => clk, o_data => s_rxif_data, o_data_en => s_rxif_data_en, o_done => s_rxif_done, o_err => s_rxif_err, pin_i_rx_clk => pin_i_rx_clk, pin_i_rxd => pin_i_rxd, pin_i_rx_dv => pin_i_rx_dv, pin_i_crs => pin_i_crs, pin_i_col => pin_i_col ); rxframe: e_io_eth_rxframe PORT MAP ( rst => rst, clk => clk, i_data => s_rxif_data, i_data_en => s_rxif_data_en, i_done => s_rxif_done, i_err => s_rxif_err, i_mac => r_mac, o_data => s_rxframe_data, o_data_en => s_rxframe_data_en, o_done => s_rxframe_done, o_err => s_rxframe_err ); p_rx_next: PROCESS(r_rx_start, r_rx_cur, r_rx_size, r_rx_end, r_rx_new_start, r_rx_new_end, r_rx_new_en, s_rxframe_data, s_rxframe_data_en, s_rxframe_done, s_rxframe_err, s_wrbuf_wr_rdy) VARIABLE v_abort: boolean; -- abort current packet reception VARIABLE v_ignore: boolean; -- ignore rest of packet VARIABLE v_store: boolean; -- store packet data VARIABLE v_finish: boolean; -- finish reception of packet BEGIN n_rx_start <= r_rx_start; n_rx_cur <= r_rx_cur; n_rx_size <= r_rx_size; n_rx_end <= r_rx_end; s_wrbuf_wr_data <= (OTHERS => '0'); s_wrbuf_wr_en <= '0'; s_rx_new <= '0'; -- determine which action to perform v_abort := false; v_ignore := false; v_store := false; v_finish := false; -- incoming data IF s_rxframe_data_en = '1' THEN IF r_rx_start = r_rx_end THEN v_ignore := true; -- buffer not available -> ignore rest of packet ELSIF r_rx_cur = r_rx_end THEN v_ignore := true; -- buffer full -> ignore rest of packet ELSIF s_wrbuf_wr_rdy = '0' THEN v_ignore := true; -- bus master write buffer full -> ignore rest of packet ELSE v_store := true; -- store data END IF; -- packet complete ELSIF s_rxframe_done = '1' THEN IF r_rx_start = r_rx_end THEN v_abort := true; -- buffer not available -> abort ELSIF r_rx_cur = r_rx_end THEN v_abort := true; -- buffer full -> abort ELSIF s_wrbuf_wr_rdy = '0' THEN v_abort := true; -- bus master write buffer full -> abort ELSIF r_rx_size = X"00000000" THEN v_abort := true; -- empty packet -> abort ELSE v_finish := true; -- finish packet recpetion END IF; -- error ELSIF s_rxframe_err = '1' THEN v_abort := true; -- abort END IF; -- perform action selected above -- abort current packet IF v_abort THEN n_rx_cur <= std_logic_vector(unsigned(r_rx_start) + X"00000004"); n_rx_size <= X"00000000"; -- ignore rest of packet -- count size to ensure it is not zero ELSIF v_ignore THEN n_rx_cur <= r_rx_end; n_rx_size <= std_logic_vector(unsigned(r_rx_size) + X"00000004"); -- store data to current address and advance in buffer ELSIF v_store THEN s_wrbuf_wr_data <= r_rx_cur & s_rxframe_data; s_wrbuf_wr_en <= '1'; n_rx_cur <= std_logic_vector(unsigned(r_rx_cur) + X"00000004"); n_rx_size <= std_logic_vector(unsigned(r_rx_size) + X"00000004"); -- store size to start and restart after packet ELSIF v_finish THEN s_wrbuf_wr_data <= r_rx_start & r_rx_size; s_wrbuf_wr_en <= '1'; n_rx_start <= r_rx_cur; n_rx_cur <= std_logic_vector(unsigned(r_rx_cur) + X"00000004"); n_rx_size <= X"00000000"; END IF; -- use new buffer -- if no action, no data yet and new_en set IF NOT v_abort AND NOT v_ignore AND NOT v_store AND NOT v_finish AND r_rx_size = X"00000000" AND r_rx_new_en = '1' THEN -- terminate old buffer by writing size 0 (if old buffer was available) IF r_rx_start /= r_rx_end THEN s_wrbuf_wr_data <= r_rx_start & X"00000000"; s_wrbuf_wr_en <= '1'; END IF; -- take over new_start and new_end n_rx_start <= r_rx_new_start; n_rx_cur <= std_logic_vector(unsigned(r_rx_new_start) + X"00000004"); n_rx_size <= X"00000000"; n_rx_end <= r_rx_new_end; -- signal overtake of new buffer to process p_write s_rx_new <= '1'; END IF; END PROCESS p_rx_next; txif: e_io_eth_txif PORT MAP ( rst => rst, clk => clk, i_data => s_txif_data, i_data_en => s_txif_data_en, o_data_ack => s_txif_data_ack, pin_i_tx_clk => pin_i_tx_clk, pin_o_txd => pin_o_txd, pin_o_tx_en => pin_o_tx_en ); txframe: e_io_eth_txframe PORT MAP ( rst => rst, clk => clk, o_if_data => s_txif_data, o_if_data_en => s_txif_data_en, i_if_data_ack => s_txif_data_ack, i_frame_en => s_txframe_en, i_frame_data => s_txbuf_rd_data, i_frame_data_en => s_txbuf_rd_rdy, o_frame_data_ack => s_txbuf_rd_en, o_frame_done => s_txframe_done ); txbuf: e_block_fifo GENERIC MAP ( addr_width => 2, data_width => 32 ) PORT MAP ( rst => rst, clk => clk, o_wr_rdy => s_txbuf_wr_rdy, i_wr_data => s_txbuf_wr_data, i_wr_en => s_txbuf_wr_en, o_rd_rdy => s_txbuf_rd_rdy, o_rd_data => s_txbuf_rd_data, i_rd_en => s_txbuf_rd_en ); p_reg_sync: PROCESS(rst, clk) BEGIN IF rst = '1' THEN r_rx_start <= X"00000000"; r_rx_cur <= X"00000004"; r_rx_size <= X"00000000"; r_rx_end <= X"00000000"; r_rx_new_start <= X"00000000"; r_rx_new_end <= X"00000000"; r_rx_new_en <= '0'; r_tx_start <= X"00000000"; r_tx_end <= X"00000000"; r_tx_en <= '0'; r_mac <= X"FFFFFFFFFFFF"; ELSIF rising_edge(clk) THEN r_rx_start <= n_rx_start; r_rx_cur <= n_rx_cur; r_rx_size <= n_rx_size; r_rx_end <= n_rx_end; r_rx_new_start <= n_rx_new_start; r_rx_new_end <= n_rx_new_end; r_rx_new_en <= n_rx_new_en; r_tx_start <= n_tx_start; r_tx_end <= n_tx_end; r_tx_en <= n_tx_en; r_mac <= n_mac; END IF; END PROCESS p_reg_sync; -- register interface write p_write: PROCESS(r_rx_new_start, r_rx_new_end, r_rx_new_en, s_rx_new, r_tx_start, r_tx_end, r_tx_en, s_txframe_done, r_mac, i_addr, i_wr_data, i_wr_en) BEGIN n_rx_new_start <= r_rx_new_start; n_rx_new_end <= r_rx_new_end; n_rx_new_en <= r_rx_new_en; n_tx_start <= r_tx_start; n_tx_end <= r_tx_end; n_tx_en <= r_tx_en; n_mac <= r_mac; s_txframe_en <= '0'; IF s_rx_new = '1' THEN n_rx_new_en <= '0'; -- new buffer has been overtaken, reset rx_new_en END IF; IF s_txframe_done = '1' THEN n_tx_en <= '0'; -- TX operation completed, reset tx_en END IF; CASE i_addr IS WHEN "0100" => IF i_wr_en = "1111" THEN n_rx_new_start <= i_wr_data; END IF; WHEN "0101" => IF i_wr_en = "1111" THEN n_rx_new_end <= i_wr_data; END IF; WHEN "0110" => IF i_wr_en(0) = '1' AND i_wr_data(0) = '1' THEN n_rx_new_en <= '1'; END IF; WHEN "1000" => IF i_wr_en = "1111" AND r_tx_en = '0' THEN n_tx_start <= i_wr_data; END IF; WHEN "1001" => IF i_wr_en = "1111" AND r_tx_en = '0' THEN n_tx_end <= i_wr_data; END IF; WHEN "1010" => IF i_wr_en(0) = '1' AND i_wr_data(0) = '1' THEN IF r_tx_en = '0' THEN s_txframe_en <= '1'; END IF; n_tx_en <= '1'; END IF; WHEN "1100" => IF i_wr_en(0) = '1' THEN n_mac(7 DOWNTO 0) <= i_wr_data(7 DOWNTO 0); END IF; IF i_wr_en(1) = '1' THEN n_mac(15 DOWNTO 8) <= i_wr_data(15 DOWNTO 8); END IF; IF i_wr_en(2) = '1' THEN n_mac(23 DOWNTO 16) <= i_wr_data(23 DOWNTO 16); END IF; IF i_wr_en(3) = '1' THEN n_mac(31 DOWNTO 24) <= i_wr_data(31 DOWNTO 24); END IF; WHEN "1101" => IF i_wr_en(0) = '1' THEN n_mac(39 DOWNTO 32) <= i_wr_data(7 DOWNTO 0); END IF; IF i_wr_en(1) = '1' THEN n_mac(47 DOWNTO 40) <= i_wr_data(15 DOWNTO 8); END IF; WHEN OTHERS => NULL; END CASE; END PROCESS p_write; -- register interface read p_read: PROCESS(rst, clk) BEGIN IF rst = '1' THEN o_rd_data <= (OTHERS => '0'); ELSIF rising_edge(clk) THEN o_rd_data <= (OTHERS => '0'); CASE i_addr IS WHEN "0000" => o_rd_data <= r_rx_start; WHEN "0001" => o_rd_data <= r_rx_cur; WHEN "0010" => o_rd_data <= r_rx_size; WHEN "0011" => o_rd_data <= r_rx_end; WHEN "0100" => o_rd_data <= r_rx_new_start; WHEN "0101" => o_rd_data <= r_rx_new_end; WHEN "0110" => o_rd_data(0) <= r_rx_new_en; WHEN "1000" => o_rd_data <= r_tx_start; WHEN "1001" => o_rd_data <= r_tx_end; WHEN "1010" => o_rd_data(0) <= r_tx_en; WHEN "1100" => o_rd_data <= r_mac(31 DOWNTO 0); WHEN "1101" => o_rd_data(15 DOWNTO 0) <= r_mac(47 DOWNTO 32); WHEN OTHERS => NULL; END CASE; END IF; END PROCESS p_read; -- bus master write buffer -- as the core has lower bus priority than ethernet, -- the core will never access memory or ethernet registers -- when data is in this buffer wrbuf: e_block_fifo GENERIC MAP ( addr_width => 2, data_width => 64 ) PORT MAP ( rst => rst, clk => clk, o_wr_rdy => s_wrbuf_wr_rdy, i_wr_data => s_wrbuf_wr_data, i_wr_en => s_wrbuf_wr_en, o_rd_rdy => s_wrbuf_rd_rdy, o_rd_data => s_wrbuf_rd_data, i_rd_en => s_wrbuf_rd_en ); -- TX busmaster read state machine p_tx_rd_next: PROCESS(r_tx_pos, r_tx_rem, r_tx_start, r_tx_end, s_txframe_en, s_txbuf_wr_en) VARIABLE v_rem: signed(31 DOWNTO 0); BEGIN n_tx_pos <= r_tx_pos; n_tx_rem <= r_tx_rem; IF s_txframe_en = '1' THEN n_tx_pos <= r_tx_start; n_tx_rem <= signed(r_tx_end) - signed(r_tx_start); ELSIF s_txbuf_wr_en = '1' THEN n_tx_pos <= std_logic_vector(unsigned(r_tx_pos) + X"00000004"); v_rem := r_tx_rem - X"00000004"; IF v_rem < X"00000000" THEN v_rem := X"00000000"; END IF; n_tx_rem <= v_rem; END IF; END PROCESS p_tx_rd_next; p_tx_rd_sync: PROCESS(clk, rst) BEGIN IF rst = '1' THEN r_tx_pos <= X"00000000"; r_tx_rem <= X"00000000"; ELSIF rising_edge(clk) THEN r_tx_pos <= n_tx_pos; r_tx_rem <= n_tx_rem; END IF; END PROCESS p_tx_rd_sync; -- busmaster read completed p_bm_rd: PROCESS(r_bm_rd, r_tx_rem, i_bm_rd_data) BEGIN s_txbuf_wr_data <= X"00000000"; s_txbuf_wr_en <= '0'; -- read was just completed IF r_bm_rd = '1' THEN CASE r_tx_rem IS WHEN X"00000000" => NULL; WHEN X"00000001" => s_txbuf_wr_data( 7 DOWNTO 0) <= i_bm_rd_data( 7 DOWNTO 0); WHEN X"00000002" => s_txbuf_wr_data(15 DOWNTO 0) <= i_bm_rd_data(15 DOWNTO 0); WHEN X"00000003" => s_txbuf_wr_data(23 DOWNTO 0) <= i_bm_rd_data(23 DOWNTO 0); WHEN OTHERS => s_txbuf_wr_data <= i_bm_rd_data; END CASE; s_txbuf_wr_en <= '1'; END IF; END PROCESS p_bm_rd; -- bus master write (and request side of read) p_bm_wr: PROCESS(r_bm_rd, r_tx_en, r_tx_pos, r_tx_rem, s_txbuf_wr_rdy, s_wrbuf_rd_rdy, s_wrbuf_rd_data, i_bm_grant) VARIABLE v_read: boolean; VARIABLE v_write: boolean; BEGIN n_bm_rd <= '0'; s_wrbuf_rd_en <= '0'; o_bm_req <= '0'; o_bm_addr <= (OTHERS => '0'); o_bm_rd_en <= (OTHERS => '0'); o_bm_wr_data <= (OTHERS => '0'); o_bm_wr_en <= (OTHERS => '0'); -- check for requests -- read: no read just completed, TX running, not reached end, -- space in buffer -- write: write request in write buffer v_read := r_bm_rd = '0' AND r_tx_en = '1' AND r_tx_rem /= X"00000000" AND s_txbuf_wr_rdy = '1'; v_write := s_wrbuf_rd_rdy = '1'; -- read access IF v_read THEN n_bm_rd <= i_bm_grant; o_bm_req <= '1'; o_bm_addr <= r_tx_pos; o_bm_rd_en <= "1111"; -- write access (arbitrate with read) ELSIF v_write THEN s_wrbuf_rd_en <= i_bm_grant; o_bm_req <= '1'; o_bm_addr <= s_wrbuf_rd_data(63 DOWNTO 32); o_bm_wr_data <= s_wrbuf_rd_data(31 DOWNTO 0); o_bm_wr_en <= "1111"; END IF; END PROCESS p_bm_wr; p_bm_sync: PROCESS(clk, rst) BEGIN IF rst = '1' THEN r_bm_rd <= '0'; ELSIF rising_edge(clk) THEN r_bm_rd <= n_bm_rd; END IF; END PROCESS p_bm_sync; END ARCHITECTURE a_io_eth;