forked from SpenceKonde/jtag2updi
-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathupdi_io_soft.cpp
283 lines (232 loc) · 9.33 KB
/
updi_io_soft.cpp
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
/*
* updi_io_soft.cpp
*
* Created: 11-08-2018 22:08:14
* Author: Cristian Balint <cristian dot balint at gmail dot com>
*/
// Includes (note: sys.h defines F_CPU, so it should be included before util/delay.h)
#include "sys.h"
#include "updi_io.h"
#include <avr/io.h>
#include <util/delay.h>
#if UPDI_IO_TYPE == 2
// Cycle timing (convert to float and add 0.5 to achieve round to nearest instead of truncate)
#define BITTIME (uint8_t) ( ((1.0 * F_CPU)/UPDI_BAUD) / 3 + 0.5 )
// Check
#if ( (2 * (F_CPU/UPDI_BAUD) / 3) > 254 )
# error Low baud rates are not supported - use higher UPDI_BAUD
#endif
#define ASM_MACROS \
".ifndef delay_macros \n"\
".set delay_macros, 1 \n"\
\
/* Delays 3n cycles, preserves carry flag */ \
".macro delay n \n"\
" ldi r18, \\n \n"\
"0: \n"\
" dec r18 \n"\
" brne 0b \n"\
".endm \n"\
\
/* Delays 3n cycles, clears carry flag */ \
".macro delay_cc n \n"\
" ldi r18, \\n \n"\
"0: \n"\
" subi r18, 1 \n"\
" brne 0b \n"\
".endm \n"\
\
".endif \n"
// Functions
/* Sends regular characters through the UPDI link */
#ifndef DISABLE_TARGET_TIMEOUT
uint8_t UPDI_io::get() {
// rx input
DDR(UPDI_PORT) &= ~(1 << UPDI_PIN);
// no pullup
PORT(UPDI_PORT) &= ~(1 << UPDI_PIN);
SYS::startTimer();
uint8_t c;
__asm volatile
(
" ldi %0, 128 \n\t" // init
// wait for start edge
"WaitStart: \n\t"
" ld r18,%a[timerflags] \n\t" // load the timer flags to r18. which isn't needed until later
" andi r18,%[targflag] \n\t" // AND with WAIT_FOR_TARGET
" brne EndByte \n\t" // If the product of the ANDI operation was 0, the BRNE will jump it out of here
" sbic %[uart_port], %[uart_pin] \n\t"
" rjmp WaitStart \n\t"
// all that stuff with the timer will add 4 cycles on a classic AVR and 3 on a XAVR.
// so thhe "scatter" introduced by this will be in the range 0~6 or 0~5 ias opposed to
// 0~2 like it was before.
// skew into middle of bit
" ldi r18, %[rxdelay] /2 + 6/3 \n\t" // 0.5 bit cycle delay -
"HBitDelay: \n\t"
" dec r18 \n\t"
" brne HBitDelay \n\t"
// 8 bits
"RxBLoop: \n\t"
" ldi r18, %[rxdelay] - 6/3 \n\t" // 1 bit cycle delay
"RxBDelay: \n\t"
" subi r18, 1 \n\t"
" brne RxBDelay \n\t"
" sbic %[uart_port], %[uart_pin] \n\t"
" sec \n\t"
" ror %0 \n\t"
" brcc RxBLoop \n\t"
// Wait approx. 2 bit times: skip to centre of 1st stop bit (ignore parity).
// The function returns approx 1.5 bit times before the 2nd stop bit completes
// to allow burst reads at high UPDI speeds on 8MHz chips.
// This time needs to be compensated in the Tx function.
" ldi r18, 2 * %[rxdelay] \n\t" // delay counter
"StopDelay: \n\t"
" dec r18 \n\t"
" brne StopDelay \n\t"
"EndByte: \n\t"
: "=r" (c)
: [uart_port] "i" (_SFR_IO_ADDR(PIN(UPDI_PORT))),
[uart_pin] "i" (UPDI_PIN),
[rxdelay] "i" (BITTIME),
[targflag] "M" (WAIT_FOR_TARGET),
[timerflags]"e" (_SFR_MEM_ADDR(TIMEOUT_REG))
: "r18"
);
// re-enable pull up
PORT(UPDI_PORT) |= (1 << UPDI_PIN);
SYS::stopTimer();
return c;
}
#else
//version without target timeout
uint8_t UPDI_io::get() {
// rx input
DDR(UPDI_PORT) &= ~(1 << UPDI_PIN);
// no pullup
PORT(UPDI_PORT) &= ~(1 << UPDI_PIN);
uint8_t c;
__asm volatile
(
" ldi %0, 128 \n\t" // init
// wait for start edge
"WaitStart: \n\t"
" sbic %[uart_port], %[uart_pin] \n\t"
" rjmp WaitStart \n\t"
// skew into middle of bit
" ldi r18, %[rxdelay] /2 + 6/3 \n\t" // 0.5 bit cycle delay -
"HBitDelay: \n\t"
" dec r18 \n\t"
" brne HBitDelay \n\t"
// 8 bits
"RxBLoop: \n\t"
" ldi r18, %[rxdelay] - 6/3 \n\t" // 1 bit cycle delay
"RxBDelay: \n\t"
" subi r18, 1 \n\t"
" brne RxBDelay \n\t"
" sbic %[uart_port], %[uart_pin] \n\t"
" sec \n\t"
" ror %0 \n\t"
" brcc RxBLoop \n\t"
// Wait approx. 2 bit times: skip to centre of 1st stop bit (ignore parity).
// The function returns approx 1.5 bit times before the 2nd stop bit completes
// to allow burst reads at high UPDI speeds on 8MHz chips.
// This time needs to be compensated in the Tx function.
" ldi r18, 2 * %[rxdelay] \n\t" // delay counter
"StopDelay: \n\t"
" dec r18 \n\t"
" brne StopDelay \n\t"
: "=r" (c)
: [uart_port] "i" (_SFR_IO_ADDR(PIN(UPDI_PORT))),
[uart_pin] "i" (UPDI_PIN),
[rxdelay] "i" (BITTIME)
: "r18"
);
// re-enable pull up
PORT(UPDI_PORT) |= (1 << UPDI_PIN);
return c;
}
#endif
uint8_t UPDI_io::put(char c) {
// tx enable
DDR(UPDI_PORT) |= (1 << UPDI_PIN);
__asm volatile
(
ASM_MACROS
" in r0, %[uart_port] \n\t" // port state
" ldi r19, 0x78 \n\t" // High nibble: bits counter (8); Low nibble: parity accumulator
// pre delay (stop bits from previous sent byte)
// Note that pre-delay and stop bit edge rise delay should add up to ~2x bit time
" delay %[txdelay] + (%[txdelay] + 1)/2 \n\t"
// start bit
" cbi %[uart_port], %[uart_pin] \n\t"
" breq TxLoop \n\t" // 2 cycle delay to equalize timing
// 8 bits
"TxLoop: \n\t"
" delay (%[txdelay] - 9/3) \n\t"
" bst %[ch], 0 \n\t" // load bit in T
" bld r0, %[uart_pin] \n\t" // store T bit in r0
" ror %[ch] \n\t" // shift right into carry
" sbci r19, 0x10 \n\t" // subtract carry (accumulate parity) and decrement bits counter
" nop \n\t" // adjust loop cycle count to 9
" bld %[ch], 7 \n\t" // store data back in bit 7 to leave argument unchanged after the 8 cycles
" out %[uart_port], r0 \n\t" // send bit out
" brcc TxLoop \n\t" // loop for each bit
// parity bit
" delay (%[txdelay] - 3/3) \n\t"
" bst r19, 0 \n\t" // extract accumulated parity
" bld r0, %[uart_pin] \n\t"
" out %[uart_port], r0 \n\t" // send bit out to serial link
// stop bits
" delay %[txdelay] \n\t"
" sbi %[uart_port], %[uart_pin] \n\t" // send bit out to serial link
// edge rise delay
// we must give some time for the stop bit edge to rise before changing the data pin to input
// this is because of possible parasitic capacitance affecting the UPDI line
// Note that pre-delay and stop bit edge rise delay should add up to ~2x bit time
" delay (%[txdelay] + 1)/2 \n\t"
:
: [uart_port] "i" (_SFR_IO_ADDR(PORT(UPDI_PORT))),
[uart_pin] "i" (UPDI_PIN),
[txdelay] "i" (BITTIME),
[ch] "r" (c)
: "r0","r18","r19"
);
// Ready for RX input
DDR(UPDI_PORT) &= ~(1 << UPDI_PIN);
return c;
}
static inline void send_break() {
// tx enable
DDR(UPDI_PORT) |= (1 << UPDI_PIN);
//
// 13 cycles = 24.60ms
//
// low 12 cycle
PORT(UPDI_PORT) &= ~(1 << UPDI_PIN);
_delay_us(2048*11);
// high 1 cycle
PORT(UPDI_PORT) |= (1 << UPDI_PIN);
_delay_us(2048);
// RX enable
DDR(UPDI_PORT) &= ~(1 << UPDI_PIN);
return;
}
/* Sends special sequences through the UPDI link */
uint8_t UPDI_io::put(ctrl c) {
switch (c) {
case double_break:
send_break();
send_break();
break;
case single_break:
send_break();
break;
case enable:
default:
break;
}
return 0;
}
void UPDI_io::init(void) {
}
#endif //__AVR_ATmega16__