-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathArduinoXVC.ino
348 lines (300 loc) · 12.3 KB
/
ArduinoXVC.ino
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
/*!***************************************************************************
* @file
* ArduinoXVC.ino
*
* Arduino UNO/Leonardo implementation of the XVC Server documented at
* https://github.com/Xilinx/XilinxVirtualCable.
* Requires an ethernet-to-serial bridge in order to work with programming
* tools, e.g. https://github.com/pyserial/pyserial/blob/master/examples/
* tcp_serial_redirect.py.
*
* @copyright
* Copyright 2019 inselc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may
* obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*****************************************************************************/
/*- Symbolic Constants ------------------------------------------------------*/
/*! Open Drain output type */
#define OPENDRAIN INPUT
/*! Message buffer size */
#if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_LEONARDO)
#define DATA_BUF_SIZE 632
#elif defined(ARDUINO_AVR_MEGA)
#define DATA_BUF_SIZE 1468
#else
#define DATA_BUF_SIZE 128
#endif
#define DATA_VEC_LEN (DATA_BUF_SIZE * 8)
/*! Pin definitions for JTAG connection
* @{ */
#define TCK_PIN 2
#define TMS_PIN 3
#define TDI_PIN 4
#define TDO_PIN 5
/*! @} */
/*! Status LED */
#define LED_PIN 13
/*! Macro for min/max limit */
#define limit(x, l, h) ((x < l) ? l : ((x > h) ? h : x))
/*- Type definitions --------------------------------------------------------*/
/*!***************************************************************************
* @brief
* State coding for JTAG interface handler FSM
*****************************************************************************/
typedef enum {
/*! Start transmission */
EN_JTAG_STATE_START,
/*! Generate TCK rising edge */
EN_JTAG_STATE_RISINGEDGE,
/*! Generate TCK falling edge */
EN_JTAG_STATE_FALLINGEDGE,
/*! Transfer complete */
EN_JTAG_STATE_DONE
} teJtagState;
/*- Variables accessed by interrupts ----------------------------------------*/
/*! Timer 1 counter reload value */
volatile uint16_t uiTimer1Reload;
/*! Timer 2 counter reload value */
volatile uint8_t ucTimer2Reload;
/*! TMS data vector */
volatile uint8_t aucDataTMS[DATA_BUF_SIZE];
/*! TDI/TDO data vector */
volatile uint8_t aucDataTDx[DATA_BUF_SIZE];
/*! JTAG vector length (bits) */
volatile uint16_t uiDataLen;
/*! Bit index in both JTAG data vectors */
volatile uint16_t uiDataIndex;
/*! Interface handler state variable */
volatile teJtagState eState;
/*- Local Functions ---------------------------------------------------------*/
/*!***************************************************************************
* @brief
* Emulate Open Drain output
*
* @param[in] pin Same as pin for digitalWrite
* @param[in] value "HIGH" will turn pin High-Z, "LOW" will pull pin low
*****************************************************************************/
static void digitalWrite_OD(uint8_t pin, uint8_t value)
{
pinMode(pin, (value == LOW) ? OUTPUT : OPENDRAIN);
digitalWrite(pin, value);
}
/*!***************************************************************************
* @brief
* Set Timer period for TCK generation
*
* @param[in] period_us Requested period in us
* @return uint32_t Actual period in us
*****************************************************************************/
static uint32_t setTimer1Period(uint32_t period_us)
{
#define MAX_PERIOD_US 4194240UL /* 4194.24 ms */
#define MIN_PERIOD_US 1UL /* 100 us */
const uint8_t prescalerValues[] = {
0 /* 1 */,
3 /* 8 */,
6 /* 64 */,
8 /* 256 */,
10 /* 1024 */
};
uint8_t prescalerSel;
uint32_t reloadValue;
bool valid = false;
/* Limit TCK period range */
period_us = limit(period_us, MIN_PERIOD_US, MAX_PERIOD_US);
/* Calculate prescaler and reset value based on frequency in F_CPU */
for (prescalerSel = 0; prescalerSel < 5; ++prescalerSel)
{
reloadValue = ((F_CPU / 1000000UL) * period_us) >> prescalerValues[prescalerSel];
if ((reloadValue > 0UL) && (reloadValue < 65535UL))
{
/* Found possible configuration */
valid = true;
break;
}
}
if (valid)
{
uiTimer1Reload = 65535 - reloadValue;
/* Critical section - disable Timer 1 interrupts */
TIMSK1 &= ~(1 << TOIE1);
TCCR1A = 0; /* Normal mode */
TCCR1B = (prescalerSel + 1) & 0x07; /* Prescaler */
TCNT1 = uiTimer1Reload; /* Preload counter value */
/* End of critical section - reenable Timer 1 interrupts */
TIMSK1 |= 1 << TOIE1;
}
return ((1UL << prescalerValues[prescalerSel]) * (65535UL - uiTimer1Reload)) / (F_CPU / 1000000UL);
#undef MAX_PERIOD_US
#undef MIN_PERIOD_US
}
/*!***************************************************************************
* @brief
* Read bit value from multibyte vector
*
* @param[in] *vector Bit vector
* @param[in] bit Bit index (0-indexed)
* @return bool Bit value
*****************************************************************************/
static bool getBitFromVector(const uint8_t* vector, uint16_t bit)
{
bool value = vector[bit >> 3] & (1 << (bit & 0x7));
return value;
}
/*!***************************************************************************
* @brief
* Set bit value in multibyte vector
*
* @param[inout] *vector Bit vector
* @param[in] bit Bit index (0-indexed)
* @param[in] value New bit value
*****************************************************************************/
static void setBitInVector(uint8_t* vector, uint16_t bit, bool value)
{
if (value)
{
vector[bit >> 3] |= 1 << (bit & 0x7);
}
else
{
vector[bit >> 3] &= ~(1 << (bit & 0x7));
}
}
/*!***************************************************************************
* @brief
* Setup routine
*****************************************************************************/
void setup()
{
/* Start serial port at 115200 baud, no parity, 8 bit words and 1 stop bit */
Serial.begin(115200);
/* Initialise digital GPIO pins */
pinMode(TMS_PIN, OPENDRAIN);
pinMode(TCK_PIN, OPENDRAIN);
pinMode(TDO_PIN, OPENDRAIN);
pinMode(TDI_PIN, INPUT);
pinMode(LED_PIN, OUTPUT);
/* Initialise state machine */
eState = EN_JTAG_STATE_DONE;
uiDataLen = 0;
uiDataIndex = 0;
memset((uint8_t*)aucDataTMS, 0, sizeof(aucDataTMS));
memset((uint8_t*)aucDataTDx, 0, sizeof(aucDataTDx));
/* Initialise TCK timer */
setTimer1Period(5UL);
}
/*!***************************************************************************
* @brief
* Background program loop
*****************************************************************************/
void loop()
{
String cmd = Serial.readStringUntil(':');
if (cmd.compareTo("getinfo") == 0)
{
/* Get protocol version and max. vector length */
Serial.print("xvcServer_v1.0:");
Serial.print(DATA_BUF_SIZE, DEC);
Serial.print("0\n");
}
else if (cmd.compareTo("settck") == 0)
{
/* Set TCK period (in ns) */
uint32_t period_req;
uint32_t period_act;
Serial.readBytes((uint8_t*)&period_req, 4);
/* Period must be twice as fast for rising and falling edges */
period_act = setTimer1Period(period_req / 2000UL) * 500UL;
Serial.write((const uint8_t*)&period_act, 4);
}
else if (cmd.compareTo("shift") == 0)
{
/* Shift data in/out of the device */
uint32_t num;
uint32_t num_bytes;
Serial.readBytes((uint8_t*)&num, 4);
num_bytes = (num + 7) >> 3; /* (n + 7) / 8 */
if (num_bytes <= DATA_BUF_SIZE)
{
/* Prepare data and start transfer */
Serial.readBytes((uint8_t*)aucDataTMS, num_bytes);
Serial.readBytes((uint8_t*)aucDataTDx, num_bytes);
memset((uint8_t*)aucDataTDx + num_bytes, 0, DATA_BUF_SIZE - num_bytes - 1);
uiDataLen = num;
eState = EN_JTAG_STATE_START;
/* Wait until transfer is complete */
while (eState != EN_JTAG_STATE_DONE);
/* Reply with TDO vector */
Serial.write((const char*)aucDataTDx, num_bytes);
}
else
{
/* Error - shift out zero-bytes */
do {
Serial.write((uint8_t)'\0');
--num_bytes;
} while (num_bytes > 0);
}
}
}
/*!***************************************************************************
* @brief
* Handle JTAG data transfer synchronously to TCK in Timer 1 OVF interrupt
*****************************************************************************/
ISR(TIMER1_OVF_vect)
{
TCNT1 = uiTimer1Reload;
/* JTAG interface handler FSM */
switch (eState)
{
case EN_JTAG_STATE_START:
/* Start new transfer
* Set up initial TDI/TMS/TCK values */
uiDataIndex = 0;
digitalWrite_OD(TCK_PIN, LOW);
digitalWrite(LED_PIN, HIGH);
digitalWrite_OD(TMS_PIN, getBitFromVector((const uint8_t*)aucDataTMS, uiDataIndex));
digitalWrite_OD(TDI_PIN, getBitFromVector((const uint8_t*)aucDataTDx, uiDataIndex));
eState = EN_JTAG_STATE_RISINGEDGE;
break;
case EN_JTAG_STATE_RISINGEDGE:
/* Generate rising edge on TCK
* Shift in TDO */
if (uiDataIndex < uiDataLen)
{
setBitInVector((uint8_t*)aucDataTDx, uiDataIndex, digitalRead(TDO_PIN));
digitalWrite_OD(TCK_PIN, HIGH);
eState = EN_JTAG_STATE_FALLINGEDGE;
}
else
{
/* Shifted in last bit - transfer complete */
digitalWrite(LED_PIN, LOW);
eState = EN_JTAG_STATE_DONE;
}
break;
case EN_JTAG_STATE_FALLINGEDGE:
/* Generate falling edge on TCK
* Set up new TMS/TDI */
digitalWrite_OD(TCK_PIN, LOW);
++uiDataIndex;
if (uiDataIndex < uiDataLen)
{
digitalWrite_OD(TMS_PIN, getBitFromVector((const uint8_t*)aucDataTMS, uiDataIndex));
digitalWrite_OD(TDI_PIN, getBitFromVector((const uint8_t*)aucDataTDx, uiDataIndex));
}
eState = EN_JTAG_STATE_RISINGEDGE;
break;
case EN_JTAG_STATE_DONE:
default:
;
}
}