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
可以根据需求自己制作板子,或者使用现成的开发板。
测试完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的支持。文章来源:https://www.toymoban.com/news/detail-853591.html
- 首先,根据系统版本,安装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模板网!