diff --git a/EWARM/Modbus.ewp b/EWARM/Modbus.ewp
index 3987a39..e5ecdc1 100644
--- a/EWARM/Modbus.ewp
+++ b/EWARM/Modbus.ewp
@@ -1281,6 +1281,9 @@
$PROJ_DIR$\..\PLSR\Inc\plsr_core.h
+
+ $PROJ_DIR$\..\PLSR\Inc\plsr_job.h
+
$PROJ_DIR$\..\PLSR\Src\plsr_persistence.c
@@ -1290,6 +1293,9 @@
$PROJ_DIR$\..\PLSR\Src\plsr_resource.c
+
+ $PROJ_DIR$\..\PLSR\Src\plsr_job.c
+
$PROJ_DIR$\..\PLSR\Src\plsr_core.c
diff --git a/PLSR/Inc/plsr_core.h b/PLSR/Inc/plsr_core.h
index 89e2b3e..9580ce0 100644
--- a/PLSR/Inc/plsr_core.h
+++ b/PLSR/Inc/plsr_core.h
@@ -1,6 +1,7 @@
#ifndef PLSR_CORE_H
#define PLSR_CORE_H
+#include "plsr_job.h"
#include "plsr_types.h"
#include
@@ -13,9 +14,12 @@ void PlsrTask(void *argument);
void PlsrProcess(void);
PLSR_RESULT PlsrPostStart(const PLSR_START_REQUEST *request);
+PLSR_RESULT PlsrPostCall(const PLSR_CALL *call);
PLSR_RESULT PlsrPostCommand(const PLSR_COMMAND *command);
PLSR_RESULT PlsrPostEvent(uint8_t axis, uint32_t eventMask);
PLSR_RESULT PlsrGetStatus(uint8_t axis, PLSR_STATUS *status);
+PLSR_RESULT PlsrGetLastParseDetail(uint8_t axis,
+ PLSR_PARSE_DETAIL *detail);
PLSR_RESULT PlsrStateTransition(uint8_t axis,
PLSR_STATE target,
diff --git a/PLSR/Inc/plsr_job.h b/PLSR/Inc/plsr_job.h
new file mode 100644
index 0000000..f63cbc9
--- /dev/null
+++ b/PLSR/Inc/plsr_job.h
@@ -0,0 +1,198 @@
+#ifndef PLSR_JOB_H
+#define PLSR_JOB_H
+
+#include "plsr_types.h"
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define PLSR_MAX_SEGMENTS (100U)
+#define PLSR_OUTPUT_MODE_FROM_SFD (0xFFU)
+#define PLSR_FREQUENCY_MIN_HZ (1UL)
+#define PLSR_FREQUENCY_MAX_HZ (100000UL)
+
+typedef enum
+{
+ PLSR_DEVICE_D = 0,
+ PLSR_DEVICE_HD,
+ PLSR_DEVICE_FD,
+ PLSR_DEVICE_X,
+ PLSR_DEVICE_M,
+ PLSR_DEVICE_HM
+} PLSR_DEVICE_TYPE;
+
+typedef uint8_t (*PLSR_VALIDATE_WORDS_FN)(void *context,
+ PLSR_DEVICE_TYPE device,
+ uint32_t firstAddress,
+ uint32_t wordCount);
+typedef uint8_t (*PLSR_READ_WORD_FN)(void *context,
+ PLSR_DEVICE_TYPE device,
+ uint32_t address,
+ uint16_t *value);
+typedef uint8_t (*PLSR_READ_BIT_FN)(void *context,
+ PLSR_DEVICE_TYPE device,
+ uint32_t address,
+ uint8_t *value);
+
+typedef struct
+{
+ void *context;
+ PLSR_VALIDATE_WORDS_FN validateWords;
+ PLSR_READ_WORD_FN readWord;
+ PLSR_READ_BIT_FN readBit;
+} PLSR_DATA_SOURCE;
+
+typedef struct
+{
+ PLSR_DEVICE_TYPE device;
+ uint32_t address;
+} PLSR_DATA_REF;
+
+typedef enum
+{
+ PLSR_OPERAND_CONSTANT = 0,
+ PLSR_OPERAND_DATA
+} PLSR_OPERAND_TYPE;
+
+typedef struct
+{
+ PLSR_OPERAND_TYPE type;
+ PLSR_DATA_REF data;
+ int32_t constant;
+} PLSR_VALUE_OPERAND;
+
+typedef struct
+{
+ uint32_t sequence;
+ /* The source context must remain valid while the queued/running job exists. */
+ PLSR_DATA_SOURCE source;
+ PLSR_DATA_REF s0;
+ PLSR_DATA_REF s1;
+ PLSR_VALUE_OPERAND s2;
+ uint8_t dAxis;
+ uint8_t outputModeOverride;
+} PLSR_CALL;
+
+typedef enum
+{
+ PLSR_WAIT_PULSE_COMPLETE = 0,
+ PLSR_WAIT_TIME,
+ PLSR_WAIT_SIGNAL,
+ PLSR_WAIT_ACT_TIME,
+ PLSR_WAIT_EXT,
+ PLSR_WAIT_EXT_OR_COMPLETE
+} PLSR_WAIT_CONDITION;
+
+typedef enum
+{
+ PLSR_VALUE_CONSTANT = 0,
+ PLSR_VALUE_D,
+ PLSR_VALUE_HD,
+ PLSR_VALUE_FD,
+ PLSR_VALUE_X,
+ PLSR_VALUE_M,
+ PLSR_VALUE_HM
+} PLSR_VALUE_SOURCE;
+
+#define PLSR_SEGMENT_FLAG_ZERO_PULSE (1U << 0U)
+#define PLSR_SEGMENT_FLAG_DEFAULT_SPEED (1U << 1U)
+#define PLSR_SEGMENT_FLAG_SPEED_CLAMPED (1U << 2U)
+#define PLSR_SEGMENT_FLAG_SELF_LOOP (1U << 3U)
+
+typedef struct
+{
+ uint32_t targetFrequency;
+ int32_t pulseOrTarget;
+ int32_t waitValueOrAddress;
+ int32_t jumpValueOrAddress;
+ uint8_t waitCondition;
+ uint8_t waitSource;
+ uint8_t jumpSource;
+ uint8_t flags;
+} PLSR_SEGMENT_SNAPSHOT;
+
+typedef struct
+{
+ uint32_t defaultSpeed;
+ uint32_t maximumSpeed;
+ uint32_t startSpeed;
+ uint32_t stopSpeed;
+ uint32_t zrnHighSpeed;
+ uint32_t zrnCrawlSpeed;
+ uint16_t accelerationMs;
+ uint16_t decelerationMs;
+ uint16_t gapAccelerationMs;
+ uint16_t directionDelayUs;
+ uint8_t curveMode;
+ uint8_t follow;
+ uint8_t feedforwardPercent;
+ uint8_t refreshCode;
+} PLSR_S2_SNAPSHOT;
+
+typedef struct
+{
+ PLSR_DATA_SOURCE source;
+ PLSR_DATA_REF s0;
+ PLSR_DATA_REF s1;
+ PLSR_S2_SNAPSHOT s2;
+ PLSR_SEGMENT_SNAPSHOT segments[PLSR_MAX_SEGMENTS];
+ uint32_t timerClockHz;
+ uint32_t pairedTimerClockHz;
+ uint16_t segmentCount;
+ uint16_t startSegment;
+ uint8_t dAxis;
+ uint8_t s2Set;
+ uint8_t positioningMode;
+ uint8_t outputMode;
+ uint8_t directionPoint;
+ uint8_t directionActiveHigh;
+ uint8_t initialDirectionPositive;
+ uint8_t speedClamped;
+ uint8_t hasSelfLoop;
+} PLSR_JOB_SNAPSHOT;
+
+typedef enum
+{
+ PLSR_PARSE_BLOCK_NONE = 0,
+ PLSR_PARSE_BLOCK_S0,
+ PLSR_PARSE_BLOCK_S1,
+ PLSR_PARSE_BLOCK_S2,
+ PLSR_PARSE_BLOCK_OUTPUT,
+ PLSR_PARSE_BLOCK_POSITION
+} PLSR_PARSE_BLOCK;
+
+typedef struct
+{
+ PLSR_RESULT result;
+ PLSR_PARSE_BLOCK block;
+ uint32_t address;
+ int32_t value;
+ uint16_t segment;
+} PLSR_PARSE_DETAIL;
+
+typedef struct
+{
+ int64_t logicalPosition;
+ uint8_t positionValid;
+} PLSR_PARSE_CONTEXT;
+
+PLSR_RESULT PlsrBuildJobSnapshot(const PLSR_CALL *call,
+ const PLSR_PARSE_CONTEXT *context,
+ PLSR_JOB_SNAPSHOT *snapshot,
+ PLSR_PARSE_DETAIL *detail);
+PLSR_RESULT PlsrResolveLiveFrequency(const PLSR_JOB_SNAPSHOT *snapshot,
+ uint16_t segment,
+ uint32_t *frequency,
+ uint8_t *clamped);
+PLSR_RESULT PlsrCalculateTimerDivider(uint32_t timerClockHz,
+ uint32_t frequencyHz,
+ uint16_t *psc,
+ uint16_t *arr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PLSR_JOB_H */
diff --git a/PLSR/Inc/plsr_types.h b/PLSR/Inc/plsr_types.h
index 2c948f0..0f6a61f 100644
--- a/PLSR/Inc/plsr_types.h
+++ b/PLSR/Inc/plsr_types.h
@@ -60,7 +60,21 @@ typedef enum
PLSR_RESULT_BUSY,
PLSR_RESULT_PERSISTENCE_ERROR,
PLSR_RESULT_NOT_SUPPORTED,
- PLSR_RESULT_INTERNAL_ERROR
+ PLSR_RESULT_INTERNAL_ERROR,
+ PLSR_RESULT_DATA_ACCESS,
+ PLSR_RESULT_ADDRESS_OVERFLOW,
+ PLSR_RESULT_BLOCK_OVERLAP,
+ PLSR_RESULT_SEGMENT_OVERFLOW,
+ PLSR_RESULT_RESERVED_NOT_ZERO,
+ PLSR_RESULT_INVALID_POSITION_MODE,
+ PLSR_RESULT_INVALID_S2,
+ PLSR_RESULT_INVALID_FREQUENCY,
+ PLSR_RESULT_INVALID_WAIT,
+ PLSR_RESULT_INVALID_JUMP,
+ PLSR_RESULT_PATH_CYCLE,
+ PLSR_RESULT_POSITION_INVALID,
+ PLSR_RESULT_POSITION_OVERFLOW,
+ PLSR_RESULT_DIVIDER_UNREPRESENTABLE
} PLSR_RESULT;
typedef enum
@@ -159,6 +173,12 @@ typedef struct
uint8_t directionPositive;
uint8_t highResourceMask;
uint8_t directionPoint;
+ uint8_t positionValid;
+ uint8_t jobValid;
+ uint8_t s2Set;
+ uint8_t speedClamped;
+ uint16_t segmentCount;
+ uint16_t startSegment;
} PLSR_STATUS;
#ifdef __cplusplus
diff --git a/PLSR/Src/plsr_core.c b/PLSR/Src/plsr_core.c
index 87b22ce..10885eb 100644
--- a/PLSR/Src/plsr_core.c
+++ b/PLSR/Src/plsr_core.c
@@ -17,6 +17,8 @@ typedef struct
PLSR_STOP_REASON stopReason;
PLSR_STATE pendingTerminal;
PLSR_RESOURCE_LEASE lease;
+ PLSR_JOB_SNAPSHOT job;
+ PLSR_PARSE_DETAIL parseDetail;
volatile uint32_t pendingEvents;
uint32_t lastCommandSequence;
PLSR_RESULT lastCommandResult;
@@ -27,14 +29,18 @@ typedef struct
uint8_t done;
uint8_t directionPositive;
uint8_t immediateStopPending;
+ uint8_t positionValid;
+ uint8_t jobValid;
} PLSR_AXIS;
typedef struct
{
PLSR_COMMAND command;
PLSR_START_REQUEST start;
+ PLSR_CALL call;
uint32_t ticket;
uint8_t hasStart;
+ uint8_t hasCall;
uint8_t occupied;
} PLSR_COMMAND_SLOT;
@@ -42,6 +48,7 @@ static PLSR_AXIS PlsrAxes[PLSR_AXIS_COUNT];
static PLSR_COMMAND_SLOT PlsrCommandQueue[PLSR_COMMAND_QUEUE_DEPTH];
static uint32_t PlsrNextTicket;
static uint8_t PlsrInitialized;
+static PLSR_JOB_SNAPSHOT PlsrJobScratch;
static uint32_t PlsrCoreEnterCritical(void)
{
@@ -240,7 +247,8 @@ static uint8_t PlsrCommandPriority(PLSR_COMMAND_OPCODE opcode)
}
static PLSR_RESULT PlsrQueueCommand(const PLSR_COMMAND *command,
- const PLSR_START_REQUEST *start)
+ const PLSR_START_REQUEST *start,
+ const PLSR_CALL *call)
{
uint32_t interruptState;
uint8_t freeSlot = PLSR_COMMAND_QUEUE_DEPTH;
@@ -292,6 +300,18 @@ static PLSR_RESULT PlsrQueueCommand(const PLSR_COMMAND *command,
sizeof(PlsrCommandQueue[freeSlot].start));
PlsrCommandQueue[freeSlot].hasStart = 0U;
}
+ if (call != NULL)
+ {
+ PlsrCommandQueue[freeSlot].call = *call;
+ PlsrCommandQueue[freeSlot].hasCall = 1U;
+ }
+ else
+ {
+ (void)memset(&PlsrCommandQueue[freeSlot].call,
+ 0,
+ sizeof(PlsrCommandQueue[freeSlot].call));
+ PlsrCommandQueue[freeSlot].hasCall = 0U;
+ }
PlsrCommandQueue[freeSlot].ticket = PlsrNextTicket++;
PlsrCommandQueue[freeSlot].occupied = 1U;
PlsrCoreExitCritical(interruptState);
@@ -361,7 +381,30 @@ PLSR_RESULT PlsrPostStart(const PLSR_START_REQUEST *request)
command.axis = request->axis;
command.opcode = PLSR_CMD_START;
command.argument = 0;
- return PlsrQueueCommand(&command, request);
+ return PlsrQueueCommand(&command, request, NULL);
+}
+
+PLSR_RESULT PlsrPostCall(const PLSR_CALL *call)
+{
+ PLSR_COMMAND command;
+
+ if (call == NULL)
+ {
+ return PLSR_RESULT_INVALID_ARGUMENT;
+ }
+ if (call->dAxis >= PLSR_AXIS_COUNT)
+ {
+ return PLSR_RESULT_INVALID_AXIS;
+ }
+ if (PlsrInitialized == 0U)
+ {
+ return PLSR_RESULT_INVALID_STATE;
+ }
+ command.sequence = call->sequence;
+ command.axis = call->dAxis;
+ command.opcode = PLSR_CMD_START;
+ command.argument = 0;
+ return PlsrQueueCommand(&command, NULL, call);
}
PLSR_RESULT PlsrPostCommand(const PLSR_COMMAND *command)
@@ -383,7 +426,7 @@ PLSR_RESULT PlsrPostCommand(const PLSR_COMMAND *command)
{
return PLSR_RESULT_INVALID_STATE;
}
- return PlsrQueueCommand(command, NULL);
+ return PlsrQueueCommand(command, NULL, NULL);
}
PLSR_RESULT PlsrPostEvent(uint8_t axis, uint32_t eventMask)
@@ -464,6 +507,7 @@ static PLSR_RESULT PlsrStartAxis(PLSR_AXIS *axisObject,
axisObject->pendingTerminal = PLSR_STATE_UNINITIALIZED;
axisObject->immediateStopPending = 0U;
axisObject->done = 0U;
+ axisObject->jobValid = 0U;
result = PlsrStateTransition(start->axis,
PLSR_STATE_ACCEL,
PLSR_TRANSITION_START);
@@ -474,6 +518,67 @@ static PLSR_RESULT PlsrStartAxis(PLSR_AXIS *axisObject,
return result;
}
+static PLSR_RESULT PlsrStartCall(PLSR_AXIS *axisObject,
+ const PLSR_CALL *call)
+{
+ PLSR_RESOURCE_REQUEST resourceRequest;
+ PLSR_PARSE_CONTEXT parseContext;
+ PLSR_RESULT result;
+
+ if ((axisObject->state != PLSR_STATE_IDLE)
+ && (axisObject->state != PLSR_STATE_COMPLETED)
+ && (axisObject->state != PLSR_STATE_STOPPED))
+ {
+ return PLSR_RESULT_INVALID_STATE;
+ }
+
+ parseContext.logicalPosition = axisObject->logicalPosition;
+ parseContext.positionValid = axisObject->positionValid;
+ result = PlsrBuildJobSnapshot(call,
+ &parseContext,
+ &PlsrJobScratch,
+ &axisObject->parseDetail);
+ if (result != PLSR_RESULT_OK)
+ {
+ return result;
+ }
+
+ resourceRequest.ownerAxis = call->dAxis;
+ resourceRequest.outputMode =
+ (PLSR_OUTPUT_MODE)PlsrJobScratch.outputMode;
+ resourceRequest.dAxis = call->dAxis;
+ resourceRequest.directionPoint = PlsrJobScratch.directionPoint;
+ result = PlsrResourceReserve(&resourceRequest, &axisObject->lease);
+ if (result != PLSR_RESULT_OK)
+ {
+ axisObject->error = (result == PLSR_RESULT_RESOURCE_CONFLICT)
+ ? PLSR_ERROR_RESOURCE_CONFLICT
+ : PLSR_ERROR_INVALID_RESOURCE;
+ axisObject->parseDetail.result = result;
+ axisObject->parseDetail.block = PLSR_PARSE_BLOCK_OUTPUT;
+ return result;
+ }
+
+ axisObject->job = PlsrJobScratch;
+ axisObject->jobValid = 1U;
+ axisObject->outputMode = (PLSR_OUTPUT_MODE)axisObject->job.outputMode;
+ axisObject->directionPositive = axisObject->job.initialDirectionPositive;
+ axisObject->error = PLSR_ERROR_NONE;
+ axisObject->stopReason = PLSR_STOP_REASON_NONE;
+ axisObject->pendingTerminal = PLSR_STATE_UNINITIALIZED;
+ axisObject->immediateStopPending = 0U;
+ axisObject->done = 0U;
+ result = PlsrStateTransition(call->dAxis,
+ PLSR_STATE_ACCEL,
+ PLSR_TRANSITION_START);
+ if (result != PLSR_RESULT_OK)
+ {
+ axisObject->jobValid = 0U;
+ PlsrResourceRelease(&axisObject->lease);
+ }
+ return result;
+}
+
static PLSR_RESULT PlsrStopImmediate(uint8_t axis)
{
PLSR_AXIS *axisObject = &PlsrAxes[axis];
@@ -590,9 +695,16 @@ static PLSR_RESULT PlsrExecuteCommand(const PLSR_COMMAND_SLOT *slot)
switch (slot->command.opcode)
{
case PLSR_CMD_START:
- result = (slot->hasStart != 0U)
- ? PlsrStartAxis(axisObject, &slot->start)
- : PLSR_RESULT_INVALID_ARGUMENT;
+ if (slot->hasCall != 0U)
+ {
+ result = PlsrStartCall(axisObject, &slot->call);
+ }
+ else
+ {
+ result = (slot->hasStart != 0U)
+ ? PlsrStartAxis(axisObject, &slot->start)
+ : PLSR_RESULT_INVALID_ARGUMENT;
+ }
break;
case PLSR_CMD_STOP_DECEL:
@@ -630,6 +742,7 @@ static PLSR_RESULT PlsrExecuteCommand(const PLSR_COMMAND_SLOT *slot)
else
{
axisObject->logicalPosition = slot->command.argument;
+ axisObject->positionValid = 1U;
(void)PlcDevicePublishHsdPair(
(uint16_t)(slot->command.axis
* PLSR_HSD_RUNTIME_AXIS_COUNT),
@@ -646,6 +759,7 @@ static PLSR_RESULT PlsrExecuteCommand(const PLSR_COMMAND_SLOT *slot)
else
{
axisObject->logicalPosition = 0;
+ axisObject->positionValid = 1U;
(void)PlcDevicePublishHsdPair(
(uint16_t)(slot->command.axis
* PLSR_HSD_RUNTIME_AXIS_COUNT),
@@ -888,6 +1002,7 @@ static void PlsrProcessNormalEvents(uint8_t axis, uint32_t events)
PLSR_RESULT PlsrInit(void)
{
+ int64_t restoredPosition;
uint8_t axis;
(void)memset(PlsrAxes, 0, sizeof(PlsrAxes));
@@ -901,6 +1016,15 @@ PLSR_RESULT PlsrInit(void)
PlsrAxes[axis].state = PLSR_STATE_UNINITIALIZED;
PlsrAxes[axis].pendingTerminal = PLSR_STATE_UNINITIALIZED;
PlsrAxes[axis].lease.directionPoint = PLSR_DIRECTION_POINT_NONE;
+ if ((PlcDeviceGetLastHsdLoadResult() == PLSR_PERSISTENCE_OK)
+ && (PlcDeviceReadHsdPair(
+ (uint16_t)(axis * PLSR_HSD_RUNTIME_AXIS_COUNT),
+ &restoredPosition)
+ == PLC_DEVICE_OK))
+ {
+ PlsrAxes[axis].logicalPosition = restoredPosition;
+ PlsrAxes[axis].positionValid = 1U;
+ }
if (PlsrStateTransition(axis,
PLSR_STATE_IDLE,
PLSR_TRANSITION_INITIALIZED)
@@ -1001,6 +1125,37 @@ PLSR_RESULT PlsrGetStatus(uint8_t axis, PLSR_STATUS *status)
status->directionPoint = (axisObject->lease.valid != 0U)
? axisObject->lease.directionPoint
: PLSR_DIRECTION_POINT_NONE;
+ status->positionValid = axisObject->positionValid;
+ status->jobValid = axisObject->jobValid;
+ status->s2Set = (axisObject->jobValid != 0U) ? axisObject->job.s2Set : 0U;
+ status->speedClamped = (axisObject->jobValid != 0U)
+ ? axisObject->job.speedClamped
+ : 0U;
+ status->segmentCount = (axisObject->jobValid != 0U)
+ ? axisObject->job.segmentCount
+ : 0U;
+ status->startSegment = (axisObject->jobValid != 0U)
+ ? axisObject->job.startSegment
+ : 0U;
+ PlsrCoreExitCritical(interruptState);
+ return PLSR_RESULT_OK;
+}
+
+PLSR_RESULT PlsrGetLastParseDetail(uint8_t axis,
+ PLSR_PARSE_DETAIL *detail)
+{
+ uint32_t interruptState;
+
+ if (axis >= PLSR_AXIS_COUNT)
+ {
+ return PLSR_RESULT_INVALID_AXIS;
+ }
+ if (detail == NULL)
+ {
+ return PLSR_RESULT_INVALID_ARGUMENT;
+ }
+ interruptState = PlsrCoreEnterCritical();
+ *detail = PlsrAxes[axis].parseDetail;
PlsrCoreExitCritical(interruptState);
return PLSR_RESULT_OK;
}
diff --git a/PLSR/Src/plsr_job.c b/PLSR/Src/plsr_job.c
new file mode 100644
index 0000000..138260f
--- /dev/null
+++ b/PLSR/Src/plsr_job.c
@@ -0,0 +1,1262 @@
+#include "plsr_job.h"
+#include "plc_device.h"
+#include "plsr_address_map.h"
+#include
+#include
+
+#define PLSR_S0_HEADER_WORDS (10UL)
+#define PLSR_S0_SEGMENT_WORDS (10UL)
+#define PLSR_S1_WORDS (4UL)
+#define PLSR_SFD_AXIS_STRIDE (130U)
+#define PLSR_SFD_SET_OFFSET (50U)
+#define PLSR_S2_SET_WORDS (20U)
+#define PLSR_HSD_SET_BASE (460U)
+#define PLSR_TIMER_168MHZ (168000000UL)
+#define PLSR_TIMER_84MHZ (84000000UL)
+
+static void PlsrSetDetail(PLSR_PARSE_DETAIL *detail,
+ PLSR_RESULT result,
+ PLSR_PARSE_BLOCK block,
+ uint32_t address,
+ int32_t value,
+ uint16_t segment)
+{
+ if (detail != NULL)
+ {
+ detail->result = result;
+ detail->block = block;
+ detail->address = address;
+ detail->value = value;
+ detail->segment = segment;
+ }
+}
+
+static uint8_t PlsrIsWordDevice(PLSR_DEVICE_TYPE device)
+{
+ return ((device == PLSR_DEVICE_D) || (device == PLSR_DEVICE_HD)
+ || (device == PLSR_DEVICE_FD))
+ ? 1U
+ : 0U;
+}
+
+static uint8_t PlsrSourceIsUsable(const PLSR_DATA_SOURCE *source)
+{
+ return ((source != NULL) && (source->validateWords != NULL)
+ && (source->readWord != NULL))
+ ? 1U
+ : 0U;
+}
+
+static PLSR_RESULT PlsrValidateRange(const PLSR_DATA_SOURCE *source,
+ PLSR_DATA_REF reference,
+ uint32_t wordCount,
+ PLSR_PARSE_BLOCK block,
+ PLSR_PARSE_DETAIL *detail)
+{
+ uint64_t endAddress;
+
+ if ((PlsrIsWordDevice(reference.device) == 0U) || (wordCount == 0UL))
+ {
+ PlsrSetDetail(detail,
+ PLSR_RESULT_DATA_ACCESS,
+ block,
+ reference.address,
+ (int32_t)reference.device,
+ 0U);
+ return PLSR_RESULT_DATA_ACCESS;
+ }
+ endAddress = (uint64_t)reference.address + (uint64_t)wordCount - 1ULL;
+ if (endAddress > UINT32_MAX)
+ {
+ PlsrSetDetail(detail,
+ PLSR_RESULT_ADDRESS_OVERFLOW,
+ block,
+ reference.address,
+ (int32_t)wordCount,
+ 0U);
+ return PLSR_RESULT_ADDRESS_OVERFLOW;
+ }
+ if (source->validateWords(source->context,
+ reference.device,
+ reference.address,
+ wordCount)
+ == 0U)
+ {
+ PlsrSetDetail(detail,
+ PLSR_RESULT_DATA_ACCESS,
+ block,
+ reference.address,
+ (int32_t)wordCount,
+ 0U);
+ return PLSR_RESULT_DATA_ACCESS;
+ }
+ return PLSR_RESULT_OK;
+}
+
+static PLSR_RESULT PlsrReadWord(const PLSR_DATA_SOURCE *source,
+ PLSR_DEVICE_TYPE device,
+ uint32_t address,
+ uint16_t *value,
+ PLSR_PARSE_BLOCK block,
+ uint16_t segment,
+ PLSR_PARSE_DETAIL *detail)
+{
+ if ((value == NULL)
+ || (source->readWord(source->context, device, address, value) == 0U))
+ {
+ PlsrSetDetail(detail,
+ PLSR_RESULT_DATA_ACCESS,
+ block,
+ address,
+ 0,
+ segment);
+ return PLSR_RESULT_DATA_ACCESS;
+ }
+ return PLSR_RESULT_OK;
+}
+
+static PLSR_RESULT PlsrReadInt32(const PLSR_DATA_SOURCE *source,
+ PLSR_DEVICE_TYPE device,
+ uint32_t address,
+ int32_t *value,
+ PLSR_PARSE_BLOCK block,
+ uint16_t segment,
+ PLSR_PARSE_DETAIL *detail)
+{
+ uint16_t lowWord;
+ uint16_t highWord;
+ PLSR_RESULT result;
+
+ if (address == UINT32_MAX)
+ {
+ PlsrSetDetail(detail,
+ PLSR_RESULT_ADDRESS_OVERFLOW,
+ block,
+ address,
+ 2,
+ segment);
+ return PLSR_RESULT_ADDRESS_OVERFLOW;
+ }
+ result = PlsrReadWord(source,
+ device,
+ address,
+ &lowWord,
+ block,
+ segment,
+ detail);
+ if (result != PLSR_RESULT_OK)
+ {
+ return result;
+ }
+ result = PlsrReadWord(source,
+ device,
+ address + 1UL,
+ &highWord,
+ block,
+ segment,
+ detail);
+ if (result != PLSR_RESULT_OK)
+ {
+ return result;
+ }
+ *value = (int32_t)(((uint32_t)highWord << 16U) | (uint32_t)lowWord);
+ return PLSR_RESULT_OK;
+}
+
+static uint8_t PlsrBlocksOverlap(PLSR_DATA_REF first,
+ uint32_t firstWords,
+ PLSR_DATA_REF second,
+ uint32_t secondWords)
+{
+ uint64_t firstEnd;
+ uint64_t secondEnd;
+
+ if (first.device != second.device)
+ {
+ return 0U;
+ }
+ firstEnd = (uint64_t)first.address + (uint64_t)firstWords - 1ULL;
+ secondEnd = (uint64_t)second.address + (uint64_t)secondWords - 1ULL;
+ return (((uint64_t)first.address <= secondEnd)
+ && ((uint64_t)second.address <= firstEnd))
+ ? 1U
+ : 0U;
+}
+
+static PLSR_RESULT PlsrReadFixedWord(uint8_t useHsd,
+ uint16_t address,
+ uint16_t *value,
+ PLSR_PARSE_DETAIL *detail)
+{
+ int32_t rawValue = 0;
+ PLC_DEVICE_RESULT deviceResult;
+
+ deviceResult = (useHsd != 0U) ? PlcDeviceReadHsd(address, &rawValue)
+ : PlcDeviceReadSfd(address, &rawValue);
+ if ((deviceResult != PLC_DEVICE_OK) || (rawValue < 0)
+ || (rawValue > (int32_t)UINT16_MAX))
+ {
+ PlsrSetDetail(detail,
+ PLSR_RESULT_INVALID_S2,
+ PLSR_PARSE_BLOCK_S2,
+ address,
+ rawValue,
+ 0U);
+ return PLSR_RESULT_INVALID_S2;
+ }
+ *value = (uint16_t)rawValue;
+ return PLSR_RESULT_OK;
+}
+
+static PLSR_RESULT PlsrReadFixedDword(uint8_t useHsd,
+ uint16_t address,
+ uint32_t *value,
+ PLSR_PARSE_DETAIL *detail)
+{
+ uint16_t lowWord;
+ uint16_t highWord;
+ PLSR_RESULT result;
+
+ result = PlsrReadFixedWord(useHsd, address, &lowWord, detail);
+ if (result != PLSR_RESULT_OK)
+ {
+ return result;
+ }
+ result = PlsrReadFixedWord(useHsd,
+ (uint16_t)(address + 1U),
+ &highWord,
+ detail);
+ if (result != PLSR_RESULT_OK)
+ {
+ return result;
+ }
+ *value = ((uint32_t)highWord << 16U) | (uint32_t)lowWord;
+ return PLSR_RESULT_OK;
+}
+
+PLSR_RESULT PlsrCalculateTimerDivider(uint32_t timerClockHz,
+ uint32_t frequencyHz,
+ uint16_t *psc,
+ uint16_t *arr)
+{
+ uint64_t minimumDivider;
+ uint64_t periodTicks;
+
+ if ((timerClockHz == 0UL) || (frequencyHz == 0UL) || (psc == NULL)
+ || (arr == NULL))
+ {
+ return PLSR_RESULT_INVALID_ARGUMENT;
+ }
+
+ minimumDivider = ((uint64_t)timerClockHz
+ + ((uint64_t)frequencyHz * 65536ULL) - 1ULL)
+ / ((uint64_t)frequencyHz * 65536ULL);
+ if (minimumDivider == 0ULL)
+ {
+ minimumDivider = 1ULL;
+ }
+ if (minimumDivider > 65536ULL)
+ {
+ return PLSR_RESULT_DIVIDER_UNREPRESENTABLE;
+ }
+ periodTicks = ((uint64_t)timerClockHz
+ + ((uint64_t)frequencyHz * minimumDivider) / 2ULL)
+ / ((uint64_t)frequencyHz * minimumDivider);
+ if ((periodTicks < 2ULL) || (periodTicks > 65536ULL))
+ {
+ return PLSR_RESULT_DIVIDER_UNREPRESENTABLE;
+ }
+ *psc = (uint16_t)(minimumDivider - 1ULL);
+ *arr = (uint16_t)(periodTicks - 1ULL);
+ return PLSR_RESULT_OK;
+}
+
+static PLSR_RESULT PlsrValidateFrequencyDivider(
+ const PLSR_JOB_SNAPSHOT *snapshot,
+ uint32_t frequency,
+ PLSR_PARSE_DETAIL *detail,
+ uint16_t segment)
+{
+ uint16_t psc;
+ uint16_t arr;
+ PLSR_RESULT result;
+
+ result = PlsrCalculateTimerDivider(snapshot->timerClockHz,
+ frequency,
+ &psc,
+ &arr);
+ if ((result == PLSR_RESULT_OK)
+ && (snapshot->pairedTimerClockHz != 0UL))
+ {
+ result = PlsrCalculateTimerDivider(snapshot->pairedTimerClockHz,
+ frequency,
+ &psc,
+ &arr);
+ }
+ if (result != PLSR_RESULT_OK)
+ {
+ PlsrSetDetail(detail,
+ PLSR_RESULT_DIVIDER_UNREPRESENTABLE,
+ PLSR_PARSE_BLOCK_S0,
+ snapshot->s0.address
+ + (uint32_t)segment * PLSR_S0_SEGMENT_WORDS,
+ (int32_t)frequency,
+ segment);
+ return PLSR_RESULT_DIVIDER_UNREPRESENTABLE;
+ }
+ return PLSR_RESULT_OK;
+}
+
+static PLSR_RESULT PlsrResolveOperand(const PLSR_CALL *call,
+ const PLSR_VALUE_OPERAND *operand,
+ int32_t *value,
+ PLSR_PARSE_DETAIL *detail)
+{
+ PLSR_RESULT result;
+
+ if (operand->type == PLSR_OPERAND_CONSTANT)
+ {
+ *value = operand->constant;
+ return PLSR_RESULT_OK;
+ }
+ if ((operand->type != PLSR_OPERAND_DATA)
+ || (PlsrIsWordDevice(operand->data.device) == 0U))
+ {
+ PlsrSetDetail(detail,
+ PLSR_RESULT_INVALID_S2,
+ PLSR_PARSE_BLOCK_S2,
+ operand->data.address,
+ (int32_t)operand->type,
+ 0U);
+ return PLSR_RESULT_INVALID_S2;
+ }
+ result = PlsrValidateRange(&call->source,
+ operand->data,
+ 2UL,
+ PLSR_PARSE_BLOCK_S2,
+ detail);
+ if (result != PLSR_RESULT_OK)
+ {
+ return result;
+ }
+ return PlsrReadInt32(&call->source,
+ operand->data.device,
+ operand->data.address,
+ value,
+ PLSR_PARSE_BLOCK_S2,
+ 0U,
+ detail);
+}
+
+static PLSR_RESULT PlsrLoadS2(const PLSR_CALL *call,
+ PLSR_JOB_SNAPSHOT *snapshot,
+ PLSR_PARSE_DETAIL *detail)
+{
+ uint16_t commonBase;
+ uint16_t setBase;
+ uint16_t word;
+ int32_t selectedSet;
+ uint8_t useHsd;
+ PLSR_RESULT result;
+
+ result = PlsrResolveOperand(call, &call->s2, &selectedSet, detail);
+ if (result != PLSR_RESULT_OK)
+ {
+ return result;
+ }
+ if ((selectedSet < 0) || (selectedSet > 4))
+ {
+ PlsrSetDetail(detail,
+ PLSR_RESULT_INVALID_S2,
+ PLSR_PARSE_BLOCK_S2,
+ call->s2.data.address,
+ selectedSet,
+ 0U);
+ return PLSR_RESULT_INVALID_S2;
+ }
+ snapshot->s2Set = (uint8_t)selectedSet;
+ commonBase = (uint16_t)(PLSR_SFD_CONFIG_START
+ + (uint16_t)call->dAxis
+ * PLSR_SFD_AXIS_STRIDE);
+ if (snapshot->s2Set == 0U)
+ {
+ useHsd = 1U;
+ setBase = (uint16_t)(PLSR_HSD_SET_BASE
+ + (uint16_t)call->dAxis * PLSR_S2_SET_WORDS);
+ }
+ else
+ {
+ useHsd = 0U;
+ setBase = (uint16_t)(commonBase + PLSR_SFD_SET_OFFSET
+ + (uint16_t)(snapshot->s2Set - 1U)
+ * PLSR_S2_SET_WORDS);
+ }
+
+ result = PlsrReadFixedDword(useHsd,
+ setBase,
+ &snapshot->s2.defaultSpeed,
+ detail);
+ if (result != PLSR_RESULT_OK) return result;
+ result = PlsrReadFixedWord(useHsd,
+ (uint16_t)(setBase + 2U),
+ &snapshot->s2.accelerationMs,
+ detail);
+ if (result != PLSR_RESULT_OK) return result;
+ result = PlsrReadFixedWord(useHsd,
+ (uint16_t)(setBase + 3U),
+ &snapshot->s2.decelerationMs,
+ detail);
+ if (result != PLSR_RESULT_OK) return result;
+ result = PlsrReadFixedWord(useHsd,
+ (uint16_t)(setBase + 4U),
+ &snapshot->s2.gapAccelerationMs,
+ detail);
+ if (result != PLSR_RESULT_OK) return result;
+ result = PlsrReadFixedWord(useHsd,
+ (uint16_t)(setBase + 5U),
+ &word,
+ detail);
+ if (result != PLSR_RESULT_OK) return result;
+ snapshot->s2.curveMode = (uint8_t)(word & 0x03U);
+ result = PlsrReadFixedDword(useHsd,
+ (uint16_t)(setBase + 6U),
+ &snapshot->s2.maximumSpeed,
+ detail);
+ if (result != PLSR_RESULT_OK) return result;
+ result = PlsrReadFixedDword(useHsd,
+ (uint16_t)(setBase + 8U),
+ &snapshot->s2.startSpeed,
+ detail);
+ if (result != PLSR_RESULT_OK) return result;
+ result = PlsrReadFixedDword(useHsd,
+ (uint16_t)(setBase + 10U),
+ &snapshot->s2.stopSpeed,
+ detail);
+ if (result != PLSR_RESULT_OK) return result;
+ result = PlsrReadFixedWord(useHsd,
+ (uint16_t)(setBase + 12U),
+ &word,
+ detail);
+ if (result != PLSR_RESULT_OK) return result;
+ if ((word < 1U) || (word > 100U))
+ {
+ PlsrSetDetail(detail,
+ PLSR_RESULT_INVALID_S2,
+ PLSR_PARSE_BLOCK_S2,
+ (uint16_t)(setBase + 12U),
+ word,
+ 0U);
+ return PLSR_RESULT_INVALID_S2;
+ }
+ snapshot->s2.follow = (uint8_t)word;
+ result = PlsrReadFixedWord(useHsd,
+ (uint16_t)(setBase + 13U),
+ &word,
+ detail);
+ if (result != PLSR_RESULT_OK) return result;
+ if (word > 100U)
+ {
+ PlsrSetDetail(detail,
+ PLSR_RESULT_INVALID_S2,
+ PLSR_PARSE_BLOCK_S2,
+ (uint16_t)(setBase + 13U),
+ word,
+ 0U);
+ return PLSR_RESULT_INVALID_S2;
+ }
+ snapshot->s2.feedforwardPercent = (uint8_t)word;
+ result = PlsrReadFixedWord(useHsd,
+ (uint16_t)(setBase + 14U),
+ &word,
+ detail);
+ if (result != PLSR_RESULT_OK) return result;
+ snapshot->s2.refreshCode = (uint8_t)(word & 0x03U);
+ result = PlsrReadFixedDword(useHsd,
+ (uint16_t)(setBase + 16U),
+ &snapshot->s2.zrnHighSpeed,
+ detail);
+ if (result != PLSR_RESULT_OK) return result;
+ result = PlsrReadFixedDword(useHsd,
+ (uint16_t)(setBase + 18U),
+ &snapshot->s2.zrnCrawlSpeed,
+ detail);
+ if (result != PLSR_RESULT_OK) return result;
+
+ result = PlsrReadFixedWord(0U, commonBase, &word, detail);
+ if (result != PLSR_RESULT_OK) return result;
+ snapshot->directionActiveHigh = ((word & (1U << 1U)) != 0U) ? 1U : 0U;
+ if (call->outputModeOverride == PLSR_OUTPUT_MODE_FROM_SFD)
+ {
+ snapshot->outputMode = ((word & (1U << 13U)) != 0U)
+ ? (uint8_t)PLSR_OUTPUT_AB
+ : (uint8_t)PLSR_OUTPUT_PULSE_DIR;
+ }
+ else if (call->outputModeOverride <= (uint8_t)PLSR_OUTPUT_CW_CCW)
+ {
+ snapshot->outputMode = call->outputModeOverride;
+ }
+ else
+ {
+ PlsrSetDetail(detail,
+ PLSR_RESULT_INVALID_RESOURCE,
+ PLSR_PARSE_BLOCK_OUTPUT,
+ commonBase,
+ call->outputModeOverride,
+ 0U);
+ return PLSR_RESULT_INVALID_RESOURCE;
+ }
+ result = PlsrReadFixedWord(0U,
+ (uint16_t)(commonBase + 6U),
+ &word,
+ detail);
+ if (result != PLSR_RESULT_OK) return result;
+ if (word > UINT8_MAX)
+ {
+ PlsrSetDetail(detail,
+ PLSR_RESULT_INVALID_RESOURCE,
+ PLSR_PARSE_BLOCK_OUTPUT,
+ (uint16_t)(commonBase + 6U),
+ word,
+ 0U);
+ return PLSR_RESULT_INVALID_RESOURCE;
+ }
+ snapshot->directionPoint = (uint8_t)word;
+ result = PlsrReadFixedWord(0U,
+ (uint16_t)(commonBase + 7U),
+ &snapshot->s2.directionDelayUs,
+ detail);
+ if (result != PLSR_RESULT_OK) return result;
+
+ if ((snapshot->s2.maximumSpeed < PLSR_FREQUENCY_MIN_HZ)
+ || (snapshot->s2.maximumSpeed > PLSR_FREQUENCY_MAX_HZ)
+ || (snapshot->s2.curveMode > 2U) || (snapshot->s2.follow < 1U)
+ || (snapshot->s2.follow > 100U)
+ || (snapshot->s2.feedforwardPercent > 100U)
+ || ((snapshot->s2.refreshCode != 0U)
+ && (snapshot->s2.refreshCode != 2U)))
+ {
+ PlsrSetDetail(detail,
+ PLSR_RESULT_INVALID_S2,
+ PLSR_PARSE_BLOCK_S2,
+ setBase,
+ (int32_t)snapshot->s2.maximumSpeed,
+ 0U);
+ return PLSR_RESULT_INVALID_S2;
+ }
+ if (snapshot->s2.startSpeed > snapshot->s2.maximumSpeed)
+ {
+ snapshot->s2.startSpeed = snapshot->s2.maximumSpeed;
+ snapshot->speedClamped = 1U;
+ }
+ if (snapshot->s2.stopSpeed > snapshot->s2.maximumSpeed)
+ {
+ snapshot->s2.stopSpeed = snapshot->s2.maximumSpeed;
+ snapshot->speedClamped = 1U;
+ }
+ return PLSR_RESULT_OK;
+}
+
+static PLSR_RESULT PlsrValidateVariableReference(
+ const PLSR_CALL *call,
+ uint8_t sourceCode,
+ int32_t addressValue,
+ uint8_t bitReference,
+ int32_t *currentValue,
+ PLSR_PARSE_BLOCK block,
+ uint16_t segment,
+ PLSR_PARSE_DETAIL *detail)
+{
+ PLSR_DEVICE_TYPE device;
+ PLSR_DATA_REF reference;
+ uint8_t bitValue;
+ PLSR_RESULT result;
+
+ if ((sourceCode == 0U) || (sourceCode > 6U) || (addressValue < 0))
+ {
+ PlsrSetDetail(detail,
+ (block == PLSR_PARSE_BLOCK_S0)
+ ? PLSR_RESULT_INVALID_WAIT
+ : PLSR_RESULT_DATA_ACCESS,
+ block,
+ (uint32_t)addressValue,
+ sourceCode,
+ segment);
+ return (block == PLSR_PARSE_BLOCK_S0) ? PLSR_RESULT_INVALID_WAIT
+ : PLSR_RESULT_DATA_ACCESS;
+ }
+ device = (PLSR_DEVICE_TYPE)(sourceCode - 1U);
+ if (bitReference != 0U)
+ {
+ if ((device < PLSR_DEVICE_X) || (call->source.readBit == NULL)
+ || (call->source.readBit(call->source.context,
+ device,
+ (uint32_t)addressValue,
+ &bitValue)
+ == 0U))
+ {
+ PlsrSetDetail(detail,
+ PLSR_RESULT_INVALID_WAIT,
+ block,
+ (uint32_t)addressValue,
+ sourceCode,
+ segment);
+ return PLSR_RESULT_INVALID_WAIT;
+ }
+ *currentValue = (int32_t)bitValue;
+ return PLSR_RESULT_OK;
+ }
+
+ if (PlsrIsWordDevice(device) == 0U)
+ {
+ PlsrSetDetail(detail,
+ PLSR_RESULT_INVALID_WAIT,
+ block,
+ (uint32_t)addressValue,
+ sourceCode,
+ segment);
+ return PLSR_RESULT_INVALID_WAIT;
+ }
+ reference.device = device;
+ reference.address = (uint32_t)addressValue;
+ result = PlsrValidateRange(&call->source,
+ reference,
+ 2UL,
+ block,
+ detail);
+ if (result != PLSR_RESULT_OK)
+ {
+ return result;
+ }
+ return PlsrReadInt32(&call->source,
+ device,
+ reference.address,
+ currentValue,
+ block,
+ segment,
+ detail);
+}
+
+static PLSR_RESULT PlsrValidateWait(const PLSR_CALL *call,
+ PLSR_SEGMENT_SNAPSHOT *segmentData,
+ uint16_t segment,
+ PLSR_PARSE_DETAIL *detail)
+{
+ int32_t currentValue;
+ PLSR_RESULT result;
+
+ if ((segmentData->waitCondition > PLSR_WAIT_EXT_OR_COMPLETE)
+ || (segmentData->waitSource > PLSR_VALUE_HM))
+ {
+ goto invalidWait;
+ }
+ if (segmentData->waitCondition == PLSR_WAIT_PULSE_COMPLETE)
+ {
+ if (segmentData->waitSource != PLSR_VALUE_CONSTANT)
+ {
+ goto invalidWait;
+ }
+ return PLSR_RESULT_OK;
+ }
+ if ((segmentData->waitCondition == PLSR_WAIT_TIME)
+ || (segmentData->waitCondition == PLSR_WAIT_ACT_TIME))
+ {
+ if (segmentData->waitSource == PLSR_VALUE_CONSTANT)
+ {
+ currentValue = segmentData->waitValueOrAddress;
+ }
+ else
+ {
+ if (segmentData->waitSource > PLSR_VALUE_FD)
+ {
+ goto invalidWait;
+ }
+ result = PlsrValidateVariableReference(call,
+ segmentData->waitSource,
+ segmentData->waitValueOrAddress,
+ 0U,
+ ¤tValue,
+ PLSR_PARSE_BLOCK_S0,
+ segment,
+ detail);
+ if (result != PLSR_RESULT_OK) return result;
+ }
+ if (currentValue < 0) goto invalidWait;
+ return PLSR_RESULT_OK;
+ }
+ if (segmentData->waitCondition == PLSR_WAIT_SIGNAL)
+ {
+ if ((segmentData->waitSource < PLSR_VALUE_X)
+ || (segmentData->waitSource > PLSR_VALUE_HM))
+ {
+ goto invalidWait;
+ }
+ }
+ else if ((segmentData->waitCondition == PLSR_WAIT_EXT)
+ || (segmentData->waitCondition == PLSR_WAIT_EXT_OR_COMPLETE))
+ {
+ if (segmentData->waitSource != PLSR_VALUE_X)
+ {
+ goto invalidWait;
+ }
+ }
+ result = PlsrValidateVariableReference(call,
+ segmentData->waitSource,
+ segmentData->waitValueOrAddress,
+ 1U,
+ ¤tValue,
+ PLSR_PARSE_BLOCK_S0,
+ segment,
+ detail);
+ return result;
+
+invalidWait:
+ PlsrSetDetail(detail,
+ PLSR_RESULT_INVALID_WAIT,
+ PLSR_PARSE_BLOCK_S0,
+ call->s0.address
+ + (uint32_t)segment * PLSR_S0_SEGMENT_WORDS + 4UL,
+ ((int32_t)segmentData->waitCondition << 8)
+ | segmentData->waitSource,
+ segment);
+ return PLSR_RESULT_INVALID_WAIT;
+}
+
+static PLSR_RESULT PlsrValidateJump(const PLSR_CALL *call,
+ const PLSR_JOB_SNAPSHOT *snapshot,
+ PLSR_SEGMENT_SNAPSHOT *segmentData,
+ uint16_t segment,
+ int32_t *resolvedJump,
+ PLSR_PARSE_DETAIL *detail)
+{
+ PLSR_RESULT result;
+
+ if (segmentData->jumpSource > PLSR_VALUE_FD)
+ {
+ goto invalidJump;
+ }
+ if (segmentData->jumpSource == PLSR_VALUE_CONSTANT)
+ {
+ *resolvedJump = segmentData->jumpValueOrAddress;
+ }
+ else
+ {
+ result = PlsrValidateVariableReference(call,
+ segmentData->jumpSource,
+ segmentData->jumpValueOrAddress,
+ 0U,
+ resolvedJump,
+ PLSR_PARSE_BLOCK_NONE,
+ segment,
+ detail);
+ if (result != PLSR_RESULT_OK)
+ {
+ PlsrSetDetail(detail,
+ PLSR_RESULT_INVALID_JUMP,
+ PLSR_PARSE_BLOCK_S0,
+ (uint32_t)segmentData->jumpValueOrAddress,
+ segmentData->jumpSource,
+ segment);
+ return PLSR_RESULT_INVALID_JUMP;
+ }
+ }
+ if ((*resolvedJump < 0)
+ || (*resolvedJump > (int32_t)snapshot->segmentCount))
+ {
+ goto invalidJump;
+ }
+ if (*resolvedJump == (int32_t)segment)
+ {
+ segmentData->flags |= PLSR_SEGMENT_FLAG_SELF_LOOP;
+ }
+ return PLSR_RESULT_OK;
+
+invalidJump:
+ PlsrSetDetail(detail,
+ PLSR_RESULT_INVALID_JUMP,
+ PLSR_PARSE_BLOCK_S0,
+ call->s0.address
+ + (uint32_t)segment * PLSR_S0_SEGMENT_WORDS + 7UL,
+ *resolvedJump,
+ segment);
+ return PLSR_RESULT_INVALID_JUMP;
+}
+
+static PLSR_RESULT PlsrCheckConstantPath(PLSR_JOB_SNAPSHOT *snapshot,
+ PLSR_PARSE_DETAIL *detail)
+{
+ uint8_t visited[PLSR_MAX_SEGMENTS];
+ uint16_t segment = snapshot->startSegment;
+ int32_t next;
+
+ (void)memset(visited, 0, sizeof(visited));
+ while ((segment >= 1U) && (segment <= snapshot->segmentCount))
+ {
+ if (visited[segment - 1U] != 0U)
+ {
+ PlsrSetDetail(detail,
+ PLSR_RESULT_PATH_CYCLE,
+ PLSR_PARSE_BLOCK_S0,
+ snapshot->s0.address
+ + (uint32_t)segment * PLSR_S0_SEGMENT_WORDS
+ + 8UL,
+ segment,
+ segment);
+ return PLSR_RESULT_PATH_CYCLE;
+ }
+ visited[segment - 1U] = 1U;
+ if (snapshot->segments[segment - 1U].jumpSource
+ != PLSR_VALUE_CONSTANT)
+ {
+ return PLSR_RESULT_OK;
+ }
+ next = snapshot->segments[segment - 1U].jumpValueOrAddress;
+ if (next == (int32_t)segment)
+ {
+ snapshot->hasSelfLoop = 1U;
+ return PLSR_RESULT_OK;
+ }
+ if (next == 0)
+ {
+ if (segment == snapshot->segmentCount)
+ {
+ return PLSR_RESULT_OK;
+ }
+ segment++;
+ }
+ else
+ {
+ segment = (uint16_t)next;
+ }
+ }
+ return PLSR_RESULT_OK;
+}
+
+static PLSR_RESULT PlsrCheckInitialPosition(
+ PLSR_JOB_SNAPSHOT *snapshot,
+ const PLSR_PARSE_CONTEXT *context,
+ PLSR_PARSE_DETAIL *detail)
+{
+ uint8_t visited[PLSR_MAX_SEGMENTS];
+ int64_t position = context->logicalPosition;
+ int32_t movement;
+ int32_t next;
+ uint16_t segment = snapshot->startSegment;
+
+ if ((snapshot->positioningMode != 0U) && (context->positionValid == 0U))
+ {
+ PlsrSetDetail(detail,
+ PLSR_RESULT_POSITION_INVALID,
+ PLSR_PARSE_BLOCK_POSITION,
+ snapshot->s1.address,
+ 1,
+ 0U);
+ return PLSR_RESULT_POSITION_INVALID;
+ }
+
+ (void)memset(visited, 0, sizeof(visited));
+ while ((segment >= 1U) && (segment <= snapshot->segmentCount)
+ && (visited[segment - 1U] == 0U))
+ {
+ PLSR_SEGMENT_SNAPSHOT *segmentData = &snapshot->segments[segment - 1U];
+ visited[segment - 1U] = 1U;
+ if (snapshot->positioningMode == 0U)
+ {
+ movement = segmentData->pulseOrTarget;
+ if (((movement > 0) && (position > INT64_MAX - movement))
+ || ((movement < 0) && (position < INT64_MIN - movement)))
+ {
+ PlsrSetDetail(detail,
+ PLSR_RESULT_POSITION_OVERFLOW,
+ PLSR_PARSE_BLOCK_POSITION,
+ snapshot->s0.address
+ + (uint32_t)segment
+ * PLSR_S0_SEGMENT_WORDS
+ + 2UL,
+ movement,
+ segment);
+ return PLSR_RESULT_POSITION_OVERFLOW;
+ }
+ position += movement;
+ }
+ else
+ {
+ movement = (segmentData->pulseOrTarget > position)
+ ? 1
+ : ((segmentData->pulseOrTarget < position) ? -1 : 0);
+ position = segmentData->pulseOrTarget;
+ }
+ if ((movement != 0) && (snapshot->initialDirectionPositive == 0U))
+ {
+ snapshot->initialDirectionPositive = (movement > 0) ? 2U : 1U;
+ }
+ if (segmentData->jumpSource != PLSR_VALUE_CONSTANT)
+ {
+ break;
+ }
+ next = segmentData->jumpValueOrAddress;
+ if (next == (int32_t)segment)
+ {
+ break;
+ }
+ if (next == 0)
+ {
+ if (segment == snapshot->segmentCount) break;
+ segment++;
+ }
+ else
+ {
+ segment = (uint16_t)next;
+ }
+ }
+ snapshot->initialDirectionPositive =
+ (snapshot->initialDirectionPositive == 2U) ? 1U : 0U;
+ return PLSR_RESULT_OK;
+}
+
+PLSR_RESULT PlsrBuildJobSnapshot(const PLSR_CALL *call,
+ const PLSR_PARSE_CONTEXT *context,
+ PLSR_JOB_SNAPSHOT *snapshot,
+ PLSR_PARSE_DETAIL *detail)
+{
+ uint64_t s0Words64;
+ uint32_t s0Words;
+ uint32_t segmentBase;
+ int32_t intValue;
+ int32_t resolvedJump;
+ uint16_t word;
+ uint16_t segment;
+ uint8_t index;
+ PLSR_RESULT result;
+
+ if ((call == NULL) || (context == NULL) || (snapshot == NULL)
+ || (PlsrSourceIsUsable(&call->source) == 0U))
+ {
+ return PLSR_RESULT_INVALID_ARGUMENT;
+ }
+ if (call->dAxis >= PLSR_AXIS_COUNT)
+ {
+ return PLSR_RESULT_INVALID_AXIS;
+ }
+ (void)memset(snapshot, 0, sizeof(*snapshot));
+ if (detail != NULL) (void)memset(detail, 0, sizeof(*detail));
+ snapshot->source = call->source;
+ snapshot->s0 = call->s0;
+ snapshot->s1 = call->s1;
+ snapshot->dAxis = call->dAxis;
+ snapshot->timerClockHz = ((call->dAxis == 0U) || (call->dAxis == 2U))
+ ? PLSR_TIMER_168MHZ
+ : PLSR_TIMER_84MHZ;
+
+ result = PlsrValidateRange(&call->source,
+ call->s0,
+ PLSR_S0_HEADER_WORDS,
+ PLSR_PARSE_BLOCK_S0,
+ detail);
+ if (result != PLSR_RESULT_OK) return result;
+ result = PlsrReadInt32(&call->source,
+ call->s0.device,
+ call->s0.address,
+ &intValue,
+ PLSR_PARSE_BLOCK_S0,
+ 0U,
+ detail);
+ if ((result != PLSR_RESULT_OK) || (intValue < 1)
+ || (intValue > (int32_t)PLSR_MAX_SEGMENTS))
+ {
+ if (result == PLSR_RESULT_OK)
+ {
+ PlsrSetDetail(detail,
+ PLSR_RESULT_SEGMENT_OVERFLOW,
+ PLSR_PARSE_BLOCK_S0,
+ call->s0.address,
+ intValue,
+ 0U);
+ result = PLSR_RESULT_SEGMENT_OVERFLOW;
+ }
+ return result;
+ }
+ snapshot->segmentCount = (uint16_t)intValue;
+ s0Words64 = (uint64_t)snapshot->segmentCount * PLSR_S0_SEGMENT_WORDS
+ + PLSR_S0_HEADER_WORDS;
+ if (s0Words64 > UINT32_MAX)
+ {
+ return PLSR_RESULT_ADDRESS_OVERFLOW;
+ }
+ s0Words = (uint32_t)s0Words64;
+ result = PlsrValidateRange(&call->source,
+ call->s0,
+ s0Words,
+ PLSR_PARSE_BLOCK_S0,
+ detail);
+ if (result != PLSR_RESULT_OK) return result;
+ result = PlsrValidateRange(&call->source,
+ call->s1,
+ PLSR_S1_WORDS,
+ PLSR_PARSE_BLOCK_S1,
+ detail);
+ if (result != PLSR_RESULT_OK) return result;
+ if (PlsrBlocksOverlap(call->s0, s0Words, call->s1, PLSR_S1_WORDS) != 0U)
+ {
+ PlsrSetDetail(detail,
+ PLSR_RESULT_BLOCK_OVERLAP,
+ PLSR_PARSE_BLOCK_S1,
+ call->s1.address,
+ (int32_t)s0Words,
+ 0U);
+ return PLSR_RESULT_BLOCK_OVERLAP;
+ }
+ for (index = 2U; index < 10U; index++)
+ {
+ result = PlsrReadWord(&call->source,
+ call->s0.device,
+ call->s0.address + index,
+ &word,
+ PLSR_PARSE_BLOCK_S0,
+ 0U,
+ detail);
+ if (result != PLSR_RESULT_OK) return result;
+ if (word != 0U)
+ {
+ PlsrSetDetail(detail,
+ PLSR_RESULT_RESERVED_NOT_ZERO,
+ PLSR_PARSE_BLOCK_S0,
+ call->s0.address + index,
+ word,
+ 0U);
+ return PLSR_RESULT_RESERVED_NOT_ZERO;
+ }
+ }
+
+ result = PlsrReadInt32(&call->source,
+ call->s1.device,
+ call->s1.address,
+ &intValue,
+ PLSR_PARSE_BLOCK_S1,
+ 0U,
+ detail);
+ if (result != PLSR_RESULT_OK) return result;
+ if ((intValue < 0) || (intValue > 1))
+ {
+ PlsrSetDetail(detail,
+ PLSR_RESULT_INVALID_POSITION_MODE,
+ PLSR_PARSE_BLOCK_S1,
+ call->s1.address,
+ intValue,
+ 0U);
+ return PLSR_RESULT_INVALID_POSITION_MODE;
+ }
+ snapshot->positioningMode = (uint8_t)intValue;
+ result = PlsrReadInt32(&call->source,
+ call->s1.device,
+ call->s1.address + 2UL,
+ &intValue,
+ PLSR_PARSE_BLOCK_S1,
+ 0U,
+ detail);
+ if (result != PLSR_RESULT_OK) return result;
+ if ((intValue < 0) || (intValue > snapshot->segmentCount))
+ {
+ PlsrSetDetail(detail,
+ PLSR_RESULT_INVALID_ARGUMENT,
+ PLSR_PARSE_BLOCK_S1,
+ call->s1.address + 2UL,
+ intValue,
+ 0U);
+ return PLSR_RESULT_INVALID_ARGUMENT;
+ }
+ snapshot->startSegment = (intValue <= 1) ? 1U : (uint16_t)intValue;
+
+ result = PlsrLoadS2(call, snapshot, detail);
+ if (result != PLSR_RESULT_OK) return result;
+ if ((snapshot->outputMode == PLSR_OUTPUT_AB)
+ || (snapshot->outputMode == PLSR_OUTPUT_CW_CCW))
+ {
+ if ((call->dAxis != 0U) && (call->dAxis != 2U))
+ {
+ PlsrSetDetail(detail,
+ PLSR_RESULT_INVALID_RESOURCE,
+ PLSR_PARSE_BLOCK_OUTPUT,
+ call->dAxis,
+ snapshot->outputMode,
+ 0U);
+ return PLSR_RESULT_INVALID_RESOURCE;
+ }
+ snapshot->pairedTimerClockHz = PLSR_TIMER_84MHZ;
+ }
+
+ for (segment = 1U; segment <= snapshot->segmentCount; segment++)
+ {
+ PLSR_SEGMENT_SNAPSHOT *segmentData = &snapshot->segments[segment - 1U];
+ segmentBase = call->s0.address
+ + (uint32_t)segment * PLSR_S0_SEGMENT_WORDS;
+ result = PlsrReadInt32(&call->source,
+ call->s0.device,
+ segmentBase,
+ &intValue,
+ PLSR_PARSE_BLOCK_S0,
+ segment,
+ detail);
+ if (result != PLSR_RESULT_OK) return result;
+ if (intValue < 0)
+ {
+ PlsrSetDetail(detail,
+ PLSR_RESULT_INVALID_FREQUENCY,
+ PLSR_PARSE_BLOCK_S0,
+ segmentBase,
+ intValue,
+ segment);
+ return PLSR_RESULT_INVALID_FREQUENCY;
+ }
+ segmentData->targetFrequency = (uint32_t)intValue;
+ result = PlsrReadInt32(&call->source,
+ call->s0.device,
+ segmentBase + 2UL,
+ &segmentData->pulseOrTarget,
+ PLSR_PARSE_BLOCK_S0,
+ segment,
+ detail);
+ if (result != PLSR_RESULT_OK) return result;
+ result = PlsrReadWord(&call->source,
+ call->s0.device,
+ segmentBase + 4UL,
+ &word,
+ PLSR_PARSE_BLOCK_S0,
+ segment,
+ detail);
+ if (result != PLSR_RESULT_OK) return result;
+ segmentData->waitSource = (uint8_t)(word & 0xFFU);
+ segmentData->waitCondition = (uint8_t)(word >> 8U);
+ result = PlsrReadInt32(&call->source,
+ call->s0.device,
+ segmentBase + 5UL,
+ &segmentData->waitValueOrAddress,
+ PLSR_PARSE_BLOCK_S0,
+ segment,
+ detail);
+ if (result != PLSR_RESULT_OK) return result;
+ result = PlsrReadWord(&call->source,
+ call->s0.device,
+ segmentBase + 7UL,
+ &word,
+ PLSR_PARSE_BLOCK_S0,
+ segment,
+ detail);
+ if (result != PLSR_RESULT_OK) return result;
+ if ((word & 0xFF00U) != 0U)
+ {
+ PlsrSetDetail(detail,
+ PLSR_RESULT_RESERVED_NOT_ZERO,
+ PLSR_PARSE_BLOCK_S0,
+ segmentBase + 7UL,
+ word,
+ segment);
+ return PLSR_RESULT_RESERVED_NOT_ZERO;
+ }
+ segmentData->jumpSource = (uint8_t)(word & 0xFFU);
+ result = PlsrReadInt32(&call->source,
+ call->s0.device,
+ segmentBase + 8UL,
+ &segmentData->jumpValueOrAddress,
+ PLSR_PARSE_BLOCK_S0,
+ segment,
+ detail);
+ if (result != PLSR_RESULT_OK) return result;
+
+ if ((snapshot->positioningMode == 0U)
+ && (segmentData->pulseOrTarget == 0))
+ {
+ segmentData->flags |= PLSR_SEGMENT_FLAG_ZERO_PULSE;
+ segmentData->targetFrequency = 0UL;
+ }
+ else if (segmentData->targetFrequency == 0UL)
+ {
+ if (snapshot->s2.defaultSpeed == 0UL)
+ {
+ PlsrSetDetail(detail,
+ PLSR_RESULT_INVALID_FREQUENCY,
+ PLSR_PARSE_BLOCK_S2,
+ 0UL,
+ 0,
+ segment);
+ return PLSR_RESULT_INVALID_FREQUENCY;
+ }
+ segmentData->targetFrequency = snapshot->s2.defaultSpeed;
+ segmentData->flags |= PLSR_SEGMENT_FLAG_DEFAULT_SPEED;
+ }
+ if (segmentData->targetFrequency > snapshot->s2.maximumSpeed)
+ {
+ segmentData->targetFrequency = snapshot->s2.maximumSpeed;
+ segmentData->flags |= PLSR_SEGMENT_FLAG_SPEED_CLAMPED;
+ snapshot->speedClamped = 1U;
+ }
+ if (segmentData->targetFrequency != 0UL)
+ {
+ result = PlsrValidateFrequencyDivider(snapshot,
+ segmentData->targetFrequency,
+ detail,
+ segment);
+ if (result != PLSR_RESULT_OK) return result;
+ }
+ result = PlsrValidateWait(call, segmentData, segment, detail);
+ if (result != PLSR_RESULT_OK) return result;
+ result = PlsrValidateJump(call,
+ snapshot,
+ segmentData,
+ segment,
+ &resolvedJump,
+ detail);
+ if (result != PLSR_RESULT_OK) return result;
+ }
+
+ result = PlsrCheckConstantPath(snapshot, detail);
+ if (result != PLSR_RESULT_OK) return result;
+ result = PlsrCheckInitialPosition(snapshot, context, detail);
+ if (result != PLSR_RESULT_OK) return result;
+ if (detail != NULL) detail->result = PLSR_RESULT_OK;
+ return PLSR_RESULT_OK;
+}
+
+PLSR_RESULT PlsrResolveLiveFrequency(const PLSR_JOB_SNAPSHOT *snapshot,
+ uint16_t segment,
+ uint32_t *frequency,
+ uint8_t *clamped)
+{
+ int32_t rawFrequency;
+ uint32_t segmentAddress;
+ PLSR_RESULT result;
+
+ if ((snapshot == NULL) || (frequency == NULL) || (clamped == NULL)
+ || (segment < 1U) || (segment > snapshot->segmentCount))
+ {
+ return PLSR_RESULT_INVALID_ARGUMENT;
+ }
+ if ((snapshot->segments[segment - 1U].flags
+ & PLSR_SEGMENT_FLAG_ZERO_PULSE)
+ != 0U)
+ {
+ *frequency = 0UL;
+ *clamped = 0U;
+ return PLSR_RESULT_OK;
+ }
+ segmentAddress = snapshot->s0.address
+ + (uint32_t)segment * PLSR_S0_SEGMENT_WORDS;
+ result = PlsrReadInt32(&snapshot->source,
+ snapshot->s0.device,
+ segmentAddress,
+ &rawFrequency,
+ PLSR_PARSE_BLOCK_S0,
+ segment,
+ NULL);
+ if (result != PLSR_RESULT_OK) return result;
+ if (rawFrequency < 0) return PLSR_RESULT_INVALID_FREQUENCY;
+ *frequency = (rawFrequency == 0) ? snapshot->s2.defaultSpeed
+ : (uint32_t)rawFrequency;
+ *clamped = 0U;
+ if (*frequency > snapshot->s2.maximumSpeed)
+ {
+ *frequency = snapshot->s2.maximumSpeed;
+ *clamped = 1U;
+ }
+ if (*frequency == 0UL) return PLSR_RESULT_INVALID_FREQUENCY;
+ return PlsrValidateFrequencyDivider(snapshot, *frequency, NULL, segment);
+}
diff --git a/PLSR/Test/run_host_tests.ps1 b/PLSR/Test/run_host_tests.ps1
index 3432abb..f57beda 100644
--- a/PLSR/Test/run_host_tests.ps1
+++ b/PLSR/Test/run_host_tests.ps1
@@ -28,9 +28,21 @@ $tests = @(
"$workspacePath\PLSR\Src\plc_device.c"
"$workspacePath\PLSR\Src\plsr_persistence.c"
"$workspacePath\PLSR\Src\plsr_resource.c"
+ "$workspacePath\PLSR\Src\plsr_job.c"
"$workspacePath\PLSR\Src\plsr_core.c"
"$workspacePath\PLSR\Test\test_plsr_core.c"
)
+ },
+ @{
+ Name = 'test_plsr_job'
+ Sources = @(
+ "$workspacePath\PLSR\Src\plc_device.c"
+ "$workspacePath\PLSR\Src\plsr_persistence.c"
+ "$workspacePath\PLSR\Src\plsr_resource.c"
+ "$workspacePath\PLSR\Src\plsr_job.c"
+ "$workspacePath\PLSR\Src\plsr_core.c"
+ "$workspacePath\PLSR\Test\test_plsr_job.c"
+ )
}
)
diff --git a/PLSR/Test/test_plsr_job.c b/PLSR/Test/test_plsr_job.c
new file mode 100644
index 0000000..ef0c5f7
--- /dev/null
+++ b/PLSR/Test/test_plsr_job.c
@@ -0,0 +1,410 @@
+#include "plc_device.h"
+#include "plsr_core.h"
+#include "plsr_job.h"
+#include "plsr_persistence.h"
+#include
+#include
+
+#define TEST_WORD_CAPACITY (3000U)
+#define TEST_BIT_CAPACITY (128U)
+
+typedef struct
+{
+ uint16_t words[3][TEST_WORD_CAPACITY];
+ uint8_t bits[3][TEST_BIT_CAPACITY];
+} TEST_MEMORY;
+
+static int TestFailures;
+static int TestChecks;
+
+#define CHECK(condition) \
+ do \
+ { \
+ TestChecks++; \
+ if (!(condition)) \
+ { \
+ TestFailures++; \
+ (void)printf("FAIL line %d: %s\n", __LINE__, #condition); \
+ } \
+ } while (0)
+
+static uint8_t TestValidateWords(void *context,
+ PLSR_DEVICE_TYPE device,
+ uint32_t firstAddress,
+ uint32_t wordCount)
+{
+ (void)context;
+ if ((device > PLSR_DEVICE_FD) || (wordCount == 0UL))
+ {
+ return 0U;
+ }
+ return (((uint64_t)firstAddress + wordCount) <= TEST_WORD_CAPACITY)
+ ? 1U
+ : 0U;
+}
+
+static uint8_t TestReadWord(void *context,
+ PLSR_DEVICE_TYPE device,
+ uint32_t address,
+ uint16_t *value)
+{
+ TEST_MEMORY *memory = (TEST_MEMORY *)context;
+
+ if ((memory == NULL) || (value == NULL) || (device > PLSR_DEVICE_FD)
+ || (address >= TEST_WORD_CAPACITY))
+ {
+ return 0U;
+ }
+ *value = memory->words[device][address];
+ return 1U;
+}
+
+static uint8_t TestReadBit(void *context,
+ PLSR_DEVICE_TYPE device,
+ uint32_t address,
+ uint8_t *value)
+{
+ TEST_MEMORY *memory = (TEST_MEMORY *)context;
+ uint8_t index;
+
+ if ((memory == NULL) || (value == NULL) || (device < PLSR_DEVICE_X)
+ || (device > PLSR_DEVICE_HM) || (address >= TEST_BIT_CAPACITY))
+ {
+ return 0U;
+ }
+ index = (uint8_t)(device - PLSR_DEVICE_X);
+ *value = memory->bits[index][address];
+ return 1U;
+}
+
+static void TestWriteDword(TEST_MEMORY *memory,
+ PLSR_DEVICE_TYPE device,
+ uint32_t address,
+ int32_t value)
+{
+ uint32_t raw = (uint32_t)value;
+
+ memory->words[device][address] = (uint16_t)(raw & 0xFFFFUL);
+ memory->words[device][address + 1UL] = (uint16_t)(raw >> 16U);
+}
+
+static void TestWriteSfdDword(uint16_t address, uint32_t value)
+{
+ CHECK(PlcDeviceWriteSfd(address, (int32_t)(value & 0xFFFFUL))
+ == PLC_DEVICE_OK);
+ CHECK(PlcDeviceWriteSfd((uint16_t)(address + 1U),
+ (int32_t)(value >> 16U))
+ == PLC_DEVICE_OK);
+}
+
+static void TestConfigureAxis0K1(void)
+{
+ CHECK(PlcDeviceWriteSfd(900U, 0) == PLC_DEVICE_OK);
+ CHECK(PlcDeviceWriteSfd(906U, 4) == PLC_DEVICE_OK);
+ CHECK(PlcDeviceWriteSfd(907U, 10) == PLC_DEVICE_OK);
+ TestWriteSfdDword(950U, 1000UL);
+ CHECK(PlcDeviceWriteSfd(952U, 100) == PLC_DEVICE_OK);
+ CHECK(PlcDeviceWriteSfd(953U, 120) == PLC_DEVICE_OK);
+ CHECK(PlcDeviceWriteSfd(954U, 10) == PLC_DEVICE_OK);
+ CHECK(PlcDeviceWriteSfd(955U, 0) == PLC_DEVICE_OK);
+ TestWriteSfdDword(956U, 100000UL);
+ TestWriteSfdDword(958U, 120000UL);
+ TestWriteSfdDword(960U, 500UL);
+ CHECK(PlcDeviceWriteSfd(962U, 50) == PLC_DEVICE_OK);
+ CHECK(PlcDeviceWriteSfd(963U, 20) == PLC_DEVICE_OK);
+ CHECK(PlcDeviceWriteSfd(964U, 0) == PLC_DEVICE_OK);
+ TestWriteSfdDword(966U, 2000UL);
+ TestWriteSfdDword(968U, 200UL);
+}
+
+static void TestBuildValidBlocks(TEST_MEMORY *memory)
+{
+ (void)memset(memory, 0, sizeof(*memory));
+ TestWriteDword(memory, PLSR_DEVICE_D, 100U, 2);
+
+ TestWriteDword(memory, PLSR_DEVICE_D, 110U, 150000);
+ TestWriteDword(memory, PLSR_DEVICE_D, 112U, 100);
+ memory->words[PLSR_DEVICE_D][114U] = 0U;
+ TestWriteDword(memory, PLSR_DEVICE_D, 115U, 0);
+ memory->words[PLSR_DEVICE_D][117U] = 0U;
+ TestWriteDword(memory, PLSR_DEVICE_D, 118U, 0);
+
+ TestWriteDword(memory, PLSR_DEVICE_D, 120U, 0);
+ TestWriteDword(memory, PLSR_DEVICE_D, 122U, -50);
+ memory->words[PLSR_DEVICE_D][124U] = 0x0100U;
+ TestWriteDword(memory, PLSR_DEVICE_D, 125U, 0);
+ memory->words[PLSR_DEVICE_D][127U] = 0U;
+ TestWriteDword(memory, PLSR_DEVICE_D, 128U, 2);
+
+ TestWriteDword(memory, PLSR_DEVICE_D, 200U, 0);
+ TestWriteDword(memory, PLSR_DEVICE_D, 202U, 0);
+ TestWriteDword(memory, PLSR_DEVICE_HD, 10U, 1);
+}
+
+static PLSR_CALL TestMakeCall(TEST_MEMORY *memory)
+{
+ PLSR_CALL call;
+
+ (void)memset(&call, 0, sizeof(call));
+ call.sequence = 77UL;
+ call.source.context = memory;
+ call.source.validateWords = TestValidateWords;
+ call.source.readWord = TestReadWord;
+ call.source.readBit = TestReadBit;
+ call.s0.device = PLSR_DEVICE_D;
+ call.s0.address = 100UL;
+ call.s1.device = PLSR_DEVICE_D;
+ call.s1.address = 200UL;
+ call.s2.type = PLSR_OPERAND_CONSTANT;
+ call.s2.constant = 1;
+ call.dAxis = 0U;
+ call.outputModeOverride = PLSR_OUTPUT_MODE_FROM_SFD;
+ return call;
+}
+
+static void TestValidSnapshotAndLiveFrequency(void)
+{
+ TEST_MEMORY memory;
+ PLSR_CALL call;
+ PLSR_PARSE_CONTEXT context;
+ PLSR_JOB_SNAPSHOT snapshot;
+ PLSR_PARSE_DETAIL detail;
+ uint32_t frequency;
+ uint8_t clamped;
+
+ TestBuildValidBlocks(&memory);
+ call = TestMakeCall(&memory);
+ context.logicalPosition = 0;
+ context.positionValid = 0U;
+ CHECK(PlsrBuildJobSnapshot(&call, &context, &snapshot, &detail)
+ == PLSR_RESULT_OK);
+ CHECK(detail.result == PLSR_RESULT_OK);
+ CHECK(snapshot.segmentCount == 2U);
+ CHECK(snapshot.startSegment == 1U);
+ CHECK(snapshot.s2Set == 1U);
+ CHECK(snapshot.outputMode == PLSR_OUTPUT_PULSE_DIR);
+ CHECK(snapshot.directionPoint == 4U);
+ CHECK(snapshot.s2.defaultSpeed == 1000UL);
+ CHECK(snapshot.s2.maximumSpeed == 100000UL);
+ CHECK(snapshot.s2.startSpeed == 100000UL);
+ CHECK(snapshot.speedClamped == 1U);
+ CHECK(snapshot.segments[0].targetFrequency == 100000UL);
+ CHECK((snapshot.segments[0].flags & PLSR_SEGMENT_FLAG_SPEED_CLAMPED)
+ != 0U);
+ CHECK(snapshot.segments[1].targetFrequency == 1000UL);
+ CHECK((snapshot.segments[1].flags & PLSR_SEGMENT_FLAG_DEFAULT_SPEED)
+ != 0U);
+ CHECK((snapshot.segments[1].flags & PLSR_SEGMENT_FLAG_SELF_LOOP) != 0U);
+ CHECK(snapshot.hasSelfLoop == 1U);
+ CHECK(snapshot.initialDirectionPositive == 1U);
+
+ TestWriteDword(&memory, PLSR_DEVICE_D, 112U, 9999);
+ CHECK(snapshot.segments[0].pulseOrTarget == 100);
+ TestWriteDword(&memory, PLSR_DEVICE_D, 110U, 2500);
+ CHECK(PlsrResolveLiveFrequency(&snapshot, 1U, &frequency, &clamped)
+ == PLSR_RESULT_OK);
+ CHECK(frequency == 2500UL);
+ CHECK(clamped == 0U);
+ TestWriteDword(&memory, PLSR_DEVICE_D, 110U, 200000);
+ CHECK(PlsrResolveLiveFrequency(&snapshot, 1U, &frequency, &clamped)
+ == PLSR_RESULT_OK);
+ CHECK(frequency == 100000UL);
+ CHECK(clamped == 1U);
+
+ call.s2.type = PLSR_OPERAND_DATA;
+ call.s2.data.device = PLSR_DEVICE_HD;
+ call.s2.data.address = 10UL;
+ CHECK(PlsrBuildJobSnapshot(&call, &context, &snapshot, &detail)
+ == PLSR_RESULT_OK);
+ CHECK(snapshot.s2Set == 1U);
+}
+
+static void TestValidationFailures(void)
+{
+ TEST_MEMORY memory;
+ PLSR_CALL call;
+ PLSR_PARSE_CONTEXT context = {0, 0U};
+ PLSR_JOB_SNAPSHOT snapshot;
+ PLSR_PARSE_DETAIL detail;
+
+ TestBuildValidBlocks(&memory);
+ call = TestMakeCall(&memory);
+ call.s1.address = 120UL;
+ CHECK(PlsrBuildJobSnapshot(&call, &context, &snapshot, &detail)
+ == PLSR_RESULT_BLOCK_OVERLAP);
+
+ TestBuildValidBlocks(&memory);
+ call = TestMakeCall(&memory);
+ memory.words[PLSR_DEVICE_D][103U] = 1U;
+ CHECK(PlsrBuildJobSnapshot(&call, &context, &snapshot, &detail)
+ == PLSR_RESULT_RESERVED_NOT_ZERO);
+ CHECK(detail.address == 103UL);
+
+ TestBuildValidBlocks(&memory);
+ call = TestMakeCall(&memory);
+ TestWriteDword(&memory, PLSR_DEVICE_D, 100U, 101);
+ CHECK(PlsrBuildJobSnapshot(&call, &context, &snapshot, &detail)
+ == PLSR_RESULT_SEGMENT_OVERFLOW);
+
+ TestBuildValidBlocks(&memory);
+ call = TestMakeCall(&memory);
+ TestWriteDword(&memory, PLSR_DEVICE_D, 110U, -1);
+ CHECK(PlsrBuildJobSnapshot(&call, &context, &snapshot, &detail)
+ == PLSR_RESULT_INVALID_FREQUENCY);
+ CHECK(detail.segment == 1U);
+
+ TestBuildValidBlocks(&memory);
+ call = TestMakeCall(&memory);
+ memory.words[PLSR_DEVICE_D][124U] = 0x0405U;
+ CHECK(PlsrBuildJobSnapshot(&call, &context, &snapshot, &detail)
+ == PLSR_RESULT_INVALID_WAIT);
+
+ TestBuildValidBlocks(&memory);
+ call = TestMakeCall(&memory);
+ TestWriteDword(&memory, PLSR_DEVICE_D, 128U, 3);
+ CHECK(PlsrBuildJobSnapshot(&call, &context, &snapshot, &detail)
+ == PLSR_RESULT_INVALID_JUMP);
+
+ TestBuildValidBlocks(&memory);
+ call = TestMakeCall(&memory);
+ TestWriteDword(&memory, PLSR_DEVICE_D, 118U, 2);
+ TestWriteDword(&memory, PLSR_DEVICE_D, 128U, 1);
+ CHECK(PlsrBuildJobSnapshot(&call, &context, &snapshot, &detail)
+ == PLSR_RESULT_PATH_CYCLE);
+
+ TestBuildValidBlocks(&memory);
+ call = TestMakeCall(&memory);
+ TestWriteDword(&memory, PLSR_DEVICE_D, 200U, 1);
+ CHECK(PlsrBuildJobSnapshot(&call, &context, &snapshot, &detail)
+ == PLSR_RESULT_POSITION_INVALID);
+
+ TestBuildValidBlocks(&memory);
+ call = TestMakeCall(&memory);
+ call.s0.address = UINT32_MAX - 5UL;
+ CHECK(PlsrBuildJobSnapshot(&call, &context, &snapshot, &detail)
+ == PLSR_RESULT_ADDRESS_OVERFLOW);
+
+ TestBuildValidBlocks(&memory);
+ call = TestMakeCall(&memory);
+ call.s2.constant = 5;
+ CHECK(PlsrBuildJobSnapshot(&call, &context, &snapshot, &detail)
+ == PLSR_RESULT_INVALID_S2);
+
+ TestBuildValidBlocks(&memory);
+ call = TestMakeCall(&memory);
+ CHECK(PlcDeviceWriteSfd(962U, 356) == PLC_DEVICE_OK);
+ CHECK(PlsrBuildJobSnapshot(&call, &context, &snapshot, &detail)
+ == PLSR_RESULT_INVALID_S2);
+ CHECK(PlcDeviceWriteSfd(962U, 50) == PLC_DEVICE_OK);
+ CHECK(PlcDeviceWriteSfd(964U, 1) == PLC_DEVICE_OK);
+ CHECK(PlsrBuildJobSnapshot(&call, &context, &snapshot, &detail)
+ == PLSR_RESULT_INVALID_S2);
+ CHECK(PlcDeviceWriteSfd(964U, 0) == PLC_DEVICE_OK);
+}
+
+static void TestDynamicReferences(void)
+{
+ TEST_MEMORY memory;
+ PLSR_CALL call;
+ PLSR_PARSE_CONTEXT context = {0, 0U};
+ PLSR_JOB_SNAPSHOT snapshot;
+ PLSR_PARSE_DETAIL detail;
+
+ TestBuildValidBlocks(&memory);
+ call = TestMakeCall(&memory);
+ memory.words[PLSR_DEVICE_D][124U] = 0x0101U;
+ TestWriteDword(&memory, PLSR_DEVICE_D, 125U, 300);
+ TestWriteDword(&memory, PLSR_DEVICE_D, 300U, 25);
+ memory.words[PLSR_DEVICE_D][127U] = 1U;
+ TestWriteDword(&memory, PLSR_DEVICE_D, 128U, 302);
+ TestWriteDword(&memory, PLSR_DEVICE_D, 302U, 2);
+ CHECK(PlsrBuildJobSnapshot(&call, &context, &snapshot, &detail)
+ == PLSR_RESULT_OK);
+ CHECK(snapshot.segments[1].waitSource == PLSR_VALUE_D);
+ CHECK(snapshot.segments[1].waitValueOrAddress == 300);
+ CHECK(snapshot.segments[1].jumpSource == PLSR_VALUE_D);
+ CHECK(snapshot.segments[1].jumpValueOrAddress == 302);
+
+ memory.words[PLSR_DEVICE_D][124U] = 0x0404U;
+ TestWriteDword(&memory, PLSR_DEVICE_D, 125U, 7);
+ memory.bits[0][7U] = 1U;
+ memory.words[PLSR_DEVICE_D][127U] = 0U;
+ TestWriteDword(&memory, PLSR_DEVICE_D, 128U, 2);
+ CHECK(PlsrBuildJobSnapshot(&call, &context, &snapshot, &detail)
+ == PLSR_RESULT_OK);
+}
+
+static void TestOutputAndDivider(void)
+{
+ TEST_MEMORY memory;
+ PLSR_CALL call;
+ PLSR_PARSE_CONTEXT context = {0, 0U};
+ PLSR_JOB_SNAPSHOT snapshot;
+ PLSR_PARSE_DETAIL detail;
+ uint16_t psc;
+ uint16_t arr;
+
+ TestBuildValidBlocks(&memory);
+ call = TestMakeCall(&memory);
+ CHECK(PlcDeviceWriteSfd(900U, (1U << 13U)) == PLC_DEVICE_OK);
+ CHECK(PlsrBuildJobSnapshot(&call, &context, &snapshot, &detail)
+ == PLSR_RESULT_OK);
+ CHECK(snapshot.outputMode == PLSR_OUTPUT_AB);
+ CHECK(snapshot.pairedTimerClockHz == 84000000UL);
+ CHECK(PlsrCalculateTimerDivider(168000000UL, 1UL, &psc, &arr)
+ == PLSR_RESULT_OK);
+ CHECK(PlsrCalculateTimerDivider(84000000UL, 100000UL, &psc, &arr)
+ == PLSR_RESULT_OK);
+ CHECK(PlsrCalculateTimerDivider(84000000UL, 0UL, &psc, &arr)
+ == PLSR_RESULT_INVALID_ARGUMENT);
+ CHECK(PlsrCalculateTimerDivider(1UL, 100000UL, &psc, &arr)
+ == PLSR_RESULT_DIVIDER_UNREPRESENTABLE);
+ CHECK(PlcDeviceWriteSfd(900U, 0) == PLC_DEVICE_OK);
+}
+
+static void TestCoreSubmission(void)
+{
+ TEST_MEMORY memory;
+ PLSR_CALL call;
+ PLSR_STATUS status;
+ PLSR_PARSE_DETAIL detail;
+
+ TestBuildValidBlocks(&memory);
+ call = TestMakeCall(&memory);
+ call.sequence = 500UL;
+ CHECK(PlsrInit() == PLSR_RESULT_OK);
+ CHECK(PlsrPostCall(&call) == PLSR_RESULT_QUEUED);
+ PlsrProcess();
+ CHECK(PlsrGetStatus(0U, &status) == PLSR_RESULT_OK);
+ CHECK(status.lastCommandResult == PLSR_RESULT_OK);
+ CHECK(status.state == PLSR_STATE_ACCEL);
+ CHECK(status.jobValid == 1U);
+ CHECK(status.segmentCount == 2U);
+ CHECK(status.startSegment == 1U);
+ CHECK(status.s2Set == 1U);
+ CHECK(status.speedClamped == 1U);
+ CHECK(PlsrGetLastParseDetail(0U, &detail) == PLSR_RESULT_OK);
+ CHECK(detail.result == PLSR_RESULT_OK);
+}
+
+int main(void)
+{
+ PlsrPersistenceTestResetStorage();
+ CHECK(PlcDeviceInit() == PLC_DEVICE_OK);
+ TestConfigureAxis0K1();
+ TestValidSnapshotAndLiveFrequency();
+ TestValidationFailures();
+ TestDynamicReferences();
+ TestOutputAndDivider();
+ TestCoreSubmission();
+
+ if (TestFailures != 0)
+ {
+ (void)printf("FAIL: %d of %d PLSR job checks failed\n",
+ TestFailures,
+ TestChecks);
+ return 1;
+ }
+ (void)printf("PASS: %d PLSR job checks\n", TestChecks);
+ return 0;
+}