无线FPGA调试器ESP32-XVC

这篇具有很好参考价值的文章主要介绍了无线FPGA调试器ESP32-XVC。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

xiaowuzxc/无线Xilinx FPGA调试器ESP32-XVC - 码云 - 开源中国 (gitee.com)https://gitee.com/xiaowuzxc/ESP32-XVC/tree/master

已板级验证

介绍

ESP32方案的无线FPGA调试器,成本非常低,可以使用Vivado无线调试XilinxAMD FPGA。相比原工程,提高了可移植性,增加自动配网功能,增加oled显示连接状态、IP地址。

作者们

主要作者derekmulcahy:Xilinx Virtual Cable Server for Raspberry Pi
ciniml向ESP32移植:Xilinx Virtual Cable Server for ESP32
Kholia进行了优化:kholia/xvc-esp32

本工程fork自Kholia的版本,进行了以下改动:

  • 提高了可移植性,兼容所有的ESP32
  • 加入智能配网,有手机就行
  • 加入连接状态、IP地址显示,连接Vivado更方便
  • 加入0.91 oled显示屏,可以方便地看到状态

如何使用

硬件平台

本工程板级验证所使用PCB的原理图为SCH_原理图.pdf
可以根据需求自己制作板子,或者使用现成的开发板。
 

无线FPGA调试器ESP32-XVC,fpga开发


测试完spi flash就拆走了~~,卸磨杀驴~~

连接WIFI

本工程加入了智能配网功能,不需要在程序中固化WIFI名称和密码,通过手机即可进行配置。
ESP32上电后,默认尝试连接上一次的WIFI,连接状态会在串口和oled屏显示。多次尝试连接失败,进入智能配网状态。

  • 掏出你的手机,保持WIFI开启,连接你希望ESP32连接的WIFI。
  • 打开微信,搜索安信可科技公众号,点击应用开发-微信配网,根据提示配置网络。
  • 若连接成功,会在串口和oled显示WIFI名称和连接状态。
信息显示

通过UART0,会显示一些重要信息。 打开串口软件,波特率为115200,选择对应的串口。

与Vivado连接

需要将ESP32对应引脚与FPGA的JTAG接口连接,保证ESP32和电脑连接同一个路由器,上电。

  • 打开Vivado->Open Hardware Manager
  • 点击Open target->Open new target->一路next ->在Host name中输入ESP32在串口中输出的IP地址。如果Hardware窗口中已存在localhost(),则localhost()->右键->Add Xilinx Virtual Cable(XVC)
  • 如果看到调试器Hardware Target与FPGA器件Hardware Devices,表示连接成功,开始愉快的无线烧录吧!

编译源码

本工程基于Arduino开发环境,适用于任意型号的ESP32,如:ESP32 ESP32-S2 ESP32-C3,而ESP32-S3目前还未受到arduino的支持。

  • 首先,根据系统版本,安装Arduino:Arduino官网
  • 打开Arduino->文件->首选项->附加开发板管理网站,添加:
  • https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
  • 打开Arduino->工具->开发板->开发板管理器,搜索并安装esp32
  • 打开Arduino->项目->加载库->管理库,搜索关键词esp32 ssd1306,并安装ESP8288 and ESP32 OLED driver for SSD1306 displays
  • 工具中,根据开发板的芯片型号和硬件设计,选择合适的选项。
  • 点击上传,编译并烧录。

移植说明

移植到其他ESP32硬件平台,仅需修改以下注释包住的内容。
/*------引脚分配,可修改-------*/
其中包括oled接口,led灯,jtag引脚配置文章来源地址https://www.toymoban.com/news/detail-853591.html

/*
   Description: Xilinx Virtual Cable Server for ESP32

   See Licensing information at End of File.
*/

// #include <M5Atom.h>
#include <WiFi.h>
#include <lwip/sockets.h>
#include <lwip/netdb.h>
#include <cstdio>
#include <cstdlib>
#include <cstdint>
#include <cstring>
#include <memory>
#include <cstdlib>
#include <SSD1306Wire.h>
/*------引脚分配,可修改-------*/
#define LED_blk 12 //led引脚,低电平点亮

