Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,11 @@ build-server:
$(MAKE) --no-print-directory -C $(BUILDDIR) simulith_server_standalone
# Create components directory and copy shared libraries
mkdir -p $(BUILDDIR)/components
cp $(BUILDDIR)/*.so $(BUILDDIR)/components/ 2>/dev/null || true
cp $(TOPDIR)/comp/demo/sim/build/*.so $(BUILDDIR)/components/ 2>/dev/null || true
@for dir in $(TOPDIR)/comp/*/sim/build ; do \
if [ -d "$$dir" ] && [ -f "$$dir/Makefile" ]; then \
cp $$dir/*.so $(BUILDDIR)/components/ 2>/dev/null || true; \
fi; \
done
# Copy 42 configuration to build directory for runtime
cp -r 42/InOut $(BUILDDIR)/ 2>/dev/null || true
cp -r 42/Model $(BUILDDIR)/ 2>/dev/null || true
Expand Down
94 changes: 57 additions & 37 deletions include/simulith_i2c.h
Original file line number Diff line number Diff line change
@@ -1,64 +1,84 @@
/*
* Simulith I2C - Requirements
*
* Shall utilize ZMQ to communicate between nodes
* Shall have functions to initialize, read, write, transaction (both write then read), and close
* Shall communicate directly to the other end of the node
* Shall not block on any function
* Shall fail gracefully if peer is unavailable and return error codes (non-zero) instead of crashing.
* Shall not rely on a server as each node will be initialized with its name and destination.
*/

#ifndef SIMULITH_I2C_H
#define SIMULITH_I2C_H

#include <stdint.h>
#include <stddef.h>
#include "simulith.h"

#define SIMULITH_I2C_SUCCESS 0
#define SIMULITH_I2C_ERROR -1

#define SIMULITH_I2C_INITIALIZED 255

#define SIMULITH_I2C_BASE_PORT 7000

#ifdef __cplusplus
extern "C"
{
#endif

/**
* @brief Callback function type for I2C read operations
* @param addr Device address
* @param reg Register address
* @param data Buffer to store read data
* @param len Number of bytes to read
* @return 0 on success, -1 on failure
*/
typedef int (*simulith_i2c_read_callback)(uint8_t addr, uint8_t reg, uint8_t *data, size_t len);
typedef struct {
char name[64];
char address[128];
int is_server;
void* zmq_ctx;
void* zmq_sock;
int init;
uint8_t bus_id;
uint8_t device_addr;
} i2c_device_t;

/**
* @brief Callback function type for I2C write operations
* @param addr Device address
* @param reg Register address
* @param data Data to write
* @param len Number of bytes to write
* @return 0 on success, -1 on failure
* @brief Initialize an I2C device
* @param device Pointer to I2C device structure
* @return SIMULITH_I2C_SUCCESS on success, SIMULITH_I2C_ERROR on failure
*/
typedef int (*simulith_i2c_write_callback)(uint8_t addr, uint8_t reg, const uint8_t *data, size_t len);

/**
* @brief Initialize an I2C bus
* @param bus_id Bus identifier (0-7)
* @param read_cb Callback function for read operations
* @param write_cb Callback function for write operations
* @return 0 on success, -1 on failure
*/
int simulith_i2c_init(uint8_t bus_id, simulith_i2c_read_callback read_cb, simulith_i2c_write_callback write_cb);
int simulith_i2c_init(i2c_device_t *device);

/**
* @brief Read data from an I2C device
* @param bus_id Bus identifier
* @param addr Device address
* @param reg Register address
* @param device Pointer to I2C device structure
* @param data Buffer to store read data
* @param len Number of bytes to read
* @return 0 on success, -1 on failure
* @return Number of bytes read on success, SIMULITH_I2C_ERROR on failure
*/
int simulith_i2c_read(uint8_t bus_id, uint8_t addr, uint8_t reg, uint8_t *data, size_t len);
int simulith_i2c_read(i2c_device_t *device, uint8_t *data, size_t len);

/**
* @brief Write data to an I2C device
* @param bus_id Bus identifier
* @param addr Device address
* @param reg Register address
* @param device Pointer to I2C device structure
* @param data Data to write
* @param len Number of bytes to write
* @return 0 on success, -1 on failure
* @return Number of bytes written on success, SIMULITH_I2C_ERROR on failure
*/
int simulith_i2c_write(i2c_device_t *device, const uint8_t *data, size_t len);

/**
* @brief Perform I2C transaction (write then read)
* @param device Pointer to I2C device structure
* @param tx_data Data to write
* @param tx_len Number of bytes to write
* @param rx_data Buffer to store read data
* @param rx_len Number of bytes to read
* @return SIMULITH_I2C_SUCCESS on success, SIMULITH_I2C_ERROR on failure
*/
int simulith_i2c_transaction(i2c_device_t *device, const uint8_t *tx_data, size_t tx_len, uint8_t *rx_data, size_t rx_len);

/**
* @brief Close an I2C device
* @param device Pointer to I2C device structure
* @return SIMULITH_I2C_SUCCESS on success, SIMULITH_I2C_ERROR on failure
*/
int simulith_i2c_write(uint8_t bus_id, uint8_t addr, uint8_t reg, const uint8_t *data, size_t len);
int simulith_i2c_close(i2c_device_t *device);

#ifdef __cplusplus
}
Expand Down
13 changes: 6 additions & 7 deletions include/simulith_uart.h
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
/*
* Simulith UART - Requirements
*
* - Shall utilize ZMQ to communicate between nodes
* - Shall have functions to initialize, send, receive, check available, and flush data
* - Shall communicate directly to the other end of the node
* - Shall not block on any function
* - Shall fail gracefully if peer is unavailable and return error codes (non-zero) instead of crashing.
* - Shall not rely on a server as each node will be initialized with its name and destination.
* Shall utilize ZMQ to communicate between nodes
* Shall have functions to initialize, send, receive, check available, and flush data
* Shall communicate directly to the other end of the node
* Shall not block on any function
* Shall fail gracefully if peer is unavailable and return error codes (non-zero) instead of crashing.
* Shall not rely on a server as each node will be initialized with its name and destination.
*/

