// SPDX-License-Identifier: AGPL-3.0-Only /* * Copyright (C) 2023 Sean Anderson */ `include "common.vh" module hub ( input clk_125, input clk_250, input [PORT_COUNT - 1:0] indicate_data, input [PORT_COUNT - 1:0] signal_detect, output [PORT_COUNT - 1:0] request_data, /* Wishbone management */ output wb_ack, wb_err, input wb_cyc, wb_stb, wb_we, input [PORT_COUNT + 5 - 1:0] wb_addr, input [15:0] wb_data_write, output [15:0] wb_data_read, /* Other status (for LEDs) */ output reg collision, transmitting, output [PORT_COUNT - 1:0] link_status, output [PORT_COUNT - 1:0] receiving ); parameter WISHBONE = 1; parameter PORT_COUNT = 4; parameter ELASTIC_BUF_SIZE = 3; parameter ENABLE_COUNTERS = 1; parameter [23:0] OUI = 0; parameter [5:0] MODEL = 0; parameter [3:0] REVISION = 0; localparam MII_100_RATIO = 5; reg buf_ce, tx_ce; wire collision_next, transmitting_next; wire [PORT_COUNT - 1:0] rx_ce, rx_dv, rx_er, tx_en, tx_er, buf_dv, buf_er, col, crs; wire [PORT_COUNT * 4 - 1:0] rxd, txd, buf_data; hub_core #( .PORT_COUNT(PORT_COUNT) ) hub ( .clk(clk_125), .rx_dv(buf_dv), .rx_er(buf_er), .rxd(buf_data), .tx_en(tx_en), .tx_er(tx_er), .txd(txd), .jam(collision_next), .activity(transmitting_next) ); wire [PORT_COUNT - 1:0] bus_ack, bus_err, bus_cyc, bus_stb, bus_we; wire [5 * PORT_COUNT - 1:0] bus_addr; wire [16 * PORT_COUNT - 1:0] bus_data_write, bus_data_read; generate if (WISHBONE) begin wb_mux #( .ADDR_WIDTH(5), .DATA_WIDTH(16), .SLAVES(PORT_COUNT) ) mux ( .m_ack(wb_ack), .m_err(wb_err), .m_cyc(wb_cyc), .m_stb(wb_stb), .m_we(wb_we), .m_addr(wb_addr), .m_data_write(wb_data_write), .m_data_read(wb_data_read), .s_ack(bus_ack), .s_err(bus_err), .s_cyc(bus_cyc), .s_stb(bus_stb), .s_we(bus_we), .s_addr(bus_addr), .s_data_write(bus_data_write), .s_data_read(bus_data_read) ); end else begin assign wb_ack = 0; assign wb_err = wb_cyc && wb_stb; assign bus_cyc = {PORT_COUNT{1'b0}}; assign bus_stb = {PORT_COUNT{1'b0}}; end endgenerate genvar i; generate for (i = 0; i < PORT_COUNT; i = i + 1) begin : port phy_internal #( .WISHBONE(WISHBONE), .ENABLE_COUNTERS(ENABLE_COUNTERS), .OUI(OUI), .MODEL(MODEL), .REVISION(REVISION) ) phy ( .clk_125(clk_125), .clk_250(clk_250), .indicate_data(indicate_data[i]), .signal_detect(signal_detect[i]), .request_data(request_data[i]), .mii_tx_ce(tx_ce), .mii_tx_en(tx_en[i]), .mii_tx_er(tx_er[i]), .mii_txd(txd[i * 4 +: 4]), .mii_rx_ce(rx_ce[i]), .mii_rx_dv(rx_dv[i]), .mii_rx_er(rx_er[i]), .mii_rxd(rxd[i * 4 +: 4]), .wb_ack(bus_ack[i]), .wb_err(bus_err[i]), .wb_cyc(bus_cyc[i]), .wb_stb(bus_stb[i]), .wb_we(bus_we[i]), .wb_addr(bus_addr[i * 5 +: 5]), .wb_data_write(bus_data_write[i * 16 +: 16]), .wb_data_read(bus_data_read[i * 16 +: 16]), .link_status(link_status[i]), .receiving(receiving[i]) ); mii_elastic_buffer #( .BUF_SIZE(ELASTIC_BUF_SIZE) ) buffer ( .clk(clk_125), .tx_ce(rx_ce[i]), .tx_en(rx_dv[i]), .tx_er(rx_er[i]), .txd(rxd[i * 4 +: 4]), .rx_ce(buf_ce), .rx_dv(buf_dv[i]), .rx_er(buf_er[i]), .rxd(buf_data[i * 4 +: 4]) ); end endgenerate reg buf_ce_next; reg [3:0] tx_counter, tx_counter_next; initial begin buf_ce = 0; tx_ce = 0; tx_counter = MII_100_RATIO - 1; end always @(*) begin buf_ce_next = 0; tx_counter_next = tx_counter - 1; if (!tx_counter) begin tx_counter_next = MII_100_RATIO - 1; buf_ce_next = 1; end end always @(posedge clk_125) begin buf_ce <= buf_ce_next; tx_ce <= buf_ce; tx_counter <= tx_counter_next; if (tx_ce) begin collision <= collision_next; transmitting <= transmitting_next; end end endmodule