#define SDA 0 //oled iic SDA
#define SCL 1 //oled iic SCL
// 选择JTAG信号与GPIO的关联,注意不要与UART,I2C等功能冲突
// 填入GPIOx的编号
static constexpr const int tms_gpio = 3;//TMS
static constexpr const int tck_gpio = 4;//TCK
static constexpr const int tdo_gpio = 5;//TDO
static constexpr const int tdi_gpio = 6;//TDI
/*------引脚分配,可修改-------*/
char ip_string[20];
SSD1306Wire display(0x3c, SDA, SCL, GEOMETRY_128_32);
bool led_sta=1;
#define ERROR_JTAG_INIT_FAILED -1
#define ERROR_OK 1
// 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 (int 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;
}

static int sread(int fd, void* target, int len) {
  std::uint8_t *t = reinterpret_cast<std::uint8_t*>(target);
  while (len) {
    int r = read(fd, t, len);
    if (r <= 0)
      return r;
    t += r;
    len -= r;
  }
  return 1;
}

static constexpr const char* TAG = "XVC";

struct Socket
{
  int fd;
  Socket() : fd(-1) {}
  Socket(int fd) : fd(fd) {}
  Socket(int domain, int family, int protocol)
  {
    this->fd = socket(domain, family, protocol);
  }
  Socket(const Socket&) = delete;
  Socket(Socket&& rhs) : fd(rhs.fd) {
    rhs.fd = -1;
  }
  ~Socket() {
    this->release();
  }
  void release() {
    if (this->is_valid()) {
      closesocket(this->fd);
      this->fd = -1;
    }
  }
  int get() const {
    return this->fd;
  }

  Socket& operator=(Socket&& rhs) {
    this->fd = rhs.fd;
    rhs.fd = -1;
    return *this;
  }
  bool is_valid() const {
    return this->fd > 0;
  }
  operator int() const {
    return this->get();
  }
};