#ifndef SIMULITH_UART_H
Expand All @@ -26,7 +26,6 @@ extern "C"
{
#endif


typedef struct {
char name[64];
char address[128];
Expand Down
161 changes: 106 additions & 55 deletions src/simulith_i2c.c
Original file line number Diff line number Diff line change
@@ -1,75 +1,126 @@
#include "simulith.h"
#include <errno.h>
/*
* Simulith I2C - Requirements
*
* Shall utilize ZMQ to communicate between nodes
* Shall have functions to initialize, read, write, transaction (both write then read), and close
* Shall communicate directly to the other end of the node
* Shall not block on any function
* Shall fail gracefully if peer is unavailable and return error codes (non-zero) instead of crashing.
* Shall not rely on a server as each node will be initialized with its name and destination.
*/

#define MAX_I2C_BUSES 8
#include "simulith_i2c.h"

typedef struct
int simulith_i2c_init(i2c_device_t *device)
{
bool initialized;
simulith_i2c_read_callback read_cb;
simulith_i2c_write_callback write_cb;
} i2c_bus_t;
if (!device) return SIMULITH_I2C_ERROR;
if (device->init == SIMULITH_I2C_INITIALIZED) return SIMULITH_I2C_SUCCESS;

static i2c_bus_t i2c_buses[MAX_I2C_BUSES] = {0};
device->zmq_ctx = zmq_ctx_new();
if (!device->zmq_ctx) {
simulith_log("simulith_i2c_init: Failed to create ZMQ context\n");
return SIMULITH_I2C_ERROR;
}
device->zmq_sock = zmq_socket(device->zmq_ctx, ZMQ_PAIR);
if (!device->zmq_sock) {
simulith_log("simulith_i2c_init: Failed to create ZMQ socket\n");
zmq_ctx_term(device->zmq_ctx);
return SIMULITH_I2C_ERROR;
}
if (strlen(device->name) > 0) {
zmq_setsockopt(device->zmq_sock, ZMQ_IDENTITY, device->name, strlen(device->name));
Copy link

Copilot AI Aug 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The return value of zmq_setsockopt is not checked. If setting the socket identity fails, it could lead to unexpected behavior in peer identification.

Suggested change
zmq_setsockopt(device->zmq_sock, ZMQ_IDENTITY, device->name, strlen(device->name));
int rc_id = zmq_setsockopt(device->zmq_sock, ZMQ_IDENTITY, device->name, strlen(device->name));
if (rc_id != 0) {
simulith_log("simulith_i2c_init: Failed to set ZMQ_IDENTITY for '%s'\n", device->name);
zmq_close(device->zmq_sock);
zmq_ctx_term(device->zmq_ctx);
return SIMULITH_I2C_ERROR;
}

Copilot uses AI. Check for mistakes.
}
int rc;
if (device->is_server) {
rc = zmq_bind(device->zmq_sock, device->address);
if (rc != 0) {
simulith_log("simulith_i2c_init: Failed to bind to %s\n", device->address);
zmq_close(device->zmq_sock);
zmq_ctx_term(device->zmq_ctx);
return SIMULITH_I2C_ERROR;
}
simulith_log("simulith_i2c_init: Bound to %s as '%s'\n", device->address, device->name);
} else {
rc = zmq_connect(device->zmq_sock, device->address);
if (rc != 0) {
simulith_log("simulith_i2c_init: Failed to connect to %s\n", device->address);
zmq_close(device->zmq_sock);
zmq_ctx_term(device->zmq_ctx);
return SIMULITH_I2C_ERROR;
}
simulith_log("simulith_i2c_init: Connected to %s as '%s'\n", device->address, device->name);
}
device->init = SIMULITH_I2C_INITIALIZED;
return SIMULITH_I2C_SUCCESS;
}

int simulith_i2c_init(uint8_t bus_id, simulith_i2c_read_callback read_cb, simulith_i2c_write_callback write_cb)
int simulith_i2c_write(i2c_device_t *device, const uint8_t *data, size_t len)
{
if (bus_id >= MAX_I2C_BUSES)
{
simulith_log("Invalid I2C bus ID: %d\n", bus_id);
errno = EINVAL;
return -1;
if (!device || device->init != SIMULITH_I2C_INITIALIZED) {
simulith_log("simulith_i2c_write: Uninitialized I2C device\n");
return SIMULITH_I2C_ERROR;
}

if (!read_cb || !write_cb)
{
simulith_log("Invalid I2C callbacks\n");
errno = EINVAL;
return -1;
int rc = zmq_send(device->zmq_sock, data, len, ZMQ_DONTWAIT);
if (rc < 0) {
simulith_log("simulith_i2c_write: zmq_send failed (peer may be unavailable)\n");
return SIMULITH_I2C_ERROR;
}

i2c_buses[bus_id].initialized = true;
i2c_buses[bus_id].read_cb = read_cb;
i2c_buses[bus_id].write_cb = write_cb;

simulith_log("I2C bus %d initialized\n", bus_id);
return 0;
simulith_log("I2C TX[%s]: %zu bytes\n", device->name, len);
return (int)len;
}

int simulith_i2c_read(uint8_t bus_id, uint8_t addr, uint8_t reg, uint8_t *data, size_t len)
int simulith_i2c_read(i2c_device_t *device, uint8_t *data, size_t len)
{
if (bus_id >= MAX_I2C_BUSES || !i2c_buses[bus_id].initialized)
{
simulith_log("Invalid or uninitialized I2C bus: %d\n", bus_id);
errno = EINVAL;
return -1;
if (!device || device->init != SIMULITH_I2C_INITIALIZED) {
simulith_log("simulith_i2c_read: Uninitialized I2C device\n");
return SIMULITH_I2C_ERROR;
}

if (!data || len == 0)
{
simulith_log("Invalid read parameters\n");
errno = EINVAL;
return -1;
int rc = zmq_recv(device->zmq_sock, data, len, ZMQ_DONTWAIT);
if (rc < 0) {
if (zmq_errno() == EAGAIN) {
// No data available, non-blocking
return 0;
}
simulith_log("simulith_i2c_read: zmq_recv failed (peer may be unavailable)\n");
return SIMULITH_I2C_ERROR;
}

return i2c_buses[bus_id].read_cb(addr, reg, data, len);
simulith_log("I2C RX[%s]: %d bytes\n", device->name, rc);
return rc;
Copy link

Copilot AI Aug 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The zmq_recv function may return more data than the buffer can hold if the incoming message is larger than 'len'. Consider checking the message size first using zmq_msg_size() or handling potential buffer overflow.

Suggested change
return rc;
zmq_msg_t msg;
if (zmq_msg_init(&msg) != 0) {
simulith_log("simulith_i2c_read: zmq_msg_init failed\n");
return SIMULITH_I2C_ERROR;
}
int rc = zmq_msg_recv(&msg, device->zmq_sock, ZMQ_DONTWAIT);
if (rc < 0) {
zmq_msg_close(&msg);
if (zmq_errno() == EAGAIN) {
// No data available, non-blocking
return 0;
}
simulith_log("simulith_i2c_read: zmq_msg_recv failed (peer may be unavailable)\n");
return SIMULITH_I2C_ERROR;
}
size_t msg_size = zmq_msg_size(&msg);
size_t copy_size = msg_size < len ? msg_size : len;
memcpy(data, zmq_msg_data(&msg), copy_size);
zmq_msg_close(&msg);
simulith_log("I2C RX[%s]: %zu bytes (received %zu, copied %zu)\n", device->name, msg_size, msg_size, copy_size);
return (int)copy_size;

Copilot uses AI. Check for mistakes.
}

int simulith_i2c_write(uint8_t bus_id, uint8_t addr, uint8_t reg, const uint8_t *data, size_t len)
int simulith_i2c_transaction(i2c_device_t *device, const uint8_t *tx_data, size_t tx_len, uint8_t *rx_data, size_t rx_len)
{
if (bus_id >= MAX_I2C_BUSES || !i2c_buses[bus_id].initialized)
{
simulith_log("Invalid or uninitialized I2C bus: %d\n", bus_id);
errno = EINVAL;
return -1;
if (!device || device->init != SIMULITH_I2C_INITIALIZED) {
simulith_log("simulith_i2c_transaction: Uninitialized I2C device\n");
return SIMULITH_I2C_ERROR;
}

if (!data || len == 0)
{
simulith_log("Invalid write parameters\n");
errno = EINVAL;
return -1;

// First, perform the write operation
int write_result = simulith_i2c_write(device, tx_data, tx_len);
if (write_result < 0) {
simulith_log("simulith_i2c_transaction: Write operation failed\n");
return SIMULITH_I2C_ERROR;
}

// Then, perform the read operation
int read_result = simulith_i2c_read(device, rx_data, rx_len);
if (read_result < 0) {
simulith_log("simulith_i2c_transaction: Read operation failed\n");
return SIMULITH_I2C_ERROR;
}

simulith_log("I2C Transaction[%s]: wrote %d bytes, read %d bytes\n", device->name, write_result, read_result);
return SIMULITH_I2C_SUCCESS;
}

return i2c_buses[bus_id].write_cb(addr, reg, data, len);
int simulith_i2c_close(i2c_device_t *device)
{
if (!device || device->init != SIMULITH_I2C_INITIALIZED) {
return SIMULITH_I2C_ERROR;
}
zmq_close(device->zmq_sock);
zmq_ctx_term(device->zmq_ctx);
device->init = 0;
Copy link

Copilot AI Aug 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using a magic number (0) instead of a defined constant for the uninitialized state. Consider defining SIMULITH_I2C_UNINITIALIZED or similar constant for consistency with SIMULITH_I2C_INITIALIZED.

Copilot uses AI. Check for mistakes.
simulith_log("I2C device %s closed\n", device->name);
return SIMULITH_I2C_SUCCESS;
}
17 changes: 8 additions & 9 deletions src/simulith_uart.c
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
/*
* Simulith UART
* Simulith UART - Requirements
*
* Requirements:
* - Shall utilize ZMQ to communicate between nodes
* - Shall have functions to initialize, send, receive, check available, and flush data
* - Shall communicate directly to the other end of the node
* - Shall not block on any function
* - Shall fail gracefully if peer is unavailable and return error codes (non-zero) instead of crashing
* - Shall not rely on a server as each node will be initialized with its name and destination
*/
* Shall utilize ZMQ to communicate between nodes
* Shall have functions to initialize, send, receive, check available, and flush data
* Shall communicate directly to the other end of the node
* Shall not block on any function
* Shall fail gracefully if peer is unavailable and return error codes (non-zero) instead of crashing.
* Shall not rely on a server as each node will be initialized with its name and destination.
*/

#include "simulith_uart.h"

Expand Down
Loading