--- /dev/null
+#
+# Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+#
+# 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 <http://www.gnu.org/licenses/>.
+
+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
-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 <buserror@gmail.com>
--- /dev/null
+/*
+ atmega328p_dummy_blinky.c
+
+ Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+
+ 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 <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifdef F_CPU
+#undef F_CPU
+#endif
+
+#define F_CPU 16000000
+
+#include <avr/io.h>
+#include <stdio.h>
+#include <avr/interrupt.h>
+#include <avr/eeprom.h>
+#include <avr/sleep.h>
+
+/*
+ * 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();
+}
+
--- /dev/null
+/*
+ simduino.c
+
+ Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+
+ 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <sys/mman.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <libgen.h>
+
+#include <GL/glut.h>
+#include <pthread.h>
+
+#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();
+}
#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
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);
}
/*
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);
}
}
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;
}
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);
}
}
}
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);
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