]> cloudbase.mooo.com Git - z180-stamp.git/blob - avr/cmd_cpu.c
6e36b2b64f5c622cabe1cc352387777307c5364d
[z180-stamp.git] / avr / cmd_cpu.c
1 /*
2 * (C) Copyright 2018 Leo C. <erbl259-lmu@yahoo.de>
3 *
4 * SPDX-License-Identifier: GPL-2.0
5 */
6
7 #include "cmd_cpu.h"
8 //#include <ctype.h>
9 #include <util/atomic.h>
10
11 #include "z80-if.h"
12 #include "con-utils.h"
13 #include "env.h"
14 #include "eval_arg.h"
15 #include "timer.h"
16 #include "getopt-min.h"
17 #include "debug.h"
18
19 /* hack to get Z180 loadfile into flash memory */
20 #define const const FLASH
21 #include "../z180/cpuinfo.h"
22 #undef const
23
24 #define DEBUG_CPU 1 /* set to 1 to debug */
25
26 #define debug_cpu(fmt, args...) \
27 debug_cond(DEBUG_CPU, fmt, ##args)
28
29
30 char * ulltoa (uint64_t val, char *s)
31 {
32 char *p = s;
33
34 while (val >= 10) {
35 *p++ = (val % 10) + '0';
36 val = val / 10;
37 }
38 *p++ = val + '0';
39 *p = '\0';
40
41 return strrev(s);
42 }
43
44 /*
45 * delay for <count> ms...
46 */
47 static void test_delay(uint32_t count)
48 {
49 uint32_t ts = get_timer(0);
50
51 while (get_timer(ts) <= count);
52 }
53
54 static uint32_t z80_measure_phi(uint_fast8_t cycles)
55 {
56 uint16_t ref_stop;
57 uint16_t ref_ovfl;
58 uint8_t x_ovfl;
59 uint32_t x_freq;
60
61
62 PRR1 &= ~_BV(PRTIM3);
63 TCCR3A = 0;
64 TCCR3B = 0b000<<CS30; /* stop counter */
65 TCNT3 = 0;
66 x_ovfl = 0;
67 TIFR3 = _BV(TOV3);
68 ref_ovfl = 0;
69
70 ATOMIC_BLOCK(ATOMIC_FORCEON) {
71 /* Reset pending int */
72 EIFR = _BV(INTF6);
73 /* Wait for falling edge */
74 while ((EIFR & _BV(INTF6)) == 0)
75 ;
76 OCR4B = TCNT4;
77 TCCR3B = 0b110<<CS30; /* Count falling edges on T3 (==INT6) */
78 TIFR4 = _BV(OCF4B); /* clear compare match flag */
79
80 while (ref_ovfl < 60) {
81 if ((TIFR4 & _BV(OCF4B)) != 0) {
82 TIFR4 = _BV(OCF4B);
83 ++ref_ovfl;
84 }
85 if ((TIFR3 & _BV(TOV3)) != 0) {
86 TIFR3 = _BV(TOV3);
87 ++x_ovfl;
88 }
89 }
90
91 EIFR = _BV(INTF6);
92 for (;;) {
93 if (EIFR & _BV(INTF6)) {
94 TCCR3B = 0b000<<CS30; /* stop counter */
95 ref_stop = TCNT4;
96 break;
97 }
98 if ((TIFR4 & _BV(OCF4B)) != 0) {
99 TIFR4 = _BV(OCF4B);
100 ++ref_ovfl;
101 }
102 }
103 }
104
105 if ((TIFR3 & _BV(TOV3)) != 0) {
106 TIFR3 = _BV(TOV3);
107 x_ovfl++;
108 }
109
110 uint32_t ref_cnt = (ref_stop - OCR4B) + ((uint32_t)ref_ovfl << 16);
111 uint32_t x_cnt = TCNT3 + ((uint32_t) x_ovfl << 16);
112 uint64_t x_tmp = (uint64_t) 100000 * (x_cnt * cycles);
113
114 // char x_tmp_str[21];
115 //
116 // debug_cpu("TCNT3: %6u, ref_cnt: %9lu\n", TCNT3, ref_cnt);
117 // ulltoa(x_tmp, x_tmp_str);
118 // debug_cpu("x_tmp: %s\n", x_tmp_str);
119
120 x_tmp = (x_tmp * getenv_ulong(PSTR(ENV_FMON), 10, F_CPU) + (ref_cnt / 2)) / ref_cnt;
121
122 // ulltoa(x_tmp, x_tmp_str);
123 // debug_cpu("x_tmp: %s\n", x_tmp_str);
124
125 /* round to 5 decimal digits */
126 int_fast8_t sc = 5;
127 while (sc > 0 || x_tmp >= 100000) {
128 x_tmp = (x_tmp + 5)/10;
129 sc--;
130 }
131 x_freq = x_tmp;
132 while (sc < 0) {
133 x_freq *= 10;
134 sc++;
135 }
136
137 /* Stop Timer */
138 TCCR3B = 0;
139 PRR1 |= _BV(PRTIM3);
140
141 return x_freq;
142 }
143
144 static const FLASH char * const FLASH cpu_strings[] = {
145 FSTR("Unknown"),
146 FSTR("8080"),
147 FSTR("8085"),
148 FSTR("Z80"),
149 FSTR("x180"),
150 FSTR("HD64180"),
151 FSTR("Z80180"),
152 FSTR("Z80S180"),
153 };
154
155 #define O_SILENT (1<<0)
156 #define O_WENV (1<<1)
157 #define O_LOAD_LOOP (1<<2)
158 #define O_UNLOAD_LOOP (1<<3)
159
160 static const FLASH char * const FLASH opt_strings[] = {
161 FSTR("swnu"), /* Options for chkcpu */
162 FSTR("swnuc:t:"), /* Oprions for cpufreq */
163 };
164
165 command_ret_t do_cpu_freq_chk(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
166 {
167 uint_fast8_t options = O_LOAD_LOOP | O_UNLOAD_LOOP;
168 uint_fast8_t cputype = 0;
169 uint32_t cpu_freq = 0;
170 uint_fast8_t lcycles = 0;
171 uint_fast8_t freq_cmd = 0;
172 uint16_t timeout = 1000;
173 uint8_t eimsk_save;
174 ERRNUM err = ESUCCESS;
175
176 if (argv[0][0] == 'f')
177 freq_cmd = 1;
178
179 int opt;
180 while ((opt = getopt(argc, argv, opt_strings[freq_cmd])) != -1) {
181 switch (opt) {
182 case 's':
183 options |= O_SILENT;
184 break;
185 case 'w':
186 options |= O_WENV;
187 break;
188 case 'n':
189 options &= ~O_LOAD_LOOP;
190 break;
191 case 'u':
192 options &= ~O_UNLOAD_LOOP;
193 break;
194 case 'c':
195 lcycles = eval_arg(optarg, NULL);
196 break;
197 case 't':
198 timeout = eval_arg(optarg, NULL);
199 break;
200 default: /* '?' */
201 return CMD_RET_USAGE;
202 }
203 }
204 if (argc - optind != 0)
205 return CMD_RET_USAGE;
206
207 if (z80_bus_state() & ZST_RUNNING)
208 cmd_error(CMD_RET_FAILURE, ERUNNING, NULL);
209
210 uint8_t *mem_save = NULL;
211 if (options & O_LOAD_LOOP) {
212 mem_save = (uint8_t *) malloc(cpuinfo_length);
213 if (mem_save == NULL)
214 cmd_error(CMD_RET_FAILURE, ENOMEM, NULL);
215 z80_bus_cmd(Request);
216 z80_read_block(mem_save, 0, cpuinfo_length);
217 z80_load_mem(0, cpuinfo, &cpuinfo_sections, cpuinfo_address,
218 cpuinfo_length_of_sections);
219 z80_bus_cmd(Release);
220 }
221
222 /* Save state and disable INT5/INT6 */
223 ATOMIC_BLOCK(ATOMIC_FORCEON) {
224 eimsk_save = EIMSK;
225 EIMSK &= ~_BV(INT6);
226 EIMSK &= ~_BV(INT5);
227 }
228 EIFR = _BV(INTF5); /* Reset pending int */
229
230 z80_bus_cmd(Run);
231
232 clear_ctrlc(); /* forget any previous Control C */
233 /* Wait for falling edge */
234 do {
235 /* check for ctrl-c to abort... */
236 if (had_ctrlc() || ctrlc()) {
237 err = EINTR;
238 break;
239 }
240 } while ((EIFR & _BV(INTF5)) == 0);
241
242 if (freq_cmd) {
243 if (lcycles == 0) {
244 z80_bus_cmd(Request);
245 if (z80_read(3) == 0xFF)
246 lcycles = z80_read(5);
247 z80_bus_cmd(Release);
248 }
249 if (!err)
250 cpu_freq = z80_measure_phi(lcycles);
251 }
252 z80_bus_cmd(Reset);
253
254 /* Restore INT5/INT6 */
255 ATOMIC_BLOCK(ATOMIC_FORCEON) {
256 if ((eimsk_save & _BV(INT5)) != 0)
257 EIMSK |= _BV(INT5);
258 if ((eimsk_save & _BV(INT6)) != 0)
259 EIMSK |= _BV(INT6);
260 /* Reset pending int */
261 EIFR = _BV(INTF5);
262 EIFR = _BV(INTF6);
263 }
264 Stat &= ~S_MSG_PENDING;
265 Stat &= ~S_CON_PENDING;
266
267 if (freq_cmd == 0) {
268 z80_bus_cmd(Request);
269 if (z80_read(3) == 0xFF)
270 cputype = z80_read(4);
271 z80_bus_cmd(Release);
272 }
273
274 if ((mem_save != NULL) && options & O_UNLOAD_LOOP) {
275 z80_bus_cmd(Request);
276 z80_write_block(mem_save, 0, cpuinfo_length);
277 z80_bus_cmd(Release);
278 }
279 free(mem_save);
280
281 if (err)
282 cmd_error(CMD_RET_FAILURE, err, NULL);
283
284 if (freq_cmd) {
285
286 if (!(options & O_SILENT))
287 printf_P(PSTR("%lu\n"), cpu_freq);
288
289 #if 0
290 if (options & O_WENV) {
291 if (setenv_ulong(PSTR(ENV_CPU_FREQ), cpu_freq)) {
292 if (!(options & O_SILENT))
293 printf_P(PSTR("'SETENV (%S, %lu)' failed!\n"), PSTR(ENV_CPU_FREQ), cpu_freq);
294 return CMD_RET_FAILURE;
295 }
296 }
297 #endif
298 } else {
299 if (cputype >= ARRAY_SIZE(cpu_strings))
300 cputype = 0;
301 printf_P(PSTR("Detected CPU: %S\n"), cpu_strings[cputype]);
302 }
303
304 return CMD_RET_SUCCESS;
305 }
306
307 command_ret_t do_cpu_test(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
308 {
309
310 uint32_t pulsewidth = 10; /* ms */
311
312 int opt;
313 while ((opt = getopt(argc, argv, PSTR("t:"))) != -1) {
314 switch (opt) {
315 case 't':
316 pulsewidth = eval_arg(optarg, NULL);
317 break;
318 default: /* '?' */
319 return CMD_RET_USAGE;
320 }
321 }
322
323 if ((z80_bus_state() & ZST_ACQUIRED) != RESET)
324 cmd_error(CMD_RET_FAILURE, ERUNNING, NULL);
325
326 clear_ctrlc(); /* forget any previous Control C */
327 do {
328 z80_bus_cmd(Request);
329 test_delay(pulsewidth);
330 z80_bus_cmd(Release);
331 test_delay(pulsewidth);
332 } while (!(had_ctrlc() || ctrlc()));
333
334 return CMD_RET_SUCCESS;
335 }
336
337 command_ret_t do_bus_test(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc UNUSED, char * const argv[] UNUSED)
338 {
339 int ch;
340
341 #if 0
342 int opt;
343 while ((opt = getopt(argc, argv, PSTR("t:"))) != -1) {
344 switch (opt) {
345 case 't':
346 pulsewidth = eval_arg(optarg, NULL);
347 break;
348 default: /* '?' */
349 return CMD_RET_USAGE;
350 }
351 }
352 #endif
353
354 my_puts_P(PSTR(
355 " 1: RESET 4: RUN r: Toggle /RESET\n"
356 " 2: REQUEST 5: RESTART b: Toggle /BUSREQ\n"
357 " 3: RELEASE 6: M_CYCLE\n"
358 "\n"
359 //"Bus state: "
360 ));
361
362 do {
363 ch = my_getchar(1);
364 if (ch >= 0) {
365 switch (ch) {
366 case '1': /* bus_cmd RESET */
367 case '2': /* bus_cmd REQUEST */
368 case '3': /* bus_cmd RELEASE */
369 case '4': /* bus_cmd RUN */
370 case '5': /* bus_cmd RESTART */
371 case '6': /* bus_cmd M_CYCLE */
372 z80_bus_cmd(ch - '1' + Reset);
373 break;
374 case 'r': /* Toggle RESET */
375 z80_toggle_reset();
376 break;
377 case 'b': /* Toggle BUSREQ */
378 z80_toggle_busreq();
379 break;
380 }
381 test_delay(10);
382 uint32_t cycles = z80_get_busreq_cycles();
383 printf_P(PSTR("\rState: %.2x, cycles: %lu, time: %luus "),
384 z80_bus_state(), cycles, (uint32_t) (cycles * 1000000LL / F_CPU));
385 }
386 } while (ch != 0x03);
387
388 putchar('\n');
389 return CMD_RET_SUCCESS;
390 }
391
392 command_ret_t do_busack_test(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc UNUSED, char * const argv[] UNUSED)
393 {
394
395 if ((z80_bus_state() & ZST_ACQUIRED) != RESET)
396 cmd_error(CMD_RET_FAILURE, ERUNNING, NULL);
397
398 z80_bus_cmd(Request);
399 uint32_t result = z80_get_busreq_cycles();
400 test_delay(20);
401 z80_bus_cmd(Release);
402
403 #if 0
404 long div;
405
406 pinconf = gpio_config_get(pin);
407 if (pinconf == OUTPUT_TIMER) {
408 div = gpio_clockdiv_get(pin);
409 }
410 #endif
411
412
413 printf_P(PSTR("cycles: %lu, time: %luus\n"), result, (uint32_t) (result * 1000000LL / F_CPU));
414
415 return CMD_RET_SUCCESS;
416 }
417
418
419 /*
420 * command table for subcommands
421 */
422
423 cmd_tbl_t cmd_tbl_cpu[] = {
424 CMD_TBL_ITEM(
425 freq, CONFIG_SYS_MAXARGS, CTBL_RPT, do_cpu_freq_chk,
426 "Measure cpu frequency",
427 "[-qwn] [-c loopcycles] [-t timeout]\n"
428 " -q Be quiet\n"
429 // " -w Write result to environment variable '"ENV_CPU_FREQ"'"
430 ),
431 CMD_TBL_ITEM(
432 chkcpu, CONFIG_SYS_MAXARGS, CTBL_RPT|CTBL_SUBCMDAUTO, do_cpu_freq_chk,
433 "Check/Identify CPU",
434 ""
435 ),
436 CMD_TBL_ITEM(
437 buscmd, CONFIG_SYS_MAXARGS, CTBL_RPT, do_bus_test,
438 "Bus commands",
439 ""
440 ),
441 CMD_TBL_ITEM(
442 test, CONFIG_SYS_MAXARGS, CTBL_RPT, do_cpu_test,
443 "Do bus request/release cycles",
444 "[-t pulsewidth]"
445 ),
446 CMD_TBL_ITEM(
447 busack, 2, CTBL_RPT, do_busack_test,
448 "Get time from /Reset high to /BUSACK low",
449 ""
450 ),
451
452 CMD_TBL_ITEM(
453 help, CONFIG_SYS_MAXARGS, CTBL_RPT, do_help,
454 "Print sub command description/usage",
455 "\n"
456 " - print brief description of all sub commands\n"
457 "fat help command ...\n"
458 " - print detailed usage of sub cmd 'command'"
459 ),
460
461 /* This does not use the CMD_TBL_ITEM macro as ? can't be used in symbol names */
462 {FSTR("?"), CONFIG_SYS_MAXARGS, CTBL_RPT, do_help,
463 NULL,
464 #ifdef CONFIG_SYS_LONGHELP
465 FSTR(""),
466 #endif /* CONFIG_SYS_LONGHELP */
467 NULL,
468 #ifdef CONFIG_AUTO_COMPLETE
469 NULL,
470 #endif
471 },
472 /* Mark end of table */
473 CMD_TBL_END(cmd_tbl_cpu)
474 };
475
476
477 command_ret_t do_cpu(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc UNUSED, char * const argv[] UNUSED)
478 {
479 //puts_P(PSTR("Huch?"));
480
481 return CMD_RET_USAGE;
482 }
483
484
485 #if 0 /* Z180 Single Step Functions */
486 /*
487 * Z180 Single Step Functions
488 *
489 */
490
491
492 #define P_RUN PORTG
493 #define RUN 1
494 #define DDR_RUN DDRG
495 #define P_STEP PORTG
496 #define STEP 0
497 #define DDR_STEP DDRG
498 #define P_WAIT PORTG
499 #define WAIT 2
500 #define DDR_WAIT DDRG
501 /* All three signals are on the same Port (PortG) */
502 #define PORT_SS PORTG
503 #define DDR_SS DDRG
504 #define PIN_SS PING
505
506 static bool ss_available;
507
508 int single_step_setup(void)
509 {
510 ss_available = false;
511
512 #if 0
513 if (z80_bus_state() & ZST_RUNNING ||
514 !(z80_bus_cmd(Request) & ZST_ACQUIRED))
515 return -1;
516 #endif
517
518 /* STEP, RUN output, WAIT input */
519
520 PORT_SS |= _BV(RUN) | _BV(STEP);
521 DDR_SS |= _BV(RUN) | _BV(STEP);
522 DDR_SS &= ~_BV(WAIT);
523
524 /* RUN high, MREQ pulse --> WAIT should be low */
525 z80_mreq_pulse();
526
527 if ((PIN_SS & _BV(WAIT)) == 0) {
528
529 /* RUN high, STEP pulse --> WAIT should be high */
530 PIN_SS = _BV(STEP);
531 PIN_SS = _BV(STEP);
532 if ((PIN_SS & _BV(WAIT)) != 0) {
533
534 /* RUN high, MREQ pulse --> WAIT should be low */
535 z80_mreq_pulse();
536 if ((PIN_SS & _BV(WAIT)) == 0) {
537
538 /* RUN low --> WAIT should be high */
539 PIN_SS = _BV(RUN);
540 if ((PIN_SS & _BV(WAIT)) != 0) {
541
542 /* RUN low, STEP pulse --> WAIT should be high */
543 PIN_SS = _BV(STEP);
544 PIN_SS = _BV(STEP);
545 if ((PIN_SS & _BV(WAIT)) != 0) {
546
547 /* all tests passed */
548 ss_available = true;
549 }
550 }
551 }
552 }
553 }
554
555 if (!ss_available) {
556 DDR_SS &= ~(_BV(STEP) | _BV(RUN));
557 PORT_SS |= _BV(RUN) | _BV(STEP);
558 }
559
560 return ss_available ? 0 : -1;
561 }
562 #endif