|
| 1 | +#include "architecture-xc7.h" |
| 2 | + |
| 3 | +#include <cstdint> |
| 4 | +#include <optional> |
| 5 | +#include <ostream> |
| 6 | + |
| 7 | +#include "absl/types/span.h" |
| 8 | +#include "fpga/xilinx/bit-ops.h" |
| 9 | +#include "fpga/xilinx/configuration-packet.h" |
| 10 | + |
| 11 | +namespace fpga { |
| 12 | +namespace xilinx { |
| 13 | +namespace xc7 { |
| 14 | +std::ostream &operator<<(std::ostream &o, const ConfigurationRegister &value) { |
| 15 | + switch (value) { |
| 16 | + case ConfigurationRegister::kCRC: return o << "CRC"; |
| 17 | + case ConfigurationRegister::kFAR: return o << "Frame Address"; |
| 18 | + case ConfigurationRegister::kFDRI: return o << "Frame Data Input"; |
| 19 | + case ConfigurationRegister::kFDRO: return o << "Frame Data Output"; |
| 20 | + case ConfigurationRegister::kCMD: return o << "Command"; |
| 21 | + case ConfigurationRegister::kCTL0: return o << "Control 0"; |
| 22 | + case ConfigurationRegister::kMASK: return o << "Mask for CTL0 and CTL1"; |
| 23 | + case ConfigurationRegister::kSTAT: return o << "Status"; |
| 24 | + case ConfigurationRegister::kLOUT: return o << "Legacy Output"; |
| 25 | + case ConfigurationRegister::kCOR0: return o << "Configuration Option 0"; |
| 26 | + case ConfigurationRegister::kMFWR: return o << "Multiple Frame Write"; |
| 27 | + case ConfigurationRegister::kCBC: return o << "Initial CBC Value"; |
| 28 | + case ConfigurationRegister::kIDCODE: return o << "Device ID"; |
| 29 | + case ConfigurationRegister::kAXSS: return o << "User Access"; |
| 30 | + case ConfigurationRegister::kCOR1: return o << "Configuration Option 1"; |
| 31 | + case ConfigurationRegister::kWBSTAR: return o << "Warm Boot Start Address"; |
| 32 | + case ConfigurationRegister::kTIMER: return o << "Watchdog Timer"; |
| 33 | + case ConfigurationRegister::kBOOTSTS: return o << "Boot History Status"; |
| 34 | + case ConfigurationRegister::kCTL1: return o << "Control 1"; |
| 35 | + case ConfigurationRegister::kBSPI: |
| 36 | + return o << "BPI/SPI Configuration Options"; |
| 37 | + default: return o << "Unknown"; |
| 38 | + } |
| 39 | +} |
| 40 | + |
| 41 | +ConfigurationPacket::ParseResult ConfigurationPacket::InitWithWordsImpl( |
| 42 | + absl::Span<uint32_t> words, const ConfigurationPacket *previous_packet) { |
| 43 | + using ConfigurationRegister = ConfigurationRegister; |
| 44 | + // Need at least one 32-bit word to have a valid packet header. |
| 45 | + if (words.empty() < 1) { |
| 46 | + return {words, {}}; |
| 47 | + } |
| 48 | + const ConfigurationPacketType header_type = |
| 49 | + static_cast<ConfigurationPacketType>(bit_field_get(words[0], 31, 29)); |
| 50 | + switch (header_type) { |
| 51 | + case ConfigurationPacketType::kNONE: |
| 52 | + // Type 0 is emitted at the end of a configuration row |
| 53 | + // when BITSTREAM.GENERAL.DEBUGBITSTREAM is set to YES. |
| 54 | + // These seem to be padding that are interepreted as |
| 55 | + // NOPs. Since Type 0 packets don't exist according to |
| 56 | + // UG470 and they seem to be zero-filled, just consume |
| 57 | + // the bytes without generating a packet. |
| 58 | + return {words.subspan(1), |
| 59 | + {{static_cast<uint32_t>(header_type), |
| 60 | + Opcode::kNOP, |
| 61 | + ConfigurationRegister::kCRC, |
| 62 | + {}}}}; |
| 63 | + case ConfigurationPacketType::kTYPE1: { |
| 64 | + const Opcode opcode = static_cast<Opcode>(bit_field_get(words[0], 28, 27)); |
| 65 | + const ConfigurationRegister address = |
| 66 | + static_cast<ConfigurationRegister>(bit_field_get(words[0], 26, 13)); |
| 67 | + const uint32_t data_word_count = bit_field_get(words[0], 10, 0); |
| 68 | + |
| 69 | + // If the full packet has not been received, return as |
| 70 | + // though no valid packet was found. |
| 71 | + if (data_word_count > words.size() - 1) { |
| 72 | + return {words, {}}; |
| 73 | + } |
| 74 | + |
| 75 | + return {words.subspan(data_word_count + 1), |
| 76 | + {{static_cast<uint32_t>(header_type), opcode, address, |
| 77 | + words.subspan(1, data_word_count)}}}; |
| 78 | + } |
| 79 | + case ConfigurationPacketType::kTYPE2: { |
| 80 | + std::optional<ConfigurationPacket> packet; |
| 81 | + const Opcode opcode = static_cast<Opcode>(bit_field_get(words[0], 28, 27)); |
| 82 | + const uint32_t data_word_count = bit_field_get(words[0], 26, 0); |
| 83 | + |
| 84 | + // If the full packet has not been received, return as |
| 85 | + // though no valid packet was found. |
| 86 | + if (data_word_count > words.size() - 1) { |
| 87 | + return {words, {}}; |
| 88 | + } |
| 89 | + |
| 90 | + if (previous_packet) { |
| 91 | + packet = ConfigurationPacket(static_cast<uint32_t>(header_type), opcode, |
| 92 | + previous_packet->address(), |
| 93 | + words.subspan(1, data_word_count)); |
| 94 | + } |
| 95 | + |
| 96 | + return {words.subspan(data_word_count + 1), packet}; |
| 97 | + } |
| 98 | + default: return {{}, {}}; |
| 99 | + } |
| 100 | +} |
| 101 | +} // namespace xc7 |
| 102 | +} // namespace xilinx |
| 103 | +} // namespace fpga |
0 commit comments