creole interface

This commit is contained in:
Peter McGoron 2023-04-03 03:14:19 +00:00
parent 5b390929f5
commit 1911d58725
7 changed files with 576 additions and 121 deletions

2
creole

@ -1 +1 @@
Subproject commit 13c67aba9a3ed72326cfa308b7c3e77e3e9934ed
Subproject commit dc1abae13a7d8d50043dfbf22b8c8592692617f8

368
software/src/access.c Normal file
View File

@ -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 <zephyr/kernel.h>
#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);
}
}

36
software/src/access.h Normal file
View File

@ -0,0 +1,36 @@
#pragma once
#include <zephyr/kernel.h>
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);

View File

@ -1,75 +1,39 @@
#include <sys_clock.h>
#include <sys/util.h>
#include <zephyr/sys_clock.h>
#include <zephyr/kernel.h>
#include <zephyr/sys/util.h>
#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();
}

View File

@ -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);
}
}

View File

@ -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

View File

@ -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