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