The detailed of the solution to ex4 are embedded in the file. Look for the lines starting with //ex 4 for the details <' struct packet_s { // control the error creation has_errors: bool; keep has_errors => !good_addr or !good_parity; keep !has_errors => good_addr and good_parity; good_addr: bool; good_parity: bool; // the address field of the packet %addr: uint (bits: 2); // set the address to respond to address errors keep good_addr => addr in [0..2]; keep !good_addr => addr == 3; // the length field of the packet %length: uint (bits: 6); // the payload field of the packet // it's size is controlled be the length field %data[length]: list of byte; // the parity field %parity: byte; // set the parity to respond to parity errors keep good_parity => parity == parity_val; keep !good_parity => parity != parity_val; // set a variable with the true parity value. parity_val: byte; keep parity_val == calc_parity(addr, length, data); // compute the parity calc_parity(addr: uint (bits: 2), length: uint (bits: 6), data: list of byte): byte is { result[1:0] = addr; result[7:2] = length; for each (item) in data { result ^= item; }; }; }; // define the env unit unit env_u { // attach the clock of the due to an e event event clock is fall('clock') @sim; }; // create an instance of env under sys and set direct it // to the verilog entity extend sys { env: env_u is instance; keep env.hdl_path() == "top"; run() is also { set_config(run, tick_max, 1m); }; }; unit channel_u { id: uint; }; extend env_u { channels[3]: list of channel_u is instance; keep for each in channels { it.id == index; }; // Define a delay between two consecutive packets packet_delay: uint; keep soft packet_delay < 100; // Define the total number of packets to generate num_of_packets: uint; // Use a temporary field to hold the generated packet. // This will enable us to constraint it from the test. !curr_packet: packet_s; // The injector method. This is the only place in the // code that knows the input protocol. inject_packet(packet: packet_s)@clock is { var items: list of byte; items = pack(packing.low, packet); var parity: byte = items.pop(); gen packet_delay; wait [packet_delay] * cycle; 'packet_valid' = 1; for each in items { sync true('suspend_data_in' == 0); 'data' = it; wait cycle; }; sync true('suspend_data_in' == 0); 'packet_valid' = 0; 'data' = parity; out(sys.time,"] Injected packet: ",packet); //ex 4: Add the sent packet to the scoreboard of the out // going channel if packet.good_addr then { channels[packet.addr].sb.add(packet); }; wait cycle; }; // The main loop that generates packets and send them // to the DUT. inject_packets()@clock is { for i from 1 to num_of_packets { // generate the packet. Note: all specific costraints // will be done from outside by each specific test gen curr_packet; inject_packet(curr_packet); }; // In this simple environment we control the test end from // the input. We assume all packets are processed and out // before 128 cycles has passed from the end of the last // injected packet wait [128] * cycle; stop_run(); }; // create a thread that injects the packet as soon as the // test starts run() is also { start inject_packets(); }; }; extend channel_u { // point to the env that contains this channel // this is going to be used to access the env clock env: env_u; keep env == get_enclosing_unit(env_u); // create a scoreboard for each channel sb: channel_sb_u is instance; keep sb.id == id; // Delay of more than 30 clock cycles is invalid according to the // output protocol, so we build a mechanism to support it for // "bad machine" tests. legal_delay: bool; keep soft legal_delay == TRUE; // Define the delay of the response to the outgoing packet packet_delay: uint; keep legal_delay => packet_delay < 30; // This is the collector method. It is the only one // that knows the output protocol collect_packet()@env.clock is { var bytes: list of byte; var packet: packet_s; sync true('vld_chan_(id)' == 1); gen packet_delay; wait [packet_delay] * cycle; 'read_enb_(id)' = 1; while 'vld_chan_(id)' == 1 { wait cycle; sync true('read_enb_(id)' == 1); bytes.add('channel(id)'); }; 'read_enb_(id)' = 0; unpack(packing.low, bytes, packet); out(sys.time, "] Chan: ", id, " Collected packet: ",packet); //ex 4: call the scoreboard to check and remove the packet from // it's list sb.remove(packet); }; // Collect packets forever collect_packets()@env.clock is { while TRUE { collect_packet(); }; }; run() is also { start collect_packets(); }; }; // ex 4: the scoreboard definition // the scoreboard of the packets. // It assumes output packets come in order. unit channel_sb_u { // the corresponding channel id for error messages id: uint; // the stack of the packets that needs to output !expected_packets: list of packet_s; add(packet: packet_s) is { expected_packets.add(packet); }; // the packet is removed from the head of the list and checked // to be the same as the outgoing packet. remove(packet: packet_s) is { check that expected_packets.size() > 0 else dut_error("Received packet: ", packet, " but scoreboard is empty"); var expected: packet_s = expected_packets.pop0(); var diff: list of string = deep_compare_physical(packet, expected, 10); check that diff.size() == 0 else dut_error("Received packet: ", packet, " Differ from expected: ", expected, " By: \n",str_join(diff,"\n")); out("Chan ", id, " Checked and removed packet: ", packet); }; // This method is called for each existing struct in specman when // the test ends. In the scoreboard we check that there are no packets // left. That is: every packet that got in, also got out. check() is also { check that expected_packets.size() == 0 else dut_error("Not all packets sent to channel ", id, " has been recieved ", expected_packets); }; }; '>