#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) { PLC_DEVICE_RESULT deviceResult; deviceResult = (useHsd != 0U) ? PlcDeviceReadHsd(address, value) : PlcDeviceReadSfd(address, value); if (deviceResult != PLC_DEVICE_OK) { PlsrSetDetail(detail, PLSR_RESULT_INVALID_S2, PLSR_PARSE_BLOCK_S2, address, 0, 0U); return PLSR_RESULT_INVALID_S2; } 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; uint16_t commonFlags; uint16_t switchLogic; uint16_t limitPoints; uint32_t rawLimit; 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; commonFlags = word; snapshot->directionActiveHigh = ((commonFlags & (1U << 1U)) != 0U) ? 1U : 0U; snapshot->limits.softLimitEnabled = ((commonFlags & (1U << 2U)) != 0U) ? 1U : 0U; snapshot->equivalent.unitCode = (uint8_t)((commonFlags >> 8U) & 0x07U); if (PlsrPositionUnitCodeIsValid(snapshot->equivalent.unitCode) == 0U) { PlsrSetDetail(detail, PLSR_RESULT_INVALID_S2, PLSR_PARSE_BLOCK_S2, commonBase, snapshot->equivalent.unitCode, 0U); return PLSR_RESULT_INVALID_S2; } result = PlsrReadFixedDword(0U, (uint16_t)(commonBase + 2U), &snapshot->equivalent.pulsesPerRevolution, detail); if (result != PLSR_RESULT_OK) return result; result = PlsrReadFixedDword(0U, (uint16_t)(commonBase + 4U), &snapshot->equivalent.movementPerRevolution, detail); if (result != PLSR_RESULT_OK) return result; if (PlsrPositionValidateEquivalent(&snapshot->equivalent) != PLSR_RESULT_OK) { uint16_t invalidAddress = (snapshot->equivalent.pulsesPerRevolution == 0UL) ? (uint16_t)(commonBase + 2U) : (uint16_t)(commonBase + 4U); PlsrSetDetail(detail, PLSR_RESULT_INVALID_S2, PLSR_PARSE_BLOCK_S2, invalidAddress, 0, 0U); return PLSR_RESULT_INVALID_S2; } result = PlsrReadFixedWord(0U, (uint16_t)(commonBase + 12U), &switchLogic, detail); if (result != PLSR_RESULT_OK) return result; result = PlsrReadFixedWord(0U, (uint16_t)(commonBase + 15U), &limitPoints, detail); if (result != PLSR_RESULT_OK) return result; snapshot->limits.positiveInputPoint = (uint8_t)(limitPoints & 0xFFU); snapshot->limits.negativeInputPoint = (uint8_t)(limitPoints >> 8U); snapshot->limits.positiveInputActiveLow = ((switchLogic & (1U << 2U)) != 0U) ? 1U : 0U; snapshot->limits.negativeInputActiveLow = ((switchLogic & (1U << 3U)) != 0U) ? 1U : 0U; if (((snapshot->limits.positiveInputPoint != 0xFFU) || (snapshot->limits.negativeInputPoint != 0xFFU)) && (call->source.readBit == NULL)) { PlsrSetDetail(detail, PLSR_RESULT_DATA_ACCESS, PLSR_PARSE_BLOCK_S2, (uint16_t)(commonBase + 15U), limitPoints, 0U); return PLSR_RESULT_DATA_ACCESS; } result = PlsrReadFixedDword(0U, (uint16_t)(commonBase + 30U), &rawLimit, detail); if (result != PLSR_RESULT_OK) return result; result = PlsrPositionAbsoluteUnitsToPulses( &snapshot->equivalent, (int32_t)rawLimit, &snapshot->limits.positiveSoftLimitPulses); if (result != PLSR_RESULT_OK) { PlsrSetDetail(detail, PLSR_RESULT_POSITION_OVERFLOW, PLSR_PARSE_BLOCK_POSITION, (uint16_t)(commonBase + 30U), (int32_t)rawLimit, 0U); return PLSR_RESULT_POSITION_OVERFLOW; } result = PlsrReadFixedDword(0U, (uint16_t)(commonBase + 32U), &rawLimit, detail); if (result != PLSR_RESULT_OK) return result; result = PlsrPositionAbsoluteUnitsToPulses( &snapshot->equivalent, (int32_t)rawLimit, &snapshot->limits.negativeSoftLimitPulses); if (result != PLSR_RESULT_OK) { PlsrSetDetail(detail, PLSR_RESULT_POSITION_OVERFLOW, PLSR_PARSE_BLOCK_POSITION, (uint16_t)(commonBase + 32U), (int32_t)rawLimit, 0U); return PLSR_RESULT_POSITION_OVERFLOW; } if ((snapshot->limits.softLimitEnabled != 0U) && (snapshot->limits.positiveSoftLimitPulses <= snapshot->limits.negativeSoftLimitPulses)) { PlsrSetDetail(detail, PLSR_RESULT_INVALID_S2, PLSR_PARSE_BLOCK_S2, (uint16_t)(commonBase + 30U), (int32_t)rawLimit, 0U); return PLSR_RESULT_INVALID_S2; } if (call->outputModeOverride == PLSR_OUTPUT_MODE_FROM_SFD) { snapshot->outputMode = ((commonFlags & (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.directionDelayMs, 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; } snapshot->inputDefaultSpeed = snapshot->s2.defaultSpeed; snapshot->inputMaximumSpeed = snapshot->s2.maximumSpeed; 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; int64_t movement; int64_t targetPosition; int64_t remainder = 0; int32_t next; uint16_t segment = snapshot->startSegment; PLSR_RESULT result; 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) { result = PlsrPositionUnitsToPulses(&snapshot->equivalent, segmentData->pulseOrTarget, remainder, &movement, &remainder); if (result != PLSR_RESULT_OK) { PlsrSetDetail(detail, PLSR_RESULT_POSITION_OVERFLOW, PLSR_PARSE_BLOCK_POSITION, snapshot->s0.address + (uint32_t)segment * PLSR_S0_SEGMENT_WORDS + 2UL, segmentData->pulseOrTarget, segment); return PLSR_RESULT_POSITION_OVERFLOW; } 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 { result = PlsrPositionAbsoluteUnitsToPulses( &snapshot->equivalent, segmentData->pulseOrTarget, &targetPosition); if (result != PLSR_RESULT_OK) { return PLSR_RESULT_POSITION_OVERFLOW; } movement = (targetPosition > position) ? 1 : ((targetPosition < position) ? -1 : 0); position = targetPosition; } 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; } static PLSR_RESULT PlsrConvertSnapshotSpeeds( PLSR_JOB_SNAPSHOT *snapshot, PLSR_PARSE_DETAIL *detail) { uint16_t segment; PLSR_RESULT result; result = PlsrPositionSpeedToPulseFrequency(&snapshot->equivalent, snapshot->s2.defaultSpeed, &snapshot->s2.defaultSpeed); if (result != PLSR_RESULT_OK) return result; result = PlsrPositionSpeedToPulseFrequency(&snapshot->equivalent, snapshot->s2.maximumSpeed, &snapshot->s2.maximumSpeed); if (result != PLSR_RESULT_OK) return result; result = PlsrPositionSpeedToPulseFrequency(&snapshot->equivalent, snapshot->s2.startSpeed, &snapshot->s2.startSpeed); if (result != PLSR_RESULT_OK) return result; result = PlsrPositionSpeedToPulseFrequency(&snapshot->equivalent, snapshot->s2.stopSpeed, &snapshot->s2.stopSpeed); if (result != PLSR_RESULT_OK) return result; result = PlsrPositionSpeedToPulseFrequency(&snapshot->equivalent, snapshot->s2.zrnHighSpeed, &snapshot->s2.zrnHighSpeed); if (result != PLSR_RESULT_OK) return result; result = PlsrPositionSpeedToPulseFrequency(&snapshot->equivalent, snapshot->s2.zrnCrawlSpeed, &snapshot->s2.zrnCrawlSpeed); if (result != PLSR_RESULT_OK) return result; if ((snapshot->s2.maximumSpeed < PLSR_FREQUENCY_MIN_HZ) || (snapshot->s2.maximumSpeed > PLSR_FREQUENCY_MAX_HZ)) { PlsrSetDetail(detail, PLSR_RESULT_INVALID_FREQUENCY, PLSR_PARSE_BLOCK_S2, 0UL, (int32_t)snapshot->s2.maximumSpeed, 0U); return PLSR_RESULT_INVALID_FREQUENCY; } for (segment = 1U; segment <= snapshot->segmentCount; segment++) { PLSR_SEGMENT_SNAPSHOT *segmentData = &snapshot->segments[segment - 1U]; if (segmentData->targetFrequency == 0UL) { continue; } result = PlsrPositionSpeedToPulseFrequency( &snapshot->equivalent, segmentData->targetFrequency, &segmentData->targetFrequency); if ((result != PLSR_RESULT_OK) || (segmentData->targetFrequency < PLSR_FREQUENCY_MIN_HZ) || (segmentData->targetFrequency > PLSR_FREQUENCY_MAX_HZ)) { PlsrSetDetail(detail, PLSR_RESULT_INVALID_FREQUENCY, PLSR_PARSE_BLOCK_S0, snapshot->s0.address + (uint32_t)segment * PLSR_S0_SEGMENT_WORDS, (int32_t)segmentData->targetFrequency, segment); return PLSR_RESULT_INVALID_FREQUENCY; } result = PlsrValidateFrequencyDivider(snapshot, segmentData->targetFrequency, detail, segment); if (result != PLSR_RESULT_OK) return result; } 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; } 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 = PlsrConvertSnapshotSpeeds(snapshot, 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->inputDefaultSpeed : (uint32_t)rawFrequency; *clamped = 0U; if (*frequency > snapshot->inputMaximumSpeed) { *frequency = snapshot->inputMaximumSpeed; *clamped = 1U; } if (*frequency == 0UL) return PLSR_RESULT_INVALID_FREQUENCY; result = PlsrPositionSpeedToPulseFrequency(&snapshot->equivalent, *frequency, frequency); if ((result != PLSR_RESULT_OK) || (*frequency == 0UL) || (*frequency > PLSR_FREQUENCY_MAX_HZ)) { return PLSR_RESULT_INVALID_FREQUENCY; } return PlsrValidateFrequencyDivider(snapshot, *frequency, NULL, segment); }