diff --git a/lib/emac/src/emac_rx.vhd b/lib/emac/src/emac_rx.vhd new file mode 100644 index 0000000..5cf8540 --- /dev/null +++ b/lib/emac/src/emac_rx.vhd @@ -0,0 +1,248 @@ +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 : 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 fifo_din : unsigned(35 downto 0); + signal fifo_dout : unsigned(35 downto 0); + signal fifo_we : std_logic; + signal fifo_re : std_logic; + signal fifo_empty : std_logic; + signal fifo_full : std_logic; + + signal flush_en : std_logic; + signal flush_rdy : std_logic; + signal flush_ptr_set : std_logic; + signal flush_ptr : word_ptr_t; + + signal fill_size : word_ptr_t; + signal fill_cnt : word_ptr_t; + signal fill_ptr : word_ptr_t; + signal fill_rdy : std_logic; + signal fill_cnt_set : std_logic; + signal fill_cnt_adv : std_logic; + signal fill_ptr_set : std_logic; + signal fill_ptr_adv : std_logic; + signal fill_remain : unsigned(1 downto 0); + + signal nwords_free : word_ptr_t; + + signal free_en : std_logic; + signal uncommit_en : 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(35 downto 0); + signal ram_dout_a : unsigned(35 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(35 downto 0); + signal ram_dout_b : unsigned(35 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_rst : std_logic; + + signal byte_count : word_ptr_t; + signal byte_count_reg : word_ptr_t; + signal rx_dv_r : std_logic; + + type xfer_state_t is (xfer_init, xfer_idle, xfer_setup, xfer_wait, xfer_active, xfer_free); + signal xfer_s, xfer_sn : xfer_state_t; + + alias ram_data_in_a is ram_din_a(31 downto 0); + alias ram_tag_in_a is ram_din_a(35 downto 32); + alias ram_data_out_a is ram_dout_a(31 downto 0); + alias ram_tag_out_a is ram_dout_a(35 downto 32); + + alias ram_data_in_b is ram_din_b(31 downto 0); + alias ram_tag_in_b is ram_din_b(35 downto 32); + alias ram_data_out_b is ram_dout_b(31 downto 0); + alias ram_tag_out_b is ram_dout_b(35 downto 32); + +begin + + dout <= sipo_dout; + fifo_din <= sipo_dout_be & sipo_dout; + +------------------------------------------------------------------ +-- 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_rx_dv) + begin + + xfer_sn <= xfer_s; + + case xfer_s is + + when xfer_init => + xfer_sn <= xfer_idle; + + when xfer_idle => + xfer_sn <= xfer_setup; + + when xfer_setup => + xfer_sn <= xfer_wait; + + when xfer_wait => + if mii_rx_dv = '1' then + xfer_sn <= xfer_active; + end if; + + when xfer_active => + if mii_rx_dv = '0' then + xfer_sn <= xfer_free; + end if; + + when xfer_free => + xfer_sn <= xfer_init; + + when others => + xfer_sn <= xfer_idle; + end case; + + end process; + +------------------------------------------------------------------ +byte_counter: + process(mii_rx_clk) + begin + if rising_edge(mii_rx_clk) then + if mii_rx_dv = '0' then + byte_count <= (others => '0'); + else + byte_count <= byte_count + 1; + end if; + rx_dv_r <= mii_rx_dv; + 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 rx_dv_r = '1' and mii_rx_dv = '0' then + byte_count_reg <= byte_count; + end if; + end if; + end process; + + sipo_rst <= not mii_rx_dv; +------------------------------------------------------------------ +inst_ram : entity work.dpram_2w2r + GENERIC MAP + ( + addr_width => RAM_ADDR_WIDTH, + data_width => 36 + ) + PORT MAP + ( + clk_a => clk, + clk_b => clk, + en_a => ram_en_a, + en_b => ram_en_b, + we_a => ram_we_a, + we_b => ram_we_b, + addr_a => ram_addr_a, + addr_b => ram_addr_b, + din_a => ram_din_a, + din_b => ram_din_b, + dout_a => ram_dout_a, + dout_b => ram_dout_b + ); + +-- Instantiate synchronous FIFO +inst_fifo: entity work.fifo_async + GENERIC MAP + ( + addr_width => 5, + data_width => 36, + do_last_read_update => true + ) + PORT MAP + ( + rst => rst, + clk_w => clk, + clk_r => mii_rx_clk, + we => fifo_we, + re => fifo_re, + fifo_full => fifo_full, + fifo_empty => fifo_empty, + fifo_afull => open, + fifo_aempty => open, + data_w => fifo_din, + data_r => fifo_dout + ); + +inst_sipo : entity work.sipo + GENERIC MAP + ( + data_width_in => 8, + data_width_out => 32, + msb_first => false + ) + PORT MAP + ( + rst => sipo_rst, + clk => mii_rx_clk, + enable => '1', + din_en => mii_rx_dv, + din => mii_rx, + dout_be => sipo_dout_be, + dout_vld => sipo_dout_vld, + dout => sipo_dout + + ); + +end behavior; diff --git a/lib/emac/src/emac_top_jb.vhd b/lib/emac/src/emac_top_jb.vhd new file mode 100644 index 0000000..5cb5924 --- /dev/null +++ b/lib/emac/src/emac_top_jb.vhd @@ -0,0 +1,189 @@ +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_top_jb IS + Generic + ( + f_sysclk : real := 100.0; + TX_RAM_SIZE : natural := 2048; + RX_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_top_jb; + +ARCHITECTURE behavior OF emac_top_jb IS + + -- Signals for EMAC connections + signal tx_ctrl_in : tx_ctrl_in_t; + signal tx_ctrl_out : tx_ctrl_out_t; + signal tx_din : unsigned(31 downto 0); + + signal rx_ctrl_in : rx_ctrl_in_t; + signal rx_ctrl_out : rx_ctrl_out_t; + signal rx_dout : unsigned(31 downto 0); + + signal rx_int_en : std_logic; + signal tx_int_en : std_logic; + signal irq_rx : std_logic; + signal irq_tx : std_logic; + + signal if_rdy : std_logic; +begin + + SRDY_O <= CYC_I and if_rdy; + INT_O <= irq_rx or irq_tx; + mii_gtx_clk <= '0'; + +------------------------------------------------------------------ +registers_write: + process(CLK_I) + begin + if rising_edge(CLK_I) then + if_rdy <= '1'; + tx_ctrl_in.data_vld <= '0'; + tx_ctrl_in.alloc_req_size_vld <= '0'; + tx_ctrl_in.alloc_req_en <= '0'; + if RST_I = '1' then + rx_int_en <= '0'; + tx_int_en <= '0'; + tx_ctrl_in.tx_er <= '0'; + elsif (STB_I and CYC_I and WE_I) = '1' then + case ADDR_I(5 downto 2) is + + when "0000" => + tx_ctrl_in.tx_er <= DAT_I(31); + tx_ctrl_in.alloc_req_en <= DAT_I(16); + tx_int_en <= DAT_I(5); + rx_int_en <= DAT_I(4); + + when "0001" => + if_rdy <= '0'; -- allow request size to propagate inside TX module + tx_ctrl_in.alloc_req_size_vld <= '1'; + tx_din <= X"0000" & DAT_I(31 downto 16); + + when "0010" => + tx_ctrl_in.data_vld <= '1'; + tx_din <= 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_ctrl_in.tx_er; + DAT_O(30) <= mii_rx_er; + DAT_O(29) <= mii_col; + DAT_O(28) <= mii_crs; + DAT_O(16) <= tx_ctrl_out.alloc_req; + DAT_O(5) <= tx_int_en; + DAT_O(4) <= rx_int_en; + + when "0001" => + DAT_O(31 downto 16) <= tx_ctrl_out.alloc_req_size(15 downto 0); + + when "0010" => + + when others => null; + end case; + end if; + end if; + end process; + +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; + +inst_emac_rx : entity work.emac_rx + GENERIC MAP + ( + f_sysclk => f_sysclk, + RAM_SIZE => RX_RAM_SIZE + ) + PORT MAP + ( + clk => CLK_I, + rst => RST_I, + dout => rx_dout, + ctrl_in => rx_ctrl_in, + ctrl_out => rx_ctrl_out, + mii_rx_clk => mii_rx_clk, + mii_rx_dv => mii_rx_dv, + mii_rx_er => mii_rx_er, + mii_rx => mii_rx, + mii_crs => mii_crs, + mii_col => mii_col + + ); + + +inst_emac_tx : entity work.emac_tx + GENERIC MAP + ( + f_sysclk => f_sysclk, + RAM_SIZE => TX_RAM_SIZE + ) + PORT MAP + ( + clk => CLK_I, + rst => RST_I, + din => tx_din, + ctrl_in => tx_ctrl_in, + ctrl_out => tx_ctrl_out, + mii_tx_clk => mii_tx_clk, + mii_tx_en => mii_tx_en, + mii_tx_er => mii_tx_er, + mii_tx => mii_tx + + ); + + +end behavior; diff --git a/lib/emac/src/emac_tx.vhd b/lib/emac/src/emac_tx.vhd new file mode 100644 index 0000000..a79fc53 --- /dev/null +++ b/lib/emac/src/emac_tx.vhd @@ -0,0 +1,571 @@ +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 : 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 fifo_din : unsigned(35 downto 0); + signal fifo_dout : unsigned(35 downto 0); + signal fifo_we : std_logic; + signal fifo_re : std_logic; + signal fifo_empty : std_logic; + signal fifo_full : std_logic; + + signal flush_en : std_logic; + signal flush_rdy : std_logic; + signal flush_ptr_set : std_logic; + signal flush_ptr : word_ptr_t; + + signal alloc_req_size : unsigned(15 downto 0); + + signal fill_size : word_ptr_t; + signal fill_cnt : word_ptr_t; + signal fill_ptr : word_ptr_t; + signal fill_rdy : std_logic; + signal fill_cnt_set : std_logic; + signal fill_cnt_adv : std_logic; + signal fill_ptr_set : std_logic; + signal fill_ptr_adv : std_logic; + signal fill_remain : unsigned(1 downto 0); + + signal alloc_OK : std_logic; + signal alloc_req : std_logic; + signal alloc_ack : std_logic; + + signal nwords_free : word_ptr_t; + signal alloc_base : word_ptr_t; + signal alloc_size : word_ptr_t; + signal alloc_en : std_logic; + + signal free_en : std_logic; + signal commit_en : std_logic; + signal uncommit_en : std_logic; + + signal xfer_size : word_ptr_t; + signal xfer_ptr : word_ptr_t; + signal xfer_cnt : 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 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 ram_en_a : 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_dout_a : unsigned(35 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(35 downto 0); + signal ram_dout_b : unsigned(35 downto 0); + + signal piso_din_be : unsigned(3 downto 0); + signal piso_din_rdy : std_logic; + signal 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 ram_data_in_a is ram_din_a(31 downto 0); + alias ram_tag_in_a is ram_din_a(35 downto 32); + alias ram_data_out_a is ram_dout_a(31 downto 0); + alias ram_tag_out_a is ram_dout_a(35 downto 32); + + alias ram_data_in_b is ram_din_b(31 downto 0); + alias ram_tag_in_b is ram_din_b(35 downto 32); + alias ram_data_out_b is ram_dout_b(31 downto 0); + alias ram_tag_out_b is ram_dout_b(35 downto 32); + + type host_state_t is (host_init, host_flush, 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_setup, xfer_wait, xfer_active, xfer_free); + 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", + "0001", + "0011", + "0111" + ); + + +begin + + ctrl_out.alloc_req <= alloc_req; + + mii_tx_er <= ctrl_in.tx_er; + + ram_tag_in_b <= "0000"; + ram_en_b <= not fifo_full or uncommit_en or flush_en; + ram_we_b <= uncommit_en or flush_en; + ram_addr_b <= flush_ptr when (flush_en = '1' or uncommit_en = '1') else xfer_ptr; + xfer_tag <= ram_tag_out_b; + + ram_en_a <= '1'; + ram_we_a <= commit_en or fill_cnt_adv; + ram_tag_in_a <= fill_remain & "01" when commit_en = '1' else "0000"; + ram_data_in_a <= resize(fill_size, 16) & resize(alloc_base, 16) when commit_en = '1' else din; + ram_addr_a <= alloc_base when commit_en = '1' else fill_ptr; + + piso_din_be <= (others => '1'); + fifo_re <= piso_din_rdy; + data_vld <= not fifo_empty; + + fifo_din(35 downto 32) <= piso_byte_mask_rom(to_integer(xfer_remain)) when xfer_remain_en = '1' else "1111"; + fifo_din(31 downto 0) <= ram_data_out_b; + +------------------------------------------------------------------ +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, flush_rdy, alloc_req, alloc_OK, fill_rdy, ctrl_in.data_vld) + begin + + flush_en <= '0'; + flush_ptr_set <= '0'; + alloc_ack <= '0'; + fill_cnt_set <= '0'; + fill_cnt_adv <= '0'; + fill_ptr_set <= '0'; + fill_ptr_adv <= '0'; + commit_en <= '0'; + alloc_en <= '0'; + + host_sn <= host_s; + + case host_s is + + when host_init => + flush_ptr_set <= '1'; + host_sn <= host_flush; + + when host_flush => + flush_en <= '1'; + if flush_rdy = '1' then + flush_en <= '0'; + host_sn <= host_idle; + end if; + + 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; + alloc_ack <= '1'; + end if; + + when host_setup => + fill_cnt_set <= '1'; + fill_ptr_set <= '1'; + host_sn <= host_arm; + + when host_arm => + fill_ptr_adv <= '1'; + fill_cnt_adv <= '1'; + host_sn <= host_fill; + + when host_fill => + fill_cnt_adv <= ctrl_in.data_vld; + fill_ptr_adv <= not fill_rdy and ctrl_in.data_vld; + if alloc_req = '1' then + host_sn <= host_alloc; + elsif fill_rdy = '1' then + host_sn <= host_commit; + end if; + + when host_commit => + commit_en <= '1'; + alloc_en <= '1'; + host_sn <= host_idle; + + when others => + host_sn <= host_idle; + end case; + + end process; + +------------------------------------------------------------------ +-- Allocation stuff +------------------------------------------------------------------ +alloc_req_size_register: + process(clk) + begin + if rising_edge(clk) then + if ctrl_in.alloc_req_size_vld = '1' then + alloc_req_size <= din(15 downto 0); + end if; + end if; + end process; + +------------------------------------------------------------------ +alloc_request_logic: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' then + alloc_req <= '0'; + elsif ctrl_in.alloc_req_en = '1' and alloc_req = '0' then + alloc_req <= '1'; + fill_remain <= alloc_req_size(1 downto 0); + fill_size <= alloc_req_size(word_ptr_t'left+2 downto 2); + if din(1 downto 0) /= "00" then + fill_size <= alloc_req_size(word_ptr_t'left+2 downto 2) + 1; + end if; + ctrl_out.alloc_req_size <= resize(alloc_req_size(word_ptr_t'left downto 0), 16); + + elsif alloc_ack = '1' then + alloc_req <= '0'; + alloc_size <= fill_size; + end if; + end if; + end process; + +------------------------------------------------------------------ +alloc_base_logic: + process(clk) + begin + if rising_edge(clk) then + if flush_en = '1' then + alloc_base <= (others => '0'); + elsif commit_en = '1' then + alloc_base <= fill_ptr; + end if; + end if; + end process; + +------------------------------------------------------------------ +alloc_eval_logic: + process(clk) + begin + if rising_edge(clk) then + alloc_OK <= '0'; + if alloc_req = '1' then + if fill_size < nwords_free then + alloc_OK <= '1'; + end if; + end if; + end if; + end process; + +------------------------------------------------------------------ +nwords_free_counter: + process(clk) + begin + if rising_edge(clk) then + if flush_en = '1' then + nwords_free <= (others => '1'); + elsif alloc_en = '1' then + nwords_free <= nwords_free - fill_size; + elsif free_en = '1' then + nwords_free <= nwords_free + xfer_size; + end if; + end if; + end process; + +------------------------------------------------------------------ +flush_counter: + process(clk) + begin + if rising_edge(clk) then + if flush_en = '0' then + flush_rdy <= '0'; + if flush_ptr_set = '1' then + flush_ptr <= (others => '1'); + elsif xfer_ptr_set = '1' then + flush_ptr <= ram_data_out_b(flush_ptr'left downto 0); + end if; + elsif flush_ptr /= 0 then + flush_ptr <= flush_ptr - 1; + else + flush_rdy <= '1'; + end if; + end if; + end process; + +------------------------------------------------------------------ +-- Fill stuff +------------------------------------------------------------------ +fill_counter: + process(clk) + begin + if rising_edge(clk) then + if fill_cnt_set = '1' then + fill_rdy <= '0'; + fill_cnt <= alloc_size; + elsif fill_cnt_adv = '1' then + if fill_cnt /= 0 then + fill_cnt <= fill_cnt - 1; + else + fill_rdy <= '1'; + end if; + end if; + end if; + end process; + +fill_pointer: + process(clk) + begin + if rising_edge(clk) then + if fill_ptr_set = '1' then + fill_ptr <= alloc_base; + elsif fill_ptr_adv = '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, xfer_tag, xfer_odd, xfer_rdy, inter_frame_gap_rdy, alloc_en, fifo_full) + begin + + xfer_cnt_set <= '0'; + xfer_cnt_adv <= '0'; + xfer_ptr_set <= '0'; + xfer_ptr_adv <= '0'; + xfer_remain_en <= '0'; + fifo_we <= '0'; + free_en <= '0'; + uncommit_en <= '0'; + + xfer_sn <= xfer_s; + + case xfer_s is + + when xfer_init => + xfer_sn <= xfer_idle; + + when xfer_idle => + if xfer_tag(0) = '1' then + xfer_ptr_set <= '1'; + xfer_cnt_set <= '1'; + xfer_sn <= xfer_wait; + end if; + + when xfer_wait => + if inter_frame_gap_rdy = '1' then + xfer_sn <= xfer_setup; + xfer_ptr_adv <= '1'; + xfer_cnt_adv <= '1'; + uncommit_en <= '1'; + end if; + + when xfer_setup => + xfer_ptr_adv <= '1'; + xfer_cnt_adv <= '1'; + xfer_sn <= xfer_active; + + when xfer_active => + fifo_we <= '1'; + xfer_cnt_adv <= not fifo_full; + xfer_ptr_adv <= not fifo_full; + if xfer_rdy = '1' then + xfer_remain_en <= xfer_odd; + xfer_cnt_adv <= '0'; + xfer_ptr_adv <= '0'; + if fifo_full = '0' then + xfer_sn <= xfer_free; + end if; + end if; + + when xfer_free => + free_en <= not alloc_en; + if alloc_en = '0' then + xfer_sn <= xfer_idle; + end if; + + when others => + xfer_sn <= xfer_idle; + end case; + + end process; + +------------------------------------------------------------------ +interframe_gap_counter: + process(clk) + begin + if rising_edge(clk) then + if flush_en = '1' then + inter_frame_gap_rdy <= '1'; + elsif 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; + +------------------------------------------------------------------ +xfer_counter: + process(clk) + begin + if rising_edge(clk) then + if xfer_cnt_set = '1' then + xfer_size <= (others => '0'); + xfer_rdy <= '0'; + xfer_cnt <= ram_data_out_b(xfer_ptr'left+16 downto 16); + elsif xfer_cnt_adv = '1' then + if xfer_cnt /= 0 then + xfer_cnt <= xfer_cnt - 1; + xfer_size <= xfer_size + 1; + else + xfer_rdy <= '1'; + end if; + end if; + end if; + + end process; + +xfer_pointer: + process(clk) + begin + if rising_edge(clk) then + if xfer_ptr_set = '1' then + xfer_ptr <= ram_data_out_b(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 xfer_ptr_adv = '1' then + xfer_ptr <= xfer_ptr + 1; + end if; + end if; + + end process; + +inst_ram : entity work.dpram_2w2r + GENERIC MAP + ( + addr_width => RAM_ADDR_WIDTH, + data_width => 36 + ) + PORT MAP + ( + clk_a => clk, + clk_b => clk, + en_a => ram_en_a, + en_b => ram_en_b, + we_a => ram_we_a, + we_b => ram_we_b, + addr_a => ram_addr_a, + addr_b => ram_addr_b, + din_a => ram_din_a, + din_b => ram_din_b, + dout_a => ram_dout_a, + dout_b => ram_dout_b + ); + +-- Instantiate synchronous FIFO +inst_fifo: entity work.fifo_async + GENERIC MAP + ( + addr_width => 4, + data_width => 36, + do_last_read_update => true + ) + PORT MAP + ( + rst => rst, + clk_w => clk, + clk_r => mii_tx_clk, + we => fifo_we, + re => fifo_re, + fifo_full => fifo_full, + fifo_empty => fifo_empty, + fifo_afull => open, + fifo_aempty => open, + data_w => fifo_din, + data_r => fifo_dout + ); + +inst_piso : entity work.piso + GENERIC MAP + ( + data_width_in => 32, + data_width_out => 8, + msb_first => false + ) + PORT MAP + ( + rst => rst, + clk => mii_tx_clk, + din_vld => data_vld, + din_rdy => piso_din_rdy, + din_be => fifo_dout(35 downto 32), + din => fifo_dout(31 downto 0), + dout_en => mii_tx_en, + dout => mii_tx + + ); + +end behavior; diff --git a/lib/emac/src/emac_types.vhd b/lib/emac/src/emac_types.vhd new file mode 100644 index 0000000..979a66e --- /dev/null +++ b/lib/emac/src/emac_types.vhd @@ -0,0 +1,47 @@ +-- Package File Template +-- +-- Purpose: This package defines supplemental types, subtypes, +-- constants, and functions + + +library IEEE; +use IEEE.STD_LOGIC_1164.ALL; +use IEEE.NUMERIC_STD.ALL; +use IEEE.MATH_REAL.ALL; + +package emac_types is + + -- Constants + + -- Types + type tx_ctrl_in_t is record + tx_er : std_logic; + alloc_req_size_vld : std_logic; + data_vld : std_logic; + alloc_req_en : std_logic; + end record; + + type tx_ctrl_out_t is record + alloc_req_size : unsigned(15 downto 0); + alloc_req : std_logic; + end record; + + type rx_ctrl_in_t is record + dummy : std_logic; + end record; + + type rx_ctrl_out_t is record + rx_er : std_logic; + data_vld : std_logic; + end record; + + -- Functions + +end emac_types; + + +package body emac_types is + +end emac_types; + + diff --git a/lib/emac/src/tb_emac_top_jb.vhd b/lib/emac/src/tb_emac_top_jb.vhd new file mode 100644 index 0000000..5cf347b --- /dev/null +++ b/lib/emac/src/tb_emac_top_jb.vhd @@ -0,0 +1,288 @@ +------------------------------------------------------------------------- +-- Project: JCPU, a portable 8-bit RISC CPU written in VHDL +-- This file: testbench for system test using Xilinx ML-402 + +-- Copyright (C) 2007 J. Ahrensfeld + +-- This library is free software; you can redistribute it and/or +-- modify it under the terms of the GNU Lesser General Public +-- License as published by the Free Software Foundation; either +-- version 2.1 of the License, or (at your option) any later version. + +-- This library is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +-- Lesser General Public License for more details. + +-- You should have received a copy of the GNU Lesser General Public +-- License along with this library; if not, write to the Free Software +-- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +-- For questions and ideas, please contact the author at jens@jayfield.org + +----------------------------------------------------------------------- + +LIBRARY ieee; +use IEEE.STD_LOGIC_1164.ALL; +USE ieee.numeric_std.ALL; + +ENTITY tb_emac_top_jb IS +END tb_emac_top_jb; + +ARCHITECTURE behavior OF tb_emac_top_jb IS + + constant CLK_PERIOD : time := 10 ns; + constant MII_CLK_PERIOD : time := 8 ns; + + signal CLK : std_logic := '1'; + signal RST : std_logic := '1'; + + -- Slave + signal CYC_I : std_logic := '0'; + signal STB_I : std_logic := '0'; + signal WE_I : std_logic := '0'; + signal SEL_I : unsigned(3 downto 0) := (others => '1'); + signal ACK_O : std_logic; + signal INT_O : std_logic; + signal MRDY_I : std_logic := '0'; + signal SRDY_O : std_logic; + signal ADDR_I : unsigned(31 downto 0) := (others => '-'); + signal DAT_I : unsigned(31 downto 0) := (others => '-'); + signal DAT_O : unsigned(31 downto 0) := (others => '-'); + signal DAT_O_reg : unsigned(31 downto 0) := (others => '-'); + + -- MII signals + signal mii_rx_clk_board : std_logic := '1'; + signal mii_tx_clk_board : std_logic := '1'; + signal mii_rx_clk : std_logic := '1'; + signal mii_tx_clk : std_logic := '1'; + signal mii_rx : unsigned(7 downto 0) := (others => '-'); + signal mii_tx : unsigned(7 downto 0); + signal mii_rx_dv : std_logic := '0'; + signal mii_rx_er : std_logic := '0'; + signal mii_tx_en : std_logic; + signal mii_tx_er : std_logic; + signal mii_crs : std_logic := '0'; + signal mii_col : std_logic := '0'; + signal mii_gtx_clk : std_logic; + + signal loop_back_en : std_logic := '0'; + + type emac_action_t is (emac_idle, emac_alloc, emac_write, emac_read); + signal emac_action : emac_action_t; + +BEGIN + +inst_emac_top_jb : entity work.emac_top_jb + GENERIC MAP + ( + f_sysclk => 100.0, + RX_RAM_SIZE => 2048, + TX_RAM_SIZE => 2048 + ) + PORT MAP + ( + CLK_I => CLK, + RST_I => RST, + INT_O => INT_O, + CYC_I => CYC_I, + STB_I => STB_I, + SEL_I => SEL_I, + WE_I => WE_I, + ACK_O => ACK_O, + SRDY_O => SRDY_O, + MRDY_I => MRDY_I, + ADDR_I => ADDR_I, + DAT_I => DAT_I, + DAT_O => DAT_O, + mii_rx_clk => mii_rx_clk_board, + mii_rx_dv => mii_rx_dv, + mii_rx_er => mii_rx_er, + mii_rx => mii_rx, + mii_tx_clk => mii_tx_clk_board, + mii_tx_en => mii_tx_en, + mii_tx_er => mii_tx_er, + mii_tx => mii_tx, + mii_gtx_clk => mii_gtx_clk, + mii_crs => mii_crs, + mii_col => mii_col + + ); + +CLK_GEN: process + begin + wait for CLK_PERIOD/2; + CLK <= not CLK; + end process; + +MII_CLK_GEN: process + begin + wait for MII_CLK_PERIOD/2; + mii_rx_clk <= not mii_rx_clk; + mii_tx_clk <= not mii_tx_clk; + end process; + + mii_rx_clk_board <= mii_rx_clk; + mii_tx_clk_board <= mii_tx_clk; + +DAT_O_register: + process(CLK) + begin + if rising_edge(CLK) then + if ACK_O = '1' then + DAT_O_reg <= DAT_O; + end if; + end if; + end process; + + + mii_rx <= mii_tx when loop_back_en = '1' else (others => 'Z'); + mii_rx_dv <= mii_tx_en when loop_back_en = '1' else '0'; + mii_rx_er <= mii_tx_er when loop_back_en = '1' else '0'; + mii_crs <= '0'; + mii_col <= '0'; + +-- Master +STIMULUS: process + + procedure emac_alloc (size : natural) is + begin + + -- TX-ALLOCATION + -- set TX-size + emac_action <= emac_alloc; + CYC_I <= '1'; + wait until rising_edge(CLK) and SRDY_O = '1'; + DAT_I <= to_unsigned(size, 16) & X"55AA"; + STB_I <= '1'; + WE_I <= '1'; + ADDR_I <= X"0000_0004"; + wait until rising_edge(CLK) and SRDY_O = '1'; + STB_I <= '0'; + + -- set alloc request + CYC_I <= '1'; + wait until rising_edge(CLK) and SRDY_O = '1'; + DAT_I <= X"0001_0000"; + STB_I <= '1'; + WE_I <= '1'; + ADDR_I <= X"0000_0000"; + wait until rising_edge(CLK) and SRDY_O = '1'; + STB_I <= '0'; + + WE_I <= '0'; + + -- check alloc busy + check_bsy: + while (true) loop + CYC_I <= '1'; + wait until rising_edge(CLK) and SRDY_O = '1'; + STB_I <= '1'; + ADDR_I <= X"0000_0000"; + wait until rising_edge(CLK) and SRDY_O = '1'; + STB_I <= '0'; + wait until rising_edge(CLK) and ACK_O = '1'; + wait until rising_edge(CLK); + if DAT_O_reg(16) = '0' then + exit check_bsy; + end if; + end loop; + emac_action <= emac_idle; + + end procedure emac_alloc; + + procedure emac_write (size : natural) is + variable data : unsigned (7 downto 0); + variable num_words : natural; + begin + + -- FILL TX data + emac_action <= emac_write; + if (size mod 4) = 0 then + num_words := size/4; + else + num_words := size/4 + 1; + end if; + + CYC_I <= '1'; + wait until rising_edge(CLK) and SRDY_O = '1'; + data := X"00"; + ADDR_I <= X"0000_0008"; + WE_I <= '1'; + for i in 1 to num_words loop + STB_I <= '1'; + DAT_I(7 downto 0) <= data; + data := data + 1; + DAT_I(15 downto 8) <= data; + data := data + 1; + DAT_I(23 downto 16) <= data; + data := data + 1; + DAT_I(31 downto 24) <= data; + data := data + 1; + wait until rising_edge(CLK) and SRDY_O = '1'; + end loop; + + STB_I <= '0'; + wait until rising_edge(CLK) and SRDY_O = '1'; + emac_action <= emac_idle; + + end procedure emac_write; + + begin + + wait for 6*MII_CLK_PERIOD; + RST <= '0'; + wait for 60*CLK_PERIOD; + + loop_back_en <= '1'; + + emac_alloc (1536); + emac_write (1536); + + emac_alloc (1536); + emac_write (1536); + + emac_alloc (1536); + emac_write (1536); + + emac_alloc (1536); + emac_write (1536); + + emac_alloc (1536); + emac_write (1536); + + emac_alloc (1536); + emac_write (1536); + + emac_alloc (1536); + emac_write (1536); + + emac_alloc (64); + emac_write (64); + + emac_alloc (512); + emac_write (512); + + emac_alloc (1536); + emac_write (1536); + + emac_alloc (65); + emac_write (65); + + emac_alloc (66); + emac_write (66); + + emac_alloc (67); + emac_write (67); + + emac_alloc (512); + emac_write (64); + + emac_alloc (128); + emac_write (128); + + wait; + + end process; + +END;