├── LICENSE ├── README.md └── jtag.ino /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Vadzim Dambrouski 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # xvc-esp8266 2 | Xilinx Virtual Cable Implementation based on ESP8266 3 | 4 | # Compiling 5 | 6 | Please set the CPU frequency to 160MHz and lwIP variant to "v2 Higher bandwidth" in Arduino IDE for better performance. 7 | 8 | # Pinout 9 | 10 | JTAG A, TCP port 2542 11 | 12 | |GPIO|JTAG| 13 | |-|-| 14 | |GPIO4|TCK| 15 | |GPIO5|TDO| 16 | |GPIO10|TDI| 17 | |GPIO9|TMS| 18 | 19 | JTAG B, TCP port 2543 20 | 21 | |GPIO|JTAG| 22 | |-|-| 23 | |GPIO14|TCK| 24 | |GPIO12|TDO| 25 | |GPIO13|TDI| 26 | |GPIO2|TMS| 27 | 28 | UART, TCP port 2544 29 | 30 | |GPIO|UART| 31 | |-|-| 32 | |TX0 (GPIO1)|TX| 33 | |RX0 (GPIO3)|RX| 34 | 35 | ## Note: 36 | Don't use GPIO pin 16. It's controlled by a different register and it's very slow to toggle, at least 2x slower than other GPIO pins. 37 | -------------------------------------------------------------------------------- /jtag.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | const char *wifiName = "network"; 4 | const char *wifiPass = "password"; 5 | 6 | template 10 | class JtagPort 11 | { 12 | public: 13 | static void begin() 14 | { 15 | pinMode(tck_pin, OUTPUT); 16 | pinMode(tdo_pin, INPUT); 17 | pinMode(tdi_pin, OUTPUT); 18 | pinMode(tms_pin, OUTPUT); 19 | GPOC = tck_pin_mask | 20 | tdi_pin_mask | 21 | tms_pin_mask; 22 | } 23 | 24 | static bool step(bool tms, bool tdi) 25 | { 26 | uint32_t value[2] = {tck_pin_mask, 0}; 27 | value[tms] |= tms_pin_mask; 28 | value[tdi] |= tdi_pin_mask; 29 | GPOC = value[0]; 30 | GPOS = value[1]; 31 | GPOS = tck_pin_mask; 32 | bool tdo = (GPI & tdo_pin_mask) != 0; 33 | return tdo; 34 | } 35 | 36 | static void shift(uint32_t bit_len, uint32_t byte_len, uint8_t *buffer) 37 | { 38 | uint8_t mask = 1; 39 | uint8_t tms_byte = *buffer; 40 | uint8_t tdi_byte = *(buffer + byte_len); 41 | uint8_t tdo_byte = 0; 42 | for (uint32_t bit_index = 0; bit_index < bit_len; bit_index++) 43 | { 44 | bool tms = (tms_byte & mask) != 0; 45 | bool tdi = (tdi_byte & mask) != 0; 46 | bool tdo = step(tms, tdi); 47 | if (tdo) 48 | tdo_byte |= mask; 49 | mask <<= 1; 50 | if (mask == 0) 51 | { 52 | *buffer++ = tdo_byte; 53 | mask = 1; 54 | tms_byte = *buffer; 55 | tdi_byte = *(buffer + byte_len); 56 | tdo_byte = 0; 57 | } 58 | } 59 | *buffer = tdo_byte; 60 | GPOC = tck_pin_mask; 61 | } 62 | 63 | private: 64 | static constexpr const uint32_t tck_pin_mask = (1 << tck_pin); 65 | static constexpr const uint32_t tdo_pin_mask = (1 << tdo_pin); 66 | static constexpr const uint32_t tdi_pin_mask = (1 << tdi_pin); 67 | static constexpr const uint32_t tms_pin_mask = (1 << tms_pin); 68 | }; 69 | 70 | template 71 | class JtagServer 72 | { 73 | enum class ProtocolState 74 | { 75 | WaitingCommand, 76 | GetInfoCommand, 77 | SetClockCommand, 78 | ShiftCommand, 79 | ShiftData, 80 | }; 81 | 82 | public: 83 | JtagServer(uint16_t port) : server(port), client() 84 | { 85 | server.setNoDelay(true); 86 | } 87 | 88 | void begin() 89 | { 90 | jtag_port::begin(); 91 | server.begin(); 92 | } 93 | 94 | void handle() 95 | { 96 | if (client.connected()) 97 | { 98 | if (client.available()) 99 | { 100 | size_t len = client.read(buffer + position, remaining); 101 | remaining -= len; 102 | position += len; 103 | if (remaining == 0) 104 | { 105 | next_state(); 106 | } 107 | } 108 | } 109 | else if (server.hasClient()) 110 | { 111 | client = server.available(); 112 | enter_waiting_command(); 113 | } 114 | } 115 | 116 | private: 117 | void enter_waiting_command() 118 | { 119 | state = ProtocolState::WaitingCommand; 120 | remaining = 2; 121 | position = 0; 122 | } 123 | 124 | void enter_error_state() 125 | { 126 | client.stop(); 127 | enter_waiting_command(); 128 | } 129 | 130 | void parse_command() 131 | { 132 | position = 0; 133 | if (memcmp(buffer, "ge", 2) == 0) 134 | { 135 | remaining = 6; 136 | state = ProtocolState::GetInfoCommand; 137 | } 138 | else if (memcmp(buffer, "se", 2) == 0) 139 | { 140 | remaining = 9; 141 | state = ProtocolState::SetClockCommand; 142 | } 143 | else if (memcmp(buffer, "sh", 2) == 0) 144 | { 145 | remaining = 8; 146 | state = ProtocolState::ShiftCommand; 147 | } 148 | else 149 | { 150 | enter_error_state(); 151 | } 152 | } 153 | 154 | void next_state() 155 | { 156 | switch (state) 157 | { 158 | case ProtocolState::WaitingCommand: 159 | parse_command(); 160 | break; 161 | case ProtocolState::GetInfoCommand: 162 | client.printf("xvcServer_v1.0:%u\n", max_buffer_size); 163 | enter_waiting_command(); 164 | break; 165 | case ProtocolState::SetClockCommand: 166 | client.write(buffer + 5, 4); 167 | enter_waiting_command(); 168 | break; 169 | case ProtocolState::ShiftCommand: 170 | bit_len = buffer[7]; 171 | bit_len = (bit_len << 8) | buffer[6]; 172 | bit_len = (bit_len << 8) | buffer[5]; 173 | bit_len = (bit_len << 8) | buffer[4]; 174 | byte_len = (bit_len + 7) / 8; 175 | if (byte_len <= max_buffer_size) 176 | { 177 | state = ProtocolState::ShiftData; 178 | remaining = byte_len * 2; 179 | position = 0; 180 | } 181 | else 182 | { 183 | enter_error_state(); 184 | } 185 | break; 186 | case ProtocolState::ShiftData: 187 | jtag_port::shift(bit_len, byte_len, buffer); 188 | client.write(buffer, byte_len); 189 | enter_waiting_command(); 190 | break; 191 | default: 192 | enter_error_state(); 193 | break; 194 | } 195 | } 196 | 197 | private: 198 | WiFiServer server; 199 | WiFiClient client; 200 | 201 | ProtocolState state; 202 | size_t remaining; 203 | size_t position; 204 | 205 | uint32_t bit_len; 206 | uint32_t byte_len; 207 | 208 | static constexpr size_t max_buffer_size = 16 * 1024; 209 | uint8_t buffer[max_buffer_size]; 210 | }; 211 | 212 | class UartServer 213 | { 214 | public: 215 | UartServer(uint16_t port) : server(port), client() 216 | { 217 | server.setNoDelay(true); 218 | } 219 | 220 | void begin() 221 | { 222 | Serial.begin(115200); 223 | Serial.setRxBufferSize(2048); 224 | server.begin(); 225 | } 226 | 227 | void handle() 228 | { 229 | if (client.connected()) 230 | { 231 | client.sendAvailable(Serial); 232 | Serial.sendAvailable(client); 233 | } 234 | else if (server.hasClient()) 235 | { 236 | client = server.available(); 237 | } 238 | } 239 | 240 | private: 241 | WiFiServer server; 242 | WiFiClient client; 243 | }; 244 | 245 | using JtagPortA = JtagPort<4, 5, 10, 9>; 246 | using JtagPortB = JtagPort<14, 12, 13, 2>; 247 | 248 | static JtagServer serverA(2542); 249 | static JtagServer serverB(2543); 250 | static UartServer serverC(2544); 251 | 252 | void setup() 253 | { 254 | WiFi.mode(WIFI_STA); 255 | WiFi.begin(wifiName, wifiPass); 256 | while (WiFi.status() != WL_CONNECTED) 257 | { 258 | delay(500); 259 | } 260 | serverA.begin(); 261 | serverB.begin(); 262 | serverC.begin(); 263 | } 264 | 265 | void loop() 266 | { 267 | serverA.handle(); 268 | serverB.handle(); 269 | serverC.handle(); 270 | } 271 | --------------------------------------------------------------------------------