2 * (C) Copyright 2018 Leo C. <erbl259-lmu@yahoo.de>
4 * SPDX-License-Identifier: GPL-2.0
8 #include <util/atomic.h>
11 #include "con-utils.h"
15 #include "getopt-min.h"
18 /* hack to get Z180 loadfile into flash memory */
19 #define const const FLASH
20 #include "../z180/cpuinfo.h"
23 #define DEBUG_CPU 1 /* set to 1 to debug */
25 #define debug_cpu(fmt, args...) \
26 debug_cond(DEBUG_CPU, fmt, ##args)
29 char * ulltoa (uint64_t val
, char *s
)
34 *p
++ = (val
% 10) + '0';
44 * delay for <count> ms...
46 static void test_delay(uint32_t count
)
48 uint32_t ts
= get_timer(0);
50 while (get_timer(ts
) <= count
);
53 static uint32_t z80_measure_phi(uint_fast8_t cycles
)
63 TCCR3B
= 0b000<<CS30
; /* stop counter */
69 ATOMIC_BLOCK(ATOMIC_FORCEON
) {
70 EIFR
= _BV(INTF6
); /* Reset pending int */
71 while ((EIFR
& _BV(INTF6
)) == 0) /* Wait for falling edge */
74 TCCR3B
= 0b110<<CS30
; /* Count falling edges on T3 (==INT6) */
75 TIFR4
= _BV(OCF4B
); /* clear compare match flag */
77 while (ref_ovfl
< 60) {
78 if ((TIFR4
& _BV(OCF4B
)) != 0) {
82 if ((TIFR3
& _BV(TOV3
)) != 0) {
90 if (EIFR
& _BV(INTF6
)) {
91 TCCR3B
= 0b000<<CS30
; /* stop counter */
95 if ((TIFR4
& _BV(OCF4B
)) != 0) {
102 if ((TIFR3
& _BV(TOV3
)) != 0) {
107 uint32_t ref_cnt
= (ref_stop
- OCR4B
) + ((uint32_t)ref_ovfl
<< 16);
108 uint32_t x_cnt
= TCNT3
+ ((uint32_t) x_ovfl
<< 16);
109 uint64_t x_tmp
= (uint64_t) 100000 * (x_cnt
* cycles
);
115 // char x_tmp_str[21];
117 // debug_cpu("TCNT3: %6u, ref_cnt: %9lu\n", TCNT3, ref_cnt);
118 // ulltoa(x_tmp, x_tmp_str);
119 // debug_cpu("x_tmp: %s\n", x_tmp_str);
121 x_tmp
= (x_tmp
* getenv_ulong(PSTR(ENV_FMON
), 10, F_CPU
) + (ref_cnt
/ 2)) / ref_cnt
;
123 // ulltoa(x_tmp, x_tmp_str);
124 // debug_cpu("x_tmp: %s\n", x_tmp_str);
126 /* round to 5 decimal digits */
128 for ( ; sc
> 0 || x_tmp
>= 100000; sc
--) x_tmp
= (x_tmp
+ 5)/10;
130 for ( ; sc
< 0; sc
++) x_freq
*= 10;
135 static const FLASH
char * const FLASH cpu_strings
[] = {
146 #define O_SILENT (1<<0)
147 #define O_WENV (1<<1)
148 #define O_LOAD_LOOP (1<<2)
149 #define O_UNLOAD_LOOP (1<<3)
151 static const FLASH
char * const FLASH opt_strings
[] = {
152 FSTR("swnu"), /* Options for chkcpu */
153 FSTR("swnuc:"), /* Oprions for cpufreq */
156 static const FLASH
char * const FLASH env_names
[] = {
157 FSTR(ENV_CPU
), /* Env var for chkcpu result */
158 FSTR(ENV_CPU_FREQ
), /* Env var for cpufreq result */
161 command_ret_t
do_cpu_freq_chk(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
163 uint_fast8_t options
= O_LOAD_LOOP
| O_UNLOAD_LOOP
;
164 uint_fast8_t cputype
= 0;
165 uint32_t cpu_freq
= 0;
166 uint_fast8_t lcycles
= 0;
167 uint_fast8_t freq_cmd
= 0;
168 // uint16_t timeout = 1000;
170 ERRNUM err
= ESUCCESS
;
172 if (argv
[0][0] == 'f')
176 while ((opt
= getopt(argc
, argv
, opt_strings
[freq_cmd
])) != -1) {
185 options
&= ~O_LOAD_LOOP
;
188 options
&= ~O_UNLOAD_LOOP
;
191 lcycles
= eval_arg(optarg
, NULL
);
194 // timeout = eval_arg(optarg, NULL);
197 return CMD_RET_USAGE
;
200 if (argc
- optind
!= 0)
201 return CMD_RET_USAGE
;
203 if (z80_bus_state() & ZST_RUNNING
)
204 cmd_error(CMD_RET_FAILURE
, ERUNNING
, NULL
);
206 uint8_t *mem_save
= NULL
;
207 if (options
& O_LOAD_LOOP
) {
208 mem_save
= (uint8_t *) malloc(cpuinfo_length
);
209 if (mem_save
== NULL
)
210 cmd_error(CMD_RET_FAILURE
, ENOMEM
, NULL
);
211 z80_bus_cmd(Request
);
212 z80_read_block(mem_save
, 0, cpuinfo_length
);
213 z80_load_mem(0, cpuinfo
, &cpuinfo_sections
, cpuinfo_address
,
214 cpuinfo_length_of_sections
);
215 z80_bus_cmd(Release
);
218 /* Save state and disable INT5/INT6 */
219 ATOMIC_BLOCK(ATOMIC_FORCEON
) {
224 EIFR
= _BV(INTF5
); /* Reset pending int */
228 clear_ctrlc(); /* forget any previous Control C */
229 /* Wait for falling edge */
231 /* check for ctrl-c to abort... */
232 if (had_ctrlc() || ctrlc()) {
236 } while ((EIFR
& _BV(INTF5
)) == 0);
240 z80_bus_cmd(Request
);
241 if (z80_read(3) == 0xFF)
242 lcycles
= z80_read(5);
243 z80_bus_cmd(Release
);
246 cpu_freq
= z80_measure_phi(lcycles
);
250 /* Restore INT5/INT6 */
251 ATOMIC_BLOCK(ATOMIC_FORCEON
) {
252 if ((eimsk_save
& _BV(INT5
)) != 0)
254 if ((eimsk_save
& _BV(INT6
)) != 0)
256 /* Reset pending int */
260 Stat
&= ~S_MSG_PENDING
;
261 Stat
&= ~S_CON_PENDING
;
264 z80_bus_cmd(Request
);
265 if (z80_read(3) == 0xFF)
266 cputype
= z80_read(4);
267 z80_bus_cmd(Release
);
270 if ((mem_save
!= NULL
) && options
& O_UNLOAD_LOOP
) {
271 z80_bus_cmd(Request
);
272 z80_write_block(mem_save
, 0, cpuinfo_length
);
273 z80_bus_cmd(Release
);
278 cmd_error(CMD_RET_FAILURE
, err
, NULL
);
283 ultoa(cpu_freq
, result_str
, 10);
285 if (cputype
>= ARRAY_SIZE(cpu_strings
))
287 strcpy_P(result_str
, cpu_strings
[cputype
]);
290 if (!(options
& O_SILENT
))
291 printf_P(PSTR("%s\n"), result_str
);
293 if (options
& O_WENV
) {
294 if (setenv(env_names
[freq_cmd
], result_str
)) {
295 if (!(options
& O_SILENT
)) {
296 printf_P(PSTR("'setenv %S %s' failed!\n"), env_names
[freq_cmd
], result_str
);
297 //cmd_error(CMD_RET_FAILURE, ENOMEM, PSTR("'setenv (%S, %s)' failed"), env_names[freq_cmd], result_str);
299 return CMD_RET_FAILURE
;
303 return CMD_RET_SUCCESS
;
306 command_ret_t
do_cpu_test(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
309 uint32_t pulsewidth
= 10; /* ms */
312 while ((opt
= getopt(argc
, argv
, PSTR("t:"))) != -1) {
315 pulsewidth
= eval_arg(optarg
, NULL
);
318 return CMD_RET_USAGE
;
322 if ((z80_bus_state() & ZST_ACQUIRED
) != RESET
)
323 cmd_error(CMD_RET_FAILURE
, ERUNNING
, NULL
);
325 clear_ctrlc(); /* forget any previous Control C */
327 z80_bus_cmd(Request
);
328 test_delay(pulsewidth
);
329 z80_bus_cmd(Release
);
330 test_delay(pulsewidth
);
331 } while (!(had_ctrlc() || ctrlc()));
333 return CMD_RET_SUCCESS
;
336 command_ret_t
do_bus_test(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc UNUSED
, char * const argv
[] UNUSED
)
342 while ((opt
= getopt(argc
, argv
, PSTR("t:"))) != -1) {
345 pulsewidth
= eval_arg(optarg
, NULL
);
348 return CMD_RET_USAGE
;
354 " 1: RESET 4: RUN r: Toggle /RESET\n"
355 " 2: REQUEST 5: RESTART b: Toggle /BUSREQ\n"
356 " 3: RELEASE 6: M_CYCLE\n"
365 case '1': /* bus_cmd RESET */
366 case '2': /* bus_cmd REQUEST */
367 case '3': /* bus_cmd RELEASE */
368 case '4': /* bus_cmd RUN */
369 case '5': /* bus_cmd RESTART */
370 case '6': /* bus_cmd M_CYCLE */
371 z80_bus_cmd(ch
- '1' + Reset
);
373 case 'r': /* Toggle RESET */
376 case 'b': /* Toggle BUSREQ */
381 uint32_t cycles
= z80_get_busreq_cycles();
382 printf_P(PSTR("\rState: %.2x, cycles: %lu, time: %luus "),
383 z80_bus_state(), cycles
, (uint32_t) (cycles
* 1000000LL / F_CPU
));
385 } while (ch
!= 0x03);
388 return CMD_RET_SUCCESS
;
391 command_ret_t
do_busack_test(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc UNUSED
, char * const argv
[] UNUSED
)
394 if ((z80_bus_state() & ZST_ACQUIRED
) != RESET
)
395 cmd_error(CMD_RET_FAILURE
, ERUNNING
, NULL
);
397 z80_bus_cmd(Request
);
398 uint32_t result
= z80_get_busreq_cycles();
400 z80_bus_cmd(Release
);
405 pinconf
= gpio_config_get(pin
);
406 if (pinconf
== OUTPUT_TIMER
) {
407 div
= gpio_clockdiv_get(pin
);
412 printf_P(PSTR("cycles: %lu, time: %luus\n"), result
, (uint32_t) (result
* 1000000LL / F_CPU
));
414 return CMD_RET_SUCCESS
;
419 * command table for subcommands
421 cmd_tbl_t cmd_tbl_cpu
[] = {
423 freq
, CONFIG_SYS_MAXARGS
, CTBL_RPT
, do_cpu_freq_chk
,
424 "Measure cpu frequency",
425 // "[-swnu] [-c loopcycles] [-t timeout]\n"
426 "[-swnu] [-c loopcycles]\n"
428 " -w Write result to environment variable '"ENV_CPU_FREQ
"'\n"
429 " -n Don't load code snippet. \n"
430 " -u Don't unload. Leave code snippet in ram.\n"
431 " -c Overwrite cycles per lopp for in \"l: a,(50h)/jp l\" loop."
432 // " -t Timeout (ms)\n"
435 chkcpu
, CONFIG_SYS_MAXARGS
, CTBL_RPT
|CTBL_SUBCMDAUTO
, do_cpu_freq_chk
,
436 "Check/Identify CPU",
437 // "[-swnu] [-c loopcycles] [-t timeout]\n"
438 "[-swnu] [-c loopcycles]\n"
440 " -w Write result to environment variable '"ENV_CPU
"'\n"
441 " -n Don't load code snippet. \n"
442 " -u Don't unload. Leave code snippet in ram."
443 // " -t Timeout (ms)\n"
446 buscmd
, CONFIG_SYS_MAXARGS
, CTBL_RPT
, do_bus_test
,
451 test
, CONFIG_SYS_MAXARGS
, CTBL_RPT
, do_cpu_test
,
452 "Do bus request/release cycles",
456 busack
, 2, CTBL_RPT
, do_busack_test
,
457 "Get time from /Reset high to /BUSACK low",
462 help
, CONFIG_SYS_MAXARGS
, CTBL_RPT
, do_help
,
463 "Print sub command description/usage",
465 " - print brief description of all sub commands\n"
466 "fat help command ...\n"
467 " - print detailed usage of sub cmd 'command'"
470 /* This does not use the CMD_TBL_ITEM macro as ? can't be used in symbol names */
471 {FSTR("?"), CONFIG_SYS_MAXARGS
, CTBL_RPT
, do_help
,
473 #ifdef CONFIG_SYS_LONGHELP
475 #endif /* CONFIG_SYS_LONGHELP */
477 #ifdef CONFIG_AUTO_COMPLETE
481 /* Mark end of table */
482 CMD_TBL_END(cmd_tbl_cpu
)
486 command_ret_t
do_cpu(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc UNUSED
, char * const argv
[] UNUSED
)
488 //puts_P(PSTR("Huch?"));
490 return CMD_RET_USAGE
;
494 #if 0 /* Z180 Single Step Functions */
496 * Z180 Single Step Functions
506 #define DDR_STEP DDRG
509 #define DDR_WAIT DDRG
510 /* All three signals are on the same Port (PortG) */
511 #define PORT_SS PORTG
515 static bool ss_available
;
517 int single_step_setup(void)
519 ss_available
= false;
522 if (z80_bus_state() & ZST_RUNNING
||
523 !(z80_bus_cmd(Request
) & ZST_ACQUIRED
))
527 /* STEP, RUN output, WAIT input */
529 PORT_SS
|= _BV(RUN
) | _BV(STEP
);
530 DDR_SS
|= _BV(RUN
) | _BV(STEP
);
531 DDR_SS
&= ~_BV(WAIT
);
533 /* RUN high, MREQ pulse --> WAIT should be low */
536 if ((PIN_SS
& _BV(WAIT
)) == 0) {
538 /* RUN high, STEP pulse --> WAIT should be high */
541 if ((PIN_SS
& _BV(WAIT
)) != 0) {
543 /* RUN high, MREQ pulse --> WAIT should be low */
545 if ((PIN_SS
& _BV(WAIT
)) == 0) {
547 /* RUN low --> WAIT should be high */
549 if ((PIN_SS
& _BV(WAIT
)) != 0) {
551 /* RUN low, STEP pulse --> WAIT should be high */
554 if ((PIN_SS
& _BV(WAIT
)) != 0) {
556 /* all tests passed */
565 DDR_SS
&= ~(_BV(STEP
) | _BV(RUN
));
566 PORT_SS
|= _BV(RUN
) | _BV(STEP
);
569 return ss_available
? 0 : -1;