/* Copyright (c) 2021-2023 Barcelona Supercomputing Center (BSC) * SPDX-License-Identifier: GPL-3.0-or-later */ #include "emu/mux.h" #include "common.h" #define N 10 static void check_output(struct mux *mux, struct value expected) { struct value out_value = value_null(); if (chan_read(mux->output, &out_value) != 0) die("chan_read() failed for output channel\n"); if (!value_is_equal(&out_value, &expected)) { die("unexpected value found %s in output (expected %s)\n", value_str(out_value), value_str(expected)); } err("----- output ok -----\n"); } static void test_select(struct mux *mux, int key) { if (chan_set(mux->select, value_int64(key)) != 0) die("chan_set failed\n"); if (bay_propagate(mux->bay) != 0) die("bay_propagate failed\n"); check_output(mux, value_int64(1000 + key)); } static void test_input(struct mux *mux, int key) { /* Set the select channel to the selected key */ test_select(mux, key); int new_value = 2000 + key; /* Then change that channel */ struct mux_input *mi = mux_get_input(mux, key); if (mi == NULL) die("mux_get_input failed to locate input %d\n", key); if (chan_set(mi->chan, value_int64(new_value)) != 0) die("chan_set failed\n"); if (bay_propagate(mux->bay) != 0) die("bay_propagate failed\n"); check_output(mux, value_int64(new_value)); } static void test_select_and_input(struct mux *mux, int key) { /* Set the select channel to the selected key, but don't * propagate the changes yet */ if (chan_set(mux->select, value_int64(key)) != 0) die("chan_set failed\n"); int new_value = 2000 + key; /* Also change that channel */ struct mux_input *mi = mux_get_input(mux, key); if (mi == NULL) die("mux_get_input failed to locate input %d\n", key); if (chan_set(mi->chan, value_int64(new_value)) != 0) die("chan_set failed\n"); /* Write twice to the output */ if (bay_propagate(mux->bay) != 0) die("bay_propagate failed\n"); check_output(mux, value_int64(new_value)); } static void test_input_and_select(struct mux *mux, int key) { int new_value = 2000 + key; /* First change the input */ struct mux_input *mi = mux_get_input(mux, key); if (mi == NULL) die("mux_get_input failed to locate input %d\n", key); if (chan_set(mi->chan, value_int64(new_value)) != 0) die("chan_set failed\n"); /* Then change select */ if (chan_set(mux->select, value_int64(key)) != 0) die("chan_set failed\n"); /* Write twice to the output */ if (bay_propagate(mux->bay) != 0) die("bay_propagate failed\n"); check_output(mux, value_int64(new_value)); } static void test_mid_propagate(struct mux *mux, int key) { int new_value = 2000 + key; struct mux_input *mi = mux_get_input(mux, key); if (mi == NULL) die("mux_get_input failed to locate input %d\n", key); if (chan_set(mi->chan, value_int64(new_value)) != 0) die("chan_set failed\n"); if (bay_propagate(mux->bay) != 0) die("bay_propagate failed\n"); if (chan_set(mux->select, value_int64(key)) != 0) die("chan_set failed\n"); if (bay_propagate(mux->bay) != 0) die("bay_propagate failed\n"); check_output(mux, value_int64(new_value)); } static void test_duplicate_output(struct mux *mux, int key1, int key2) { int new_value = 2000 + key1; struct mux_input *in1 = mux_get_input(mux, key1); if (in1 == NULL) die("mux_get_input failed to locate input1 %d\n", key1); struct mux_input *in2 = mux_get_input(mux, key2); if (in2 == NULL) die("mux_get_input failed to locate input2 %d\n", key2); if (chan_set(in1->chan, value_int64(new_value)) != 0) die("chan_set failed\n"); if (chan_set(in2->chan, value_int64(new_value)) != 0) die("chan_set failed\n"); /* Select input 1 */ if (chan_set(mux->select, value_int64(key1)) != 0) die("chan_set failed\n"); if (bay_propagate(mux->bay) != 0) die("bay_propagate failed\n"); check_output(mux, value_int64(new_value)); /* Now switch to input 2, which has the same value */ if (chan_set(mux->select, value_int64(key2)) != 0) die("chan_set failed\n"); if (bay_propagate(mux->bay) != 0) die("bay_propagate failed\n"); check_output(mux, value_int64(new_value)); } /* Ensure that the output of a mux is correct while the mux is connected * to the bay with a clean select channel but that already contains a * valid value of a input of the mux. The select must be marked as dirty * */ static void test_delayed_connect(void) { struct bay bay; bay_init(&bay); struct chan input, output, select; chan_init(&output, CHAN_SINGLE, "output"); chan_init(&select, CHAN_SINGLE, "select"); chan_init(&input, CHAN_SINGLE, "input.0"); /* Register all channels in the bay */ if (bay_register(&bay, &select) != 0) die("bay_register failed\n"); if (bay_register(&bay, &output) != 0) die("bay_register failed\n"); if (bay_register(&bay, &input) != 0) die("bay_register failed\n"); /* Setup channel values */ if (chan_set(&select, value_int64(0)) != 0) die("chan_set failed\n"); if (chan_set(&input, value_int64(1000)) != 0) die("chan_set failed\n"); /* Propagate now so they are clean */ if (bay_propagate(&bay) != 0) die("bay_propagate failed\n"); /* ----- delayed connect ----- */ struct mux mux; if (mux_init(&mux, &bay, &select, &output, NULL, 1) != 0) die("mux_init failed\n"); if (mux_set_input(&mux, 0, &input) != 0) die("mux_add_input failed\n"); /* Don't modify the input of the select until propagation, the * mux_init must have marked the select as dirty. */ if (bay_propagate(&bay) != 0) die("bay_propagate failed\n"); /* The mux must have selected the first input */ check_output(&mux, value_int64(1000)); } 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 */ if (bay_register(&bay, &select) != 0) die("bay_register failed\n"); for (int i = 0; i < N; i++) { if (bay_register(&bay, &inputs[i]) != 0) die("bay_register failed\n"); } struct mux mux; /* Attempt to init the mux without registering the output */ if (mux_init(&mux, &bay, &select, &output, NULL, N) == 0) die("mux_init didn't fail\n"); if (bay_register(&bay, &output) != 0) die("bay_register failed\n"); if (mux_init(&mux, &bay, &select, &output, NULL, N) != 0) die("mux_init failed\n"); for (int i = 0; i < N; i++) { if (mux_set_input(&mux, i, &inputs[i]) != 0) die("mux_add_input failed\n"); } /* 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"); test_select(&mux, 1); test_input(&mux, 2); test_select_and_input(&mux, 3); test_input_and_select(&mux, 4); test_mid_propagate(&mux, 5); test_duplicate_output(&mux, 6, 7); test_delayed_connect(); err("OK\n"); return 0; }