From 0a764c4ef24512959bcbb47eb905470a113fa856 Mon Sep 17 00:00:00 2001 From: Michel Pollet Date: Thu, 24 Dec 2009 16:59:33 +0000 Subject: [PATCH] simduino: Bootloader now works! Can program simduino with avrdude! See the readme for the howto Signed-off-by: Michel Pollet --- examples/board_simduino/Makefile | 47 ++++ examples/board_simduino/README | 73 +++++- .../board_simduino/atmega328p_dummy_blinky.c | 65 +++++ examples/board_simduino/simduino.c | 238 ++++++++++++++++++ examples/parts/uart_udp.c | 52 +++- examples/parts/uart_udp.h | 2 +- 6 files changed, 459 insertions(+), 18 deletions(-) create mode 100644 examples/board_simduino/Makefile create mode 100644 examples/board_simduino/atmega328p_dummy_blinky.c create mode 100644 examples/board_simduino/simduino.c diff --git a/examples/board_simduino/Makefile b/examples/board_simduino/Makefile new file mode 100644 index 0000000..6d81178 --- /dev/null +++ b/examples/board_simduino/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 . + +board= simduino +firm_src = ${wildcard atmega*.c} +firmware = ${firm_src:.c=.hex} +simavr = ../../ + +SHELL = /bin/bash + +IPATH = . +IPATH += ../parts +IPATH += ${simavr}/include +IPATH += ${simavr}/simavr/sim + +VPATH = . +VPATH += ../parts + +LDFLAGS += -lglut -lpthread + +all: obj ${firmware} ${board} + +include ${simavr}/Makefile.common + +${board} : ${OBJ}/button.o +${board} : ${OBJ}/uart_udp.o +${board} : ${OBJ}/${board}.o + @echo LD $@ + @gcc -MD ${CFLAGS} -o $@ $^ $(LDFLAGS) ${simavr}/simavr/libsimavr.a + +clean: + rm -rf obj *.a *.axf ${board} *.vcd diff --git a/examples/board_simduino/README b/examples/board_simduino/README index e7ac83a..fe6dd69 100644 --- a/examples/board_simduino/README +++ b/examples/board_simduino/README @@ -1,8 +1,73 @@ -It's not working. don't get all excited. It loads and run the bootloader, but -thats all. -It /will/ work at some point, but it's not. It's missing a heck of a lot. +This will emulate an arduino. Right now it loads the bootloader, then wait on it's "serial port" +(a UDP socket on port 4321) until someone programs it... eventualy it timesout and run the previous +app that was programmed. -Told ya. forget about it ! :=) +To actualy program it, you need "socat" to make a bridge between avrdude and simavr. + +% socat -d -d PTY UDP:localhost:4321 + 2009/12/24 16:19:26 socat[26974] N PTY is /dev/pts/10 + 2009/12/24 16:19:26 socat[26974] N opening connection to AF=2 127.0.0.1:4321 + 2009/12/24 16:19:26 socat[26974] N successfully connected from local address AF=2 127.0.0.1:48246 + 2009/12/24 16:19:26 socat[26974] N starting data transfer loop with FDs [3,3] and [6,6] + +Once that bridge is setup, launch simduino: + +% ./simduino + Starting atmega328 - flashend 7fff ramend 08ff e2end 03ff + atmega328 init + avr_flash_init init SPM 0057 + read_ihex_file: ./ATmegaBOOT_168_atmega328.ihex, unsupported check type 03 + Booloader 7800: 1950 + uart_udp_init bridge on port 4321 + UART-0 configured to 0010 = 58823 baud + +What that is done, you have a few seconds to program it: + +% avrdude -p m328p -c arduino -P /dev/pts/10 -U flash:w:atmega328p_dummy_blinky.hex + + avrdude: AVR device initialized and ready to accept instructions + + Reading | ################################################## | 100% 0.01s + + avrdude: Device signature = 0x1e950f + avrdude: NOTE: FLASH memory has been specified, an erase cycle will be performed + To disable this feature, specify the -D option. + avrdude: erasing chip + avrdude: reading input file "atmega328p_dummy_blinky.hex" + avrdude: input file atmega328p_dummy_blinky.hex auto detected as Intel Hex + avrdude: writing flash (394 bytes): + + Writing | ################################################## | 100% 0.15s + + avrdude: 394 bytes of flash written + avrdude: verifying flash memory against atmega328p_dummy_blinky.hex: + avrdude: load data flash data from input file atmega328p_dummy_blinky.hex: + avrdude: input file atmega328p_dummy_blinky.hex auto detected as Intel Hex + avrdude: input file atmega328p_dummy_blinky.hex contains 394 bytes + avrdude: reading on-chip flash data: + + Reading | ################################################## | 100% 0.13s + + avrdude: verifying ... + avrdude: 394 bytes of flash verified + + avrdude: safemode: Fuses OK + + avrdude done. Thank you. + +simduino will display that: + + Erasing page 0000 (128) + Writing page 0000 (128) + Erasing page 0001 (128) + Writing page 0001 (128) + Erasing page 0002 (128) + Writing page 0002 (128) + Erasing page 0003 (128) + Writing page 0003 (128) + +And then jump to the program that was programmed. The example doesn't do anything useful, it's +a placeholder... Michel Pollet diff --git a/examples/board_simduino/atmega328p_dummy_blinky.c b/examples/board_simduino/atmega328p_dummy_blinky.c new file mode 100644 index 0000000..e1a9680 --- /dev/null +++ b/examples/board_simduino/atmega328p_dummy_blinky.c @@ -0,0 +1,65 @@ +/* + atmega328p_dummy_blinky.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 . + */ + + +#ifdef F_CPU +#undef F_CPU +#endif + +#define F_CPU 16000000 + +#include +#include +#include +#include +#include + +/* + * This demonstrate how to use the avr_mcu_section.h file + * The macro adds a section to the ELF file with useful + * information for the simulator + */ +#include "avr_mcu_section.h" +AVR_MCU(F_CPU, "atmega328p"); + +static int uart_putchar(char c, FILE *stream) { + if (c == '\n') + uart_putchar('\r', stream); + loop_until_bit_is_set(UCSR0A, UDRE0); + UDR0 = c; + return 0; +} + +static FILE mystdout = FDEV_SETUP_STREAM(uart_putchar, NULL, + _FDEV_SETUP_WRITE); + + +int main() +{ + stdout = &mystdout; + + printf("Bootloader properly programmed, and ran me! Huzzah!\n"); + + // this quits the simulator, since interrupts are off + // this is a "feature" that allows running tests cases and exit + sleep_cpu(); +} + diff --git a/examples/board_simduino/simduino.c b/examples/board_simduino/simduino.c new file mode 100644 index 0000000..6a3dcdc --- /dev/null +++ b/examples/board_simduino/simduino.c @@ -0,0 +1,238 @@ +/* + simduino.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 +#include +#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_udp.h" +#include "sim_vcd_file.h" + +#include "button.h" + +button_t button; +uart_udp_t uart_udp; +int do_button_press = 0; +avr_t * avr = NULL; +avr_vcd_t vcd_file; +uint8_t pin_state = 0; // current port B + +float pixsize = 64; +int window; + +/* + * called when the AVR change any of the pins on port B + * so lets update our buffer + */ +void pin_changed_hook(struct avr_irq_t * irq, uint32_t value, void * param) +{ +// pin_state = (pin_state & ~(1 << irq->irq)) | (value << irq->irq); +} + +void displayCB(void) /* function called whenever redisplay needed */ +{ + // OpenGL rendering goes here... + glClear(GL_COLOR_BUFFER_BIT); + + // Set up modelview matrix + glMatrixMode(GL_MODELVIEW); // Select modelview matrix + glLoadIdentity(); // Start with an identity matrix + + float grid = pixsize; + float size = grid * 0.8; + glBegin(GL_QUADS); + glColor3f(1,0,0); + +#if 0 + for (int di = 0; di < 8; di++) { + char on = (pin_state & (1 << di)) != 0; + if (on) { + float x = (di) * grid; + float y = 0; //(si * grid * 8) + (di * grid); + glVertex2f(x + size, y + size); + glVertex2f(x, y + size); + glVertex2f(x, y); + glVertex2f(x + size, y); + } + } +#endif + glEnd(); + glutSwapBuffers(); + //glFlush(); /* Complete any pending operations */ +} + +void keyCB(unsigned char key, int x, int y) /* called on key press */ +{ + if (key == 'q') + exit(0); + static uint8_t buf[64]; + switch (key) { + case 'q': + case 0x1f: // escape + exit(0); + break; + case ' ': + do_button_press++; // pass the message to the AVR thread + break; + case 'r': + printf("Starting VCD trace\n"); + avr_vcd_start(&vcd_file); + break; + case 's': + printf("Stopping VCD trace\n"); + avr_vcd_stop(&vcd_file); + break; + } +} + +// gl timer. if the pin have changed states, refresh display +void timerCB(int i) +{ + static uint8_t oldstate = 0xff; + // restart timer + glutTimerFunc(1000/64, timerCB, 0); +#if 0 + if (oldstate != pin_state) { + oldstate = pin_state; + glutPostRedisplay(); + } +#endif +} + +static void * avr_run_thread(void * oaram) +{ + int b_press = do_button_press; + + while (1) { + avr_run(avr); +#if 0 + if (do_button_press != b_press) { + b_press = do_button_press; + printf("Button pressed\n"); + button_press(&button, 1000000); + } +#endif + } +} + + +int main(int argc, char *argv[]) +{ + elf_firmware_t f; + const char * pwd = dirname(argv[0]); + + avr = avr_make_mcu_by_name("atmega328p"); + if (!avr) { + fprintf(stderr, "%s: Error creating the AVR core\n", argv[0]); + exit(1); + } + avr_init(avr); + avr->frequency = 16000000; + + // this trick creates a file that contains /and keep/ the flash + // in the same state as it was before. This allow the bootloader + // app to be kept, and re-run if the bootloader doesn't get a + // new one + { + char path[256]; + sprintf(path, "%s/%s", pwd, "simduino_flash.bin"); + + int fd = open(path, O_RDWR|O_CREAT, 0644); + if (fd < 0) { + perror(path); + exit(1); + } + ftruncate(fd, avr->flashend + 1); + uint8_t * mm = (uint8_t*)mmap(NULL, avr->flashend + 1 /* 32k is multiple of 4096 */, + PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + if (!mm) { + perror(path); + exit(1); + } + + // reload bootloader anyway + free(avr->flash); + avr->flash = mm; + + uint32_t base, size; + sprintf(path, "%s/%s", pwd, "ATmegaBOOT_168_atmega328.ihex"); + uint8_t * boot = read_ihex_file(path, &size, &base); + if (!boot) { + fprintf(stderr, "%s: Unable to load %s\n", argv[0], path); + exit(1); + } + printf("Booloader %04x: %d\n", base, size); + memcpy(mm + base, boot, size); + free(boot); + avr->pc = base; + avr->codeend = avr->flashend; + } + //avr->trace = 1; + + // even if not setup at startup, activate gdb if crashing + avr->gdb_port = 1234; + if (0) { + //avr->state = cpu_Stopped; + avr_gdb_init(avr); + } + + uart_udp_init(avr, &uart_udp); + uart_udp_connect(&uart_udp, '0'); + + /* + * OpenGL init, can be ignored + */ + glutInit(&argc, argv); /* initialize GLUT system */ + + glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE); + glutInitWindowSize(8 * pixsize, 1 * pixsize); /* width=400pixels height=500pixels */ + window = glutCreateWindow("Glut"); /* create window */ + + // Set up projection matrix + glMatrixMode(GL_PROJECTION); // Select projection matrix + glLoadIdentity(); // Start with an identity matrix + glOrtho(0, 8 * pixsize, 0, 1 * pixsize, 0, 10); + glScalef(1,-1,1); + glTranslatef(0, -1 * pixsize, 0); + + glutDisplayFunc(displayCB); /* set window's display callback */ + glutKeyboardFunc(keyCB); /* set window's key callback */ + glutTimerFunc(1000 / 24, timerCB, 0); + + // the AVR run on it's own thread. it even allows for debugging! + pthread_t run; + pthread_create(&run, NULL, avr_run_thread, NULL); + + glutMainLoop(); +} diff --git a/examples/parts/uart_udp.c b/examples/parts/uart_udp.c index cebf41c..468d9c7 100644 --- a/examples/parts/uart_udp.c +++ b/examples/parts/uart_udp.c @@ -34,7 +34,7 @@ #include "avr_uart.h" #include "sim_hex.h" -DEFINE_FIFO(uint8_t,uart_udp_fifo, 128); +DEFINE_FIFO(uint8_t,uart_udp_fifo, 512); /* * called when a byte is send via the uart on the AVR @@ -42,6 +42,8 @@ DEFINE_FIFO(uint8_t,uart_udp_fifo, 128); static void uart_udp_in_hook(struct avr_irq_t * irq, uint32_t value, void * param) { uart_udp_t * p = (uart_udp_t*)param; +// printf("uart_udp_in_hook %02x\n", value); + uart_udp_fifo_write(&p->in, value); } /* @@ -51,13 +53,15 @@ static void uart_udp_in_hook(struct avr_irq_t * irq, uint32_t value, void * para static void uart_udp_xon_hook(struct avr_irq_t * irq, uint32_t value, void * param) { uart_udp_t * p = (uart_udp_t*)param; - if (!p->xon) - printf("uart_udp_xon_hook\n"); +// if (!p->xon) +// printf("uart_udp_xon_hook\n"); p->xon = 1; // try to empty our fifo, the uart_udp_xoff_hook() will be called when // other side is full while (p->xon && !uart_udp_fifo_isempty(&p->out)) { - avr_raise_irq(p->irq + IRQ_UART_UDP_BYTE_OUT, uart_udp_fifo_read(&p->out)); + uint8_t byte = uart_udp_fifo_read(&p->out); + // printf("uart_udp_xon_hook send %02x\n", byte); + avr_raise_irq(p->irq + IRQ_UART_UDP_BYTE_OUT, byte); } } @@ -67,8 +71,8 @@ static void uart_udp_xon_hook(struct avr_irq_t * irq, uint32_t value, void * par static void uart_udp_xoff_hook(struct avr_irq_t * irq, uint32_t value, void * param) { uart_udp_t * p = (uart_udp_t*)param; - if (p->xon) - printf("uart_udp_xoff_hook\n"); +// if (p->xon) +// printf("uart_udp_xoff_hook\n"); p->xon = 0; } @@ -77,28 +81,42 @@ static void * uart_udp_thread(void * param) uart_udp_t * p = (uart_udp_t*)param; while (1) { - fd_set read_set; - int max; + fd_set read_set, write_set; + int max = p->s + 1; FD_ZERO(&read_set); + FD_ZERO(&write_set); FD_SET(p->s, &read_set); - max = p->s + 1; + if (!uart_udp_fifo_isempty(&p->in)) + FD_SET(p->s, &write_set); - struct timeval timo = { 0, 100 }; // short, but not too short interval - int ret = select(max, &read_set, NULL, NULL, &timo); + struct timeval timo = { 0, 500 }; // short, but not too short interval + int ret = select(max, &read_set, &write_set, NULL, &timo); if (FD_ISSET(p->s, &read_set)) { - uint8_t buffer[1024]; + uint8_t buffer[512]; socklen_t len = sizeof(p->peer); ssize_t r = recvfrom(p->s, buffer, sizeof(buffer)-1, 0, (struct sockaddr*)&p->peer, &len); - hdump("udp", buffer, len); + // hdump("udp recv", buffer, r); // write them in fifo uint8_t * src = buffer; while (r-- && !uart_udp_fifo_isfull(&p->out)) uart_udp_fifo_write(&p->out, *src++); + if (r > 0) + printf("UDP dropped %d bytes\n", r); + } + if (FD_ISSET(p->s, &write_set)) { + uint8_t buffer[512]; + // write them in fifo + uint8_t * dst = buffer; + while (!uart_udp_fifo_isempty(&p->in) && dst < (buffer+sizeof(buffer))) + *dst++ = uart_udp_fifo_read(&p->in); + socklen_t len = dst - buffer; + size_t r = sendto(p->s, buffer, len, 0, (struct sockaddr*)&p->peer, sizeof(p->peer)); + // hdump("udp send", buffer, r); } } } @@ -124,12 +142,20 @@ void uart_udp_init(struct avr_t * avr, uart_udp_t * p) return ; } + printf("uart_udp_init bridge on port %d\n", 4321); + pthread_create(&p->thread, NULL, uart_udp_thread, p); } void uart_udp_connect(uart_udp_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); diff --git a/examples/parts/uart_udp.h b/examples/parts/uart_udp.h index 20bdd74..10b06fe 100644 --- a/examples/parts/uart_udp.h +++ b/examples/parts/uart_udp.h @@ -33,7 +33,7 @@ enum { IRQ_UART_UDP_COUNT }; -DECLARE_FIFO(uint8_t,uart_udp_fifo, 128); +DECLARE_FIFO(uint8_t,uart_udp_fifo, 512); typedef struct uart_udp_t { avr_irq_t * irq; // irq list -- 2.39.5