From 02e24f0f533fe904c3a5275c4060c10c38d7c17a Mon Sep 17 00:00:00 2001 From: Ján Sučan Date: Wed, 10 May 2017 15:13:29 +0200 Subject: Uvodny commit, subory su rovnake ako na CD prilozenom k vytlacenemu texu bakalarskej prace, naviac je pridany len subor LICENCIA --- Mcu.cpp | 422 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 422 insertions(+) create mode 100755 Mcu.cpp (limited to 'Mcu.cpp') diff --git a/Mcu.cpp b/Mcu.cpp new file mode 100755 index 0000000..16a0383 --- /dev/null +++ b/Mcu.cpp @@ -0,0 +1,422 @@ +#include + +#include "fw_stage_1.hpp" +#include "fw_ident.hpp" +#include "ExitCodes.hpp" +#include "Logger.hpp" +#include "ExitException.hpp" +#include "McuSt10f269.hpp" +#include "McuSt10f168.hpp" + +#include + +using FwCommon::fw_stage_1; +using FwCommon::fw_stage_1_length; +using FwCommon::fw_ident; +using FwCommon::fw_ident_length; +using std::ostringstream; +using std::nothrow; + +#define FW_1_MAX_LENGTH 32 +#define FW_MAX_LENGTH 2048 +#define BOOTSTRAP_ACK 0xD5 +#define SHELL_ACK 0xAB + +#define PAD_BYTE 0xFF + +#define CMD_PING 0x00 +#define CMD_ERASE_BLOCKS 0x01 +#define CMD_READ 0x02 +#define CMD_WRITE 0x03 +#define CMD_IDENTIFY 0x04 +#define CMD_ERASE_CHIP 0x05 + +#define RET_SERIAL_OVERRUN 0x20 +#define RET_BAD_ECHO 0x21 + +#define JUNK_BYTE_COUNT 128 + + +CMcu::CMcu(CSerialPort & serialPort, float mcuFrequency) + : mSerialPort(serialPort), mMcuFrequency(mcuFrequency) +{ + // Check in which mode MCU is + CLogger::info("Sending zero byte"); + uint8_t ack = CMD_PING; + // Send zero byte + mSerialPort.write(&ack, 1, 1); + // Read response + mSerialPort.read(&ack, 1); + + // If some unknown data were received, try to read out junk bytes from + // previous operations without collected return status + for (int i = 0; (ack != BOOTSTRAP_ACK) && (ack != SHELL_ACK) && (i < JUNK_BYTE_COUNT); ++i) { + mSerialPort.read(&ack, 1); + } + + uint16_t idchip, idmanuf; + uint8_t data[4]; + + if (ack == SHELL_ACK) { + CLogger::info("Received stage 2 firmware ACK byte " + CLogger::decToHex(ack)); + CLogger::info("Skipping MCU initialization, NOT LOADING stage 2 firmware"); + + // Get IDCHIP and IDMANUF registers using Ident command for initialized MCU + CLogger::info("Getting IDMAUNF and IDCHIP registers of already initialized MCU"); + + mSerialPort.sendSafeByte(CMD_IDENTIFY); + mSerialPort.read(data, 4); + decodeIdentData(data, idchip, idmanuf); + + setMcuSpecificsById(idchip, idmanuf); + } else if (ack == BOOTSTRAP_ACK) { + // Ziskame identifikaciu a nahrame shell mcuSpecfics + CLogger::info("Received bootstrap loader ACK byte " + CLogger::decToHex(ack)); + + // Write the first stage loader + CLogger::info("Writing stage 1 firmware"); + mSerialPort.write(fw_stage_1, fw_stage_1_length, FW_1_MAX_LENGTH); + + // Get identification + CLogger::info("Getting IDMAUNF and IDCHIP registers"); + mSerialPort.write(fw_ident, fw_ident_length, FW_MAX_LENGTH); + mSerialPort.read(data, 4); + + decodeIdentData(data, idchip, idmanuf); + setMcuSpecificsById(idchip, idmanuf); + + // Load stage 2 firmware + CLogger::info("Writing stage 2 firmware"); + mSerialPort.write(mMcuSpecifics->getFirmware(), + mMcuSpecifics->getFirmwareLength(), FW_MAX_LENGTH); + // Wait for initialization end + uint16_t r = mSerialPort.readWord(); + if (r != 0x00) { + CLogger::error("Cannot initialize MCU: " + getMessageForRetCode(r), EXIT_MCU); + } + } else { + CLogger::error("Received unknown ack byte " + CLogger::decToHex(ack) + + ", expected " + CLogger::decToHex(BOOTSTRAP_ACK) + + " or " + CLogger::decToHex(SHELL_ACK), EXIT_MCU); + } +} + +void +CMcu::decodeIdentData(uint8_t data[4], uint16_t & idmanuf, uint16_t & idchip) +{ + idmanuf = 0; + idmanuf |= data[0]; + idmanuf |= (data[1]) << 8; + + idchip = 0; + idchip |= data[2]; + idchip |= (data[3]) << 8; +} + + +string +CMcu::getMessageForRetCode(uint16_t ret) +{ + string s; + + switch (ret) { + case RET_SERIAL_OVERRUN: + s = "MCU serial receiver buffer overrun, use lower serial speed"; + break; + case RET_BAD_ECHO: + s = "MCU received corrupted double word value"; + break; + default: + if (mMcuSpecifics == 0 || (s = mMcuSpecifics->getMessageForRetCode(ret)).empty()) + s = "Unknown MCU return code: " + CLogger::decToHex(ret); + break; + } + + return s; +} + +void +CMcu::setMcuSpecificsById(uint16_t idmanuf, uint16_t idchip) +{ + if (CMcuSt10f269::hasThisId(idmanuf, idchip)) { + CLogger::info("ST10F269 MCU found"); + mMcuSpecifics.reset(new CMcuSt10f269()); + } else if (CMcuSt10f168::hasThisId(idmanuf, idchip)) { + CLogger::info("ST10F168 MCU found"); + mMcuSpecifics.reset(new CMcuSt10f168()); + } else { + ostringstream os; + os << "Received unknown MCU identification registers values: "; + os << "IDMANUF=" << CLogger::decToHex(idmanuf); + os << " IDCHIP=" << CLogger::decToHex(idchip); + CLogger::error(os.str(), EXIT_MCU); + } +} + +// ----------------------------------------------------------------------------- +// Operacie +// ----------------------------------------------------------------------------- + +void +CMcu::sendShellCommand(uint8_t cmd) +{ + // Check needed frequency + if ((mMcuSpecifics->getName() != "ST10F168") && (mMcuFrequency > 0)) + CLogger::info("Ignoring -f option for this MCU"); + if ((mMcuSpecifics->getName() == "ST10F168") && (mMcuFrequency == 0)) + CLogger::error("Missing -f option for MCU " + mMcuSpecifics->getName(), EXIT_MCU); + + mSerialPort.sendSafeByte(cmd); + // Write configuration data + list d = mMcuSpecifics->getConfigData(mMcuFrequency); + list::const_iterator it; + for (it = d.begin(); it != d.end(); ++it) + mSerialPort.sendSafeWord(*it); +} + +string +CMcu::ident() +{ + return mMcuSpecifics->getName(); +} + +void +CMcu::erase() +{ + uint16_t r; + + sendShellCommand(CMD_ERASE_CHIP); + // Wait for return status of erase operation + mSerialPort.setReadTimeout(mMcuSpecifics->getEraseTimeout()); + r = mSerialPort.readWord(); + mSerialPort.setDefaultTimeout(); + + if (r != 0) + CLogger::error(getMessageForRetCode(r), EXIT_MCU); +} + +void +CMcu::erase(uint32_t startAddr, uint32_t endAddr) +{ + list bs = mMcuSpecifics->getBlockSizes(); + + // Block address space + typedef struct { + uint32_t a; + uint32_t b; + } range_t; + + unique_ptr ba(new(nothrow) range_t[bs.size()]); + + if (!ba) + CLogger::error("Cannot allocate memory", EXIT_MCU); + + list::const_iterator it; + unsigned int i; + uint32_t a = 0; + for (i = 0, it = bs.begin(); it != bs.end(); ++it, ++i) { + ba[i].a = a; + a = (*it * 1024); + ba[i].b = ba[i].a + a - 1; + a = ba[i].a + a; + } + + uint32_t lastValidAddr = mMcuSpecifics->getFlashSize() - 1; + + if (startAddr > lastValidAddr) { + ostringstream os; + os << "Start erase address " << startAddr << " is out of range [0," << lastValidAddr << "]"; + CLogger::error(os.str(), EXIT_MCU); + } + + if (endAddr > lastValidAddr) { + ostringstream os; + os << "End erase address " << endAddr << " is out of range [0," << lastValidAddr << "]"; + CLogger::error(os.str(), EXIT_MCU); + } + + if (startAddr > endAddr) { + ostringstream os; + os << "Start erase address " << startAddr << " is greater than end erase address " << endAddr; + CLogger::error(os.str(), EXIT_MCU); + } + + // Construct block list + list l; + // Find block containing startAddr + for (i = 0; i < bs.size(); ++i) { + if (startAddr >= ba[i].a && startAddr <= ba[i].b) + break; + } + // Find blocks up to the block containing endAddr + for ( ; i < bs.size(); ++i) { + l.push_back(i); + if (endAddr >= ba[i].a && endAddr <= ba[i].b) + break; + } + + // Erase blocks covering the range + erase(l); +} + +void +CMcu::erase(list blockList) +{ + // Construct mask + uint16_t mask = 0; + list::const_iterator it; + unsigned int bc = mMcuSpecifics->getBlockSizes().size(); + + for (it = blockList.begin(); it != blockList.end(); ++it) { + if (*it >= bc) { + ostringstream os; + os << "Block number " << *it << " is out of range [0," << (bc - 1) << "]"; + CLogger::error(os.str(), EXIT_MCU); + } + mask |= 1 << *it; + } + ostringstream os; + os << "Erasing blocks: "; + for (it = blockList.begin(); it != blockList.end(); ++it) { + if (it != blockList.begin()) + os << ","; + os << *it; + } + os << " (mask = " << CLogger::decToHex(mask) << ")"; + CLogger::info(os.str()); + + // Erase blocks + sendShellCommand(CMD_ERASE_BLOCKS); + mSerialPort.sendSafeWord(mask); + // Read status + mSerialPort.setReadTimeout(mMcuSpecifics->getEraseTimeout()); + uint16_t r = mSerialPort.readWord(); + + if (r != 0x00) + CLogger::error(getMessageForRetCode(r), EXIT_MCU); + + mSerialPort.setDefaultTimeout(); +} + +void +CMcu::write(vector data, bool printProgress) +{ + if ((data.size() < 1) || (data.size() > mMcuSpecifics->getFlashSize())) { + ostringstream os; + os << "Data length " << data.size() << " to write is not in range "; + os << "[1-" << mMcuSpecifics->getFlashSize() << "]"; + CLogger::error(os.str(), EXIT_MCU); + } + // Number of bytes to be written + uint32_t bw = ((data.size() % 2) == 1) ? data.size() + 1 : data.size(); + ostringstream os; + os << "Writing " << data.size() << " bytes"; + if (bw != data.size()) + os << " + 1 byte pad"; + CLogger::info(os.str()); + // Write command + sendShellCommand(CMD_WRITE); + // Write number of bytes to read + mSerialPort.sendSafeDoubleWord(bw); + // Write by 1024 blocks and read return status after each one + uint32_t i = 0; + + if (printProgress) + CLogger::progress(i, data.size()); + + while (i < bw) { + uint32_t s = ((bw - i) > 1024) ? 1024 : (bw - i); + if (((i + s) >= bw) && (bw != data.size())) { + // Going to write last block of size increased by pad + mSerialPort.write(data.data() + i, s - 1, s - 1); + uint8_t b = PAD_BYTE; + mSerialPort.write(&b, 1, 1); + } else { + mSerialPort.write(data.data() + i, s, s); + } + i += s; + + if (printProgress) + CLogger::progress(i, data.size()); + + // Read status + uint16_t r = mSerialPort.readWord(); + if (r == 0) { + // Read position + uint32_t u = mSerialPort.readDoubleWord(); + // Check position + if ((bw - u) != i) { + ostringstream os; + os << "Serial communication error: position status mismatch, expected " << i << ", have " << (bw - u); + CLogger::error(os.str(), EXIT_MCU); + } + } else { + CLogger::error(getMessageForRetCode(r), EXIT_MCU); + } + } +} + +vector +CMcu::read(bool printProgress) +{ + return read(mMcuSpecifics->getFlashSize(), printProgress); +} + +vector +CMcu::read(uint32_t size, bool printProgress) +{ + uint32_t r; + + if ((size < 1) || (size > mMcuSpecifics->getFlashSize())) { + ostringstream os; + os << "Data length " << size << " to read is outside address range "; + os << "[1-" << mMcuSpecifics->getFlashSize() << "]"; + CLogger::error(os.str(), EXIT_MCU); + } + + // Check if size is odd or even number + r = size; + if ((size % 2) == 1) + r++; + // Write command + sendShellCommand(CMD_READ); + // Write number of bytes to read + mSerialPort.sendSafeDoubleWord(r); + // Get data from FLASH memory by 1024 byte blocks + uint32_t i = 0; + if (printProgress) + CLogger::progress(i, size); + + vector data; + + while (i < r) { + uint32_t s = ((r - i) > 1024) ? 1024 : (r - i); + // Read data + data.resize(data.size() + s); + mSerialPort.read(data.data() + i , s); + i += s; + + if (printProgress) + CLogger::progress(i, size); + + // Read status + uint16_t t = mSerialPort.readWord(); + if (t == 0) { + // Read position + uint32_t u = mSerialPort.readDoubleWord(); + // Check position + if ((r - u) != i) { + ostringstream os; + os << "Serial communication error: position status mismatch"; + os << ", expected " << i << " has " << (r - u); + CLogger::error(os.str(), EXIT_MCU); + } + } else { + CLogger::error(getMessageForRetCode(r), EXIT_MCU); + } + } + + // Discard possible rounding/pad byte + data.resize(size); + return data; +} -- cgit v1.2.3