mirror of
https://github.com/pConst/basic_verilog.git
synced 2025-01-14 06:42:54 +08:00
Add simple UDP Ethernet packet generator
This commit is contained in:
parent
bb0897a409
commit
213e17c657
180
udp_packet.sv
Normal file
180
udp_packet.sv
Normal file
@ -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
|
||||
|
205
udp_packet_tb.sv
Normal file
205
udp_packet_tb.sv
Normal file
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user