LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; USE IEEE.NUMERIC_STD.ALL; use std.textio.all; -- Imports the standard textio package. use work.utils_pkg.all; ENTITY emac_jb IS Generic ( f_sysclk : real := 100.0; TX_RAM_SIZE : natural := 2048 ); Port ( CLK_I : in STD_LOGIC; RST_I : in STD_LOGIC; INT_O : out STD_LOGIC; CYC_I : in STD_LOGIC; STB_I : in STD_LOGIC; SEL_I : in unsigned(3 downto 0); WE_I : in STD_LOGIC; ACK_O : out STD_LOGIC; SRDY_O : out STD_LOGIC; MRDY_I : in STD_LOGIC; ADDR_I : in unsigned(31 downto 0); DAT_I : in unsigned(31 downto 0); DAT_O : out unsigned(31 downto 0); mii_rx_clk : in STD_LOGIC; mii_rx_dv : in STD_LOGIC; mii_rx_er : in STD_LOGIC; mii_rx : in unsigned(7 downto 0); mii_tx_clk : in STD_LOGIC; mii_tx_en : out STD_LOGIC; mii_tx_er : out STD_LOGIC; mii_tx : out unsigned(7 downto 0); mii_gtx_clk : out STD_LOGIC; mii_crs : in STD_LOGIC; mii_col : in STD_LOGIC ); END emac_jb; ARCHITECTURE behavior OF emac_jb IS constant TX_RAM_ADDR_WIDTH : natural := NextExpBaseTwo(TX_RAM_SIZE); subtype word_ptr_t is unsigned(TX_RAM_ADDR_WIDTH-1 downto 0); -- Signals for EMAC connections signal tx_fifo_din : unsigned(35 downto 0); signal tx_fifo_dout : unsigned(35 downto 0); signal tx_fifo_we : std_logic; signal tx_fifo_re : std_logic; signal tx_fifo_empty : std_logic; signal tx_fifo_full : std_logic; signal rx_int_en : std_logic; signal tx_int_en : std_logic; signal irq_rx : std_logic; signal irq_tx : std_logic; signal tx_err : std_logic; signal tx_flush_en : std_logic; signal tx_flush_rdy : std_logic; signal tx_flush_ptr_set : std_logic; signal tx_commit_en : std_logic; signal tx_flush_ptr : word_ptr_t; signal host_data_we : std_logic; signal host_data_reg : unsigned(31 downto 0); signal host_tx_size : unsigned(15 downto 0); signal tx_fill_size : word_ptr_t; signal tx_fill_cnt : word_ptr_t; signal tx_fill_ptr : word_ptr_t; signal tx_fill_rdy : std_logic; signal tx_fill_cnt_set : std_logic; signal tx_fill_cnt_adv : std_logic; signal tx_fill_ptr_set : std_logic; signal tx_fill_ptr_adv : std_logic; signal tx_alloc_OK : std_logic; signal tx_alloc_req_en : std_logic; signal tx_alloc_req : std_logic; signal tx_alloc_ack : std_logic; signal tx_nwords_free : word_ptr_t; signal tx_alloc_base : word_ptr_t; signal tx_alloc_size : word_ptr_t; signal tx_alloc_en : std_logic; signal tx_free_en : std_logic; signal tx_uncommit_en : std_logic; signal tx_fill_remain : unsigned(1 downto 0); signal tx_xfer_size : word_ptr_t; signal tx_xfer_ptr : word_ptr_t; signal tx_xfer_cnt : word_ptr_t; signal tx_xfer_en : std_logic; signal tx_xfer_ptr_set : std_logic; signal tx_xfer_ptr_adv : std_logic; signal tx_xfer_cnt_set : std_logic; signal tx_xfer_cnt_adv : std_logic; signal tx_xfer_rdy : std_logic; signal xfer_tag : unsigned(3 downto 0); signal xfer_remain : unsigned(1 downto 0); signal xfer_odd : std_logic; signal xfer_remain_en : std_logic; signal tx_ram_en_a : std_logic; signal tx_ram_we_a : std_logic; signal tx_ram_addr_a : unsigned(TX_RAM_ADDR_WIDTH-1 downto 0); signal tx_ram_din_a : unsigned(35 downto 0); signal tx_ram_dout_a : unsigned(35 downto 0); signal tx_ram_en_b : std_logic; signal tx_ram_we_b : std_logic; signal tx_ram_addr_b : unsigned(TX_RAM_ADDR_WIDTH-1 downto 0); signal tx_ram_din_b : unsigned(35 downto 0); signal tx_ram_dout_b : unsigned(35 downto 0); signal piso_din_be : unsigned(3 downto 0); signal piso_din_rdy : std_logic; signal tx_data_vld : std_logic; subtype inter_frame_gap_cnt_t is natural range 0 to natural(1.0*f_sysclk)-1; signal inter_frame_gap_cnt : inter_frame_gap_cnt_t; signal inter_frame_gap_rdy : std_logic; alias tx_ram_data_in_a is tx_ram_din_a(31 downto 0); alias tx_ram_tag_in_a is tx_ram_din_a(35 downto 32); alias tx_ram_data_out_a is tx_ram_dout_a(31 downto 0); alias tx_ram_tag_out_a is tx_ram_dout_a(35 downto 32); alias tx_ram_data_in_b is tx_ram_din_b(31 downto 0); alias tx_ram_tag_in_b is tx_ram_din_b(35 downto 32); alias tx_ram_data_out_b is tx_ram_dout_b(31 downto 0); alias tx_ram_tag_out_b is tx_ram_dout_b(35 downto 32); type host_tx_state_t is (host_tx_init, host_tx_flush, host_tx_idle, host_tx_alloc, host_tx_setup, host_tx_arm, host_tx_fill, host_tx_commit); signal host_tx_s, host_tx_sn : host_tx_state_t; type xfer_tx_state_t is (xfer_tx_init, xfer_tx_idle, xfer_tx_setup, xfer_wait, xfer_tx_active, xfer_tx_free); signal xfer_tx_s, xfer_tx_sn : xfer_tx_state_t; type piso_byte_mask_array_t is array (0 to 3) of unsigned (3 downto 0); constant piso_byte_mask_rom : piso_byte_mask_array_t := ( "1111", "0001", "0011", "0111" ); begin SRDY_O <= CYC_I; INT_O <= irq_rx or irq_tx; mii_gtx_clk <= '0'; mii_tx_er <= tx_err; tx_ram_tag_in_b <= "0000"; tx_ram_en_b <= '1'; tx_ram_we_b <= tx_uncommit_en or tx_flush_en; tx_ram_addr_b <= tx_flush_ptr when (tx_flush_en = '1' or tx_uncommit_en = '1') else tx_xfer_ptr; xfer_tag <= tx_ram_tag_out_b; tx_ram_en_a <= '1'; tx_ram_we_a <= tx_commit_en or tx_fill_cnt_adv; tx_ram_tag_in_a <= tx_fill_remain & "01" when tx_commit_en = '1' else "0000"; tx_ram_data_in_a <= resize(tx_fill_size, 16) & resize(tx_alloc_base, 16) when tx_commit_en = '1' else host_data_reg; tx_ram_addr_a <= tx_alloc_base when tx_commit_en = '1' else tx_fill_ptr; piso_din_be <= (others => '1'); tx_fifo_re <= piso_din_rdy; tx_data_vld <= not tx_fifo_empty; tx_fifo_din(35 downto 32) <= piso_byte_mask_rom(to_integer(xfer_remain)) when xfer_remain_en = '1' else "1111"; tx_fifo_din(31 downto 0) <= tx_ram_data_out_b; ------------------------------------------------------------------ host_tx_state_next: process(CLK_I) begin if rising_edge(CLK_I) then if RST_I = '1' then host_tx_s <= host_tx_init; else host_tx_s <= host_tx_sn; end if; end if; end process; host_tx_state: process(host_tx_s, tx_flush_rdy, tx_alloc_req, tx_alloc_OK, tx_fill_rdy, host_data_we) begin tx_flush_en <= '0'; tx_flush_ptr_set <= '0'; tx_alloc_ack <= '0'; tx_fill_cnt_set <= '0'; tx_fill_cnt_adv <= '0'; tx_fill_ptr_set <= '0'; tx_fill_ptr_adv <= '0'; tx_commit_en <= '0'; tx_alloc_en <= '0'; host_tx_sn <= host_tx_s; case host_tx_s is when host_tx_init => tx_flush_ptr_set <= '1'; host_tx_sn <= host_tx_flush; when host_tx_flush => tx_flush_en <= '1'; if tx_flush_rdy = '1' then tx_flush_en <= '0'; host_tx_sn <= host_tx_idle; end if; when host_tx_idle => if tx_alloc_req = '1' then host_tx_sn <= host_tx_alloc; end if; when host_tx_alloc => if tx_alloc_OK = '1' then host_tx_sn <= host_tx_setup; tx_alloc_ack <= '1'; end if; when host_tx_setup => tx_fill_cnt_set <= '1'; tx_fill_ptr_set <= '1'; host_tx_sn <= host_tx_arm; when host_tx_arm => tx_fill_ptr_adv <= '1'; tx_fill_cnt_adv <= '1'; host_tx_sn <= host_tx_fill; when host_tx_fill => tx_fill_cnt_adv <= host_data_we; tx_fill_ptr_adv <= not tx_fill_rdy and host_data_we; if tx_alloc_req = '1' then host_tx_sn <= host_tx_alloc; elsif tx_fill_rdy = '1' then host_tx_sn <= host_tx_commit; end if; when host_tx_commit => tx_commit_en <= '1'; tx_alloc_en <= '1'; host_tx_sn <= host_tx_idle; when others => host_tx_sn <= host_tx_idle; end case; end process; ------------------------------------------------------------------ -- Allocation stuff ------------------------------------------------------------------ alloc_request_logic: process(CLK_I) begin if rising_edge(CLK_I) then if RST_I = '1' then tx_alloc_req <= '0'; elsif tx_alloc_req_en = '1' and tx_alloc_req = '0' then tx_alloc_req <= '1'; tx_fill_remain <= host_tx_size(1 downto 0); tx_fill_size <= host_tx_size(word_ptr_t'left+2 downto 2); if host_tx_size(1 downto 0) /= "00" then tx_fill_size <= host_tx_size(word_ptr_t'left+2 downto 2) + 1; end if; elsif tx_alloc_ack = '1' then tx_alloc_req <= '0'; tx_alloc_size <= tx_fill_size; end if; end if; end process; ------------------------------------------------------------------ alloc_base_logic: process(CLK_I) begin if rising_edge(CLK_I) then if tx_flush_en = '1' then tx_alloc_base <= (others => '0'); elsif tx_commit_en = '1' then tx_alloc_base <= tx_fill_ptr; end if; end if; end process; ------------------------------------------------------------------ alloc_eval_logic: process(CLK_I) begin if rising_edge(CLK_I) then tx_alloc_OK <= '0'; if tx_alloc_req = '1' then if tx_fill_size < tx_nwords_free then tx_alloc_OK <= '1'; end if; end if; end if; end process; ------------------------------------------------------------------ tx_nwords_free_counter: process(CLK_I) begin if rising_edge(CLK_I) then if tx_flush_en = '1' then tx_nwords_free <= (others => '1'); elsif tx_alloc_en = '1' then tx_nwords_free <= tx_nwords_free - tx_fill_size; elsif tx_free_en = '1' then tx_nwords_free <= tx_nwords_free + tx_xfer_size; end if; end if; end process; ------------------------------------------------------------------ tx_flush_counter: process(CLK_I) begin if rising_edge(CLK_I) then if tx_flush_en = '0' then tx_flush_rdy <= '0'; if tx_flush_ptr_set = '1' then tx_flush_ptr <= (others => '1'); elsif tx_xfer_ptr_set = '1' then tx_flush_ptr <= tx_ram_data_out_b(tx_flush_ptr'left downto 0); end if; elsif tx_flush_ptr /= 0 then tx_flush_ptr <= tx_flush_ptr - 1; else tx_flush_rdy <= '1'; end if; end if; end process; ------------------------------------------------------------------ -- Fill stuff ------------------------------------------------------------------ tx_fill_counter: process(CLK_I) begin if rising_edge(CLK_I) then if tx_fill_cnt_set = '1' then tx_fill_rdy <= '0'; tx_fill_cnt <= tx_alloc_size; elsif tx_fill_cnt_adv = '1' then if tx_fill_cnt /= 0 then tx_fill_cnt <= tx_fill_cnt - 1; else tx_fill_rdy <= '1'; end if; end if; end if; end process; tx_fill_pointer: process(CLK_I) begin if rising_edge(CLK_I) then if tx_fill_ptr_set = '1' then tx_fill_ptr <= tx_alloc_base; elsif tx_fill_ptr_adv = '1' then tx_fill_ptr <= tx_fill_ptr + 1; end if; end if; end process; ------------------------------------------------------------------ -- Transfer stuff ------------------------------------------------------------------ xfer_tx_state_next: process(CLK_I) begin if rising_edge(CLK_I) then if RST_I = '1' then xfer_tx_s <= xfer_tx_init; else xfer_tx_s <= xfer_tx_sn; end if; end if; end process; xfer_tx_state: process(xfer_tx_s, xfer_tag, xfer_odd, tx_xfer_rdy, inter_frame_gap_rdy, tx_alloc_en, tx_fifo_full) begin tx_xfer_cnt_set <= '0'; tx_xfer_cnt_adv <= '0'; tx_xfer_ptr_set <= '0'; tx_xfer_ptr_adv <= '0'; tx_xfer_en <= '0'; xfer_remain_en <= '0'; tx_free_en <= '0'; tx_uncommit_en <= '0'; xfer_tx_sn <= xfer_tx_s; case xfer_tx_s is when xfer_tx_init => xfer_tx_sn <= xfer_tx_idle; when xfer_tx_idle => if xfer_tag(0) = '1' then tx_xfer_ptr_set <= '1'; tx_xfer_cnt_set <= '1'; xfer_tx_sn <= xfer_wait; end if; when xfer_wait => if inter_frame_gap_rdy = '1' then xfer_tx_sn <= xfer_tx_setup; end if; when xfer_tx_setup => tx_uncommit_en <= '1'; tx_xfer_ptr_adv <= '1'; tx_xfer_cnt_adv <= '1'; xfer_tx_sn <= xfer_tx_active; when xfer_tx_active => tx_xfer_en <= '1'; tx_xfer_cnt_adv <= not tx_fifo_full; tx_xfer_ptr_adv <= not tx_fifo_full; if tx_xfer_rdy = '1' then xfer_remain_en <= xfer_odd; tx_xfer_en <= '0'; tx_xfer_cnt_adv <= '0'; tx_xfer_ptr_adv <= '0'; xfer_tx_sn <= xfer_tx_free; end if; when xfer_tx_free => tx_free_en <= not tx_alloc_en; if tx_alloc_en = '0' then xfer_tx_sn <= xfer_tx_idle; end if; when others => xfer_tx_sn <= xfer_tx_idle; end case; end process; ------------------------------------------------------------------ interframe_gap_counter: process(CLK_I) begin if rising_edge(CLK_I) then if tx_flush_en = '1' then inter_frame_gap_rdy <= '1'; elsif tx_fifo_empty = '0' then inter_frame_gap_rdy <= '0'; inter_frame_gap_cnt <= inter_frame_gap_cnt_t'high; elsif inter_frame_gap_cnt /= 0 then inter_frame_gap_cnt <= inter_frame_gap_cnt - 1; else inter_frame_gap_rdy <= '1'; end if; end if; end process; ------------------------------------------------------------------ tx_xfer_counter: process(CLK_I) begin if rising_edge(CLK_I) then if tx_xfer_cnt_set = '1' then tx_xfer_size <= (others => '0'); tx_xfer_rdy <= '0'; tx_xfer_cnt <= tx_ram_data_out_b(tx_xfer_ptr'left+16 downto 16); elsif tx_xfer_cnt_adv = '1' then if tx_xfer_cnt /= 0 then tx_xfer_cnt <= tx_xfer_cnt - 1; tx_xfer_size <= tx_xfer_size + 1; else tx_xfer_rdy <= '1'; end if; end if; end if; end process; tx_xfer_pointer: process(CLK_I) begin if rising_edge(CLK_I) then tx_fifo_we <= tx_xfer_en; if tx_xfer_ptr_set = '1' then tx_xfer_ptr <= tx_ram_data_out_b(tx_xfer_ptr'left downto 0); xfer_remain <= xfer_tag(3 downto 2); xfer_odd <= '0'; if xfer_tag(3 downto 2) /= "00" then xfer_odd <= '1'; end if; elsif tx_xfer_ptr_adv = '1' then tx_xfer_ptr <= tx_xfer_ptr + 1; end if; end if; end process; ------------------------------------------------------------------ registers_write: process(CLK_I) begin if rising_edge(CLK_I) then host_data_we <= '0'; tx_alloc_req_en <= '0'; if RST_I = '1' then rx_int_en <= '0'; tx_int_en <= '0'; tx_err <= '0'; elsif (STB_I and CYC_I and WE_I) = '1' then case ADDR_I(5 downto 2) is when "0000" => tx_err <= DAT_I(31); tx_alloc_req_en <= DAT_I(16); tx_int_en <= DAT_I(5); rx_int_en <= DAT_I(4); when "0001" => if tx_alloc_req = '0' then host_tx_size <= DAT_I(31 downto 16); end if; when "0010" => host_data_we <= '1'; host_data_reg <= DAT_I; when others => null; end case; end if; end if; end process; registers_read: process(CLK_I) begin if rising_edge(CLK_I) then ACK_O <= '0'; if (STB_I and CYC_I) = '1' then ACK_O <= not WE_I; DAT_O <= (others => '0'); case ADDR_I(5 downto 2) is when "0000" => DAT_O(31) <= tx_err; DAT_O(30) <= mii_rx_er; DAT_O(29) <= mii_col; DAT_O(28) <= mii_crs; DAT_O(16) <= tx_alloc_req; DAT_O(5) <= tx_int_en; DAT_O(4) <= rx_int_en; when "0001" => DAT_O(31 downto 16) <= host_tx_size; when "0010" => when others => null; end case; end if; end if; end process; inst_tx_ram : entity work.dpram_2w2r GENERIC MAP ( addr_width => TX_RAM_ADDR_WIDTH, data_width => 36 ) PORT MAP ( clk_a => CLK_I, clk_b => CLK_I, en_a => tx_ram_en_a, en_b => tx_ram_en_b, we_a => tx_ram_we_a, we_b => tx_ram_we_b, addr_a => tx_ram_addr_a, addr_b => tx_ram_addr_b, din_a => tx_ram_din_a, din_b => tx_ram_din_b, dout_a => tx_ram_dout_a, dout_b => tx_ram_dout_b ); -- Instantiate synchronous FIFO inst_tx_fifo: entity work.fifo_async GENERIC MAP ( addr_width => 5, data_width => 36, do_last_read_update => true ) PORT MAP ( rst => RST_I, clk_w => CLK_I, clk_r => mii_tx_clk, we => tx_fifo_we, re => tx_fifo_re, fifo_full => tx_fifo_full, fifo_empty => tx_fifo_empty, fifo_afull => open, fifo_aempty => open, data_w => tx_fifo_din, data_r => tx_fifo_dout ); inst_piso : entity work.piso GENERIC MAP ( data_width_in => 32, data_width_out => 8, msb_first => false ) PORT MAP ( rst => RST_I, clk => mii_tx_clk, dout_en => '1', din_vld => tx_data_vld, din_rdy => piso_din_rdy, din_be => tx_fifo_dout(35 downto 32), din => tx_fifo_dout(31 downto 0), dout_vld => mii_tx_en, dout => mii_tx ); irq_register: process(CLK_I) begin if rising_edge(CLK_I) then irq_tx <= tx_int_en; irq_rx <= rx_int_en; end if; end process; end behavior;