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_rx IS Generic ( f_sysclk : real := 100.0; RAM_SIZE : natural := 2048 ); Port ( clk : in STD_LOGIC; rst : in STD_LOGIC; dout_vld : out STD_LOGIC; dout : out unsigned(31 downto 0); ctrl_in : in rx_ctrl_in_t; ctrl_out : out rx_ctrl_out_t; 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_crs : in STD_LOGIC; mii_col : in STD_LOGIC ); END emac_rx; ARCHITECTURE behavior OF emac_rx IS constant RAM_ADDR_WIDTH : natural := NextExpBaseTwo(RAM_SIZE); subtype word_ptr_t is unsigned(RAM_ADDR_WIDTH-1 downto 0); -- Signals for EMAC connections signal mii_fifo_din : unsigned(35 downto 0); signal mii_fifo_dout : unsigned(35 downto 0); signal mii_fifo_we : std_logic; signal mii_fifo_re : std_logic; signal mii_fifo_empty : std_logic; signal mii_fifo_full : std_logic; signal cmd_fifo_din : unsigned(35 downto 0); signal cmd_fifo_dout : unsigned(35 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 flush_en : std_logic; signal fill_ptr : word_ptr_t; signal commit_en : std_logic; signal xfer_ram_limit : word_ptr_t; signal xfer_ram_full : std_logic; signal xfer_base : word_ptr_t; signal xfer_size : word_ptr_t; signal xfer_ptr : word_ptr_t; signal xfer_ptr_set : std_logic; signal xfer_ptr_adv : std_logic; signal xfer_cnt_set : std_logic; signal xfer_cnt_adv : std_logic; signal xfer_rdy : std_logic; signal ram_en_a : std_logic; signal ram_we_a : std_logic; signal ram_addr_a : word_ptr_t; signal ram_din_a : unsigned(31 downto 0); signal ram_dout_a : unsigned(31 downto 0); signal ram_en_b : std_logic; signal ram_we_b : std_logic; signal ram_addr_b : word_ptr_t; signal ram_din_b : unsigned(31 downto 0); signal ram_dout_b : unsigned(31 downto 0); signal sipo_dout : unsigned(31 downto 0); signal sipo_dout_be : unsigned(3 downto 0); signal sipo_dout_vld : std_logic; signal sipo_dout_en : std_logic; signal sipo_rst : std_logic; signal sipo_10mbps_dout : unsigned(7 downto 0); signal sipo_10mbps_rst : std_logic; signal sipo_10mbps_dout_vld : std_logic; signal sipo_10mbps_dout_en : std_logic; signal byte_active_r : std_logic; signal word_active_r : std_logic; signal byte_count : word_ptr_t; signal byte_count_reg : word_ptr_t; signal byte_count_reg_s : word_ptr_t; signal word_count : word_ptr_t; signal word_count_reg : word_ptr_t; signal word_count_set : std_logic; signal word_count_clr : std_logic; signal word_count_ack : std_logic; signal word_count_vld : std_logic; type host_state_t is (host_init, host_flush, host_idle); signal host_s, host_sn : host_state_t; type xfer_state_t is (xfer_init, xfer_idle, xfer_setup, xfer_active, xfer_drop, xfer_free); signal xfer_s, xfer_sn : xfer_state_t; begin ctrl_out.rx_size <= cmd_fifo_dout(31 downto 16); ctrl_out.pkt_avail <= not cmd_fifo_empty; ctrl_out.rx_er <= mii_rx_er; dout <= ram_dout_a; mii_fifo_din <= "0000" & sipo_dout; mii_fifo_we <= sipo_dout_vld; ram_en_a <= '1'; ram_din_a <= (others => '-'); ram_addr_a <= fill_ptr; ram_we_a <= '0'; ram_en_b <= '1'; ram_din_b <= mii_fifo_dout(31 downto 0); ram_addr_b <= xfer_ptr; ram_we_b <= xfer_ptr_adv; cmd_fifo_we <= commit_en; cmd_fifo_re <= ctrl_in.pkt_free_en; cmd_fifo_din <= "0000" & resize(byte_count_reg_s, 16) & resize(xfer_base, 16); ------------------------------------------------------------------ host_state_next: process(clk) begin if rising_edge(clk) then if rst = '1' then host_s <= host_init; else host_s <= host_sn; end if; end if; end process; host_state: process(host_s) begin flush_en <= '0'; host_sn <= host_s; case host_s is when host_init => flush_en <= '1'; host_sn <= host_flush; when host_flush => flush_en <= '1'; host_sn <= host_idle; when host_idle => when others => host_sn <= host_idle; end case; end process; ------------------------------------------------------------------ -- Fill stuff ------------------------------------------------------------------ fill_pointer: process(clk) begin if rising_edge(clk) then dout_vld <= ctrl_in.pkt_read_en; if ctrl_in.pkt_req_en = '1' then fill_ptr <= resize(cmd_fifo_dout(15 downto 0), word_ptr_t'length); elsif ctrl_in.pkt_read_en = '1' then fill_ptr <= fill_ptr + 1; end if; end if; end process; ------------------------------------------------------------------ -- Transfer stuff ------------------------------------------------------------------ xfer_state_next: process(clk) begin if rising_edge(clk) then if rst = '1' then xfer_s <= xfer_init; else xfer_s <= xfer_sn; end if; end if; end process; xfer_state: process(xfer_s, mii_fifo_empty, xfer_rdy, xfer_ram_full) begin commit_en <= '0'; xfer_ptr_set <= '0'; xfer_ptr_adv <= '0'; xfer_cnt_set <= '0'; xfer_cnt_adv <= '0'; word_count_ack <= '0'; mii_fifo_re <= '0'; xfer_sn <= xfer_s; case xfer_s is when xfer_init => xfer_sn <= xfer_idle; when xfer_idle => if mii_fifo_empty = '0' then xfer_sn <= xfer_setup; end if; when xfer_setup => xfer_cnt_set <= '1'; xfer_ptr_set <= '1'; xfer_sn <= xfer_active; when xfer_active => mii_fifo_re <= '1'; xfer_cnt_adv <= not mii_fifo_empty; xfer_ptr_adv <= not mii_fifo_empty; if xfer_ram_full = '1' then xfer_sn <= xfer_drop; elsif xfer_rdy = '1' then if mii_fifo_empty = '1' then xfer_sn <= xfer_free; end if; end if; when xfer_drop => mii_fifo_re <= '1'; xfer_cnt_adv <= not mii_fifo_empty; if xfer_rdy = '1' then if mii_fifo_empty = '1' then xfer_sn <= xfer_free; end if; end if; when xfer_free => word_count_ack <= '1'; commit_en <= not xfer_ram_full; xfer_sn <= xfer_idle; when others => xfer_sn <= xfer_idle; end case; end process; ------------------------------------------------------------------ pkt_lost_register: process(clk) begin if rising_edge(clk) then if flush_en = '1' then ctrl_out.pkt_lost <= '0'; elsif word_count_ack = '1' and xfer_ram_full = '1' then ctrl_out.pkt_lost <= '1'; elsif ctrl_in.pkt_free_en = '1' then ctrl_out.pkt_lost <= '0'; end if; end if; end process; xfer_limit_register: process(clk) begin if rising_edge(clk) then if xfer_ptr_set = '1' then if cmd_fifo_empty = '1' then xfer_ram_limit <= xfer_base - 1; else xfer_ram_limit <= resize(cmd_fifo_dout(15 downto 0), word_ptr_t'length) - 1; end if; end if; end if; end process; xfer_ram_full_detect: process(clk) begin if rising_edge(clk) then if xfer_ptr_set = '1' or flush_en = '1' then xfer_ram_full <= '0'; elsif xfer_ptr_adv = '1' then if xfer_ptr = xfer_ram_limit then xfer_ram_full <= '1'; end if; end if; end if; end process; xfer_counter: process(clk) begin if rising_edge(clk) then xfer_rdy <= '0'; if xfer_size = word_count_reg then xfer_rdy <= word_count_vld; end if; if xfer_cnt_set = '1' then xfer_size <= (others => '0'); elsif xfer_cnt_adv = '1' then xfer_size <= xfer_size + 1; end if; end if; end process; xfer_pointer: process(clk) begin if rising_edge(clk) then if xfer_ptr_set = '1' then xfer_ptr <= xfer_base; elsif xfer_ptr_adv = '1' then xfer_ptr <= xfer_ptr + 1; end if; end if; end process; xfer_base_logic: process(clk) begin if rising_edge(clk) then if flush_en = '1' then xfer_base <= (others => '0'); elsif commit_en = '1' then xfer_base <= xfer_ptr; end if; end if; end process; ------------------------------------------------------------------ byte_counter: process(mii_rx_clk) begin if rising_edge(mii_rx_clk) then if sipo_10mbps_dout_en = '0' then byte_count <= (others => '0'); elsif sipo_10mbps_dout_vld = '1' then byte_count <= byte_count + 1; end if; byte_active_r <= sipo_10mbps_dout_en; end if; end process; byte_count_register: process(mii_rx_clk) begin if rising_edge(mii_rx_clk) then if rst = '1' then byte_count_reg <= (others => '0'); elsif byte_active_r = '1' and sipo_10mbps_dout_vld = '0' then byte_count_reg <= byte_count; end if; end if; end process; word_counter: process(mii_rx_clk) begin if rising_edge(mii_rx_clk) then word_active_r <= sipo_dout_en; if rst = '1' or word_count_clr = '1' then word_count <= (others => '0'); elsif sipo_dout_vld = '1' then word_count <= word_count + 1; end if; end if; end process; -- MII doamin word_count_set_logic: process(mii_rx_clk) begin if rising_edge(mii_rx_clk) then if rst = '1' or word_count_clr = '1' then word_count_set <= '0'; elsif word_active_r = '1' and sipo_dout_en = '0' then word_count_set <= '1'; end if; end if; end process; -- Host doamin word_count_clr_logic: process(clk) begin if rising_edge(clk) then if rst = '1' then word_count_clr <= '0'; elsif word_count_set = '1' then if word_count_ack = '1' then word_count_clr <= '1'; end if; else word_count_clr <= '0'; end if; end if; end process; word_count_register: process(clk) begin if rising_edge(clk) then if rst = '1' or word_count_ack = '1' then word_count_vld <= '0'; elsif word_count_set = '1' then word_count_reg <= word_count; byte_count_reg_s <= byte_count_reg; word_count_vld <= '1'; end if; end if; end process; ------------------------------------------------------------------ -- Instantiate synchronous FIFO inst_cmd_fifo: entity work.fifo_sync 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 => flush_en, clk => 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_ram : entity work.dpram_1w1r GENERIC MAP ( addr_width => RAM_ADDR_WIDTH, data_width => 32 ) PORT MAP ( clka => clk, clkb => clk, en_a => ram_en_b, en_b => ram_en_a, we_a => ram_we_b, addr_a => ram_addr_b, addr_b => ram_addr_a, din_a => ram_din_b, dout_b => ram_dout_a ); -- Instantiate synchronous FIFO inst_mii_fifo: entity work.fifo_async GENERIC MAP ( addr_width => 5, data_width => 36, do_last_read_update => true ) PORT MAP ( rst => rst, clk_w => mii_rx_clk, clk_r => 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 => mii_fifo_din, data_r => mii_fifo_dout ); inst_sipo : entity work.sipo GENERIC MAP ( data_width_in => 8, data_width_out => 32, msb_first => true ) PORT MAP ( rst => sipo_rst, clk => mii_rx_clk, din_vld => sipo_10mbps_dout_vld, din_en => sipo_10mbps_dout_en, din => sipo_10mbps_dout, dout_be => sipo_dout_be, dout_vld => sipo_dout_vld, dout => sipo_dout, dout_en => sipo_dout_en ); sipo_rst <= rst or word_count_set; inst_sipo_10mbps : entity work.sipo GENERIC MAP ( data_width_in => 4, data_width_out => 8, msb_first => false ) PORT MAP ( rst => sipo_10mbps_rst, clk => mii_rx_clk, din_vld => mii_rx_dv, din_en => mii_rx_dv, din => mii_rx(3 downto 0), dout_be => open, dout_vld => sipo_10mbps_dout_vld, dout => sipo_10mbps_dout, dout_en => sipo_10mbps_dout_en ); sipo_10mbps_rst <= rst or word_count_set; end behavior;