├── .gitignore ├── ESP-32-Dev-Board.jpg ├── M5-Atom-pinout.webp ├── Makefile ├── README-Original.md ├── README.md ├── Screenshot_2021-08-14_21-21-21.png ├── arduino-cli.yaml ├── board_detect.py ├── esp32-wroom-wifi-devkit-v1_pinout.pdf ├── formatter.conf ├── picture.jpg ├── sketch.json ├── vivado_ila.png └── xvc-esp32 ├── credentials.h └── xvc-esp32.ino /.gitignore: -------------------------------------------------------------------------------- 1 | *.bin 2 | *.elf 3 | 4 | *credentials* 5 | -------------------------------------------------------------------------------- /ESP-32-Dev-Board.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kholia/xvc-esp32/5d7eb0550f77707c16dbff7968164edfd257065e/ESP-32-Dev-Board.jpg -------------------------------------------------------------------------------- /M5-Atom-pinout.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kholia/xvc-esp32/5d7eb0550f77707c16dbff7968164edfd257065e/M5-Atom-pinout.webp -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # https://github.com/arduino/arduino-cli/releases 2 | 3 | port := $(shell python3 board_detect.py 2>/dev/null) 4 | fqbn=esp32:esp32:esp32 5 | src=xvc-esp32 6 | 7 | default: 8 | arduino-cli compile --warnings=all --fqbn="${fqbn}" "${src}" 9 | 10 | upload: 11 | @# echo $(port) 12 | arduino-cli compile --fqbn="${fqbn}" "${src}" 13 | arduino-cli -v upload -p "${port}" --fqbn="${fqbn}" "${src}" 14 | 15 | install_platform: 16 | arduino-cli core update-index --config-file arduino-cli.yaml 17 | arduino-cli core install esp32:esp32 18 | 19 | deps: 20 | pip3 install pyserial 21 | # arduino-cli lib install "Etherkit Si5351" 22 | 23 | install_arduino_cli: 24 | curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh | BINDIR=~/.local/bin sh 25 | 26 | format: 27 | astyle --options="formatter.conf" xvc-esp32/xvc-esp32.ino 28 | -------------------------------------------------------------------------------- /README-Original.md: -------------------------------------------------------------------------------- 1 | # Xilinx Virtual Cable Server for ESP32 2 | 3 | ## 概要 4 | 5 | Xilinx製FPGAへの書き込みを行うためのプロトコル (XVC : Xilinx Virtual Cable) のESP32向け実装です。 6 | 7 | Derek Mulcahy氏の Raspberry Pi向け実装(https://github.com/derekmulcahy/xvcpi) をESP32向けに移植しています。 8 | 9 | ESP32を対象FPGAのJTAGピン (TDI, TDO, TMS, TCK) に接続することにより、WiFi経由でXilinx製のツール (Vivadoなど) からFPGAのJTAGポートにアクセスすることができます。 10 | 11 | ![M5Atom](picture.jpg) 12 | 13 | 写真の例では、M5Stack製のESP32モジュール [M5Atom Lite](https://docs.m5stack.com/#/en/core/atom_lite) とZynq XC7Z010を接続しています。 14 | 15 | この状態で、VivadoのHardware Managerから `Add Virtual Cable` でVirtual Cableとして追加して接続すると、通常のJTAGアダプタと同様にILAの波形観測などを行えます。 16 | 17 | ![ILA](vivado_ila.png) 18 | 19 | ## 使い方 20 | 21 | `xvc-esp32.ino` を使いたいESP32ボード向けにArduino IDEでビルドして書き込むだけです。 22 | 23 | その際、現状のスケッチではWiFiの接続先をコードに埋め込んでいますので、適宜修正してください。 24 | 25 | ```c++ 26 | static const char* MY_SSID = "ssid"; 27 | static const char* MY_PASSPHRASE = "wifi_passphrase"; 28 | ``` 29 | 30 | また、現状は M5Atom用のピン定義になっていますので、使いたいESP32ボード向けにピン定義を変更してください。 31 | 32 | ``` 33 | /* GPIO numbers for each signal. Negative values are invalid */ 34 | /* Note that currently only supports GPIOs below 32 to improve performance */ 35 | static constexpr const int tms_gpio = 22; 36 | static constexpr const int tck_gpio = 19; 37 | static constexpr const int tdo_gpio = 21; 38 | static constexpr const int tdi_gpio = 25; 39 | ``` 40 | 41 | コメントにあるように、現状は高速化のためにGPIO0~31までのみ使用可能です。 42 | 43 | 44 | ## ライセンス 45 | 46 | 大本の AVNET による実装が CC0 1.0 だったので、Derek Mulcahy氏の Raspberry Pi向け実装も CC0 1.0となっています。 47 | 48 | 従って、本実装も (日本国内でのCC0の有効性はともかく) CC0 1.0 ということにします。 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### Xilinx Virtual Cable Server for ESP32 2 | 3 | #### Overview 4 | 5 | This is an implementation of the `XVC - Xilinx Virtual Cable` protocol for 6 | writing to Xilinx FPGAs from an ESP32. 7 | 8 | Main Author: Kenta IDA (https://github.com/ciniml) 9 | 10 | Author 2: Dhiru Kholia (remove gpio optimizations, fix `jtag_xfer` function to mimic `xvcpi`, fix portability problems) 11 | 12 | Upstream URL: https://github.com/ciniml/xvc-esp32 13 | 14 | This is a port of Derek Mulcahy's XVC implementation for Raspberry Pi 15 | (https://github.com/derekmulcahy/xvcpi) to ESP32. 16 | 17 | By connecting the ESP32 to the JTAG pins (TDI, TDO, TMS, TCK) of the target 18 | FPGA, you can access the JTAG port of the FPGA from a Xilinx tool (Vivado, 19 | etc.) via WiFi. 20 | 21 | ![M5Atom](picture.jpg) 22 | 23 | In this example photo, the M5Stack ESP32 module [M5Atom Lite](https://docs.m5stack.com/#/en/core/atom_lite) is connected to a Zynq XC7Z010 FPGA (on EBAZ4205 'Development' Board). 24 | 25 | In this state, if you add and connect as a Virtual Cable from Vivado Hardware Manager with `Add Virtual Cable`, you can observe the waveform of ILA in the same way as a normal JTAG adapter. 26 | 27 | ![ILA](vivado_ila.png) 28 | 29 | #### How to use 30 | 31 | Change the WiFi credentials in the `credentials.h` file. 32 | 33 | Note: The default pin mappings for the common, low-cost `ESP32 WROOM DevKit v1` 34 | development board are: 35 | 36 | ``` 37 | TDI = D25, TDO = D21, TCK = D19, TMS = D22 38 | ``` 39 | 40 | Feel free to experiment with different ESP32 development boards - most should 41 | just work with any problems. 42 | 43 | ![Common ESP32 Dev Board](./ESP-32-Dev-Board.jpg) 44 | 45 | | ESP32 Dev Board | JTAG | 46 | |-----------------|------| 47 | | 25 (D25) | TDI | 48 | | 21 (D21) | TDO | 49 | | 19 (D19) | TCK | 50 | | 22 (D22) | TMS | 51 | 52 | Finally, build the program using Arduino IDE and write it to the ESP32 board. 53 | 54 | #### How to use (Linux version) 55 | 56 | ``` 57 | make install_arduino_cli 58 | make install_platform 59 | make deps 60 | make upload 61 | ``` 62 | 63 | #### Rough Performance Stats ("Speed") 64 | 65 | Writing a small bitstream (Blinky with `PS7 IP` + Ethernet stuff) sized around 66 | 400 KiB using Vivado 2021.1 takes around 10 seconds. 67 | 68 | Kenta-San's version may offer faster performance, perhaps at the cost of 69 | losing some portability across different ESP32 boards. 70 | 71 | ### Tips 72 | 73 | If you see the `End of startup status: LOW` error message in Vivado, check the 74 | FPGA power supply's voltage and current ratings. 75 | 76 | If cost and ease-of-availability are the driving constraints (at the cost of 77 | speed), then this project can suffice. If higher programming speed is a 78 | requirement, I recommend using `xc3sprog` with an FT2232H board. 79 | 80 | ### Related Ideas / Projects 81 | 82 | - https://github.com/kholia/xvcpi 83 | - https://github.com/kholia/xvc-pico 84 | - https://github.com/kholia/xvc-esp8266 85 | - https://github.com/kholia/Colorlight-5A-75B 86 | - https://github.com/fusesoc/blinky#ebaz4205-development-board 87 | 88 | ## License 89 | 90 | CC0 1.0 Universal (CC0 1.0) - Public Domain Dedication 91 | 92 | https://creativecommons.org/publicdomain/zero/1.0/ 93 | 94 | Note: See `README-Original.md` for full licensing information. 95 | -------------------------------------------------------------------------------- /Screenshot_2021-08-14_21-21-21.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kholia/xvc-esp32/5d7eb0550f77707c16dbff7968164edfd257065e/Screenshot_2021-08-14_21-21-21.png -------------------------------------------------------------------------------- /arduino-cli.yaml: -------------------------------------------------------------------------------- 1 | board_manager: 2 | additional_urls: 3 | - https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_dev_index.json 4 | -------------------------------------------------------------------------------- /board_detect.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # pip3 install pyserial 4 | 5 | import serial.tools.list_ports 6 | import serial 7 | 8 | a = serial.tools.list_ports.comports() 9 | for w in a: 10 | # https://devicehunt.com/view/type/usb/vendor/1A86/device/7523 11 | # https://devicehunt.com/view/type/usb/vendor/10C4/device/EA60 12 | if "1a86" in w.hwid.lower() or "10c4" in w.hwid.lower(): 13 | print(w.device) 14 | -------------------------------------------------------------------------------- /esp32-wroom-wifi-devkit-v1_pinout.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kholia/xvc-esp32/5d7eb0550f77707c16dbff7968164edfd257065e/esp32-wroom-wifi-devkit-v1_pinout.pdf -------------------------------------------------------------------------------- /formatter.conf: -------------------------------------------------------------------------------- 1 | # This configuration file contains a selection of the available options provided by the formatting tool "Artistic Style" 2 | # http://astyle.sourceforge.net/astyle.html 3 | # 4 | # If you wish to change them, don't edit this file. 5 | # Instead, copy it in the same folder of file "preferences.txt" and modify the copy. This way, you won't lose your custom formatter settings when upgrading the IDE 6 | # If you don't know where file preferences.txt is stored, open the IDE, File -> Preferences and you'll find a link 7 | 8 | mode=c 9 | 10 | # 2 spaces indentation 11 | indent=spaces=2 12 | 13 | # also indent macros 14 | indent-preprocessor 15 | 16 | # indent classes, switches (and cases), comments starting at column 1 17 | indent-classes 18 | indent-switches 19 | indent-cases 20 | indent-col1-comments 21 | 22 | # put a space around operators 23 | pad-oper 24 | 25 | # put a space after if/for/while 26 | pad-header 27 | 28 | # if you like one-liners, keep them 29 | keep-one-line-statements 30 | 31 | remove-comment-prefix 32 | -------------------------------------------------------------------------------- /picture.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kholia/xvc-esp32/5d7eb0550f77707c16dbff7968164edfd257065e/picture.jpg -------------------------------------------------------------------------------- /sketch.json: -------------------------------------------------------------------------------- 1 | { 2 | "cpu": { 3 | "fqbn": "m5stack:esp32:m5stack-atom", 4 | "port": "/dev/ttyUSB0" 5 | } 6 | } -------------------------------------------------------------------------------- /vivado_ila.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kholia/xvc-esp32/5d7eb0550f77707c16dbff7968164edfd257065e/vivado_ila.png -------------------------------------------------------------------------------- /xvc-esp32/credentials.h: -------------------------------------------------------------------------------- 1 | static const char* MY_SSID = "ssid"; 2 | static const char* MY_PASSPHRASE = "wifi_passphrase"; 3 | -------------------------------------------------------------------------------- /xvc-esp32/xvc-esp32.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Description: Xilinx Virtual Cable Server for ESP32 3 | 4 | See Licensing information at End of File. 5 | */ 6 | 7 | // #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | // #define USE_STATIC_IP // Un-comment this line to enable the static ip address 19 | static IPAddress ip(192, 168, 1, 12); // Our address 20 | static IPAddress gateway(192, 168, 1, 1); 21 | static IPAddress netmask(255, 255, 255, 0); 22 | 23 | 24 | // WiFi credentials 25 | #include "credentials.h" 26 | 27 | #define ERROR_JTAG_INIT_FAILED -1 28 | #define ERROR_OK 1 29 | 30 | // GPIO numbers for each signal. Negative values are invalid. 31 | // Note that currently only supports GPIOs below 32 to improve performance. 32 | static constexpr const int tms_gpio = 22; 33 | static constexpr const int tck_gpio = 19; 34 | static constexpr const int tdo_gpio = 21; 35 | static constexpr const int tdi_gpio = 25; 36 | 37 | // Transition delay coefficients 38 | static const unsigned int jtag_delay = 10; // NOTE! 39 | 40 | static std::uint32_t jtag_xfer(std::uint_fast8_t n, std::uint32_t tms, std::uint32_t tdi) 41 | { 42 | std::uint32_t tdo = 0; 43 | for (int i = 0; i < n; i++) { 44 | jtag_write(0, tms & 1, tdi & 1); 45 | tdo |= jtag_read() << i; 46 | jtag_write(1, tms & 1, tdi & 1); 47 | tms >>= 1; 48 | tdi >>= 1; 49 | } 50 | return tdo; 51 | } 52 | 53 | static bool jtag_read(void) 54 | { 55 | return digitalRead(tdo_gpio) & 1; 56 | } 57 | 58 | static void jtag_write(std::uint_fast8_t tck, std::uint_fast8_t tms, std::uint_fast8_t tdi) 59 | { 60 | digitalWrite(tck_gpio, tck); 61 | digitalWrite(tms_gpio, tms); 62 | digitalWrite(tdi_gpio, tdi); 63 | 64 | for (std::uint32_t i = 0; i < jtag_delay; i++) 65 | asm volatile ("nop"); 66 | } 67 | 68 | static int jtag_init(void) 69 | { 70 | pinMode(tdo_gpio, INPUT); 71 | pinMode(tdi_gpio, OUTPUT); 72 | pinMode(tck_gpio, OUTPUT); 73 | pinMode(tms_gpio, OUTPUT); 74 | 75 | digitalWrite(tdi_gpio, 0); 76 | digitalWrite(tck_gpio, 0); 77 | digitalWrite(tms_gpio, 1); 78 | 79 | return ERROR_OK; 80 | } 81 | 82 | static int sread(int fd, void* target, int len) { 83 | std::uint8_t *t = reinterpret_cast(target); 84 | while (len) { 85 | int r = read(fd, t, len); 86 | if (r <= 0) 87 | return r; 88 | t += r; 89 | len -= r; 90 | } 91 | return 1; 92 | } 93 | 94 | static constexpr const char* TAG = "XVC"; 95 | 96 | struct Socket 97 | { 98 | int fd; 99 | Socket() : fd(-1) {} 100 | Socket(int fd) : fd(fd) {} 101 | Socket(int domain, int family, int protocol) 102 | { 103 | this->fd = socket(domain, family, protocol); 104 | } 105 | Socket(const Socket&) = delete; 106 | Socket(Socket&& rhs) : fd(rhs.fd) { 107 | rhs.fd = -1; 108 | } 109 | ~Socket() { 110 | this->release(); 111 | } 112 | void release() { 113 | if (this->is_valid()) { 114 | closesocket(this->fd); 115 | this->fd = -1; 116 | } 117 | } 118 | int get() const { 119 | return this->fd; 120 | } 121 | 122 | Socket& operator=(Socket&& rhs) { 123 | this->fd = rhs.fd; 124 | rhs.fd = -1; 125 | return *this; 126 | } 127 | bool is_valid() const { 128 | return this->fd > 0; 129 | } 130 | operator int() const { 131 | return this->get(); 132 | } 133 | }; 134 | 135 | class XvcServer 136 | { 137 | private: 138 | Socket listen_socket; 139 | Socket client_socket; 140 | unsigned char buffer[2048], result[1024]; 141 | public: 142 | XvcServer(std::uint16_t port) { 143 | Socket sock(AF_INET, SOCK_STREAM, 0); 144 | { 145 | int value = 1; 146 | setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value)); 147 | } 148 | 149 | sockaddr_in address; 150 | address.sin_addr.s_addr = INADDR_ANY; 151 | address.sin_port = htons(port); 152 | address.sin_family = AF_INET; 153 | 154 | if (bind(sock, reinterpret_cast(&address), sizeof(address)) < 0) { 155 | ESP_LOGE(TAG, "Failed to bind socket."); 156 | } 157 | if (listen(sock, 0) < 0) { 158 | ESP_LOGE(TAG, "Failed to listen the socket."); 159 | } 160 | 161 | ESP_LOGI(TAG, "Begin XVC Server. port=%d", port); 162 | this->listen_socket = std::move(sock); 163 | } 164 | XvcServer(const XvcServer&) = delete; 165 | 166 | bool wait_connection() 167 | { 168 | fd_set conn; 169 | int maxfd = this->listen_socket.get(); 170 | 171 | FD_ZERO(&conn); 172 | FD_SET(this->listen_socket.get(), &conn); 173 | 174 | fd_set read = conn, except = conn; 175 | int fd; 176 | 177 | if (select(maxfd + 1, &read, 0, &except, 0) < 0) { 178 | ESP_LOGE(TAG, "select"); 179 | return false; 180 | } 181 | 182 | for (fd = 0; fd <= maxfd; ++fd) { 183 | if (FD_ISSET(fd, &read)) { 184 | if (fd == this->listen_socket.get()) { 185 | int newfd; 186 | sockaddr_in address; 187 | socklen_t nsize = sizeof(address); 188 | newfd = accept(this->listen_socket.get(), reinterpret_cast(&address), &nsize); 189 | 190 | ESP_LOGI(TAG, "connection accepted - fd %d\n", newfd); 191 | if (newfd < 0) { 192 | ESP_LOGE(TAG, "accept returned an error."); 193 | } else { 194 | if (newfd > maxfd) { 195 | maxfd = newfd; 196 | } 197 | FD_SET(newfd, &conn); 198 | this->client_socket = Socket(newfd); 199 | return true; 200 | } 201 | } 202 | 203 | } 204 | } 205 | 206 | return false; 207 | } 208 | 209 | bool handle_data() 210 | { 211 | const char xvcInfo[] = "xvcServer_v1.0:2048\n"; 212 | int fd = this->client_socket.get(); 213 | 214 | std::uint8_t cmd[16]; 215 | std::memset(cmd, 0, 16); 216 | 217 | if (sread(fd, cmd, 2) != 1) 218 | return false; 219 | 220 | if (memcmp(cmd, "ge", 2) == 0) { 221 | if (sread(fd, cmd, 6) != 1) 222 | return 1; 223 | memcpy(result, xvcInfo, strlen(xvcInfo)); 224 | if (write(fd, result, strlen(xvcInfo)) != strlen(xvcInfo)) { 225 | ESP_LOGE(TAG, "write"); 226 | return 1; 227 | } 228 | ESP_LOGD(TAG, "%u : Received command: 'getinfo'\n", (int)time(NULL)); 229 | ESP_LOGD(TAG, "\t Replied with %s\n", xvcInfo); 230 | return true; 231 | } else if (memcmp(cmd, "se", 2) == 0) { 232 | if (sread(fd, cmd, 9) != 1) 233 | return 1; 234 | memcpy(result, cmd + 5, 4); 235 | if (write(fd, result, 4) != 4) { 236 | ESP_LOGE(TAG, "write"); 237 | return 1; 238 | } 239 | ESP_LOGD(TAG, "%u : Received command: 'settck'\n", (int)time(NULL)); 240 | ESP_LOGD(TAG, "\t Replied with '%.*s'\n\n", 4, cmd + 5); 241 | return true; 242 | } else if (memcmp(cmd, "sh", 2) == 0) { 243 | if (sread(fd, cmd, 4) != 1) 244 | return false; 245 | ESP_LOGD(TAG, "%u : Received command: 'shift'\n", (int)time(NULL)); 246 | } else { 247 | 248 | ESP_LOGE(TAG, "invalid cmd '%s'\n", cmd); 249 | return false; 250 | } 251 | 252 | int len; 253 | if (sread(fd, &len, 4) != 1) { 254 | ESP_LOGE(TAG, "reading length failed\n"); 255 | return false; 256 | } 257 | 258 | int nr_bytes = (len + 7) / 8; 259 | if (nr_bytes * 2 > sizeof(buffer)) { 260 | ESP_LOGE(TAG, "buffer size exceeded\n"); 261 | return false; 262 | } 263 | 264 | if (sread(fd, buffer, nr_bytes * 2) != 1) { 265 | ESP_LOGE(TAG, "reading data failed\n"); 266 | return false; 267 | } 268 | memset(result, 0, nr_bytes); 269 | 270 | ESP_LOGD(TAG, "Number of Bits : %d\n", len); 271 | ESP_LOGD(TAG, "Number of Bytes : %d \n", nr_bytes); 272 | 273 | jtag_write(0, 1, 1); 274 | 275 | int bytesLeft = nr_bytes; 276 | int bitsLeft = len; 277 | int byteIndex = 0; 278 | uint32_t tdi, tms, tdo; 279 | 280 | while (bytesLeft > 0) { 281 | tms = 0; 282 | tdi = 0; 283 | tdo = 0; 284 | if (bytesLeft >= 4) { 285 | memcpy(&tms, &buffer[byteIndex], 4); 286 | memcpy(&tdi, &buffer[byteIndex + nr_bytes], 4); 287 | 288 | tdo = jtag_xfer(32, tms, tdi); 289 | memcpy(&result[byteIndex], &tdo, 4); 290 | 291 | bytesLeft -= 4; 292 | bitsLeft -= 32; 293 | byteIndex += 4; 294 | 295 | ESP_LOGD(TAG, "LEN : 0x%08x\n", 32); 296 | ESP_LOGD(TAG, "TMS : 0x%08x\n", tms); 297 | ESP_LOGD(TAG, "TDI : 0x%08x\n", tdi); 298 | ESP_LOGD(TAG, "TDO : 0x%08x\n", tdo); 299 | } else { 300 | memcpy(&tms, &buffer[byteIndex], bytesLeft); 301 | memcpy(&tdi, &buffer[byteIndex + nr_bytes], bytesLeft); 302 | 303 | tdo = jtag_xfer(bitsLeft, tms, tdi); 304 | memcpy(&result[byteIndex], &tdo, bytesLeft); 305 | 306 | bytesLeft = 0; 307 | 308 | ESP_LOGD(TAG, "LEN : 0x%08x\n", bitsLeft); 309 | ESP_LOGD(TAG, "TMS : 0x%08x\n", tms); 310 | ESP_LOGD(TAG, "TDI : 0x%08x\n", tdi); 311 | ESP_LOGD(TAG, "TDO : 0x%08x\n", tdo); 312 | break; 313 | } 314 | } 315 | 316 | jtag_write(0, 1, 0); 317 | 318 | if (write(fd, result, nr_bytes) != nr_bytes) { 319 | ESP_LOGE(TAG, "write"); 320 | return false; 321 | } 322 | 323 | return true; 324 | } 325 | 326 | void run() 327 | { 328 | if (this->client_socket.is_valid()) { 329 | if (!this->handle_data()) { 330 | this->client_socket.release(); 331 | } 332 | } 333 | else { 334 | if (this->wait_connection()) { 335 | // Nothing to do. 336 | } 337 | } 338 | } 339 | }; 340 | 341 | static void serialTask(void*) 342 | { 343 | std::size_t pendingBytesH2T = 0; 344 | std::size_t bytesWrittenH2T = 0; 345 | static std::uint8_t h2tBuffer[128]; 346 | std::size_t pendingBytesT2H = 0; 347 | std::size_t bytesWrittenT2H = 0; 348 | static std::uint8_t t2hBuffer[128]; 349 | 350 | while (true) { 351 | if (Serial.available()) { 352 | Serial1.write(Serial.read()); 353 | } 354 | else if (Serial1.available()) { 355 | Serial.write(Serial1.read()); 356 | } 357 | else { 358 | vTaskDelay(pdMS_TO_TICKS(1)); 359 | } 360 | } 361 | } 362 | 363 | void setup() 364 | { 365 | jtag_init(); 366 | #ifdef USE_STATIC_IP 367 | WiFi.config(ip, gateway, netmask); 368 | #endif 369 | WiFi.begin(MY_SSID, MY_PASSPHRASE); 370 | Serial.begin(115200); 371 | Serial1.begin(115200, SERIAL_8N1, 33, 23); 372 | 373 | TaskHandle_t handle; 374 | xTaskCreatePinnedToCore(serialTask, "serial", 4096, nullptr, 1, &handle, APP_CPU_NUM); 375 | } 376 | 377 | enum class AppState 378 | { 379 | WaitingAPConnection, 380 | APConnected, 381 | ClientConnected, 382 | }; 383 | 384 | static std::unique_ptr server; 385 | 386 | void loop() 387 | { 388 | static AppState state = AppState::WaitingAPConnection; 389 | switch (state) { 390 | case AppState::WaitingAPConnection: { 391 | if (WiFi.isConnected()) { 392 | ESP_LOGI(TAG, "WiFi connection ready. IP: %s", WiFi.localIP().toString().c_str()); 393 | state = AppState::APConnected; 394 | } 395 | break; 396 | } 397 | case AppState::APConnected: { 398 | if (!WiFi.isConnected()) { 399 | ESP_LOGI(TAG, "disconnected from WiFi."); 400 | server.release(); 401 | state = AppState::WaitingAPConnection; 402 | break; 403 | } 404 | 405 | if (!server) { 406 | server.reset(new XvcServer(2542)); 407 | } 408 | if (server) { 409 | server->run(); 410 | } 411 | break; 412 | } 413 | } 414 | } 415 | 416 | 417 | /* 418 | This work, "xvc-esp32.ino", is a derivative of "xvcpi.c" (https://github.com/derekmulcahy/xvcpi) 419 | by Derek Mulcahy. 420 | 421 | "xvc-esp32.ino" is licensed under CC0 1.0 Universal (http://creativecommons.org/publicdomain/zero/1.0/) 422 | by Kenta IDA (fuga@fugafuga.org) 423 | 424 | The original license information of "xvcpi.c" is attached below. 425 | */ 426 | 427 | /* 428 | This work, "xvcpi.c", is a derivative of "xvcServer.c" (https://github.com/Xilinx/XilinxVirtualCable) 429 | by Avnet and is used by Xilinx for XAPP1251. 430 | 431 | "xvcServer.c" is licensed under CC0 1.0 Universal (http://creativecommons.org/publicdomain/zero/1.0/) 432 | by Avnet and is used by Xilinx for XAPP1251. 433 | 434 | "xvcServer.c", is a derivative of "xvcd.c" (https://github.com/tmbinc/xvcd) 435 | by tmbinc, used under CC0 1.0 Universal (http://creativecommons.org/publicdomain/zero/1.0/). 436 | 437 | Portions of "xvcpi.c" are derived from OpenOCD (http://openocd.org) 438 | 439 | "xvcpi.c" is licensed under CC0 1.0 Universal (http://creativecommons.org/publicdomain/zero/1.0/) 440 | by Derek Mulcahy. 441 | */ 442 | --------------------------------------------------------------------------------