libxr  1.0
Want to be the best embedded framework
Loading...
Searching...
No Matches
jtag_general_gpio.hpp
1#pragma once
2
3#include <cstdint>
4
5#include "gpio.hpp"
6#include "jtag.hpp"
7#include "libxr_def.hpp"
8#include "libxr_mem.hpp"
9
10namespace LibXR::Debug
11{
25template <typename TckGpioType, typename TmsGpioType, typename TdiGpioType,
26 typename TdoGpioType>
27class JtagGeneralGPIO final : public Jtag
28{
29 static constexpr uint32_t MIN_HZ = 10'000u;
30 static constexpr uint32_t MAX_HZ = 100'000'000u;
31
32 static constexpr uint32_t NS_PER_SEC = 1'000'000'000u;
33 static constexpr uint32_t LOOPS_SCALE = 1000u;
34 static constexpr uint32_t CEIL_BIAS = LOOPS_SCALE - 1u;
35
36 static constexpr uint32_t DEFAULT_CLOCK_HZ = 500'000u;
37 static constexpr uint32_t RESET_CYCLES = 5u;
38
39 static constexpr uint32_t HALF_PERIOD_NS_MAX =
40 (NS_PER_SEC + (2u * MIN_HZ) - 1u) / (2u * MIN_HZ);
41 static constexpr uint32_t MAX_LOOPS_PER_US =
42 (UINT32_MAX - CEIL_BIAS) / HALF_PERIOD_NS_MAX;
43
44 public:
54 explicit JtagGeneralGPIO(TckGpioType& tck, TmsGpioType& tms, TdiGpioType& tdi,
55 TdoGpioType& tdo, uint32_t loops_per_us,
56 uint32_t default_hz = DEFAULT_CLOCK_HZ)
57 : tck_(tck), tms_(tms), tdi_(tdi), tdo_(tdo), loops_per_us_(loops_per_us)
58 {
59 if (loops_per_us_ > MAX_LOOPS_PER_US)
60 {
61 loops_per_us_ = MAX_LOOPS_PER_US;
62 }
63
64 tck_.SetConfig({TckGpioType::Direction::OUTPUT_PUSH_PULL, TckGpioType::Pull::NONE});
65 tms_.SetConfig({TmsGpioType::Direction::OUTPUT_PUSH_PULL, TmsGpioType::Pull::UP});
66 tdi_.SetConfig({TdiGpioType::Direction::OUTPUT_PUSH_PULL, TdiGpioType::Pull::NONE});
67 tdo_.SetConfig({TdoGpioType::Direction::INPUT, TdoGpioType::Pull::NONE});
68
69 tck_.Write(false);
70 tms_.Write(true);
71 tdi_.Write(false);
72
73 (void)SetClockHz(default_hz);
74 (void)ResetTap();
75 }
76
77 ~JtagGeneralGPIO() override = default;
78
79 JtagGeneralGPIO(const JtagGeneralGPIO&) = delete;
80 JtagGeneralGPIO& operator=(const JtagGeneralGPIO&) = delete;
81
82 ErrorCode SetClockHz(uint32_t hz) override
83 {
84 if (hz == 0u)
85 {
86 clock_hz_ = 0u;
87 half_period_ns_ = 0u;
88 half_period_loops_ = 0u;
89 return ErrorCode::OK;
90 }
91
92 if (hz < MIN_HZ)
93 {
94 hz = MIN_HZ;
95 }
96 if (hz > MAX_HZ)
97 {
98 hz = MAX_HZ;
99 }
100
101 clock_hz_ = hz;
102
103 half_period_ns_ = (NS_PER_SEC + (2u * hz) - 1u) / (2u * hz);
104
105 if (loops_per_us_ == 0u)
106 {
107 half_period_loops_ = 0u;
108 return ErrorCode::OK;
109 }
110
111 const uint64_t LOOPS_SCALED =
112 static_cast<uint64_t>(loops_per_us_) * static_cast<uint64_t>(half_period_ns_);
113 if (LOOPS_SCALED < LOOPS_SCALE)
114 {
115 half_period_loops_ = 0u;
116 }
117 else
118 {
119 const uint64_t LOOPS_CEIL = (LOOPS_SCALED + CEIL_BIAS) / LOOPS_SCALE;
120 half_period_loops_ =
121 (LOOPS_CEIL > UINT32_MAX) ? UINT32_MAX : static_cast<uint32_t>(LOOPS_CEIL);
122 }
123
124 return ErrorCode::OK;
125 }
126
127 void Close() override
128 {
129 tck_.Write(false);
130 tms_.Write(true);
131 tdi_.Write(false);
132 }
133
135 {
136 tms_.Write(true);
137 for (uint32_t i = 0; i < RESET_CYCLES; ++i)
138 {
139 ClockCycle(true, false, nullptr);
140 }
141 current_state_ = TapState::TEST_LOGIC_RESET;
142 return ErrorCode::OK;
143 }
144
145 ErrorCode GotoState(TapState target) override
146 {
147 if (target == current_state_)
148 {
149 return ErrorCode::OK;
150 }
151
152 switch (target)
153 {
154 case TapState::TEST_LOGIC_RESET:
155 return ResetTap();
156 case TapState::RUN_TEST_IDLE:
157 EnsureIdle();
158 return ErrorCode::OK;
159 case TapState::SHIFT_IR:
160 EnsureIdle();
161 ApplyTmsSequence(SEQ_TO_SHIFT_IR, SEQ_TO_SHIFT_IR_LEN);
162 current_state_ = TapState::SHIFT_IR;
163 return ErrorCode::OK;
164 case TapState::SHIFT_DR:
165 EnsureIdle();
166 ApplyTmsSequence(SEQ_TO_SHIFT_DR, SEQ_TO_SHIFT_DR_LEN);
167 current_state_ = TapState::SHIFT_DR;
168 return ErrorCode::OK;
169 default:
171 }
172 }
173
174 ErrorCode ShiftIR(uint32_t bits, const uint8_t* in_lsb_first,
175 uint8_t* out_lsb_first) override
176 {
177 if (bits == 0u)
178 {
179 return ErrorCode::OK;
180 }
181
182 const ErrorCode EC = GotoState(TapState::SHIFT_IR);
183 if (EC != ErrorCode::OK)
184 {
185 return EC;
186 }
187
188 ShiftBits(bits, in_lsb_first, out_lsb_first);
189 ExitToIdle();
190 return ErrorCode::OK;
191 }
192
193 ErrorCode ShiftDR(uint32_t bits, const uint8_t* in_lsb_first,
194 uint8_t* out_lsb_first) override
195 {
196 if (bits == 0u)
197 {
198 return ErrorCode::OK;
199 }
200
201 const ErrorCode EC = GotoState(TapState::SHIFT_DR);
202 if (EC != ErrorCode::OK)
203 {
204 return EC;
205 }
206
207 ShiftBits(bits, in_lsb_first, out_lsb_first);
208 ExitToIdle();
209 return ErrorCode::OK;
210 }
211
212 ErrorCode Sequence(uint32_t cycles, bool tms, const uint8_t* tdi_lsb_first,
213 uint8_t* tdo_lsb_first) override
214 {
215 if (cycles == 0u)
216 {
217 return ErrorCode::OK;
218 }
219
220 if (tdo_lsb_first != nullptr)
221 {
222 const uint32_t BYTES = (cycles + 7u) / 8u;
223 Memory::FastSet(tdo_lsb_first, 0, BYTES);
224 }
225
226 for (uint32_t i = 0; i < cycles; ++i)
227 {
228 const bool TDI_VAL = (tdi_lsb_first == nullptr)
229 ? false
230 : (((tdi_lsb_first[i / 8u] >> (i & 7u)) & 0x1u) != 0u);
231 bool tdo_bit = false;
232 ClockCycle(tms, TDI_VAL, tdo_lsb_first ? &tdo_bit : nullptr);
233
234 if (tdo_lsb_first != nullptr && tdo_bit)
235 {
236 tdo_lsb_first[i / 8u] =
237 static_cast<uint8_t>(tdo_lsb_first[i / 8u] | (1u << (i & 7u)));
238 }
239 }
240
241 return ErrorCode::OK;
242 }
243
244 void IdleClocks(uint32_t cycles) override
245 {
246 if (cycles == 0u)
247 {
248 return;
249 }
250
251 (void)GotoState(TapState::RUN_TEST_IDLE);
252 for (uint32_t i = 0; i < cycles; ++i)
253 {
254 ClockCycle(false, false, nullptr);
255 }
256 }
257
258 private:
259 static constexpr uint8_t SEQ_TO_SHIFT_DR[] = {1u, 0u, 0u};
260 static constexpr uint32_t SEQ_TO_SHIFT_DR_LEN = 3u;
261 static constexpr uint8_t SEQ_TO_SHIFT_IR[] = {1u, 1u, 0u, 0u};
262 static constexpr uint32_t SEQ_TO_SHIFT_IR_LEN = 4u;
263
264 void ApplyTmsSequence(const uint8_t* seq, uint32_t len)
265 {
266 for (uint32_t i = 0; i < len; ++i)
267 {
268 const bool TMS_VAL = (seq[i] != 0u);
269 ClockCycle(TMS_VAL, false, nullptr);
270 }
271 }
272
273 void EnsureIdle()
274 {
275 if (current_state_ == TapState::RUN_TEST_IDLE)
276 {
277 return;
278 }
279 (void)ResetTap();
280 ClockCycle(false, false, nullptr);
281 current_state_ = TapState::RUN_TEST_IDLE;
282 }
283 void ExitToIdle()
284 {
285 // EXIT1 -> UPDATE (TMS=1), UPDATE -> IDLE (TMS=0)
286 ClockCycle(true, false, nullptr);
287 ClockCycle(false, false, nullptr);
288 current_state_ = TapState::RUN_TEST_IDLE;
289 }
290
291 void ShiftBits(uint32_t bits, const uint8_t* in_lsb_first, uint8_t* out_lsb_first)
292 {
293 if (out_lsb_first != nullptr && bits != 0u)
294 {
295 const uint32_t BYTES = (bits + 7u) / 8u;
296 Memory::FastSet(out_lsb_first, 0, BYTES);
297 }
298
299 if (bits == 0u)
300 {
301 return;
302 }
303
304 if (bits > 1u)
305 {
306 (void)Sequence(bits - 1u, false, in_lsb_first, out_lsb_first);
307 }
308
309 const uint32_t LAST = bits - 1u;
310 const bool LAST_TDI = (in_lsb_first == nullptr)
311 ? false
312 : (((in_lsb_first[LAST / 8u] >> (LAST & 7u)) & 0x1u) != 0u);
313 bool last_tdo = false;
314 ClockCycle(true, LAST_TDI, out_lsb_first ? &last_tdo : nullptr);
315
316 if (out_lsb_first != nullptr && last_tdo)
317 {
318 out_lsb_first[LAST / 8u] =
319 static_cast<uint8_t>(out_lsb_first[LAST / 8u] | (1u << (LAST & 7u)));
320 }
321 }
322
323 void ClockCycle(bool tms, bool tdi, bool* tdo)
324 {
325 tms_.Write(tms);
326 tdi_.Write(tdi);
327
328 if (half_period_loops_ == 0u)
329 {
330 tck_.Write(false);
331 tck_.Write(true);
332 if (tdo != nullptr)
333 {
334 *tdo = tdo_.Read();
335 }
336 tck_.Write(false);
337 }
338 else
339 {
340 tck_.Write(false);
341 DelayHalf();
342 tck_.Write(true);
343 DelayHalf();
344 if (tdo != nullptr)
345 {
346 *tdo = tdo_.Read();
347 }
348 tck_.Write(false);
349 }
350 }
351
352 inline void DelayHalf() { BusyLoop(half_period_loops_); }
353
354 static void BusyLoop(uint32_t loops)
355 {
356 volatile uint32_t sink = loops;
357 while (sink--)
358 {
359 }
360 }
361
362 private:
363 TckGpioType& tck_;
364 TmsGpioType& tms_;
365 TdiGpioType& tdi_;
366 TdoGpioType& tdo_;
367
368 uint32_t clock_hz_ = 0u;
369 uint32_t loops_per_us_ = 0u;
370 uint32_t half_period_ns_ = 0u;
371 uint32_t half_period_loops_ = 0u;
372
373 TapState current_state_ = TapState::TEST_LOGIC_RESET;
374};
375
376} // namespace LibXR::Debug
基于 GpioType 轮询 bit-bang 的 JTAG 探针。 JTAG probe based on polling bit-bang using GpioType.
void Close() override
Close JTAG and release resources.
void IdleClocks(uint32_t cycles) override
Insert idle clock cycles in Run-Test/Idle.
ErrorCode ResetTap() override
Reset TAP to Test-Logic-Reset.
JtagGeneralGPIO(TckGpioType &tck, TmsGpioType &tms, TdiGpioType &tdi, TdoGpioType &tdo, uint32_t loops_per_us, uint32_t default_hz=DEFAULT_CLOCK_HZ)
构造函数。Constructor.
ErrorCode ShiftIR(uint32_t bits, const uint8_t *in_lsb_first, uint8_t *out_lsb_first) override
Shift IR, LSB first.
ErrorCode SetClockHz(uint32_t hz) override
Set TCK frequency, if supported.
ErrorCode GotoState(TapState target) override
Move to the requested TAP state.
ErrorCode Sequence(uint32_t cycles, bool tms, const uint8_t *tdi_lsb_first, uint8_t *tdo_lsb_first) override
Shift a fixed-TMS sequence, LSB first.
ErrorCode ShiftDR(uint32_t bits, const uint8_t *in_lsb_first, uint8_t *out_lsb_first) override
Shift DR, LSB first.
Abstract JTAG base class providing TAP control and IR/DR shifting.
Definition jtag.hpp:37
static void FastSet(void *dst, uint8_t value, size_t size)
快速内存填充 / Fast memory fill
ErrorCode
定义错误码枚举
@ NOT_SUPPORT
不支持 | Not supported
@ OK
操作成功 | Operation successful