// UDP generator
/*
Takes an input stream and generates a valid UDP/IPv4 packet, then outputs it as a stream which can be directly connected to TSE.

Remarks:
- Both input and output are Avalon ST ports, 8-bit symbol, 4 symbols per beat
- Start/end of packet signals are REQUIRED.
- Empty signals are discarded. An incoming packet should have a length of 4N. If not, it will be padded with what's on the bus.
- Backpressure is supported and actively used during transfer. Data source must be backpressurizable.
- The transfer is performed on the fly, without buffering. Input gets backpressed for the time of sending headers.
- Size of payload must be known in advance and programmed over Avalon MM interface. 
- Longer packets will be cut. Shorter packets will be padded with zeros.
- UDP checksum is not calculated (the price of realtime approach) and filled with zeros. IP header checksum is calculated.
- The Programming interface is an Avalon MM. It's 32-bit wide and allows byte, halfword and word operations. Block transfers are not supported.
- Source MAC is not programmable; it must be filled by TSE. 
- Two leading zero bytes are added on front of the packet for 32-bit alignment. TSE settings must comply wit this.
- If transfer is disabled (see control bits), the input will be backpressed all the time.
- Maximum transfer size is about 64K, but limitations of the Ethernet must be taken into account.
- The IPv4 header has the 'Don't fragment' flag set.

Register map: (byte offsets are given)
UDPGEN_REG_CSR		0	//16 bits 	8 bits control(LSB) + 8 bits status(MSB)
UDPGEN_REG_SIZE		2	//16 bits	payload size in words
UDPGEN_REG_SRCIP	4	//32 bits	source IP, last octet first
UDPGEN_REG_DSTIP	8	//32 bits	destination IP. last octet first
UDPGEN_REG_DSTPORT	12	//16 bits	destination port
UDPGEN_REG_SRCPORT	14	//16 bits	source port
UDPGEN_REG_DSTMAC	16	//2*32 bits	destination MAC, last octet first
UDPGEN_REG_RES1		24	//32 bits	reserved
UDPGEN_REG_RES2		28 	//32 bits	reserved

Control bits:
0: transfer enable
1-7: unused

Status bits:
0-3: state of the machine
7-4: unused

*/


