Add support for patchbay and muxes
This commit is contained in:
parent
2d44c4763f
commit
1cea193ea3
@ -1,47 +1,53 @@
|
||||
# Copyright (c) 2021-2022 Barcelona Supercomputing Center (BSC)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
add_library(trace STATIC trace.c)
|
||||
target_link_libraries(trace parson ovni-static)
|
||||
|
||||
add_library(emu STATIC
|
||||
chan.c
|
||||
emu.c
|
||||
nosv.c
|
||||
openmp.c
|
||||
ovni.c
|
||||
tampi.c
|
||||
nodes.c
|
||||
kernel.c
|
||||
nanos6.c
|
||||
task.c
|
||||
pcf.c
|
||||
prv.c
|
||||
)
|
||||
|
||||
include_directories(
|
||||
"${CMAKE_SOURCE_DIR}/src/include"
|
||||
"${CMAKE_SOURCE_DIR}/src"
|
||||
"${CMAKE_SOURCE_DIR}/include"
|
||||
)
|
||||
|
||||
add_executable(ovniemu ovniemu.c)
|
||||
target_link_libraries(ovniemu emu trace)
|
||||
add_library(trace STATIC trace.c)
|
||||
target_link_libraries(trace parson ovni-static)
|
||||
|
||||
add_executable(ovnidump ovnidump.c)
|
||||
target_link_libraries(ovnidump emu trace)
|
||||
add_library(chan STATIC
|
||||
chan.c
|
||||
bay.c
|
||||
mux.c
|
||||
)
|
||||
|
||||
add_executable(ovnisort ovnisort.c)
|
||||
target_link_libraries(ovnisort emu trace)
|
||||
|
||||
# Use <PackageName>_ROOT variables if available, commonly used by MPI
|
||||
# installations
|
||||
if(POLICY CMP0074)
|
||||
cmake_policy(SET CMP0074 NEW)
|
||||
endif()
|
||||
|
||||
find_package(MPI REQUIRED)
|
||||
add_executable(ovnisync ovnisync.c)
|
||||
target_link_libraries(ovnisync m MPI::MPI_C)
|
||||
|
||||
install(TARGETS ovniemu ovnidump ovnisync ovnisort RUNTIME DESTINATION bin)
|
||||
#add_library(emu STATIC
|
||||
# chan.c
|
||||
# emu.c
|
||||
# nosv.c
|
||||
# openmp.c
|
||||
# ovni.c
|
||||
# tampi.c
|
||||
# nodes.c
|
||||
# kernel.c
|
||||
# nanos6.c
|
||||
# task.c
|
||||
# pcf.c
|
||||
# prv.c
|
||||
#)
|
||||
#
|
||||
#add_executable(ovniemu ovniemu.c)
|
||||
#target_link_libraries(ovniemu emu trace)
|
||||
#
|
||||
#add_executable(ovnidump ovnidump.c)
|
||||
#target_link_libraries(ovnidump emu trace)
|
||||
#
|
||||
#add_executable(ovnisort ovnisort.c)
|
||||
#target_link_libraries(ovnisort emu trace)
|
||||
#
|
||||
## Use <PackageName>_ROOT variables if available, commonly used by MPI
|
||||
## installations
|
||||
#if(POLICY CMP0074)
|
||||
# cmake_policy(SET CMP0074 NEW)
|
||||
#endif()
|
||||
#
|
||||
#find_package(MPI REQUIRED)
|
||||
#add_executable(ovnisync ovnisync.c)
|
||||
#target_link_libraries(ovnisync m MPI::MPI_C)
|
||||
#
|
||||
#install(TARGETS ovniemu ovnidump ovnisync ovnisort RUNTIME DESTINATION bin)
|
||||
|
151
src/emu/bay.c
Normal file
151
src/emu/bay.c
Normal file
@ -0,0 +1,151 @@
|
||||
#define ENABLE_DEBUG
|
||||
|
||||
#include "bay.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "uthash.h"
|
||||
#include "utlist.h"
|
||||
|
||||
/* Called from the channel when it becomes dirty */
|
||||
static int
|
||||
cb_chan_is_dirty(struct chan *chan, void *arg)
|
||||
{
|
||||
UNUSED(chan);
|
||||
struct bay_chan *bchan = arg;
|
||||
struct bay *bay = bchan->bay;
|
||||
|
||||
/* TODO: check duplicate? */
|
||||
DL_APPEND(bay->dirty, bchan);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct bay_chan *
|
||||
find_bay_chan(struct bay *bay, const char *name)
|
||||
{
|
||||
struct bay_chan *bchan = NULL;
|
||||
HASH_FIND_STR(bay->channels, name, bchan);
|
||||
|
||||
return bchan;
|
||||
}
|
||||
|
||||
struct chan *
|
||||
bay_find(struct bay *bay, const char *name)
|
||||
{
|
||||
struct bay_chan *bchan = find_bay_chan(bay, name);
|
||||
|
||||
if (bchan != NULL)
|
||||
return bchan->chan;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
bay_register(struct bay *bay, struct chan *chan)
|
||||
{
|
||||
struct bay_chan *bchan = find_bay_chan(bay, chan->name);
|
||||
if (bchan != NULL) {
|
||||
err("bay_register: channel %s already registered\n",
|
||||
chan->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
bchan = calloc(1, sizeof(struct bay_chan));
|
||||
if (bchan == NULL) {
|
||||
err("bay_register: calloc failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
bchan->chan = chan;
|
||||
bchan->bay = bay;
|
||||
chan_set_dirty_cb(chan, cb_chan_is_dirty, bchan);
|
||||
|
||||
/* Add to hash table */
|
||||
HASH_ADD_STR(bay->channels, chan->name, bchan);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
bay_add_cb(struct bay *bay, struct chan *chan, bay_cb_func_t func, void *arg)
|
||||
{
|
||||
if (func == NULL) {
|
||||
err("bay_add_cb: func is NULL\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct bay_chan *bchan = find_bay_chan(bay, chan->name);
|
||||
if (bchan == NULL) {
|
||||
err("bay_add_cb: cannot find channel %s in bay\n", chan->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct bay_cb *cb = calloc(1, sizeof(struct bay_cb));
|
||||
|
||||
if (cb == NULL) {
|
||||
err("bay_add_cb: calloc failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
cb->func = func;
|
||||
cb->arg = arg;
|
||||
|
||||
DL_APPEND(bchan->cb, cb);
|
||||
bchan->ncallbacks++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
bay_init(struct bay *bay)
|
||||
{
|
||||
memset(bay, 0, sizeof(struct bay));
|
||||
bay->state = BAY_READY;
|
||||
}
|
||||
|
||||
static int
|
||||
propagate_chan(struct bay_chan *bchan)
|
||||
{
|
||||
dbg("propagating dirty channel %p\n", (void *) bchan);
|
||||
|
||||
struct bay_cb *cur = NULL;
|
||||
struct bay_cb *tmp = NULL;
|
||||
DL_FOREACH_SAFE(bchan->cb, cur, tmp) {
|
||||
if (cur->func(bchan->chan, cur->arg) != 0) {
|
||||
err("propagate_chan: callback failed\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
bay_propagate(struct bay *bay)
|
||||
{
|
||||
dbg("propagating channels\n");
|
||||
struct bay_chan *cur = NULL;
|
||||
struct bay_chan *tmp = NULL;
|
||||
DL_FOREACH_SAFE(bay->dirty, cur, tmp) {
|
||||
/* May add more dirty channels */
|
||||
if (propagate_chan(cur) != 0) {
|
||||
err("bay_propagate: propagate_chan failed\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
bay->dirty = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//int
|
||||
//bay_emit(struct bay *bay)
|
||||
//{
|
||||
// for (chan in dirty channels) {
|
||||
// /* May add more dirty channels */
|
||||
// emit_chan(chan);
|
||||
// }
|
||||
//
|
||||
// return 0;
|
||||
//}
|
61
src/emu/bay.h
Normal file
61
src/emu/bay.h
Normal file
@ -0,0 +1,61 @@
|
||||
#ifndef BAY_H
|
||||
#define BAY_H
|
||||
|
||||
#include "chan.h"
|
||||
#include "uthash.h"
|
||||
|
||||
/* Handle connections between channels and callbacks */
|
||||
|
||||
struct bay;
|
||||
struct bay_cb;
|
||||
struct bay_chan;
|
||||
|
||||
typedef int (*bay_cb_func_t)(struct chan *chan, void *ptr);
|
||||
|
||||
struct bay_cb {
|
||||
bay_cb_func_t func;
|
||||
void *arg;
|
||||
|
||||
/* List of callbacks in one channel */
|
||||
struct bay_cb *next;
|
||||
struct bay_cb *prev;
|
||||
};
|
||||
|
||||
#define MAX_BAY_NAME 1024
|
||||
|
||||
struct bay_chan {
|
||||
struct chan *chan;
|
||||
int ncallbacks;
|
||||
struct bay_cb *cb;
|
||||
struct bay *bay;
|
||||
|
||||
/* Global hash table with all channels in bay */
|
||||
UT_hash_handle hh;
|
||||
|
||||
/* Used by dirty list */
|
||||
struct bay_chan *next;
|
||||
struct bay_chan *prev;
|
||||
};
|
||||
|
||||
enum bay_state {
|
||||
BAY_CONFIG = 1,
|
||||
BAY_READY,
|
||||
BAY_PROPAGATING,
|
||||
BAY_PROPAGATED,
|
||||
BAY_EMITTING,
|
||||
BAY_EMITTED
|
||||
};
|
||||
|
||||
struct bay {
|
||||
enum bay_state state;
|
||||
struct bay_chan *channels;
|
||||
struct bay_chan *dirty;
|
||||
};
|
||||
|
||||
int bay_register(struct bay *bay, struct chan *chan);
|
||||
struct chan *bay_find(struct bay *bay, const char *name);
|
||||
int bay_add_cb(struct bay *bay, struct chan *chan, bay_cb_func_t func, void *arg);
|
||||
void bay_init(struct bay *bay);
|
||||
int bay_propagate(struct bay *bay);
|
||||
|
||||
#endif /* BAY_H */
|
506
src/emu/chan.c
506
src/emu/chan.c
@ -1,402 +1,216 @@
|
||||
/* Copyright (c) 2021 Barcelona Supercomputing Center (BSC)
|
||||
/* Copyright (c) 2021-2022 Barcelona Supercomputing Center (BSC)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#define ENABLE_DEBUG
|
||||
|
||||
#include "chan.h"
|
||||
#include "common.h"
|
||||
#include <string.h>
|
||||
|
||||
#include "emu.h"
|
||||
#include "prv.h"
|
||||
#include "utlist.h"
|
||||
|
||||
static void
|
||||
chan_init(struct ovni_chan *chan, enum chan_track track, int row, int type, FILE *prv, int64_t *clock)
|
||||
void
|
||||
chan_init(struct chan *chan, enum chan_type type, const char *name)
|
||||
{
|
||||
chan->n = 0;
|
||||
int len = strlen(name);
|
||||
if (len >= MAX_CHAN_NAME)
|
||||
die("chan_init: name '%s' too long\n", name);
|
||||
|
||||
memset(chan, 0, sizeof(*chan));
|
||||
memcpy(chan->name, name, len + 1);
|
||||
chan->type = type;
|
||||
chan->enabled = 0;
|
||||
chan->badst = ST_NULL;
|
||||
chan->ev = -1;
|
||||
chan->prv = prv;
|
||||
chan->clock = clock;
|
||||
chan->t = *clock;
|
||||
chan->row = row;
|
||||
chan->dirty = 0;
|
||||
chan->track = track;
|
||||
chan->lastst = -1;
|
||||
}
|
||||
|
||||
static void
|
||||
mark_dirty(struct ovni_chan *chan, enum chan_dirty dirty)
|
||||
{
|
||||
if (chan->dirty != CHAN_CLEAN)
|
||||
die("mark_dirty: chan %d already dirty\n", chan->id);
|
||||
|
||||
if (dirty == CHAN_CLEAN)
|
||||
die("mark_dirty: cannot use CHAN_CLEAN\n");
|
||||
|
||||
dbg("adding dirty chan %d to list\n", chan->id);
|
||||
chan->dirty = dirty;
|
||||
DL_APPEND(*chan->update_list, chan);
|
||||
}
|
||||
|
||||
void
|
||||
chan_th_init(struct ovni_ethread *th,
|
||||
struct ovni_chan **update_list,
|
||||
enum chan id,
|
||||
enum chan_track track,
|
||||
int init_st,
|
||||
int enabled,
|
||||
int dirty,
|
||||
int row,
|
||||
FILE *prv,
|
||||
int64_t *clock)
|
||||
chan_set_dirty_cb(struct chan *chan, chan_cb_t func, void *arg)
|
||||
{
|
||||
struct ovni_chan *chan = &th->chan[id];
|
||||
int prvth = chan_to_prvtype[id];
|
||||
|
||||
chan_init(chan, track, row, prvth, prv, clock);
|
||||
|
||||
chan->id = id;
|
||||
chan->thread = th;
|
||||
chan->update_list = update_list;
|
||||
chan->enabled = enabled;
|
||||
chan->stack[chan->n++] = init_st;
|
||||
if (dirty)
|
||||
mark_dirty(chan, CHAN_DIRTY_ACTIVE);
|
||||
chan->dirty_cb = func;
|
||||
chan->dirty_arg = arg;
|
||||
}
|
||||
|
||||
void
|
||||
chan_cpu_init(struct ovni_cpu *cpu,
|
||||
struct ovni_chan **update_list,
|
||||
enum chan id,
|
||||
enum chan_track track,
|
||||
int init_st,
|
||||
int enabled,
|
||||
int dirty,
|
||||
int row,
|
||||
FILE *prv,
|
||||
int64_t *clock)
|
||||
enum chan_type
|
||||
chan_get_type(struct chan *chan)
|
||||
{
|
||||
struct ovni_chan *chan = &cpu->chan[id];
|
||||
int prvcpu = chan_to_prvtype[id];
|
||||
|
||||
chan_init(chan, track, row, prvcpu, prv, clock);
|
||||
|
||||
chan->id = id;
|
||||
chan->cpu = cpu;
|
||||
chan->update_list = update_list;
|
||||
chan->enabled = enabled;
|
||||
chan->stack[chan->n++] = init_st;
|
||||
if (dirty)
|
||||
mark_dirty(chan, CHAN_DIRTY_ACTIVE);
|
||||
return chan->type;
|
||||
}
|
||||
|
||||
static void
|
||||
chan_dump_update_list(struct ovni_chan *chan)
|
||||
static int
|
||||
set_dirty(struct chan *chan)
|
||||
{
|
||||
dbg("update list for chan %d at %p:\n", chan->id, (void *) chan);
|
||||
|
||||
for (struct ovni_chan *c = *chan->update_list; c; c = c->next) {
|
||||
dbg(" chan %d at %p\n", c->id, (void *) c);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
chan_enable(struct ovni_chan *chan, int enabled)
|
||||
{
|
||||
/* Can be dirty */
|
||||
|
||||
dbg("chan_enable chan=%d enabled=%d\n", chan->id, enabled);
|
||||
|
||||
if (chan->enabled == enabled) {
|
||||
err("chan_enable: chan already in enabled=%d\n", enabled);
|
||||
abort();
|
||||
if (chan->is_dirty) {
|
||||
err("channel %s already dirty\n", chan->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
chan->enabled = enabled;
|
||||
chan->t = *chan->clock;
|
||||
chan->is_dirty = 1;
|
||||
|
||||
/* Only append if not dirty */
|
||||
if (!chan->dirty) {
|
||||
mark_dirty(chan, CHAN_DIRTY_ACTIVE);
|
||||
} else {
|
||||
dbg("already dirty chan %d: skip update list\n",
|
||||
chan->id);
|
||||
chan_dump_update_list(chan);
|
||||
if (chan->dirty_cb != NULL) {
|
||||
if (chan->dirty_cb(chan, chan->dirty_arg) != 0) {
|
||||
err("dirty callback failed\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
chan_disable(struct ovni_chan *chan)
|
||||
static int
|
||||
check_duplicates(struct chan *chan, struct value *v)
|
||||
{
|
||||
chan_enable(chan, 0);
|
||||
/* If duplicates are allowed just skip the check */
|
||||
//if (chan->oflags & CHAN_DUP)
|
||||
// return 0;
|
||||
|
||||
if (value_is_equal(&chan->last_value, v)) {
|
||||
err("check_duplicates: same value as last_value\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
chan_is_enabled(const struct ovni_chan *chan)
|
||||
chan_set(struct chan *chan, struct value value)
|
||||
{
|
||||
return chan->enabled;
|
||||
if (chan->type != CHAN_SINGLE) {
|
||||
err("chan_set: cannot set on non-single channel\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (chan->is_dirty) {
|
||||
err("chan_set: cannot modify dirty channel\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (check_duplicates(chan, &value) != 0) {
|
||||
err("chan_set: cannot set a duplicated value\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
char buf[128];
|
||||
dbg("chan_set: channel %p sets value %s\n", (void *) chan,
|
||||
value_str(value, buf));
|
||||
chan->data.value = value;
|
||||
|
||||
if (set_dirty(chan) != 0) {
|
||||
err("chan_set: set_dirty failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
chan_set(struct ovni_chan *chan, int st)
|
||||
/** Adds one value to the stack. Fails if the stack is full.
|
||||
*
|
||||
* @param ivalue The new integer value to be added on the stack.
|
||||
*
|
||||
* @return On success returns 0, otherwise returns -1.
|
||||
*/
|
||||
int
|
||||
chan_push(struct chan *chan, struct value value)
|
||||
{
|
||||
/* Can be dirty */
|
||||
|
||||
dbg("chan_set chan %d st=%d\n", chan->id, st);
|
||||
|
||||
if (!chan->enabled)
|
||||
die("chan_set: chan %d not enabled\n", chan->id);
|
||||
|
||||
/* Only chan_set can set the 0 state */
|
||||
if (st < 0)
|
||||
die("chan_set: cannot set a negative state %d\n", st);
|
||||
|
||||
/* Don't enforce this check if we are dirty because the channel was
|
||||
* just enabled; it may collide with a new state 0 set via chan_set()
|
||||
* used by the tracking channels */
|
||||
if (chan->dirty != CHAN_DIRTY_ACTIVE
|
||||
&& chan->lastst >= 0
|
||||
&& chan->lastst == st) {
|
||||
err("chan_set id=%d cannot emit the state %d twice\n",
|
||||
chan->id, st);
|
||||
abort();
|
||||
if (chan->type != CHAN_STACK) {
|
||||
err("chan_push: cannot push on non-stack channel\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (chan->n == 0)
|
||||
chan->n = 1;
|
||||
|
||||
chan->stack[chan->n - 1] = st;
|
||||
chan->t = *chan->clock;
|
||||
|
||||
/* Only append if not dirty */
|
||||
if (!chan->dirty) {
|
||||
mark_dirty(chan, CHAN_DIRTY_VALUE);
|
||||
} else {
|
||||
dbg("already dirty chan %d: skip update list\n",
|
||||
chan->id);
|
||||
chan_dump_update_list(chan);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
chan_push(struct ovni_chan *chan, int st)
|
||||
{
|
||||
dbg("chan_push chan %d st=%d\n", chan->id, st);
|
||||
|
||||
if (!chan->enabled)
|
||||
die("chan_push: chan %d not enabled\n", chan->id);
|
||||
|
||||
if (st <= 0) {
|
||||
err("chan_push: cannot push a non-positive state %d\n", st);
|
||||
abort();
|
||||
if (chan->is_dirty) {
|
||||
err("chan_push: cannot modify dirty channel\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Cannot be dirty */
|
||||
if (chan->dirty != CHAN_CLEAN)
|
||||
die("chan_push: chan %d not clean", chan->id);
|
||||
|
||||
if (chan->lastst >= 0 && chan->lastst == st) {
|
||||
err("chan_push id=%d cannot emit the state %d twice\n",
|
||||
chan->id, st);
|
||||
abort();
|
||||
if (check_duplicates(chan, &value) != 0) {
|
||||
err("chan_push: cannot push a duplicated value\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (chan->n >= MAX_CHAN_STACK) {
|
||||
struct chan_stack *stack = &chan->data.stack;
|
||||
|
||||
if (stack->n >= MAX_CHAN_STACK) {
|
||||
err("chan_push: channel stack full\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
chan->stack[chan->n++] = st;
|
||||
chan->t = *chan->clock;
|
||||
stack->values[stack->n++] = value;
|
||||
|
||||
mark_dirty(chan, CHAN_DIRTY_VALUE);
|
||||
}
|
||||
|
||||
int
|
||||
chan_pop(struct ovni_chan *chan, int expected_st)
|
||||
{
|
||||
dbg("chan_pop chan %d expected_st=%d\n", chan->id, expected_st);
|
||||
|
||||
if (!chan->enabled)
|
||||
die("chan_pop: chan %d not enabled\n", chan->id);
|
||||
|
||||
/* Cannot be dirty */
|
||||
if (chan->dirty != CHAN_CLEAN)
|
||||
die("chan_pop: chan %d not clean", chan->id);
|
||||
|
||||
if (chan->n <= 0) {
|
||||
err("chan_pop: channel empty\n");
|
||||
abort();
|
||||
if (set_dirty(chan) != 0) {
|
||||
err("chan_set: set_dirty failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int st = chan->stack[chan->n - 1];
|
||||
|
||||
if (expected_st >= 0 && st != expected_st) {
|
||||
err("chan_pop: unexpected channel state %d (expected %d)\n",
|
||||
st, expected_st);
|
||||
abort();
|
||||
}
|
||||
|
||||
chan->n--;
|
||||
|
||||
/* Take the current stack value */
|
||||
st = chan_get_st(chan);
|
||||
|
||||
/* A st == 0 can be obtained when returning to the initial state */
|
||||
if (st < 0) {
|
||||
err("chan_pop: obtained negative state %d from the stack\n", st);
|
||||
abort();
|
||||
}
|
||||
|
||||
if (chan->lastst >= 0 && chan->lastst == st) {
|
||||
err("chan_pop id=%d cannot emit the state %d twice\n",
|
||||
chan->id, st);
|
||||
abort();
|
||||
}
|
||||
|
||||
chan->t = *chan->clock;
|
||||
|
||||
mark_dirty(chan, CHAN_DIRTY_VALUE);
|
||||
|
||||
return st;
|
||||
}
|
||||
|
||||
void
|
||||
chan_ev(struct ovni_chan *chan, int ev)
|
||||
{
|
||||
dbg("chan_ev chan %d ev=%d\n", chan->id, ev);
|
||||
|
||||
if (!chan->enabled)
|
||||
die("chan_ev: chan %d not enabled\n", chan->id);
|
||||
|
||||
/* Cannot be dirty */
|
||||
if (chan->dirty != CHAN_CLEAN)
|
||||
die("chan_ev: chan %d is dirty\n", chan->id);
|
||||
|
||||
if (ev <= 0)
|
||||
die("chan_ev: cannot emit non-positive state %d\n", ev);
|
||||
|
||||
if (chan->lastst >= 0 && chan->lastst == ev)
|
||||
die("chan_ev id=%d cannot emit the state %d twice\n",
|
||||
chan->id, ev);
|
||||
|
||||
chan->ev = ev;
|
||||
chan->t = *chan->clock;
|
||||
|
||||
mark_dirty(chan, CHAN_DIRTY_VALUE);
|
||||
}
|
||||
|
||||
int
|
||||
chan_get_st(const struct ovni_chan *chan)
|
||||
{
|
||||
if (chan->enabled == 0)
|
||||
return chan->badst;
|
||||
|
||||
if (chan->n == 0)
|
||||
return 0;
|
||||
|
||||
if (chan->n < 0)
|
||||
die("chan_get_st: chan %d has negative n\n", chan->id);
|
||||
|
||||
return chan->stack[chan->n - 1];
|
||||
}
|
||||
|
||||
void
|
||||
chan_copy(struct ovni_chan *dst, const struct ovni_chan *src)
|
||||
/** Remove one value from the stack. Fails if the top of the stack
|
||||
* doesn't match the expected value.
|
||||
*
|
||||
* @param expected The expected value on the top of the stack.
|
||||
*
|
||||
* @return On success returns 0, otherwise returns -1.
|
||||
*/
|
||||
int
|
||||
chan_pop(struct chan *chan, struct value evalue)
|
||||
{
|
||||
if (!chan_is_enabled(src)) {
|
||||
chan_disable(dst);
|
||||
return;
|
||||
if (chan->type != CHAN_STACK) {
|
||||
err("chan_pop: cannot pop on non-stack channel\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!chan_is_enabled(dst))
|
||||
chan_enable(dst, 1);
|
||||
|
||||
if (src->ev != -1) {
|
||||
chan_ev(dst, src->ev);
|
||||
} else {
|
||||
int src_st = chan_get_st(src);
|
||||
int dst_st = chan_get_st(dst);
|
||||
if (src_st != dst_st)
|
||||
chan_set(dst, src_st);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
emit(struct ovni_chan *chan, int64_t t, int state)
|
||||
{
|
||||
if (chan->dirty == CHAN_CLEAN)
|
||||
die("emit: chan %d is not dirty\n", chan->id);
|
||||
|
||||
/* A channel can only emit the same state as lastst if is dirty because
|
||||
* it has been enabled or disabled. Otherwise is a bug (ie. you have two
|
||||
* consecutive ovni events?) */
|
||||
if (chan->lastst != -1
|
||||
&& chan->dirty == CHAN_DIRTY_VALUE
|
||||
&& chan->lastst == state) {
|
||||
/* TODO: Print the raw clock of the offending event */
|
||||
die("emit: chan %d cannot emit the same state %d twice\n",
|
||||
chan->id, state);
|
||||
if (chan->is_dirty) {
|
||||
err("chan_pop: cannot modify dirty channel\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (chan->lastst != state) {
|
||||
prv_ev(chan->prv, chan->row, t, chan->type, state);
|
||||
struct chan_stack *stack = &chan->data.stack;
|
||||
|
||||
chan->lastst = state;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
emit_ev(struct ovni_chan *chan)
|
||||
{
|
||||
if (!chan->enabled)
|
||||
die("emit_ev: chan %d is not enabled\n", chan->id);
|
||||
|
||||
if (chan->ev == -1)
|
||||
die("emit_ev: chan %d cannot emit -1 ev\n", chan->id);
|
||||
|
||||
int new = chan->ev;
|
||||
int last = chan_get_st(chan);
|
||||
|
||||
emit(chan, chan->t - 1, new);
|
||||
emit(chan, chan->t, last);
|
||||
|
||||
chan->ev = -1;
|
||||
}
|
||||
|
||||
static void
|
||||
emit_st(struct ovni_chan *chan)
|
||||
{
|
||||
int st = chan_get_st(chan);
|
||||
|
||||
emit(chan, chan->t, st);
|
||||
}
|
||||
|
||||
/* Emits either the current state or punctual event in the PRV file */
|
||||
void
|
||||
chan_emit(struct ovni_chan *chan)
|
||||
{
|
||||
if (likely(chan->dirty == 0))
|
||||
return;
|
||||
|
||||
dbg("chan_emit chan %d\n", chan->id);
|
||||
|
||||
/* Emit badst if disabled */
|
||||
if (chan->enabled == 0) {
|
||||
/* No punctual events allowed when disabled */
|
||||
if (chan->ev != -1)
|
||||
die("chan_emit: no punctual event allowed when disabled\n");
|
||||
|
||||
emit_st(chan);
|
||||
goto shower;
|
||||
if (stack->n <= 0) {
|
||||
err("chan_pop: channel stack empty\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Otherwise, emit punctual event if any or the state */
|
||||
if (chan->ev != -1)
|
||||
emit_ev(chan);
|
||||
else
|
||||
emit_st(chan);
|
||||
struct value *value = &stack->values[stack->n - 1];
|
||||
|
||||
shower:
|
||||
chan->dirty = 0;
|
||||
if (!value_is_equal(value, &evalue)) {
|
||||
err("chan_pop: unexpected value %ld (expected %ld)\n",
|
||||
value->i, evalue.i);
|
||||
return -1;
|
||||
}
|
||||
|
||||
stack->n--;
|
||||
|
||||
if (set_dirty(chan) != 0) {
|
||||
err("chan_set: set_dirty failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
get_value(struct chan *chan, struct value *value)
|
||||
{
|
||||
if (chan->type == CHAN_SINGLE) {
|
||||
*value = chan->data.value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct chan_stack *stack = &chan->data.stack;
|
||||
if (stack->n <= 0) {
|
||||
err("get_value: channel stack empty\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
*value = stack->values[stack->n - 1];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Reads the current value of a channel */
|
||||
int
|
||||
chan_read(struct chan *chan, struct value *value)
|
||||
{
|
||||
if (get_value(chan, value) != 0) {
|
||||
err("chan_read: get_value failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,63 +1,58 @@
|
||||
/* Copyright (c) 2021 Barcelona Supercomputing Center (BSC)
|
||||
/* Copyright (c) 2021-2022 Barcelona Supercomputing Center (BSC)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#ifndef OVNI_CHAN_H
|
||||
#define OVNI_CHAN_H
|
||||
#ifndef CHAN_H
|
||||
#define CHAN_H
|
||||
|
||||
#include "emu.h"
|
||||
#include <stdint.h>
|
||||
#include "value.h"
|
||||
|
||||
void
|
||||
chan_th_init(struct ovni_ethread *th,
|
||||
struct ovni_chan **update_list,
|
||||
enum chan id,
|
||||
enum chan_track track,
|
||||
int init_st,
|
||||
int enabled,
|
||||
int dirty,
|
||||
int row,
|
||||
FILE *prv,
|
||||
int64_t *clock);
|
||||
#define MAX_CHAN_STACK 512
|
||||
#define MAX_CHAN_NAME 512
|
||||
|
||||
void
|
||||
chan_cpu_init(struct ovni_cpu *cpu,
|
||||
struct ovni_chan **update_list,
|
||||
enum chan id,
|
||||
enum chan_track track,
|
||||
int row,
|
||||
int init_st,
|
||||
int enabled,
|
||||
int dirty,
|
||||
FILE *prv,
|
||||
int64_t *clock);
|
||||
enum chan_type {
|
||||
CHAN_SINGLE = 0,
|
||||
CHAN_STACK = 1,
|
||||
CHAN_MAXTYPE,
|
||||
};
|
||||
|
||||
void
|
||||
chan_enable(struct ovni_chan *chan, int enabled);
|
||||
struct chan_stack {
|
||||
int n;
|
||||
struct value values[MAX_CHAN_STACK];
|
||||
};
|
||||
|
||||
void
|
||||
chan_disable(struct ovni_chan *chan);
|
||||
union chan_data {
|
||||
struct chan_stack stack;
|
||||
struct value value;
|
||||
};
|
||||
|
||||
int
|
||||
chan_is_enabled(const struct ovni_chan *chan);
|
||||
struct chan;
|
||||
|
||||
void
|
||||
chan_set(struct ovni_chan *chan, int st);
|
||||
typedef int (*chan_cb_t)(struct chan *chan, void *ptr);
|
||||
|
||||
void
|
||||
chan_push(struct ovni_chan *chan, int st);
|
||||
struct chan {
|
||||
char name[MAX_CHAN_NAME];
|
||||
enum chan_type type;
|
||||
union chan_data data;
|
||||
int is_dirty;
|
||||
struct value err_value;
|
||||
struct value last_value;
|
||||
chan_cb_t dirty_cb;
|
||||
void *dirty_arg;
|
||||
};
|
||||
|
||||
int
|
||||
chan_pop(struct ovni_chan *chan, int expected_st);
|
||||
//int chan_enable(struct chan *chan);
|
||||
//int chan_disable(struct chan *chan);
|
||||
//int chan_is_enabled(const struct chan *chan);
|
||||
|
||||
void
|
||||
chan_ev(struct ovni_chan *chan, int ev);
|
||||
void chan_init(struct chan *chan, enum chan_type type, const char *name);
|
||||
int chan_set(struct chan *chan, struct value value);
|
||||
int chan_push(struct chan *chan, struct value value);
|
||||
int chan_pop(struct chan *chan, struct value expected);
|
||||
int chan_read(struct chan *chan, struct value *value);
|
||||
enum chan_type chan_get_type(struct chan *chan);
|
||||
|
||||
int
|
||||
chan_get_st(const struct ovni_chan *chan);
|
||||
/* Called when it becomes dirty */
|
||||
void chan_set_dirty_cb(struct chan *chan, chan_cb_t func, void *arg);
|
||||
|
||||
void
|
||||
chan_emit(struct ovni_chan *chan);
|
||||
|
||||
void
|
||||
chan_copy(struct ovni_chan *dst, const struct ovni_chan *src);
|
||||
|
||||
#endif /* OVNI_CHAN_H */
|
||||
#endif /* CHAN_H */
|
||||
|
196
src/emu/mux.c
Normal file
196
src/emu/mux.c
Normal file
@ -0,0 +1,196 @@
|
||||
#define ENABLE_DEBUG
|
||||
|
||||
#include "mux.h"
|
||||
|
||||
static int
|
||||
default_select(struct mux *mux,
|
||||
struct value key,
|
||||
struct mux_input **input)
|
||||
{
|
||||
if (value_is_null(key)) {
|
||||
*input = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct mux_input *tmp = mux_find_input(mux, key);
|
||||
|
||||
if (tmp == NULL) {
|
||||
char buf[128];
|
||||
err("default_select: cannot find input with key %s\n",
|
||||
value_str(key, buf));
|
||||
return -1;
|
||||
}
|
||||
|
||||
*input = tmp;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
select_input(struct mux *mux,
|
||||
struct value key,
|
||||
struct mux_input **input)
|
||||
{
|
||||
if (mux->select_func == NULL)
|
||||
die("select_input: select_func is NULL\n");
|
||||
|
||||
if (mux->select_func(mux, key, input) != 0) {
|
||||
err("select_input: select_func failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Called when the select channel changes its value */
|
||||
static int
|
||||
cb_select(struct chan *sel_chan, void *ptr)
|
||||
{
|
||||
struct mux *mux = ptr;
|
||||
|
||||
struct value sel_value;
|
||||
if (chan_read(sel_chan, &sel_value) != 0) {
|
||||
err("cb_select: chan_read(select) failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
char buf[128];
|
||||
dbg("select channel got value %s\n",
|
||||
value_str(sel_value, buf));
|
||||
|
||||
struct mux_input *input = NULL;
|
||||
if (select_input(mux, sel_value, &input) != 0) {
|
||||
err("cb_select: select_input failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
dbg("selecting mux input %p\n", (void *) input);
|
||||
|
||||
/* Set to null by default */
|
||||
struct value out_value = value_null();
|
||||
if (input != NULL && chan_read(input->chan, &out_value) != 0) {
|
||||
err("cb_select: chan_read() failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (chan_set(mux->output, out_value) != 0) {
|
||||
err("cb_select: chan_set() failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Called when the input channel changes its value */
|
||||
static int
|
||||
cb_input(struct chan *in_chan, void *ptr)
|
||||
{
|
||||
struct mux *mux = ptr;
|
||||
|
||||
/* TODO: We may need to cache the last read value from select to avoid
|
||||
* problems reading the select channel too soon */
|
||||
struct value sel_value;
|
||||
if (chan_read(mux->select, &sel_value) != 0) {
|
||||
err("cb_input: chan_read(select) failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct mux_input *input = NULL;
|
||||
if (select_input(mux, sel_value, &input) != 0) {
|
||||
err("cb_input: select_input failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Nothing to do, the input is not selected */
|
||||
if (input == NULL || input->chan != in_chan)
|
||||
return 0;
|
||||
|
||||
dbg("selected mux input %s changed\n", in_chan->name);
|
||||
|
||||
/* Set to null by default */
|
||||
struct value out_value = value_null();
|
||||
|
||||
if (chan_read(in_chan, &out_value) != 0) {
|
||||
err("cb_input: chan_read() failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (chan_set(mux->output, out_value) != 0) {
|
||||
err("cb_input: chan_set() failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
mux_init(struct mux *mux,
|
||||
struct bay *bay,
|
||||
struct chan *select,
|
||||
struct chan *output,
|
||||
mux_select_func_t select_func)
|
||||
{
|
||||
if (chan_get_type(output) != CHAN_SINGLE) {
|
||||
err("mux_init: output channel must be of type single\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(mux, 0, sizeof(struct mux_input));
|
||||
mux->select = select;
|
||||
mux->output = output;
|
||||
|
||||
if (select_func == NULL)
|
||||
select_func = default_select;
|
||||
|
||||
mux->select_func = select_func;
|
||||
mux->bay = bay;
|
||||
|
||||
if (bay_add_cb(bay, select, cb_select, mux) != 0) {
|
||||
err("mux_init: bay_add_cb failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct mux_input *
|
||||
mux_find_input(struct mux *mux, struct value value)
|
||||
{
|
||||
struct mux_input *input = NULL;
|
||||
/* Only int64 due to garbage */
|
||||
if (value.type != VALUE_INT64)
|
||||
die("bad value type\n");
|
||||
|
||||
HASH_FIND(hh, mux->input, &value.i, sizeof(value.i), input);
|
||||
return input;
|
||||
}
|
||||
|
||||
int
|
||||
mux_add_input(struct mux *mux, struct value key, struct chan *chan)
|
||||
{
|
||||
if (mux_find_input(mux, key) != NULL) {
|
||||
char buf[128];
|
||||
err("mux_add_input: input key %s already in mux\n",
|
||||
value_str(key, buf));
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct mux_input *input = calloc(1, sizeof(struct mux_input));
|
||||
|
||||
if (input == NULL) {
|
||||
err("mux_add_input: calloc failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
input->key = key;
|
||||
input->chan = chan;
|
||||
|
||||
HASH_ADD_KEYPTR(hh, mux->input, &input->key.i, sizeof(input->key.i), input);
|
||||
|
||||
if (bay_add_cb(mux->bay, chan, cb_input, mux) != 0) {
|
||||
err("mux_add_input: bay_add_cb failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
58
src/emu/mux.h
Normal file
58
src/emu/mux.h
Normal file
@ -0,0 +1,58 @@
|
||||
#ifndef MUX_H
|
||||
#define MUX_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "bay.h"
|
||||
#include "uthash.h"
|
||||
|
||||
struct mux_input {
|
||||
struct value key;
|
||||
struct chan *chan;
|
||||
UT_hash_handle hh;
|
||||
};
|
||||
|
||||
struct mux;
|
||||
|
||||
typedef int (* mux_select_func_t)(struct mux *mux,
|
||||
struct value value,
|
||||
struct mux_input **input);
|
||||
|
||||
/* MUX logic:
|
||||
*
|
||||
* select input output
|
||||
* ---------------------------
|
||||
* null - null
|
||||
* input x x
|
||||
*
|
||||
*/
|
||||
|
||||
struct mux {
|
||||
struct bay *bay;
|
||||
int64_t ninputs;
|
||||
struct mux_input *input;
|
||||
mux_select_func_t select_func;
|
||||
struct chan *select;
|
||||
struct chan *output;
|
||||
};
|
||||
|
||||
void mux_input_init(struct mux_input *mux,
|
||||
struct value key,
|
||||
struct chan *chan);
|
||||
|
||||
int mux_init(struct mux *mux,
|
||||
struct bay *bay,
|
||||
struct chan *select,
|
||||
struct chan *output,
|
||||
mux_select_func_t select_func);
|
||||
|
||||
struct mux_input *mux_find_input(struct mux *mux,
|
||||
struct value key);
|
||||
|
||||
int mux_add_input(struct mux *mux,
|
||||
struct value key,
|
||||
struct chan *input);
|
||||
|
||||
int mux_register(struct mux *mux,
|
||||
struct bay *bay);
|
||||
|
||||
#endif /* MUX_H */
|
77
src/emu/value.h
Normal file
77
src/emu/value.h
Normal file
@ -0,0 +1,77 @@
|
||||
#ifndef VALUE_H
|
||||
#define VALUE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "common.h"
|
||||
|
||||
enum value_type {
|
||||
VALUE_NULL = 0,
|
||||
VALUE_INT64,
|
||||
VALUE_DOUBLE
|
||||
};
|
||||
|
||||
struct value {
|
||||
enum value_type type;
|
||||
union {
|
||||
int64_t i;
|
||||
double d;
|
||||
};
|
||||
};
|
||||
|
||||
static inline int
|
||||
value_is_equal(struct value *a, struct value *b)
|
||||
{
|
||||
if (a->type != b->type)
|
||||
return 0;
|
||||
|
||||
if (a->type == VALUE_INT64 && a->i == b->i)
|
||||
return 1;
|
||||
else if (a->type == VALUE_DOUBLE && a->d == b->d)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline struct value
|
||||
value_int64(int64_t i)
|
||||
{
|
||||
struct value v = { .type = VALUE_INT64, .i = i };
|
||||
return v;
|
||||
}
|
||||
|
||||
static inline struct value
|
||||
value_null(void)
|
||||
{
|
||||
struct value v = { .type = VALUE_NULL };
|
||||
return v;
|
||||
}
|
||||
|
||||
static inline int
|
||||
value_is_null(struct value a)
|
||||
{
|
||||
return (a.type == VALUE_NULL);
|
||||
}
|
||||
|
||||
static inline char *
|
||||
value_str(struct value a, char *buf)
|
||||
{
|
||||
switch (a.type) {
|
||||
case VALUE_NULL:
|
||||
sprintf(buf, "{NULL}");
|
||||
break;
|
||||
case VALUE_INT64:
|
||||
sprintf(buf, "{int64_t %ld}", a.i);
|
||||
break;
|
||||
case VALUE_DOUBLE:
|
||||
sprintf(buf, "{double %e}", a.d);
|
||||
break;
|
||||
default:
|
||||
die("value_str: unexpected value type\n");
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
#endif /* VALUE_H */
|
@ -7,5 +7,5 @@ set(OVNI_TEST_BUILD_DIR "${CMAKE_BINARY_DIR}/test")
|
||||
include(macros.cmake)
|
||||
|
||||
add_subdirectory(unit)
|
||||
add_subdirectory(emu)
|
||||
add_subdirectory(rt)
|
||||
#add_subdirectory(emu)
|
||||
#add_subdirectory(rt)
|
||||
|
@ -35,7 +35,7 @@ function(unit_test source)
|
||||
"${CMAKE_SOURCE_DIR}/include"
|
||||
)
|
||||
add_executable("${OVNI_TEST_NAME}" "${OVNI_TEST_SOURCE}")
|
||||
target_link_libraries("${OVNI_TEST_NAME}" PRIVATE ovni emu)
|
||||
target_link_libraries("${OVNI_TEST_NAME}" PRIVATE ovni chan)
|
||||
|
||||
add_test(NAME "${OVNI_TEST_NAME}"
|
||||
COMMAND "${OVNI_TEST_NAME}"
|
||||
|
@ -1,4 +1,13 @@
|
||||
# Copyright (c) 2022 Barcelona Supercomputing Center (BSC)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
unit_test(version.c)
|
||||
#unit_test(version.c)
|
||||
#unit_test(task.c)
|
||||
#unit_test(taskstack.c)
|
||||
#unit_test(taskstack-bad.c)
|
||||
#unit_test(taskmodel.c)
|
||||
#unit_test(tbm.c)
|
||||
#unit_test(tbm_trace.c)
|
||||
unit_test(chan.c)
|
||||
unit_test(bay.c)
|
||||
unit_test(mux.c)
|
||||
|
51
test/unit/bay.c
Normal file
51
test/unit/bay.c
Normal file
@ -0,0 +1,51 @@
|
||||
#include "emu/bay.h"
|
||||
#include "common.h"
|
||||
|
||||
static int
|
||||
callback(struct chan *chan, void *ptr)
|
||||
{
|
||||
struct value value;
|
||||
if (chan_read(chan, &value) != 0)
|
||||
die("callback: chan_read failed\n");
|
||||
|
||||
if (value.type != VALUE_INT64)
|
||||
die("callback: unexpected value type\n");
|
||||
|
||||
int64_t *ival = ptr;
|
||||
*ival = value.i;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
struct bay bay;
|
||||
bay_init(&bay);
|
||||
|
||||
struct chan chan;
|
||||
chan_init(&chan, CHAN_SINGLE, "testchan");
|
||||
|
||||
bay_register(&bay, &chan);
|
||||
|
||||
int64_t data = 0;
|
||||
bay_add_cb(&bay, &chan, callback, &data);
|
||||
|
||||
if (data != 0)
|
||||
die("data changed after bay_chan_append_cb\n");
|
||||
|
||||
struct value one = value_int64(1);
|
||||
if (chan_set(&chan, one) != 0)
|
||||
die("chan_set failed\n");
|
||||
|
||||
if (data != 0)
|
||||
die("data changed after chan_set\n");
|
||||
|
||||
/* Now the callback should modify 'data' */
|
||||
if (bay_propagate(&bay) != 0)
|
||||
die("bay_propagate failed\n");
|
||||
|
||||
if (data != 1)
|
||||
die("data didn't change after bay_propagate\n");
|
||||
|
||||
return 0;
|
||||
}
|
97
test/unit/chan.c
Normal file
97
test/unit/chan.c
Normal file
@ -0,0 +1,97 @@
|
||||
#include "emu/chan.h"
|
||||
#include "common.h"
|
||||
|
||||
static void
|
||||
check_single(void)
|
||||
{
|
||||
struct chan chan;
|
||||
struct value one = { .type = VALUE_INT64, .i = 1 };
|
||||
struct value two = { .type = VALUE_INT64, .i = 2 };
|
||||
//struct value nil = { .type = VALUE_NULL, .i = 0 };
|
||||
|
||||
chan_init(&chan, CHAN_SINGLE, "testchan");
|
||||
|
||||
/* Ensure we cannot push as stack */
|
||||
if (chan_push(&chan, one) == 0)
|
||||
die("chan_push didn't fail\n");
|
||||
|
||||
/* Now we should be able to write with set */
|
||||
if (chan_set(&chan, one) != 0)
|
||||
die("chan_set failed\n");
|
||||
|
||||
/* Now is dirty, it shouldn't allow another set */
|
||||
if (chan_set(&chan, two) == 0)
|
||||
die("chan_set didn't fail\n");
|
||||
|
||||
struct value value;
|
||||
|
||||
if (chan_read(&chan, &value) != 0)
|
||||
die("chan_read failed\n");
|
||||
|
||||
if (!value_is_equal(&value, &one))
|
||||
die("chan_read returned unexpected value\n");
|
||||
}
|
||||
|
||||
//static void
|
||||
//check_stack(void)
|
||||
//{
|
||||
// struct chan chan;
|
||||
//
|
||||
// chan_init(&chan, CHAN_STACK);
|
||||
//
|
||||
// /* Ensure we cannot set as single */
|
||||
// if (chan_set(&chan, 1) == 0)
|
||||
// die("chan_set didn't fail\n");
|
||||
//
|
||||
// /* Channels are closed after init */
|
||||
// if (chan_push(&chan, 1) != 0)
|
||||
// die("chan_push failed\n");
|
||||
//
|
||||
// /* Now is closed, it shouldn't allow another value */
|
||||
// if (chan_push(&chan, 2) == 0)
|
||||
// die("chan_push didn't fail\n");
|
||||
//
|
||||
// struct chan_value value = { 0 };
|
||||
//
|
||||
// if (chan_flush(&chan, &value) != 0)
|
||||
// die("chan_flush failed\n");
|
||||
//
|
||||
// if (!value.ok || value.i != 1)
|
||||
// die("chan_flush returned unexpected value\n");
|
||||
//
|
||||
// /* Now it should allow to push another value */
|
||||
// if (chan_push(&chan, 2) != 0)
|
||||
// die("chan_push failed\n");
|
||||
//
|
||||
// if (chan_flush(&chan, &value) != 0)
|
||||
// die("chan_flush failed\n");
|
||||
//
|
||||
// if (!value.ok || value.i != 2)
|
||||
// die("chan_flush returned unexpected value\n");
|
||||
//
|
||||
// /* Now pop the values */
|
||||
// if (chan_pop(&chan, 2) != 0)
|
||||
// die("chan_pop failed\n");
|
||||
//
|
||||
// if (chan_flush(&chan, &value) != 0)
|
||||
// die("chan_flush failed\n");
|
||||
//
|
||||
// if (!value.ok || value.i != 1)
|
||||
// die("chan_flush returned unexpected value\n");
|
||||
//
|
||||
// if (chan_pop(&chan, 1) != 0)
|
||||
// die("chan_pop failed\n");
|
||||
//
|
||||
// /* Now the stack should be empty */
|
||||
//
|
||||
// if (chan_pop(&chan, 1) == 0)
|
||||
// die("chan_pop didn't fail\n");
|
||||
//
|
||||
//}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
check_single();
|
||||
//check_stack();
|
||||
return 0;
|
||||
}
|
109
test/unit/mux.c
Normal file
109
test/unit/mux.c
Normal file
@ -0,0 +1,109 @@
|
||||
#include "emu/mux.h"
|
||||
#include "common.h"
|
||||
|
||||
#define N 4
|
||||
|
||||
//static int
|
||||
//select_active_thread(struct mux *mux,
|
||||
// struct value *value,
|
||||
// struct mux_input **input)
|
||||
//{
|
||||
// if (value->type == VALUE_NULL) {
|
||||
// *input = NULL;
|
||||
// return 0;
|
||||
// }
|
||||
//
|
||||
// if (value->type != VALUE_INT64) {
|
||||
// err("expecting NULL or INT64 channel value\n");
|
||||
// return -1;
|
||||
// }
|
||||
//
|
||||
// enum thread_state state = (enum thread_state) value->i;
|
||||
//
|
||||
// if (mux->ninputs != 1) {
|
||||
// err("expecting NULL or INT64 channel value\n");
|
||||
// return -1;
|
||||
// }
|
||||
//
|
||||
// switch (state) {
|
||||
// case TH_ST_RUNNING:
|
||||
// case TH_ST_COOLING:
|
||||
// case TH_ST_WARMING:
|
||||
// *input = only_input;
|
||||
// break;
|
||||
// case TH_ST_PAUSED:
|
||||
// *input = NULL;
|
||||
// break;
|
||||
// }
|
||||
//
|
||||
// return 0;
|
||||
//}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
struct bay bay;
|
||||
bay_init(&bay);
|
||||
|
||||
struct chan inputs[N];
|
||||
struct chan output;
|
||||
struct chan select;
|
||||
|
||||
chan_init(&output, CHAN_SINGLE, "output");
|
||||
chan_init(&select, CHAN_SINGLE, "select");
|
||||
|
||||
for (int i = 0; i < N; i++) {
|
||||
char buf[MAX_CHAN_NAME];
|
||||
sprintf(buf, "input.%d", i);
|
||||
chan_init(&inputs[i], CHAN_SINGLE, buf);
|
||||
}
|
||||
|
||||
/* Register all channels in the bay */
|
||||
bay_register(&bay, &output);
|
||||
bay_register(&bay, &select);
|
||||
for (int i = 0; i < N; i++) {
|
||||
bay_register(&bay, &inputs[i]);
|
||||
}
|
||||
|
||||
struct mux mux;
|
||||
mux_init(&mux, &bay, &select, &output, NULL);
|
||||
|
||||
for (int i = 0; i < N; i++)
|
||||
mux_add_input(&mux, value_int64(i), &inputs[i]);
|
||||
|
||||
/* Write something to the input channels */
|
||||
for (int i = 0; i < N; i++) {
|
||||
if (chan_set(&inputs[i], value_int64(1000 + i)) != 0)
|
||||
die("chan_set failed\n");
|
||||
}
|
||||
|
||||
/* Propagate values and call the callbacks */
|
||||
if (bay_propagate(&bay) != 0)
|
||||
die("bay_propagate failed\n");
|
||||
|
||||
|
||||
/* Change select channel */
|
||||
if (chan_set(&select, value_int64(2)) != 0)
|
||||
die("chan_set failed\n");
|
||||
|
||||
/* Propagate values and call the callbacks */
|
||||
if (bay_propagate(&bay) != 0)
|
||||
die("bay_propagate failed\n");
|
||||
|
||||
struct value out_value = value_null();
|
||||
if (chan_read(&output, &out_value) != 0)
|
||||
die("chan_read() failed for output channel\n");
|
||||
|
||||
struct value expected_value = value_int64(1002);
|
||||
if (!value_is_equal(&out_value, &expected_value)) {
|
||||
char buf1[128];
|
||||
char buf2[128];
|
||||
die("unexpected value found %s in output (expected %s)\n",
|
||||
value_str(out_value, buf1),
|
||||
value_str(expected_value, buf2));
|
||||
}
|
||||
|
||||
err("OK\n");
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user