]> cloudbase.mooo.com Git - z180-stamp.git/blob - avr/cmd_cpu.c
merge/unify chkcpu and freq. wip
[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
145 #define O_SILENT (1<<0)
146 #define O_WENV (1<<1)
147 #define O_LOAD_LOOP (1<<2)
148 #define O_UNLOAD_LOOP (1<<3)
149
150 command_ret_t do_cpu_freq(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
151 {
152 uint_fast8_t options = O_LOAD_LOOP | O_UNLOAD_LOOP;
153 uint_fast8_t lcycles = 0;
154 uint16_t timeout = 1000;
155 uint8_t eimsk_save;
156 ERRNUM err = ESUCCESS;
157
158 int opt;
159 while ((opt = getopt(argc, argv, PSTR("swnuc:t:"))) != -1) {
160 switch (opt) {
161 case 's':
162 options |= O_SILENT;
163 break;
164 case 'w':
165 options |= O_WENV;
166 break;
167 case 'n':
168 options &= ~O_LOAD_LOOP;
169 break;
170 case 'u':
171 options &= ~O_UNLOAD_LOOP;
172 break;
173 case 'c':
174 lcycles = eval_arg(optarg, NULL);
175 break;
176 case 't':
177 timeout = eval_arg(optarg, NULL);
178 break;
179 default: /* '?' */
180 return CMD_RET_USAGE;
181 }
182 }
183 if (argc - optind != 0)
184 return CMD_RET_USAGE;
185
186 if (z80_bus_state() & ZST_RUNNING)
187 cmd_error(CMD_RET_FAILURE, ERUNNING, NULL);
188
189 uint8_t *mem_save = NULL;
190 if (options & O_LOAD_LOOP) {
191 mem_save = (uint8_t *) malloc(cpuinfo_length);
192 if (mem_save == NULL)
193 cmd_error(CMD_RET_FAILURE, ENOMEM, NULL);
194 z80_bus_cmd(Request);
195 z80_read_block(mem_save, 0, cpuinfo_length);
196 z80_load_mem(0, cpuinfo, &cpuinfo_sections, cpuinfo_address,
197 cpuinfo_length_of_sections);
198 z80_bus_cmd(Release);
199 }
200
201 /* Save state and disable INT5/INT6 */
202 ATOMIC_BLOCK(ATOMIC_FORCEON) {
203 eimsk_save = EIMSK;
204 EIMSK &= ~_BV(INT6);
205 EIMSK &= ~_BV(INT5);
206 }
207 EIFR = _BV(INTF5); /* Reset pending int */
208
209 z80_bus_cmd(Run);
210
211 clear_ctrlc(); /* forget any previous Control C */
212 /* Wait for falling edge */
213 do {
214 /* check for ctrl-c to abort... */
215 if (had_ctrlc() || ctrlc()) {
216 err = EINTR;
217 break;
218 }
219 } while ((EIFR & _BV(INTF5)) == 0);
220
221 if (lcycles == 0) {
222 z80_bus_cmd(Request);
223 if (z80_read(3) == 0xFF)
224 lcycles = z80_read(5);
225 z80_bus_cmd(Release);
226 }
227 uint32_t cpu_freq = 0;
228 if (!err)
229 cpu_freq = z80_measure_phi(lcycles);
230
231 z80_bus_cmd(Reset);
232
233 /* Restore INT5/INT6 */
234 ATOMIC_BLOCK(ATOMIC_FORCEON) {
235 if ((eimsk_save & _BV(INT5)) != 0)
236 EIMSK |= _BV(INT5);
237 if ((eimsk_save & _BV(INT6)) != 0)
238 EIMSK |= _BV(INT6);
239 /* Reset pending int */
240 EIFR = _BV(INTF5);
241 EIFR = _BV(INTF6);
242 }
243 Stat &= ~S_MSG_PENDING;
244 Stat &= ~S_CON_PENDING;
245
246
247
248
249
250
251
252 if ((mem_save != NULL) && options & O_UNLOAD_LOOP) {
253 z80_bus_cmd(Request);
254 z80_write_block(mem_save, 0, cpuinfo_length);
255 z80_bus_cmd(Release);
256 }
257 free(mem_save);
258
259 if (err)
260 cmd_error(CMD_RET_FAILURE, err, NULL);
261
262 if (!(options & O_SILENT))
263 printf_P(PSTR("%lu\n"), cpu_freq);
264
265 #if 0
266 if (options & O_WENV) {
267 if (setenv_ulong(PSTR(ENV_CPU_FREQ), cpu_freq)) {
268 if (!(options & O_SILENT))
269 printf_P(PSTR("'SETENV (%S, %lu)' failed!\n"), PSTR(ENV_CPU_FREQ), cpu_freq);
270 return CMD_RET_FAILURE;
271 }
272 }
273 #endif
274 return CMD_RET_SUCCESS;
275 }
276
277 static const FLASH char * const FLASH cpu_strings[] = {
278 FSTR("Unknown"),
279 FSTR("8080"),
280 FSTR("8085"),
281 FSTR("Z80"),
282 FSTR("x180"),
283 FSTR("HD64180"),
284 FSTR("Z80180"),
285 FSTR("Z80S180"),
286 };
287
288 command_ret_t do_cpuchk(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc UNUSED, char * const argv[] UNUSED)
289 {
290 uint_fast8_t options = O_LOAD_LOOP | O_UNLOAD_LOOP;
291 uint_fast8_t cputype = 0;
292 ERRNUM err = ESUCCESS;
293 uint8_t eimsk_save;
294
295 int opt;
296 while ((opt = getopt(argc, argv, PSTR("swnu"))) != -1) {
297 switch (opt) {
298 case 's':
299 options |= O_SILENT;
300 break;
301 case 'w':
302 options |= O_WENV;
303 break;
304 case 'n':
305 options &= ~O_LOAD_LOOP;
306 break;
307 case 'u':
308 options &= ~O_UNLOAD_LOOP;
309 break;
310 default: /* '?' */
311 return CMD_RET_USAGE;
312 }
313 }
314 if (argc - optind != 0)
315 return CMD_RET_USAGE;
316
317 if (z80_bus_state() & ZST_RUNNING)
318 cmd_error(CMD_RET_FAILURE, ERUNNING, NULL);
319
320 uint8_t *mem_save = NULL;
321 if (options & O_LOAD_LOOP) {
322 mem_save = (uint8_t *) malloc(cpuinfo_length);
323 if (mem_save == NULL)
324 cmd_error(CMD_RET_FAILURE, ENOMEM, NULL);
325 z80_bus_cmd(Request);
326 z80_read_block(mem_save, 0, cpuinfo_length);
327 z80_load_mem(0, cpuinfo, &cpuinfo_sections, cpuinfo_address,
328 cpuinfo_length_of_sections);
329 z80_bus_cmd(Release);
330 }
331
332 /* Save state and disable INT5/INT6 */
333 ATOMIC_BLOCK(ATOMIC_FORCEON) {
334 eimsk_save = EIMSK;
335 EIMSK &= ~_BV(INT6);
336 EIMSK &= ~_BV(INT5);
337 }
338 EIFR = _BV(INTF5); /* Reset pending int */
339
340 z80_bus_cmd(Run);
341
342 clear_ctrlc(); /* forget any previous Control C */
343 do {
344 /* check for ctrl-c to abort... */
345 if (had_ctrlc() || ctrlc()) {
346 err = EINTR;
347 break;
348 }
349 } while ((EIFR & _BV(INTF5)) == 0);
350
351
352
353
354
355
356
357
358
359
360
361 z80_bus_cmd(Reset);
362
363 /* Restore INT5/INT6 */
364 ATOMIC_BLOCK(ATOMIC_FORCEON) {
365 if ((eimsk_save & _BV(INT5)) != 0)
366 EIMSK |= _BV(INT5);
367 if ((eimsk_save & _BV(INT6)) != 0)
368 EIMSK |= _BV(INT6);
369 /* Reset pending int */
370 EIFR = _BV(INTF5);
371 EIFR = _BV(INTF6);
372 }
373 Stat &= ~S_MSG_PENDING;
374 Stat &= ~S_CON_PENDING;
375
376 z80_bus_cmd(Request);
377 if (z80_read(3) == 0xFF) {
378 cputype = z80_read(4);
379 }
380 z80_bus_cmd(Release);
381
382 if ((mem_save != NULL) && options & O_UNLOAD_LOOP) {
383 z80_bus_cmd(Request);
384 z80_write_block(mem_save, 0, cpuinfo_length);
385 z80_bus_cmd(Release);
386 }
387 free(mem_save);
388
389 if (err)
390 cmd_error(CMD_RET_FAILURE, err, NULL);
391
392 if (cputype >= ARRAY_SIZE(cpu_strings))
393 cputype = 0;
394 printf_P(PSTR("Detected CPU: %S\n"), cpu_strings[cputype]);
395
396 return CMD_RET_SUCCESS;
397 }
398
399 command_ret_t do_cpu_test(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
400 {
401
402 uint32_t pulsewidth = 10; /* ms */
403
404 int opt;
405 while ((opt = getopt(argc, argv, PSTR("t:"))) != -1) {
406 switch (opt) {
407 case 't':
408 pulsewidth = eval_arg(optarg, NULL);
409 break;
410 default: /* '?' */
411 return CMD_RET_USAGE;
412 }
413 }
414
415 if ((z80_bus_state() & ZST_ACQUIRED) != RESET)
416 cmd_error(CMD_RET_FAILURE, ERUNNING, NULL);
417
418 clear_ctrlc(); /* forget any previous Control C */
419 do {
420 z80_bus_cmd(Request);
421 test_delay(pulsewidth);
422 z80_bus_cmd(Release);
423 test_delay(pulsewidth);
424 } while (!(had_ctrlc() || ctrlc()));
425
426 return CMD_RET_SUCCESS;
427 }
428
429 command_ret_t do_bus_test(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc UNUSED, char * const argv[] UNUSED)
430 {
431 int ch;
432
433 #if 0
434 int opt;
435 while ((opt = getopt(argc, argv, PSTR("t:"))) != -1) {
436 switch (opt) {
437 case 't':
438 pulsewidth = eval_arg(optarg, NULL);
439 break;
440 default: /* '?' */
441 return CMD_RET_USAGE;
442 }
443 }
444 #endif
445
446 my_puts_P(PSTR(
447 " 1: RESET 4: RUN r: Toggle /RESET\n"
448 " 2: REQUEST 5: RESTART b: Toggle /BUSREQ\n"
449 " 3: RELEASE 6: M_CYCLE\n"
450 "\n"
451 //"Bus state: "
452 ));
453
454 do {
455 ch = my_getchar(1);
456 if (ch >= 0) {
457 switch (ch) {
458 case '1': /* bus_cmd RESET */
459 case '2': /* bus_cmd REQUEST */
460 case '3': /* bus_cmd RELEASE */
461 case '4': /* bus_cmd RUN */
462 case '5': /* bus_cmd RESTART */
463 case '6': /* bus_cmd M_CYCLE */
464 z80_bus_cmd(ch - '1' + Reset);
465 break;
466 case 'r': /* Toggle RESET */
467 z80_toggle_reset();
468 break;
469 case 'b': /* Toggle BUSREQ */
470 z80_toggle_busreq();
471 break;
472 }
473 test_delay(10);
474 uint32_t cycles = z80_get_busreq_cycles();
475 printf_P(PSTR("\rState: %.2x, cycles: %lu, time: %luus "),
476 z80_bus_state(), cycles, (uint32_t) (cycles * 1000000LL / F_CPU));
477 }
478 } while (ch != 0x03);
479
480 putchar('\n');
481 return CMD_RET_SUCCESS;
482 }
483
484 command_ret_t do_busack_test(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc UNUSED, char * const argv[] UNUSED)
485 {
486
487 if ((z80_bus_state() & ZST_ACQUIRED) != RESET)
488 cmd_error(CMD_RET_FAILURE, ERUNNING, NULL);
489
490 z80_bus_cmd(Request);
491 uint32_t result = z80_get_busreq_cycles();
492 test_delay(20);
493 z80_bus_cmd(Release);
494
495 #if 0
496 long div;
497
498 pinconf = gpio_config_get(pin);
499 if (pinconf == OUTPUT_TIMER) {
500 div = gpio_clockdiv_get(pin);
501 }
502 #endif
503
504
505 printf_P(PSTR("cycles: %lu, time: %luus\n"), result, (uint32_t) (result * 1000000LL / F_CPU));
506
507 return CMD_RET_SUCCESS;
508 }
509
510
511 /*
512 * command table for subcommands
513 */
514
515 cmd_tbl_t cmd_tbl_cpu[] = {
516 CMD_TBL_ITEM(
517 chkcpu, CONFIG_SYS_MAXARGS, CTBL_RPT|CTBL_SUBCMDAUTO, do_cpuchk,
518 "Check/Identify CPU",
519 ""
520 ),
521 CMD_TBL_ITEM(
522 buscmd, CONFIG_SYS_MAXARGS, CTBL_RPT, do_bus_test,
523 "Bus commands",
524 ""
525 ),
526 CMD_TBL_ITEM(
527 test, CONFIG_SYS_MAXARGS, 1, do_cpu_test,
528 "Do bus request/release cycles",
529 "[-t pulsewidth]"
530 ),
531 CMD_TBL_ITEM(
532 busack, 2, 1, do_busack_test,
533 "Get time from /Reset high to /BUSACK low",
534 ""
535 ),
536 CMD_TBL_ITEM(
537 freq, CONFIG_SYS_MAXARGS, 1, do_cpu_freq,
538 "Measure cpu frequency",
539 "[-qwn] [-c loopcycles] [-t timeout]\n"
540 " -q Be quiet\n"
541 // " -w Write result to environment variable '"ENV_CPU_FREQ"'"
542 ),
543
544 CMD_TBL_ITEM(
545 help, CONFIG_SYS_MAXARGS, CTBL_RPT, do_help,
546 "Print sub command description/usage",
547 "\n"
548 " - print brief description of all sub commands\n"
549 "fat help command ...\n"
550 " - print detailed usage of sub cmd 'command'"
551 ),
552
553 /* This does not use the CMD_TBL_ITEM macro as ? can't be used in symbol names */
554 {FSTR("?"), CONFIG_SYS_MAXARGS, 1, do_help,
555 NULL,
556 #ifdef CONFIG_SYS_LONGHELP
557 FSTR(""),
558 #endif /* CONFIG_SYS_LONGHELP */
559 NULL,
560 #ifdef CONFIG_AUTO_COMPLETE
561 NULL,
562 #endif
563 },
564 /* Mark end of table */
565 CMD_TBL_END(cmd_tbl_cpu)
566 };
567
568
569 command_ret_t do_cpu(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc UNUSED, char * const argv[] UNUSED)
570 {
571 //puts_P(PSTR("Huch?"));
572
573 return CMD_RET_USAGE;
574 }
575
576
577 #if 0 /* Z180 Single Step Functions */
578 /*
579 * Z180 Single Step Functions
580 *
581 */
582
583
584 #define P_RUN PORTG
585 #define RUN 1
586 #define DDR_RUN DDRG
587 #define P_STEP PORTG
588 #define STEP 0
589 #define DDR_STEP DDRG
590 #define P_WAIT PORTG
591 #define WAIT 2
592 #define DDR_WAIT DDRG
593 /* All three signals are on the same Port (PortG) */
594 #define PORT_SS PORTG
595 #define DDR_SS DDRG
596 #define PIN_SS PING
597
598 static bool ss_available;
599
600 int single_step_setup(void)
601 {
602 ss_available = false;
603
604 #if 0
605 if (z80_bus_state() & ZST_RUNNING ||
606 !(z80_bus_cmd(Request) & ZST_ACQUIRED))
607 return -1;
608 #endif
609
610 /* STEP, RUN output, WAIT input */
611
612 PORT_SS |= _BV(RUN) | _BV(STEP);
613 DDR_SS |= _BV(RUN) | _BV(STEP);
614 DDR_SS &= ~_BV(WAIT);
615
616 /* RUN high, MREQ pulse --> WAIT should be low */
617 z80_mreq_pulse();
618
619 if ((PIN_SS & _BV(WAIT)) == 0) {
620
621 /* RUN high, STEP pulse --> WAIT should be high */
622 PIN_SS = _BV(STEP);
623 PIN_SS = _BV(STEP);
624 if ((PIN_SS & _BV(WAIT)) != 0) {
625
626 /* RUN high, MREQ pulse --> WAIT should be low */
627 z80_mreq_pulse();
628 if ((PIN_SS & _BV(WAIT)) == 0) {
629
630 /* RUN low --> WAIT should be high */
631 PIN_SS = _BV(RUN);
632 if ((PIN_SS & _BV(WAIT)) != 0) {
633
634 /* RUN low, STEP pulse --> WAIT should be high */
635 PIN_SS = _BV(STEP);
636 PIN_SS = _BV(STEP);
637 if ((PIN_SS & _BV(WAIT)) != 0) {
638
639 /* all tests passed */
640 ss_available = true;
641 }
642 }
643 }
644 }
645 }
646
647 if (!ss_available) {
648 DDR_SS &= ~(_BV(STEP) | _BV(RUN));
649 PORT_SS |= _BV(RUN) | _BV(STEP);
650 }
651
652 return ss_available ? 0 : -1;
653 }
654 #endif