mirror of https://github.com/openXC7/prjxray.git
230 lines
7.0 KiB
C++
230 lines
7.0 KiB
C++
/*
|
|
* 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
|