概述
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
ESP8266 | JTAG系列 |
---|---|
第六天 | TDI的 |
第4天 | TDO公司 |
第7天 | TCK公司 |
第5天 | TMS系统 |
随意尝试不同的ESP8266开发板 - 大多数应该 只要解决任何问题。
接下来,使用Arduino IDE构建程序并将其写入ESP8266板。
最后,在 Vivado 中选择选项并提及ESP8266板。Add Xilinx Virtual Cable (XVC)
Hardware Manager
IP 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板一起使用。xc3sprog
openFPGALoader
这个项目可能是最便宜的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 = "";