class XvcServer
{
  private:
    Socket listen_socket;
    Socket client_socket;
    unsigned char buffer[2048], result[1024];
  public:
    XvcServer(std::uint16_t port) {
      Socket sock(AF_INET, SOCK_STREAM, 0);
      {
        int value = 1;
        setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value));
      }

      sockaddr_in address;
      address.sin_addr.s_addr = INADDR_ANY;
      address.sin_port = htons(port);
      address.sin_family = AF_INET;

      if (bind(sock, reinterpret_cast<sockaddr*>(&address), sizeof(address)) < 0) {
        ESP_LOGE(TAG, "Failed to bind socket.");
      }
      if (listen(sock, 0) < 0) {
        ESP_LOGE(TAG, "Failed to listen the socket.");
      }

      ESP_LOGI(TAG, "Begin XVC Server. port=%d", port);
      this->listen_socket = std::move(sock);
    }
    XvcServer(const XvcServer&) = delete;

    bool wait_connection()
    {
      fd_set conn;
      int maxfd = this->listen_socket.get();

      FD_ZERO(&conn);
      FD_SET(this->listen_socket.get(), &conn);

      fd_set read = conn, except = conn;
      int fd;

      if (select(maxfd + 1, &read, 0, &except, 0) < 0) {
        ESP_LOGE(TAG, "select");
        return false;
      }

      for (fd = 0; fd <= maxfd; ++fd) {
        if (FD_ISSET(fd, &read)) {
          if (fd == this->listen_socket.get()) {
            int newfd;
            sockaddr_in address;
            socklen_t nsize = sizeof(address);
            newfd = accept(this->listen_socket.get(), reinterpret_cast<sockaddr*>(&address), &nsize);

            ESP_LOGI(TAG, "connection accepted - fd %d\n", newfd);
            if (newfd < 0) {
              ESP_LOGE(TAG, "accept returned an error.");
            } else {
              if (newfd > maxfd) {
                maxfd = newfd;
              }
              FD_SET(newfd, &conn);
              this->client_socket = Socket(newfd);
              return true;
            }
          }

        }
      }

      return false;
    }

    bool handle_data()
    {
      const char xvcInfo[] = "xvcServer_v1.0:2048\n";
      int fd = this->client_socket.get();

      std::uint8_t cmd[16];
      std::memset(cmd, 0, 16);

      if (sread(fd, cmd, 2) != 1)
        return false;

      if (memcmp(cmd, "ge", 2) == 0) {
        if (sread(fd, cmd, 6) != 1)
          return 1;
        memcpy(result, xvcInfo, strlen(xvcInfo));
        if (write(fd, result, strlen(xvcInfo)) != strlen(xvcInfo)) {
          ESP_LOGE(TAG, "write");
          return 1;
        }
        ESP_LOGD(TAG, "%u : Received command: 'getinfo'\n", (int)time(NULL));
        ESP_LOGD(TAG, "\t Replied with %s\n", xvcInfo);
        return true;
      } else if (memcmp(cmd, "se", 2) == 0) {
        if (sread(fd, cmd, 9) != 1)
          return 1;
        memcpy(result, cmd + 5, 4);
        if (write(fd, result, 4) != 4) {
          ESP_LOGE(TAG, "write");
          return 1;
        }
        ESP_LOGD(TAG, "%u : Received command: 'settck'\n", (int)time(NULL));
        ESP_LOGD(TAG, "\t Replied with '%.*s'\n\n", 4, cmd + 5);
        return true;
      } else if (memcmp(cmd, "sh", 2) == 0) {
        if (sread(fd, cmd, 4) != 1)
          return false;
        ESP_LOGD(TAG, "%u : Received command: 'shift'\n", (int)time(NULL));
      } else {

        ESP_LOGE(TAG, "invalid cmd '%s'\n", cmd);
        return false;
      }

      int len;
      if (sread(fd, &len, 4) != 1) {
        ESP_LOGE(TAG, "reading length failed\n");
        return false;
      }

      int nr_bytes = (len + 7) / 8;
      if (nr_bytes * 2 > sizeof(buffer)) {
        ESP_LOGE(TAG, "buffer size exceeded\n");
        return false;
      }

      if (sread(fd, buffer, nr_bytes * 2) != 1) {
        ESP_LOGE(TAG, "reading data failed\n");
        return false;
      }
      memset(result, 0, nr_bytes);

      ESP_LOGD(TAG, "Number of Bits  : %d\n", len);
      ESP_LOGD(TAG, "Number of Bytes : %d \n", 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;

          ESP_LOGD(TAG, "LEN : 0x%08x\n", 32);
          ESP_LOGD(TAG, "TMS : 0x%08x\n", tms);
          ESP_LOGD(TAG, "TDI : 0x%08x\n", tdi);
          ESP_LOGD(TAG, "TDO : 0x%08x\n", tdo);
        } 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;

          ESP_LOGD(TAG, "LEN : 0x%08x\n", bitsLeft);
          ESP_LOGD(TAG, "TMS : 0x%08x\n", tms);
          ESP_LOGD(TAG, "TDI : 0x%08x\n", tdi);
          ESP_LOGD(TAG, "TDO : 0x%08x\n", tdo);
          break;
        }
      }

      jtag_write(0, 1, 0);

      if (write(fd, result, nr_bytes) != nr_bytes) {
        ESP_LOGE(TAG, "write");
        return false;
      }

      return true;
    }

    void run()
    {
      if (this->client_socket.is_valid()) {
        if (!this->handle_data()) {
          this->client_socket.release();
        }
      }
      else {
        if (this->wait_connection()) {
          // Nothing to do.
        }
      }
    }
};

static void serialTask(void*)
{
  std::size_t pendingBytesH2T = 0;
  std::size_t bytesWrittenH2T = 0;
  static std::uint8_t h2tBuffer[128];
  std::size_t pendingBytesT2H = 0;
  std::size_t bytesWrittenT2H = 0;
  static std::uint8_t t2hBuffer[128];

  while (true) {
    if (Serial.available()) {
      Serial1.write(Serial.read());
    }
    else if (Serial1.available()) {
      Serial.write(Serial1.read());
    }
    else {
      vTaskDelay(pdMS_TO_TICKS(1));
    }
  }
}

