blob: 591c728b7383d941508776528d6518a579177e26 [file] [log] [blame]
/******************************************************************************
* (C) Copyright 2014 AMIQ Consulting
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* NAME: amiq_eth_packet_ipv4.sv
* PROJECT: amiq_eth
* Description: This file declare the IPV4 Ethernet packet.
* Implementation is done based on: http://en.wikipedia.org/wiki/Internet_Protocol_version_4
*******************************************************************************/
`ifndef __AMIQ_ETH_PACKET_IPV4
//protection against multiple includes
`define __AMIQ_ETH_PACKET_IPV4
//Ethernet IPV4 packet header
class amiq_eth_packet_ipv4_header extends uvm_object;
`uvm_object_utils(amiq_eth_packet_ipv4_header)
//Version
rand amiq_eth_ipv4_header_version version;
constraint version_c {
version == `AMIQ_ETH_IPV4_HEADER_VERSION_VALUE;
}
//Internet Header Length (IHL)
rand amiq_eth_ipv4_header_ihl ihl;
constraint ihl_c {
ihl >= `AMIQ_ETH_MINIMUM_HEADER_LENGTH_IN_WORDS;
}
//Differentiated Services Code Point (DSCP)
rand amiq_eth_ipv4_header_dscp dscp;
//Explicit Congestion Notification (ECN)
rand amiq_eth_ipv4_header_ecn ecn;
//Total Length
rand amiq_eth_ipv4_header_total_length total_length;
constraint total_length_c {
total_length >= (ihl * 4) & total_length <= `AMIQ_ETH_IPV4_REQUIRED_REASSEMBLE_LENGTH;
}
//Identification
rand amiq_eth_ipv4_header_identification identification;
//Flags
rand amiq_eth_ipv4_header_flags flags;
//Fragment Offset
rand amiq_eth_ipv4_header_fragment_offset fragment_offset;
//Time To Live (TTL)
rand amiq_eth_ipv4_header_ttl ttl;
//Protocol
rand amiq_eth_ipv4_header_protocol protocol;
//Header Checksum
rand amiq_eth_ipv4_header_checksum checksum;
//determine if to use the correct checksum or not
rand bit use_correct_checksum;
constraint use_correct_checksum_c {
use_correct_checksum == 1;
}
//Source address
rand amiq_eth_ipv4_header_source_ip_address source_ip_address;
//Destination address
rand amiq_eth_ipv4_header_destination_ip_address destination_ip_address;
//Options
rand amiq_eth_ipv4_header_option options[];
constraint options_c {
solve ihl before options;
options.size() == (ihl - get_minimum_header_length_in_words());
}
//constructor
//@param name - the name assigned to the instance of this class
function new(string name = "");
super.new(name);
endfunction
//get the minimum length of the header, in bits
//@return the minimum length of the header, in bits
virtual function int unsigned get_minimum_header_length_in_bits();
return (`AMIQ_ETH_MINIMUM_HEADER_LENGTH_IN_BITS);
endfunction
//get the minimum length of the header, in bytes
//@return the minimum length of the header, in bytes
virtual function int unsigned get_minimum_header_length_in_bytes();
return (get_minimum_header_length_in_bits() / 8);
endfunction
//get the minimum length of the header, in words
//@return the minimum length of the header, in words
virtual function int unsigned get_minimum_header_length_in_words();
return (get_minimum_header_length_in_bits() / 32);
endfunction
//get the header length in bytes
//@return the header length in bytes
virtual function int unsigned get_header_length_in_bytes();
return (ihl * 4);
endfunction
//get the options size in words
//@param local_ihl - Internet Header Length (IHL)
//@return the options size in words
virtual function int unsigned get_options_size_in_words(amiq_eth_ipv4_header_ihl local_ihl);
if(local_ihl < get_minimum_header_length_in_words()) begin
`uvm_fatal("AMIQ_ETH", $sformatf("Internet Header Length (%0d) should be bigger or equal then Minimum Header Length in words (%0d) - AMIQ_ETH_MINIMUM_HEADER_LENGTH_IN_WORDS: %0d",
local_ihl, get_minimum_header_length_in_words(), `AMIQ_ETH_MINIMUM_HEADER_LENGTH_IN_WORDS));
return 0;
end
return (local_ihl - get_minimum_header_length_in_words());
endfunction
//get the data length in bytes
//@return the data length in bytes
virtual function int unsigned get_data_length_in_bytes();
if(total_length < get_header_length_in_bytes()) begin
`uvm_fatal("AMIQ_ETH", $sformatf("Total Length (%0d) should be bigger or equal to IHL in bytes (%0d)", total_length, get_header_length_in_bytes()));
end
return (total_length - get_header_length_in_bytes());
endfunction
//pack the entire Ethernet packet
//@param packer - the packer used by this function
virtual function void do_pack(uvm_packer packer);
`uvm_pack_int(version);
`uvm_pack_int(ihl);
`uvm_pack_int(dscp);
`uvm_pack_int(ecn);
`uvm_pack_int(total_length);
`uvm_pack_int(identification);
`uvm_pack_int(flags);
`uvm_pack_int(fragment_offset);
`uvm_pack_int(ttl);
`uvm_pack_int(protocol);
`uvm_pack_int(checksum);
`uvm_pack_int(source_ip_address);
`uvm_pack_int(destination_ip_address);
for (int index = 0; index < options.size(); index++) begin
`uvm_pack_int(options[index]);
end
endfunction
//unpack the entire Ethernet packet
//@param packer - the packer used by this function
virtual function void do_unpack(uvm_packer packer);
`uvm_unpack_int(version);
`uvm_unpack_int(ihl);
`uvm_unpack_int(dscp);
`uvm_unpack_int(ecn);
`uvm_unpack_int(total_length);
`uvm_unpack_int(identification);
`uvm_unpack_int(flags);
`uvm_unpack_int(fragment_offset);
`uvm_unpack_int(ttl);
`uvm_unpack_int(protocol);
`uvm_unpack_int(checksum);
`uvm_unpack_int(source_ip_address);
`uvm_unpack_int(destination_ip_address);
begin
int unsigned minimum_header_length = get_minimum_header_length_in_words();
if(ihl < get_minimum_header_length_in_words()) begin
`uvm_fatal("AMIQ_ETH", $sformatf("Internet Header Length (%0d) should be bigger or equal to minimum length (%0d)", ihl, minimum_header_length))
end
options = new[ihl - minimum_header_length];
`uvm_unpack_array(options);
end
endfunction
//get an easy-to-read string containing the IP value
//@param address - the IP address
//@return easy-to-read string containing the IP value
local function string get_printable_ip(bit[31:0] address);
string result = "";
for(int i = 3; i >= 0; i--) begin
byte unsigned data = (address >> (8 * i)) & 8'hFF;
result = $sformatf("%s%0d%s", result, data, ((i > 0) ? "." : ""));
end
return result;
endfunction
//converts the information containing in the instance of this class to an easy-to-read string
//@return easy-to-read string with the information contained in the instance of this class
virtual function string convert2string();
string source_ip = get_printable_ip(source_ip_address);
string destination_ip = get_printable_ip(destination_ip_address);
return $sformatf("Version: %X%sIHL: %0d%sDSCP: %X%sECN: %X%sTotal Length: %0d%sIdentification: %X%sFlags: %X%sFragment Offset: %X%sTTL: %0d%sProtocol: %0d%sChecksum: %X%sSource IP: %s%sDestination IP: %s%sOptions size: %0d",
version, `AMIQ_ETH_FIELD_SEPARATOR,
ihl, `AMIQ_ETH_FIELD_SEPARATOR,
dscp, `AMIQ_ETH_FIELD_SEPARATOR,
ecn, `AMIQ_ETH_FIELD_SEPARATOR,
total_length, `AMIQ_ETH_FIELD_SEPARATOR,
identification, `AMIQ_ETH_FIELD_SEPARATOR,
flags, `AMIQ_ETH_FIELD_SEPARATOR,
fragment_offset, `AMIQ_ETH_FIELD_SEPARATOR,
ttl, `AMIQ_ETH_FIELD_SEPARATOR,
protocol, `AMIQ_ETH_FIELD_SEPARATOR,
checksum, `AMIQ_ETH_FIELD_SEPARATOR,
source_ip, `AMIQ_ETH_FIELD_SEPARATOR,
destination_ip, `AMIQ_ETH_FIELD_SEPARATOR,
options.size());
endfunction
//compares the current class instance with the one provided as an argument
//@param rhs - Right Hand Side object
//@param comparer - The UVM comparer object used in evaluating this comparison - default is "null"
//@return 1 - objects are the same, 0 - objects are different
virtual function bit compare (uvm_object rhs, uvm_comparer comparer=null);
amiq_eth_packet_ipv4_header casted_rhs;
if($cast(casted_rhs, rhs) == 0) begin
return 0;
end
if(ihl != casted_rhs.ihl) begin
return 0;
end
if(dscp != casted_rhs.dscp) begin
return 0;
end
if(ecn != casted_rhs.ecn) begin
return 0;
end
if(total_length != casted_rhs.total_length) begin
return 0;
end
if(identification != casted_rhs.identification) begin
return 0;
end
if(flags != casted_rhs.flags) begin
return 0;
end
if(fragment_offset != casted_rhs.fragment_offset) begin
return 0;
end
if(ttl != casted_rhs.ttl) begin
return 0;
end
if(protocol != casted_rhs.protocol) begin
return 0;
end
if(checksum != casted_rhs.checksum) begin
return 0;
end
if(source_ip_address != casted_rhs.source_ip_address) begin
return 0;
end
if(destination_ip_address != casted_rhs.destination_ip_address) begin
return 0;
end
if(options.size() != casted_rhs.options.size()) begin
return 0;
end
for(int i = 0; i < options.size(); i++) begin
if(options[i] != casted_rhs.options[i]) begin
return 0;
end
end
return 1;
endfunction
//copy the right hand side class instance to this current instance
//@param rhs - Right Hand Side object
function void copy(uvm_object rhs);
amiq_eth_packet_ipv4_header casted_rhs;
if($cast(casted_rhs, rhs) == 0) begin
`uvm_fatal("AMIQ_ETH", "Could not cast object to a amiq_eth_packet_ipv4_header");
end
version = casted_rhs.version;
ihl = casted_rhs.ihl;
dscp = casted_rhs.dscp;
ecn = casted_rhs.ecn;
total_length = casted_rhs.total_length;
identification = casted_rhs.identification;
flags = casted_rhs.flags;
fragment_offset = casted_rhs.fragment_offset;
ttl = casted_rhs.ttl;
protocol = casted_rhs.protocol;
checksum = casted_rhs.checksum;
source_ip_address = casted_rhs.source_ip_address;
destination_ip_address = casted_rhs.destination_ip_address;
options = new[casted_rhs.options.size()];
for(int i = 0; i < options.size(); i++) begin
options[i] = casted_rhs.options[i];
end
endfunction
//get the correct checksum
//@return returns the value of the correct checksum
virtual function amiq_eth_ipv4_header_checksum get_correct_checksum();
//this logic is based on wiki - http://en.wikipedia.org/wiki/IPv4_header_checksum
amiq_eth_packet_ipv4_header header;
bit unsigned bitstream[];
bit[31:0] halfwords_sum = 0;
bit[15:0] halfwords[];
header = amiq_eth_packet_ipv4_header::type_id::create("header");
header.copy(this);
header.checksum = 0;
void'(header.pack(bitstream));
if(bitstream.size() != ihl * 32) begin
`uvm_fatal("AMIQ_ETH", $sformatf("Bit stream size error detected - expected: %0d, received: %0d",
ihl * 32, bitstream.size()));
end
halfwords = { >> {bitstream}};
if(halfwords.size() != (2 * ihl)) begin
`uvm_fatal("AMIQ_ETH", $sformatf("Halfwords size error detected - expected: %0d, received: %0d",
(2 * ihl), halfwords.size()));
end
for(int i = 0; i < halfwords.size(); i++) begin
halfwords_sum += halfwords[i];
end
get_correct_checksum = halfwords_sum[31:16] + halfwords_sum[15:0];
get_correct_checksum = ~get_correct_checksum;
endfunction
function void post_randomize();
if(use_correct_checksum == 1) begin
checksum = get_correct_checksum();
end
endfunction
endclass
//Ethernet IPV4 packet
class amiq_eth_packet_ipv4 extends amiq_eth_packet_ether_type;
`uvm_object_utils(amiq_eth_packet_ipv4)
//header
rand amiq_eth_packet_ipv4_header header;
//data
rand amiq_eth_data data[];
//Pad field
rand amiq_eth_data pad[];
//Frame Check Sequence
rand amiq_eth_fcs fcs;
//determine if to use the correct fcs or not
rand bit use_correct_fcs;
constraint use_correct_fcs_c {
use_correct_fcs == 1;
}
//flag to determine if to pack/unpack the pad
local bit pack_pad;
//flag to determine if to pack/unpack the fcs
local bit pack_fcs;
//get the pad size based on the value of the input parameters
//@param min_frame_size - minimum frame size
//@param - header - Ethernet IPV4 packet header
//@return the pad size
virtual function int unsigned get_pad_size_by_fields(int min_frame_size, amiq_eth_packet_ipv4_header header);
int unsigned value = ((header.total_length + (2 * (`AMIQ_ETH_ADDRESS_WIDTH / 8)) + 6));
if(min_frame_size >= value) begin
return (min_frame_size - value);
end
else begin
return 0;
end
endfunction
//get the pad size
//@return the pad size
virtual function int unsigned get_pad_size();
return get_pad_size_by_fields(min_frame_size, header);
endfunction
constraint pad_c {
solve header.total_length before pad;
foreach (pad[i]) {
pad[i] == 8'hFF;
}
pad.size() == (min_frame_size - ((header.total_length + (2 * (`AMIQ_ETH_ADDRESS_WIDTH / 8)) + 6)));
}
//get data length in bytes
//@param local_header - Ethernet IPV4 packet header
//@return data length in bytes
virtual function int unsigned get_data_length_in_bytes(amiq_eth_packet_ipv4_header local_header);
if(local_header.total_length < local_header.get_header_length_in_bytes()) begin
`uvm_fatal("AMIQ_ETH", $sformatf("Total Length (%0d) should be bigger or equal to IHL in bytes (%0d)", local_header.total_length, local_header.get_header_length_in_bytes()));
end
return (local_header.total_length - local_header.get_header_length_in_bytes());
endfunction
constraint data_c {
data.size() == (header.total_length - (header.ihl * 4));
}
//constructor
//@param name - the name assigned to the instance of this class
function new(string name = "");
super.new(name);
ether_type = AMIQ_ETH_IPV4;
header = amiq_eth_packet_ipv4_header::type_id::create("header");
pack_pad = 1;
pack_fcs = 1;
endfunction
//pack data field
//@param packer - the packer used by this function
virtual function void do_pack_data(uvm_packer packer);
for (int index = 0; index < data.size(); index++) begin
`uvm_pack_int(data[index]);
end
endfunction
//unpack data field
//@param packer - the packer used by this function
virtual function void do_unpack_data(uvm_packer packer);
data = new[header.get_data_length_in_bytes()];
for (int index = 0; index < data.size(); index++) begin
`uvm_unpack_int(data[index]);
end
endfunction
//pack pad field
//@param packer - the packer used by this function
virtual function void do_pack_pad(uvm_packer packer);
for (int index = 0; index < pad.size(); index++) begin
`uvm_pack_int(pad[index]);
end
endfunction
//unpack pad field
//@param packer - the packer used by this function
virtual function void do_unpack_pad(uvm_packer packer);
int pad_size = get_pad_size();
pad = new[pad_size];
for (int index = 0; index < pad_size; index++) begin
`uvm_unpack_int(pad[index]);
end
endfunction
//pack fcs field
//@param packer - the packer used by this function
virtual function void do_pack_fcs(uvm_packer packer);
`uvm_pack_int(fcs);
endfunction
//unpack fcs field
//@param packer - the packer used by this function
virtual function void do_unpack_fcs(uvm_packer packer);
`uvm_unpack_int(fcs);
endfunction
//pack the entire Ethernet packet
//@param packer - the packer used by this function
virtual function void do_pack(uvm_packer packer);
super.do_pack(packer);
header.do_pack(packer);
do_pack_data(packer);
if(pack_pad) begin
do_pack_pad(packer);
end
if(pack_fcs) begin
do_pack_fcs(packer);
end
endfunction
//unpack the entire Ethernet packet
//@param packer - the packer used by this function
virtual function void do_unpack(uvm_packer packer);
super.do_unpack(packer);
header.do_unpack(packer);
do_unpack_data(packer);
if(pack_pad) begin
do_unpack_pad(packer);
end
if(pack_fcs) begin
do_unpack_fcs(packer);
end
endfunction
//converts the information containing in the instance of this class to an easy-to-read string
//@return easy-to-read string with the information contained in the instance of this class
virtual function string convert2string();
string fcs_info;
amiq_eth_fcs correct_fcs = get_correct_fcs();
if(correct_fcs == fcs) begin
fcs_info = $sformatf("FCS is correct");
end
else begin
fcs_info = $sformatf("FCS is wrong - expecting %X", correct_fcs);
end
return $sformatf("%s%s%s%sdata size: %0d%spad.size(): %0d%sFCS: %0X - %s",
super.convert2string(), `AMIQ_ETH_FIELD_SEPARATOR,
header.convert2string(), `AMIQ_ETH_FIELD_SEPARATOR,
data.size(), `AMIQ_ETH_FIELD_SEPARATOR,
pad.size(), `AMIQ_ETH_FIELD_SEPARATOR,
fcs, fcs_info);
endfunction
//compares the current class instance with the one provided as an argument
//@param rhs - Right Hand Side object
//@param comparer - The UVM comparer object used in evaluating this comparison - default is "null"
//@return 1 - objects are the same, 0 - objects are different
virtual function bit compare (uvm_object rhs, uvm_comparer comparer=null);
amiq_eth_packet_ipv4 casted_rhs;
if($cast(casted_rhs, rhs) == 0) begin
return 0;
end
if(super.compare(rhs, comparer) == 0) begin
return 0;
end
if(header.compare(casted_rhs.header, comparer) == 0) begin
return 0;
end
if(data.size() != casted_rhs.data.size()) begin
return 0;
end
for(int i = 0; i < data.size(); i++) begin
if(data[i] != casted_rhs.data[i]) begin
return 0;
end
end
if(pad.size() != casted_rhs.pad.size()) begin
return 0;
end
for(int i = 0; i < pad.size(); i++) begin
if(pad[i] != casted_rhs.pad[i]) begin
return 0;
end
end
if(fcs != casted_rhs.fcs) begin
return 0;
end
return 1;
endfunction
//function for packing the Ethernet packet into an UVM generic payload class
//@return an instance of the UVM generic payload containing the packed Ethernet packet
virtual function uvm_tlm_generic_payload to_generic_payload();
uvm_tlm_generic_payload result = super.to_generic_payload();
result.set_address(`AMIQ_ETH_PACKET_IPV4_CODE);
return result;
endfunction
function void post_randomize();
//calling header.post_randomize() is necessary here in order to ensure that the checksum from the header
//is computed - normally header.post_randomize() will be called after this.post_randomize()
header.post_randomize();
if(use_correct_fcs == 1) begin
fcs = get_correct_fcs();
end
endfunction
//function for packing the Ethernet packet using only the required information for computing the FCS
//@param bitstream - the packed bit stream is placed in "bitstream" parameter
virtual function void pack_for_fcs(ref bit bitstream[]);
bit current_pack_pad = pack_pad;
bit current_pack_fcs = pack_fcs;
pack_pad = 0;
pack_fcs = 0;
super.pack_for_fcs(bitstream);
pack_pad = current_pack_pad;
pack_fcs = current_pack_fcs;
endfunction
endclass
`endif