OpenCores
URL https://opencores.org/ocsvn/ht_tunnel/ht_tunnel/trunk

Subversion Repositories ht_tunnel

[/] [ht_tunnel/] [tags/] [START/] [bench/] [flow_control_l2/] [flow_control_l2_tb/] [flow_control_l2_tb.cpp] - Rev 2

Go to most recent revision | Compare with Previous | Blame | View Log

//flow_control_l2_tb.cpp
/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (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.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is HyperTransport Tunnel IP Core.
 *
 * The Initial Developer of the Original Code is
 * Ecole Polytechnique de Montreal.
 * Portions created by the Initial Developer are Copyright (C) 2005
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Ami Castonguay <acastong@grm.polymtl.ca>
 *
 * Alternatively, the contents of this file may be used under the terms
 * of the Polytechnique HyperTransport Tunnel IP Core Source Code License 
 * (the  "PHTICSCL License", see the file PHTICSCL.txt), in which case the
 * provisions of PHTICSCL License are applicable instead of those
 * above. If you wish to allow use of your version of this file only
 * under the terms of the PHTICSCL License and not to allow others to use
 * your version of this file under the MPL, indicate your decision by
 * deleting the provisions above and replace them with the notice and
 * other provisions required by the PHTICSCL License. If you do not delete
 * the provisions above, a recipient may use your version of this file
 * under either the MPL or the PHTICSCL License."
 *
 * ***** END LICENSE BLOCK ***** */
 
#include "flow_control_l2_tb.h"
#include <sstream>
 
using namespace std;
 
const unsigned flow_control_l2_tb::next_node_databuffers_maximum[3] = {8,8,8};
const unsigned flow_control_l2_tb::next_node_buffers_maximum[3] = {8,8,8};
const int flow_control_l2_tb::nop_counter_delay = NOP_COUNTER_DELAY;
 
flow_control_l2_tb::flow_control_l2_tb(sc_module_name name){
	SC_THREAD(simulate_memory);
	sensitive_pos(clk);
 
	SC_THREAD(produce_inputs);
	sensitive_pos(clk);
 
	//verify_output() MUST run after produce_inputs, 
	//so wait for it to produce an event instead of being sensitive to clock
	SC_THREAD(verify_output);
	sensitive(input_produced);
 
	SC_THREAD(control_testbench);
	sensitive_pos(clk);
 
	srand(1987);
	error = false;
 
	for(int n = 0; n < 10; n++)
		nop_delay[n].nop_received = false;
}
 
void flow_control_l2_tb::simulate_memory(){
	while(true){
		history_memory_output = history_memory[(int)history_memory_read_address.read()];
		if(history_memory_write.read())
			history_memory[(unsigned)history_memory_write_address.read()] = 
				(unsigned)(sc_uint<32>)history_memory_write_data.read();
		wait();
	}
}
 
void flow_control_l2_tb::control_testbench(){
	lk_rx_connected = true;
	percent_chance_from_eh = 0;
	percent_chance_from_csr = 0;
	percent_chance_from_fwd = 0;
	percent_chance_from_ui = 0;
 
	percent_chance_nop_req_db = 0;
	percent_chance_nop_req_ro = 0;
 
	percent_chance_nop_received = 0;
 
	ldtstopx = true;
	resetx = false;
	csr_retry = true;
	for(int n = 0; n < 3; n++) wait();
	if(error) return;
	resetx = true;
 
	percent_chance_from_eh = 20;
	percent_chance_from_csr = 40;
	percent_chance_from_fwd = 60;
	percent_chance_from_ui = 7;
 
	percent_chance_nop_req_db = 8;
	percent_chance_nop_req_ro = 8;
 
	percent_chance_nop_received = 15;
 
	for(int n = 0; n < 65; n++) wait();
	if(error) return;
	cd_initiate_retry_disconnect = true;
	wait();
	cd_initiate_retry_disconnect = false;
 
	for(int n = 0; n < 20; n++) wait();
 
	lk_rx_connected = false;
	for(int n = 0; n < 5; n++) wait();
	lk_rx_connected = true;
 
}
 
void flow_control_l2_tb::produce_inputs(){
	sc_bv<32> cmd;
 
	////////////////////////////////////////////////
	//Generate a packet from the different sources
	//Output them, but don't make them available yet
	/////////////////////////////////////////////////
 
	//Output Error handler packet
	generate_random_response(cmd,data_left_eh);
	eh_cmd_data_fc = cmd;
	eh_available_fc = false;
 
	//Output CSR packet
	generate_random_response(cmd,data_left_csr);
	csr_dword_fc = cmd;
	csr_available_fc = false;
 
	//Output UI packet
	sc_bv<64> pkt;
	unsigned datalength;
	VirtualChannel ui_vc = VC_NONE;
	ui_packet_fc = 0;
	ui_available_fc = false;
 
 
	//OutputFWD packet
	syn_ControlPacketComplete pkt_complete;
	VirtualChannel fwd_vc = generate_random_packet(pkt,datalength);
	//Not used by flow_control_l3, so any value is ok.  The state machine reads
	//the chain bit off of the packet if needed
	pkt_complete.isPartOfChain = false;
	pkt_complete.packet = pkt;
	pkt_complete.error64BitExtension = false;
	pkt_complete.data_address = rand();
 
	ro_packet_fwd = pkt_complete;
	ro_packet_vc_fwd = fwd_vc;
	ro_available_fwd = false;
 
	unsigned data_sent_ui = 0;
 
	crc1 = 0xFFFFFFFF;
	crc2 = 0xFFFFFFFF;
 
	int retry_sequence_count = 0;
 
 
	//////////////////////////////////////
	// Misc init
	//////////////////////////////////////
	current_user_data_vc = VC_NONE;
	current_user_data_vc_output = VC_NONE;
	fc_status = FC_NONE;
 
	cd_rx_next_pkt_to_ack_fc = 0;
	data_sent_db = 0;
 
	wait();
	while(!error){
 
		/////////////////////////////////////////////////////
		// Handle sending packets from the different sources
		/////////////////////////////////////////////////////
 
		//Randomly set if input data is available
		eh_available_fc = (rand() % 100 < percent_chance_from_eh) || fc_status == FC_SENDING_EH;
		csr_available_fc = (rand() % 100 < percent_chance_from_csr) || fc_status == FC_SENDING_CSR;
		ro_available_fwd = rand() % 100 < percent_chance_from_fwd;
 
		//If reading data from errorhandler, add it to the queue of expected output dword
		if(fc_ack_eh.read() && resetx.read()){
			if(fc_status != FC_SENDING_EH && fc_status != FC_NONE){
				cout << "ERROR: Reading EH while other task not done" <<endl;
				cout << "fc_status: " << fc_status << endl;
				error = true; continue;
			}
 
			OutputDword expected;
			expected.dword = eh_cmd_data_fc.read();
			if(fc_status == FC_NONE){
				expected.lctl = true;  expected.hctl = true;
			}
			else{
				expected.lctl = false;  expected.hctl = false;
			}
			expected_output.push_back(expected);
 
			if(data_left_eh){
				data_left_eh--;
				getRandomVector(cmd);
				eh_cmd_data_fc = cmd;
				fc_status = FC_SENDING_EH;
 
				//In retry mode, calculate the CRC of the packet
				if(csr_retry.read()){
					bool command = (crc2 == 0xFFFFFFFF);
					calculate_crc2(eh_cmd_data_fc.read(),command, command);
				}
			}
			else{
				//In retry mode, expect a following CRC
				if(csr_retry.read()){
					if(crc2 != 0xFFFFFFFF){
						calculate_crc2(eh_cmd_data_fc.read(),false, false);
						expected.dword = ~crc2;
						expected.lctl = false;  expected.hctl = true;
						crc2 = 0xFFFFFFFF;
					}
					else{
						calculate_crc1(eh_cmd_data_fc.read(),true, true);
						expected.dword = ~crc1;
						expected.lctl = true;  expected.hctl = false;
						crc1 = 0xFFFFFFFF;
					}
					expected_output.push_back(expected);
				}
 
				//When packet done being sent, generate a new one
				generate_random_response(cmd,data_left_eh);
				eh_cmd_data_fc = cmd;
				fc_status = FC_NONE;
			}
		}
 
		//If reading data from CSR, add it to the queue of expected output dword
		if(fc_ack_csr.read() && resetx.read()){
			if(fc_status != FC_SENDING_CSR && fc_status != FC_NONE){
				cout << "ERROR: Reading CSR while other task not done" <<endl;
				cout << "Current status: " << fc_status << endl;
				error = true; continue;
			}
 
			OutputDword expected;
			expected.dword = csr_dword_fc.read();
			if(fc_status == FC_NONE){
				expected.lctl = true;  expected.hctl = true;
			}
			else{
				expected.lctl = false;  expected.hctl = false;
			}
			expected_output.push_back(expected);
 
			if(data_left_csr){
				data_left_csr--;
				getRandomVector(cmd);
				csr_dword_fc = cmd;
				fc_status = FC_SENDING_CSR;
 
				//In retry mode, calculate the CRC of the packet
				if(csr_retry.read()){
					bool command = (crc2 == 0xFFFFFFFF);
					calculate_crc2(csr_dword_fc.read(),command, command);
				}
			}
			else{
				//In retry mode, expect a following CRC
				if(csr_retry.read()){
					if(crc2 != 0xFFFFFFFF){
						calculate_crc2(csr_dword_fc.read(),false, false);
						expected.dword = ~crc2;
						expected.lctl = false;  expected.hctl = true;
						crc2 = 0xFFFFFFFF;
					}
					else{
						calculate_crc1(csr_dword_fc.read(),true, true);
						expected.dword = ~crc1;
						expected.lctl = true;  expected.hctl = false;
						crc1 = 0xFFFFFFFF;
					}
					expected_output.push_back(expected);
				}
				generate_random_response(cmd,data_left_csr);
				csr_dword_fc = cmd;
				fc_status = FC_NONE;
			}
		}
 
 
		//If reading data from command buffers, add it to the queue of expected output dword
		if(fwd_ack_ro.read() && resetx.read()){
			if(fc_status != FC_NONE){
				cout << "ERROR: Reading FWD command while other task not done" <<endl;
				cout << "  - fc_status=" << fc_status << endl;
				error = true; continue;
			}
 
			OutputDword expected;
			expected.dword = ro_packet_fwd.read().packet.range(31,0);
			expected.lctl = true;  expected.hctl = true;
			expected_output.push_back(expected);
 
			PacketCommand cmd = getPacketCommand(ro_packet_fwd.read().packet.range(5,0));
			bool current_dword = isDwordPacket(ro_packet_fwd.read().packet,cmd);
			bool current_data = hasDataAssociated(cmd);
			int current_datalength = (int)getDataLengthm1(ro_packet_fwd.read().packet) + 1;
			VirtualChannel current_vc = getVirtualChannel(ro_packet_fwd.read().packet,cmd);
 
			if(!current_dword){
				expected.dword = ro_packet_fwd.read().packet.range(63,32);
				expected_output.push_back(expected);
			}
 
			if(current_datalength){
				sc_uint<4> addr = ro_packet_fwd.read().data_address;
				update_databuffer_entry(current_datalength,current_vc,addr);
			}
 
			fwd_vc = generate_random_packet(pkt,datalength);
			//Not used by flow_control_l3, so any value is ok.  The state machine reads
			//the chain bit off of the packet if needed
			pkt_complete.packet = pkt;
			pkt_complete.data_address = rand();
 
			//In retry mode, calculate the CRC of the packet
			if(csr_retry.read()){
				//If it is a command packet with associated data, the CRC will also
				//need to be calculated from the data packet, so don't add the CRC to
				//the expected queue just yet
				if(current_data){
					calculate_crc2(ro_packet_fwd.read().packet.range(31,0),true, true);
					if(!current_dword)
						calculate_crc2(ro_packet_fwd.read().packet.range(63,32),true, true);
				}
				else{
					//If it is a command packet without associated data, expect a following CRC
					calculate_crc1(ro_packet_fwd.read().packet.range(31,0),true, true);
					if(!current_dword)
						calculate_crc1(ro_packet_fwd.read().packet.range(63,32),true, true);
					expected.dword = ~crc1;
					expected.lctl = true;  expected.hctl = false;
					crc1 = 0xFFFFFFFF;
					expected_output.push_back(expected);
				}
			}
			if(current_data)
				fc_status = FC_SENDING_FWD;
 
			ro_packet_fwd = pkt_complete;
			ro_packet_vc_fwd = fwd_vc;
		}
 
 
		///////////////////////////////////////////////////////////////////////////////
		//Packets from UI work a bit differently.  It's a write in a FIFO and we must
		//only make a packet available when it CAN be accepted.
		///////////////////////////////////////////////////////////////////////////////
		//Generate a random packet
		ui_vc = generate_random_packet(pkt,datalength);
 
		//Check that it can be sent
		bool can_send_ui = !(sc_bit)fc_user_fifo_ge2_ui.read()[(unsigned)ui_vc];
 
		//send if(necessary
		bool send_ui = (rand() % 100 < percent_chance_from_ui) && can_send_ui;
 
		if(send_ui && resetx.read()){
			ui_available_fc = true;
 
			if(datalength != 0)
				add_ui_data_packet(datalength,ui_vc,pkt);
			ui_packet_fc = pkt;
			user_packets.push_back(pkt);
			//cout << "SENDING_UI: pkt=" << pkt.to_string(SC_HEX) << " datalength=" << datalength << endl;
		}
		else{
			ui_available_fc = false;
		}
 
 
		/////////////////////////////////////////////////////
		// Handle nop related signals
		/////////////////////////////////////////////////////
		db_nop_req_fc =  rand()% 100 < percent_chance_nop_req_db;;
		ro_nop_req_fc =  rand()% 100 < percent_chance_nop_req_ro;;
 
		ro_buffer_cnt_fc = rand() & 0x3F;
		db_buffer_cnt_fc = rand() & 0x3F;
 
		//Force to 0 the buffer count as if all buffers were sent during retry sequence
		//(needed to start the retry sequence)
		if(!retry_sequence || !lk_rx_connected.read()) retry_sequence_count = 0;
		if(cd_nop_received_fc.read())retry_sequence_count++;
 
		if(retry_sequence && retry_sequence_count > 2 && retry_sequence_count < 5){
			ro_buffer_cnt_fc = 0;
			db_buffer_cnt_fc = 0;
		}
 
		cd_nop_received_fc = nop_delay[nop_counter_delay-1].nop_received;
		if(nop_delay[nop_counter_delay-1].nop_received){
			cd_nop_ack_value_fc = nop_delay[nop_counter_delay-1].nop_ack_value;
			cd_nopinfo_fc = nop_delay[nop_counter_delay-1].nop_information;
		}
 
		//Generate a value that we can check in the received nops.  The 0x20 is just
		//an arbitrary value ored in to make sure that rand() doesn't produce 0.  Having
		//0 is not wanted because receiving 0 *could* also mean that the design isn't working.
		if(resetx.read())
			cd_rx_next_pkt_to_ack_fc = (rand() & 0xFF) | 0x20;
		else
			cd_rx_next_pkt_to_ack_fc = 0;
 
		//If a nop is sent, tag it as being expected with the correct value
		if(fc_nop_sent.read() && resetx.read()){
			OutputDword expected;
			expected.dword = 0;
			expected.dword.range(19,18) = db_buffer_cnt_fc.read().range(5,4);
			expected.dword.range(17,16) = ro_buffer_cnt_fc.read().range(5,4);
			expected.dword.range(15,14) = db_buffer_cnt_fc.read().range(3,2);
			expected.dword.range(13,12) = ro_buffer_cnt_fc.read().range(3,2);
			expected.dword.range(11,10) = db_buffer_cnt_fc.read().range(1,0);
			expected.dword.range(9,8)   = ro_buffer_cnt_fc.read().range(1,0);
 
			expected.dword.range(31,24) = cd_rx_next_pkt_to_ack_fc.read();
			expected.lctl = true;
			expected.hctl = true;
			expected_output.push_back(expected);
 
			if(csr_retry.read()){
				calculate_crc1(expected.dword,true, true);
				expected.dword = ~crc1;
				expected.lctl = true;  expected.hctl = false;
				crc1 = 0xFFFFFFFF;
				expected_output.push_back(expected);
			}
		}
 
		/////////////////////////////////////////////////////
		// Handle sending data from Databuffer and UI
		/////////////////////////////////////////////////////
 
		//from databuffer
		if(fwd_address_db.read() == databuffer_data.address &&
			fwd_vctype_db.read() == databuffer_data.vc &&
			data_sent_db < (unsigned)databuffer_data.size)
		{
			db_data_fwd = databuffer_data.dwords[data_sent_db];
		}
		else{
			db_data_fwd = 0;
		}
 
		if(fwd_read_db.read() && resetx.read()){
			if(fc_status != FC_SENDING_FWD){
				cout << "ERROR: Reading FWD data without having previously read a fwd command" <<endl;
				cout << "  - fc_status=" << fc_status << endl;
				error = true; continue;
			}
			if(databuffer_output_vc != databuffer_data.vc ||
				databuffer_output_addr != databuffer_data.address ||
				data_sent_db == databuffer_data.size)
			{
				cout << "ERROR: Invalid read to databuffer data" << endl;
				if(databuffer_output_vc != databuffer_data.vc) 
					cout << "  -VC expected: " << databuffer_data.vc << " Received: " << databuffer_output_vc << endl;
				if(databuffer_output_addr != databuffer_data.address) 
					cout << "  -address expected: " << databuffer_data.address << " Received: " << databuffer_output_addr << endl;
				if(data_sent_db == databuffer_data.size) cout << "  completed sending data already" << endl;
				error = true; continue;
			}
 
			OutputDword expected;
			expected.dword = db_data_fwd.read();
			expected.lctl = false;  expected.hctl = false;
			expected_output.push_back(expected);
			if(csr_retry.read())
				calculate_crc2( db_data_fwd.read(),false, false);
 
			data_sent_db++;
 
			//cout << "Data sent db: " << data_sent_db <<  "  Data size: " << databuffer_data.size << endl;
 
			if(data_sent_db == databuffer_data.size){
				db_data_fwd = 0;
				fc_status = FC_NONE;
				data_sent_db = 0;
				if(csr_retry.read()){
					expected.dword = ~crc2;
					expected.lctl = false;  expected.hctl = true;
					crc2 = 0xFFFFFFFF;
					expected_output.push_back(expected);
				}
			}
			else{
				db_data_fwd = databuffer_data.dwords[data_sent_db];
			}
		}
 
		databuffer_output_vc = fwd_vctype_db.read();
		databuffer_output_addr = (unsigned)fwd_address_db.read();
 
		//from UI
		if(current_user_data_vc != VC_NONE){
			if(fc_data_vc_ui.read() != current_user_data_vc){
				cout << "ERROR: VC value not held until all UI data read" << endl;
				error = true; continue;
			}
 
			if(fc_consume_data_ui.read()){
				data_sent_ui++;
				if(data_sent_ui != user_data[current_user_data_vc].front().size){
					ui_data_fc = user_data[current_user_data_vc].front().dwords[data_sent_ui];
				}
				else{
					data_sent_ui = 0;
					current_user_data_vc = VC_NONE;
					fc_status = FC_NONE;
				}
 
			}
		}
		else{
			if(fc_consume_data_ui.read()){
				if(fc_status != FC_NONE){
					cout << "ERROR: Reading UI data while other task not done" <<endl;
					cout << "  - fc_status=" << fc_status << endl;
					error = true; continue;
				}
				if(user_data[fc_data_vc_ui.read()].empty() ||
					fc_data_vc_ui.read() !=current_user_data_vc_output)
				{
					cout << "ERROR: << Invalid VC when reading UI data" << endl;
					error = true; continue;
				}
				if(user_data[fc_data_vc_ui.read()].front().size != 1){
					data_sent_ui = 1;
					current_user_data_vc = fc_data_vc_ui.read();
					fc_status = FC_SENDING_UI;
					ui_data_fc = user_data[fc_data_vc_ui.read()].front().dwords[data_sent_ui];
				}
			}
			else{
				if(fc_data_vc_ui.read() == VC_NONE){
					ui_data_fc = 0;
				}
				else if(user_data[fc_data_vc_ui.read()].empty())
					ui_data_fc = 0;
				else{
					ui_data_fc = user_data[fc_data_vc_ui.read()].front().dwords[0];
				}
			}
		}
 
		current_user_data_vc_output = fc_data_vc_ui.read();
 
		//verify_output() MUST run after produce_inputs, 
		//so when produce_inputs is done, do a notification
		input_produced.notify();
		wait();		
	}
}
 
void flow_control_l2_tb::verify_output(){
 
	////////////////////////////////////////////////////
	// Initialization
	////////////////////////////////////////////////////
 
	clear_next_node_information();
 
	ignore_next_dword_for_ack = false;
 
	bool receiving_ui_datapacket = false;
	VirtualChannel ui_packet_vc = VC_NONE;
	unsigned ui_data_received = 0;
	bool expected_second_ui_command_dword = false;
	bool expecting_nop_crc = false;
	sc_bv<32> second_ui_command_dword;
 
	//At the beggining of a connection, we can expect to receive NOP's
	//even though they're not really expected
	bool connection_start = true;
 
	bool expected_discon_nop = false;
	bool received_discon_nop = false;
	bool retry_discon_nop = false;
	int max_delay_discon_nop = 0;
 
	retry_sequence = false;
 
	//Loop while no error is detected
	//Catch any exception
	try	{ while(!error){
		//Under reset, clear some information
		if(!resetx.read()){
			clear_next_node_information();
			connection_start = true;
			retry_sequence = false;
		}
 
		//////////////////////
		//Analyze disconnect nops
		//////////////////////
		if(expected_discon_nop && !received_discon_nop){
			max_delay_discon_nop--;
			if(max_delay_discon_nop <= 0){
				cout << "ERROR: Disconnect nop expected but never received (timeout)" << endl;
				error = true; continue;
			}
		}
 
		if((!ldtstopx.read() || 
			lk_initiate_retry_disconnect.read() && csr_retry.read() || 
			cd_initiate_retry_disconnect.read() && csr_retry.read())&& !expected_discon_nop)
		{
			expected_discon_nop = true;
			max_delay_discon_nop = 32;
			retry_discon_nop = csr_retry.read();
		}
 
		if(!retry_discon_nop && ldtstopx.read()){
			expected_discon_nop = false;
			received_discon_nop = false;
		}
		//////////////////////
		//Validate packet sent
		//////////////////////
		lk_consume_fc = rand() % 10 < 9;
 
		//There is always a first empty nop at the begginning, read it
		if(connection_start && lk_consume_fc.read() && resetx.read()){
			connection_start = false;
			crc1 = 0xFFFFFFFF;
			crc2 = 0xFFFFFFFF;
			//If in retry mode, expect a nop CRC
			OutputDword o; o.dword = 0; o.lctl = true; o.hctl = true;
			expected_output.push_back(o);
			if(csr_retry.read()){
				calculate_crc1(o.dword,true,true);
				o.dword = ~crc1; o.lctl = true; o.hctl = false;
				crc1 = 0xFFFFFFFF;
				expected_output.push_back(o);
			}
		}
 
		//Stop ignoring output when fc_disconnect_lk returns to false
		/** It is needed to do it here because if it is done in
		    manage_retry_sequence, if there is nothing in the history
			to play back, the first dword would be missed.*/
		if(!fc_disconnect_lk.read()) retry_disconnect = false;
 
 
		/**
			In this section, we check if what is received is valid.
			There are two possible correct values : a packet from the
			expected list or a packet that was stored in the UI fifo.
 
			All packets exluding packets from the UI are consumed by
			the Flow Control module so we know that we are expecting
			thos packets.  UI packets on the other hand are stored
			in a buffer before being sent, so we do not know exactly
			when they will be received.
 
			So we first check if it was from the expected list, and
			then if it's a valid packet that was sent from the UI.
		*/
		if(retry_sequence || retry_disconnect){
			manage_retry_sequence();
		}	
		else if(lk_consume_fc.read() && !connection_start){
			OutputDword expected;
			if(!expected_output.empty())
				expected = expected_output.front();
			/*
				bool receiving_ui_packet = false;
				VirtualChannel ui_packet_vc = VC_NONE;
				unsigned ui_data_received = 0;
			*/
			//If we are currently receiving a UI DATA packet
			if(expected_second_ui_command_dword){
				if(fc_dword_lk.read() != second_ui_command_dword ||
					!fc_lctl_lk.read() || !fc_hctl_lk.read())
				{
					cout << "ERROR: Invalid second UI dword received" << endl;
					cout << "Dword expected: " << second_ui_command_dword.to_string(SC_HEX) <<
						" received: " << fc_dword_lk.read().to_string(SC_HEX) << endl;
					error = true; continue;
				}
				expected_second_ui_command_dword = false;
			}
			//If expecting a nop CRC, just check that it is correct in the expected
			//dwords list
			else if(expecting_nop_crc){
				if(fc_dword_lk.read() == expected.dword &&
					fc_lctl_lk.read() == expected.lctl &&
					fc_hctl_lk.read() == expected.hctl &&
					!expected_output.empty())
				{
					expected_output.pop_front();
					expecting_nop_crc = false;
				}
				else{
					cout << "ERROR: Invalid NOP CRC received while receiving UI data" << endl;
				}
			}
			//If already receiving data packet
			else if(receiving_ui_datapacket){
				//Check if receiving a nop
				if(fc_lctl_lk.read() || fc_hctl_lk.read()){
					if(!(fc_lctl_lk.read()&& fc_hctl_lk.read() &&
						fc_dword_lk.read().range(5,0) == 0))
					{
						cout << "ERROR: Invalid non data packet received while receiving UI data" << endl;
						cout << "Received: " << fc_dword_lk.read().to_string(SC_HEX) << endl;
						error = true; continue;
					}
					if(fc_dword_lk.read() == expected.dword &&
						fc_lctl_lk.read() == expected.lctl &&
						fc_hctl_lk.read() == expected.hctl &&
						!expected_output.empty())
					{
						expected_output.pop_front();
						expecting_nop_crc = csr_retry.read();
					}
					else{
						cout << "ERROR: Unexpected NOP received while receiving UI data" << endl;
						cout << "Received: " << fc_dword_lk.read().to_string(SC_HEX);
						error = true; continue;
					}
				}
				//Check if the correct data is received
				else{
					PacketData& data = user_data[ui_packet_vc].front();
					//cout << "Receiving UI datapacket" << fc_dword_lk.read().to_string(SC_HEX) << endl;
					//cout << "Size of data: " << data.size << " data reveived so far: " << ui_data_received << endl;
					//cout << "Control packet: " << data.associated_control_pkt.to_string(SC_HEX) << endl;
 
					if(data.dwords[ui_data_received++] != fc_dword_lk.read())
					{
						cout << "ERROR: Invalid data received while reveiving ui data" << endl;
						cout << "Expected: " << sc_uint<32>(data.dwords[ui_data_received-1]).to_string(SC_HEX) << 
							" received: " << fc_dword_lk.read().to_string(SC_HEX) << endl;
						error = true; continue;
					}
					//If retry mode, expect the CRC
					if(data.size == ui_data_received){
						receiving_ui_datapacket = false;
						ui_data_received = 0;
						if(csr_retry.read()){
							add_expected_crc_for_packet(user_data[ui_packet_vc].front());
						}
						user_data[ui_packet_vc].pop_front();
					}
				}
			}
			//If the packet is expected
			else if(fc_dword_lk.read() == expected.dword &&
				fc_lctl_lk.read() == expected.lctl &&
				fc_hctl_lk.read() == expected.hctl &&
				!expected_output.empty())
			{
				expected_output.pop_front();
			}
			//Check for a disconnect nop
			else if(fc_lctl_lk.read() && fc_hctl_lk.read() && fc_dword_lk.read() == 0x00000040){
				if(!expected_discon_nop){
					cout << "ERROR: Unexpected disconnect nop" << endl;
					error = true; continue;
				}
				else{
					if(retry_discon_nop){
						expected_discon_nop = false;
						start_retry_sequence();
					}
					else{
						received_discon_nop = true;
					}
				}
			}
			//Check if it is in the UI fifo
			else if(fc_lctl_lk.read() && fc_hctl_lk.read()){
				//Try to find the packet in the list send from UI
				bool found = false;
				deque<sc_bv<64> >::iterator i;
				PacketCommand cmd = getPacketCommand(fc_dword_lk.read().range(5,0));
				receiving_ui_datapacket = hasDataAssociated(cmd);
				for(i = user_packets.begin(); i != user_packets.end();i++){
					if((*i).range(31,0) == fc_dword_lk.read()){
						expected_second_ui_command_dword = !isDwordPacket((*i),cmd);
						second_ui_command_dword = (*i).range(63,32);
						found = true;
 
						/**This is a hack to keep the code a bit more simple.  When a 
						CRC is expected for a UI packet without data, add it to the
						expected list.  It is impossible to do this for packets that have
						data because NOPs might get inserted to the expected list, the CRC
						calculation is taken car of in the UI data reception code.*/
						if(csr_retry.read() && !receiving_ui_datapacket){
							unsigned tmp = crc1;
							crc1 = 0xFFFFFFFF;
							calculate_crc1(fc_dword_lk.read(),true,true);
							if(expected_second_ui_command_dword)
								calculate_crc1((*i).range(63,32),true,true);
 
							OutputDword o;
							o.dword = ~crc1;
							o.lctl = true;
							o.hctl = false;
							expected_output.push_front(o);
 
							crc1 = tmp;
						}
 
						break;
					}
				}
 
 
 
				//If not found, generate an error
				if(!found){
					cout << "ERROR: Packet not found in the packet list sent from fifo" << endl;
					cout << "Dword not found: " << fc_dword_lk.read().to_string(SC_HEX) << endl;
					if(expected_output.empty()){
						cout << "No expected dword" << endl;	
					}
					else{
						cout << "Expected dword: " << expected.dword.to_string(SC_HEX) << endl;
						cout << "Expected LCTL: " << expected.lctl << endl;
						cout << "Expected HCTL: " << expected.hctl << endl;
					}
					error = true; continue;
				}
 
				//Check if it's legal to send that packet by checking the previous packets
				bool passPW = getPassPW(*i);
				ui_packet_vc = getVirtualChannel(*i,cmd);
				bool illegal = false;
				deque<sc_bv<64> >::iterator n;
				for(n = user_packets.begin(); n != i && !illegal;n++){
					PacketCommand cmd_n = getPacketCommand(n->range(5,0));
					VirtualChannel vc_n = getVirtualChannel(*n,cmd_n);
					bool passPW_n = getPassPW(*n);
					switch(ui_packet_vc){
						case VC_POSTED:
							illegal = vc_n == VC_POSTED && (passPW_n || !passPW);
							break;
						case VC_NON_POSTED:
							illegal = !passPW || passPW_n;
							break;
						case VC_RESPONSE:
							illegal = vc_n == VC_RESPONSE || !passPW && vc_n == VC_POSTED;
							break;
						default:
							cout << "ERROR: Internal error, an invalide packet was received but was expected" << endl;
							error = true; continue;
					}
					if(illegal) break;
				}
				if(illegal){
					cout << "ERROR: Received a packet which broke ordering rules within UI FIFO" << endl;
					cout << "Packet: " << i->to_string(SC_HEX) << " passed: " << n->to_string(SC_HEX) << endl;
					error = true; continue;
				}
				user_packets.erase(i);
			}
			else{
				cout << "ERROR: Unexpected non command packet received" << endl;
				cout << "Received: " << fc_dword_lk.read().to_string(SC_HEX) <<
						" LCTL: " << fc_lctl_lk.read() <<
						" HCTL: " << fc_hctl_lk.read() << endl;
				if(expected_output.empty()){
					cout << "Nothing expected" << endl;
				}
				else{
					cout << "Expected: " << expected_output.front().dword.to_string(SC_HEX) << 
						" LCTL: " << expected_output.front().lctl <<
						" HCTL: " << expected_output.front().hctl << endl;
				}
				error = true; continue;
			}
		}
 
		manage_ack_buffer_count();
		send_next_node_nops();
		free_buffers();
		if(csr_retry.read()){
			manage_history();
		}
		wait();
	}/* while*/ } // try
	catch(TestbenchError tbe){
		cout << tbe.message << endl;
		error = true;
	}
	resetx = false;
}
 
void flow_control_l2_tb::manage_retry_sequence(){
	//Analyze output when data is read, unless we are ignoring output because
	//a retry disconnect sequence was just initiated
	if(lk_consume_fc.read() && !retry_disconnect){
		//A nop was sent on previous cycle, we expect a nop CRC
		if(retry_expect_nop_crc){
			if(retry_playback_history.empty()){
				cout << "*!!!* History playback empty" << endl;
				retry_sequence = false;
			}
			if(expected_output.empty()){
				TestbenchError te;
				te.message = "No nop CRC in expected output (retry sequence)";
				throw te;
			}
			if(expected_output.front().dword != fc_dword_lk.read() ||
				expected_output.front().lctl != fc_lctl_lk.read() ||
				expected_output.front().hctl != fc_hctl_lk.read())
			{
				TestbenchError te;
				ostringstream o;
				o << "Expecting nop CRC (retry sequence), invalid dword received" << endl
					<< "Expected: " << expected_output.front().dword.to_string(SC_HEX) << " Received: " << 
					fc_dword_lk.read().to_string(SC_HEX) << endl
					<< "Expected LCTL: " << expected_output.front().lctl << " xpected HCTL: " << expected_output.front().hctl;
				te.message = o.str();
				throw te;
			}
			expected_output.pop_front();
 
			retry_expect_nop_crc = false;
		}
		//First dword of a quad word packet sent last cycle, expect the secont dword
		else if(retry_second_dword_next){
			retry_second_dword_next = false;
			if(fc_dword_lk.read() != retry_playback_history.front().pkt.range(63,32) ||
				!fc_lctl_lk.read() || !fc_hctl_lk.read())
			{
				TestbenchError err;
				err.message = "ERROR: Wrong second command dword during history playback\n";
				throw err;
			}
		}
		//Packet was done being sent last cycle, expect the following CRC
		else if(retry_crc_next){
			retry_crc_next = false;
			bool has_data = retry_playback_history.front().data_size != 0;
			if(fc_dword_lk.read() != retry_playback_history.front().crc ||
				fc_lctl_lk.read() == has_data || fc_hctl_lk.read() != has_data)
			{
				TestbenchError err;
				err.message = "ERROR: Wrong crc dword during history playback\n";
				throw err;
			}
			retry_playback_history.pop_front();
 
			//Retry sequence is over the the playback history is empty
			retry_sequence = !retry_playback_history.empty();
		}
		//Command packet with data associated received last cycle, now expect the following data
		else if(retry_receive_data){
			if(fc_lctl_lk.read() && fc_hctl_lk.read() && !expected_output.empty()){
				if(fc_dword_lk.read() == expected_output.front().dword){
					expected_output.pop_front();
					retry_expect_nop_crc = true;
				}
				else{
					TestbenchError err;
					ostringstream o;
					o << "ERROR: Unexpected command packet received while receiving retry data\n" <<
						"Received: " << fc_dword_lk.read().to_string(SC_HEX) << 
						" Expected: " << expected_output.front().dword.to_string(SC_HEX);
					err.message = o.str();
					throw err;
				}
			}
			else{
				if(fc_dword_lk.read() != retry_playback_history.front().data[retry_data_count++] ||
					fc_lctl_lk.read() || fc_hctl_lk.read())
				{
					TestbenchError err;
					ostringstream o;
					o << "ERROR: Wrong data dword during history playback\nExpected[" << retry_data_count-1 <<"]: " << 
						sc_uint<32>(retry_playback_history.front().data[retry_data_count-1]).to_string(SC_HEX)
						<< " Received: " << fc_dword_lk.read().to_string(SC_HEX);
					err.message = o.str();
					throw err;
				}
				//cout << "Data size: " << retry_playback_history.front().data_size << " retry_data_count: " << retry_data_count << endl;
				retry_receive_data = retry_playback_history.front().data_size != retry_data_count;
				retry_crc_next = !retry_receive_data;
			}
		}
		//Expecting another 
		else{
			if(fc_lctl_lk.read() && fc_hctl_lk.read() && fc_dword_lk.read() == expected_output.front().dword){
				expected_output.pop_front();
				retry_expect_nop_crc = true;
			}
			else{
				if(fc_dword_lk.read() != retry_playback_history.front().pkt.range(31,0) ||
				!fc_lctl_lk.read() || !fc_hctl_lk.read())
				{
					TestbenchError err;
					ostringstream o;
					o << "ERROR: Wrong first command dword during history playback\n" <<
						"Received: " << fc_dword_lk.read().to_string(SC_HEX) << " Expected: " <<
						retry_playback_history.front().pkt.to_string(SC_HEX);
					err.message = o.str();
					throw err;
				}
				retry_receive_data = retry_playback_history.front().data_size;
				retry_data_count = 0;
				PacketCommand cmd = getPacketCommand( retry_playback_history.front().pkt);
				retry_second_dword_next = !isDwordPacket(retry_playback_history.front().pkt,cmd);
				retry_crc_next = !retry_receive_data;
			}
		}
	}
 
	//Start ignoring output when fc_disconnect_lk is true and output is read
	if(lk_consume_fc.read()){
		retry_disconnect = fc_disconnect_lk.read();
		//If there is no data in playback history, sequence is over
		retry_sequence = !retry_playback_history.empty();
	}
}
 
void flow_control_l2_tb::start_retry_sequence(){
	retry_sequence = true;
	retry_disconnect = false;
	retry_second_dword_next = false;
	retry_crc_next = false;
	retry_receive_data = false;
 
	//Tag the nops in the delay loop so that they're not sent
	for(int n = nop_counter_delay - 1; n != 0; n--)
		nop_delay[n].nop_received = false;
 
	//Reset the next_node_ack_value to the last sent value
	next_node_ack_value = cd_nop_ack_value_fc.read();
	next_node_ack_value_pending = cd_nop_ack_value_fc.read();
 
	//Reset buffer counts
	for(int n = 0; n < 3; n++){
		next_node_buffers_advertised[n] = 0;
		next_node_databuffers_advertised[n] = 0;
	}
 
 
	//history might get modified while playback so we take a snapshot for the playback
	retry_playback_history = history;
 
	cout << "First packet in history at retry begin : " << history.front().pkt.to_string(SC_HEX) <<
		" ID: " << history.front().nop_ack_value << endl;
 
	//We enter retry sequence when receiving a disconnect nop, expect the following crc
	retry_expect_nop_crc = true;
	crc1 = 0xFFFFFFFF;
	calculate_crc1(fc_dword_lk.read(),true,true);
	OutputDword o;
	o.dword = ~crc1; o.lctl = true; o.hctl = false;
	crc1 = 0xFFFFFFFF;
	expected_output.push_back(o);
}
 
void flow_control_l2_tb::manage_ack_buffer_count(){
	/////////////////////////////////////////////
	//Calculate Ack value and update buffer count
	/////////////////////////////////////////////
	if(lk_consume_fc.read()){
		///Ignore the second dword of a quad word command packet
		if(ignore_next_dword_for_ack){
			ignore_next_dword_for_ack = false;
		}
		///Check for dwords that are command packets (both CTL asserted)
		else if(fc_lctl_lk.read() && fc_hctl_lk.read()){
			//Set the last ack value (value pending) only when another command packet is sent
			//Meaning that it has been completely sent
			next_node_ack_value = next_node_ack_value_pending;
 
			sc_bv<64> pkt = fc_dword_lk.read();
			PacketCommand cmd = getPacketCommand(pkt.range(6,0));
			bool dword = isDwordPacket(pkt,cmd);
			VirtualChannel vc = getVirtualChannel(pkt,cmd);
			bool has_data = hasDataAssociated(cmd);
 
			///Increment the ack value
			if(csr_retry.read() && 
				fc_dword_lk.read().range(5,0) != "000000" && //Unless it's a nop
				fc_dword_lk.read().range(5,0) != "110111" && //A flow control packet
				fc_dword_lk.read().range(5,0) != "111111") next_node_ack_value_pending++;//Or a sync packet
 
			if(vc != VC_NONE){
				ignore_next_dword_for_ack = !dword;
				if(has_data){
					if(next_node_databuffers_advertised[vc] == 0){
						TestbenchError err;
						err.message = "ERROR: Received a packet for a Virtual channel that did not have available buffers" ;
						throw err;
					}
					next_node_databuffers_advertised[vc]--;
				}
				else{
					if(next_node_buffers_advertised[vc] == 0){
						TestbenchError err;
						err.message = "ERROR: Received a packet for a Virtual channel that did not have available buffers";
						throw err;
					}
					next_node_buffers_advertised[vc]--;
				}
			}
		}
	}
}
 
 
void flow_control_l2_tb::send_next_node_nops(){
	//////////////////////
	//Send next node nops
	//////////////////////
 
	//Take care of the delay
	for(int n = nop_counter_delay - 1; n != 0; n--)
		nop_delay[n] = nop_delay[n-1];
 
	//Decide if sending nop
	nop_delay[0].nop_received = rand()% 100 < percent_chance_nop_received &&
		!fc_disconnect_lk.read() && lk_rx_connected.read();
 
	//If sending nop, choose content
	if(nop_delay[0].nop_received){
		//Set the current ack value
		nop_delay[0].nop_ack_value = next_node_ack_value;
 
		//Randomly set how many buffers are freed
		sc_bv<12> nop_information = 0;
 
		nop_information = rand();
 
		//If the randomly set value exceeds the maximum value, saturate it.  Repeat for every
		//kind of buffer (3 vcs for data and for command)
		if(((sc_uint<2>)(sc_bv<2>)nop_information.range(1,0) + next_node_buffers_advertised[VC_POSTED])
				> next_node_buffers_free[VC_POSTED])
			nop_information.range(1,0) = next_node_buffers_free[VC_POSTED] - next_node_buffers_advertised[VC_POSTED];
		if(((sc_uint<2>)(sc_bv<2>)nop_information.range(3,2) + next_node_databuffers_advertised[VC_POSTED])
				> next_node_databuffers_free[VC_POSTED])
			nop_information.range(3,2) = next_node_databuffers_free[VC_POSTED] - next_node_databuffers_advertised[VC_POSTED];
		if(((sc_uint<2>)(sc_bv<2>)nop_information.range(5,4) + next_node_buffers_advertised[VC_RESPONSE])
				> next_node_buffers_free[VC_RESPONSE])
			nop_information.range(5,4) = next_node_buffers_free[VC_RESPONSE] - next_node_buffers_advertised[VC_RESPONSE];
		if(((sc_uint<2>)(sc_bv<2>)nop_information.range(7,6) + next_node_databuffers_advertised[VC_RESPONSE])
				> next_node_databuffers_free[VC_RESPONSE])
			nop_information.range(7,6) = next_node_databuffers_free[VC_RESPONSE] - next_node_databuffers_advertised[VC_RESPONSE];
		if(((sc_uint<2>)(sc_bv<2>)nop_information.range(9,8) + next_node_buffers_advertised[VC_NON_POSTED])
				> next_node_buffers_free[VC_NON_POSTED])
			nop_information.range(9,8) = next_node_buffers_free[VC_NON_POSTED] - next_node_buffers_advertised[VC_NON_POSTED];
		if(((sc_uint<2>)(sc_bv<2>)nop_information.range(11,10) + next_node_databuffers_advertised[VC_NON_POSTED])
				> next_node_databuffers_free[VC_NON_POSTED])
			nop_information.range(11,10) = next_node_databuffers_free[VC_NON_POSTED] - next_node_databuffers_advertised[VC_NON_POSTED];
 
		//Add the advertised value
		next_node_buffers_advertised[VC_POSTED] += (unsigned)(sc_uint<2>)(sc_bv<2>)nop_information.range(1,0);
		next_node_databuffers_advertised[VC_POSTED] += (unsigned)(sc_uint<2>)(sc_bv<2>)nop_information.range(3,2);
		next_node_buffers_advertised[VC_RESPONSE] += (unsigned)(sc_uint<2>)(sc_bv<2>)nop_information.range(5,4);
		next_node_databuffers_advertised[VC_RESPONSE] += (unsigned)(sc_uint<2>)(sc_bv<2>)nop_information.range(7,6);
		next_node_buffers_advertised[VC_NON_POSTED] += (unsigned)(sc_uint<2>)(sc_bv<2>)nop_information.range(9,8);
		next_node_databuffers_advertised[VC_NON_POSTED] += (unsigned)(sc_uint<2>)(sc_bv<2>)nop_information.range(11,10);
 
 
		nop_delay[0].nop_information = nop_information;
	}
}
 
void flow_control_l2_tb::free_buffers(){
	//////////////////////
	//Free some buffers
	//////////////////////
	for(int n = 0; n < 3; n++){
		//Free command packets
 
		//Generate a random free value that has a maximum of 3
		unsigned tmp = rand() % 20;
		unsigned free = tmp;
		if(free > 3) free = 0;
 
		//Add the value to the number of free buffers (and saturated at the maximum)
		if((next_node_buffers_free[n] + free) > next_node_buffers_maximum[n])
			next_node_buffers_free[n] = next_node_buffers_maximum[n];
		else
			next_node_buffers_free[n] += free;
 
		//Free data packets
 
		//Generate a random free value that has a maximum of 3
		tmp = rand() % 20;
		free = tmp;
		if(free > 3) free = 0;
 
		//Add the value to the number of free buffers (and saturated at the maximum)
		if((next_node_databuffers_free[n] + free) > next_node_buffers_maximum[n])
			next_node_databuffers_free[n] = next_node_buffers_maximum[n];
		else
			next_node_databuffers_free[n] += free;
	}
}
 
void flow_control_l2_tb::manage_history(){
	//Start by erasing packet acked in the history
	bool done = false;
 
	if(!resetx.read()){
		history_ack_value = 0;
		history_ignore_nop_crc = false;
		history_crc_next = false;
		history_data_count = 0;
		current_history_entry.data_size = 0;
	}
 
	while(!history.empty() && !done){
		sc_uint<8> current_ack_value = history.front().nop_ack_value;
		int diff = ((int)(cd_nop_ack_value_fc.read() - current_ack_value)+256) % 256;
		if(diff < 128){
			//cout << "Popping history" << endl;
			//cout << "cd_nop_ack_value_fc: " << cd_nop_ack_value_fc.read() << " current_ack_value: " << current_ack_value << endl;
			history.pop_front();
		}
		else done = true;
	}
 
	//Add entries to the history
	if(!retry_sequence && resetx.read()){
		if(lk_consume_fc.read()){
			if(history_ignore_nop_crc){
				history_ignore_nop_crc = false;
			}
			else if(history_second_dword_next){
				history_second_dword_next = false;
				current_history_entry.pkt.range(63,32) = fc_dword_lk.read();
				if(current_history_entry.data_size == 0) history_crc_next = true;
			}
			else if(history_crc_next){
				history_crc_next = false;
				current_history_entry.crc = (int)(sc_uint<32>)fc_dword_lk.read();
				//cout << "History entry pushed in" << endl;
				history.push_back(current_history_entry);
			}
			else if(history_data_count != current_history_entry.data_size){
				//Means we have a nop
				if(fc_lctl_lk.read()){
					history_ignore_nop_crc = true;
				}
				else{
					current_history_entry.data[history_data_count++] = (int)(sc_uint<32>)fc_dword_lk.read();
					if(history_data_count == current_history_entry.data_size)
						history_crc_next = true;
				}
			}
			else{
				if(fc_dword_lk.read().range(5,0) != 0)
				{
					//cout << "HISTORY: Adding packet: " << fc_dword_lk.read().to_string(SC_HEX) << endl;
					sc_bv<64> pkt = fc_dword_lk.read();
					current_history_entry.pkt = pkt;
					PacketCommand cmd = getPacketCommand(pkt.range(5,0));
					history_second_dword_next = !isDwordPacket(pkt,cmd);
					if(hasDataAssociated(cmd)){
						current_history_entry.data_size = (int)getDataLengthm1(pkt) + 1;
					}
					else{
						current_history_entry.data_size = 0;
					}
					history_crc_next = !history_second_dword_next && current_history_entry.data_size == 0;
					history_ack_value = (history_ack_value + 1) % 256;
					current_history_entry.nop_ack_value = history_ack_value;
					history_data_count = 0;
				}
				else{
					history_ignore_nop_crc = true;
				}
			}
		}
	}
}
 
void flow_control_l2_tb::clear_next_node_information(){
	//This is the number of buffers that are free (command and data)
	for(int n = 0; n < 3; n++){
		next_node_buffers_free[n] = next_node_buffers_maximum[n];
		next_node_databuffers_free[3] = next_node_databuffers_maximum[n];
	}
 
	for(int n = 0; n < 3; n++){
		//This is the number of buffers that have been advertised as being free through
		//nops as seen by the next node (does not take into account the nop delay)
		next_node_buffers_advertised[n] = 0;
		next_node_databuffers_advertised[n] = 0;
 
		//This is the number of buffers that have been advertised as being free through
		//nops as seen by the current node (takes into account the nop delay)
		buffers_available[n] = 0;
		databuffers_available[n] = 0;
	}
 
	next_node_ack_value = 0;
	next_node_ack_value_pending = 0;
	user_packets.clear();
	user_data[0].clear();user_data[1].clear();user_data[2].clear();
	expected_output.clear();
}
 
void flow_control_l2_tb::generate_random_response(sc_bv<32> &pkt, unsigned &data_size){
	int type = rand() % 2;
	sc_bv<6> command;
	sc_uint<4> data_size_m1;
	switch(type){
	case 0:
		//RdResponse
		data_size_m1 = rand() % 16;
		data_size = (unsigned)data_size_m1 + 1;
		getRandomVector(pkt);
		command = "110000";
		pkt.range(5,0) = command;
		pkt.range(25,22) = data_size_m1;
		break;
	case 1:
		//TargetDone
		data_size = 0;
		getRandomVector(pkt);
		command = "110011";
		pkt.range(5,0) = command;
	}
}
 
void flow_control_l2_tb::generate_random_posted(sc_bv<64> &pkt, unsigned &data_size){
	int type = rand() % 3;
	sc_bv<6> command;
	sc_uint<4> data_size_m1;
	switch(type){
	case 0:
		//Posted write
		data_size_m1 = rand() % 16;
		data_size = (unsigned)data_size_m1 + 1;
		getRandomVector(pkt);
		command = "101";
		pkt.range(5,3) = command;
		pkt.range(25,22) = data_size_m1;
 
		//Never create a chain packet.  If it is desired to test
		//chains, they must be explicitely generated for the purpose of
		//that test
		pkt[19] = false;
		break;
	case 1:
		//Broadcast
		data_size = 0;
		getRandomVector(pkt);
		command = "111010";
		pkt.range(5,0) = command;
		break;
	case 2:
		//Fence
		data_size = 0;
		getRandomVector(pkt);
		pkt.range(63,32) = 0;
		command = "111100";
		pkt.range(5,0) = command;
	}
}
 
void flow_control_l2_tb::generate_random_nposted(sc_bv<64> &pkt, unsigned &data_size){
	int type = rand() % 5;
 
	bool dword_read = true;
	sc_uint<4> data_size_m1 = rand() % 16;
	data_size = (unsigned)data_size_m1 + 1;
	switch(type){
	case 0:
		{
			//NPosted write
			getRandomVector(pkt);
			sc_bv<3> command = "001";
			pkt.range(5,3) = command;
			pkt.range(25,22) = data_size_m1;
		}
		break;
	case 1:
		//Read Byte
		dword_read = false;
	case 2:
		{
			//Read Dword
			getRandomVector(pkt);
			sc_bv<2> command = "01";
			pkt.range(5,4) = command;
			pkt[2] = dword_read;
			pkt.range(25,22) = data_size_m1;
			data_size = 0;
		}
		break;
	case 3:
		{
			//Atomic
			getRandomVector(pkt);
			sc_bv<6> command = "111101";
			pkt.range(5,0) = command;
			pkt.range(25,22) = data_size_m1;
		}
		break;
	case 4:
		{
			//Flush
			data_size = 0;
			getRandomVector(pkt);
			pkt.range(63,32) = 0;
			sc_bv<6> command = "000010";
			pkt.range(5,0) = command;
		}
	}
}
 
VirtualChannel flow_control_l2_tb::generate_random_packet(sc_bv<64> &pkt, unsigned &data_size){
 
	int vc = rand() % 3;
	switch(vc){
	case VC_POSTED:
		generate_random_posted(pkt,data_size);
		break;
	case VC_NON_POSTED:
		generate_random_nposted(pkt,data_size);
		break;
	default:
		sc_bv<32> r_pkt;
		generate_random_response(r_pkt,data_size);
		pkt = 0;
		pkt.range(31,0) = r_pkt;
	}
	return vc;	
}
 
 
void flow_control_l2_tb::getRandomVector(sc_bv<32> &vector){
	vector.range(14,0) = sc_uint<15>(rand());
	vector.range(29,15) = sc_uint<15>(rand());
	vector.range(31,30) = sc_uint<2>(rand());
}
 
void flow_control_l2_tb::getRandomVector(sc_bv<64> &vector){
	for(int n = 0; n < 4; n++)
		vector.range(15*(n+1) - 1,15*n) = sc_uint<15>(rand());
	vector.range(63,60) = sc_uint<4>(rand());
}
 
void flow_control_l2_tb::add_ui_data_packet(unsigned datalength,
									   VirtualChannel ui_vc,
									   sc_bv<64> &associated_control_packet){
	PacketData data;
	data.associated_control_pkt = associated_control_packet;
	data.size = datalength;
	for(unsigned n = 0; n < datalength; n++){
		data.dwords[n] = 
			(rand() & 0x7FFF) |  ((rand() & 0x7FFF ) << 15) | ((rand() & 0x3 ) << 30);
	}
	//cout << "ADDED UI DATA ENTRY: " << data.associated_control_pkt.to_string(SC_HEX)
	//	<< " Datalength: " << data.size << endl;
	user_data[ui_vc].push_back(data);
}
 
void flow_control_l2_tb::update_databuffer_entry(unsigned datalength,
										VirtualChannel ui_vc,
										sc_uint<4> &databuffer_address)
{
	databuffer_data.vc = ui_vc;
	databuffer_data.size = datalength;
	for(unsigned n = 0; n < datalength; n++){
		databuffer_data.dwords[n] = 
			(rand() & 0x7FFF) |  ((rand() & 0x7FFF ) << 15) | ((rand() & 0x3 ) << 30);
	}
	databuffer_data.address = databuffer_address;
}
 
void flow_control_l2_tb::calculate_crc1(sc_bv<32> dword,bool lctl, bool hctl){
	sc_bv<34> data;
	data[33] = hctl;
	data.range(32,17) = dword.range(31,16);
	data[16] = lctl;
	data.range(15,0) = dword.range(15,0);
	calculate_crc(crc1,data);
}
 
void flow_control_l2_tb::calculate_crc2(sc_bv<32> dword,bool lctl, bool hctl){
	sc_bv<34> data;
	data[33] = hctl;
	data.range(32,17) = dword.range(31,16);
	data[16] = lctl;
	data.range(15,0) = dword.range(15,0);
	calculate_crc(crc2,data);
}
 
void flow_control_l2_tb::calculate_crc(unsigned &crc,sc_bv<34> &data){
	static unsigned poly = 0x04C11DB7;
 
	for(int i = 0; i < 34; i++){
		/* xor highest bit w/ message: */
		unsigned tmp = ((crc >> 31) & 1) ^ ( ((sc_bit)data[i]) ? 1 : 0);
 
		/* substract poly if greater: */
		crc = (tmp) ? (crc << 1) ^ poly : ((crc << 1) | tmp);
	}
}
 
ostream &operator<<(ostream &out,const flow_control_l2_tb::FlowControlStatus fc_status){
	switch(fc_status){
		case flow_control_l2_tb::FC_SENDING_CSR:
			out << "FC_SENDING_CSR";
			break;
		case flow_control_l2_tb::FC_SENDING_EH:
			out << "FC_SENDING_EH";
			break;
		case flow_control_l2_tb::FC_SENDING_FWD:
			out << "FC_SENDING_FWD";
			break;
		case flow_control_l2_tb::FC_SENDING_UI:
			out << "FC_SENDING_UI";
			break;
		case flow_control_l2_tb::FC_NONE:
			out << "FC_NONE";
			break;
		default:
			out << "Invalid FlowControlStatus value";
	}
	return out;
}
 
void flow_control_l2_tb::add_expected_crc_for_packet(PacketData &data){
	unsigned tmp;
	tmp = crc2;
	crc2 = 0xFFFFFFFF;
 
	calculate_crc2(data.associated_control_pkt.range(31,0),true,true);
	PacketCommand cmd = getPacketCommand(data.associated_control_pkt.range(5,0));
	if(!isDwordPacket(data.associated_control_pkt,cmd))
		calculate_crc2(data.associated_control_pkt.range(63,32),true,true);
 
	for(int n = 0; n < data.size; n++){
		calculate_crc2(data.dwords[n],false,false);
	}
 
	OutputDword o;
	o.dword = ~crc2;
	o.lctl = false;
	o.hctl = true;
	expected_output.push_front(o);
 
	crc2 = tmp;
}
 
 
 
 

Go to most recent revision | Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.