bool AutoConfig()//自动连接
{
    WiFi.begin();
    //如果觉得时间太长可改
    for (int i = 0; i < 6; i++)
    {
        led_sta=!led_sta;
        digitalWrite(LED_blk, led_sta);
        int wstatus = WiFi.status();
        if (wstatus == WL_CONNECTED)
        {
          Serial.println("WIFI SmartConfig Success");
          Serial.printf("SSID:%s", WiFi.SSID().c_str());
          Serial.printf(", PSW:%s\r\n", WiFi.psk().c_str());
          Serial.print("LocalIP:");
          Serial.print(WiFi.localIP());
          Serial.print(" ,GateIP:");
          Serial.println(WiFi.gatewayIP());
          return true;
        }
        else
        {
          Serial.print("WIFI AutoConfig Waiting......");
          Serial.println(wstatus);
          delay(600);
        }
    }
    Serial.println("WIFI AutoConfig Faild!" );
    return false;
}
void SmartConfig()//智能配网,用手机操作
{
  WiFi.mode(WIFI_STA);
  Serial.println("\r\nWait for Smartconfig...");
  WiFi.beginSmartConfig();

  display.clear();
  display.drawString(0, 0, "Use SmartConfig      ");
  display.display();
  digitalWrite(LED_blk, 1);
  while (1)
  {
    led_sta=!led_sta;
    digitalWrite(LED_blk, led_sta);
    Serial.print(".");
    delay(334);                   // wait for a second
    if (WiFi.smartConfigDone())
    {
      Serial.println("SmartConfig Success");
      Serial.printf("SSID:%s\r\n", WiFi.SSID().c_str());
      Serial.printf("PSW:%s\r\n", WiFi.psk().c_str());
      display.clear();
      display.drawString(0, 0, "SmartConfig Success      ");
      display.display();
      break;
    }
  }
}

void setup()
{
  pinMode(LED_blk, OUTPUT);//led
  digitalWrite(LED_blk, led_sta);//off
  display.init();//绘图
  display.drawString(0, 0, "xiaowuzxc/ESP32-XVC");
  display.display();
  jtag_init();
  Serial.begin(115200);

  if (!AutoConfig())//开启自动配网
    SmartConfig();
  digitalWrite(LED_blk, 0);
  display.drawString(0, 10, "IP:");
  display.drawString(16, 10, WiFi.localIP().toString().c_str());
  display.display();
  TaskHandle_t handle;
  led_sta=0;//success, led liang
  digitalWrite(LED_blk, led_sta);
}

enum class AppState
{
  WaitingAPConnection,
  APConnected,
  ClientConnected,
};

static std::unique_ptr<XvcServer> server;

void loop()
{
  static AppState state = AppState::WaitingAPConnection;
  switch (state) {
    case AppState::WaitingAPConnection: {
        if (WiFi.isConnected()) {
          ESP_LOGI(TAG, "WiFi connection ready. IP: %s", WiFi.localIP().toString().c_str());
          Serial.print(WiFi.localIP());
          state = AppState::APConnected;
        }
        break;
      }
    case AppState::APConnected: {
        if (!WiFi.isConnected()) {
          ESP_LOGI(TAG, "disconnected from WiFi.");
          server.release();
          state = AppState::WaitingAPConnection;
          break;
        }

        if (!server) {
          server.reset(new XvcServer(2542));
        }
        if (server) {
          server->run();
        }
        break;
      }
  }
}


/*
   This work, "xvc-esp32.ino", is a derivative of "xvcpi.c" (https://github.com/derekmulcahy/xvcpi)
   by Derek Mulcahy.

   "xvc-esp32.ino" is licensed under CC0 1.0 Universal (http://creativecommons.org/publicdomain/zero/1.0/)
   by Kenta IDA (fuga@fugafuga.org)

   The original license information of "xvcpi.c" is attached below.
*/

/*
   This work, "xvcpi.c", is a derivative of "xvcServer.c" (https://github.com/Xilinx/XilinxVirtualCable)
   by Avnet and is used by Xilinx for XAPP1251.

   "xvcServer.c" is licensed under CC0 1.0 Universal (http://creativecommons.org/publicdomain/zero/1.0/)
   by Avnet and is used by Xilinx for XAPP1251.

   "xvcServer.c", is a derivative of "xvcd.c" (https://github.com/tmbinc/xvcd)
   by tmbinc, used under CC0 1.0 Universal (http://creativecommons.org/publicdomain/zero/1.0/).

   Portions of "xvcpi.c" are derived from OpenOCD (http://openocd.org)

   "xvcpi.c" is licensed under CC0 1.0 Universal (http://creativecommons.org/publicdomain/zero/1.0/)
   by Derek Mulcahy.
*/

