From fcf3a5999ccfbb4a88f747ee3d9ed1c9c81891de Mon Sep 17 00:00:00 2001 From: Andy Spencer Date: Mon, 10 Mar 2014 00:59:02 +0000 Subject: [PATCH] Work on second mbed Add control program file: - Can be used to send a time initialization message to the mbed Add install program: - Install programs on both mbeds and initialize the time on one Add IRQ driven serial interface: - Since we don't have spare DMA controllers available for this homework we need to transmit serial data some other way. - The IRQ code allows us to queue up large messages to be transmitted asynchronous to main background loop. It should also help with receiving data at a high rate. Add task table to main: - This lets us schedule tasks at fixed intervals. Fix various bugs: - Timer dma code reset avoid config errors cause by periodical request while the DMA controller has nothing to transmit. --- .gitignore | 1 + common.mk | 6 +- hw2/control.c | 59 +++++++ hw2/install.sh | 27 ++++ hw2/main.cpp | 411 ++++++++++++++++++++++++++++++++++++++++------- hw2/makefile | 16 +- hw2/messages.h | 40 +++++ hw2/serial_irq.c | 114 +++++++++++++ hw2/serial_irq.h | 44 +++++ hw2/timer_dma.c | 87 ++++++++-- hw2/timer_dma.h | 8 +- 11 files changed, 731 insertions(+), 82 deletions(-) create mode 100644 hw2/control.c create mode 100755 hw2/install.sh create mode 100644 hw2/messages.h create mode 100644 hw2/serial_irq.c create mode 100644 hw2/serial_irq.h diff --git a/.gitignore b/.gitignore index 1f72ef6..d42653c 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ __pycache__ xively config.mk settings.cfg +control diff --git a/common.mk b/common.mk index 6798650..a0f65a8 100644 --- a/common.mk +++ b/common.mk @@ -55,15 +55,15 @@ clean: @rm -f $(PROG).bin $(PROG).elf $(OBJS) # Rules -%.o: %.c makefile ../common.mk ../config.mk +%.o: %.c $(wildcard *.h) makefile ../common.mk ../config.mk @echo "CC $<" @$(GCC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $< -%.o: %.cc makefile ../common.mk ../config.mk +%.o: %.cc $(wildcard *.h) makefile ../common.mk ../config.mk @echo "CXX $<" @$(GXX) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $< -%.o: %.cpp makefile ../common.mk ../config.mk +%.o: %.cpp $(wildcard *.h) makefile ../common.mk ../config.mk @echo "CXX $<" @$(GXX) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $< diff --git a/hw2/control.c b/hw2/control.c new file mode 100644 index 0000000..fa54a97 --- /dev/null +++ b/hw2/control.c @@ -0,0 +1,59 @@ +#include +#include + +#include + +#include + +#include "messages.h" + +void error(char *msg) +{ + printf("Error: %s\n", msg); + exit(0); +} + +void dump(const char *label, uint8_t *data, int len) +{ + int i; + printf("%s: ", label); + for (i = 0; i < len; i++) + printf("%02hhx ", data[i]); + printf("\n"); +} + +int main(int argc, char **argv) +{ + char *device = argv[1]; + if (!device) + error("usage: host /dev/ttyACM0"); + + header_t head = {}; + sync_msg_t body = {}; + + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + + head.header = MSG_HEADER; + head.msgid = MSG_ID_SYNC; + head.length = sizeof(sync_msg_t); + head.cksum = 0; // todo + + body.seq = 0; + body.time.seconds = ts.tv_sec; + body.time.nanosec = ts.tv_nsec; + + dump("head", (uint8_t*)&head, sizeof(head)); + dump("body", (uint8_t*)&body, sizeof(body)); + + FILE *fd = fopen(device, "a+"); + if (!fd) error("opening device"); + int len = 0; + len += fwrite(&head, 1, sizeof(head), fd); + len += fwrite(&body, 1, sizeof(body), fd); + fclose(fd); + + printf("wrote %d bytes\n", len); + + return 0; +} diff --git a/hw2/install.sh b/hw2/install.sh new file mode 100755 index 0000000..f00994e --- /dev/null +++ b/hw2/install.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +function install +{ + bin=$1 + dev=$2 + mnt=$3 + + if [ -b "$dev" ]; then + echo "Installing: $bin -> $mnt" + mount "$mnt" + cp "$bin" "$mnt" + umount "$mnt" + else + echo Error: No USB found + fi +} + +if [ -z "$1" ] || [ "$1" = "-h" ]; then + echo "usage: install.sh image.bin" + exit 0 +fi + +install "$1" /dev/sdb /mnt/mbed1 & +install "$1" /dev/sdc /mnt/mbed2 & + +wait diff --git a/hw2/main.cpp b/hw2/main.cpp index 4f42dd2..82f28f7 100644 --- a/hw2/main.cpp +++ b/hw2/main.cpp @@ -1,4 +1,7 @@ +#include "messages.h" + #include "mbed.h" +#include "serial_irq.h" #include "serial_dma.h" #include "timer_dma.h" @@ -17,53 +20,36 @@ * Time synchronization is performed in both directions. */ -/*********************** - * Message Definitions * - ***********************/ - -#define MSG_HEADER 0x1234 - -typedef enum { - MSG_ID_SYNC, // Time synchronization - MSG_ID_EVENT, // Event occurred -} msgid_t; - -typedef struct { - uint32_t seconds; // Seconds since 1970 (without leap seconds) - uint32_t nanosec; // Nanoseconds since 'seconds' -} ntime_t; - -typedef struct { - uint16_t header; // Message Header - uint16_t mesgid; // Message ID - uint16_t length; // Body length - uint16_t cksum; // Body checksum -} header_t; - -typedef struct { - uint16_t seq; // Current sequence counter - uint16_t prev; // Sequence of previous message - ntime_t time; // Time of previous message -} sync_msg_t; - -typedef struct { - uint16_t device; // Device ID - uint16_t event; // Event ID - ntime_t time; // Timestamp -} event_msg_t; - /******************* * Timer functions * *******************/ +#define NSEC_PER_SEC 1000000000ULL + +uint64_t time_last_local; // timestamp at last time sync +uint64_t time_last_world; // offset at last time sync + +/** + * Generate time stamp for an async event: + * local: drift compensated wall-clock time + * world: nanoseconds in world time world + * valid: local timestamp at valid valid + */ +//uint64_t time_to_local(uint64_t world, uint64_t valid) +//{ +// uint64_t now = +// local = + (stamp); +//} + /** * Generate time stamp for an async event: * time: drift compensated wall-clock time - * stamp: event timestamp from Timer/PWM Module + * stamp: event timestamp from PIT Module */ -void time_stamp(ntime_t *time, uint32_t stamp) +uint64_t time_to_world(uint64_t local) { - // todo + uint64_t elapsed = local - time_last_local; + return time_last_world + elapsed; } /** @@ -79,23 +65,98 @@ void time_rtc_comp(void) /** * Synchronize the timer internal state with updates * from an external time sync message. - * ours: our internal timestamp for the event - * ref: reference timestamp from the other device + * local: our internal timestamp for the event + * world: reference timestamp from the other device */ -void time_ext_sync(ntime_t *ours, ntime_t *ref) +void time_ext_init(uint64_t local, uint64_t world) { - // todo + sirq_printf("initialize clocks: %d -> %d\r\n", + (int)(local/NSEC_PER_SEC), + (int)(world/NSEC_PER_SEC)); + + time_last_local = local; + time_last_world = world; +} + +/** + * Synchronize the timer internal state with updates + * from an external time sync message. + * local: our internal timestamp for the event + * world: reference timestamp from the other device + */ +void time_ext_sync(uint64_t local, uint64_t world) +{ + sirq_printf("syncing clocks: %d -> %d\r\n", + (int)(local/NSEC_PER_SEC), + (int)(world/NSEC_PER_SEC)); + + time_last_local = local; + time_last_world = world; } /************************ * Serial I/O functions * ************************/ +typedef struct { + int index; + int state; + uint8_t buffer[256]; +} parser_t; + +const uint64_t serial_sync_delay = NSEC_PER_SEC * 2; // 1hz +static uint64_t serial_sync_due = 0; + +static tdma_t *serial_tdma_rcv = NULL; +static tdma_t *serial_tdma_xmt = NULL; + +static uint64_t serial_prev_local = 0; +static uint64_t serial_prev_seq = 0; + +static uint64_t serial_xmt_local = 0; +static uint64_t serial_xmt_seq = 0; + /** * Output time sync message */ -void serial_send_sync(void) +void serial_send_sync(sirq_t *port, uint64_t now) { + if (serial_sync_due == 0 || now < serial_sync_due) + return; // not ready + + // Calculate world time + uint64_t world = time_to_world(serial_xmt_local); + + // Message data + header_t head; + sync_msg_t body; + + // Transmit sync message + head.header = MSG_HEADER; + head.msgid = MSG_ID_SYNC; + head.length = sizeof(body); + head.cksum = 0; // todo + + body.seq = serial_xmt_seq; + body.time.seconds = world / NSEC_PER_SEC; + body.time.nanosec = world % NSEC_PER_SEC; + + sirq_write(port, &head, sizeof(head)); + sirq_write(port, &body, sizeof(body)); + + serial_xmt_seq += 1; + serial_sync_due = 0; + + // Debug + sirq_printf("sync msg transmit\r\n"); + + // save transmit time + for (int i = 0; i < 1000; i++) + asm("nop"); + int valid = tdma_stamp(serial_tdma_xmt, &serial_xmt_local); + if (!valid) + sirq_printf("missing sync transmit time\r\n"); + tdma_reset(serial_tdma_xmt); } /** @@ -103,15 +164,124 @@ void serial_send_sync(void) * event: id of the received event * time: compensated timestamp of the event */ -void serial_send_event(uint16_t event, ntime_t *time) +void serial_send_event(uint16_t event, uint64_t local) { + uint64_t world = time_to_world(local); + + ntime_t time = {}; + time.seconds = (uint32_t)(world / NSEC_PER_SEC); + time.nanosec = (uint32_t)(world % NSEC_PER_SEC); + + sirq_printf("event received - %08x:%08x - %u.%u\r\n", + (uint32_t)(local >> 32), (uint32_t)local, + time.seconds, time.nanosec); + // todo +} + +/** + * Handle sync message + */ +void serial_handle_sync(sync_msg_t *msg) +{ + // Read receive timestamp for next time sync message + uint64_t current = 0; + int valid = tdma_stamp(serial_tdma_rcv, ¤t); + if (!valid) + sirq_printf("missing sync receive time\r\n"); + tdma_reset(serial_tdma_rcv); + + // Lookup times + uint64_t world = ((uint64_t)msg->time.seconds) * NSEC_PER_SEC + + ((uint64_t)msg->time.nanosec); + + // Initialize + if (msg->seq == 0) { + uint64_t local = tdma_time(); + time_ext_init(local, world); + } + + // Valid times timestamp + if (serial_prev_seq == (msg->seq-1)) { + uint64_t local = serial_prev_local; + time_ext_sync(local, world); + } + + // Queue transmit to other board + serial_sync_due = tdma_time() + serial_sync_delay; + + // Update states + serial_prev_local = current; + serial_prev_seq = msg->seq; +} + +/** + * Handle event message + */ +void serial_handle_event(event_msg_t *msg) +{ +} + +/** + * Deliver message + */ +void serial_deliver(int msgid, void *body) +{ + switch (msgid) { + case MSG_ID_SYNC: + sirq_printf("received sync msg\r\n"); + serial_handle_sync((sync_msg_t*)body); + break; + case MSG_ID_EVENT: + sirq_printf("received event msg\r\n"); + serial_handle_event((event_msg_t*)body); + break; + } } /** * Process serial receive messages */ -void serial_receive(void) +void serial_receive(parser_t *parser, int byte) { + //sirq_printf("serial_receive - %02x\r\n", byte); + + // Lookup pointers + header_t *head = (header_t*)parser->buffer; + void *body = (void*)(head+1); + const int max_length = sizeof(parser->buffer)-sizeof(header_t); + + // Process uart messages + parser->buffer[parser->index++] = byte; + switch (parser->state) { + case 0: // Search + if (parser->index == sizeof(uint16_t)) { + if (head->header == MSG_HEADER) { + parser->state = 1; + } else { + parser->buffer[0] = parser->buffer[1]; + parser->index = 1; + } + } + break; + case 1: // Header + if (parser->index == sizeof(header_t)) { + if (head->length <= max_length && + head->msgid <= MSG_MAXID) { + parser->state = 2; + } else { + parser->index = 0; + parser->state = 0; + } + } + break; + case 2: // Data + if (parser->index == (int)sizeof(header_t)+head->length) { + serial_deliver(head->msgid, body); + parser->index = 0; + parser->state = 0; + } + break; + } } /******************** @@ -122,35 +292,152 @@ void serial_receive(void) DigitalOut led1(LED1); DigitalOut led2(LED2); -// UARTs tx rx -Serial uart0(USBTX, USBRX); -Serial uart1(PTE0, PTE1); -Serial uart2(PTD3, PTD2); +// Message Parsers +parser_t parser_dbg; +parser_t parser_bbb; +parser_t parser_mbed; -// Serial DMA -sdma_t *sdma0; -sdma_t *sdma1; -sdma_t *sdma2; +// Serial IRQ +sirq_t *sirq_dbg; +sirq_t *sirq_bbb; +sirq_t *sirq_mbed; // Timer DMA -tdma_t *tdma0; -tdma_t *tdma1; -tdma_t *tdma2; -tdma_t *tdma3; +tdma_t *tdma_evt; +tdma_t *tdma_rcv; +tdma_t *tdma_xmt; + +/********* + * Tasks * + *********/ + +void task_serial(uint64_t local, uint64_t world) +{ + while (sirq_ready(sirq_dbg)) { + //sirq_printf("serial recv - dbg\r\n"); + serial_receive(&parser_dbg, sirq_getc(sirq_dbg)); + } + + while (sirq_ready(sirq_bbb)) { + //sirq_printf("serial recv - bbb\r\n"); + serial_receive(&parser_bbb, sirq_getc(sirq_bbb)); + } + + while (sirq_ready(sirq_mbed)) { + //sirq_printf("serial recv - mbed\r\n"); + serial_receive(&parser_mbed, sirq_getc(sirq_mbed)); + } +} + +void task_events(uint64_t local, uint64_t world) +{ + uint64_t event = 0; + +#ifdef VERBOSE + if (tdma_stamp(tdma_evt, &event)) { + sirq_printf("event received - evt\r\n"); + if (tdma_stamp(tdma_rcv, &event)) + sirq_printf("event received - rcv\r\n"); + if (tdma_stamp(tdma_xmt, &event)) + sirq_printf("event received - xmt\r\n"); +#endif + + if (tdma_stamp(tdma_evt, &event)) + serial_send_event(0, event); + tdma_reset(tdma_evt); +} + +void task_sync(uint64_t local, uint64_t world) +{ + serial_send_sync(sirq_bbb, local); +} + +void task_leds(uint64_t local, uint64_t world) +{ + static uint32_t which = 0; + led1 = (which == 0); + led2 = (which == 1); + which ^= 1; +} + +void task_debug(uint64_t local, uint64_t world) +{ + //tdma_debug(tdma_rcv); + sirq_printf("background - %6d.%02d -> %d.%02d\r\n", + (uint32_t)(local / NSEC_PER_SEC), + (uint32_t)(local % NSEC_PER_SEC / 10000000), + (uint32_t)(world / NSEC_PER_SEC), + (uint32_t)(world % NSEC_PER_SEC / 10000000)); +} /******** * Main * ********/ -void test_main(void); +#define N_ELEM(x) (sizeof(x) / sizeof((x)[0])) + +extern void test_main(void); +extern serial_t stdio_uart; + +static struct { + void (*task)(uint64_t, uint64_t); + uint64_t period; + uint64_t due; +} tasks[] = { + { task_serial, 0 }, // always + { task_events, 1000000000 }, // always + { task_sync, 0 }, // always + { task_leds, 100000000 }, // 10hz + { task_debug, 1000000000 }, // 1hz +}; + +void background(void) +{ + // Debugging + uint64_t local = tdma_time(); + uint64_t world = time_to_world(local); + + // Run the scheduler + for (unsigned i = 0; i < N_ELEM(tasks); i++) { + if (local >= tasks[i].due) { + tasks[i].task(local, world); + tasks[i].due += tasks[i].period; + } + } +} int main(int argc, char **argv) { - uart0.baud(115200); - uart1.baud(115200); - uart2.baud(115200); + tdma_init(); + + // Open serial ports + sirq_dbg = sirq_open(SIRQ_UART0, USBTX, USBRX, 115200); // to pc + sirq_bbb = sirq_open(SIRQ_UART1, PTD3, PTD2, 115200); // to bbb + sirq_mbed = sirq_open(SIRQ_UART2, PTE0, PTE1, 115200); // to mbed + + + // Setup timers + tdma_evt = tdma_open(TDMA_CHAN0, 3, PTC9, PullDown); // async event + + // mbed time sync + tdma_rcv = tdma_open(TDMA_CHAN2, 3, PTD2, PullUp); // time sync rcv + tdma_xmt = tdma_open(TDMA_CHAN3, 3, PTD3, PullUp); // time sync xmt + + // host time sync + //tdma_rcv = tdma_open(TDMA_CHAN2, 2, USBRX, PullUp); // time sync rcv + //tdma_xmt = tdma_open(TDMA_CHAN3, 2, USBTX, PullUp); // time sync xmt + + // Serial timestamping + serial_tdma_rcv = tdma_rcv; + serial_tdma_xmt = tdma_xmt; + + // Run background loop + printf("hello"); + while (true) + background(); - test_main(); + // Run tests + //test_main(); return 0; } diff --git a/hw2/makefile b/hw2/makefile index 0f9b65d..b32f808 100644 --- a/hw2/makefile +++ b/hw2/makefile @@ -1,10 +1,22 @@ PROG = mbed -OBJS = main.o test.o serial_dma.o timer_dma.o +UART = /dev/ttyACM0 +OBJS = main.o test.o serial_irq.o serial_dma.o timer_dma.o CPPFLAGS = LDFLAGS = -lm -default: info install +default: info run + +control: control.c + gcc -Wall -o control control.c + +run: all control install.sh + @./install.sh $(PROG).bin + @./control $(UART) + +terms: + term /dev/ttyACM0 & + term /dev/ttyACM1 & dist: zip mbed.zip makefile ../common.mk *.{c,cpp,h} */*.{cpp,.h} diff --git a/hw2/messages.h b/hw2/messages.h new file mode 100644 index 0000000..20887cc --- /dev/null +++ b/hw2/messages.h @@ -0,0 +1,40 @@ +/*********************** + * Message Definitions * + ***********************/ + +#include + +#define MSG_HEADER 0x1234 +#define MSG_MAXID 2 + +#pragma pack(1) + +typedef enum { + MSG_ID_SYNC, // Time synchronization + MSG_ID_EVENT, // Event occurred +} msgid_t; + +typedef struct { + uint32_t seconds; // Seconds since 1970 (without leap seconds) + uint32_t nanosec; // Nanoseconds since 'seconds' +} ntime_t; + +typedef struct { + uint16_t header; // Message Header + uint16_t msgid; // Message ID + uint16_t length; // Body length + uint16_t cksum; // Body checksum +} header_t; + +typedef struct { + uint32_t seq; // Current sequence counter + ntime_t time; // Time of previous message +} sync_msg_t; + +typedef struct { + uint16_t device; // Device ID + uint16_t event; // Event ID + ntime_t time; // Timestamp +} event_msg_t; + +#pragma pack() diff --git a/hw2/serial_irq.c b/hw2/serial_irq.c new file mode 100644 index 0000000..67beeab --- /dev/null +++ b/hw2/serial_irq.c @@ -0,0 +1,114 @@ +#include + +#include +#include +#include "serial_api.h" +#include "serial_irq.h" + +/* Defines */ +#define SIRQ_LEN 1024 + +/* Port structure */ +typedef struct { + int rix; + int wix; + uint8_t buf[SIRQ_LEN]; +} queue_t; + +struct sirq_t { + serial_t uart; + queue_t xmt; + queue_t rcv; +}; + +/* Port data */ +static sirq_t sirq_ports[SIRQ_NUM_UART]; + +/* Receive handler */ +void sirq_handler(uint32_t _port, SerialIrq event) +{ + sirq_t *port = (sirq_t *)_port; + + // Handle transmit + if (event == TxIrq && port->xmt.rix != port->xmt.wix) { + int byte = port->xmt.buf[port->xmt.rix]; + serial_putc(&port->uart, byte); + port->xmt.rix = (port->xmt.rix+1) % SIRQ_LEN; + } else { + serial_irq_set(&port->uart, TxIrq, 0); + } + + // Handle receive + if (event == RxIrq) { + int byte = serial_getc(&port->uart); + port->rcv.buf[port->rcv.wix] = byte; + port->rcv.wix = (port->rcv.wix+1) % SIRQ_LEN; + } +} + +/* Open port */ +sirq_t *sirq_open(sirq_uart_t uart, PinName tx, PinName rx, int baud) +{ + // Allocate port + sirq_t *port = &sirq_ports[uart]; + + // Configure port + serial_init(&port->uart, tx, rx); + serial_baud(&port->uart, baud); + + // Set IRQ handlers + serial_irq_handler(&port->uart, sirq_handler, (uint32_t)port); + serial_irq_set(&port->uart, RxIrq, 1); + + return port; +} + +/* Write byte to the port */ +void sirq_putc(sirq_t *port, int byte) +{ + port->xmt.buf[port->xmt.wix] = byte; + port->xmt.wix = (port->xmt.wix+1) % SIRQ_LEN; + serial_irq_set(&port->uart, TxIrq, 1); +} + +/* Read byte from the port */ +int sirq_getc(sirq_t *port) +{ + int byte = 0; + if (port->rcv.rix < port->rcv.wix) { + byte = port->rcv.buf[port->rcv.rix]; + port->rcv.rix = (port->rcv.rix+1) % SIRQ_LEN; + } + return byte; +} + +/* Buffered write */ +void sirq_write(sirq_t *port, void *data, int len) +{ + uint8_t *bytes = (uint8_t*)data; + for (int i = 0; i < len; i++) + sirq_putc(port, bytes[i]); +} + +/* Check if port is writable */ +int sirq_ready(sirq_t *port) +{ + return port->rcv.rix < port->rcv.wix; +} + +/* Write ASCII data to the output queue */ +void sirq_vprintf(const char *fmt, va_list ap) +{ + static char buf[512]; + int len = vsnprintf(buf, sizeof(buf), fmt, ap); + for (int i = 0; i < len; i++) + sirq_putc(&sirq_ports[0], buf[i]); +} + +void sirq_printf(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + sirq_vprintf(fmt, ap); + va_end(ap); +} diff --git a/hw2/serial_irq.h b/hw2/serial_irq.h new file mode 100644 index 0000000..5595d14 --- /dev/null +++ b/hw2/serial_irq.h @@ -0,0 +1,44 @@ +#ifndef SERIAL_IRQ_H +#define SERIAL_IRQ_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Sizes */ +#define SIRQ_NUM_UART 3 + +/* Serial Ports */ +typedef enum { + SIRQ_UART0, + SIRQ_UART1, + SIRQ_UART2, +} sirq_uart_t; + +/* Port */ +typedef struct sirq_t sirq_t; + +/* Open */ +sirq_t *sirq_open(sirq_uart_t uart, PinName tx, PinName rx, int baud); + +/* Read/Write */ +void sirq_putc(sirq_t *port, int byte); +int sirq_getc(sirq_t *port); +void sirq_write(sirq_t *port, void *data, int len); + +/* Read In */ +int sirq_ready(sirq_t *port); + +/* Print */ +void sirq_vprintf(const char *fmt, va_list ap); +void sirq_printf(const char *fmt, ...); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/hw2/timer_dma.c b/hw2/timer_dma.c index 23f11e2..2e19718 100644 --- a/hw2/timer_dma.c +++ b/hw2/timer_dma.c @@ -71,7 +71,7 @@ void tdma_init(void) } /* DMA Functions */ -tdma_t *tdma_open(tdma_chan_t chan, PinName pin, PinMode mode) +tdma_t *tdma_open(tdma_chan_t chan, int alt, PinName pin, PinMode mode) { int req = pin >= PTD0 ? TDMA_REQ_PTD : pin >= PTC0 ? TDMA_REQ_PTC : @@ -95,7 +95,7 @@ tdma_t *tdma_open(tdma_chan_t chan, PinName pin, PinMode mode) | DMA_DCR_DINC_MASK // Dest increment | DMA_DCR_SSIZE(0) // 32-bit access | DMA_DCR_DSIZE(0) // 32-bit access - | DMA_DCR_ERQ_MASK; // Enable port request + | DMA_DCR_D_REQ_MASK; // Only run once // Setup and enable DMA MUX port->mux->cfg = DMAMUX_CHCFG_SOURCE(req) // Request source @@ -103,42 +103,101 @@ tdma_t *tdma_open(tdma_chan_t chan, PinName pin, PinMode mode) // Set pin to generate DMA req port->pin->pcr = PORT_PCR_ISF_MASK // Clear ISR flag - | PORT_PCR_MUX(3) // Pin mapping + | PORT_PCR_MUX(alt) // Pin mapping | PORT_PCR_IRQC(ircq) // DMA on falling edge | mode; // Pin pull up/down + // Initial reset + tdma_reset(port); + return port; } void tdma_reset(tdma_t *port) { + if (!port) + return; + // Clear previous time port->time[0] = 0; port->time[1] = 0; + // Freeze DMA channel + port->dma->dcr &= ~DMA_DCR_ERQ_MASK; + // Reset DMA channel - port->dma->dsr = DMA_DSR_BCR_DONE_MASK; + port->dma->dsr = DMA_DSR_BCR_DONE_MASK; // Set addresses and size - port->dma->sar = (uint32_t)&PIT->LTMR64H; // Global timer - port->dma->dar = (uint32_t)&port->time; // Temp timer buffer - port->dma->dsr = DMA_DSR_BCR_BCR(8); // 64-bit timer + port->dma->sar = (uint32_t)&PIT->LTMR64H; // Global timer + port->dma->dar = (uint32_t)&port->time; // Temp timer buffer + port->dma->dsr = DMA_DSR_BCR_BCR(8); // 64-bit timer + + // Enable port request + port->dma->dcr |= DMA_DCR_ERQ_MASK; } int tdma_stamp(tdma_t *port, uint64_t *time) { + uint64_t clocks; + + if (!port) + return 0; + if (port->dma->dsr & DMA_DSR_BCR_BCR_MASK) return 0; // Read the timestamp - *time = ((uint64_t)~port->time[0]) << 32 - | ((uint64_t)~port->time[1]) << 0; + clocks = ((uint64_t)~port->time[0]) << 32 + | ((uint64_t)~port->time[1]) << 0; - // Debug output.. - //printf(" - sar:%08lx dar:%08lx pcr:%08lx dsr:%08lx time:%08lx:%08lx", - // port->dma->sar, port->dma->dar, - // port->pin->pcr, port->dma->dsr, - // (uint32_t)(*time >> 32), (uint32_t)*time); + // Convert to nanoseconds + *time = clocks * 1000 / 24; return 1; } + +uint64_t tdma_time(void) +{ + uint32_t tmh = PIT->LTMR64H; + uint32_t tml = PIT->LTMR64L; + + // Read the timestamp + uint64_t clocks = ((uint64_t)~tmh) << 32 + | ((uint64_t)~tml) << 0; + + // Convert to nanoseconds + return clocks * 1000 / 24; +} + +void tdma_debug(tdma_t *port) +{ + int dsr = port->dma->dsr; + int dcr = port->dma->dcr; + + printf("dsr: %s %s %s %s %s %s %d\r\n", + dsr & DMA_DSR_BCR_CE_MASK ? "CE" : "ce", + dsr & DMA_DSR_BCR_BES_MASK ? "BSE" : "bse", + dsr & DMA_DSR_BCR_BED_MASK ? "BED" : "bed", + dsr & DMA_DSR_BCR_REQ_MASK ? "REQ" : "req", + dsr & DMA_DSR_BCR_BSY_MASK ? "BSY" : "bsy", + dsr & DMA_DSR_BCR_DONE_MASK ? "DONE" : "done", + dsr & DMA_DSR_BCR_BCR_MASK); + + printf("dcr: %s %s %s %s %s %s %s %s ssize=%d:%d mod=%d:%d link=%d:%d:%d\r\n", + dcr & DMA_DCR_EINT_MASK ? "EINT" : "eint", + dcr & DMA_DCR_ERQ_MASK ? "ERQ" : "erq", + dcr & DMA_DCR_CS_MASK ? "CS" : "cs", + dcr & DMA_DCR_AA_MASK ? "AA" : "aa", + dcr & DMA_DCR_EADREQ_MASK ? "EADRREQ" : "eadrreq", + dcr & DMA_DCR_SINC_MASK ? "SINC" : "sinc", + dcr & DMA_DCR_DINC_MASK ? "DINC" : "dinc", + dcr & DMA_DCR_D_REQ_MASK ? "DREQ" : "dreq", + (dcr & DMA_DCR_SSIZE_MASK ) >> DMA_DCR_SSIZE_SHIFT, + (dcr & DMA_DCR_DSIZE_MASK ) >> DMA_DCR_DSIZE_SHIFT, + (dcr & DMA_DCR_SMOD_MASK ) >> DMA_DCR_SMOD_SHIFT, + (dcr & DMA_DCR_DMOD_MASK ) >> DMA_DCR_DMOD_SHIFT, + (dcr & DMA_DCR_LINKCC_MASK) >> DMA_DCR_LINKCC_SHIFT, + (dcr & DMA_DCR_LCH1_MASK ) >> DMA_DCR_LCH1_SHIFT, + (dcr & DMA_DCR_LCH2_MASK ) >> DMA_DCR_LCH2_SHIFT); +} diff --git a/hw2/timer_dma.h b/hw2/timer_dma.h index cf672d0..c6d2763 100644 --- a/hw2/timer_dma.h +++ b/hw2/timer_dma.h @@ -27,12 +27,18 @@ typedef struct tdma_t tdma_t; void tdma_init(void); /* Open */ -tdma_t *tdma_open(tdma_chan_t chan, PinName pin, PinMode mode); +tdma_t *tdma_open(tdma_chan_t chan, int alt, PinName pin, PinMode mode); /* Flush/Wait */ void tdma_reset(tdma_t *port); int tdma_stamp(tdma_t *port, uint64_t *time); +/* Time */ +uint64_t tdma_time(void); + +/* Debug print */ +void tdma_debug(tdma_t *port); + #ifdef __cplusplus } #endif -- 2.43.2