// SPDX-License-Identifier: AGPL-3.0-Only
/*
 * Copyright (C) 2023 Sean Anderson <seanga2@gmail.com>
 */

`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