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; +}