From: Michel Pollet Date: Wed, 29 Mar 2017 10:19:14 +0000 (+0100) Subject: shavr: avr shell prototype X-Git-Url: https://git.htl-mechatronik.at/public/?a=commitdiff_plain;h=670668846c62f42c84d75c17e97373bc0ca99f1b;p=sx%2Fsimavr.git shavr: avr shell prototype Some bits I spliced out of simreprap to have a shell to interract with a running firmware. Very prototype, just a placeholder for now. Signed-off-by: Michel Pollet --- diff --git a/Makefile.common b/Makefile.common index 9b56e27..8d32924 100644 --- a/Makefile.common +++ b/Makefile.common @@ -41,7 +41,7 @@ ARCH := ${shell $(CC) -dumpmachine | sed -e 's/^x/i/' -e 's/\(.\).*/\1/'} CFLAGS += -O2 -Wall -Wextra -Wno-unused-parameter \ -Wno-unused-result -Wno-missing-field-initializers \ - -Wno-sign-compare + -Wno-sign-compare -Wno-unused-function CFLAGS += -g CORE_CFLAGS = -DAVR_CORE=1 diff --git a/shavr/Makefile b/shavr/Makefile new file mode 100644 index 0000000..79f05ea --- /dev/null +++ b/shavr/Makefile @@ -0,0 +1,47 @@ +# +# Copyright 2008, 2009 Michel Pollet +# +# This file is part of simavr. +# +# simavr is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# simavr is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with simavr. If not, see . + +target = shavr +simavr = ../ + +IPATH = . +IPATH += ${simavr}/include +IPATH += ${simavr}/simavr/sim +IPATH += ${simavr}/simavr/ + +VPATH = src + +srcs := ${wildcard src/*.c} +LDFLAGS += -lpthread -lutil + +all: obj ${target} + +include ${simavr}/Makefile.common + +objs := ${patsubst src/%.c, ${OBJ}/%.o, ${srcs}} + +board = ${OBJ}/${target}.elf + +${board} : ${objs} +${board} : ${OBJ}/${target}.o + +${target}: ${board} + @echo $@ done + +clean: clean-${OBJ} + rm -rf *.a *.axf ${target} *.vcd *.hex diff --git a/shavr/src/hcmd_adc.c b/shavr/src/hcmd_adc.c new file mode 100644 index 0000000..4bdd3d7 --- /dev/null +++ b/shavr/src/hcmd_adc.c @@ -0,0 +1,93 @@ +/* + * hcmd_adc.c + * + * Created on: 15 Oct 2015 + * Author: michel + */ + +#include +#include +#include +#include +#include +#include + +#include "history_avr.h" +#include "avr_adc.h" + +static uint16_t adc_mask = 0; +static uint16_t adc_val[16]; +struct avr_irq_t * adc0_irq = NULL; + +static void +adc_update_hook( + struct avr_irq_t * irq, + uint32_t value, + void * param) +{ + union { + avr_adc_mux_t mux; + uint32_t v; + } m = { .v = value }; +// /printf("read mux %d\r\n", m.mux.src); + if (!adc0_irq) + adc0_irq = avr_io_getirq(avr, AVR_IOCTL_ADC_GETIRQ, ADC_IRQ_ADC0); + if (adc_mask & (1 << m.mux.src)) { + int noise = 0; //(random() % 3) - 1; + int adc = (int)adc_val[m.mux.src] + noise; + // printf("ADC %d %d:%d\n", m.mux.src, adc, noise); + avr_raise_irq(adc0_irq + m.mux.src, noise < 0 ? 0 : adc); + } +} + +/* + * called when the AVR change any of the pins we listen to + */ +static void +pin_changed_hook( + struct avr_irq_t * irq, + uint32_t value, + void * param) +{ + // pin_state = (pin_state & ~(1 << irq->irq)) | (value << irq->irq); + AVR_LOG(avr, LOG_OUTPUT, "PORT B%d changed to %d\n", irq->irq, value); +} + + +static int +_cmd_adc( + wordexp_t * l) +{ + char *cmd = l->we_wordv[0]; + char * it = l->we_wordv[1]; + int index = 0; + if (!sscanf(it, "%d", &index) || index > 15) { + fprintf(stderr, "%s: invalid adc number '%s'\r\n", cmd, it); + } else { + char * val = l->we_wordv[2]; + if (val) { + int v = 0; + if (!sscanf(val, "%d", &v) || v < 0 || v > 5500) { + fprintf(stderr, "%s: invalid adc value '%s'\r\n", cmd, + val); + } else { + adc_val[index] = v; + adc_mask |= (1 << index); + } + } + fprintf(stderr, "ADC %d = %d (0x%04x)\r\n", index, + adc_val[index], adc_val[index]); + } + return 0; +} + + +const history_cmd_t cmd_adc = { + .names[0] = "adc", + .usage = " []: Get/set ADC value", + .help = "Get and sets ADC input IRQ value", + .parameter_map = (1 << 1) | (1 << 2), + .execute = _cmd_adc, +}; +HISTORY_CMD_REGISTER(cmd_adc); + diff --git a/shavr/src/hcmd_symbols.c b/shavr/src/hcmd_symbols.c new file mode 100644 index 0000000..0cf4c8f --- /dev/null +++ b/shavr/src/hcmd_symbols.c @@ -0,0 +1,189 @@ +/* + * hcmd_symbols.c + * + * Created on: 15 Oct 2015 + * Author: michel + */ + +#include +#include +#include +#include +#include +#include + +#include "history_avr.h" + +static const char * +demangle( + const char *symbol) +{ + if (symbol[0] == '_' && symbol[1] == 'Z') { + symbol += 3; + while (isdigit(*symbol)) + symbol++; + } + return symbol; +} + +int +_locate_sym( + const char *name, + avr_symbol_t ** out, + int * outsize) +{ + int size = 1; + avr_symbol_t * sym = NULL; + for (int i = 0; i < code.symbolcount && !sym; i++) { + if (!strcmp(demangle(code.symbol[i]->symbol), name)) { + sym = code.symbol[i]; + if (i < code.symbolcount-1) { + if ((code.symbol[i+1]->addr >> 16) == (sym->addr >> 16)) { + size = code.symbol[i+1]->addr - sym->addr; + // printf("deduced size %d\r\n", size); + } + } + } + } + *out = sym; + *outsize = size; + + return sym != NULL; +} + +static int +_cmd_print( + wordexp_t * l) +{ + char *cmd = l->we_wordv[0]; + char * nt = l->we_wordv[1]; + char * st = l->we_wordv[2]; + + int size = 1; + avr_symbol_t * sym = NULL; + _locate_sym(nt, &sym, &size); + + if (st) { + if (!strncmp(st, "0x", 2)) { + st += 2; + sscanf(st, "%x", &size); + } else if (st[0] == 'x') { + st++; + sscanf(st, "%x", &size); + } else + sscanf(st, "%d", &size); + } + + if (!sym) { + fprintf(stderr, "%s: invalid symbol name '%s'\r\n", cmd, nt); + } else if ((sym->addr >> 16) != 0x80) { + fprintf(stderr, "%s: '%s' is not in sram\r\n", cmd, nt); + } else { + int offset = sym->addr & 0xffff; + printf("%s (%04x): ", demangle(sym->symbol), offset); + if (size > 23) + printf("\r\n "); + for (int i = 0; i < size; i++, offset++) + printf("%s%02x%s", + i > 1 && (i % 32) == 1 ? " " : "", + avr->data[offset], + i > 1 && (i % 32) == 0 ? "\r\n" : " "); + printf("\r\n"); + } + return 0; +} + + +const history_cmd_t cmd_print = { + .names = { "print", "p" }, + .usage = " [size]: print SRAM variable data", + .help = "Prints SRAM variable", + .parameter_map = (1 << 1) | (1 << 2), + .execute = _cmd_print, +}; +HISTORY_CMD_REGISTER(cmd_print); + + +static int +_cmd_set( + wordexp_t * l) +{ + if (l->we_wordc < 3) { + fprintf(stderr, "%s: invalid syntax\r\n", l->we_wordv[0]); + return 0; + } + char *cmd = l->we_wordv[0]; + char * nt = l->we_wordv[1]; + + int size = 1; + avr_symbol_t * sym = NULL; + _locate_sym(nt, &sym, &size); + + if (!sym) { + fprintf(stderr, "%s: invalid symbol name '%s'\r\n", cmd, nt); + } else if ((sym->addr >> 16) != 0x80) { + fprintf(stderr, "%s: '%s' is not in sram\r\n", cmd, nt); + } else { + int offset = sym->addr & 0xffff; + printf("%s (%04x): ", demangle(sym->symbol), offset); + + for (int i = 2; i < l->we_wordc; i++) { + char * ht = l->we_wordv[i]; + + while (isxdigit(ht[0]) && isxdigit(ht[1])) { + const char *hex = "0123456789abcdef"; + uint8_t b = ((index(hex, tolower(ht[0])) - hex) << 4) | + (index(hex, tolower(ht[1])) - hex); + avr->data[offset++] = b; + printf("%02x", b); + ht += 2; + } + } + printf("\r\n"); + } + return 0; +} + + +const history_cmd_t cmd_set = { + .names = { "set" }, + .usage = " : set SRAM variable data", + .help = "Sets SRAM variable", + .parameter_map = 0, // any numbers + .execute = _cmd_set, +}; +HISTORY_CMD_REGISTER(cmd_set); + +static int +_cmd_dump( + wordexp_t * l) +{ + int size = 1; + avr_symbol_t * sym = NULL; + for (int i = 0; i < code.symbolcount; i++) { + sym = code.symbol[i]; + size = 0; + if (i < code.symbolcount-1) { + if ((code.symbol[i+1]->addr >> 16) == (sym->addr >> 16)) + size = code.symbol[i+1]->addr - sym->addr; + } + if ((sym->addr >> 16) == 0x80 && + strncmp(sym->symbol, "__", 2) && + sym->symbol[0] != ' ' && + strcmp(sym->symbol, "_edata") && + strcmp(sym->symbol, "_end")) + printf("%04x %s (%d bytes)\n", + sym->addr, demangle(sym->symbol), size); + } + + return 0; +} + +const history_cmd_t cmd_dump = { + .names = { "dump", "du" }, + .usage = "dump SRAM ELF symbols and matching size", + .help = "dump SRAM ELF symbols and matching size", + .parameter_map = 0, // any numbers + .execute = _cmd_dump, +}; +HISTORY_CMD_REGISTER(cmd_dump); diff --git a/shavr/src/history.c b/shavr/src/history.c new file mode 100644 index 0000000..812697a --- /dev/null +++ b/shavr/src/history.c @@ -0,0 +1,337 @@ +/* + * history.c + * + * Created on: 15 Oct 2015 + * Author: michel + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "history.h" + +static struct termios orig_termios; /* TERMinal I/O Structure */ + +void fatal(char *message) { + fprintf(stderr, "fatal error: %s\n", message); + exit(1); +} + + +/* put terminal in raw mode - see termio(7I) for modes */ +static void +tty_raw(void) +{ + struct termios raw; + + raw = orig_termios; /* copy original and then modify below */ + + /* input modes - clear indicated ones giving: no break, no CR to NL, + no parity check, no strip char, no start/stop output (sic) control */ + raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); + + /* Contrary to normal raw mode, we want to keep the post processing on output + * otherwise the printf() all over fail due to their lack of \r + */ +// raw.c_oflag &= ~(OPOST); + + /* control modes - set 8 bit chars */ + raw.c_cflag |= (CS8); + + /* local modes - clear giving: echoing off, canonical off (no erase with + backspace, ^U,...), no extended functions, no signal chars (^Z,^C) */ + raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); + + /* control chars - set return condition: min number of bytes and timer */ +#if 0 + raw.c_cc[VMIN] = 5; + raw.c_cc[VTIME] = 8; /* after 5 bytes or .8 seconds + after first byte seen */ + raw.c_cc[VMIN] = 0; + raw.c_cc[VTIME] = 0; /* immediate - anything */ + raw.c_cc[VMIN] = 2; + raw.c_cc[VTIME] = 0; /* after two bytes, no timer */ +#endif + raw.c_cc[VMIN] = 0; + raw.c_cc[VTIME] = 8; /* after a byte or .8 seconds */ + + /* put terminal in raw mode after flushing */ + if (tcsetattr(1, TCSAFLUSH, &raw) < 0) + fatal("can't set raw mode"); +} + +/* reset tty - useful also for restoring the terminal when this process + wishes to temporarily relinquish the tty +*/ +static int +tty_reset(void) +{ + /* flush and reset */ + if (tcsetattr(1, TCSAFLUSH, &orig_termios) < 0) + return -1; + return 0; +} + +/* exit handler for tty reset */ +static void +tty_atexit(void) /* NOTE: If the program terminates due to a signal */ +{ /* this code will not run. This is for exit()'s */ + tty_reset(); /* only. For resetting the terminal after a signal, */ +} /* a signal handler which calls tty_reset is needed. */ + +static line_p +line_new( + history_p h) +{ + line_p res = malloc(sizeof(line_t)); + memset(res, 0, sizeof(*res)); + res->prev = h->tail; + if (!h->tail) + h->head = res; + else + h->tail->next = res; + h->tail = res; + h->current = res; + return res; +} + + +line_p +line_dup( + history_p h, + line_p l) +{ + line_p n = line_new(h); + n->pos = l->pos; + n->size = l->size; + n->len = l->len; + n->line = malloc(n->size); + memcpy(n->line, l->line, n->size); + h->current = n; + return n; +} + +void +history_display( + history_p h) +{ + char s[16]; + int pl = strlen(h->prompt); + write(h->ttyout, "\033[0m\r", 5); + write(h->ttyout, h->prompt, pl); + if (h->current) { + write(h->ttyout, h->current->line, h->current->len); + pl += h->current->pos; + } + // kill rest of line, return to 0, move back to X + sprintf(s, "\033[K\r\033[%dC", pl); + write(h->ttyout, s, strlen(s)); +} + +/* + * this is a redone skeleton protothread bunch of macros + * this will only work with gcc and clang. Use the old, bigger + * protothread if porting to another compiler + */ +// these are to force preprocessor to evaluate +#define PP_SUB_CONCAT(p1, p2) p1##p2 +#define PP_CONCAT(p1, p2) PP_SUB_CONCAT(p1, p2) + +#define PT_START(__s) do { if (__s) goto *__s; } while (0) +#define PT_END(__s) PP_CONCAT(_e, __FUNCTION__): +#define PT_YIELD(__s) \ + (__s) = &&PP_CONCAT(pt_, __LINE__); \ + goto PP_CONCAT(_e, __FUNCTION__); \ + PP_CONCAT(pt_, __LINE__):\ + (__s) = NULL; + +int +line_key( + history_p h, + uint8_t key) +{ + int insert = 0; + int delete = 0; + int cursor_move = 0; + int res = 0; + + if (!h->current) + line_new(h); + + PT_START(h->state); + + switch (key) { + case 0x7f: goto del_char; + case 0 ... ' '-1: { + switch(key) { + case 3: printf("\r\n"); exit(0); break; + case 8: // control h delete + del_char: + delete++; + cursor_move = -1; + break; + case 2: // control b : cursor -1 + previous_char: + cursor_move = -1; + break; + case 6: // control f : cursor + 1 + next_char: + cursor_move = 1; + break; + case 5: // control e : end of line + if (h->current) + h->current->pos = h->current->len; + break; + case 1: // control a : start of line + if (h->current) + h->current->pos = 0; + break; + case 23: {// control-W : delete word + if (!h->current->pos) + break; + char *d = h->current->line + h->current->pos - 1; + // first skip any trailing spaces + while (*d == ' ' && h->current->pos - delete > 0) { + delete++; + cursor_move -= 1; + d--; + } + // then delete any word before the cursor + while (*d != ' ' && h->current->pos - delete > 0) { + delete++; + cursor_move -= 1; + d--; + } + } break; + case 11: { // control-K : kill remains of line + delete = h->current->len - h->current->pos; + } break; + case 13: { // return!! + if (h->current->len) { + if (!h->process_line || h->process_line(h, h->current)) { + res++; + if (h->current != h->tail) + line_dup(h, h->current); + line_new(h); + } + } else + write(1, "\012", 1); // next line + } break; + case 16: { // control-P -- previous line + previous_line: + if (h->current->prev) + h->current = h->current->prev; + } break; + case 14: { // control-N -- next line + next_line: + if (h->current->next) + h->current = h->current->next; + } break; + case 27: { + PT_YIELD(h->state); + switch (key) { + case '[': { + h->temp = 0; + do { + PT_YIELD(h->state); + if (!isdigit(key)) + break; + h->temp = (h->temp*10) + (key - '0'); + } while (1); + // now handle last key of command + switch (key) { + // arrow keys + case 'A': goto previous_line; + case 'B': goto next_line; + case 'C': goto next_char; + case 'D': goto previous_char; + default: printf("Unknown Key '%d%c'\r\n", h->temp, key); break; + } + } break; + default: + printf("Unknown ESC key '%c'\r\n", key); break; + break; + } + } break; + } + } break; + default: { + insert = 1; + cursor_move = 1; + } break; + } + + if ((insert || delete) && h->current != h->tail) { + line_dup(h, h->current); + } + line_p l = h->current; + if (insert) { + if (l->len + insert >= l->size) { + while (l->len + insert >= l->size) + l->size += 8; + l->line = realloc(l->line, l->size); + } + if (l->pos < l->len) + memmove(l->line + l->pos + insert, l->line + l->pos, + l->len - l->pos + insert); + l->line[l->pos] = key; + l->len += insert; + l->line[l->len] = 0; + } + if (cursor_move) { + if (l->pos + cursor_move < 0) { + cursor_move = 0; + delete = 0; + l->pos = 0; + } + if (l->pos + cursor_move > l->len) { + cursor_move = 0; + l->pos = l->len; + } + l->pos += cursor_move; + } + if (delete) { + if (l->len - l->pos) { + memmove(l->line + l->pos, l->line + l->pos + delete, l->len - l->pos - delete); + l->len -= delete; + } + } + history_display(h); + PT_END(h->state); + return res; +} + + +void +history_init( + history_p h) +{ + atexit(tty_atexit); + + /* store current tty settings in orig_termios */ + if (tcgetattr(h->ttyout, &orig_termios) < 0) + fatal("can't get tty settings"); + atexit(tty_atexit); + tty_raw(); + + int fl = fcntl(h->ttyin, F_GETFL); + fcntl(0, F_SETFL, fl | O_ASYNC); + history_display(h); +} + +int +history_idle( + history_p h) +{ + uint8_t c; + ssize_t r = read(h->ttyin, &c, 1); + if (r <= 0) + return 0; + return line_key(h, c); +} diff --git a/shavr/src/history.h b/shavr/src/history.h new file mode 100644 index 0000000..2048c4c --- /dev/null +++ b/shavr/src/history.h @@ -0,0 +1,50 @@ +/* + * history.h + * + * Created on: 15 Oct 2015 + * Author: michel + */ + +#ifndef HISTORY_H_ +#define HISTORY_H_ + + +typedef struct line_t { + struct line_t * next; + struct line_t * prev; + + int size, len, pos; + char * line; +} line_t, *line_p; + + +typedef struct history_t { + int ttyin; + int ttyout; + + line_p head, tail; + line_p current; + char prompt[32]; + void * state; + int temp; + + void *param; + int (*process_line)(struct history_t *h, line_p l); + + //struct history_cmd_list_t * cmd_list; +} history_t, *history_p; + + +void +history_init( + history_p h); + +int +history_idle( + history_p h); + +void +history_display( + history_p h); + +#endif /* HISTORY_H_ */ diff --git a/shavr/src/history_avr.c b/shavr/src/history_avr.c new file mode 100644 index 0000000..6a67072 --- /dev/null +++ b/shavr/src/history_avr.c @@ -0,0 +1,123 @@ +/* + * hcmd_misc.c + * + * Created on: 15 Oct 2015 + * Author: michel + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "history_avr.h" + +int history_redisplay = 0; + + +static int +_history_process_line( + struct history_t *h, + line_p l ) +{ + printf("\r\n"); + history_cmd_execute(NULL, l->line); + + return 1; +} + +static void +raw_std_logger( + avr_t* avr, + const int level, + const char * format, + va_list args) +{ + if (!avr || avr->log >= level) { + FILE * d = (level > LOG_ERROR) ? stdout : stderr; + fprintf(d, "\r"); + vfprintf(d , format, args); + fprintf(d, "\r"); + history_redisplay++; + } +} + +static int +_cmd_quit( + wordexp_t * l) +{ + avr_terminate(avr); + exit(0); + return 0; +} + +const history_cmd_t cmd_quit = { + .names = { "quit", "q", "exit", }, + .usage = "quit simavr", + .help = "exits the program", + .parameter_map = 0, + .execute = _cmd_quit, +}; +HISTORY_CMD_REGISTER(cmd_quit); + +int prompt_fd = -1; +int prompt_event = 0; + +static void +callback_sleep_prompt( + avr_t * avr, + avr_cycle_count_t howLong) +{ + uint32_t usec = avr_pending_sleep_usec(avr, howLong); + if (prompt_fd == -1) { + usleep(usec); + return; + } +#if 1 + usleep(usec / 2); +#else + struct pollfd ev = { .fd = prompt_fd, .events = POLLIN }; + int r = poll(&ev, 1, usec / 1000 / 2); +// printf("poll %d %x\r\n", r, ev.revents); + if (r > 0 && ev.revents) + prompt_event++; +#endif +} + +history_t history = { + .ttyin = 0, + .ttyout = 1, + .prompt = "avr: ", + .process_line = _history_process_line, +}; + +void history_avr_init() +{ + history_init(&history); + prompt_fd = history.ttyin; +// avr->sleep = callback_sleep_prompt; + avr_global_logger_set(raw_std_logger); +} + +void history_avr_idle() +{ + int prompt_event = 0; + struct pollfd ev = { .fd = prompt_fd, .events = POLLIN }; + int r = poll(&ev, 1, 1000 / 2); +// printf("poll %d %x\r\n", r, ev.revents); + if (r > 0 && ev.revents) + prompt_event++; + if (history_redisplay) { + history_redisplay = 0; + history_display(&history); + prompt_event++; + } + if (prompt_event) + history_idle(&history); +} diff --git a/shavr/src/history_avr.h b/shavr/src/history_avr.h new file mode 100644 index 0000000..0ed153c --- /dev/null +++ b/shavr/src/history_avr.h @@ -0,0 +1,25 @@ +/* + * history_avr.h + * + * Created on: 18 Oct 2015 + * Author: michel + */ + +#ifndef _HISTORY_AVR_H_ +#define _HISTORY_AVR_H_ + +#include "sim_avr.h" +#include "sim_elf.h" +#include "history.h" +#include "history_cmd.h" + +extern elf_firmware_t code; + +extern avr_t * avr; + +extern int history_redisplay; + +void history_avr_init(); +void history_avr_idle(); + +#endif /* _HISTORY_AVR_H_ */ diff --git a/shavr/src/history_cmd.c b/shavr/src/history_cmd.c new file mode 100644 index 0000000..17f58da --- /dev/null +++ b/shavr/src/history_cmd.c @@ -0,0 +1,169 @@ +/* + * history_cmd.c + * + * Created on: 15 Oct 2015 + * Author: michel + */ +#include +#include +#include +#include "history_cmd.h" +#include "history.h" + +history_cmd_list_p +history_cmd_list_new() +{ + history_cmd_list_p res = (history_cmd_list_p)malloc(sizeof(*res)); + memset(res, 0, sizeof(*res)); + + return res; +} + +void +history_cmd_list_dispose( + history_cmd_list_p list ) +{ + if (list->list) + free(list->list); + list->list = NULL; + free(list); +} + + +static history_cmd_list_p global = NULL; + +void +history_cmd_list_register( + history_cmd_list_p list, + const history_cmd_p cmd ) +{ + if (!list) { + if (!global) + global = history_cmd_list_new(); + list = global; + } + + if (!list || !cmd) + return; + if ((list->count % 8) == 0) + list->list = realloc(list->list, sizeof(list->list[0]) * (list->count + 8)); + + int ins = -1; + for (int i = 0; i < list->count && ins == -1; i++) { + history_cmd_p c = list->list[i]; + if (c == cmd || strcmp(cmd->names[0], c->names[0]) <= 0) + ins = i; + } + if (ins == -1) + ins = list->count; + else + memmove(list->list + ins + 1, list->list + ins, (list->count - ins) * sizeof(list->list[0])); + list->list[ins] = cmd; + list->count++; +} + +history_cmd_p +history_cmd_lookup( + history_cmd_list_p list, + const char * cmd_name, + int * out_alias) +{ + if (!list) + list = global; + if (!list || !cmd_name) + return NULL; + for (int i = 0; i < list->count; i++) { + const history_cmd_p cmd = list->list[i]; + int alias = -1; + for (int ai = 0; ai < 8 && cmd->names[ai] && alias == -1; ai++) + if (!strcmp(cmd->names[ai], cmd_name)) { + if (out_alias) + *out_alias = ai; + return cmd; + } + } + return NULL; +} + + +int +history_cmd_execute( + history_cmd_list_p list, + const char * cmd_line ) +{ + if (!list) + list = global; + if (!list || !cmd_line) + return -1; + + int res = -1; + wordexp_t words = {0}; + while (*cmd_line == ' ' || *cmd_line == '\t') + cmd_line++; + + if (wordexp(cmd_line, &words, WRDE_NOCMD)) { + wordfree(&words); + fprintf(stderr, "Syntax error\r\n"); + goto out; + } + if (words.we_wordc == 0) { + res = 0; + goto out; + } + int alias = 0; + const history_cmd_p cmd = history_cmd_lookup(list, words.we_wordv[0], &alias); + if (!cmd) { + fprintf(stderr, "Unknown command '%s'\r\n", words.we_wordv[0]); + goto out; + } + if (cmd->parameter_map && !(cmd->parameter_map & (1 << (words.we_wordc-1)))) { + fprintf(stderr, "%s: %s\r\n", words.we_wordv[0], cmd->usage); + goto out; + } + if (cmd->execute_list) + res = cmd->execute_list(list, &words); + else if (cmd->execute) + res = cmd->execute(&words); + else { + fprintf(stderr, "%s: Internal error, no execute function", words.we_wordv[0]); + } +out: + wordfree(&words); + + return res; +} + +static int +history_cmd_help( + struct history_cmd_list_t *list, + wordexp_t * l) +{ + if (l->we_wordc == 1) { + for (int i = 0; i < list->count; i++) { + const history_cmd_p cmd = list->list[i]; + fprintf(stdout, "%-12.12s %s\r\n", cmd->names[0], cmd->usage); + } + } else { + for (int i = 1; i < l->we_wordc; i++) { + const history_cmd_p cmd = history_cmd_lookup(list, l->we_wordv[i], NULL); + if (cmd) { + fprintf(stderr, "%s: %s\r\n", l->we_wordv[i], cmd->usage); + fprintf(stderr, "\t%s\r\n", cmd->help); + } else { + fprintf(stderr, "help: Unknown command '%s'\r\n", l->we_wordv[i]); + } + } + } + return 0; +} + +const history_cmd_t cmd_help = { + .names[0] = "help", + .names[1] = "h", + .usage = "[cmd]: Display help for commands", + .help = "This helps", + .parameter_map = (1 << 0) | (1 << 1), + .execute_list = history_cmd_help, +}; +HISTORY_CMD_REGISTER(cmd_help); + diff --git a/shavr/src/history_cmd.h b/shavr/src/history_cmd.h new file mode 100644 index 0000000..f5671c9 --- /dev/null +++ b/shavr/src/history_cmd.h @@ -0,0 +1,58 @@ +/* + * history_cmd.h + * + * Created on: 15 Oct 2015 + * Author: michel + */ + +#ifndef HISTORY_CMD_H_ +#define HISTORY_CMD_H_ + +#include +#include + +struct history_cmd_list_t; + +typedef struct history_cmd_t { + const char * names[8]; + const char * usage; + const char * help; + uint8_t parameter_map; // 0 means 'any number' otherwise, bitfield of valid options + int (*execute)(wordexp_t * l); + int (*execute_list)(struct history_cmd_list_t *list, wordexp_t * l); +} history_cmd_t, *history_cmd_p; + + +typedef struct history_cmd_list_t { + int count; + history_cmd_p * list; +} history_cmd_list_t, *history_cmd_list_p; + +history_cmd_list_p +history_cmd_list_new(); + +void +history_cmd_list_dispose( + history_cmd_list_p list ); +void +history_cmd_list_register( + history_cmd_list_p list, + const history_cmd_p cmd ); + +#define HISTORY_CMD_REGISTER(__cmd) \ + __attribute__((constructor)) static void _history_register_##__cmd() { \ + history_cmd_list_register(NULL, (const history_cmd_p)&(__cmd));\ + } + +history_cmd_p +history_cmd_lookup( + history_cmd_list_p list, + const char * cmd_name, + int * out_alias); +int +history_cmd_execute( + history_cmd_list_p list, + const char * cmd_line ); + + +#endif /* HISTORY_CMD_H_ */ diff --git a/shavr/src/shavr.c b/shavr/src/shavr.c new file mode 100644 index 0000000..5d75790 --- /dev/null +++ b/shavr/src/shavr.c @@ -0,0 +1,141 @@ +/* + shavr.c + + Copyright 2017 Michel Pollet + + This file is part of simavr. + + simavr is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + simavr is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with simavr. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "sim_avr.h" +#include "avr_ioport.h" +#include "sim_elf.h" +#include "sim_hex.h" +#include "sim_gdb.h" +#include "uart_pty.h" +#include "sim_vcd_file.h" + +#include "sim_args.h" + +uart_pty_t uart_pty; +avr_t * avr = NULL; +avr_vcd_t vcd_file; +elf_firmware_t code;// = {0}; + +avr_t * +sim_prepare( + sim_args_t * a ); + + +typedef struct avr_flash_desc_t { + char avr_flash_path[1024]; + int avr_flash_fd; +} avr_flash_desc_t; + +// avr special flash initalization +// here: open and map a file to enable a persistent storage for the flash memory +static void +avr_special_init( + avr_t * avr, + void * data) +{ + avr_flash_desc_t *flash_data = (avr_flash_desc_t *)data; + + printf("%s\n", __func__); + // open the file + flash_data->avr_flash_fd = open(flash_data->avr_flash_path, + O_RDWR|O_CREAT, 0644); + if (flash_data->avr_flash_fd < 0) { + perror(flash_data->avr_flash_path); + exit(1); + } + // resize and map the file the file + (void)ftruncate(flash_data->avr_flash_fd, avr->flashend + 1); + ssize_t r = read(flash_data->avr_flash_fd, avr->flash, avr->flashend + 1); + if (r != avr->flashend + 1) { + fprintf(stderr, "unable to load flash memory\n"); + perror(flash_data->avr_flash_path); + exit(1); + } +} + +// avr special flash deinitalization +// here: cleanup the persistent storage +static void +avr_special_deinit( + avr_t* avr, + void * data) +{ + avr_flash_desc_t *flash_data = (avr_flash_desc_t *)data; + + printf("%s\n", __func__); + lseek(flash_data->avr_flash_fd, SEEK_SET, 0); + ssize_t r = write(flash_data->avr_flash_fd, avr->flash, avr->flashend + 1); + if (r != avr->flashend + 1) { + fprintf(stderr, "unable to save flash memory\n"); + perror(flash_data->avr_flash_path); + } + close(flash_data->avr_flash_fd); + uart_pty_stop(&uart_pty); +} + +int +main( + int argc, + const char *argv[]) +{ + avr_flash_desc_t flash_data; + sim_args_t args; + + if (sim_args_parse(&args, argc, argv, NULL)) { + exit(1); + } + + avr = sim_prepare(&args); + if (!avr) { + fprintf(stderr, "%s: Error creating the AVR core\n", argv[0]); + exit(1); + } + if (args.flash_file[0]) { + strncpy(flash_data.avr_flash_path, args.flash_file, sizeof(flash_data.avr_flash_path)); + flash_data.avr_flash_fd = 0; + // register our own functions + avr->custom.init = avr_special_init; + avr->custom.deinit = avr_special_deinit; + avr->custom.data = &flash_data; + } + avr_init(avr); + + uart_pty_init(avr, &uart_pty); + uart_pty_connect(&uart_pty, '0'); + + printf("Running...\n"); + while (1) { + int state = avr_run(avr); + if ( state == cpu_Done || state == cpu_Crashed) + break; + } + +} diff --git a/shavr/src/sim_args.c b/shavr/src/sim_args.c new file mode 100644 index 0000000..4fda3ef --- /dev/null +++ b/shavr/src/sim_args.c @@ -0,0 +1,186 @@ +/* + sim_args.c + + Copyright 2017 Michel Pollet + + This file is part of simavr. + + simavr is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + simavr is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with simavr. If not, see . + */ + +#include +#include +#include +#include + +#include "sim_avr.h" +#include "sim_elf.h" +#include "sim_core.h" +#include "sim_gdb.h" +#include "sim_hex.h" +#include "sim_vcd_file.h" + +#include "sim_core_decl.h" +#include "sim_args.h" + +void +sim_args_display_usage( + const char * app) +{ + printf("Usage: %s [...] \n", app); + printf( " [--freq|-f ] Sets the frequency for an .hex firmware\n" + " [--mcu|-m ] Sets the MCU type for an .hex firmware\n" + " [--list-cores] List all supported AVR cores and exit\n" + " [--help|-h] Display this usage message and exit\n" + " [--trace, -t] Run full scale decoder trace\n" + " [-ti ] Add traces for IRQ vector \n" + " [--gdb|-g] Listen for gdb connection on port 1234\n" + " [-ff <.hex file>] Load next .hex file as flash\n" + " [-ee <.hex file>] Load next .hex file as eeprom\n" + " [--input|-i ] A .vcd file to use as input signals\n" + " [--persist|-p ] Save/Load flash+eeprom from \n" + " [-v] Raise verbosity level\n" + " (can be passed more than once)\n" + " A .hex or an ELF file. ELF files are\n" + " prefered, and can include debugging syms\n"); +} + +void +sim_args_list_cores() +{ + printf( "Supported AVR cores:\n"); + for (int i = 0; avr_kind[i]; i++) { + printf(" "); + for (int ti = 0; ti < 4 && avr_kind[i]->names[ti]; ti++) + printf("%s ", avr_kind[i]->names[ti]); + printf("\n"); + } +} + + +int +sim_args_parse( + sim_args_t *a, + int argc, + const char *argv[], + sim_args_parse_t passthru) +{ + uint32_t loadBase = AVR_SEGMENT_OFFSET_FLASH; + + if (!a || !argc || !argv) + return -1; + memset(a, 0, sizeof(*a)); + + const char * progname = basename(strdup(argv[0])); + if (argc == 1) + sim_args_display_usage(progname); + + for (int pi = 1; pi < argc; pi++) { + if (!strcmp(argv[pi], "--list-cores")) { + sim_args_list_cores(); + } else if (!strcmp(argv[pi], "-h") || !strcmp(argv[pi], "--help")) { + sim_args_display_usage(progname); + } else if (!strcmp(argv[pi], "-m") || !strcmp(argv[pi], "--mcu")) { + if (pi < argc-1) + strncpy(a->name, argv[++pi], sizeof(a->name)); + else + sim_args_display_usage(progname); + } else if (!strcmp(argv[pi], "-f") || !strcmp(argv[pi], "--freq")) { + if (pi < argc-1) + a->f_cpu = atoi(argv[++pi]); + else + sim_args_display_usage(progname); + } else if (!strcmp(argv[pi], "-i") || !strcmp(argv[pi], "--input")) { + if (pi < argc-1) + strncpy(a->vcd_input, argv[++pi], sizeof(a->vcd_input)); + else + sim_args_display_usage(progname); + } else if (!strcmp(argv[pi], "-p") || !strcmp(argv[pi], "--persist")) { + if (argv[pi + 1] && argv[pi + 1][0] != '-') + strncpy(a->flash_file, argv[++pi], sizeof(a->flash_file)); + else + strncpy(a->flash_file, "simavr_flash.bin", sizeof(a->flash_file)); + } else if (!strcmp(argv[pi], "-t") || !strcmp(argv[pi], "--trace")) { + a->trace++; + } else if (!strcmp(argv[pi], "-ti")) { + if (pi < argc-1) + a->trace_vectors[a->trace_vectors_count++] = atoi(argv[++pi]); + } else if (!strcmp(argv[pi], "-g") || !strcmp(argv[pi], "--gdb")) { + a->gdb++; + } else if (!strcmp(argv[pi], "-v")) { + a->log++; + } else if (!strcmp(argv[pi], "-ee")) { + loadBase = AVR_SEGMENT_OFFSET_EEPROM; + } else if (!strcmp(argv[pi], "-ff")) { + loadBase = AVR_SEGMENT_OFFSET_FLASH; + } else if (argv[pi][0] == '-') { + // call the passthru callback + int npi = passthru(a, argc, argv, pi); + if (npi < 0) + return npi; + if (npi > pi) + pi = npi; + } else if (argv[pi][0] != '-') { + const char * filename = argv[pi]; + char * suffix = strrchr(filename, '.'); + if (suffix && !strcasecmp(suffix, ".hex")) { + if (!a->name[0] || !a->f_cpu) { + fprintf(stderr, + "%s: --mcu and --freq are mandatory to load .hex files\n", + argv[0]); + return -1; + } + ihex_chunk_p chunk = NULL; + int cnt = read_ihex_chunks(filename, &chunk); + if (cnt <= 0) { + fprintf(stderr, "%s: Unable to load IHEX file %s\n", + argv[0], argv[pi]); + return -1; + } + if (a->log) + printf("Loaded %d section of ihex\n", cnt); + for (int ci = 0; ci < cnt; ci++) { + if (chunk[ci].baseaddr < (1*1024*1024)) { + a->f.flash = chunk[ci].data; + a->f.flashsize = chunk[ci].size; + a->f.flashbase = chunk[ci].baseaddr; + if (a->log) + printf("Load HEX flash %08x, %d\n", a->f.flashbase, a->f.flashsize); + } else if (chunk[ci].baseaddr >= AVR_SEGMENT_OFFSET_EEPROM || + chunk[ci].baseaddr + loadBase >= AVR_SEGMENT_OFFSET_EEPROM) { + // eeprom! + a->f.eeprom = chunk[ci].data; + a->f.eesize = chunk[ci].size; + if (a->log) + printf("Load HEX eeprom %08x, %d\n", chunk[ci].baseaddr, a->f.eesize); + } + } + } else { + if (elf_read_firmware(filename, &a->f) == -1) { + fprintf(stderr, "%s: Unable to load firmware from file %s\n", + argv[0], filename); + return -1; + } + } + } + } + + if (strlen(a->name)) + strcpy(a->f.mmcu, a->name); + if (a->f_cpu) + a->f.frequency = a->f_cpu; + + return 0; +} + diff --git a/shavr/src/sim_args.h b/shavr/src/sim_args.h new file mode 100644 index 0000000..5365af5 --- /dev/null +++ b/shavr/src/sim_args.h @@ -0,0 +1,63 @@ +/* + sim_args.h + + Copyright 2017 Michel Pollet + + This file is part of simavr. + + simavr is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + simavr is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with simavr. If not, see . + */ + +#ifndef __SIM_ARGS_H__ +#define __SIM_ARGS_H__ + +#include + +struct sim_args_t; + +typedef int (*sim_args_parse_t)( + struct sim_args_t *a, + int argc, + const char *argv[], + int index ); + +typedef struct sim_args_t { + elf_firmware_t f; // ELF firmware + uint32_t f_cpu; // AVR core frequency + char name[24]; // AVR core name + uint32_t trace : 4, gdb : 1, log : 4; + + + uint8_t trace_vectors[8]; + int trace_vectors_count; + + char vcd_input[1024]; + char flash_file[1024]; +} sim_args_t; + +int +sim_args_parse( + sim_args_t *a, + int argc, + const char *argv[], + sim_args_parse_t passthru); + +void +sim_args_display_usage( + const char * app); +void +sim_args_list_cores(); + + +#endif /* __SIM_ARGS_H__ */ diff --git a/shavr/src/sim_prepare.c b/shavr/src/sim_prepare.c new file mode 100644 index 0000000..80c6c75 --- /dev/null +++ b/shavr/src/sim_prepare.c @@ -0,0 +1,94 @@ +/* + sim_prepare.c + + Copyright 2017 Michel Pollet + + This file is part of simavr. + + simavr is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + simavr is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with simavr. If not, see . + */ + +#include +#include +#include +#include +#include + +#include "sim_avr.h" +#include "sim_elf.h" +#include "sim_core.h" +#include "sim_gdb.h" +#include "sim_hex.h" +#include "sim_vcd_file.h" + +#include "sim_core_decl.h" +#include "sim_args.h" + +static avr_t * avr = NULL; +static avr_vcd_t input; + +static void +sig_int( + int sign) +{ + printf("signal caught, simavr terminating\n"); + if (avr) + avr_terminate(avr); + exit(0); +} + +extern char * __progname; + +avr_t * +sim_prepare( + sim_args_t * a ) +{ +// avr_t * avr; + + avr = avr_make_mcu_by_name(a->f.mmcu); + if (!avr) { + fprintf(stderr, "%s: AVR '%s' not known\n", __progname, a->f.mmcu); + return NULL; + } + avr_init(avr); + avr_load_firmware(avr, &a->f); + if (a->f.flashbase) { + printf("Attempted to load a bootloader at %04x\n", a->f.flashbase); + avr->pc = a->f.flashbase; + } + avr->log = a->log > LOG_TRACE ? LOG_TRACE : a->log; + avr->trace = a->trace; + for (int ti = 0; ti < a->trace_vectors_count; ti++) { + for (int vi = 0; vi < avr->interrupts.vector_count; vi++) + if (avr->interrupts.vector[vi]->vector == a->trace_vectors[ti]) + avr->interrupts.vector[vi]->trace = 1; + } + if (a->vcd_input[0]) { + if (avr_vcd_init_input(avr, a->vcd_input, &input)) { + fprintf(stderr, "%s: Warning: VCD input file %s failed\n", + __progname, a->vcd_input); + } + } + + // even if not setup at startup, activate gdb if crashing + avr->gdb_port = 1234; + if (a->gdb) { + avr->state = cpu_Stopped; + avr_gdb_init(avr); + } + + signal(SIGINT, sig_int); + signal(SIGTERM, sig_int); + return avr; +} diff --git a/shavr/src/uart_pty.c b/shavr/src/uart_pty.c new file mode 100644 index 0000000..4f4edd0 --- /dev/null +++ b/shavr/src/uart_pty.c @@ -0,0 +1,323 @@ +/* + uart_pty.c + + Copyright 2008, 2009 Michel Pollet + + This file is part of simavr. + + simavr is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + simavr is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with simavr. If not, see . + */ + +#include "sim_network.h" +#include +#include +#include +#include +#include +#include +#include +#ifdef __APPLE__ +#include +#else +#include +#endif + +#include "uart_pty.h" +#include "avr_uart.h" +#include "sim_time.h" +#include "sim_hex.h" + +DEFINE_FIFO(uint8_t,uart_pty_fifo); + +//#define TRACE(_w) _w +#ifndef TRACE +#define TRACE(_w) +#endif + +/* + * called when a byte is send via the uart on the AVR + */ +static void +uart_pty_in_hook( + struct avr_irq_t * irq, + uint32_t value, + void * param) +{ + uart_pty_t * p = (uart_pty_t*)param; + TRACE(printf("uart_pty_in_hook %02x\n", value);) + uart_pty_fifo_write(&p->pty.in, value); + + if (p->tap.s) { + if (p->tap.crlf && value == '\n') + uart_pty_fifo_write(&p->tap.in, '\r'); + uart_pty_fifo_write(&p->tap.in, value); + } +} + +// try to empty our fifo, the uart_pty_xoff_hook() will be called when +// other side is full +static void +uart_pty_flush_incoming( + uart_pty_t * p) +{ + while (p->xon && !uart_pty_fifo_isempty(&p->pty.out)) { + TRACE(int r = p->pty.out.read;) + uint8_t byte = uart_pty_fifo_read(&p->pty.out); + TRACE(printf("uart_pty_flush_incoming send r %03d:%02x\n", r, byte);) + avr_raise_irq(p->irq + IRQ_UART_PTY_BYTE_OUT, byte); + + if (p->tap.s) { + if (p->tap.crlf && byte == '\n') + uart_pty_fifo_write(&p->tap.in, '\r'); + uart_pty_fifo_write(&p->tap.in, byte); + } + } + if (p->tap.s) { + while (p->xon && !uart_pty_fifo_isempty(&p->tap.out)) { + uint8_t byte = uart_pty_fifo_read(&p->tap.out); + if (p->tap.crlf && byte == '\r') { + uart_pty_fifo_write(&p->tap.in, '\n'); + } + if (byte == '\n') + continue; + uart_pty_fifo_write(&p->tap.in, byte); + avr_raise_irq(p->irq + IRQ_UART_PTY_BYTE_OUT, byte); + } + } +} + +avr_cycle_count_t +uart_pty_flush_timer( + struct avr_t * avr, + avr_cycle_count_t when, + void * param) +{ + uart_pty_t * p = (uart_pty_t*)param; + + uart_pty_flush_incoming(p); + /* always return a cycle NUMBER not a cycle count */ + return p->xon ? when + avr_hz_to_cycles(p->avr, 1000) : 0; +} + +/* + * Called when the uart has room in it's input buffer. This is called repeateadly + * if necessary, while the xoff is called only when the uart fifo is FULL + */ +static void +uart_pty_xon_hook( + struct avr_irq_t * irq, + uint32_t value, + void * param) +{ + uart_pty_t * p = (uart_pty_t*)param; + TRACE(if (!p->xon) printf("uart_pty_xon_hook\n");) + p->xon = 1; + + uart_pty_flush_incoming(p); + + // if the buffer is not flushed, try to do it later + if (p->xon) + avr_cycle_timer_register(p->avr, avr_hz_to_cycles(p->avr, 1000), + uart_pty_flush_timer, param); +} + +/* + * Called when the uart ran out of room in it's input buffer + */ +static void +uart_pty_xoff_hook( + struct avr_irq_t * irq, + uint32_t value, + void * param) +{ + uart_pty_t * p = (uart_pty_t*)param; + TRACE(if (p->xon) printf("uart_pty_xoff_hook\n");) + p->xon = 0; + avr_cycle_timer_cancel(p->avr, uart_pty_flush_timer, param); +} + +static void * +uart_pty_thread( + void * param) +{ + uart_pty_t * p = (uart_pty_t*)param; + + while (1) { + fd_set read_set, write_set; + int max = 0; + FD_ZERO(&read_set); + FD_ZERO(&write_set); + + for (int ti = 0; ti < 2; ti++) if (p->port[ti].s) { + // read more only if buffer was flushed + if (p->port[ti].buffer_len == p->port[ti].buffer_done) { + FD_SET(p->port[ti].s, &read_set); + max = p->port[ti].s > max ? p->port[ti].s : max; + } + if (!uart_pty_fifo_isempty(&p->port[ti].in)) { + FD_SET(p->port[ti].s, &write_set); + max = p->port[ti].s > max ? p->port[ti].s : max; + } + } + + // short, but not too short interval + struct timeval timo = { 0, 500 }; + int ret = select(max+1, &read_set, &write_set, NULL, &timo); + + if (!ret) + continue; + if (ret < 0) + break; + + for (int ti = 0; ti < 2; ti++) if (p->port[ti].s) { + if (FD_ISSET(p->port[ti].s, &read_set)) { + ssize_t r = read(p->port[ti].s, p->port[ti].buffer, + sizeof(p->port[ti].buffer)-1); + p->port[ti].buffer_len = r; + p->port[ti].buffer_done = 0; + TRACE(if (!p->port[ti].tap) + hdump("pty recv", p->port[ti].buffer, r);) + } + if (p->port[ti].buffer_done < p->port[ti].buffer_len) { + // write them in fifo + while (p->port[ti].buffer_done < p->port[ti].buffer_len && + !uart_pty_fifo_isfull(&p->port[ti].out)) { + int index = p->port[ti].buffer_done++; + TRACE(int wi = p->port[ti].out.write;) + uart_pty_fifo_write(&p->port[ti].out, + p->port[ti].buffer[index]); + TRACE(printf("w %3d:%02x (%d/%d) %s\n", + wi, p->port[ti].buffer[index], + p->port[ti].out.read, + p->port[ti].out.write, + p->xon ? "XON" : "XOFF");) + } + } + if (FD_ISSET(p->port[ti].s, &write_set)) { + uint8_t buffer[512]; + // write them in fifo + uint8_t * dst = buffer; + while (!uart_pty_fifo_isempty(&p->port[ti].in) && + dst < (buffer + sizeof(buffer))) + *dst++ = uart_pty_fifo_read(&p->port[ti].in); + size_t len = dst - buffer; + TRACE(size_t r =) write(p->port[ti].s, buffer, len); + TRACE(if (!p->port[ti].tap) hdump("pty send", buffer, r);) + } + } + /* DO NOT call this, this create a concurency issue with the + * FIFO that can't be solved cleanly with a memory barrier + uart_pty_flush_incoming(p); + */ + } + return NULL; +} + +static const char * irq_names[IRQ_UART_PTY_COUNT] = { + [IRQ_UART_PTY_BYTE_IN] = "8avr = avr; + p->irq = avr_alloc_irq(&avr->irq_pool, 0, IRQ_UART_PTY_COUNT, irq_names); + avr_irq_register_notify(p->irq + IRQ_UART_PTY_BYTE_IN, uart_pty_in_hook, p); + + int hastap = (getenv("SIMAVR_UART_TAP") && atoi(getenv("SIMAVR_UART_TAP"))) || + (getenv("SIMAVR_UART_XTERM") && atoi(getenv("SIMAVR_UART_XTERM"))) ; + + for (int ti = 0; ti < 1 + hastap; ti++) { + int m, s; + + if (openpty(&m, &s, p->port[ti].slavename, NULL, NULL) < 0) { + fprintf(stderr, "%s: Can't create pty: %s", __FUNCTION__, strerror(errno)); + return ; + } + struct termios tio; + tcgetattr(m, &tio); + cfmakeraw(&tio); + tcsetattr(m, TCSANOW, &tio); + p->port[ti].s = m; + p->port[ti].tap = ti != 0; + p->port[ti].crlf = ti != 0; + printf("uart_pty_init %s on port *** %s ***\n", + ti == 0 ? "bridge" : "tap", p->port[ti].slavename); + } + + pthread_create(&p->thread, NULL, uart_pty_thread, p); + +} + +void +uart_pty_stop( + uart_pty_t * p) +{ + puts(__func__); + pthread_kill(p->thread, SIGINT); + for (int ti = 0; ti < 2; ti++) + if (p->port[ti].s) + close(p->port[ti].s); + void * ret; + pthread_join(p->thread, &ret); +} + +void +uart_pty_connect( + uart_pty_t * p, + char uart) +{ + // disable the stdio dump, as we are sending binary there + uint32_t f = 0; + avr_ioctl(p->avr, AVR_IOCTL_UART_GET_FLAGS(uart), &f); + f &= ~AVR_UART_FLAG_STDIO; + avr_ioctl(p->avr, AVR_IOCTL_UART_SET_FLAGS(uart), &f); + + avr_irq_t * src = avr_io_getirq(p->avr, AVR_IOCTL_UART_GETIRQ(uart), UART_IRQ_OUTPUT); + avr_irq_t * dst = avr_io_getirq(p->avr, AVR_IOCTL_UART_GETIRQ(uart), UART_IRQ_INPUT); + avr_irq_t * xon = avr_io_getirq(p->avr, AVR_IOCTL_UART_GETIRQ(uart), UART_IRQ_OUT_XON); + avr_irq_t * xoff = avr_io_getirq(p->avr, AVR_IOCTL_UART_GETIRQ(uart), UART_IRQ_OUT_XOFF); + if (src && dst) { + avr_connect_irq(src, p->irq + IRQ_UART_PTY_BYTE_IN); + avr_connect_irq(p->irq + IRQ_UART_PTY_BYTE_OUT, dst); + } + if (xon) + avr_irq_register_notify(xon, uart_pty_xon_hook, p); + if (xoff) + avr_irq_register_notify(xoff, uart_pty_xoff_hook, p); + + for (int ti = 0; ti < 1; ti++) if (p->port[ti].s) { + char link[128]; + sprintf(link, "/tmp/simavr-uart%s%c", ti == 1 ? "tap" : "", uart); + unlink(link); + if (symlink(p->port[ti].slavename, link) != 0) { + fprintf(stderr, "WARN %s: Can't create %s: %s", __func__, link, strerror(errno)); + } else { + printf("%s: %s now points to %s\n", __func__, link, p->port[ti].slavename); + } + } + if (getenv("SIMAVR_UART_XTERM") && atoi(getenv("SIMAVR_UART_XTERM"))) { + char cmd[256]; + sprintf(cmd, "xterm -e picocom -b 115200 %s >/dev/null 2>&1 &", + p->tap.slavename); + system(cmd); + } else + printf("note: export SIMAVR_UART_XTERM=1 and install picocom to get a terminal\n"); +} + diff --git a/shavr/src/uart_pty.h b/shavr/src/uart_pty.h new file mode 100644 index 0000000..c6e0603 --- /dev/null +++ b/shavr/src/uart_pty.h @@ -0,0 +1,76 @@ +/* + uart_pty.h + + Copyright 2012 Michel Pollet + + This file is part of simavr. + + simavr is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + simavr is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with simavr. If not, see . + */ + + +#ifndef __UART_PTY_H___ +#define __UART_PTY_H___ + +#include +#include "sim_irq.h" +#include "fifo_declare.h" + +enum { + IRQ_UART_PTY_BYTE_IN = 0, + IRQ_UART_PTY_BYTE_OUT, + IRQ_UART_PTY_COUNT +}; + +DECLARE_FIFO(uint8_t,uart_pty_fifo, 512); + +typedef struct uart_pty_port_t { + int tap : 1, crlf : 1; + int s; // socket we chat on + char slavename[64]; + uart_pty_fifo_t in; + uart_pty_fifo_t out; + uint8_t buffer[512]; + size_t buffer_len, buffer_done; +} uart_pty_port_t, *uart_pty_port_p; + +typedef struct uart_pty_t { + avr_irq_t * irq; // irq list + struct avr_t *avr; // keep it around so we can pause it + + pthread_t thread; + int xon; + + union { + struct { + uart_pty_port_t pty; + uart_pty_port_t tap; + }; + uart_pty_port_t port[2]; + }; +} uart_pty_t; + +void +uart_pty_init( + struct avr_t * avr, + uart_pty_t * b); +void +uart_pty_stop(uart_pty_t * p); + +void +uart_pty_connect( + uart_pty_t * p, + char uart); + +#endif /* __UART_PTY_H___ */