HITDAQ/FPGA_firmware/udp_generator.v

397 lines
12 KiB
Coq
Raw Normal View History

2024-10-11 14:42:06 +02:00
// 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