`timescale 1ns / 1ps
module rmii_serializer #(
	parameter SHIFT_SIZE = 2,
	parameter STORE_SIZE = 8,
	parameter DIR = 0, // 0 -> left; 1 -> right
	
	// AUTOCALCULATED
	parameter SHIFT_COUNT = STORE_SIZE / SHIFT_SIZE,
	parameter SHIFT_COUNT_MSB = $clog2(SHIFT_COUNT) - 1
)(
	input clk, rst, clear,	// main clock, reset, clear
	
	input [STORE_SIZE-1:0] par_in, // parallel input
	input load, // load parallel data
	output [SHIFT_SIZE-1:0] ser_out, // serialized output
	input shift, // enable shifting
	
	output single, // one shift left
	output empty,	// the inner buffer has depleted
	output exhausted // a strobe version of empty
);

// a combine reset or clear signal
wire rstclr = rst | clear;

// shift work register
reg [STORE_SIZE-1:0] shift_work; 	

// shift count register
reg [SHIFT_COUNT_MSB:0] shift_count;

/* main state machine */
always @(posedge clk)
begin
	if (rstclr)
	begin
		shift_work <= 0;
		shift_count <= 0;
	end
	else
	begin
		if (load) // load the parallel data
		begin
			shift_work <= par_in;
			shift_count <= SHIFT_COUNT - 1'b1; // data must be shifted SHIFT_COUNT - 1 times
		end
		else if (shift)
		begin
			if (shift_count > 0)
			begin
				shift_count <= shift_count - 1'b1; // decrease shift count
				shift_work <= DIR ? (shift_work >> SHIFT_SIZE) : (shift_work << SHIFT_SIZE); // shift the contents of the work register
			end
		end
	end
end

reg empty_prev;
always @(posedge clk)
begin
	if (rstclr)
	begin
		empty_prev <= 0;
	end
	else
	begin
		empty_prev <= empty;
	end
end

// output logic
assign empty = (shift_count == 0) && !load; // empty signal
assign single = (shift_count == 1);
assign exhausted = empty && !empty_prev; // strobe for depletion
assign ser_out = DIR ? shift_work[SHIFT_SIZE-1:0] : shift_work[STORE_SIZE-1:STORE_SIZE-SHIFT_SIZE]; // serial output

endmodule
