| /* | |
| * 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 <prjxray/xilinx/configuration_packet.h> | |
| #include <iomanip> | |
| #include <iostream> | |
| #include <ostream> | |
| #include <prjxray/bit_ops.h> | |
| namespace prjxray { | |
| namespace xilinx { | |
| template <> | |
| std::pair<absl::Span<uint32_t>, | |
| absl::optional<ConfigurationPacket<Spartan6ConfigurationRegister>>> | |
| ConfigurationPacket<Spartan6ConfigurationRegister>::InitWithWords( | |
| absl::Span<uint32_t> words, | |
| const ConfigurationPacket<Spartan6ConfigurationRegister>* 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<Opcode>( | |
| bit_field_get(words[0], 12, 11)); | |
| ConfigurationRegister address = | |
| static_cast<ConfigurationRegister>( | |
| 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<ConfigurationPacket> packet; | |
| Opcode opcode = static_cast<Opcode>( | |
| bit_field_get(words[0], 12, 11)); | |
| ConfigurationRegister address = | |
| static_cast<ConfigurationRegister>( | |
| 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::Span<uint32_t>, | |
| absl::optional<ConfigurationPacket<Series7ConfigurationRegister>>> | |
| ConfigurationPacket<Series7ConfigurationRegister>::InitWithWords( | |
| absl::Span<uint32_t> words, | |
| const ConfigurationPacket<Series7ConfigurationRegister>* 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<Opcode>( | |
| bit_field_get(words[0], 28, 27)); | |
| ConfigurationRegister address = | |
| static_cast<ConfigurationRegister>( | |
| 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<ConfigurationPacket> packet; | |
| Opcode opcode = static_cast<Opcode>( | |
| 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 <class ConfigRegType> | |
| std::ostream& operator<<(std::ostream& o, | |
| const ConfigurationPacket<ConfigRegType>& packet) { | |
| if (packet.header_type() == 0x0) { | |
| return o << "[Zero-pad]" << std::endl; | |
| } | |
| switch (packet.opcode()) { | |
| case ConfigurationPacket<ConfigRegType>::Opcode::NOP: | |
| o << "[NOP]" << std::endl; | |
| break; | |
| case ConfigurationPacket<ConfigRegType>::Opcode::Read: | |
| o << "[Read Type="; | |
| o << packet.header_type(); | |
| o << " Address="; | |
| o << std::setw(2) << std::hex; | |
| o << static_cast<int>(packet.address()); | |
| o << " Length="; | |
| o << std::setw(10) << std::dec << packet.data().size(); | |
| o << " Reg=\"" << packet.address() << "\""; | |
| o << "]" << std::endl; | |
| break; | |
| case ConfigurationPacket<ConfigRegType>::Opcode::Write: | |
| o << "[Write Type="; | |
| o << packet.header_type(); | |
| o << " Address="; | |
| o << std::setw(2) << std::hex; | |
| o << static_cast<int>(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<Spartan6ConfigurationRegister>&); | |
| template std::ostream& operator<<( | |
| std::ostream&, | |
| const ConfigurationPacket<Series7ConfigurationRegister>&); | |
| } // namespace xilinx | |
| } // namespace prjxray |