2 * (C) Copyright 2018 Leo C. <erbl259-lmu@yahoo.de>
4 * SPDX-License-Identifier: GPL-2.0
9 #include <util/atomic.h>
12 #include "con-utils.h"
16 #include "getopt-min.h"
19 /* hack to get Z180 loadfile into flash memory */
20 #define const const FLASH
21 #include "../z180/cpuinfo.h"
26 * delay for <count> ms...
28 static void test_delay(uint32_t count
)
30 uint32_t ts
= get_timer(0);
32 while (get_timer(ts
) <= count
);
35 uint32_t z80_measure_phi(uint_fast8_t cycles
)
40 uint8_t eimsk_save
,eicrb_save
;
44 ATOMIC_BLOCK(ATOMIC_FORCEON
) {
45 /* Save state and disable INT6 */
48 /* Save state and set INT6 for falling edge */
50 EICRB
= (eicrb_save
& ~(0b11 << ISC60
)) | (0b10 << ISC60
);
55 TCCR3B
= 0b000<<CS30
; /* stop counter */
61 ATOMIC_BLOCK(ATOMIC_FORCEON
) {
62 /* Reset pending int */
64 /* Wait for falling edge */
65 while ((EIFR
& _BV(INTF6
)) == 0)
67 TCCR3B
= 0b110<<CS30
; /* Count falling edges on T3 (==INT6) */
69 TIFR4
= _BV(OCF4B
); /* clear compare match flag */
71 while (ref_ovfl
< 60) {
72 ATOMIC_BLOCK(ATOMIC_FORCEON
) {
73 if ((TIFR4
& _BV(OCF4B
)) != 0) {
77 if ((TIFR3
& _BV(TOV3
)) != 0) {
84 ATOMIC_BLOCK(ATOMIC_FORCEON
) {
87 if (EIFR
& _BV(INTF6
)) {
88 TCCR3B
= 0b000<<CS30
; /* stop counter */
92 if ((TIFR4
& _BV(OCF4B
)) != 0) {
99 if ((TIFR3
& _BV(TOV3
)) != 0) {
104 ATOMIC_BLOCK(ATOMIC_FORCEON
) {
107 if ((eimsk_save
& _BV(INT6
)) != 0)
109 /* Reset pending int */
113 uint32_t ref_cnt
= (ref_stop
- OCR4B
) + ((uint32_t)ref_ovfl
<< 16);
114 uint32_t x_cnt
= TCNT3
+ ((uint32_t) x_ovfl
<< 16);
115 uint64_t x_tmp
= (uint64_t) 100000 * (x_cnt
* cycles
);
117 debug("TCNT3: %6u, ref_cnt: %9lu\n", TCNT3
, ref_cnt
);
118 debug("x_tmp: %lu %lu\n", (uint32_t) (x_tmp
>> 32), (uint32_t) (x_tmp
& 0xffffffff));
120 x_tmp
= (x_tmp
* getenv_ulong(PSTR(ENV_FMON
), 10, F_CPU
) + (ref_cnt
/ 2)) / ref_cnt
;
122 debug("x_tmp: %lu %lu\n", (uint32_t) (x_tmp
>> 32), (uint32_t) (x_tmp
& 0xffffffff));
124 /* round to 5 decimal digits */
126 while (x_tmp
>= 100000) {
127 x_tmp
= (x_tmp
+ 5)/10;
135 x_freq
+= (uint32_t) sc
<< 28;
146 float z80_measure_phi(uint_fast8_t cycles
, uint16_t wait_ms
)
151 uint8_t eimsk_save
,eicrb_save
;
155 ATOMIC_BLOCK(ATOMIC_FORCEON
) {
156 /* Save state and disable INT6 */
159 /* Save state and set INT6 for falling edge */
161 EICRB
= (eicrb_save
& ~(0b11 << ISC60
)) | (0b10 << ISC60
);
164 PRR1
&= ~_BV(PRTIM3
);
166 TCCR3B
= 0b000<<CS30
; /* stop counter */
172 ATOMIC_BLOCK(ATOMIC_FORCEON
) {
173 /* Reset pending int */
175 /* Wait for falling edge */
176 while ((EIFR
& _BV(INTF6
)) == 0)
178 TCCR3B
= 0b110<<CS30
; /* Count falling edges on T3 (==INT6) */
180 TIFR4
= _BV(OCF4B
); /* clear compare match flag */
182 while (ref_ovfl
< 60) {
183 ATOMIC_BLOCK(ATOMIC_FORCEON
) {
184 if ((TIFR4
& _BV(OCF4B
)) != 0) {
188 if ((TIFR3
& _BV(TOV3
)) != 0) {
195 ATOMIC_BLOCK(ATOMIC_FORCEON
) {
198 if (EIFR
& _BV(INTF6
)) {
199 TCCR3B
= 0b000<<CS30
; /* stop counter */
203 if ((TIFR4
& _BV(OCF4B
)) != 0) {
210 if ((TIFR3
& _BV(TOV3
)) != 0) {
215 ATOMIC_BLOCK(ATOMIC_FORCEON
) {
218 if ((eimsk_save
& _BV(INT6
)) != 0)
220 /* Reset pending int */
224 uint32_t ref_cnt
= (ref_stop
- OCR4B
) + ((uint32_t)ref_ovfl
<< 16);
226 uint32_t x_cnt
= TCNT3
+ ((uint32_t) x_ovfl
<< 16);
227 x_freq
= x_cnt
* cycles
;
229 x_freq
= (x_freq
* getenv_ulong(PSTR(ENV_FMON
), 10, F_CPU
)) / ref_cnt
;
231 debug("TCNT3: %6u, ref_cnt: %9lu\n", TCNT3
, ref_cnt
);
233 debug("ref_start: %6u, ref_stop: %6u, ref_ovfl: %4u, ref_cnt: %9lu\n"
234 " TCNT3: %6u, x_cnt: %6lu, xfreq: %f\n",
235 OCR4B
, ref_stop
, ref_ovfl
, ref_cnt
,
236 TCNT3
, x_cnt
, x_freq
);
249 int32_t z80_measure_phi(uint_fast8_t cycles
, uint16_t wait_ms
)
255 uint8_t eimsk_save
,eicrb_save
;
258 ATOMIC_BLOCK(ATOMIC_FORCEON
) {
259 /* Save state and disable INT6 */
262 /* Save state and set INT6 for falling edge */
264 EICRB
= (eicrb_save
& ~(0b11 << ISC60
)) | (0b10 << ISC60
);
267 PRR1
&= ~_BV(PRTIM3
);
269 TCCR3B
= 0b000<<CS30
; /* stop counter */
275 ATOMIC_BLOCK(ATOMIC_FORCEON
) {
276 /* Reset pending int */
278 /* Wait for falling edge */
279 while ((EIFR
& _BV(INTF6
)) == 0)
282 TCCR3B
= 0b110<<CS30
; /* Count falling edges on T3 (==INT6) */
283 TIFR4
= _BV(OCF4B
); /* clear compare match flag */
285 while (ref_ovfl
< 60) {
286 ATOMIC_BLOCK(ATOMIC_FORCEON
) {
287 if ((TIFR4
& _BV(OCF4B
)) != 0) {
291 if ((TIFR3
& _BV(TOV3
)) != 0) {
298 ATOMIC_BLOCK(ATOMIC_FORCEON
) {
301 if (EIFR
& _BV(INTF6
)) {
303 TCCR3B
= 0b000<<CS30
; /* stop counter */
306 if ((TIFR4
& _BV(OCF4B
)) != 0) {
315 ATOMIC_BLOCK(ATOMIC_FORCEON
) {
318 if (EIFR
& _BV(INTF6
))
320 if (TIFR4
& _BV(OCF4B
)) {
321 if (EIFR
& _BV(INTF6
))
324 if (EIFR
& _BV(INTF6
))
327 if (EIFR
& _BV(INTF6
))
334 TCCR3B
= 0b000<<CS30
; /* stop counter */
335 if ((TIFR4
& _BV(OCF4B
)) != 0) {
342 if ((TIFR3
& _BV(TOV3
)) != 0) {
351 uint32_t ref_cnt
= (ref_stop
- OCR4B
) + ((uint32_t)ref_ovfl
<< 16);
353 x_freq
= TCNT3
+ ((uint32_t) x_ovfl
<< 16);
354 uint32_t x_cnt
= x_freq
;
357 x_freq
= ((uint64_t) x_freq
* getenv_ulong(PSTR(ENV_FMON
), 10, F_CPU
) + (ref_cnt
/ 2))/ ref_cnt
;
359 debug("ref_start: %6u, ref_stop: %6u, ref_ovfl: %4u, ref_cnt: %9lu\n"
360 " TCNT3: %6u, x_cnt: %6lu, xfreq: %9lu\n",
361 OCR4B
, ref_stop
, ref_ovfl
, ref_cnt
,
362 TCNT3
, x_cnt
, x_freq
);
364 /* round to 5 decimal digits */
366 while (x_freq
>= 100000UL) {
367 x_freq
= (x_freq
+ 5)/10;
378 ATOMIC_BLOCK(ATOMIC_FORCEON
) {
382 EICRB
= (EICRB
& ~(0b11 << ISC60
)) | (eicrb_save
& (0b11 << ISC60
));
385 if ((eimsk_save
& _BV(INT6
)) != 0)
387 /* Reset pending int */
391 return (int32_t) x_freq
;
395 static const FLASH
char * const FLASH cpu_strings
[] = {
406 command_ret_t
do_cpuchk(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc UNUSED
, char * const argv
[] UNUSED
)
408 uint_fast8_t cputype
;
409 ERRNUM err
= ESUCCESS
;
410 uint8_t ram_save
[cpuinfo_length
];
412 if (z80_bus_state() & ZST_RUNNING
) {
415 z80_bus_request_or_exit();
416 z80_read_block(ram_save
, 0, cpuinfo_length
);
417 z80_load_mem(0, cpuinfo
,
420 cpuinfo_length_of_sections
);
421 z80_bus_cmd(Release
);
423 if (argv
[1] && (argv
[1][0] == 'n'))
428 clear_ctrlc(); /* forget any previous Control C */
429 uint_fast8_t done
= 0;
430 while (done
!= 0xFF) {
432 /* check for ctrl-c to abort... */
433 if (had_ctrlc() || ctrlc()) {
437 z80_bus_cmd(Request
);
440 cputype
= z80_read(4);
441 z80_bus_cmd(Release
);
444 z80_bus_cmd(Request
);
445 // z80_write_block(ram_save, 0, cpuinfo_length);
446 z80_bus_cmd(Release
);
452 cmd_error(CMD_RET_FAILURE
, err
, NULL
);
454 if (cputype
>= ARRAY_SIZE(cpu_strings
))
456 printf_P(PSTR("Detected CPU: %S\n"), cpu_strings
[cputype
]);
458 return CMD_RET_SUCCESS
;
461 command_ret_t
do_cpu_test(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
464 uint32_t pulsewidth
= 10; /* ms */
467 while ((opt
= getopt(argc
, argv
, PSTR("t:"))) != -1) {
470 pulsewidth
= eval_arg(optarg
, NULL
);
473 return CMD_RET_USAGE
;
477 if ((z80_bus_state() & ZST_ACQUIRED
) != RESET
)
478 cmd_error(CMD_RET_FAILURE
, ERUNNING
, NULL
);
480 clear_ctrlc(); /* forget any previous Control C */
482 z80_bus_cmd(Request
);
483 test_delay(pulsewidth
);
484 z80_bus_cmd(Release
);
485 test_delay(pulsewidth
);
486 } while (!(had_ctrlc() || ctrlc()));
488 return CMD_RET_SUCCESS
;
491 command_ret_t
do_bus_test(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc UNUSED
, char * const argv
[] UNUSED
)
497 while ((opt
= getopt(argc
, argv
, PSTR("t:"))) != -1) {
500 pulsewidth
= eval_arg(optarg
, NULL
);
503 return CMD_RET_USAGE
;
509 " 1: RESET 4: RUN r: Toggle /RESET\n"
510 " 2: REQUEST 5: RESTART b: Toggle /BUSREQ\n"
511 " 3: RELEASE 6: M_CYCLE\n"
520 case '1': /* bus_cmd RESET */
521 case '2': /* bus_cmd REQUEST */
522 case '3': /* bus_cmd RELEASE */
523 case '4': /* bus_cmd RUN */
524 case '5': /* bus_cmd RESTART */
525 case '6': /* bus_cmd M_CYCLE */
526 z80_bus_cmd(ch
- '1' + Reset
);
528 case 'r': /* Toggle RESET */
531 case 'b': /* Toggle BUSREQ */
536 uint32_t cycles
= z80_get_busreq_cycles();
537 printf_P(PSTR("\rState: %.2x, cycles: %lu, time: %luus "),
538 z80_bus_state(), cycles
, (uint32_t) (cycles
* 1000000LL / F_CPU
));
540 } while (ch
!= 0x03);
543 return CMD_RET_SUCCESS
;
546 command_ret_t
do_busack_test(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc UNUSED
, char * const argv
[] UNUSED
)
549 if ((z80_bus_state() & ZST_ACQUIRED
) != RESET
)
550 cmd_error(CMD_RET_FAILURE
, ERUNNING
, NULL
);
552 z80_bus_cmd(Request
);
553 uint32_t result
= z80_get_busreq_cycles();
555 z80_bus_cmd(Release
);
560 pinconf
= gpio_config_get(pin
);
561 if (pinconf
== OUTPUT_TIMER
) {
562 div
= gpio_clockdiv_get(pin
);
567 printf_P(PSTR("cycles: %lu, time: %luus\n"), result
, (uint32_t) (result
* 1000000LL / F_CPU
));
569 return CMD_RET_SUCCESS
;
572 static const FLASH
uint8_t loop_code
[] = {
573 /* 0000 */ 0x00, /* nop */
574 /* 0001 */ 0xAF, /* xor a */
575 /* 0005 */ 0xD3,0x32, /* out (032h),a ;DCNTL */
576 /* 0002 */ 0xD3,0x36, /* out (036h),a ;RCR */
578 /* 0006 */ 0xD3,0x40, /* out (040H),a ;Ready */
580 /* */ /* ;Z80 Z180(0W) Z180(MaxW) */
581 /* 0008 */ /* loop: ;-------------------------- */
582 /* 0008 */ 0xDB,0x50, /* in a,(050h) ;11 10 +3*3 19 */
583 /* 000A */ 0xC3,0x08,0x00 /* jp loop ;10 9 +3*3 18 */
584 /* ;-------------------------- */
588 command_ret_t
do_cpu_freq(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
591 #define O_SILENT (1<<0)
592 #define O_WENV (1<<1)
593 #define O_LOAD_LOOP (1<<2)
594 #define O_UNLOAD_LOOP (1<<3)
596 uint_fast8_t options
= O_LOAD_LOOP
| O_UNLOAD_LOOP
;
597 uint_fast8_t lcycles
= 19;
598 uint16_t timeout
= 1000;
600 uint8_t mem_save
[ARRAY_SIZE(loop_code
)];
603 while ((opt
= getopt(argc
, argv
, PSTR("swnuc:t:"))) != -1) {
612 options
&= ~O_LOAD_LOOP
;
615 options
&= ~O_UNLOAD_LOOP
;
618 lcycles
= eval_arg(optarg
, NULL
);
621 timeout
= eval_arg(optarg
, NULL
);
624 return CMD_RET_USAGE
;
627 if (argc
- optind
!= 0)
628 return CMD_RET_USAGE
;
630 if (z80_bus_state() & ZST_RUNNING
) {
631 if (!(options
& O_SILENT
))
632 printf_P(PSTR("Frequency measuring failed. CPU allready running!\n"));
633 return CMD_RET_FAILURE
;
637 z80_bus_cmd(Request
);
638 if (options
& O_LOAD_LOOP
) {
639 z80_read_block(mem_save
, 0, ARRAY_SIZE(loop_code
));
640 z80_write_block_P(loop_code
, 0, ARRAY_SIZE(loop_code
));
642 Stat
&= ~S_IO_0X40
; /* Reset pending int */
643 z80_bus_cmd(Release
);
646 clear_ctrlc(); /* forget any previous Control C */
649 /* Wait for falling edge */
651 /* check for ctrl-c to abort... */
652 if (had_ctrlc() || ctrlc()) {
656 } while ((Stat
& S_IO_0X40
) == 0);
660 cpu_freq
= z80_measure_phi(lcycles
);
663 if (options
& O_UNLOAD_LOOP
) {
664 z80_bus_cmd(Request
);
665 z80_write_block(mem_save
, 0, ARRAY_SIZE(loop_code
));
666 z80_bus_cmd(Release
);
669 cmd_error(CMD_RET_FAILURE
, err
, NULL
);
671 if (!(options
& O_SILENT
)) {
672 printf_P(PSTR("%lu %3u\n"), cpu_freq
& 0x0fffffff, cpu_freq
>> 28);
674 // printf_P(PSTR("%f%S\n"), cpu_freq, cpu_freq < 0 ? PSTR("") : PSTR("Hz"));
675 // if (cpu_freq != 0)
677 // printf_P(PSTR("No CPU clock or input frequency to low!\n"));
680 if (options
& O_WENV
) {
681 if (setenv_ulong(PSTR(ENV_CPU_FREQ
), cpu_freq
)) {
682 if (!(options
& O_SILENT
))
683 printf_P(PSTR("'SETENV (%S, %lu)' failed!\n"), PSTR(ENV_CPU_FREQ
), cpu_freq
);
684 return CMD_RET_FAILURE
;
688 return CMD_RET_SUCCESS
;
693 * command table for fat subcommands
696 cmd_tbl_t cmd_tbl_cpu
[] = {
698 chkcpu
, CONFIG_SYS_MAXARGS
, CTBL_RPT
, do_cpuchk
,
703 buscmd
, CONFIG_SYS_MAXARGS
, CTBL_RPT
, do_bus_test
,
708 test
, CONFIG_SYS_MAXARGS
, 1, do_cpu_test
,
709 "Do bus request/release cycles",
713 busack
, 2, 1, do_busack_test
,
714 "Get time from /Reset high to /BUSACK low",
718 freq
, CONFIG_SYS_MAXARGS
, 1, do_cpu_freq
,
719 "Measure cpu frequency",
720 "[-qwn] [-c loopcycles] [-t timeout]\n"
722 // " -w Write result to environment variable '"ENV_CPU_FREQ"'"
726 help
, CONFIG_SYS_MAXARGS
, CTBL_RPT
, do_help
,
727 "Print sub command description/usage",
729 " - print brief description of all sub commands\n"
730 "fat help command ...\n"
731 " - print detailed usage of sub cmd 'command'"
734 /* This does not use the CMD_TBL_ITEM macro as ? can't be used in symbol names */
735 {FSTR("?"), CONFIG_SYS_MAXARGS
, 1, do_help
,
737 #ifdef CONFIG_SYS_LONGHELP
739 #endif /* CONFIG_SYS_LONGHELP */
741 #ifdef CONFIG_AUTO_COMPLETE
745 /* Mark end of table */
746 CMD_TBL_END(cmd_tbl_cpu
)
750 command_ret_t
do_cpu(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc UNUSED
, char * const argv
[] UNUSED
)
752 //puts_P(PSTR("Huch?"));
754 return CMD_RET_USAGE
;
758 #if 0 /* Z180 Single Step Functions */
760 * Z180 Single Step Functions
770 #define DDR_STEP DDRG
773 #define DDR_WAIT DDRG
774 /* All three signals are on the same Port (PortG) */
775 #define PORT_SS PORTG
779 static bool ss_available
;
781 int single_step_setup(void)
783 ss_available
= false;
786 if (z80_bus_state() & ZST_RUNNING
||
787 !(z80_bus_cmd(Request
) & ZST_ACQUIRED
))
791 /* STEP, RUN output, WAIT input */
793 PORT_SS
|= _BV(RUN
) | _BV(STEP
);
794 DDR_SS
|= _BV(RUN
) | _BV(STEP
);
795 DDR_SS
&= ~_BV(WAIT
);
797 /* RUN high, MREQ pulse --> WAIT should be low */
800 if ((PIN_SS
& _BV(WAIT
)) == 0) {
802 /* RUN high, STEP pulse --> WAIT should be high */
805 if ((PIN_SS
& _BV(WAIT
)) != 0) {
807 /* RUN high, MREQ pulse --> WAIT should be low */
809 if ((PIN_SS
& _BV(WAIT
)) == 0) {
811 /* RUN low --> WAIT should be high */
813 if ((PIN_SS
& _BV(WAIT
)) != 0) {
815 /* RUN low, STEP pulse --> WAIT should be high */
818 if ((PIN_SS
& _BV(WAIT
)) != 0) {
820 /* all tests passed */
829 DDR_SS
&= ~(_BV(STEP
) | _BV(RUN
));
830 PORT_SS
|= _BV(RUN
) | _BV(STEP
);
833 return ss_available
? 0 : -1;