`timescale 1ns / 1ps

`include "src/fifo.v"
`include "src/rmii/rmii_serializer.v"

module rmii_transmit_controller #(
	parameter FIFO_DEPTH = 4,
	parameter STORAGE_WIDTH = 8,
	
	// AUTOCALCULATED
	parameter STORAGE_MSB = STORAGE_WIDTH - 1,
	parameter SHIFT_COUNT = FIFO_DEPTH
)(
	input clk, rst, clear, // general signals
	
	/* FIFO signals */
	input [STORAGE_MSB:0] data_in, // input data
	input wrt, // store the data
	output not_full, // the internal buffer is not full
	
	/* RMII signals */
	output reg TXEN, // transmit enable
	output [1:0] TXD, // transmit data
	
	output reg busy // there's an ongoing transmission
);

wire rstclr = rst | clear;

/* TX FIFO and its signals */
wire tx_fifo_empty;
wire tx_fifo_out_valid;
wire tx_fifo_full;
wire [STORAGE_WIDTH-1:0]tx_fifo_out;
//wire start = wrt && !busy;
wire tx_fifo_pop = (busy && oser_empty) || oser_single;

fifo #(
	.DEPTH(FIFO_DEPTH),
	.WIDTH(STORAGE_WIDTH)
) tx_fifo (
	.clk(clk),
	.rst(rst),
	.clear(clear),
	
	.empty(tx_fifo_empty),
	.out_valid(tx_fifo_out_valid),
	.full(tx_fifo_full),
	
	.in(data_in),
	.push(wrt),
	
	.out(tx_fifo_out),
	.pop(tx_fifo_pop)
);

assign not_full = !tx_fifo_full;

/* output serializer */

wire oser_load = tx_fifo_out_valid;
wire oser_empty;
//wire oser_exhausted;
wire oser_single;
wire [1:0] oser_out;

rmii_serializer #(
	.STORE_SIZE(STORAGE_WIDTH),
	.DIR(1) // RIGHT
) out_serializer (
	.clk(clk),
	.rst(rst),
	.clear(clear),
	
	.par_in(tx_fifo_out),
	.ser_out(oser_out),
	.load(oser_load),
	.shift(1'b1),
	.single(oser_single),
	.empty(oser_empty),
	.exhausted(/*oser_exhausted*/)
);

/* TXEN control */
always @(posedge clk)
begin
	if (rstclr)
	begin
		TXEN <= 0;
	end
	else
	begin
		if (oser_load) // assert the same time the shift register loading occurs
		begin
			TXEN <= 1;
		end
		else if (oser_empty && tx_fifo_empty) // deassert only when all buffers has depleted
		begin
			TXEN <= 0;
		end
	end
end

/* busy control */
always @(posedge clk)
begin
	if (rstclr)
	begin
		busy <= 0;
	end
	else
	begin
		if (!busy && wrt)
		begin
			busy <= 1;
		end
		
		if (busy && !wrt && !TXEN)
		begin
			busy <= 0;
		end
	end
end

// combinatorial output
assign TXD = TXEN ? oser_out : 2'b0;

endmodule
