From 213e17c6571497e60da4b42f9f71c51afb7bca5b Mon Sep 17 00:00:00 2001 From: Konstantin Pavlov Date: Tue, 23 May 2023 13:07:08 +0300 Subject: [PATCH] Add simple UDP Ethernet packet generator --- udp_packet.sv | 180 +++++++++++++++++++++++++++++++++++++++++ udp_packet_tb.sv | 205 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 385 insertions(+) create mode 100644 udp_packet.sv create mode 100644 udp_packet_tb.sv diff --git a/udp_packet.sv b/udp_packet.sv new file mode 100644 index 0000000..6e4aac1 --- /dev/null +++ b/udp_packet.sv @@ -0,0 +1,180 @@ +//------------------------------------------------------------------------------ +// udp_packet.sv +// published as part of https://github.com/pConst/basic_verilog +// Konstantin Pavlov, pavlovconst@gmail.com +//------------------------------------------------------------------------------ + +// INFO ------------------------------------------------------------------------ +// A simple hardcoded UDP packet generator +// + + +/* --- INSTANTIATION TEMPLATE BEGIN --- + +udp_packet #( + .MODE( "BYTES" ) // "BYTES" or "NIBBLES" +) UP1 ( + .clk( clk ), + .nrst( nrst ), + + .tx_en( ), + .od( ) +); + +--- INSTANTIATION TEMPLATE END ---*/ + + +module udp_packet #( parameter + MODE = "BYTES" // "BYTES" or "NIBBLES" +)( + input clk, + input nrst, + + output logic tx_en = 1'b0, + output logic [7:0] od = '0 +); + + + logic [15:0] seq_cntr = '0; + logic nibble_high = 1'b0; + + logic [7:0] data_byte; + + // tx_en and od signals are late one cycle after their respective seq_cntr[] + always_ff @(posedge clk) begin + if( ~nrst ) begin + tx_en = 1'b0; + od[7:0] = '0; + + seq_cntr[15:0] <= '0; + nibble_high <= 1'b0; + end else begin + if ( MODE == "BYTES" ) begin + tx_en <= ( (seq_cntr[15:0] >=0) && + (seq_cntr[15:0] <72) ); + + od[7:0] <= data_byte[7:0]; + + if (seq_cntr[15:0] == 255 ) begin + // sequence reset + seq_cntr[15:0] <= '0; + end else begin + seq_cntr[15:0] <= seq_cntr[15:0] + 1'b1; + end + end else begin + tx_en <= ( (seq_cntr[15:0] >=0) && + (seq_cntr[15:0] <72) ); + + if( nibble_high ) begin + od[7:0] <= {4'h0,data_byte[3:0]}; + + if (seq_cntr[15:0] == 127 ) begin + // sequence reset + seq_cntr[15:0] <= '0; + end else begin + seq_cntr[15:0] <= seq_cntr[15:0] + 1'b1; + end + end else begin + od[7:0] <= {4'h0,data_byte[7:4]}; + end // if + + nibble_high <= !nibble_high; + end // if + end + end + + + always_comb begin + case ( seq_cntr[15:0] ) + // Sending the preambule and asserting tx_en + 0 : data_byte[7:0] <= 8'h55; + 1 : data_byte[7:0] <= 8'h55; + 2 : data_byte[7:0] <= 8'h55; + 3 : data_byte[7:0] <= 8'h55; + 4 : data_byte[7:0] <= 8'h55; + 5 : data_byte[7:0] <= 8'h55; + 6 : data_byte[7:0] <= 8'h55; + 7 : data_byte[7:0] <= 8'hd5; + + // Sending the UDP/IP-packet itself + 8 : data_byte[7:0] <= 8'hd8; + 9 : data_byte[7:0] <= 8'hd3; + 10: data_byte[7:0] <= 8'h85; + 11: data_byte[7:0] <= 8'h26; + 12: data_byte[7:0] <= 8'hc5; + 13: data_byte[7:0] <= 8'h78; + 14: data_byte[7:0] <= 8'h00; + 15: data_byte[7:0] <= 8'h23; + + 16: data_byte[7:0] <= 8'h54; + 17: data_byte[7:0] <= 8'h3c; + 18: data_byte[7:0] <= 8'h47; + 19: data_byte[7:0] <= 8'h1b; + 20: data_byte[7:0] <= 8'h08; + 21: data_byte[7:0] <= 8'h00; + 22: data_byte[7:0] <= 8'h45; + 23: data_byte[7:0] <= 8'h00; + + 24: data_byte[7:0] <= 8'h00; + 25: data_byte[7:0] <= 8'h2e; + 26: data_byte[7:0] <= 8'h00; + 27: data_byte[7:0] <= 8'h00; + 28: data_byte[7:0] <= 8'h00; + 29: data_byte[7:0] <= 8'h00; + 30: data_byte[7:0] <= 8'hc8; + 31: data_byte[7:0] <= 8'h11; + + 32: data_byte[7:0] <= 8'hd6; + 33: data_byte[7:0] <= 8'h73; + 34: data_byte[7:0] <= 8'hc0; + 35: data_byte[7:0] <= 8'ha8; + 36: data_byte[7:0] <= 8'h4d; + 37: data_byte[7:0] <= 8'h21; + 38: data_byte[7:0] <= 8'hc0; + 39: data_byte[7:0] <= 8'ha8; + + 40: data_byte[7:0] <= 8'h4d; + 41: data_byte[7:0] <= 8'hd9; + 42: data_byte[7:0] <= 8'hc3; + 43: data_byte[7:0] <= 8'h50; + 44: data_byte[7:0] <= 8'hc3; + 45: data_byte[7:0] <= 8'h60; + 46: data_byte[7:0] <= 8'h00; + 47: data_byte[7:0] <= 8'h1a; + + 48: data_byte[7:0] <= 8'h00; + 49: data_byte[7:0] <= 8'h00; + 50: data_byte[7:0] <= 8'h01; + 51: data_byte[7:0] <= 8'h02; + 52: data_byte[7:0] <= 8'h03; + 53: data_byte[7:0] <= 8'h04; + 54: data_byte[7:0] <= 8'h01; + 55: data_byte[7:0] <= 8'h01; + + 56: data_byte[7:0] <= 8'h01; + 57: data_byte[7:0] <= 8'h01; + 58: data_byte[7:0] <= 8'h01; + 59: data_byte[7:0] <= 8'h01; + 60: data_byte[7:0] <= 8'h01; + 61: data_byte[7:0] <= 8'h01; + 62: data_byte[7:0] <= 8'h01; + 63: data_byte[7:0] <= 8'h01; + + 64: data_byte[7:0] <= 8'h01; + 65: data_byte[7:0] <= 8'h01; + 66: data_byte[7:0] <= 8'h01; + 67: data_byte[7:0] <= 8'h01; + + // CRC32 checksum + 68: data_byte[7:0] <= 8'he3; + 69: data_byte[7:0] <= 8'h8e; + 70: data_byte[7:0] <= 8'hdf; + 71: data_byte[7:0] <= 8'h1f; + + default: data_byte[7:0] <= '0; // pause + endcase + + end + +endmodule + diff --git a/udp_packet_tb.sv b/udp_packet_tb.sv new file mode 100644 index 0000000..df51cf4 --- /dev/null +++ b/udp_packet_tb.sv @@ -0,0 +1,205 @@ +//------------------------------------------------------------------------------ +// udp_packet_tb.sv +// published as part of https://github.com/pConst/basic_verilog +// Konstantin Pavlov, pavlovconst@gmail.com +//------------------------------------------------------------------------------ + +// INFO ------------------------------------------------------------------------ +// udp_packet testbench + +// use this define to make some things differently in simulation +`define SIMULATION yes + +`timescale 1ns / 1ps + +module udp_packet_tb(); + +initial begin + // Print out time markers in nanoseconds + // Example: $display("[T=%0t] start=%d", $realtime, start); + $timeformat(-9, 3, " ns"); + + // seed value setting is intentionally manual to achieve repeatability between sim runs + $urandom( 1 ); // SEED value +end + +logic clk200; +sim_clk_gen #( + .FREQ( 200_000_000 ), // in Hz + .PHASE( 0 ), // in degrees + .DUTY( 50 ), // in percentage + .DISTORT( 10 ) // in picoseconds +) clk200_gen ( + .ena( 1'b1 ), + .clk( clk200 ), + .clkd( ) +); + +logic nrst_once; + +logic [31:0] clk200_div; +clk_divider #( + .WIDTH( 32 ) +) cd1 ( + .clk( clk200 ), + .nrst( nrst_once ), + .ena( 1'b1 ), + .out( clk200_div[31:0] ) +); + +logic [31:0] clk200_div_rise; +edge_detect ed1[31:0] ( + .clk( {32{clk200}} ), + .anrst( {32{nrst_once}} ), + .in( clk200_div[31:0] ), + .rising( clk200_div_rise[31:0] ), + .falling( ), + .both( ) +); + +// external device "asynchronous" clock +logic clk33; +logic clk33d; +sim_clk_gen #( + .FREQ( 200_000_000 ), // in Hz + .PHASE( 0 ), // in degrees + .DUTY( 50 ), // in percentage + .DISTORT( 1000 ) // in picoseconds +) clk33_gen ( + .ena( 1'b1 ), + .clk( clk33 ), + .clkd( clk33d ) +); + + +logic rst; +initial begin + rst = 1'b0; // initialization + repeat( 1 ) @(posedge clk200); + + forever begin + repeat( 1 ) @(posedge clk200); // synchronous rise + rst = 1'b1; + //$urandom( 1 ); // uncomment to get the same random pattern EVERY nrst + + repeat( 2 ) @(posedge clk200); // synchronous fall, controls rst pulse width + rst = 1'b0; + + repeat( 100 ) @(posedge clk200); // controls test body width + end +end +logic nrst; +assign nrst = ~rst; + + +logic rst_once; +initial begin + rst_once = 1'b0; // initialization + repeat( 1 ) @(posedge clk200); + + repeat( 1 ) @(posedge clk200); // synchronous rise + rst_once = 1'b1; + + repeat( 2 ) @(posedge clk200); // synchronous fall, controls rst_once pulse width + rst_once = 1'b0; +end +//logic nrst_once; // declared before +assign nrst_once = ~rst_once; + + +// random pattern generation +logic [31:0] rnd_data; +always_ff @(posedge clk200) begin + rnd_data[31:0] <= $urandom; + end + +initial forever begin + @(posedge nrst); + $display("[T=%0t] rnd_data[]=%h", $realtime, rnd_data[31:0]); +end + + +// helper start strobe appears unpredictable up to 20 clocks after nrst +logic start; +initial forever begin + start = 1'b0; // initialization + + @(posedge nrst); // synchronous rise after EVERY nrst + repeat( $urandom_range(0, 20) ) @(posedge clk200); + start = 1'b1; + + @(posedge clk200); // synchronous fall exactly 1 clock after rise + start = 1'b0; +end + + +initial begin +// #10000 $stop; +// #10000 $finish; +end + +// sweeping pulses +logic sp = 1'b1; +logic [4:0] sp_duty_cycle = 8'd0; +initial forever begin + if( sp_duty_cycle[4:0] == 0 ) begin + sp = 1'b1; + repeat( 10 ) @(posedge clk200); + end + sp = 1'b0; + repeat( 1 ) @(posedge clk200); + sp = 1'b1; + repeat( 1 ) @(posedge clk200); + sp = 1'b0; + repeat( sp_duty_cycle ) @(posedge clk200); + sp_duty_cycle[4:0] = sp_duty_cycle[4:0] + 1'b1; // overflow is expected here +end + + +// Module under test =========================================================== + +logic [15:0] seq_cntr = '0; + +logic [31:0] id = '0; +always_ff @(posedge clk200) begin + if( ~nrst_once ) begin + seq_cntr[15:0] <= '0; + id[31:0] <= '0; + end else begin + // incrementing sequence counter + if( seq_cntr[15:0]!= '1 ) begin + seq_cntr[15:0] <= seq_cntr[15:0] + 1'b1; + end + + if( seq_cntr[15:0]<300 ) begin + id[31:0] <= '1; + //id[31:0] <= {4{rnd_data[15:0]}}; + end else begin + id[31:0] <= '0; + end + end +end + + +udp_packet #( + .MODE( "BYTES" ) // "BYTES" or "NIBBLES" +) M1 ( + .clk( clk200 ), + .nrst( nrst_once ), + + .tx_en( ), + .od( ) +); + +udp_packet #( + .MODE( "NIBBLES" ) // "BYTES" or "NIBBLES" +) M2 ( + .clk( clk200 ), + .nrst( nrst_once ), + + .tx_en( ), + .od( ) +); + +endmodule +