| @@ -1281,6 +1281,9 @@ | |||||
| <file> | <file> | ||||
| <name>$PROJ_DIR$\..\PLSR\Inc\plsr_core.h</name> | <name>$PROJ_DIR$\..\PLSR\Inc\plsr_core.h</name> | ||||
| </file> | </file> | ||||
| <file> | |||||
| <name>$PROJ_DIR$\..\PLSR\Inc\plsr_job.h</name> | |||||
| </file> | |||||
| <file> | <file> | ||||
| <name>$PROJ_DIR$\..\PLSR\Src\plsr_persistence.c</name> | <name>$PROJ_DIR$\..\PLSR\Src\plsr_persistence.c</name> | ||||
| </file> | </file> | ||||
| @@ -1290,6 +1293,9 @@ | |||||
| <file> | <file> | ||||
| <name>$PROJ_DIR$\..\PLSR\Src\plsr_resource.c</name> | <name>$PROJ_DIR$\..\PLSR\Src\plsr_resource.c</name> | ||||
| </file> | </file> | ||||
| <file> | |||||
| <name>$PROJ_DIR$\..\PLSR\Src\plsr_job.c</name> | |||||
| </file> | |||||
| <file> | <file> | ||||
| <name>$PROJ_DIR$\..\PLSR\Src\plsr_core.c</name> | <name>$PROJ_DIR$\..\PLSR\Src\plsr_core.c</name> | ||||
| </file> | </file> | ||||
| @@ -1,6 +1,7 @@ | |||||
| #ifndef PLSR_CORE_H | #ifndef PLSR_CORE_H | ||||
| #define PLSR_CORE_H | #define PLSR_CORE_H | ||||
| #include "plsr_job.h" | |||||
| #include "plsr_types.h" | #include "plsr_types.h" | ||||
| #include <stdint.h> | #include <stdint.h> | ||||
| @@ -13,9 +14,12 @@ void PlsrTask(void *argument); | |||||
| void PlsrProcess(void); | void PlsrProcess(void); | ||||
| PLSR_RESULT PlsrPostStart(const PLSR_START_REQUEST *request); | PLSR_RESULT PlsrPostStart(const PLSR_START_REQUEST *request); | ||||
| PLSR_RESULT PlsrPostCall(const PLSR_CALL *call); | |||||
| PLSR_RESULT PlsrPostCommand(const PLSR_COMMAND *command); | PLSR_RESULT PlsrPostCommand(const PLSR_COMMAND *command); | ||||
| PLSR_RESULT PlsrPostEvent(uint8_t axis, uint32_t eventMask); | PLSR_RESULT PlsrPostEvent(uint8_t axis, uint32_t eventMask); | ||||
| PLSR_RESULT PlsrGetStatus(uint8_t axis, PLSR_STATUS *status); | 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_RESULT PlsrStateTransition(uint8_t axis, | ||||
| PLSR_STATE target, | PLSR_STATE target, | ||||
| @@ -0,0 +1,198 @@ | |||||
| #ifndef PLSR_JOB_H | |||||
| #define PLSR_JOB_H | |||||
| #include "plsr_types.h" | |||||
| #include <stdint.h> | |||||
| #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 */ | |||||
| @@ -60,7 +60,21 @@ typedef enum | |||||
| PLSR_RESULT_BUSY, | PLSR_RESULT_BUSY, | ||||
| PLSR_RESULT_PERSISTENCE_ERROR, | PLSR_RESULT_PERSISTENCE_ERROR, | ||||
| PLSR_RESULT_NOT_SUPPORTED, | 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; | } PLSR_RESULT; | ||||
| typedef enum | typedef enum | ||||
| @@ -159,6 +173,12 @@ typedef struct | |||||
| uint8_t directionPositive; | uint8_t directionPositive; | ||||
| uint8_t highResourceMask; | uint8_t highResourceMask; | ||||
| uint8_t directionPoint; | uint8_t directionPoint; | ||||
| uint8_t positionValid; | |||||
| uint8_t jobValid; | |||||
| uint8_t s2Set; | |||||
| uint8_t speedClamped; | |||||
| uint16_t segmentCount; | |||||
| uint16_t startSegment; | |||||
| } PLSR_STATUS; | } PLSR_STATUS; | ||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||
| @@ -17,6 +17,8 @@ typedef struct | |||||
| PLSR_STOP_REASON stopReason; | PLSR_STOP_REASON stopReason; | ||||
| PLSR_STATE pendingTerminal; | PLSR_STATE pendingTerminal; | ||||
| PLSR_RESOURCE_LEASE lease; | PLSR_RESOURCE_LEASE lease; | ||||
| PLSR_JOB_SNAPSHOT job; | |||||
| PLSR_PARSE_DETAIL parseDetail; | |||||
| volatile uint32_t pendingEvents; | volatile uint32_t pendingEvents; | ||||
| uint32_t lastCommandSequence; | uint32_t lastCommandSequence; | ||||
| PLSR_RESULT lastCommandResult; | PLSR_RESULT lastCommandResult; | ||||
| @@ -27,14 +29,18 @@ typedef struct | |||||
| uint8_t done; | uint8_t done; | ||||
| uint8_t directionPositive; | uint8_t directionPositive; | ||||
| uint8_t immediateStopPending; | uint8_t immediateStopPending; | ||||
| uint8_t positionValid; | |||||
| uint8_t jobValid; | |||||
| } PLSR_AXIS; | } PLSR_AXIS; | ||||
| typedef struct | typedef struct | ||||
| { | { | ||||
| PLSR_COMMAND command; | PLSR_COMMAND command; | ||||
| PLSR_START_REQUEST start; | PLSR_START_REQUEST start; | ||||
| PLSR_CALL call; | |||||
| uint32_t ticket; | uint32_t ticket; | ||||
| uint8_t hasStart; | uint8_t hasStart; | ||||
| uint8_t hasCall; | |||||
| uint8_t occupied; | uint8_t occupied; | ||||
| } PLSR_COMMAND_SLOT; | } PLSR_COMMAND_SLOT; | ||||
| @@ -42,6 +48,7 @@ static PLSR_AXIS PlsrAxes[PLSR_AXIS_COUNT]; | |||||
| static PLSR_COMMAND_SLOT PlsrCommandQueue[PLSR_COMMAND_QUEUE_DEPTH]; | static PLSR_COMMAND_SLOT PlsrCommandQueue[PLSR_COMMAND_QUEUE_DEPTH]; | ||||
| static uint32_t PlsrNextTicket; | static uint32_t PlsrNextTicket; | ||||
| static uint8_t PlsrInitialized; | static uint8_t PlsrInitialized; | ||||
| static PLSR_JOB_SNAPSHOT PlsrJobScratch; | |||||
| static uint32_t PlsrCoreEnterCritical(void) | 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, | 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; | uint32_t interruptState; | ||||
| uint8_t freeSlot = PLSR_COMMAND_QUEUE_DEPTH; | uint8_t freeSlot = PLSR_COMMAND_QUEUE_DEPTH; | ||||
| @@ -292,6 +300,18 @@ static PLSR_RESULT PlsrQueueCommand(const PLSR_COMMAND *command, | |||||
| sizeof(PlsrCommandQueue[freeSlot].start)); | sizeof(PlsrCommandQueue[freeSlot].start)); | ||||
| PlsrCommandQueue[freeSlot].hasStart = 0U; | 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].ticket = PlsrNextTicket++; | ||||
| PlsrCommandQueue[freeSlot].occupied = 1U; | PlsrCommandQueue[freeSlot].occupied = 1U; | ||||
| PlsrCoreExitCritical(interruptState); | PlsrCoreExitCritical(interruptState); | ||||
| @@ -361,7 +381,30 @@ PLSR_RESULT PlsrPostStart(const PLSR_START_REQUEST *request) | |||||
| command.axis = request->axis; | command.axis = request->axis; | ||||
| command.opcode = PLSR_CMD_START; | command.opcode = PLSR_CMD_START; | ||||
| command.argument = 0; | 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) | PLSR_RESULT PlsrPostCommand(const PLSR_COMMAND *command) | ||||
| @@ -383,7 +426,7 @@ PLSR_RESULT PlsrPostCommand(const PLSR_COMMAND *command) | |||||
| { | { | ||||
| return PLSR_RESULT_INVALID_STATE; | return PLSR_RESULT_INVALID_STATE; | ||||
| } | } | ||||
| return PlsrQueueCommand(command, NULL); | |||||
| return PlsrQueueCommand(command, NULL, NULL); | |||||
| } | } | ||||
| PLSR_RESULT PlsrPostEvent(uint8_t axis, uint32_t eventMask) | 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->pendingTerminal = PLSR_STATE_UNINITIALIZED; | ||||
| axisObject->immediateStopPending = 0U; | axisObject->immediateStopPending = 0U; | ||||
| axisObject->done = 0U; | axisObject->done = 0U; | ||||
| axisObject->jobValid = 0U; | |||||
| result = PlsrStateTransition(start->axis, | result = PlsrStateTransition(start->axis, | ||||
| PLSR_STATE_ACCEL, | PLSR_STATE_ACCEL, | ||||
| PLSR_TRANSITION_START); | PLSR_TRANSITION_START); | ||||
| @@ -474,6 +518,67 @@ static PLSR_RESULT PlsrStartAxis(PLSR_AXIS *axisObject, | |||||
| return result; | 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) | static PLSR_RESULT PlsrStopImmediate(uint8_t axis) | ||||
| { | { | ||||
| PLSR_AXIS *axisObject = &PlsrAxes[axis]; | PLSR_AXIS *axisObject = &PlsrAxes[axis]; | ||||
| @@ -590,9 +695,16 @@ static PLSR_RESULT PlsrExecuteCommand(const PLSR_COMMAND_SLOT *slot) | |||||
| switch (slot->command.opcode) | switch (slot->command.opcode) | ||||
| { | { | ||||
| case PLSR_CMD_START: | 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; | break; | ||||
| case PLSR_CMD_STOP_DECEL: | case PLSR_CMD_STOP_DECEL: | ||||
| @@ -630,6 +742,7 @@ static PLSR_RESULT PlsrExecuteCommand(const PLSR_COMMAND_SLOT *slot) | |||||
| else | else | ||||
| { | { | ||||
| axisObject->logicalPosition = slot->command.argument; | axisObject->logicalPosition = slot->command.argument; | ||||
| axisObject->positionValid = 1U; | |||||
| (void)PlcDevicePublishHsdPair( | (void)PlcDevicePublishHsdPair( | ||||
| (uint16_t)(slot->command.axis | (uint16_t)(slot->command.axis | ||||
| * PLSR_HSD_RUNTIME_AXIS_COUNT), | * PLSR_HSD_RUNTIME_AXIS_COUNT), | ||||
| @@ -646,6 +759,7 @@ static PLSR_RESULT PlsrExecuteCommand(const PLSR_COMMAND_SLOT *slot) | |||||
| else | else | ||||
| { | { | ||||
| axisObject->logicalPosition = 0; | axisObject->logicalPosition = 0; | ||||
| axisObject->positionValid = 1U; | |||||
| (void)PlcDevicePublishHsdPair( | (void)PlcDevicePublishHsdPair( | ||||
| (uint16_t)(slot->command.axis | (uint16_t)(slot->command.axis | ||||
| * PLSR_HSD_RUNTIME_AXIS_COUNT), | * PLSR_HSD_RUNTIME_AXIS_COUNT), | ||||
| @@ -888,6 +1002,7 @@ static void PlsrProcessNormalEvents(uint8_t axis, uint32_t events) | |||||
| PLSR_RESULT PlsrInit(void) | PLSR_RESULT PlsrInit(void) | ||||
| { | { | ||||
| int64_t restoredPosition; | |||||
| uint8_t axis; | uint8_t axis; | ||||
| (void)memset(PlsrAxes, 0, sizeof(PlsrAxes)); | (void)memset(PlsrAxes, 0, sizeof(PlsrAxes)); | ||||
| @@ -901,6 +1016,15 @@ PLSR_RESULT PlsrInit(void) | |||||
| PlsrAxes[axis].state = PLSR_STATE_UNINITIALIZED; | PlsrAxes[axis].state = PLSR_STATE_UNINITIALIZED; | ||||
| PlsrAxes[axis].pendingTerminal = PLSR_STATE_UNINITIALIZED; | PlsrAxes[axis].pendingTerminal = PLSR_STATE_UNINITIALIZED; | ||||
| PlsrAxes[axis].lease.directionPoint = PLSR_DIRECTION_POINT_NONE; | 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, | if (PlsrStateTransition(axis, | ||||
| PLSR_STATE_IDLE, | PLSR_STATE_IDLE, | ||||
| PLSR_TRANSITION_INITIALIZED) | PLSR_TRANSITION_INITIALIZED) | ||||
| @@ -1001,6 +1125,37 @@ PLSR_RESULT PlsrGetStatus(uint8_t axis, PLSR_STATUS *status) | |||||
| status->directionPoint = (axisObject->lease.valid != 0U) | status->directionPoint = (axisObject->lease.valid != 0U) | ||||
| ? axisObject->lease.directionPoint | ? axisObject->lease.directionPoint | ||||
| : PLSR_DIRECTION_POINT_NONE; | : 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); | PlsrCoreExitCritical(interruptState); | ||||
| return PLSR_RESULT_OK; | return PLSR_RESULT_OK; | ||||
| } | } | ||||
| @@ -28,9 +28,21 @@ $tests = @( | |||||
| "$workspacePath\PLSR\Src\plc_device.c" | "$workspacePath\PLSR\Src\plc_device.c" | ||||
| "$workspacePath\PLSR\Src\plsr_persistence.c" | "$workspacePath\PLSR\Src\plsr_persistence.c" | ||||
| "$workspacePath\PLSR\Src\plsr_resource.c" | "$workspacePath\PLSR\Src\plsr_resource.c" | ||||
| "$workspacePath\PLSR\Src\plsr_job.c" | |||||
| "$workspacePath\PLSR\Src\plsr_core.c" | "$workspacePath\PLSR\Src\plsr_core.c" | ||||
| "$workspacePath\PLSR\Test\test_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" | |||||
| ) | |||||
| } | } | ||||
| ) | ) | ||||
| @@ -0,0 +1,410 @@ | |||||
| #include "plc_device.h" | |||||
| #include "plsr_core.h" | |||||
| #include "plsr_job.h" | |||||
| #include "plsr_persistence.h" | |||||
| #include <stdio.h> | |||||
| #include <string.h> | |||||
| #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; | |||||
| } | |||||