mirror of
https://github.com/captain-amygdala/pistorm.git
synced 2026-01-11 23:43:13 +00:00
Initial work on FPGA I2C programmer
This commit is contained in:
parent
6109a894ee
commit
a0f6204c2a
3
i2c_updater/.gitignore
vendored
Normal file
3
i2c_updater/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
*.bit
|
||||
build
|
||||
.vscode
|
||||
7
i2c_updater/pi-i2c/CMakeLists.txt
Normal file
7
i2c_updater/pi-i2c/CMakeLists.txt
Normal file
@ -0,0 +1,7 @@
|
||||
cmake_minimum_required(VERSION 3.15.2)
|
||||
project(pistorm_i2c_updater C CXX)
|
||||
|
||||
file(GLOB i2c_updater_sources src/*.cpp src/*.hpp)
|
||||
add_executable(i2c_updater ${i2c_updater_sources})
|
||||
set_property(TARGET i2c_updater PROPERTY CXX_STANDARD 17)
|
||||
target_compile_options(i2c_updater PRIVATE -Wall)
|
||||
165
i2c_updater/pi-i2c/src/bitstream.cpp
Normal file
165
i2c_updater/pi-i2c/src/bitstream.cpp
Normal file
@ -0,0 +1,165 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "bitstream.hpp"
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include "file.hpp"
|
||||
|
||||
// Based on https://prjtrellis.readthedocs.io/en/latest/architecture/bitstream_format.html ,
|
||||
// Lattice Diamond programmer operation's I2C dump and "ECP5 and ECP5-5G sysCONFIG Usage Guide"
|
||||
|
||||
#define SECTION_ASCII_COMMENTS_BEGIN 0xFF00
|
||||
#define SECTION_PREAMBLE_WORD_1 0xFFFF
|
||||
#define SECTION_PREAMBLE_WORD_2 0xBDB3
|
||||
|
||||
struct tBitstreamCmd {
|
||||
enum class tId: uint8_t {
|
||||
LSC_PROG_CNTRL0 = 0x22,
|
||||
LSC_RESET_CRC = 0x3B,
|
||||
LSC_INIT_ADDRESS = 0x46,
|
||||
ISC_PROGRAM_DONE = 0x5E,
|
||||
LSC_PROG_INCR_RTI = 0x82,
|
||||
LSC_PROG_SED_CRC = 0xA2,
|
||||
LSC_EBR_WRITE = 0xB2,
|
||||
ISC_PROGRAM_USERCODE = 0xC2,
|
||||
ISC_PROGRAM_SECURITY = 0xCE,
|
||||
LSC_VERIFY_ID = 0xE2,
|
||||
LSC_EBR_ADDRESS = 0xF6,
|
||||
ISC_NOOP = 0xFF,
|
||||
};
|
||||
|
||||
tBitstreamCmd::tId m_eId;
|
||||
std::array<uint8_t, 3> m_Operands;
|
||||
std::vector<uint8_t> m_vData;
|
||||
|
||||
tBitstreamCmd(std::ifstream &FileIn);
|
||||
};
|
||||
|
||||
tBitstream::tBitstream(const std::string &FilePath)
|
||||
{
|
||||
std::ifstream FileIn;
|
||||
FileIn.open(FilePath.c_str(), std::ifstream::in | std::ifstream::binary);
|
||||
if(!FileIn.good()) {
|
||||
throw std::runtime_error("Couldn't open file");
|
||||
}
|
||||
|
||||
uint16_t uwSectionId;
|
||||
nFile::readBigEndian(FileIn, uwSectionId);
|
||||
if(uwSectionId == SECTION_ASCII_COMMENTS_BEGIN) {
|
||||
// Read comments
|
||||
std::string Comment;
|
||||
char c;
|
||||
while(!FileIn.eof()) {
|
||||
nFile::readBigEndian(FileIn, c);
|
||||
if(c == '\xFF') {
|
||||
break;
|
||||
}
|
||||
if(c == '\0') {
|
||||
m_vComments.push_back(Comment);
|
||||
Comment.clear();
|
||||
}
|
||||
else {
|
||||
Comment += c;
|
||||
}
|
||||
}
|
||||
|
||||
// Continue reading until we hit preamble
|
||||
nFile::readBigEndian(FileIn, uwSectionId);
|
||||
}
|
||||
|
||||
if(uwSectionId != SECTION_PREAMBLE_WORD_1) {
|
||||
// I want std::format so bad :(
|
||||
std::stringstream ss;
|
||||
ss << "Unexpected bitstream section: " << std::setfill('0') <<
|
||||
std::setw(4) << std::ios::hex << uwSectionId;
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
|
||||
// Read final part of preamble
|
||||
uint16_t uwPreambleEnd;
|
||||
nFile::readBigEndian(FileIn, uwPreambleEnd);
|
||||
if(uwPreambleEnd != SECTION_PREAMBLE_WORD_2) {
|
||||
// I want std::format so bad :(
|
||||
std::stringstream ss;
|
||||
ss << "Unexpected preamble ending" << std::setfill('0') <<
|
||||
std::setw(4) << std::ios::hex << uwPreambleEnd;
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
|
||||
while(!FileIn.eof()) {
|
||||
// Read bitstream commands
|
||||
tBitstreamCmd Cmd(FileIn);
|
||||
if(FileIn.eof()) {
|
||||
// Last read haven't been successfull - no more data
|
||||
break;
|
||||
}
|
||||
|
||||
// Read extra data, if any
|
||||
switch(Cmd.m_eId) {
|
||||
case tBitstreamCmd::tId::ISC_NOOP:
|
||||
case tBitstreamCmd::tId::LSC_RESET_CRC:
|
||||
case tBitstreamCmd::tId::LSC_INIT_ADDRESS:
|
||||
// No additional data
|
||||
break;
|
||||
case tBitstreamCmd::tId::LSC_VERIFY_ID:
|
||||
nFile::readData(FileIn, m_DeviceId.data(), m_DeviceId.size());
|
||||
break;
|
||||
case tBitstreamCmd::tId::LSC_PROG_CNTRL0:
|
||||
nFile::readBigEndian(FileIn, m_ulCtlReg0);
|
||||
break;
|
||||
case tBitstreamCmd::tId::LSC_PROG_INCR_RTI: {
|
||||
// Store program data
|
||||
// bool isCrcVerify = Cmd.m_Operands[0] & 0x80;
|
||||
// bool isCrcAtEnd = !(Cmd.m_Operands[0] & 0x40);
|
||||
// bool isDummyBits = !(Cmd.m_Operands[0] & 0x20);
|
||||
// bool isDummyBytes = !(Cmd.m_Operands[0] & 0x10);
|
||||
auto DummyBytesCount = Cmd.m_Operands[0] & 0xF;
|
||||
uint16_t uwRowCount = (Cmd.m_Operands[1] << 8) | Cmd.m_Operands[2];
|
||||
|
||||
for(auto RowIdx = 0; RowIdx < uwRowCount; ++RowIdx) {
|
||||
// TODO: where to get the size of each data row? From comment?
|
||||
std::vector<uint8_t> vRow;
|
||||
vRow.resize(26);
|
||||
uint16_t uwCrc;
|
||||
nFile::readData(FileIn, vRow.data(), vRow.size());
|
||||
nFile::readBigEndian(FileIn, uwCrc);
|
||||
if(uwCrc == 0) {
|
||||
// I want std::format so bad :(
|
||||
std::stringstream ss;
|
||||
ss << "Empty CRC near pos " << std::hex << FileIn.tellg();
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
m_vProgramData.push_back(vRow);
|
||||
FileIn.seekg(DummyBytesCount, std::ios::cur);
|
||||
}
|
||||
} break;
|
||||
case tBitstreamCmd::tId::ISC_PROGRAM_USERCODE:
|
||||
// Read usercode, skip CRC
|
||||
nFile::readData(FileIn, m_UserCode.data(), m_UserCode.size());
|
||||
FileIn.seekg(2, std::ios::cur);
|
||||
break;
|
||||
case tBitstreamCmd::tId::ISC_PROGRAM_DONE:
|
||||
break;
|
||||
default: {
|
||||
// I want std::format so bad :(
|
||||
std::stringstream ss;
|
||||
ss << "Unhandled bitstream cmd near file pos 0x" << std::hex <<
|
||||
FileIn.tellg() << ": 0x" << std::setw(2) << std::setfill('0') <<
|
||||
int(Cmd.m_eId);
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tBitstreamCmd::tBitstreamCmd(std::ifstream &FileIn)
|
||||
{
|
||||
uint32_t ulCmdRaw;
|
||||
nFile::readBigEndian(FileIn, ulCmdRaw);
|
||||
m_eId = tBitstreamCmd::tId(ulCmdRaw >> 24);
|
||||
m_Operands[0] = (ulCmdRaw >> 16) & 0xFF;
|
||||
m_Operands[1] = (ulCmdRaw >> 8) & 0xFF;
|
||||
m_Operands[2] = (ulCmdRaw >> 0) & 0xFF;
|
||||
}
|
||||
23
i2c_updater/pi-i2c/src/bitstream.hpp
Normal file
23
i2c_updater/pi-i2c/src/bitstream.hpp
Normal file
@ -0,0 +1,23 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef _BITSTREAM_HPP_
|
||||
#define _BITSTREAM_HPP_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <fstream>
|
||||
|
||||
struct tBitstream {
|
||||
tBitstream(const std::string &FilePath);
|
||||
|
||||
std::array<uint8_t, 4> m_DeviceId;
|
||||
uint32_t m_ulCtlReg0;
|
||||
std::array<uint8_t, 4> m_UserCode;
|
||||
std::vector<std::string> m_vComments;
|
||||
std::vector<std::vector<uint8_t>> m_vProgramData;
|
||||
};
|
||||
|
||||
#endif // _BITSTREAM_HPP_
|
||||
52
i2c_updater/pi-i2c/src/endian.hpp
Normal file
52
i2c_updater/pi-i2c/src/endian.hpp
Normal file
@ -0,0 +1,52 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef _ENDIAN_HPP_
|
||||
#define _ENDIAN_HPP_
|
||||
|
||||
#include <cstdint>
|
||||
// #include <bit> // we don't have GCC with C++20 on raspi :(
|
||||
|
||||
namespace nEndian {
|
||||
|
||||
template<typename t_tVal>
|
||||
constexpr t_tVal swapBytes(t_tVal Val) {
|
||||
t_tVal Out = 0;
|
||||
for(size_t i = 0; i < sizeof(Val); ++i) {
|
||||
Out = (Out << 8) | (Val & 0xFF);
|
||||
Val >>= 8;
|
||||
}
|
||||
return Out;
|
||||
}
|
||||
|
||||
template<typename t_tVal>
|
||||
constexpr t_tVal littleToNative(const t_tVal &Val) {
|
||||
// if(std::endian::native == std::endian::big) {
|
||||
// return swapBytes(Val);
|
||||
// }
|
||||
// else {
|
||||
return Val;
|
||||
// }
|
||||
}
|
||||
|
||||
template<typename t_tVal>
|
||||
constexpr auto nativeToLittle = littleToNative<t_tVal>;
|
||||
|
||||
template<typename t_tVal>
|
||||
constexpr t_tVal bigToNative(const t_tVal &Val) {
|
||||
// if(std::endian::native == std::endian::big) {
|
||||
// return Val;
|
||||
// }
|
||||
// else {
|
||||
return swapBytes(Val);
|
||||
// }
|
||||
}
|
||||
|
||||
template<typename t_tVal>
|
||||
constexpr auto nativeToBig = bigToNative<t_tVal>;
|
||||
|
||||
|
||||
} // namespace nEndian
|
||||
|
||||
#endif // _ENDIAN_HPP_
|
||||
35
i2c_updater/pi-i2c/src/file.hpp
Normal file
35
i2c_updater/pi-i2c/src/file.hpp
Normal file
@ -0,0 +1,35 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef _FILE_HPP_
|
||||
#define _FILE_HPP_
|
||||
|
||||
#include <fstream>
|
||||
#include "endian.hpp"
|
||||
|
||||
namespace nFile {
|
||||
|
||||
template<typename t_tStream, typename t_tData>
|
||||
t_tStream readLittleEndian(t_tStream &File, t_tData &Data)
|
||||
{
|
||||
File.read(reinterpret_cast<char*>(&Data), sizeof(Data));
|
||||
Data = nEndian::littleToNative(Data);
|
||||
}
|
||||
|
||||
template<typename t_tStream, typename t_tData>
|
||||
void readBigEndian(t_tStream &File, t_tData &Data)
|
||||
{
|
||||
File.read(reinterpret_cast<char*>(&Data), sizeof(Data));
|
||||
Data = nEndian::bigToNative(Data);
|
||||
}
|
||||
|
||||
template<typename t_tStream, typename t_tData>
|
||||
void readData(t_tStream &File, t_tData *pData, size_t ElementCount)
|
||||
{
|
||||
File.read(reinterpret_cast<char*>(pData), ElementCount * sizeof(*pData));
|
||||
}
|
||||
|
||||
} // namespace nFile
|
||||
|
||||
#endif // _FILE_HPP_
|
||||
49
i2c_updater/pi-i2c/src/i2c.cpp
Normal file
49
i2c_updater/pi-i2c/src/i2c.cpp
Normal file
@ -0,0 +1,49 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "i2c.hpp"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/i2c-dev.h>
|
||||
#include <stdexcept>
|
||||
|
||||
tI2c::tI2c(const std::string &Port)
|
||||
{
|
||||
// Open the I2C bus file handle
|
||||
m_I2cHandle = open(Port.c_str(), O_RDWR);
|
||||
if(m_I2cHandle < 0) {
|
||||
// TODO: check errno to see what went wrong
|
||||
throw std::runtime_error("Can't open the i2c bus\n");
|
||||
}
|
||||
}
|
||||
|
||||
bool tI2c::write(uint8_t ubAddr, const std::vector<uint8_t> &vData) {
|
||||
if(ioctl(m_I2cHandle, I2C_SLAVE, ubAddr) < 0) {
|
||||
// NOTE: check errno to see what went wrong
|
||||
return false;
|
||||
}
|
||||
|
||||
auto BytesWritten = ::write(m_I2cHandle, vData.data(), vData.size());
|
||||
if(BytesWritten != ssize_t(vData.size())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tI2c::read(uint8_t ubAddr, uint8_t *pDest, uint32_t ulReadSize) {
|
||||
if(ioctl(m_I2cHandle, I2C_SLAVE, ubAddr) < 0) {
|
||||
// NOTE: check errno to see what went wrong
|
||||
return false;
|
||||
}
|
||||
|
||||
auto BytesRead = ::read(m_I2cHandle, pDest, ulReadSize);
|
||||
if(BytesRead != ssize_t(ulReadSize)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
28
i2c_updater/pi-i2c/src/i2c.hpp
Normal file
28
i2c_updater/pi-i2c/src/i2c.hpp
Normal file
@ -0,0 +1,28 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef _I2C_HPP_
|
||||
#define _I2C_HPP_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class tI2c {
|
||||
public:
|
||||
tI2c(const std::string &Port);
|
||||
|
||||
bool write(uint8_t ubAddr, const std::vector<uint8_t> &vData);
|
||||
|
||||
bool read(uint8_t ubAddr, uint8_t *pDest, uint32_t ulReadSize);
|
||||
|
||||
template <typename t_tContainer>
|
||||
bool read(uint8_t ubAddr, t_tContainer &Cont) {
|
||||
return read(ubAddr, Cont.data(), Cont.size());
|
||||
}
|
||||
|
||||
private:
|
||||
int m_I2cHandle;
|
||||
};
|
||||
|
||||
#endif // _I2C_HPP_
|
||||
173
i2c_updater/pi-i2c/src/main.cpp
Normal file
173
i2c_updater/pi-i2c/src/main.cpp
Normal file
@ -0,0 +1,173 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include <thread>
|
||||
#include "bitstream.hpp"
|
||||
#include "i2c.hpp"
|
||||
#include "endian.hpp"
|
||||
|
||||
#define STATUS_BIT_BUSY (1 << 12)
|
||||
#define STATUS_BIT_ERROR (1 << 13)
|
||||
|
||||
static bool waitForNotBusy(tI2c &I2c, uint8_t ubFpgaAddr)
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
uint32_t ulStatus;
|
||||
do {
|
||||
std::this_thread::sleep_for(10ms);
|
||||
I2c.write(ubFpgaAddr, {0x3C, 0x00, 0x00 , 0x00});
|
||||
bool isRead = I2c.read(
|
||||
ubFpgaAddr, reinterpret_cast<uint8_t*>(&ulStatus), sizeof(ulStatus)
|
||||
);
|
||||
if(!isRead) {
|
||||
return false;
|
||||
}
|
||||
ulStatus = nEndian::bigToNative(ulStatus);
|
||||
if(ulStatus & STATUS_BIT_ERROR) {
|
||||
return false;
|
||||
}
|
||||
} while(ulStatus & STATUS_BIT_BUSY);
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint8_t reverseByte(uint8_t ubData) {
|
||||
// https://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith64BitsDiv
|
||||
ubData = (ubData * 0x0202020202ULL & 0x010884422010ULL) % 1023;
|
||||
return ubData;
|
||||
}
|
||||
|
||||
int main(int lArgCount, const char *pArgs[])
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
if(lArgCount < 4) {
|
||||
printf("Usage:\n\t%s i2cPort i2cSlaveAddrHex /path/to/cfg.bit\n", pArgs[0]);
|
||||
printf("e.g.:\n\t%s /dev/i2c-1 5a file.bit\n", pArgs[0]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
std::optional<tBitstream> Bitstream;
|
||||
try {
|
||||
Bitstream = tBitstream(pArgs[3]);
|
||||
}
|
||||
catch(const std::exception &Exc) {
|
||||
printf(
|
||||
"ERR: bitstream '%s' read fail: '%s'\n", pArgs[3], Exc.what()
|
||||
);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Display some info about bitstream
|
||||
printf("Successfully read bitstream:\n");
|
||||
for(const auto &Comment: Bitstream->m_vComments) {
|
||||
printf("\t%s\n", Comment.c_str());
|
||||
}
|
||||
|
||||
// Read the I2C address of the FPGA
|
||||
auto FpgaAddr = std::stoi(pArgs[2], 0, 16);
|
||||
|
||||
// Initialize I2C port
|
||||
std::optional<tI2c> I2c;
|
||||
try {
|
||||
I2c = tI2c(pArgs[1]);
|
||||
}
|
||||
catch(const std::exception &Exc) {
|
||||
printf(
|
||||
"ERR: i2c port '%s' init fail: '%s'\n", pArgs[1], Exc.what()
|
||||
);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
printf("Resetting FPGA...\n");
|
||||
// TODO: CRESET:=0
|
||||
std::this_thread::sleep_for(1s);
|
||||
I2c->write(FpgaAddr, {0xA4, 0xC6, 0xF4, 0x8A});
|
||||
// TODO: CRESET:=1
|
||||
std::this_thread::sleep_for(10ms);
|
||||
|
||||
// 1. Read ID (E0)
|
||||
printf("Checking device id...\n");
|
||||
I2c->write(FpgaAddr, {0xE0, 0x00, 0x00, 0x00});
|
||||
std::array<uint8_t, 4> DevId;
|
||||
bool isRead = I2c->read(FpgaAddr, DevId);
|
||||
if(!isRead) {
|
||||
printf("ERR: Can't read data from I2C device\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if(DevId != Bitstream->m_DeviceId) {
|
||||
printf(
|
||||
"ERR: DevId mismatch! Dev: %02X %02X %02X %02X, "
|
||||
".bit: %02X, %02X, %02X, %02X\n",
|
||||
DevId[0], DevId[1], DevId[2], DevId[3],
|
||||
Bitstream->m_DeviceId[0], Bitstream->m_DeviceId[1],
|
||||
Bitstream->m_DeviceId[2], Bitstream->m_DeviceId[3]
|
||||
);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// 2. Enable configuration interface (C6)
|
||||
printf("Initiating programming...\n");
|
||||
I2c->write(FpgaAddr, {0xC6, 0x00, 0x00, 0x00});
|
||||
std::this_thread::sleep_for(1ms);
|
||||
|
||||
// 3. Read status register (3C) and wait for busy bit go to 0
|
||||
if(!waitForNotBusy(*I2c, FpgaAddr)) {
|
||||
printf("ERR: status register has error bit set!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// 4. LSC_INIT_ADDRESS (46)
|
||||
I2c->write(FpgaAddr, {0x46, 0x00, 0x00 , 0x00});
|
||||
std::this_thread::sleep_for(100ms);
|
||||
|
||||
// 5. Program SRAM parts (82)
|
||||
uint32_t lRowIdx;
|
||||
for(const auto &Row: Bitstream->m_vProgramData) {
|
||||
printf(
|
||||
"\rProgramming row %5u/%5u...",
|
||||
++lRowIdx, Bitstream->m_vProgramData.size()
|
||||
);
|
||||
fflush(stdout);
|
||||
// Compose and send next SRAM packet
|
||||
std::vector<uint8_t> Packet = {0x82, 0x21, 0x00, 0x00};
|
||||
for(const auto &RowByte: Row) {
|
||||
Packet.push_back(RowByte);
|
||||
}
|
||||
I2c->write(FpgaAddr, Packet);
|
||||
std::this_thread::sleep_for(100us);
|
||||
|
||||
// Some kind of dummy packet
|
||||
I2c->write(FpgaAddr, {0x00, 0x00, 0x00, 0x00});
|
||||
std::this_thread::sleep_for(100us);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
// 6. Program usercode (C2)
|
||||
printf("Programming usercode...\n");
|
||||
I2c->write(FpgaAddr, {
|
||||
0x46, 0x00, 0x00 , 0x00,
|
||||
reverseByte(Bitstream->m_UserCode[3]), reverseByte(Bitstream->m_UserCode[2]),
|
||||
reverseByte(Bitstream->m_UserCode[1]), reverseByte(Bitstream->m_UserCode[0])
|
||||
});
|
||||
|
||||
// 7. Program done (5E)
|
||||
printf("Finalizing programming...\n");
|
||||
I2c->write(FpgaAddr, {0x5E, 0x00, 0x00 , 0x00});
|
||||
|
||||
// 8. Read status register (3C) and wait for busy bit go to 0
|
||||
if(!waitForNotBusy(*I2c, FpgaAddr)) {
|
||||
printf("ERR: status register has error bit set!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// 9. Exit programming mode (26)
|
||||
I2c->write(FpgaAddr, {0x26, 0x00, 0x00 , 0x00});
|
||||
|
||||
printf("All done!\n");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user