src/iceVWireless: first draft

Signed-off-by: Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com>
This commit is contained in:
Gwenhael Goavec-Merou 2022-11-27 12:41:17 +01:00
parent b79b5d652b
commit a97eaf3e66
4 changed files with 448 additions and 2 deletions

View File

@ -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)

241
src/iceVWireless.cpp Normal file
View File

@ -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 &reg,
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;
}

174
src/iceVWireless.hpp Normal file
View File

@ -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 &reg, 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_

View File

@ -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)",