到了这里,关于无线FPGA调试器ESP32-XVC的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Linux开发工具之调试器gdb

    程序的发布方式有两种,debug模式和release模式 Linux gcc/g++出来的二进制程序,默认是release模式 要使用gdb调试,必须在源代码生成二进制程序的时候, 加上 -g 选项   所以一份代码若要被调试必须是debug模式,但是在linux下我们编译代码的时候默认为release模式,要想让我们的代码

    2024年02月07日
    浏览(43)
  • stlink下载调试器使用说明(STM32采用stlink下载程序)

    stlink能干什么?  最基本的功能:下载程序。  一般STM32支持ISP串口下载,也支持stlink、jlink等下载器下载 。 使用stlink、jlink下载要比串口方便很多,在keil里直接点击下载就行了,不需要去选择hex文件,速度上要快很多,主要一点,jlink、stlink能实现硬件仿真调试,程序出问

    2024年02月02日
    浏览(49)
  • [linux开发工具]小程序--进度条、调试器 - gdb

    📙 作者简介 :RO-BERRY 📗 学习方向:致力于C、C++、数据结构、TCP/IP、数据库等等一系列知识 📒 日后方向 : 偏向于CPP开发以及大数据方向,欢迎各位关注,谢谢各位的支持 第一种情况 执行结果如下: 可以看到程序先执行printf再执行sleep 第二种情况 执行结果如下: 在这里

    2024年02月22日
    浏览(43)
  • Linux之基础开发工具gdb调试器的使用(三)

    📘北尘_ :个人主页 🌎个人专栏 :《Linux操作系统》《经典算法试题 》《C++》 《数据结构与算法》 ☀️走在路上,不忘来时的初心 yum install -y gdb 程序的发布方式有两种,debug模式和release模式 Linux gcc/g++出来的二进制程序,默认是release模式,release模式下无法调试 要使用gd

    2024年02月05日
    浏览(47)
  • 微信小程序开发 获取手机 体验版获取不到code,需打开调试器才行?

    最近开发遇到问题: 小程序开发,获取手机号时候,调用接口需要 code,但是体验版始终获取不到,只有在打开小程序调试器的时候才能获取到code.这么奇葩的问题原因竟然是小程序后台没有配置服务器域名,把相关域名在小程序后台配置下 就可以正常了。 点击允许后会调接

    2024年02月08日
    浏览(48)
  • 调试器加载错误,从任务栏打开可能会导致该问题(微信开发者工具)

    在使用HBuilder X 3.6.4 运行微信小程序的时候,发现报错如下: [微信小程序开发者工具] × #initialize-error: [error] 工具的服务端口已关闭。要使用命令行调用工具,请在下方输入 y 以确认开启,或手动打开工具 - 设置 - 安全设置,将服务端口开启。     尝试了手动开启服务端口之

    2024年02月15日
    浏览(51)
  • Linux - 还不懂 gdb 调试器?(调试软件)

    当前,我们可以使用 make/makefile 来程序化执行代码文件;可以使用 gcc/g++ 等编译器来编译代码;可以使用 vim 编辑器来编写代码;其实在 Linux 当中还有一个工具,可以实现调试工作,这个工具就是 -- gdb。 在了解调试器之前,你应该对代码的发布版本做一些了解: 我们在 VS

    2024年02月07日
    浏览(51)
  • Linux--调试器:gdb

    gcc与g++默认动态链接形成的可执行程序(比如a.out)是 release 版本,不可调试!!! 如何搞成debug可调试版本? 查看可执行程序存储了哪些内存空间:   调试语法:

    2024年02月13日
    浏览(49)
  • 水比赛专用-蓝牙调试器

    做比赛的时候免不了要做一些页面方面的展示,亦或者一些遥控什么的方面的远程启动 ,常见的无线通信方式如蓝牙,wifi等是很多大学生竞赛中的常客,因此这里我就把我之前用的很熟的一款蓝牙调试器给分享下,同时也算是做个记录吧! 该调试器是某大佬做的,我只是应

    2024年02月01日
    浏览(41)
  • Linux——gdb调试器

    目录    前言: 二.gdb定义及指令: 如何查看该exe文件是否为Debug版本?两种方法: 三.gdb调试:         调试指令1:l指令(小写L) run指令:运行程序,相当于VS中的直接运行不调试——可简化输入r  break指令:设置断点——指令可简化输入为b ​编辑  info b指令:查看整个代码

    2024年02月07日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包