diff --git a/creole b/creole index 13c67ab..dc1abae 160000 --- a/creole +++ b/creole @@ -1 +1 @@ -Subproject commit 13c67aba9a3ed72326cfa308b7c3e77e3e9934ed +Subproject commit dc1abae13a7d8d50043dfbf22b8c8592692617f8 diff --git a/software/src/access.c b/software/src/access.c new file mode 100644 index 0000000..109ed95 --- /dev/null +++ b/software/src/access.c @@ -0,0 +1,368 @@ +/* Verilog access control. + * + * Mutex management for hardware resources. + * Each mutex has a thread-local lock counter associated with it, since in + * Zephyr the recursive lock counter is not publically accessable. + * + * On the final unlock for a thread, the code will always take steps to + * reset the state of the Verilog module so that another thread will + * see a consistent start state. + */ + +#include +#include "converters.h" +#include "pin_io.h" + +static inline uint32_t +sign_extend(uint32_t in, unsigned len) +{ + if (in >> (len - 1) & 1) { + uint32_t mask = (1 << len) - 1; + return ~mask | (in & mask); + } else { + return in; + } +} + +/********************* + * DAC + *********************/ + +static struct k_mutex_t dac_mutex[DAC_MAX]; +static __thread int dac_locked[DAC_MAX]; + +int +dac_take(int dac, k_timeout_t timeout) +{ + if (dac < 0 || dac >= DAC_MAX) + return -EFAULT; + + int e = k_mutex_lock(dac_mutex + dac, timeout); + if (e == 0) { + dac_locked[dac] += 1; + } + return e; +} + +int +dac_release(int dac) +{ + if (dac < 0 || dac >= DAC_MAX) + return -EFAULT; + + if (dac_locked[dac] == 1) { + *dac_arm[dac] = 0; + while (!*dac_finished[dac]); + } + + int e = k_mutex_unlock(dac_mutex + dac); + if (e == 0) { + dac_locked[dac] -= 1; + } + return e; +} + +int +dac_read_write(int dac, creole_word send, k_timeout_t timeout, + creole_word *recv) +{ + int e = dac_take(dac, timeout); + if (e != 0) + return e; + + *to_dac[dac] = send; + *dac_arm[adc] = 1; + + /* Recursive locks should busy wait. */ + /* 10ns * (2 * 10 cycles per half DAC cycle) + * 24 bits + */ + if (dac_locked[dac] > 1) + k_sleep(K_NSEC(10*2*10*24)); + while (!*dac_finished[dac]); + + if (recv) + *recv = sign_extend(*from_dac[dac], 20); + *dac_arm[dac] = 0; + + dac_release(dac); + return 0; +} + +/********************** + * adc read + *********************/ + +static struct k_mutex_t adc_mutex[ADC_MAX]; +static __thread int adc_locked[ADC_MAX]; + +int +adc_take(int adc, int timeout) +{ + if (adc < 0 || adc >= ADC_MAX) + return -EFAULT; + + int e = k_mutex_lock(adc_mutex + adc, timeout); + if (e == 0) { + adc_locked[adc] += 1; + } + return e; +} + +int +adc_release(int adc) +{ + if (adc < 0 || adc >= ADC_MAX) + return -EFAULT; + + if (adc_locked[adc] == 1) { + *adc_arm[adc] = 0; + while (!*adc_finished[adc]); + } + + int e = k_mutex_unlock(adc_mutex + adc); + if (e == 0) { + adc_locked[adc] -= 1; + } + return e; +} + +int +adc_read(int adc, int timeout, creole_word *wrd) +{ + int e; + if ((e = adc_take(adc, timeout)) != 0) + return e; + + *adc_arm[adc] = 1; + + /* Recursive locks should busy wait. */ + if (adc_locked[adc] > 1) + k_sleep(K_NSEC(550 + 24*2*10*10)); + while (!*adc_finished[adc]); + + *wrd = sign_extend(*from_adc[adc]); + *adc_arm[adc] = 0; + + adc_release(adc); + return 0; +} + +/******** + * Control loop + *******/ + +static struct k_mutex_t cloop_mutex; +static __thread cloop_locked; + +int +cloop_take(k_timeout_t timeout) +{ + int e = k_mutex_lock(&cloop_mutex, timeout); + if (e == 0) { + cloop_locked++; + } + return e; +} + +int +cloop_release(void) +{ + /* If being unlocked for the last time. */ + if (cloop_locked == 1) { + *cl_cmd = CONTROL_LOOP_WRITE_BIT | CONTROL_LOOP_STATUS; + cl_word_out[0] = 0; + cl_word_out[1] = 0; + *cl_start_cmd = 1; + while (!*cl_finish_cmd); + *cl_start_cmd = 0; + } + int e = k_mutex_unlock(&cloop_mutex); + if (e == 0) { + cloop_locked--; + } + return e; +} + +int +cloop_read(int code, uint32_t *high_reg, uint32_t *low_reg, + k_timeout_t timeout) +{ + if (cloop_take(timeout) != 0) + return 0; + + *cl_cmd = code; + *cl_start_cmd = 1; + while (!*cl_finish_cmd); + *high_reg = cl_word_out[0]; + *low_reg = cl_word_out[1]; + *cl_start_cmd = 0; + + cloop_release(); + return 1; +} + +int +cloop_write(int code, uint32_t high_reg, uint32_t low_reg, + k_timeout_t timeout) +{ + if (cloop_take(timeout) != 0) + return 0; + + *cl_cmd = code; + cl_word_in[0] = high_val; + cl_word_in[1] = low_val; + + *cl_start_cmd = 1; + while (!*cl_finish_cmd); + *cl_start_cmd = 0; + + cloop_release(); + return 1; +} + +/************ + * Waveforms + ***********/ + +static struct k_mutex_t waveform_mutex[DAC_MAX]; +static __thread int waveform_locked[DAC_MAX]; + +int +waveform_take(int waveform, k_timeout_t timeout) +{ + if (waveform < 0 || waveform >= DAC_MAX) + return -EFAULT; + + int e = k_mutex_lock(waveform_mutex + waveform, timeout); + if (e == 0) { + waveform_locked[e]++; + } + return e; +} + +int +waveform_release(int waveform) +{ + if (waveform < 0 || waveform >= DAC_MAX) + return -EFAULT; + + if (waveform_locked[waveform] == 1) { + *wf_arm[waveform] = 0; + while (*wf_running[waveform]); + } + + int e k_mutex_unlock(waveform_mutex + waveform); + if (e == 0) { + waveform_locked[e]--; + } +} + +size_t +creole_to_array(const struct creole_reader *start, creole_word *buf, size_t buflen) +{ + size_t i = 0; + struct creole_word w; + struct creole_reader r = start; + + while (creole_decode(&r, &w) && i < buflen) { + buf[i++] = w.word; + } + + return i; +} + +int +waveform_load(uint32_t buf[MAX_WL_SIZE], int slot, k_timeout_t timeout) +{ + if (waveform_take(slot, timeout) != 0) + return 0; + + if (load_into_array(env->dats[db], buf, ARRAY_SIZE(buf)) + != MAX_WL_SIZE) + goto ret; + + *wf_start_addr[slot] = &buf; + *wf_refresh_start[slot] = 1; + while (!*wf_refresh_finished[slot]); + *wf_refresh_start[slot] = 0; + + waveform_release(slot); + return 1; +} + +int +waveform_arm(int slot, bool halt_on_finish, uint32_t wait, k_timeout_t timeout) +{ + if (waveform_take(slot, timeout) != 0) + return 0; + if (dac_take(slot, timeout) != 0) { + waveform_release(slot); + return 0; + } + + *wf_halt_on_finished[slot] = halt_on_finish; + *wf_time_to_wait[slot] = wait; + *wf_arm[slot] = 1; + + return 1; +} + +int +waveform_disarm(int slot) +{ + *wf_arm[slot] = 0; + while (*wf_running[slot]); + waveform_release(slot); + dac_release(slot); + return 1; +} + +/********** + * Init and deinit + *********/ + +void +access_release_thread(void) +{ + while (cloop_release() == 0); + + for (int i = 0; i < DAC_NUM; i++) { + while (dac_release(i) == 0); + while (waveform_release(i) == 0); + } + + for (int i = 0; i < ADC_NUM; i++) { + while (adc_release(i) == 0); + } +} + +void +access_thread_init(void) +{ + cloop_locked = 0; + + for (int i = 0; i < DAC_NUM; i++) { + dac_locked[i] = 0; + waveform_locked[i] = 0; + } + + for (int i = 0; i < ADC_NUM; i++) { + adc_locked[i] = 0; + } +} + +int +access_init(void) +{ + k_mutex_init(cloop_mutex); + + for (int i = 0; i < DAC_NUM; i++) { + k_mutex_init(dac_mutex + i); + k_mutex_init(waveform_mutex + i); + } + + for (int i = 0; i < ADC_NUM; i++) { + k_mutex_init(adc_mutex + i); + } +} diff --git a/software/src/access.h b/software/src/access.h new file mode 100644 index 0000000..6ba4836 --- /dev/null +++ b/software/src/access.h @@ -0,0 +1,36 @@ +#pragma once +#include + + +int dac_release(int dac); +int dac_read_write(int dac, creole_word send, k_timeout_t timeout, + creole_word *recv); + +int adc_take(int adc, int timeout); +int adc_release(int adc); +int adc_read(int adc, int timeout, creole_word *wrd); + +int cloop_take(k_timeout_t timeout); +int cloop_release(void); + +int waveform_take(int waveform, k_timeout_t timeout); +int waveform_release(int waveform); + +#define MAX_WL_SIZE 4096 +int waveform_load(uint32_t buf[MAX_WL_SIZE], int slot, k_timeout_t timeout); +int waveform_arm(int slot, bool halt_on_finish, uint32_t wait, k_timeout_t timeout); +int waveform_disarm(int slot); + + +/* Zephyr OS does not automatically clean up mutex resources. + * This will release all held locks. + */ +void access_release_thread(void); + +/* Initialize thread local data, which might be non-zero due + * to reusing threads. + */ +void access_thread_init(void); + +/* Called once on initializion. */ +int access_init(void); diff --git a/software/src/creole_interface.c b/software/src/creole_interface.c index e284a5f..c0dd38b 100644 --- a/software/src/creole_interface.c +++ b/software/src/creole_interface.c @@ -1,75 +1,39 @@ -#include -#include +#include +#include +#include #include "control_loop_cmds.h" #include "creole.h" #include "creole_upsilon.h" #include "pin_io.h" #include "creole.h" -static inline uint32_t -sign_extend(uint32_t in, unsigned len) +creole_word +upsilon_get_adc(creole_word adc, creole_word *ret) { - if (in >> (len - 1) & 1) { - uint32_t mask = (1 << len) - 1; - return ~mask | (in & mask); - } else { - return in; - } + return adc_read(adc, K_FOREVER, ret); } creole_word -upsilon_get_adc(creole_word adc) +upsilon_get_dac(creole_word dac, creole_word *ret) { - if (adc < 0 || adc >= ADC_MAX) - return 0; - - *adc_arm[adc] = 1; - /* TODO: sleep 550 ns */ - while (!*adc_finished[adc]); - - creole_word w = sign_extend(*from_adc[adc]); - *adc_arm[adc] = 0; - - return w; -} - -static uint32_t -send_dac(creole_word dac, uint32_t val) -{ - *to_dac[dac] = val; - *dac_arm[dac] = 1; - /* TODO: sleep */ - while (!*dac_finished[dac]); - uint32_t w = sign_extend(*from_dac[dac], 20); - *dac_arm[dac] = 0; - - return w; -} - -creole_word -upsilon_get_dac(creole_word dac) -{ - if (dac < 0 || dac >= DAC_MAX) - return 0; - - send_dac(dac, 0x1 << 23 | 0x1 << 20); - return send_dac(dac, 0); + int e; + e = dac_read_write(dac, 0x1 << 23 | 0x1 << 20, K_FOREVER, NULL); + if (e != 0) + return e; + return dac_read_write(dac, 0, K_FOREVER, ret); } creole_word upsilon_write_dac(creole_word dac, creole_word val) { - if (dac < 0 || dac >= DAC_MAX) - return 0; - send_dac(dac, 0x1 << 20 | val); - return 1; + return dac_read_write(dac, 0x1 << 20 | val, K_FOREVER, NULL); } creole_word upsilon_usec(creole_word usec) { k_sleep(K_USEC(usec)); - return 1; + return 0; } creole_word @@ -77,14 +41,7 @@ upsilon_control_loop_read(creole_word *high_reg, creole_word *low_reg, creole_word code) { - *cl_cmd = code; - *cl_start_cmd = 1; - while (!*cl_finish_cmd); - *high_reg = cl_word_out[0]; - *low_reg = cl_word_out[1]; - *cl_start_cmd = 0; - - return 1; + return cloop_read(code, high_reg, low_reg, K_FOREVER); } creole_word @@ -92,14 +49,7 @@ upsilon_control_loop_write(creole_word high_val, creole_word low_val, creole_word code) { - *cl_cmd = CONTROL_LOOP_WRITE_BIT | code; - cl_word_in[0] = high_val; - cl_word_in[1] = low_val; - *cl_start_cmd = 1; - while (!*cl_finish_cmd); - *cl_start_cmd = 0; - - return 1; + return cloop_write(code, high_val, low_val, K_FOREVER); } static size_t @@ -123,37 +73,21 @@ upsilon_load_waveform(struct creole_env *env, creole_word slot, { creole_word buf[MAX_WL_SIZE]; size_t len = load_into_array(env->dats[db], buf, ARRAY_SIZE(buf)); - - if (len != MAX_WL_SIZE) + if (len < MAX_WL_SIZE) return 0; - - *wf_start_addr[slot] = &buf; - *wf_refresh_start[slot] = 1; - while (!*wf_refresh_finished[slot]); - *wf_refresh_start[slot] = 0; - - return 1; + return waveform_load(buf, slot, K_FOREVER); } + creole_word upsilon_arm_waveform(creole_word slot, creole_word hof, creole_word wait) { - *wf_halt_on_finished[slot] = hof; - *wf_time_to_wait[slot] = wait; - *wf_arm[slot] = 1; - - if (wait) { - while (!*wf_finished[slot]); - *wf_arm[slot] = 0; - } - - return 1; + return waveform_arm(slot, hof, wait, K_FOREVER); } creole_word upsilon_disarm_waveform(creole_word slot) { - *wf_arm[slot] = 0; - return 1; + return waveform_disarm(slot); } creole_word @@ -181,3 +115,51 @@ upsilon_senddat(struct creole_env *env, creole_word db) return sock_write_buf(env->fd, &bp); } + +creole_word +upsilon_take_adc(creole_word slot, creole_word timeout) +{ + return adc_take(slot, K_USEC(timeout)); +} + +creole_word +upsilon_release_adc(creole_word slot) +{ + return adc_release(slot); +} + +creole_word +upsilon_take_dac(creole_word slot, creole_word timeout) +{ + return dac_take(slot, K_USEC(timeout)); +} + +creole_word +upsilon_release_dac(creole_word slot) +{ + return dac_release(slot); +} + +creole_word +upsilon_take_wf(creole_word slot, creole_word timeout) +{ + return waveform_take(slot, K_USEC(timeout)); +} + +creole_word +upsilon_release_wf(creole_word slot) +{ + return waveform_release(slot); +} + +creole_word +upsilon_take_cloop(creole_word timeout) +{ + return cloop_take(K_USEC(timeout)); +} + +creole_word +upsilon_release_cloop(void) +{ + return cloop_release(); +} diff --git a/software/src/main.c b/software/src/main.c index be60604..27f55f7 100644 --- a/software/src/main.c +++ b/software/src/main.c @@ -9,36 +9,107 @@ LOG_MODULE_REGISTER(main); -static void -process_client(int cli) +#define THREAD_STACK_SIZ 1024*32 +#define THREADNUM 32 +static K_KERNEL_STACK_ARRAY_DEFINE(stacks, THREADNUM, THREAD_STACK_SIZ); + +#define READBUF_SIZ 0xFFFF +static unsigned char readbuf[THREADNUM][READBUF_SIZ]; +static struct kthread_t threads[THREADNUM]; +static bool thread_ever_used[THREADNUM]; + +static int +read_size(int s) { - enum fds { - CLIENT_FD, - SCANDATA_FD, - MAXFDS + char buf[2]; + struct bufptr bp = {buf, sizeof(buf)}; + + if (!sock_read_buf(s, &bp, true)) + return -1; + return (unsigned char)buf[0] | (unsigned char) buf[1] << 8; +} + +static void +exec_creole(unsigned char *buf, int size, int sock) +{ +#define DATLEN 64 + struct creole_reader dats[DATSLEN]; +#define REGLEN 32 + creole_word reg[REGLEN]; +#define STKLEN 1024 + creole_word stk[STKLEN]; + + struct creole_env env = { + .dats = dats, + .datlen = DATLEN, + .reg = reg, + .reglen = REGLEN, + .stk = stk, + .stklen = STKLEN, + + .r_current = {buf, size}, + .r_start = {buf, size}, + .sock = sock }; - struct zsock_pollfd fds[MAXFDS] = {0}; - fds[CLIENT_FD].fd = cli; - fds[CLIENT_FD].events = ZSOCK_POLLIN; - // Currently not used - fds[SCANDATA_FD].fd = -1; +} - while (zsock_poll(fds, MAXFDS, 0) >= 0) { - if (fds[CLIENT_FD].revents | POLLIN) - client_parse(cli); +/* TODO: error messages */ + +static void +exec_entry(void *client_p, void *threadnum_p, + void *unused __attribute__((unused))) +{ + intptr_t client = client_p; + intptr_t threadnum = threadnum_p; + int size = read_size(client); + + if (size < 0) { + zsock_close(client); + return; + } + + struct bufptr bp = {readbuf[threadnum], size}; + if (!sock_read_buf(client, &bp, true)) { + zsock_close(client); + return; + } + + exec_creole(size); + zsock_close(client); +} + +/* TODO: main thread must be higher priority than execution threads */ +static void +main_loop(int srvsock) +{ + for (;;) { + int client = server_accept_client(srvsock); + int i; + + for (i = 0; i < THREADNUM; i++) { + if (!thread_ever_used[i] + || k_thread_join(threads[i], 0) == 0) { + k_thread_create(threads[i], stacks[i], + THREAD_STACK_SIZ, exec_entry, + (uintptr_t) client, (uintptr_t) i, NULL, + 1, 0, K_NO_WAIT); + } + } + + if (i == THREADNUM) { + LOG_INF("Too many connections (max %d)", + THREADNUM); + } } } void main(void) { - int srv = server_init_sock(6626); - for (;;) { - int cli = server_get_client(server_sock); - process_client(cli); - LOG_INF("Closing client socket"); - zsock_close(cli); + int sock = server_init_sock(6626); + main_loop(sock); + close(sock); } } diff --git a/software/src/sock.c b/software/src/sock.c index b522c78..7bb4ffd 100644 --- a/software/src/sock.c +++ b/software/src/sock.c @@ -63,25 +63,22 @@ server_accept_client(int server) return client; } -bool +int sock_read_buf(int sock, struct bufptr *bp, bool entire) { - if (bp->left < 2) - return false; - do { ssize_t l = zsock_recv(sock, bp->p, bp->left - 1, 0); if (l < 0) - return false; + return -errno; bp->left -= l; bp->p += l; } while (entire && bp->left > 0); - return true; + return 0; } -bool +int sock_write_buf(int sock, struct bufptr *bp) { /* Since send may not send all data in the buffer at once, @@ -90,12 +87,12 @@ sock_write_buf(int sock, struct bufptr *bp) while (bp->left) { ssize_t l = zsock_send(sock, bp->p, bp->left, 0); if (l < 0) - return false; + return -errno; bp->p += l; bp->left -= l; } - return true; + return 0; } int diff --git a/software/src/sock.h b/software/src/sock.h index ad6ad52..709ee66 100644 --- a/software/src/sock.h +++ b/software/src/sock.h @@ -10,15 +10,16 @@ int server_init_sock(int port); */ int server_accept_client(int server); -/* Read data into buffer. Returns false if error occurs. This is +/* Read data into buffer. Returns 0 if buffer is filled. This is * raw binary (no NUL termination). */ -bool sock_read_buf(int sock, struct bufptr *bp, bool entire); +int sock_read_buf(int sock, struct bufptr *bp, bool entire); /* Write raw buffer data into socket. This data is raw binary and - * does not have to be NUL terminated. + * does not have to be NUL terminated. Returns 0 when all data has + * been written. */ -bool sock_write_buf(int sock, struct bufptr *bp); +int sock_write_buf(int sock, struct bufptr *bp); /* Write formatted string into socket. The user must provide a * buffer into which the formatted string is written. These return