LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; USE IEEE.NUMERIC_STD.ALL; use std.textio.all; -- Imports the standard textio package. use work.emac_types.all; use work.utils_pkg.all; ENTITY emac_tx IS Generic ( f_sysclk : real := 100.0; RAM_SIZE : natural := 2048 ); Port ( clk : in STD_LOGIC; rst : in STD_LOGIC; din_vld : in STD_LOGIC; din : in unsigned(31 downto 0); ctrl_in : in tx_ctrl_in_t; ctrl_out : out tx_ctrl_out_t; 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) ); END emac_tx; ARCHITECTURE behavior OF emac_tx IS constant RAM_ADDR_WIDTH : natural := NextExpBaseTwo(RAM_SIZE); subtype word_ptr_t is unsigned(RAM_ADDR_WIDTH-1 downto 0); signal cmd_fifo_din : unsigned(2*word_ptr_t'length-1 downto 0); signal cmd_fifo_dout : unsigned(2*word_ptr_t'length-1 downto 0); signal cmd_fifo_we : std_logic; signal cmd_fifo_re : std_logic; signal cmd_fifo_empty : std_logic; signal cmd_fifo_full : std_logic; signal reset_en : std_logic; signal fill_base : word_ptr_t; signal fill_size : word_ptr_t; signal fill_cnt : word_ptr_t; signal fill_ptr : word_ptr_t; signal fill_pre_rdy : std_logic; signal fill_bsy : std_logic; signal fill_remain : unsigned(1 downto 0); signal fill_set : std_logic; signal fill_en : std_logic; signal fill_cnt_en : std_logic; signal alloc_OK : std_logic; signal alloc_req : std_logic; signal alloc_ack : std_logic; signal alloc_size : word_ptr_t; signal alloc_remain : unsigned(1 downto 0); signal commit_en : std_logic; signal uncommit_en : std_logic; signal mem_alloc_en : std_logic; signal mem_free_en : std_logic; signal nwords_free : word_ptr_t; signal mem_free_req : std_logic; signal mem_free_ack : unsigned(1 downto 0); signal xfer_size : word_ptr_t; signal xfer_ptr : word_ptr_t; signal xfer_cnt : word_ptr_t; signal xfer_set : std_logic; signal xfer_en : std_logic; signal xfer_cnt_en : std_logic; signal xfer_bsy : std_logic; signal xfer_free_en : std_logic; signal ram_we_a : std_logic; signal ram_addr_a : word_ptr_t; signal ram_din_a : unsigned(35 downto 0); signal ram_addr_b : word_ptr_t; signal ram_dout_b : unsigned(35 downto 0); signal piso32_din : unsigned(31 downto 0); signal piso32_din_be : unsigned(3 downto 0); signal piso32_din_re : std_logic; signal piso32_din_vld : std_logic; signal piso32_dout : unsigned(7 downto 0); signal piso32_dout_vld : std_logic; signal piso32_shift_en : std_logic; signal piso8_dout_vld : std_logic; signal piso8_din_re : std_logic; signal piso8_din_vld : std_logic; signal piso8_dout : unsigned(3 downto 0); signal piso8_din : unsigned(7 downto 0); signal reset_pipe : unsigned(31 downto 0); signal Gbps_en : std_logic; signal fcs_gen_en : std_logic; signal fcs_inject_en : std_logic; signal fcs_rst : std_logic; signal fcs_en : unsigned(7 downto 0); signal fcs_vld : std_logic; signal fcs_din_vld : std_logic; signal fcs : unsigned(31 downto 0); signal fcs_rev_endian : unsigned(31 downto 0); signal mii_fifo_we : std_logic; signal mii_fifo_re : std_logic; signal mii_fifo_full : std_logic; signal mii_fifo_empty : std_logic; signal mii_fifo_re_dly : unsigned(23 downto 0); signal mii_bsy : std_logic; type host_state_t is (host_init, host_idle, host_alloc, host_setup, host_arm, host_fill, host_commit); signal host_s, host_sn : host_state_t; type xfer_state_t is (xfer_init, xfer_idle, xfer_start, xfer_preamble0, xfer_preamble1, xfer_active, xfer_crc, xfer_stop, xfer_finish); signal xfer_s, xfer_sn : xfer_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", "1000", "1100", "1110" ); signal preamble_en : std_logic; signal preamble_addr : natural range 0 to 1; type preamble_t is array (0 to 1) of unsigned(31 downto 0); constant preamble : preamble_t := ( X"55555555", X"555555D5" ); begin ctrl_out.pkt_alloc_req <= alloc_req; ctrl_out.pkt_done <= cmd_fifo_empty; ctrl_out.reset_busy <= reset_en; ram_we_a <= din_vld and fill_bsy; ram_din_a(31 downto 0) <= din; ram_din_a(35 downto 32) <= piso_byte_mask_rom(to_integer(fill_remain)) when fill_pre_rdy = '1' else "1111"; ram_addr_a <= fill_ptr; piso32_din <= preamble(preamble_addr) when preamble_en = '1' else fcs_rev_endian when fcs_inject_en = '1' else ram_dout_b(31 downto 0); piso32_din_be <= "1111" when (preamble_en = '1' or fcs_inject_en = '1') else ram_dout_b(35 downto 32); cmd_fifo_we <= commit_en; cmd_fifo_re <= uncommit_en; cmd_fifo_din <= fill_size & fill_base; ram_addr_b <= xfer_ptr; reset_en <= reset_pipe(reset_pipe'left); xfer_cnt_en <= xfer_bsy and piso32_din_re; fill_cnt_en <= fill_bsy and din_vld; mem_free_en <= not mem_free_ack(0) and mem_free_ack(1); fcs_rev_endian <= fcs(7 downto 0) & fcs(15 downto 8) & fcs(23 downto 16) & fcs(31 downto 24); ------------------------------------------------------------------ reset_gen: process(clk) begin if rising_edge(clk) then if rst = '1' or ctrl_in.reset = '1' then reset_pipe <= (others => '1'); else reset_pipe <= reset_pipe(reset_pipe'left-1 downto 0) & '0'; end if; end if; end process; ------------------------------------------------------------------ host_state_next: process(clk) begin if rising_edge(clk) then if reset_en = '1' then host_s <= host_init; else host_s <= host_sn; end if; end if; end process; host_state: process(host_s, alloc_OK, fill_bsy, ctrl_in.pkt_commit_en, alloc_req, mem_free_ack) begin fill_set <= '0'; fill_en <= '0'; commit_en <= '0'; alloc_ack <= '0'; mem_alloc_en <= '0'; host_sn <= host_s; case host_s is when host_init => host_sn <= host_idle; when host_idle => if alloc_req = '1' then host_sn <= host_alloc; end if; when host_alloc => if alloc_OK = '1' then host_sn <= host_setup; else host_sn <= host_idle; alloc_ack <= '1'; end if; when host_setup => fill_set <= '1'; host_sn <= host_arm; when host_arm => fill_en <= '1'; alloc_ack <= '1'; host_sn <= host_fill; when host_fill => fill_en <= '1'; if alloc_req = '1' then host_sn <= host_alloc; elsif ctrl_in.pkt_commit_en = '1' then host_sn <= host_commit; end if; when host_commit => if mem_free_ack = "00" then commit_en <= '1'; mem_alloc_en <= '1'; host_sn <= host_idle; end if; when others => host_sn <= host_idle; end case; end process; ------------------------------------------------------------------ -- Allocation stuff ------------------------------------------------------------------ alloc_request_logic: process(clk) begin if rising_edge(clk) then if reset_en = '1' then alloc_req <= '0'; elsif ctrl_in.pkt_alloc_en = '1' and alloc_req = '0' then alloc_req <= '1'; alloc_remain <= ctrl_in.tx_size(1 downto 0); alloc_size <= ctrl_in.tx_size(word_ptr_t'left+2 downto 2); if ctrl_in.tx_size(1 downto 0) /= "00" then alloc_size <= ctrl_in.tx_size(word_ptr_t'left+2 downto 2) + 1; end if; ctrl_out.tx_size <= resize(ctrl_in.tx_size(word_ptr_t'left downto 0), 16); elsif alloc_ack = '1' then alloc_req <= '0'; end if; end if; end process; ------------------------------------------------------------------ host_alloc_armed_register: process(clk) begin if rising_edge(clk) then if reset_en = '1' or commit_en = '1' then ctrl_out.pkt_armed <= '0'; elsif alloc_ack = '1' then ctrl_out.pkt_armed <= alloc_OK; end if; end if; end process; ------------------------------------------------------------------ alloc_eval_logic: process(clk) begin if rising_edge(clk) then alloc_OK <= '0'; if alloc_size < nwords_free then alloc_OK <= '1'; end if; end if; end process; ------------------------------------------------------------------ fill_base_logic: process(clk) begin if rising_edge(clk) then if reset_en = '1' then fill_base <= (others => '0'); elsif commit_en = '1' then fill_base <= fill_ptr; end if; end if; end process; ------------------------------------------------------------------ mem_free_counter: process(clk) begin if rising_edge(clk) then if reset_en = '1' then nwords_free <= (others => '1'); elsif mem_alloc_en = '1' then nwords_free <= nwords_free - fill_size; elsif mem_free_en = '1' then nwords_free <= nwords_free + xfer_size; end if; end if; end process; ------------------------------------------------------------------ mem_free_mii: process(mii_tx_clk) begin if rising_edge(mii_tx_clk) then if reset_en = '1' then mem_free_req <= '0'; elsif mem_free_ack(0) = '0' then if xfer_free_en = '1' then mem_free_req <= '1'; end if; else mem_free_req <= '0'; end if; end if; end process; ------------------------------------------------------------------ mem_free_host: process(clk) begin if rising_edge(clk) then mem_free_ack(1) <= mem_free_ack(0); if reset_en = '1' then mem_free_ack <= "00"; elsif mem_free_req = '1' then if mem_alloc_en = '0' then mem_free_ack(0) <= '1'; end if; else mem_free_ack(0) <= '0'; end if; end if; end process; ------------------------------------------------------------------ -- Fill stuff ------------------------------------------------------------------ fill_counter: process(clk) begin if rising_edge(clk) then if fill_set = '1' then fill_bsy <= '0'; fill_pre_rdy <= '0'; fill_cnt <= alloc_size; fill_size <= alloc_size; fill_remain <= alloc_remain; elsif fill_en = '1' then fill_bsy <= '1'; if fill_cnt_en = '1' or fill_bsy = '0' then if fill_cnt /= 1 then fill_cnt <= fill_cnt - 1; else fill_pre_rdy <= '1'; end if; if fill_pre_rdy = '1' then fill_bsy <= '0'; end if; end if; end if; end if; end process; ------------------------------------------------------------------ fill_pointer: process(clk) begin if rising_edge(clk) then if fill_set = '1' then fill_ptr <= fill_base; elsif fill_cnt_en = '1' then fill_ptr <= fill_ptr + 1; end if; end if; end process; ------------------------------------------------------------------ -- Transfer stuff ------------------------------------------------------------------ host2xfer_sync_register: process(mii_tx_clk) begin if rising_edge(mii_tx_clk) then Gbps_en <= ctrl_in.Gbps_en; fcs_gen_en <= ctrl_in.fcs_gen_en; end if; end process; ------------------------------------------------------------------ xfer_counter: process(mii_tx_clk) begin if rising_edge(mii_tx_clk) then xfer_bsy <= '0'; if xfer_set = '1' then xfer_cnt <= cmd_fifo_dout(cmd_fifo_dout'left downto word_ptr_t'length); xfer_size <= (others => '0'); elsif xfer_en = '1' then xfer_bsy <= '1'; if xfer_cnt_en = '1' or xfer_bsy = '0' then if xfer_cnt /= 0 then xfer_cnt <= xfer_cnt - 1; xfer_size <= xfer_size + 1; else xfer_bsy <= '0'; end if; end if; end if; end if; end process; ------------------------------------------------------------------ xfer_pointer: process(mii_tx_clk) begin if rising_edge(mii_tx_clk) then if xfer_set = '1' then xfer_ptr <= cmd_fifo_dout(xfer_ptr'left downto 0); elsif xfer_en = '1' then if xfer_bsy = '0' or xfer_cnt_en = '1' then xfer_ptr <= xfer_ptr + 1; end if; end if; end if; end process; ------------------------------------------------------------------ xfer_state_next: process(mii_tx_clk) begin if rising_edge(mii_tx_clk) then if reset_en = '1' then xfer_s <= xfer_init; else xfer_s <= xfer_sn; end if; end if; end process; xfer_state: process(xfer_s, mii_bsy, cmd_fifo_empty, piso32_din_re, piso32_dout_vld, xfer_bsy, fcs_gen_en) begin piso32_din_vld <= '0'; preamble_en <= '0'; preamble_addr <= 0; xfer_set <= '0'; xfer_en <= '0'; xfer_free_en <= '0'; fcs_rst <= '0'; fcs_inject_en <= '0'; uncommit_en <= '0'; xfer_sn <= xfer_s; case xfer_s is when xfer_init => xfer_sn <= xfer_idle; when xfer_idle => if cmd_fifo_empty = '0' and mii_bsy = '0' then xfer_sn <= xfer_start; end if; when xfer_start => if cmd_fifo_empty = '0' then xfer_sn <= xfer_preamble0; end if; when xfer_preamble0 => xfer_set <= '1'; fcs_rst <= '1'; preamble_addr <= 0; preamble_en <= '1'; piso32_din_vld <= '1'; if piso32_din_re = '1' then xfer_sn <= xfer_preamble1; end if; when xfer_preamble1 => preamble_addr <= 1; preamble_en <= '1'; piso32_din_vld <= '1'; if piso32_din_re = '1' then xfer_sn <= xfer_active; xfer_en <= '1'; end if; when xfer_active => piso32_din_vld <= xfer_bsy; xfer_en <= '1'; if xfer_bsy = '0' then xfer_sn <= xfer_stop; end if; when xfer_stop => if piso32_dout_vld = '0' then xfer_sn <= xfer_finish; if fcs_gen_en = '1' then xfer_sn <= xfer_crc; end if; end if; when xfer_crc => fcs_inject_en <= piso32_din_re; piso32_din_vld <= '1'; if piso32_din_re = '1' then xfer_sn <= xfer_finish; end if; when xfer_finish => uncommit_en <= '1'; xfer_free_en <= '1'; xfer_sn <= xfer_idle; when others => xfer_sn <= xfer_idle; end case; end process; ------------------------------------------------------------------ inst_ram : entity work.dpram_1w1r2c_ro GENERIC MAP ( addr_width => RAM_ADDR_WIDTH, data_width => ram_din_a'length ) PORT MAP ( clk_a => clk, clk_b => mii_tx_clk, we_a => ram_we_a, re_b => piso32_din_re, addr_a => ram_addr_a, addr_b => ram_addr_b, din_a => ram_din_a, dout_b => ram_dout_b ); ------------------------------------------------------------------ -- Instantiate synchronous FIFO inst_cmd_fifo: entity work.fifo_async GENERIC MAP ( addr_width => NextExpBaseTwo(RAM_SIZE) - 4, -- RAMSIZE(words)/MIN_PACKET_LEN(words) data_width => cmd_fifo_din'length, do_last_read_update => true ) PORT MAP ( rst => reset_en, clk_w => clk, clk_r => mii_tx_clk, we => cmd_fifo_we, re => cmd_fifo_re, fifo_full => cmd_fifo_full, fifo_empty => cmd_fifo_empty, fifo_afull => open, fifo_aempty => open, data_w => cmd_fifo_din, data_r => cmd_fifo_dout ); ------------------------------------------------------------------ inst_piso32 : entity work.piso GENERIC MAP ( data_width_in => 32, data_width_out => 8, msb_first => true ) PORT MAP ( rst => reset_en, clk => mii_tx_clk, din_vld => piso32_din_vld, din_re => piso32_din_re, din_be => piso32_din_be, din => piso32_din, dout_vld => piso32_dout_vld, shift_en => piso32_shift_en, dout => piso32_dout ); piso32_shift_en <= not mii_fifo_full; --'1' when Gbps_en = '1' else piso8_din_re; ------------------------------------------------------------------ fcs_enable_gen: process(mii_tx_clk) begin if rising_edge(mii_tx_clk) then if fcs_rst = '1' then fcs_en <= (others => '0'); elsif piso32_dout_vld = '1' then fcs_en <= fcs_en(fcs_en'left-1 downto 0) & '1'; end if; end if; end process; ------------------------------------------------------------------ inst_fcs: entity work.crc32 GENERIC MAP ( crc32_init => X"00000000" ) PORT MAP ( rst => fcs_rst, clk => mii_tx_clk, din_vld => fcs_din_vld, din => piso32_dout, crc32_vld => fcs_vld, crc32_out => fcs ); fcs_din_vld <= fcs_en(fcs_en'left) and piso32_dout_vld and piso32_shift_en; ------------------------------------------------------------------ -- ensures continous MII-data stream and also acts as Inter Frame Gap delay mii_fifo_re_dly_gen: process(mii_tx_clk) begin if rising_edge(mii_tx_clk) then if mii_fifo_empty = '1' then if Gbps_en = '0' then mii_fifo_re_dly <= X"000000"; -- 100 mbps else mii_fifo_re_dly <= X"000FFF"; -- Gigabit end if; else mii_fifo_re_dly <= mii_fifo_re_dly(mii_fifo_re_dly'left-1 downto 0) & not mii_fifo_empty; end if; end if; end process; mii_bsy <= mii_fifo_re_dly(mii_fifo_re_dly'left); ------------------------------------------------------------------ -- Instantiate synchronous FIFO inst_mii_fifo: entity work.fifo_sync GENERIC MAP ( addr_width => 4, data_width => piso32_dout'length, do_last_read_update => true ) PORT MAP ( rst => reset_en, clk => mii_tx_clk, we => mii_fifo_we, re => mii_fifo_re, fifo_full => mii_fifo_full, fifo_empty => mii_fifo_empty, fifo_afull => open, fifo_aempty => open, data_w => piso32_dout, data_r => piso8_din ); mii_fifo_re <= mii_bsy when Gbps_en = '1' else (piso8_din_re and mii_bsy); mii_fifo_we <= piso32_dout_vld; ------------------------------------------------------------------ inst_piso8 : entity work.piso GENERIC MAP ( data_width_in => 8, data_width_out => 4, msb_first => false ) PORT MAP ( rst => reset_en, clk => mii_tx_clk, din_vld => piso8_din_vld, din_re => piso8_din_re, din_be => "11", din => piso8_din, dout_vld => piso8_dout_vld, shift_en => '1', dout => piso8_dout ); piso8_din_vld <= not mii_fifo_empty and mii_bsy; ------------------------------------------------------------------ mii_output_register: process(mii_tx_clk) begin if rising_edge(mii_tx_clk) then mii_tx_er <= '0'; if Gbps_en = '1' then mii_tx_en <= piso8_din_vld; mii_tx <= piso8_din; else mii_tx_en <= piso8_dout_vld; mii_tx <= "0000" & piso8_dout; end if; end if; end process; ------------------------------------------------------------------ end behavior;