`timescale 1ns / 1ps

module packetizer #(
	parameter MEM_WRITE_WIDTH = 16, 	// write size of the memory
	//parameter MEM_READ_WIDTH  = 8,
	parameter MEM_DEPTH_BYTE  = 1024, // depth of the memory in bytes
	parameter MAX_PACKET_SIZE = 1500,
	
	// AUTOCALCULATED
	parameter MEM_WRITE_MSB	= MEM_WRITE_WIDTH - 1,
	//parameter MEM_READ_MSB  = MEM_READ_WIDTH - 1,
	parameter MEM_DEPTH_MSB = $clog2(MEM_DEPTH_BYTE) - 1
)( 
	input clk, rst, clear, // general signals
	
	output [31:0] mac_ctrl_word, // MAC control word
	
	input [MEM_WRITE_MSB:0] data_in, // data input
	input wrt, // write signal
	
	output mem_wea, // memory write enable signal
	output [MEM_WRITE_MSB:0] mem_data, // data going into the memory
	output [MEM_DEPTH_MSB:0] mem_addr, // write address
	
	input immtx,  	// trigger an immediate transmission of memory contents
	
	output empty, 	// memory is empty
	output full		// memory is fully occupied
);

wire rstclr = rst || clear; // combined reset and clear

reg [MEM_DEPTH_MSB:0] wr_ptr; // write pointer
reg [MEM_DEPTH_MSB:0] rd_ptr; // read pointer

reg [MEM_DEPTH_MSB+1:0] level; // fill level
always @(*)
begin
	if (wr_ptr < rd_ptr)
		level <= {1'b0, wr_ptr} + MEM_DEPTH_BYTE - {1'b0, rd_ptr}; // |===w-----r===|
	else
		level <= wr_ptr - rd_ptr; // |---r===w---|
end

// write operations
always @(posedge clk)
begin
	if (rstclr)
	begin
		wr_ptr <= 0;
	end
	else
	begin
		if (wrt && !full) // increase write pointer if possible
		begin
			wr_ptr <= wr_ptr + 1'b1;
		end
	end
end

// read operations
wire level_triggers_transmit = (level > MAX_PACKET_SIZE - 1);
wire transmit = level_triggers_transmit || (immtx && !empty && !level_transmit); // level or demand-based transmission trigger

always @(posedge clk)
begin
	if (rstclr)
	begin
		rd_ptr <= 0;
	end
	else
	begin
		if (transmit)
		begin
			rd_ptr <= wr_ptr;
		end
	end
end

reg level_transmit;

always @(posedge clk)
begin
	if (rstclr)
	begin
		level_transmit <= 0;
	end
	else
	begin
		if (level_triggers_transmit)
		begin
			level_transmit <= 1;
		end
		else if (immtx)
		begin
			level_transmit <= 0;
		end
	end
end

// combinatorial outputs
assign empty = (rd_ptr == wr_ptr);
assign full = (wr_ptr == (rd_ptr - 1'b1));
assign mem_wea = wrt && !full;
assign mem_data = data_in;
assign mem_addr = wr_ptr;
assign mac_ctrl_word = 0 | (transmit << 31) | (level << (MEM_DEPTH_MSB + 1)) | rd_ptr;

endmodule
