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"
24 #define DEBUG_CPU 1 /* set to 1 to debug */
26 #define debug_cpu(fmt, args...) \
27 debug_cond(DEBUG_CPU, fmt, ##args)
31 * delay for <count> ms...
33 static void test_delay(uint32_t count
)
35 uint32_t ts
= get_timer(0);
37 while (get_timer(ts
) <= count
);
40 static uint32_t z80_measure_phi(uint_fast8_t cycles
)
50 TCCR3B
= 0b000<<CS30
; /* stop counter */
56 ATOMIC_BLOCK(ATOMIC_FORCEON
) {
57 /* Reset pending int */
59 /* Wait for falling edge */
60 while ((EIFR
& _BV(INTF6
)) == 0)
62 TCCR3B
= 0b110<<CS30
; /* Count falling edges on T3 (==INT6) */
64 TIFR4
= _BV(OCF4B
); /* clear compare match flag */
66 while (ref_ovfl
< 60) {
67 ATOMIC_BLOCK(ATOMIC_FORCEON
) {
68 if ((TIFR4
& _BV(OCF4B
)) != 0) {
72 if ((TIFR3
& _BV(TOV3
)) != 0) {
79 ATOMIC_BLOCK(ATOMIC_FORCEON
) {
82 if (EIFR
& _BV(INTF6
)) {
83 TCCR3B
= 0b000<<CS30
; /* stop counter */
87 if ((TIFR4
& _BV(OCF4B
)) != 0) {
94 if ((TIFR3
& _BV(TOV3
)) != 0) {
99 uint32_t ref_cnt
= (ref_stop
- OCR4B
) + ((uint32_t)ref_ovfl
<< 16);
100 uint32_t x_cnt
= TCNT3
+ ((uint32_t) x_ovfl
<< 16);
101 uint64_t x_tmp
= (uint64_t) 100000 * (x_cnt
* cycles
);
103 debug_cpu("TCNT3: %6u, ref_cnt: %9lu\n", TCNT3
, ref_cnt
);
104 debug_cpu("x_tmp: %lu %lu\n", (uint32_t) (x_tmp
>> 32), (uint32_t) (x_tmp
& 0xffffffff));
106 x_tmp
= (x_tmp
* getenv_ulong(PSTR(ENV_FMON
), 10, F_CPU
) + (ref_cnt
/ 2)) / ref_cnt
;
108 debug_cpu("x_tmp: %lu %lu\n", (uint32_t) (x_tmp
>> 32), (uint32_t) (x_tmp
& 0xffffffff));
110 /* round to 5 decimal digits */
112 while (sc
> 0 || x_tmp
>= 100000) {
113 x_tmp
= (x_tmp
+ 5)/10;
129 static const FLASH
uint8_t loop_code
[] = {
130 /* 0000 */ 0x00, /* nop */
131 /* 0001 */ 0xAF, /* xor a */
132 /* 0005 */ 0xD3,0x32, /* out (032h),a ;DCNTL */
133 /* 0002 */ 0xD3,0x36, /* out (036h),a ;RCR */
135 /* 0006 */ 0xD3,0x40, /* out (040H),a ;Ready */
137 /* */ /* ;Z80 Z180(0W) Z180(MaxW) */
138 /* 0008 */ /* loop: ;-------------------------- */
139 /* 0008 */ 0xDB,0x50, /* in a,(050h) ;11 10 +3*3 19 */
140 /* 000A */ 0xC3,0x08,0x00 /* jp loop ;10 9 +3*3 18 */
141 /* ;-------------------------- */
145 command_ret_t
do_cpu_freq(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
148 #define O_SILENT (1<<0)
149 #define O_WENV (1<<1)
150 #define O_LOAD_LOOP (1<<2)
151 #define O_UNLOAD_LOOP (1<<3)
153 uint_fast8_t options
= O_LOAD_LOOP
| O_UNLOAD_LOOP
;
154 uint_fast8_t lcycles
= 19;
155 uint16_t timeout
= 1000;
158 uint8_t mem_save
[ARRAY_SIZE(loop_code
)];
161 while ((opt
= getopt(argc
, argv
, PSTR("swnuc:t:"))) != -1) {
170 options
&= ~O_LOAD_LOOP
;
173 options
&= ~O_UNLOAD_LOOP
;
176 lcycles
= eval_arg(optarg
, NULL
);
179 timeout
= eval_arg(optarg
, NULL
);
182 return CMD_RET_USAGE
;
185 if (argc
- optind
!= 0)
186 return CMD_RET_USAGE
;
188 if (z80_bus_state() & ZST_RUNNING
) {
189 if (!(options
& O_SILENT
))
190 printf_P(PSTR("Frequency measuring failed. CPU allready running!\n"));
191 return CMD_RET_FAILURE
;
194 ATOMIC_BLOCK(ATOMIC_FORCEON
) {
195 /* Save state and disable INT5/INT6 */
201 z80_bus_cmd(Request
);
202 if (options
& O_LOAD_LOOP
) {
203 z80_read_block(mem_save
, 0, ARRAY_SIZE(loop_code
));
204 z80_write_block_P(loop_code
, 0, ARRAY_SIZE(loop_code
));
206 EIFR
= _BV(INTF5
); /* Reset pending int */
207 z80_bus_cmd(Release
);
210 clear_ctrlc(); /* forget any previous Control C */
213 /* Wait for falling edge */
215 /* check for ctrl-c to abort... */
216 if (had_ctrlc() || ctrlc()) {
220 } while ((EIFR
& _BV(INTF5
)) == 0);
222 uint32_t cpu_freq
= 0;
224 cpu_freq
= z80_measure_phi(lcycles
);
227 if (options
& O_UNLOAD_LOOP
) {
228 z80_bus_cmd(Request
);
229 z80_write_block(mem_save
, 0, ARRAY_SIZE(loop_code
));
230 z80_bus_cmd(Release
);
232 ATOMIC_BLOCK(ATOMIC_FORCEON
) {
233 /* Restore INT5/INT6 */
234 if ((eimsk_save
& _BV(INT5
)) != 0)
236 if ((eimsk_save
& _BV(INT6
)) != 0)
238 /* Reset pending int */
243 Stat
&= ~S_MSG_PENDING
;
244 Stat
&= ~S_CON_PENDING
;
247 cmd_error(CMD_RET_FAILURE
, err
, NULL
);
249 if (!(options
& O_SILENT
)) {
250 uint8_t sc
= cpu_freq
>> 28;
251 printf_P(PSTR("%lu %3u\n"), cpu_freq
& 0x0fffffff, sc
);
254 if (options
& O_WENV
) {
255 if (setenv_ulong(PSTR(ENV_CPU_FREQ
), cpu_freq
)) {
256 if (!(options
& O_SILENT
))
257 printf_P(PSTR("'SETENV (%S, %lu)' failed!\n"), PSTR(ENV_CPU_FREQ
), cpu_freq
);
258 return CMD_RET_FAILURE
;
262 return CMD_RET_SUCCESS
;
265 static const FLASH
char * const FLASH cpu_strings
[] = {
276 command_ret_t
do_cpuchk(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc UNUSED
, char * const argv
[] UNUSED
)
278 uint_fast8_t cputype
= 0;
279 ERRNUM err
= ESUCCESS
;
281 uint8_t ram_save
[cpuinfo_length
];
283 if (z80_bus_state() & ZST_RUNNING
) {
286 z80_bus_request_or_exit();
287 z80_read_block(ram_save
, 0, cpuinfo_length
);
288 z80_load_mem(0, cpuinfo
,
291 cpuinfo_length_of_sections
);
292 z80_bus_cmd(Release
);
294 if (argv
[1] && (argv
[1][0] == 'n'))
297 ATOMIC_BLOCK(ATOMIC_FORCEON
) {
298 /* Save state and disable INT5/INT6 */
303 EIFR
= _BV(INTF5
); /* Reset pending int */
306 clear_ctrlc(); /* forget any previous Control C */
308 /* check for ctrl-c to abort... */
309 if (had_ctrlc() || ctrlc()) {
313 } while ((EIFR
& _BV(INTF5
)) == 0);
315 ATOMIC_BLOCK(ATOMIC_FORCEON
) {
316 /* Restore INT5/INT6 */
317 if ((eimsk_save
& _BV(INT5
)) != 0)
319 if ((eimsk_save
& _BV(INT6
)) != 0)
321 /* Reset pending int */
325 Stat
&= ~S_MSG_PENDING
;
326 Stat
&= ~S_CON_PENDING
;
327 z80_bus_cmd(Request
);
328 if (z80_read(3) == 0xFF) {
329 cputype
= z80_read(4);
331 z80_write_block(ram_save
, 0, cpuinfo_length
);
332 z80_bus_cmd(Release
);
338 cmd_error(CMD_RET_FAILURE
, err
, NULL
);
340 if (cputype
>= ARRAY_SIZE(cpu_strings
))
342 printf_P(PSTR("Detected CPU: %S\n"), cpu_strings
[cputype
]);
344 return CMD_RET_SUCCESS
;
347 command_ret_t
do_cpu_test(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
350 uint32_t pulsewidth
= 10; /* ms */
353 while ((opt
= getopt(argc
, argv
, PSTR("t:"))) != -1) {
356 pulsewidth
= eval_arg(optarg
, NULL
);
359 return CMD_RET_USAGE
;
363 if ((z80_bus_state() & ZST_ACQUIRED
) != RESET
)
364 cmd_error(CMD_RET_FAILURE
, ERUNNING
, NULL
);
366 clear_ctrlc(); /* forget any previous Control C */
368 z80_bus_cmd(Request
);
369 test_delay(pulsewidth
);
370 z80_bus_cmd(Release
);
371 test_delay(pulsewidth
);
372 } while (!(had_ctrlc() || ctrlc()));
374 return CMD_RET_SUCCESS
;
377 command_ret_t
do_bus_test(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc UNUSED
, char * const argv
[] UNUSED
)
383 while ((opt
= getopt(argc
, argv
, PSTR("t:"))) != -1) {
386 pulsewidth
= eval_arg(optarg
, NULL
);
389 return CMD_RET_USAGE
;
395 " 1: RESET 4: RUN r: Toggle /RESET\n"
396 " 2: REQUEST 5: RESTART b: Toggle /BUSREQ\n"
397 " 3: RELEASE 6: M_CYCLE\n"
406 case '1': /* bus_cmd RESET */
407 case '2': /* bus_cmd REQUEST */
408 case '3': /* bus_cmd RELEASE */
409 case '4': /* bus_cmd RUN */
410 case '5': /* bus_cmd RESTART */
411 case '6': /* bus_cmd M_CYCLE */
412 z80_bus_cmd(ch
- '1' + Reset
);
414 case 'r': /* Toggle RESET */
417 case 'b': /* Toggle BUSREQ */
422 uint32_t cycles
= z80_get_busreq_cycles();
423 printf_P(PSTR("\rState: %.2x, cycles: %lu, time: %luus "),
424 z80_bus_state(), cycles
, (uint32_t) (cycles
* 1000000LL / F_CPU
));
426 } while (ch
!= 0x03);
429 return CMD_RET_SUCCESS
;
432 command_ret_t
do_busack_test(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc UNUSED
, char * const argv
[] UNUSED
)
435 if ((z80_bus_state() & ZST_ACQUIRED
) != RESET
)
436 cmd_error(CMD_RET_FAILURE
, ERUNNING
, NULL
);
438 z80_bus_cmd(Request
);
439 uint32_t result
= z80_get_busreq_cycles();
441 z80_bus_cmd(Release
);
446 pinconf
= gpio_config_get(pin
);
447 if (pinconf
== OUTPUT_TIMER
) {
448 div
= gpio_clockdiv_get(pin
);
453 printf_P(PSTR("cycles: %lu, time: %luus\n"), result
, (uint32_t) (result
* 1000000LL / F_CPU
));
455 return CMD_RET_SUCCESS
;
460 * command table for subcommands
463 cmd_tbl_t cmd_tbl_cpu
[] = {
465 chkcpu
, CONFIG_SYS_MAXARGS
, CTBL_RPT
|CTBL_SUBCMDAUTO
, do_cpuchk
,
466 "Check/Identify CPU",
470 buscmd
, CONFIG_SYS_MAXARGS
, CTBL_RPT
, do_bus_test
,
475 test
, CONFIG_SYS_MAXARGS
, 1, do_cpu_test
,
476 "Do bus request/release cycles",
480 busack
, 2, 1, do_busack_test
,
481 "Get time from /Reset high to /BUSACK low",
485 freq
, CONFIG_SYS_MAXARGS
, 1, do_cpu_freq
,
486 "Measure cpu frequency",
487 "[-qwn] [-c loopcycles] [-t timeout]\n"
489 // " -w Write result to environment variable '"ENV_CPU_FREQ"'"
493 help
, CONFIG_SYS_MAXARGS
, CTBL_RPT
, do_help
,
494 "Print sub command description/usage",
496 " - print brief description of all sub commands\n"
497 "fat help command ...\n"
498 " - print detailed usage of sub cmd 'command'"
501 /* This does not use the CMD_TBL_ITEM macro as ? can't be used in symbol names */
502 {FSTR("?"), CONFIG_SYS_MAXARGS
, 1, do_help
,
504 #ifdef CONFIG_SYS_LONGHELP
506 #endif /* CONFIG_SYS_LONGHELP */
508 #ifdef CONFIG_AUTO_COMPLETE
512 /* Mark end of table */
513 CMD_TBL_END(cmd_tbl_cpu
)
517 command_ret_t
do_cpu(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc UNUSED
, char * const argv
[] UNUSED
)
519 //puts_P(PSTR("Huch?"));
521 return CMD_RET_USAGE
;
525 #if 0 /* Z180 Single Step Functions */
527 * Z180 Single Step Functions
537 #define DDR_STEP DDRG
540 #define DDR_WAIT DDRG
541 /* All three signals are on the same Port (PortG) */
542 #define PORT_SS PORTG
546 static bool ss_available
;
548 int single_step_setup(void)
550 ss_available
= false;
553 if (z80_bus_state() & ZST_RUNNING
||
554 !(z80_bus_cmd(Request
) & ZST_ACQUIRED
))
558 /* STEP, RUN output, WAIT input */
560 PORT_SS
|= _BV(RUN
) | _BV(STEP
);
561 DDR_SS
|= _BV(RUN
) | _BV(STEP
);
562 DDR_SS
&= ~_BV(WAIT
);
564 /* RUN high, MREQ pulse --> WAIT should be low */
567 if ((PIN_SS
& _BV(WAIT
)) == 0) {
569 /* RUN high, STEP pulse --> WAIT should be high */
572 if ((PIN_SS
& _BV(WAIT
)) != 0) {
574 /* RUN high, MREQ pulse --> WAIT should be low */
576 if ((PIN_SS
& _BV(WAIT
)) == 0) {
578 /* RUN low --> WAIT should be high */
580 if ((PIN_SS
& _BV(WAIT
)) != 0) {
582 /* RUN low, STEP pulse --> WAIT should be high */
585 if ((PIN_SS
& _BV(WAIT
)) != 0) {
587 /* all tests passed */
596 DDR_SS
&= ~(_BV(STEP
) | _BV(RUN
));
597 PORT_SS
|= _BV(RUN
) | _BV(STEP
);
600 return ss_available
? 0 : -1;