src/iceVWireless: first draft
Signed-off-by: Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com>
This commit is contained in:
parent
b79b5d652b
commit
a97eaf3e66
|
|
@ -209,6 +209,10 @@ if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
|||
# uart_ll only tested on Linux
|
||||
target_sources(openFPGALoader PRIVATE src/uart_ll.cpp)
|
||||
list (APPEND OPENFPGALOADER_HEADERS src/uart_ll.hpp)
|
||||
# iceV Wireless only tested on Linux
|
||||
target_sources(openFPGALoader PRIVATE src/iceVWireless.cpp)
|
||||
list (APPEND OPENFPGALOADER_HEADERS src/iceVWireless.hpp)
|
||||
add_definitions(-DENABLE_ICEVWIRELESS=1)
|
||||
endif()
|
||||
|
||||
if (ENABLE_UDEV)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,241 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
* Copyright (C) 2022 Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com>
|
||||
*/
|
||||
|
||||
#include "iceVWireless.hpp"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <regex>
|
||||
|
||||
#include "display.hpp"
|
||||
#include "uart_ll.hpp"
|
||||
#include "rawParser.hpp"
|
||||
|
||||
IceV_Wireless::IceV_Wireless(const std::string &device,
|
||||
const std::string &filename, Device::prog_type_t prg_type)
|
||||
:uart(device, 9600, 8, false), _filename(filename)
|
||||
{
|
||||
_mode = (prg_type == Device::WR_SRAM) ? PRG_RAM : PRG_SPIFFS;
|
||||
if (!uart.flush())
|
||||
throw std::runtime_error("Flush serial interface failed");
|
||||
if (!read_vbat())
|
||||
throw std::runtime_error("Fail to read vbat");
|
||||
if (!read_info())
|
||||
throw std::runtime_error("Fail to read info");
|
||||
}
|
||||
|
||||
IceV_Wireless::~IceV_Wireless() {}
|
||||
|
||||
bool IceV_Wireless::write_cmd(uint8_t cmd,
|
||||
uint32_t reg, size_t regsize)
|
||||
{
|
||||
char payload[4] = {
|
||||
static_cast<char>((reg >> 0) & 0xFF),
|
||||
static_cast<char>((reg >> 8) & 0xFF),
|
||||
static_cast<char>((reg >> 16) & 0xFF),
|
||||
static_cast<char>((reg >> 24) & 0xFF)};
|
||||
return write_cmd(cmd, payload, regsize);
|
||||
}
|
||||
|
||||
bool IceV_Wireless::write_cmd(uint8_t cmd,
|
||||
const char *reg, uint32_t regsize)
|
||||
{
|
||||
int kBuffSize = 4 + 4 + regsize;
|
||||
uint8_t buffer[kBuffSize];
|
||||
int pos = 0;
|
||||
memset(buffer, '\0', kBuffSize);
|
||||
/* cmd */
|
||||
buffer[pos++] = 0xE0 + (cmd & 0x0F);
|
||||
buffer[pos++] = 0xBE;
|
||||
buffer[pos++] = 0xFE;
|
||||
buffer[pos++] = 0xCA;
|
||||
/* register size */
|
||||
buffer[pos++] = (regsize >> 0) & 0xFF;
|
||||
buffer[pos++] = (regsize >> 8) & 0xFF;
|
||||
buffer[pos++] = (regsize >> 16) & 0xFF;
|
||||
buffer[pos++] = (regsize >> 24) & 0xFF;
|
||||
/* register/payload */
|
||||
memcpy(buffer+pos, reg, regsize);
|
||||
/* write buffer */
|
||||
if (uart.write(buffer, kBuffSize) != kBuffSize) {
|
||||
printError("Error: uart write failed");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int IceV_Wireless::read_tokens(std::vector<std::string> *rx)
|
||||
{
|
||||
std::string payload;
|
||||
/* read message from ESP32-C3 */
|
||||
int ret = uart.read_until(&payload, '\n');
|
||||
if (ret < 0)
|
||||
return 32;
|
||||
|
||||
std::regex regex{R"([\s]+)"}; // split on space
|
||||
std::sregex_token_iterator it{payload.begin(), payload.end(), regex, -1};
|
||||
std::vector<std::string> words{it, {}};
|
||||
|
||||
/* decode / check */
|
||||
uint16_t errorCode;
|
||||
bool found = false;
|
||||
for (size_t i = 0; i < words.size(); i++){
|
||||
if (words[i] == "RX") {
|
||||
if (i + 2 <= words.size()) {
|
||||
found = true;
|
||||
errorCode = stoul(words[i+1], nullptr, 16);
|
||||
rx->resize(words.size() - i - 2);
|
||||
std::copy(words.begin()+i+2, words.end(), rx->begin());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
return 64;
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
int IceV_Wireless::read_data(std::string *rx)
|
||||
{
|
||||
std::vector<std::string> rxv;
|
||||
int errorCode = read_tokens(&rxv);
|
||||
if (errorCode != 0)
|
||||
return errorCode;
|
||||
|
||||
rx->resize(rxv[0].size());
|
||||
std::copy(rxv[0].begin(), rxv[0].end(),
|
||||
rx->begin());
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
int IceV_Wireless::wr_rd(uint8_t cmd, uint32_t reg, uint32_t regsize,
|
||||
std::vector<std::string> *rx)
|
||||
{
|
||||
write_cmd(cmd, reg, regsize);
|
||||
return read_tokens(rx);
|
||||
}
|
||||
|
||||
int IceV_Wireless::wr_rd(uint8_t cmd, uint32_t reg, uint32_t regsize,
|
||||
std::string *rx)
|
||||
{
|
||||
write_cmd(cmd, reg, regsize);
|
||||
return read_data(rx);
|
||||
}
|
||||
|
||||
int IceV_Wireless::wr_rd(uint8_t cmd, const std::string ®,
|
||||
size_t regsize, std::string *rx)
|
||||
{
|
||||
write_cmd(cmd, reg.c_str(), regsize);
|
||||
return read_data(rx);
|
||||
}
|
||||
|
||||
bool IceV_Wireless::read_vbat()
|
||||
{
|
||||
std::string rx;
|
||||
uint32_t val = wr_rd(READ_VBAT, 0, 4, &rx);
|
||||
if (val != 0)
|
||||
return false;
|
||||
val = stoul(rx, nullptr, 16);
|
||||
printInfo("Vbat = " + std::to_string(val) + " mV");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IceV_Wireless::read_info()
|
||||
{
|
||||
std::vector<std::string> rx;
|
||||
uint32_t val = wr_rd(READ_INFO, 0, 4, &rx);
|
||||
if (val != 0)
|
||||
return false;
|
||||
printInfo("info: version: " + rx[0] + " ipaddr: " +
|
||||
rx[1]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// send a file for direct load to FPGA or write to SPIFFS
|
||||
bool IceV_Wireless::send_file(uint8_t cmd, const std::string &filename)
|
||||
{
|
||||
std::string rx;
|
||||
std::string tx;
|
||||
// open file
|
||||
|
||||
printInfo("Open file " + filename + " ", false);
|
||||
RawParser _bit(filename, false);
|
||||
printSuccess("DONE");
|
||||
|
||||
printInfo("Parse file ", false);
|
||||
if (_bit.parse() == EXIT_FAILURE) {
|
||||
printError("FAIL");
|
||||
throw std::runtime_error("Failed to parse bitstream");
|
||||
}
|
||||
printSuccess("DONE");
|
||||
uint32_t size = _bit.getLength() / 8;
|
||||
uint8_t *data = _bit.getData();
|
||||
tx.append(reinterpret_cast<char *>(data), size);
|
||||
|
||||
// send to the C3 over usb
|
||||
|
||||
uint32_t ret = wr_rd(cmd, tx, size, &rx);
|
||||
if (ret != 0) {
|
||||
printError("FAIL");
|
||||
throw std::runtime_error("Error " + std::to_string(ret));
|
||||
}
|
||||
printSuccess("DONE");
|
||||
|
||||
return (ret == 0) ? true : false;
|
||||
}
|
||||
|
||||
bool IceV_Wireless::read_reg(uint32_t reg)
|
||||
{
|
||||
std::string rx;
|
||||
uint32_t val = wr_rd(READ_REG, reg, 4, &rx);
|
||||
if (val != 0)
|
||||
return false;
|
||||
printf("Read reg %u = %4x\n", reg, val);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* TODO: write reg */
|
||||
bool IceV_Wireless::write_reg(uint32_t reg, uint32_t data)
|
||||
{
|
||||
std::string rx;
|
||||
std::string tx;
|
||||
for (int i = 0; i < 4; i++)
|
||||
tx.append(1, static_cast<uint8_t>(reg >> (i * 8)));
|
||||
for (int i = 0; i < 4; i++)
|
||||
tx.append(i, static_cast<uint8_t>(data >> (i * 8)));
|
||||
uint32_t val = wr_rd(WRITE_REG, tx, 8, &rx);
|
||||
return (val == 0) ? true : false;
|
||||
}
|
||||
|
||||
/* TODO: psram write */
|
||||
/* TODO: psram read */
|
||||
/* TODO: psram init */
|
||||
/* TODO: send cred */
|
||||
bool IceV_Wireless::send_cred(uint8_t cred_type, std::string value)
|
||||
{
|
||||
std::string rep;
|
||||
value+='\0';
|
||||
/* 3 -> SSID
|
||||
* 4 -> PASS
|
||||
*/
|
||||
uint32_t val = wr_rd((uint8_t)(SEND_CRED + (cred_type & 0x01)),
|
||||
value, static_cast<size_t>(value.size()), &rep);
|
||||
return val == 0;
|
||||
}
|
||||
|
||||
// load config from SPIFFS (0: default, 1: spi pass)
|
||||
bool IceV_Wireless::load_cfg(uint32_t reg)
|
||||
{
|
||||
std::string rep;
|
||||
uint32_t val = wr_rd(LOAD_CFG, reg, 4, &rep);
|
||||
return val == 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,174 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
* Copyright (C) 2022 Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com>
|
||||
*/
|
||||
|
||||
#ifndef SRC_ICEVWIRELESS_HPP_
|
||||
#define SRC_ICEVWIRELESS_HPP_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "device.hpp"
|
||||
#include "uart_ll.hpp"
|
||||
|
||||
/*!
|
||||
* \file iceVWireless
|
||||
* \class IceV_Wireless
|
||||
* \brief iceV Wireless protocol implementation
|
||||
* \author Gwenhael Goavec-Merou
|
||||
*/
|
||||
class IceV_Wireless {
|
||||
public:
|
||||
/*!
|
||||
* \brief constructor
|
||||
* \param[in] device: /dev/xxx
|
||||
* \param[in] filename: bitstream file name
|
||||
* \param[in] prg_type: write / load
|
||||
*/
|
||||
IceV_Wireless(const std::string &device,
|
||||
const std::string &filename, Device::prog_type_t prg_type);
|
||||
|
||||
~IceV_Wireless();
|
||||
|
||||
/* iceV Wireless commands */
|
||||
enum cmd_lst {
|
||||
READ_REG = 0,
|
||||
WRITE_REG = 1,
|
||||
READ_VBAT = 2,
|
||||
SEND_CRED = 3,
|
||||
/* 4: SEND_CRED password */
|
||||
READ_INFO = 5,
|
||||
LOAD_CFG = 6,
|
||||
PSRAM_INIT = 10,
|
||||
PSRAM_READ = 11,
|
||||
PSRAM_WRITE = 12,
|
||||
PRG_SPIFFS = 14,
|
||||
PRG_RAM = 15
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief read and display ESP32-C3 ADC (battery voltage)
|
||||
* \return false is something is wrong
|
||||
*/
|
||||
bool read_vbat();
|
||||
|
||||
/*!
|
||||
* \brief read and display firmware version and IP address
|
||||
* \return false is something is wrong
|
||||
*/
|
||||
bool read_info();
|
||||
|
||||
/*!
|
||||
* \brief read the specified register
|
||||
* \param[in] reg: register address
|
||||
* \return false is something is wrong
|
||||
*/
|
||||
bool read_reg(uint32_t reg);
|
||||
|
||||
/*!
|
||||
* \brief write the specified register
|
||||
* \param[in] reg: register address
|
||||
* \param[in] data: value to write
|
||||
* \return false is something is wrong
|
||||
*/
|
||||
bool write_reg(uint32_t reg, uint32_t data);
|
||||
|
||||
/*!
|
||||
* \brief write wireless SSID or pass
|
||||
* \param[in] reg: 0 -> SSID, 1 -> password
|
||||
* \param[in] value: string to send
|
||||
* \return false if something went wrong
|
||||
*/
|
||||
bool send_cred(uint8_t reg, std::string value);
|
||||
|
||||
/*!
|
||||
* \brief send content of specified file to SPIFFS or ice40 RAM
|
||||
* \param[in] cmd: destination code (14 -> SPIFFS, 15 -> RAM)
|
||||
* \param[in] filename: bitstream path
|
||||
* \return false if something went wrong
|
||||
*/
|
||||
bool send_file(uint8_t cmd, const std::string &filename);
|
||||
bool load_cfg(uint32_t reg);
|
||||
|
||||
/*!
|
||||
* \brief send bitstream to the device (SPIFFS or RAM)
|
||||
*/
|
||||
void program() {send_file(_mode, _filename);}
|
||||
|
||||
private:
|
||||
/*!
|
||||
* \brief write command + payload, followed by a read_tokens call
|
||||
* \param[in] cmd: command
|
||||
* \param[in] reg: payload (uint32_t)
|
||||
* \param[in] regsize: payload len
|
||||
* \param[out] rx: iceV Wireless answer (vector)
|
||||
* \return error/status code
|
||||
*/
|
||||
int wr_rd(uint8_t cmd, uint32_t reg, uint32_t regsize,
|
||||
std::vector<std::string> *rx);
|
||||
|
||||
/*!
|
||||
* \brief write command + payload, followed by a read_data call
|
||||
* \param[in] cmd: command
|
||||
* \param[in] reg: payload (uint32_t)
|
||||
* \param[in] regsize: payload len
|
||||
* \param[out] rx: iceV Wireless answer (scalar)
|
||||
* \return error/status code
|
||||
*/
|
||||
int wr_rd(uint8_t cmd, uint32_t reg, uint32_t regsize,
|
||||
std::string *rx);
|
||||
|
||||
/*!
|
||||
* \brief write command + payload, followed by a read_data call
|
||||
* \param[in] cmd: command
|
||||
* \param[in] reg: payload (string)
|
||||
* \param[in] regsize: payload len
|
||||
* \param[out] rx: iceV Wireless answer (scalar)
|
||||
* \return error/status code
|
||||
*/
|
||||
int wr_rd(uint8_t cmd, const std::string ®, size_t regsize,
|
||||
std::string *rx);
|
||||
|
||||
/*!
|
||||
* \brief build and write a sequence (MAGIC + size + payload)
|
||||
* \param[in] cmd: command
|
||||
* \param[in] reg: payload (char *)
|
||||
* \param[in] regsize: payload len
|
||||
* \return false if issue with UART, true otherwise
|
||||
*/
|
||||
bool write_cmd(uint8_t cmd, const char *reg, uint32_t regsize);
|
||||
|
||||
/*!
|
||||
* \brief build and write a sequence (MAGIC + size + payload)
|
||||
* \param[in] cmd: command
|
||||
* \param[in] reg: payload (uint32_t)
|
||||
* \param[in] regsize: payload len
|
||||
* \return false if issue with UART, true otherwise
|
||||
*/
|
||||
bool write_cmd(uint8_t cmd, uint32_t reg, size_t regsize);
|
||||
|
||||
/*!
|
||||
* \brief read full ice4VWireless message, split sequence
|
||||
* returns error and fill vector<string> with answer
|
||||
* \param[out] rx: ice4VWireless splitted answer
|
||||
* \return status code
|
||||
*/
|
||||
int read_tokens(std::vector<std::string> *rx);
|
||||
|
||||
/*!
|
||||
* \brief similar to read_tokens but fill string with first
|
||||
* answer part
|
||||
* \param[out] rx: ice4VWireless answer
|
||||
* \return status code
|
||||
*/
|
||||
int read_data(std::string *rx);
|
||||
|
||||
Uart_ll uart; /*! lowlevel uart access */
|
||||
std::string _filename; /*! bitstream name */
|
||||
cmd_lst _mode; /*! SPIFFS or RAM */
|
||||
};
|
||||
|
||||
#endif // SRC_ICEVWIRELESS_HPP_
|
||||
31
src/main.cpp
31
src/main.cpp
|
|
@ -26,6 +26,9 @@
|
|||
#include "ftdispi.hpp"
|
||||
#include "gowin.hpp"
|
||||
#include "ice40.hpp"
|
||||
#if ENABLE_ICEVWIRELESS
|
||||
#include "iceVWireless.hpp"
|
||||
#endif
|
||||
#include "lattice.hpp"
|
||||
#include "libusb_ll.hpp"
|
||||
#include "jtag.hpp"
|
||||
|
|
@ -63,6 +66,7 @@ struct arguments {
|
|||
bool is_list_command;
|
||||
bool spi;
|
||||
bool dfu;
|
||||
bool iceVWireless;
|
||||
string file_type;
|
||||
string fpga_part;
|
||||
string bridge_path;
|
||||
|
|
@ -109,8 +113,8 @@ int main(int argc, char **argv)
|
|||
/* command line args. */
|
||||
struct arguments args = {0, false, false, false, false, 0, "", "", "", "-", "", -1,
|
||||
0, false, "-", false, false, false, false, Device::PRG_NONE, false,
|
||||
/* spi dfu file_type fpga_part bridge_path probe_firmware */
|
||||
false, false, "", "", "", "",
|
||||
/* spi dfu iceVWireless file_type fpga_part bridge_path probe_firmware */
|
||||
false, false, false, "", "", "", "",
|
||||
/* index_chain file_size target_flash external_flash altsetting */
|
||||
-1, 0, "primary", false, -1,
|
||||
/* vid, pid, index bus_addr, device_addr */
|
||||
|
|
@ -139,6 +143,25 @@ int main(int argc, char **argv)
|
|||
if (args.prg_type == Device::WR_FLASH)
|
||||
cout << "write to flash" << endl;
|
||||
|
||||
#ifdef ENABLE_ICEVWIRELESS
|
||||
/* ------------------- */
|
||||
/* iCE V Wireless */
|
||||
/* ------------------- */
|
||||
if (args.iceVWireless) {
|
||||
/* if no instruction from user -> select load */
|
||||
if (args.prg_type == Device::PRG_NONE)
|
||||
args.prg_type = Device::WR_SRAM;
|
||||
try {
|
||||
IceV_Wireless ice(args.device, args.bit_file, args.prg_type);
|
||||
ice.program();
|
||||
} catch (std::exception &e) {
|
||||
printError("IceV Wirelesss failed:\n\t" + string(e.what()));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (args.board[0] != '-') {
|
||||
if (board_list.find(args.board) != board_list.end()) {
|
||||
board = &(board_list[args.board]);
|
||||
|
|
@ -749,6 +772,10 @@ int parse_opt(int argc, char **argv, struct arguments *args,
|
|||
("freq", "jtag frequency (Hz)", cxxopts::value<string>(freqo))
|
||||
("f,write-flash",
|
||||
"write bitstream in flash (default: false)")
|
||||
#if ENABLE_ICEVWIRELESS
|
||||
("iceV_wireless", "iceV wireless protocol",
|
||||
cxxopts::value<bool>(args->iceVWireless))
|
||||
#endif
|
||||
("index-chain", "device index in JTAG-chain",
|
||||
cxxopts::value<int>(args->index_chain))
|
||||
("ip", "IP address (only for XVC client)",
|
||||
|
|
|
|||
Loading…
Reference in New Issue