适用于 ESP8266 的 Xilinx 虚拟电缆服务器

文章介绍了如何使用ESP8266通过XilinxVirtualCable(XVC)协议,实现通过WiFi远程控制FPGA的JTAG接口,包括设置WiFi凭证、使用ArduinoIDE编程以及Linux环境下的命令行操作。项目着重于低成本和易用性,但也提到了速度限制和替代方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概述

ESP8266 基于 xvcd 的 XVC(Xilinx Virtual Cable)协议实现 (GitHub - tmbinc/xvcd: Xilinx Virtual Cable Daemon)。

通过将ESP8266连接到目标的JTAG引脚(TDI、TDO、TMS、TCK) FPGA,您可以从 Xilinx 工具(Vivado、 等)通过WiFi。

作者: Gennaro Tortone (gtortone (Gennaro Tortone) · GitHub)

作者 2: Dhiru Kholia - 删除优化(希望获得更广泛的优化 兼容性),镜像函数从(使调试更容易)。xvcpi

注:本项目已使用 Vivado 2021.1、WeMos D1 Mini 作为 JTAG 进行测试 程序员(XVC 守护程序),并于 2021 年 8 月EBAZ4205“开发”FPGA 板。

如何使用

更改文件中的 WiFi 凭据。credentials.h

注意:常见低成本ESP8266的默认引脚映射 开发板包括:WeMos D1 Mini

ESP8266JTAG系列
第六天TDI的
第4天TDO公司
第7天TCK公司
第5天TMS系统

随意尝试不同的ESP8266开发板 - 大多数应该 只要解决任何问题。

​编辑

接下来,使用Arduino IDE构建程序并将其写入ESP8266板。

最后,在 Vivado 中选择选项并提及ESP8266板。Add Xilinx Virtual Cable (XVC)Hardware ManagerIP address

​编辑

如何使用(Linux版)
<span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><span style="color:#1f2328"><span style="color:var(--fgColor-default, var(--color-fg-default))"><span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><code>make install_arduino_cli
make install_platform
make deps
make upload
</code></span></span></span></span>

技巧

如果您在 Vivado 中看到错误消息,请选中 FPGA 电源的额定电压和电流。End of startup status: LOW

粗略性能统计(“速度”)

如果成本和易用性是驱动约束因素(以 speed),那么这个项目是“可用的”,可能就足够了。如果更高 编程速度是必需的,我建议使用或与FT2232H板一起使用。xc3sprogopenFPGALoader

这个项目可能是最便宜的JTAG编程器。Vivado-Compatible

GitHub - gtortone/esp-xvcd: ESP8266 Xilinx Virtual Cable - wifi JTAG 要快得多,但有报道称 的。FPGA programming failures

GitHub - pftbest/xvc-esp8266: Xilinx Virtual Cable Implementation based on ESP8266 - 这有 GPIO 优化 + 一个不错的 程序结构 - 谢谢!

另请参阅以下部分。Related Projects

// https://github.com/gtortone/esp-xvcd (upstream)

#include <ESP8266WiFi.h>
#include "credentials.h"

// Pin out
static constexpr const int tms_gpio = D5;
static constexpr const int tck_gpio = D7;
static constexpr const int tdo_gpio = D4;
static constexpr const int tdi_gpio = D6;

//#define VERBOSE
#define MAX_WRITE_SIZE  512
#define ERROR_OK 1
// #define XVCD_AP_MODE
#define XVCD_STATIC_IP

IPAddress ip(192, 168, 1, 13);
IPAddress gateway(192, 168, 1, 1);
IPAddress netmask(255, 255, 255, 0);
const int port = 2542;

WiFiServer server(port);
WiFiClient client;

// JTAG buffers
uint8_t cmd[16];
uint8_t buffer[1024], result[512];

/* Transition delay coefficients */
static const unsigned int jtag_delay = 10;  // NOTE!

static std::uint32_t jtag_xfer(std::uint_fast8_t n, std::uint32_t tms, std::uint32_t tdi)
{
  std::uint32_t tdo = 0;
  for (uint_fast8_t i = 0; i < n; i++) {
    jtag_write(0, tms & 1, tdi & 1);
    tdo |= jtag_read() << i;
    jtag_write(1, tms & 1, tdi & 1);
    tms >>= 1;
    tdi >>= 1;
  }
  return tdo;
}

static bool jtag_read(void)
{
  return digitalRead(tdo_gpio) & 1;
}

static void jtag_write(std::uint_fast8_t tck, std::uint_fast8_t tms, std::uint_fast8_t tdi)
{
  digitalWrite(tck_gpio, tck);
  digitalWrite(tms_gpio, tms);
  digitalWrite(tdi_gpio, tdi);

  for (std::uint32_t i = 0; i < jtag_delay; i++)
    asm volatile ("nop");
}

