CAN Bus Simulation

TEMU provides support for CAN bus based devices. The bus model interfaces are available in: temu-c/Bus/Can.h. In addition to the interfaces one CAN bus model is provided.

As CAN is a multi-node bus, a bus model object is needed to route messages to the relevant destination.

There are two types of CAN classes that can be created, firstly bus models and secondly device models. A bus model is responsible for routing messages to the device models, and the device models implement CAN message reception logic.

The standard SimpleCANBus bus model, provides fairly simple logic. It routes a sent message to all devices connected to the CAN bus (except the sender device). However, CAN devices often implements filtering of message IDs in hardware, and this filtering (which is typically based on a mask and code pair) can be used to define a smart CAN bus model. Such a model would route frames using internal routing tables.

A smart CAN bus model will by definition be system specific. It’s routing will depend on the system specific allocation of message IDs.

Interfaces

The interesting interfaces are defined in the temu-c/Bus/Can.h header. This header also define inline functions to help construct CAN frames.

typedef struct {
  uint8_t Data[8];
  uint32_t Flags;
  uint8_t Length;
  uint8_t Error;
} temu_CanFrame;

struct temu_CanDevIface {
  void (*connected)(void *Dev, temu_CanBusIfaceRef Bus);
  void (*disconnected)(void *Dev);
  void (*receive)(void *Dev, temu_CanFrame *Frame);
};

struct temu_CanBusIface {
  void (*connect)(void *Bus, temu_CanDevIfaceRef Dev);
  void (*send)(void *Bus, void *Sender, temu_CanFrame *Frame);
  void (*enableSendEvents)(void *Bus);
  void (*disableSendEvents)(void *Bus);
  void (*reportStats)(void *Bus);
  void (*setFilter)(void *Bus, temu_CanDevIfaceRef Dev, int FilterID,
                    uint32_t Mask, uint32_t Code);
};

The CAN frame is central to the transmission of CAN data. It is not a bit by bit representation of the CAN protocol, rather it is a simplified format that omit bits that are implicit and ensures that relevant bits such as RTR is fixed in location.

If a real CAN frame is needed, the frame struct need to be transformed to the needed representation. Note that the struct is optimised for performance (e.g. Data is first and can be bitcopied as a uint64).

Device models are typically simple, they implement the connected, disconnected and receive functions. If the device needs registers and MMIO handling, it tends to get more complex.

The device and bus interface support connect and disconnect events. The purpose of these are to support hot-plugging of CAN devices. As these connect and disconnect events are supported, the normal connect command should not be used when connecting a CAN device, rather the can-connect command is to be used.

Commands

Two CAN bus related commands are provided:

Name Description

can-connect

Connect a CAN device to a CAN bus.

can-disconnect

Disconnect CAN device from a CAN bus.

Classes

The SimpleCAN bus class provides a CAN bus model. In the SimpleCANBus` class, messages are forwarded to all connected devices (except the sending one). If this results in performance issues, it is possible to write a filtering CAN bus model.

SimpleCANBus Reference

Properties

Name Type Description

Class

*void

Class object

Component

*void

Pointer to component object if part of component

LoggingFlags

uint64_t

Flags for logging info

Name

*char

Object name

TimeSource

*void

Time source object

devices

temu_IfaceRefArray

CAN devices attached to bus

stats.lastReportSentBits

uint64_t

Statistics

stats.sentBits

uint64_t

Statistics

Interfaces

Name Type Description

CanBusIface

CanBusIface

CAN Bus Interface

Commands

Name Description

delete

Dispose instance of SimpleCANBus

Fault Injection

The CAN bus is multi-node bus and modification of traffic can be done using notifications.

The SimpleCANBus model emits a notification named temu.canSend. The, notification receives a pointer to the temu_CanFrame as the notification info.

Fault injection can thus be done by modifying the Error field in the frame. Or by modifying the data content directly.

The notification is emitted before the frame is forwarded to the receivers.

Another way of injecting faults is to have a custom model injecting frames when a scheduled event is triggered.

To be able to drop frames in transit, a man-in-the-middle model is needed between the bus and the receiving device model.

Examples

This example shows how to create a simple CAN device and connect it to a bus model.

Connecting CAN Devices
exec ut700.temu
import MyCanDevice
# Create a can bus
create class=SimpleCANBus name=canbus0
create class=MyCANClass name=mycan0

can-connect bus=canbus0:CanBusIface dev=occan0:CanDevIface # From ut700
can-connect bus=canbus0:CanBusIface dev=mycan0:CanDevIface

The next example shows how to implement a simple CAN device

Simple CAN Device Example
#include "temu-c/Bus/Can.h"
#include "temu-c/Bus/Objsys.h"

// This is a device / RTU model, it needs to know about its CAN bus
typedef struct MyCanDevice {
  temu_Object Super;
  temu_CanBusIfaceRef Bus;
} MyCanDevice;


void*
create(const char *Name, int Argc, const temu_CreateArg *Argv)
{
  MyCanDevice *Dev = malloc(sizeof(MyCanDevice));
  memset(Dev, 0, sizeof(MyCanDevice);
  return Dev;
}

void
dispose(void *Obj)
{
  MyCanDevice *Dev = (MyCanDevice*)Obj;
  free(Dev);
}

// Implement the CAN Device interface

void
connected(void *Obj, temu_CanBusIfaceRef Bus)
{
  MyCanDevice *Dev = (MyCanDevice*)Obj;
  Dev->Bus = Bus;
  temu_logInfo(Dev, "connected to CAN bus");
}

void
disconnected(void *Obj)
{
  MyCanDevice *Dev = (MyCanDevice*)Obj;
  Dev->Bus = {NULL, NULL};
  // NOTE: This should also stop any pending events related to
  // message transmissions
  temu_logInfo(Dev, "disconnected from CAN bus");
}

void
receive(void *Dev, temu_CanFrame *Frame)
{
  temu_logInfo(Dev, "received CAN message with msg id %u",
               temu_canGetIdent(Frame));
}

temu_CanDevIface CanIface = {
  connected,
  disconnected,
  receive,
};

TEMU_PLUGIN_INIT
{
  temu_Class *cls = temu_registerClass("MyCANClass", create, dispose);

  temu_addProperty(cls, "CANBus", teTY_IfaceRef, 1);
  temu_addInterface(cls, "CanDevIface", "CanDevIface", &CanIface);

}