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