blob: e8fbef89131ff681887147b9215485d053d35477 [file]
/******************************************************************************
* (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_magic.sv
* PROJECT: amiq_eth
* Description: This file declare the Magic Ethernet packet.
* The definition of this packet is described in IEEE 802.1X-2010.
* For more details see file docs/ieee_802.1X-2010/802.1X-2010.pdf,
* Annex F - Support for ‘Wake-on-LAN’ protocols.
* A more detail description of the magic packet was found on
* Wiki: http://en.wikipedia.org/wiki/Wake-on-LAN#Magic_packet
*******************************************************************************/
`ifndef __AMIQ_ETH_PACKET_MAGIC
//protection against multiple includes
`define __AMIQ_ETH_PACKET_MAGIC
//Ethernet Magic packet
class amiq_eth_packet_magic extends amiq_eth_packet_ether_type;
`uvm_object_utils(amiq_eth_packet_magic)
//the address of the target device
rand amiq_eth_address target_mac;
//password size
rand int unsigned password_size;
//password
rand amiq_eth_data password[];
//MAC Client Data Size
rand int unsigned client_data_size;
constraint password_c {
solve password_size before password;
solve password_size before client_data_size;
password.size() == password_size;
}
constraint client_data_size_c {
solve password_size before client_data_size;
solve target_mac before client_data_size;
password_size inside {0, 4, 6};
//problem in system-verilog: if using here get_useful_info_size() "solve-before" no longer works
client_data_size >= (((`AMIQ_ETH_MAGIC_PACKET_SYNCH_STREAM_WIDTH + (`AMIQ_ETH_MAGIC_PACKET_TARGET_MAC_REPETITIONS * `AMIQ_ETH_ADDRESS_WIDTH) + (password_size * `AMIQ_ETH_DATA_WIDTH)) / 8)) &&
client_data_size >= `AMIQ_ETH_PAYLOAD_SIZE_MIN &&
client_data_size <= `AMIQ_ETH_PAYLOAD_SIZE_MAX;
}
//MAC Client Data
rand amiq_eth_data client_data[];
constraint client_data_c {
solve client_data_size before client_data;
client_data.size() == client_data_size;
}
//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;
}
//starting index of the magic packet synchronization stream
rand int unsigned synch_stream_starting_index;
constraint synch_stream_starting_index_c {
solve client_data_size before synch_stream_starting_index;
solve password_size before synch_stream_starting_index;
synch_stream_starting_index <= client_data_size - 1 - ((`AMIQ_ETH_MAGIC_PACKET_SYNCH_STREAM_WIDTH + (`AMIQ_ETH_MAGIC_PACKET_TARGET_MAC_REPETITIONS * `AMIQ_ETH_ADDRESS_WIDTH) +
(password_size * `AMIQ_ETH_DATA_WIDTH)) / 8);
}
function int unsigned get_useful_info_size();
int unsigned result = ((`AMIQ_ETH_MAGIC_PACKET_SYNCH_STREAM_WIDTH + (`AMIQ_ETH_MAGIC_PACKET_TARGET_MAC_REPETITIONS * `AMIQ_ETH_ADDRESS_WIDTH) +
(password_size * `AMIQ_ETH_DATA_WIDTH)) / 8);
return result;
endfunction
//flag to determine if to pack/unpack the fcs
local bit pack_fcs;
//flag to determine if to pack/unpack the client_data_size
local bit pack_client_data_size;
//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
//constructor
//@param name - the name assigned to the instance of this class
function new(string name = "");
super.new(name);
ether_type = AMIQ_ETH_WAKE_ON_LAN;
pack_fcs = 1;
pack_client_data_size = 1;
endfunction
//set the information such as magic packet synchronization stream and target address in the client data
virtual function void set_useful_info_in_client_data();
if(client_data.size() < get_useful_info_size()) begin
`uvm_fatal("AMIQ_ETH", $sformatf("%0d: client_data.size(): %0d (client_data_size: %0d) should be bigger or equal to get_useful_info_size(): %0d",
get_inst_id(), client_data.size(), client_data_size, get_useful_info_size()));
end
begin
amiq_eth_data temp_data[];
temp_data = { >> {`AMIQ_ETH_MAGIC_PACKET_SYNCH_STREAM} };
for(int i = 0; i < (`AMIQ_ETH_MAGIC_PACKET_SYNCH_STREAM_WIDTH / 8); i++) begin
int unsigned client_data_index = i + synch_stream_starting_index;
client_data[client_data_index] = temp_data[i];
end
end
begin
int target_address_starting_index = synch_stream_starting_index + (`AMIQ_ETH_MAGIC_PACKET_SYNCH_STREAM_WIDTH / 8);
int target_address_width_in_bytes = ($bits(target_mac) / 8);
amiq_eth_data temp_data[];
temp_data = { >> {target_mac} };
for(int target_mac_index = 0; target_mac_index < `AMIQ_ETH_MAGIC_PACKET_TARGET_MAC_REPETITIONS; target_mac_index++) begin
for(int byte_index = 0; byte_index < target_address_width_in_bytes; byte_index++) begin
int unsigned client_data_index = target_address_starting_index + (target_mac_index * target_address_width_in_bytes) + byte_index;
client_data[client_data_index] = temp_data[byte_index];
end
end
end
endfunction
//extract the information such as magic packet synchronization stream and target address in the client data
virtual function void get_useful_info_from_client_data();
int unsigned local_synch_stream_starting_index;
amiq_eth_data local_synch_stream[];
amiq_eth_address local_target_mac;
amiq_eth_data local_password[$];
bit success = 0;
local_synch_stream = { >> {`AMIQ_ETH_MAGIC_PACKET_SYNCH_STREAM} };
begin
int unsigned client_data_index = 0;
int synch_stream_index = 0;
while(client_data_index < client_data.size()) begin
if(synch_stream_index < (`AMIQ_ETH_MAGIC_PACKET_SYNCH_STREAM_WIDTH / 8)) begin
//synchronization stream was not found
int starting_index = client_data_index;
for(synch_stream_index = 0; synch_stream_index < (`AMIQ_ETH_MAGIC_PACKET_SYNCH_STREAM_WIDTH / 8); synch_stream_index++) begin
if(client_data[client_data_index] != local_synch_stream[synch_stream_index]) begin
client_data_index = starting_index + 1;
if(synch_stream_index > 0) begin
`uvm_info("AMIQ_ETH", $sformatf("Synchronization lost - restarting at index %0d...", client_data_index), UVM_HIGH)
end
synch_stream_index = 0;
break;
end
else begin
if(synch_stream_index == 0) begin
local_synch_stream_starting_index = client_data_index;
end
client_data_index++;
end
end
if(synch_stream_index >= (`AMIQ_ETH_MAGIC_PACKET_SYNCH_STREAM_WIDTH / 8)) begin
amiq_eth_data target_mac_array[];
bit target_mac_byte_mismatch_detected = 0;
int unsigned target_mac_bytes_number = $bits(target_mac) / 8;
`uvm_info("AMIQ_ETH", $sformatf("Identified a synchronization stream starting from index: %0d, client_data_index: %0d",
local_synch_stream_starting_index, client_data_index), UVM_HIGH)
for(int target_mac_index = 0; target_mac_index < target_mac_bytes_number; target_mac_index++) begin
target_mac_array = new[target_mac_array.size() + 1] (target_mac_array);
target_mac_array[target_mac_array.size() - 1] = client_data[client_data_index];
for(int target_mac_repetition_index = 0; target_mac_repetition_index < `AMIQ_ETH_MAGIC_PACKET_TARGET_MAC_REPETITIONS; target_mac_repetition_index++) begin
int current_index = client_data_index + ((target_mac_repetition_index * target_mac_bytes_number));
if(target_mac_array[target_mac_array.size() - 1] != client_data[current_index]) begin
`uvm_info("AMIQ_ETH", $sformatf("target MAC mismatch target_mac_array[%0d]: %X, client_data[%0d]: %X",
target_mac_array.size() - 1, target_mac_array[target_mac_array.size() - 1], current_index, client_data[current_index]), UVM_HIGH)
target_mac_byte_mismatch_detected = 1;
synch_stream_index = 0;
client_data_index = starting_index + 1;
target_mac_array = new[0];
`uvm_info("AMIQ_ETH", $sformatf("Synchronization lost - restarting at index %0d...", client_data_index), UVM_HIGH)
break;
end
end
if(target_mac_byte_mismatch_detected == 1) begin
break;
end
else begin
client_data_index++;
end
end
if(target_mac_byte_mismatch_detected == 1) begin
continue;
end
else begin
client_data_index = client_data_index + (target_mac_bytes_number * (`AMIQ_ETH_MAGIC_PACKET_TARGET_MAC_REPETITIONS - 1));
local_target_mac = { >> {target_mac_array}};
`uvm_info("AMIQ_ETH", $sformatf("Identified a target MAC: %0X", local_target_mac), UVM_HIGH)
synch_stream_starting_index = local_synch_stream_starting_index;
target_mac = local_target_mac;
success = 1;
begin
int unsigned local_password_size = 0;
if(client_data_index < client_data.size() - 6) begin
local_password_size = 6;
end
else if(client_data_index < client_data.size() - 4) begin
local_password_size = 4;
end
else begin
break;
end
begin
password = new[local_password_size];
for(int password_index = 0; password_index < local_password_size; password_index++) begin
password[password_index] = client_data[client_data_index + password_index];
end
break;
end
end
end
end
end
else begin
//synchronization stream was found - the code should never get in this part
`uvm_fatal("AMIQ_ETH", $sformatf("Problem in the algorithm - code should never get here"));
end
end
end
if(success != 1) begin
`uvm_fatal("AMIQ_ETH", $sformatf("Could not find Magic pattern and Target MAC in the payload of packet: %s", convert2string()));
end
endfunction
//pack the entire Ethernet packet
//@param packer - the packer used by this function
virtual function void do_pack(uvm_packer packer);
set_useful_info_in_client_data();
super.do_pack(packer);
if(pack_client_data_size) begin
`uvm_pack_int(client_data_size)
end
for(int i = 0; i < client_data_size; i++) begin
`uvm_pack_int(client_data[i]);
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);
if(pack_client_data_size) begin
`uvm_unpack_int(client_data_size);
end
client_data = new[client_data_size];
for(int i = 0; i < client_data_size; i++) begin
amiq_eth_data local_data;
`uvm_unpack_int(local_data);
client_data[i] = local_data;
end
if(pack_fcs) begin
do_unpack_fcs(packer);
end
get_useful_info_from_client_data();
if(ether_type != AMIQ_ETH_WAKE_ON_LAN) begin
`uvm_fatal("AMIQ_ETH", $sformatf("ether_type (%s) is different then AMIQ_ETH_WAKE_ON_LAN", ether_type.name()))
end
endfunction
function void post_randomize();
if(use_correct_fcs == 1) begin
fcs = get_correct_fcs();
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 result = super.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
result = $sformatf("%s%sStarting Index: %0d", result, `AMIQ_ETH_FIELD_SEPARATOR, synch_stream_starting_index);
result = $sformatf("%s%sTarget MAC: %012X", result, `AMIQ_ETH_FIELD_SEPARATOR, target_mac);
result = $sformatf("%s%spassword.size(): %0d", result, `AMIQ_ETH_FIELD_SEPARATOR, password.size());
result = $sformatf("%s%sclient_data.size(): %0d", result, `AMIQ_ETH_FIELD_SEPARATOR, client_data.size());
result = $sformatf("%s%sFCS: %X, %s", result, `AMIQ_ETH_FIELD_SEPARATOR, fcs, fcs_info);
return result;
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_MAGIC_CODE);
return result;
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_magic casted_rhs;
if(super.compare(rhs, comparer) == 0) begin
return 0;
end
if($cast(casted_rhs, rhs) == 0) begin
return 0;
end
if(client_data_size != casted_rhs.client_data_size) begin
return 0;
end
for(int i = 0; i < client_data.size(); i++) begin
if(client_data[i] != casted_rhs.client_data[i]) begin
return 0;
end
end
if(fcs != casted_rhs.fcs) begin
return 0;
end
if(synch_stream_starting_index != casted_rhs.synch_stream_starting_index) begin
return 0;
end
if(target_mac != casted_rhs.target_mac) begin
return 0;
end
for(int i = 0; i < password.size(); i++) begin
if(i < casted_rhs.password.size()) begin
if(client_data[i] != casted_rhs.client_data[i]) begin
return 0;
end
end
else begin
break;
end
end
return 1;
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_fcs = pack_fcs;
bit current_pack_client_data_size = pack_client_data_size;
pack_fcs = 0;
pack_client_data_size = 0;
super.pack_for_fcs(bitstream);
pack_fcs = current_pack_fcs;
pack_client_data_size = current_pack_client_data_size;
endfunction
//pack the Ethernet packet to a list of bytes in the format required by Wireshark software
//@param byte_data - array in which to put the packed information
virtual function void to_wireshark_array(ref byte unsigned byte_data[$]);
bit current_pack_client_data_size = pack_client_data_size;
pack_client_data_size = 0;
super.to_wireshark_array(byte_data);
pack_client_data_size = current_pack_client_data_size;
endfunction
endclass
`endif