/* * Copyright (C) 2017-2020 The Project X-Ray Authors. * * Use of this source code is governed by a ISC-style * license that can be found in the LICENSE file or at * https://opensource.org/licenses/ISC * * SPDX-License-Identifier: ISC */ #include #include #include #include #include namespace prjxray { namespace xilinx { template <> std::pair, absl::optional>> ConfigurationPacket::InitWithWords( absl::Span words, const ConfigurationPacket* previous_packet) { using ConfigurationRegister = Spartan6ConfigurationRegister; // Need at least one 32-bit word to have a valid packet header. if (words.size() < 1) return {words, {}}; uint32_t header_type = bit_field_get(words[0], 15, 13); switch (header_type) { case NONE: // Type 0 is emitted at the end of a configuration row // when BITSTREAM.GENERAL.DEBUGBITSTREAM is set to YES. // These seem to be padding that are interepreted as // NOPs. Since Type 0 packets don't exist according to // UG470 and they seem to be zero-filled, just consume // the bytes without generating a packet. return {words.subspan(1), {{header_type, Opcode::NOP, ConfigurationRegister::CRC, {}}}}; case TYPE1: { Opcode opcode = static_cast( bit_field_get(words[0], 12, 11)); ConfigurationRegister address = static_cast( bit_field_get(words[0], 10, 5)); uint32_t data_word_count = bit_field_get(words[0], 4, 0); // If the full packet has not been received, return as // though no valid packet was found. if (data_word_count > words.size() - 1) { return {words, {}}; } return {words.subspan(data_word_count + 1), {{header_type, opcode, address, words.subspan(1, data_word_count)}}}; } case TYPE2: { absl::optional packet; Opcode opcode = static_cast( bit_field_get(words[0], 12, 11)); ConfigurationRegister address = static_cast( bit_field_get(words[0], 10, 5)); // Type 2 packets according to UG380 consist of // a header word followed by 2 WCD (Word Count Data) // words uint32_t data_word_count = (words[1] << 16) | words[2]; // If the full packet has not been received, return as // though no valid packet was found. if (data_word_count > words.size() - 1) { return {words, {}}; } // Create a packet that contains as many data words // as specified in the WCD packets, but omit them // in the configuration packet along with the header // FIXME Figure out why we need the extra 2 words packet = ConfigurationPacket( header_type, opcode, address, words.subspan(3, data_word_count + 2)); return {words.subspan(data_word_count + 3), packet}; } default: return {{}, {}}; } } template <> std::pair, absl::optional>> ConfigurationPacket::InitWithWords( absl::Span words, const ConfigurationPacket* previous_packet) { using ConfigurationRegister = Series7ConfigurationRegister; // Need at least one 32-bit word to have a valid packet header. if (words.size() < 1) return {words, {}}; uint32_t header_type = bit_field_get(words[0], 31, 29); switch (header_type) { case NONE: // Type 0 is emitted at the end of a configuration row // when BITSTREAM.GENERAL.DEBUGBITSTREAM is set to YES. // These seem to be padding that are interepreted as // NOPs. Since Type 0 packets don't exist according to // UG470 and they seem to be zero-filled, just consume // the bytes without generating a packet. return {words.subspan(1), {{header_type, Opcode::NOP, ConfigurationRegister::CRC, {}}}}; case TYPE1: { Opcode opcode = static_cast( bit_field_get(words[0], 28, 27)); ConfigurationRegister address = static_cast( bit_field_get(words[0], 26, 13)); uint32_t data_word_count = bit_field_get(words[0], 10, 0); // If the full packet has not been received, return as // though no valid packet was found. if (data_word_count > words.size() - 1) { return {words, {}}; } return {words.subspan(data_word_count + 1), {{header_type, opcode, address, words.subspan(1, data_word_count)}}}; } case TYPE2: { absl::optional packet; Opcode opcode = static_cast( bit_field_get(words[0], 28, 27)); uint32_t data_word_count = bit_field_get(words[0], 26, 0); // If the full packet has not been received, return as // though no valid packet was found. if (data_word_count > words.size() - 1) { return {words, {}}; } if (previous_packet) { packet = ConfigurationPacket( header_type, opcode, previous_packet->address(), words.subspan(1, data_word_count)); } return {words.subspan(data_word_count + 1), packet}; } default: return {{}, {}}; } } template std::ostream& operator<<(std::ostream& o, const ConfigurationPacket& packet) { if (packet.header_type() == 0x0) { return o << "[Zero-pad]" << std::endl; } switch (packet.opcode()) { case ConfigurationPacket::Opcode::NOP: o << "[NOP]" << std::endl; break; case ConfigurationPacket::Opcode::Read: o << "[Read Type="; o << packet.header_type(); o << " Address="; o << std::setw(2) << std::hex; o << static_cast(packet.address()); o << " Length="; o << std::setw(10) << std::dec << packet.data().size(); o << " Reg=\"" << packet.address() << "\""; o << "]" << std::endl; break; case ConfigurationPacket::Opcode::Write: o << "[Write Type="; o << packet.header_type(); o << " Address="; o << std::setw(2) << std::hex; o << static_cast(packet.address()); o << " Length="; o << std::setw(10) << std::dec << packet.data().size(); o << " Reg=\"" << packet.address() << "\""; o << "]" << std::endl; o << "Data in hex:" << std::endl; for (size_t ii = 0; ii < packet.data().size(); ++ii) { o << std::setw(8) << std::hex; o << packet.data()[ii] << " "; if ((ii + 1) % 4 == 0) { o << std::endl; } } if (packet.data().size() % 4 != 0) { o << std::endl; } break; default: o << "[Invalid Opcode]" << std::endl; } return o; } template std::ostream& operator<<( std::ostream&, const ConfigurationPacket&); template std::ostream& operator<<( std::ostream&, const ConfigurationPacket&); } // namespace xilinx } // namespace prjxray