static int jtag_init(void)
{
  pinMode(tdo_gpio, INPUT);
  pinMode(tdi_gpio, OUTPUT);
  pinMode(tck_gpio, OUTPUT);
  pinMode(tms_gpio, OUTPUT);

  digitalWrite(tdi_gpio, 0);
  digitalWrite(tck_gpio, 0);
  digitalWrite(tms_gpio, 1);

  return ERROR_OK;
}

int sread(void *target, int len) {

  uint8_t *t = (uint8_t *) target;

  while (len) {
    int r = client.read(t, len);
    if (r <= 0)
      return r;
    t += r;
    len -= r;
  }
  return 1;
}

int srcmd(void * target, int maxlen) {
  uint8_t *t = (uint8_t *) target;

  while (maxlen) {
    int r = client.read(t, 1);
    if (r <= 0)
      return r;

    if (*t == ':') {
      return 1;
    }

    t += r;
    maxlen -= r;
  }

  return 0;
}

void setup() {
  delay(1000);

  Serial.begin(115200);
  Serial.println();
  Serial.println();
  Serial.println();
#ifdef XVCD_AP_MODE
  WiFi.mode(WIFI_AP);
  WiFi.softAPConfig(ip, ip, netmask);
  WiFi.softAP(MY_SSID, MY_PASSPHRASE);
#else
#ifdef XVCD_STATIC_IP
  WiFi.config(ip, gateway, netmask);
#endif
  WiFi.begin(MY_SSID, MY_PASSPHRASE);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
#endif
  Serial.print("Starting XVC Server on port ");
  Serial.println(port);

  jtag_init();

  server.begin();
  server.setNoDelay(true);
}

void loop() {

start: if (!client.connected()) {
    // try to connect to a new client
    client = server.available();
  } else {
    // read data from the connected client
    if (client.available()) {
      while (client.connected()) {

        do {

          if (srcmd(cmd, 8) != 1)
            goto start;

          if (memcmp(cmd, "getinfo:", 8) == 0) {
#ifdef VERBOSE
            Serial.write("XVC_info\n");
#endif
            client.write("xvcServer_v1.0:");
            client.print(MAX_WRITE_SIZE);
            client.write("\n");
            goto start;
          }

          if (memcmp(cmd, "settck:", 7) == 0) {
#ifdef VERBOSE
            Serial.write("XVC_tck\n");
#endif
            int ntck;
            if (sread(&ntck, 4) != 1) {
              Serial.println("reading tck failed\n");
              goto start;
            }
            // Actually TCK frequency is fixed, but replying a fixed TCK will halt hw_server
            client.write((const uint8_t *)&ntck, 4);
            goto start;
          }

          if (memcmp(cmd, "shift:", 6) != 0) {
            cmd[15] = '\0';
            Serial.print("invalid cmd ");
            Serial.println((char *)cmd);
            goto start;
          }

          int len;
          if (sread(&len, 4) != 1) {
            Serial.println("reading length failed\n");
            goto start;
          }

          unsigned int nr_bytes = (len + 7) / 8;

#ifdef VERBOSE
          Serial.print("len = ");
          Serial.print(len);
          Serial.print(" nr_bytes = ");
          Serial.println(nr_bytes);
#endif
          if (nr_bytes * 2 > sizeof(buffer)) {
            Serial.println("buffer size exceeded");
            goto start;
          }

          if (sread(buffer, nr_bytes * 2) != 1) {
            Serial.println("reading data failed\n");
            goto start;
          }

          memset((uint8_t *)result, 0, nr_bytes);

          jtag_write(0, 1, 1);

          int bytesLeft = nr_bytes;
          int bitsLeft = len;
          int byteIndex = 0;
          uint32_t tdi, tms, tdo;

          while (bytesLeft > 0) {
            tms = 0;
            tdi = 0;
            tdo = 0;
            if (bytesLeft >= 4) {
              memcpy(&tms, &buffer[byteIndex], 4);
              memcpy(&tdi, &buffer[byteIndex + nr_bytes], 4);
              tdo = jtag_xfer(32, tms, tdi);
              memcpy(&result[byteIndex], &tdo, 4);
              bytesLeft -= 4;
              bitsLeft -= 32;
              byteIndex += 4;
            } else {
              memcpy(&tms, &buffer[byteIndex], bytesLeft);
              memcpy(&tdi, &buffer[byteIndex + nr_bytes], bytesLeft);
              tdo = jtag_xfer(bitsLeft, tms, tdi);
              memcpy(&result[byteIndex], &tdo, bytesLeft);
              bytesLeft = 0;
              break;
            }
          }

          jtag_write(0, 1, 0);

          if (client.write(result, nr_bytes) != nr_bytes) {
            Serial.println("write");
          }
        } while (1);
      }
    }
  }
}
static const char* MY_SSID = "";
static const char* MY_PASSPHRASE = "";

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

happy_baymax

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值