I am trying to simulate an xcore application that is using the I2C library (lib_i2c) as a Slave towards a Raspberry PI (which is acting as Master).
For this I did a plugin in the same way as the ExamplePlugin but with plugin_clock adapted to act as a Raspberry PI Master sending out an address and waiting for an ACK from the slave.
When starting the simulation I get this:
but the plugin is of course there:~/xmosworkspace/i2cTest/bin$ xsim --plugin I2CTestPlugin.o "0 XOD23 XOD13" i2cTest.xe
I2CTestPlugin.o: only ET_DYN and ET_EXEC can be loaded
ERROR: unable to open plugin 'I2CTestPlugin.o'
The plugin was built as:~/xmosworkspace/i2cTest/bin$ ls
I2CTestPlugin.o i2cTest.xe
The actual plugin code is rather simple (copied most of the stuff from ExamplePlugin.cpp):~/Tools/XMOS/xTIMEcomposer/Community_14.0.3/src/plugins/I2CTestPlugin$ make -f MakefileUnixGPP.mak
g++ -g -fPIC -O3 -Wall -Wsign-compare -Wpointer-arith -c I2CTestPlugin.cpp -o I2CTestPlugin.o -I../../../include
g++ I2CTestPlugin.o -shared -o ../../../lib/I2CTestPlugin.so -lc -ldl -lstdc++
Code: Select all
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <assert.h>
#include "I2CTestPlugin.h"
#define MAX_INSTANCES 1
#define MAX_BYTES 1024
#define CHECK_STATUS if (status != XSI_STATUS_OK) return status
/*
* Types
*/
struct LoopbackInstance
{
XsiCallbacks *xsi;
const char *package;
const char *sda;
const char *scl;
};
/*
* Static data
*/
static size_t s_num_instances = 0;
static LoopbackInstance s_instances[MAX_INSTANCES];
static unsigned int timeStamp_us = 0; // increments each us;
static unsigned int counter = 0; // used to increment timeStamp_us every 100'th call (100 MHz clock)
/* This plugin is acting as an I2C Master and is driving the scl pin indepenent
* of the I2C Slave on the Startkit. It will drive the sda during address and
* RW bit but the release the pin to the Slave during the ACK bit.
*/
const unsigned int scl_switchTimes_us[20] = {10, 17, 22, 27, 32, 37, 42, 47, 52, 57, 62, 67, 72, 77, 82, 87, 92, 97, 102, 110};
const unsigned int sda_switchTimes_us[5] = {5, 23, 33, 73, 83};
static unsigned scl_level = 1; // start with pin high
static unsigned sda_level = 1; // start with pin high
static unsigned int scl_index = 0;
static unsigned int sda_index = 0;
const unsigned int scl_ACK_start_index = 14;
const unsigned int scl_ACK_end_index = 16;
const unsigned int scl_last_index = sizeof(scl_switchTimes_us)/sizeof(scl_switchTimes_us[0]);
static unsigned int sda_ACK_counter = 0;
static unsigned sda_slave_is_ACK = 0;
static unsigned slave_should_ack = 0;
/*
* Static functions
*/
static void print_usage();
static XsiStatus split_args(const char *args, char *argv[]);
/*
* Create
*/
XsiStatus plugin_create(void **instance, XsiCallbacks *xsi, const char *arguments)
{
if (s_num_instances >= MAX_INSTANCES) {
fprintf(stderr, "ERROR: too many instances of plugin (max %d)\n", MAX_INSTANCES);
return XSI_STATUS_INVALID_INSTANCE;
}
assert(CHECK_INTERFACE_VERSION(xsi));
// Use the entry in the instances list to identify this instance
*instance = (void*)s_num_instances;
char *argv[3];
XsiStatus status = split_args(arguments, argv);
if (status != XSI_STATUS_OK) {
print_usage();
return status;
}
// Stores pin information
s_instances[s_num_instances].package = argv[0];
s_instances[s_num_instances].sda = argv[1];
s_instances[s_num_instances].scl = argv[2];
s_instances[s_num_instances].xsi = xsi;
s_num_instances++;
return XSI_STATUS_OK;
}
/*
* Clock
*/
XsiStatus plugin_clock(void *instance)
{
size_t instance_num = (size_t)instance;
if (instance_num >= s_num_instances) {
return XSI_STATUS_INVALID_INSTANCE;
}
XsiStatus status = XSI_STATUS_OK;
XsiCallbacks *xsi = s_instances[instance_num].xsi;
const char *package = s_instances[instance_num].package;
const char *sda = s_instances[instance_num].sda;
const char *scl = s_instances[instance_num].scl;
unsigned value = 0;
unsigned int sda_driving = 0;
unsigned int scl_driving = 0;
status = xsi->is_pin_driving(package, sda, &sda_driving);
CHECK_STATUS;
status = xsi->is_pin_driving(package, scl, &scl_driving);
CHECK_STATUS;
// Check that slave is not driving scl
if (1 == scl_driving) {
fprintf(stderr, "Slave is driving the SCL pin!\n");
return XSI_STATUS_INVALID_PIN;
}
// Handle time
if (100 == counter) {
timeStamp_us++;
counter = 0;
}
// Handle scl
if (sizeof(scl_switchTimes_us) > scl_index) {
if (scl_switchTimes_us[scl_index] <= timeStamp_us) {
// switch scl
if (1 > scl_level) {
// scl is low, set it high
scl_level = 1;
}
else {
// scl is high, set it low
scl_level = 0;
}
// incremt to next index
scl_index++;
}
}
// Handle sda when driving for address bits
if (sizeof(sda_switchTimes_us) > sda_index) {
if (sda_switchTimes_us[sda_index] <= timeStamp_us) {
// switch sda
if (1 > sda_level) {
// sda is low, set it high
sda_level = 1;
}
else {
// sda is high, set it low
sda_level = 0;
}
// incremt to next index
sda_index++;
}
}
// Check that slave is driving SDA low during the ACK phase
if ((scl_switchTimes_us[scl_ACK_start_index] <= timeStamp_us) &&
(scl_switchTimes_us[scl_ACK_end_index] > timeStamp_us)) {
status = xsi->sample_pin(package, sda, &value);
CHECK_STATUS;
slave_should_ack = 1;
if ((1 == sda_driving) && (0 == value)) {
sda_ACK_counter++;
sda_slave_is_ACK = 1;
}
// If slave started ACK it must keep ACK until begin of stop bit
if ((1 == sda_slave_is_ACK) && (1 == value)) {
fprintf(stderr, "Slave dropped ACK during the ninth bit!\n");
}
}
// Check that slave did ACK
if (scl_switchTimes_us[scl_ACK_end_index] < timeStamp_us) {
slave_should_ack = 0;
if (0 == sda_slave_is_ACK) {
fprintf(stderr, "Slave did not ACK during the ninth bit!\n");
}
}
// handle the stop bit
if ((scl_switchTimes_us[scl_ACK_end_index] < timeStamp_us) &&
(scl_switchTimes_us[scl_last_index-1]+5 > timeStamp_us)) {
sda_level = 0;
}
if (scl_switchTimes_us[scl_last_index-1]+5 <= timeStamp_us) {
sda_level = 1;
}
// drive the pins, scl all the time
status = xsi->drive_pin(package, scl, scl_level);
if (0 == slave_should_ack) {
// drive sda when slave is not ACK
status = xsi->drive_pin(package, sda, sda_level);
}
// Increment counter for next sim step
counter++;
return status;
}
/*
* Notify
*/
XsiStatus plugin_notify(void *instance, int type, unsigned arg1, unsigned arg2)
{
return XSI_STATUS_OK;
}
/*
* Terminate
*/
XsiStatus plugin_terminate(void *instance)
{
if ((size_t)instance >= s_num_instances) {
return XSI_STATUS_INVALID_INSTANCE;
}
return XSI_STATUS_OK;
}
/*
* Usage
*/
static void print_usage()
{
fprintf(stderr, "Usage:\n");
fprintf(stderr, " I2CTestPlugin.dll/so <package> <sda pin> <scl pin>\n");
}
/*
* Split args
*/
static XsiStatus split_args(const char *args, char *argv[])
{
char buf[MAX_BYTES];
int arg_num = 0;
while (arg_num < 3) {
char *buf_ptr = buf;
while (isspace(*args))
args++;
if (*args == '\0')
return XSI_STATUS_INVALID_ARGS;
while (*args != '\0' && !isspace(*args))
*buf_ptr++ = *args++;
*buf_ptr = '\0';
argv[arg_num] = strdup(buf);
arg_num++;
}
while (isspace(*args))
args++;
if (arg_num != 3 || *args != '\0')
return XSI_STATUS_INVALID_ARGS;
else
return XSI_STATUS_OK;
}
/Igor