`timescale 100 ps / 100 ps
module udp_generator (
		input  wire        clk_clk,                //      clk.clk
		input  wire        rst_reset,              //      rst.reset
		input  wire [2:0]  csr_address,            //      csr.address
		input  wire        csr_write,              //         .write
		input  wire [31:0] csr_writedata,          //         .writedata
		input  wire [3:0]  csr_byteenable,         //         .byteenable
		input  wire        csr_read,               //         .read
		output wire [31:0] csr_readdata,           //         .readdata
		input  wire [31:0] data_in_data,           //  data_in.data
		output wire        data_in_ready,          //         .ready
		input  wire        data_in_valid,          //         .valid
		input  wire [1:0]  data_in_empty,          //         .empty
		input  wire        data_in_endofpacket,    //         .endofpacket
		input  wire        data_in_startofpacket,  //         .startofpacket
		output wire [31:0] data_out_data,          // data_out.data
		output wire [1:0]  data_out_empty,         //         .empty
		output wire        data_out_endofpacket,   //         .endofpacket
		output wire        data_out_startofpacket, //         .startofpacket
		input  wire        data_out_ready,         //         .ready
		output wire        data_out_valid          //         .valid
	);


	// *********************** CSR stuff ************************
	
	reg 	[31:0] 	registers[7:0];		//main register space
	wire 	[31:0]	reg0_read;		//reg[0] is read indirectly to include status byt(bits 15-8)

		//read logic
	assign reg0_read[7:0] 	= registers[0][7:0];		// Readback of control bits
	assign reg0_read[31:16] = registers[0][31:16];		// Readback of payload size
	assign reg0_read[15:12] = 0;				// Unused bits of the status byte
	assign csr_readdata 	= (csr_address==0) ? reg0_read : registers[csr_address];

		//write logic
	genvar i;
	genvar j;
	generate
	for (i = 0; i < 8; i = i+1)
	begin : write_registers	//Quartus wants labels for 'for' blocks
		for (j = 0; j < 4; j = j+1)
		begin : write_bytes
			always @(posedge clk_clk or posedge rst_reset)
			begin 
				if (rst_reset)
					registers[i][8*j+7:8*j] <= 8'd0;
				else
					if (csr_byteenable[j] && (csr_address == i) && (csr_write))
						registers[i][8*j+7:8*j] <= csr_writedata[8*j+7:8*j];
			end
		end
	end	
	endgenerate
	
	
	// *********************** ST interface ***********************

	reg	[4:0]	state;		//State of the state machine
	localparam STATE_IDLE	= 0;	//waiting for SOP
	localparam STATE_MAC1	= 1;	//sending first word of MAC header: 				00 00 dest_mac[5:4]
	localparam STATE_MAC2	= 2;	//sending second word of MAC header: 				dest_mac[3:0]
	localparam STATE_MAC3	= 3;	//sending zeroes as source MAC msbs:				00 00 00 00
	localparam STATE_MAC4	= 4;	//sending zeroes as source MAC lsbs & packet type:		00 00 08 00
	localparam STATE_IP1	= 5;	//sending IP ver & header size, DSF, total length		45 00 length[1:0]
	localparam STATE_IP2	= 6;	//sending IP id, flags (don't fragment), fragm. offset=0	ip_id[1:0] 40 00
	localparam STATE_IP3	= 7;	//sending TTL, protocol, header checksum			80 11 checksum[1:0]
	localparam STATE_IP4	= 8;	//sending source IP						src_ip[4:0]
	localparam STATE_IP5	= 9;	//sending destination IP					dst_ip[4:0]
	localparam STATE_UDP1	= 10;	//sending src & dest port					src_port[1:0] dst_port[1:0]
	localparam STATE_UDP2	= 11;	//sending UDP frame length and (unused) checksum		udp_length[1:0] 00 00
	localparam STATE_DATA	= 12;	//sending packet data but the last word
	localparam STATE_PAD	= 13;	//padding packet with zeros if incoming packet was too short
	localparam STATE_DUMP	= 14;	//dumping tail of incoming packet if it was too long
	localparam STATE_END	= 15;	//transfer finished, just wait one cycle

	reg		reg_ready;	//registers to drive output pins of ST interfaces
	reg		reg_valid;
	reg	[31:0]	reg_data;
	reg		reg_startofpacket;
	reg		reg_endofpacket;
	reg		reg_empty;

		//Helper stuff
	wire	[15:0]	udp_length;	
	wire	[15:0]	total_length;
	wire	[19:0]  int_checksum;
	wire 	[16:0]	int_checksum2;	
	wire	[15:0]	header_checksum;	
	reg	[15:0]	ip_id;	//unique identifier of a packet
	reg	[15:0]	tx_ctr;	//counter of sent data words

	assign reg0_read[11:8] = state;	//state forwarded to status register

	assign udp_length = 4*registers[0][31:16] + 16'd8;	
	assign total_length = 4*registers[0][31:16] + 16'd28;

	assign int_checksum = 20'h10511 + total_length + ip_id + registers[1][31:16] + registers[1][15:0] + registers[2][31:16] + registers[2][15:0];
	assign int_checksum2 = int_checksum[15:0] + int_checksum[19:16];
	assign header_checksum = ~(int_checksum2[15:0] + int_checksum2[16]);

		//The state machine
	always @(posedge clk_clk or posedge rst_reset)
	begin 
		if (rst_reset)
		begin
			state <= STATE_IDLE;
			ip_id<= 0;
		end
		else
			case(state)
				STATE_IDLE:
				begin
					if (data_in_startofpacket && registers[0][0])
					begin
						state <= STATE_MAC1;
						ip_id <= ip_id+1;
						tx_ctr <= 1;
					end
				end
				STATE_MAC1, STATE_MAC2, STATE_MAC3, STATE_MAC4, STATE_IP1, STATE_IP2, STATE_IP3, STATE_IP4, STATE_IP5, STATE_UDP1, STATE_UDP2:
				begin
					if (data_out_ready)
						state <= state + 1;
				end
				STATE_DATA:
				begin
					if (data_out_ready && data_in_valid)
					begin
						tx_ctr <= tx_ctr+1;
						if ((tx_ctr == registers[0][31:16]) && data_in_endofpacket)	//last word, size matches
							state <= STATE_END;
						else if (data_in_endofpacket)					//packet too short
							state <= STATE_PAD;
						else if (tx_ctr == registers[0][31:16])				//packet too long
							state <= STATE_DUMP;
					end
				end
				STATE_PAD:
				begin
					if (data_out_ready)
					begin
						tx_ctr <= tx_ctr+1;
						if (tx_ctr == registers[0][31:16])				
							state <= STATE_END;
					end	
				end
				STATE_DUMP:
				begin
					if (data_out_ready && data_in_valid)
					begin
						if (data_in_endofpacket)
							state <= STATE_END;
					end
				end

				default:
				begin
					state <= STATE_IDLE;
				end
			endcase
	end

		//Driving bus signals
	always @( * )
	begin
		if (rst_reset)
		begin
			reg_ready = 0;
			reg_valid = 0;
			reg_data = 0;
			reg_startofpacket = 0;
			reg_endofpacket = 0;
			reg_empty = 0;
		end
		else
			case(state)
				STATE_IDLE:
				begin
					reg_ready 		= 0;
					reg_valid 		= 0;
					reg_data 		= 0;
					reg_startofpacket 	= 0;
					reg_endofpacket 	= 0;
					reg_empty 		= 0;
				end
				STATE_MAC1:
				begin
					reg_ready 		= 0;
					reg_valid 		= 1;
					reg_data[31:16] 	= 0;
					reg_data[15:0] 		= registers[5][15:0];
					reg_startofpacket 	= 1;
					reg_endofpacket 	= 0;
					reg_empty 		= 0;
				end
				STATE_MAC2:
				begin
					reg_ready 		= 0;
					reg_valid 		= 1;
					reg_data 		= registers[4];
					reg_startofpacket 	= 0;
					reg_endofpacket 	= 0;
					reg_empty 		= 0;
				end
				STATE_MAC3:
				begin
					reg_ready 		= 0;
					reg_valid 		= 1;
					reg_data 		= 32'h00000000;
					reg_startofpacket 	= 0;
					reg_endofpacket 	= 0;
					reg_empty 		= 0;
				end
				STATE_MAC4:
				begin
					reg_ready 		= 0;
					reg_valid 		= 1;
					reg_data 		= 32'h00000800;
					reg_startofpacket 	= 0;
					reg_endofpacket 	= 0;
					reg_empty 		= 0;
				end
				STATE_IP1:
				begin
					reg_ready 		= 0;
					reg_valid 		= 1;
					reg_data[31:16]		= 16'h4500;
					reg_data[15:0]		= total_length;
					reg_startofpacket 	= 0;
					reg_endofpacket 	= 0;
					reg_empty 		= 0;
				end
				STATE_IP2:
				begin
					reg_ready 		= 0;
					reg_valid 		= 1;
					reg_data[31:16]		= ip_id;
					reg_data[15:0]		= 16'h4000;
					reg_startofpacket 	= 0;
					reg_endofpacket 	= 0;
					reg_empty 		= 0;
				end
				STATE_IP3:
				begin
					reg_ready 		= 0;
					reg_valid 		= 1;
					reg_data[31:16]		= 16'h8011;
					reg_data[15:0]		= header_checksum;
					reg_startofpacket 	= 0;
					reg_endofpacket 	= 0;
					reg_empty 		= 0;
				end
				STATE_IP4:
				begin
					reg_ready 		= 0;
					reg_valid 		= 1;
					reg_data		= registers[1];
					reg_startofpacket 	= 0;
					reg_endofpacket 	= 0;
					reg_empty 		= 0;
				end
				STATE_IP5:
				begin
					reg_ready 		= 0;
					reg_valid 		= 1;
					reg_data		= registers[2];
					reg_startofpacket 	= 0;
					reg_endofpacket 	= 0;
					reg_empty 		= 0;
				end
				STATE_UDP1:
				begin
					reg_ready 		= 0;
					reg_valid 		= 1;
					reg_data		= registers[3];
					reg_startofpacket 	= 0;
					reg_endofpacket 	= 0;
					reg_empty 		= 0;
				end
				STATE_UDP2:
				begin
					reg_ready 		= 0;
					reg_valid 		= 1;
					reg_data[31:16]		= udp_length;
					reg_data[15:0]		= 16'h0000;
					reg_startofpacket 	= 0;
					reg_endofpacket 	= 0;
					reg_empty 		= 0;
				end
				STATE_DATA:
				begin
					reg_ready 		= data_out_ready;
					reg_valid 		= data_in_valid;
					reg_data		= data_in_data;
					reg_startofpacket 	= 0;
					if (tx_ctr == registers[0][31:16])
						reg_endofpacket 	= 1;
					else
						reg_endofpacket 	= 0;
					reg_empty 		= 0;
				end
				STATE_PAD:
				begin
					reg_ready 		= 0;
					reg_valid 		= 1;
					reg_data		= 32'h0;
					reg_startofpacket 	= 0;
					if (tx_ctr == registers[0][31:16])
						reg_endofpacket 	= 1;
					else
						reg_endofpacket 	= 0;
					reg_empty 		= 0;
				end
				STATE_DUMP:
				begin
					reg_ready 		= 1;
					reg_valid 		= 0;
					reg_data		= 0;
					reg_startofpacket = 0;
					reg_endofpacket = 0;
					reg_empty 		= 0;
				end
				STATE_END:
				begin
					reg_ready 		= 0;
					reg_valid 		= 0;
					reg_data 		= 0;
					reg_startofpacket 	= 0;
					reg_endofpacket 	= 0;
					reg_empty 		= 0;
				end
				default:
				begin
					reg_ready = 0;
					reg_valid = 0;
					reg_data = 0;
					reg_startofpacket = 0;
					reg_endofpacket = 0;
					reg_empty = 0;
				end
			endcase
	end

	
	assign data_in_ready = reg_ready;
	assign data_out_valid = reg_valid;
	assign data_out_data = reg_data;
	assign data_out_startofpacket = reg_startofpacket;
	assign data_out_endofpacket = reg_endofpacket;
	assign data_out_empty = reg_empty;

endmodule
