`timescale 1ns / 1ps

module mac_memory_access_controller #(
	parameter DATA_WIDTH = 8,
	parameter ADDR_WIDTH = 12,
	
	// AUTOCALCULATED
	parameter DATA_MSB = DATA_WIDTH - 1,
	parameter ADDR_MSB = ADDR_WIDTH - 1,
	parameter LEN_MSB = 10
)(
	input clk, rst, clear, // general signals
	
	input [DATA_MSB:0] mem_data, // data fetched from the memory
	output reg [ADDR_MSB:0] mem_addr, // memory address for data fetching
		
	input transmitter_busy, // indicates that the transmitter is busy with previous transmission
	input start, // start a new transmission
	input [ADDR_MSB:0] frame_addr, // starting address of the Ethernet frame
	input [LEN_MSB:0] frame_len, // length of the Ethernet frame
	
	output busy, // a frame is currently being processed and transmitted
	
	output [DATA_MSB:0] tx_data_in, // data input for the transmitter block
	input tx_not_full, // transmitter block supports storing new blocks of data
	output tx_wrt // issue a write onto the transmitter block
);

wire rstclr = rst | clear;

// state machine states
localparam IDLE = 0;
localparam FETCH_FRAME = 1;
localparam WAIT_TX_COMPL = 2;

// main state machine
reg [1:0] state;
wire frame_params_valid = frame_len != 0;

// frame setup and transmission
reg [LEN_MSB:0] len_left;

always @(posedge clk)
begin
	if (rstclr)
	begin
		len_left <= 0;
		//tx_wrt <= 0;
		mem_addr <= 0;
		len_left <= 0;
	end	
	else
	begin
		case (state)
			IDLE:
			begin
				mem_addr <= frame_addr;
				len_left <= frame_len - 1;
			end
			
			FETCH_FRAME:
			begin
				if (len_left != 0)
				begin
					//tx_wrt <= 1;
					
					if (tx_not_full)
					begin
						mem_addr <= mem_addr + 1;
						len_left <= len_left - 1;
					end
				end
				else
				begin
					//tx_wrt <= 0;
				end
			end
			
			default: ;
		endcase
	end
	
	// next state logic
	case (state)
		IDLE: if (start && frame_params_valid) state <= FETCH_FRAME;
		FETCH_FRAME: if ((len_left == 0) && tx_not_full) state <= WAIT_TX_COMPL;
		WAIT_TX_COMPL: if (!transmitter_busy) state <= IDLE;
		default: state <= IDLE;
	endcase
end


// combinatorial outputs
assign busy = (state != IDLE);
assign tx_data_in = mem_data;
assign tx_wrt = (state == FETCH_FRAME);

endmodule
