First commit

This commit is contained in:
2023-04-22 10:18:26 +02:00
commit ec23854ab7
1078 changed files with 986020 additions and 0 deletions
+566
View File
@@ -0,0 +1,566 @@
/* Routines for reading/writing Intel INHX8M and INHX32 files
Copyright 2002 Brandon Fosdick (BSD License)
*/
#include <fstream>
#include <stdio.h>
#include <stdlib.h>
#include <string>
//#include <unistd.h>
#include "intelhex.h"
namespace intelhex
{
#define INH32M_HEADER ":020000040000FA"
// Array access operator
value_type& hex_data::operator[](address_type address)
{
// Start at the end of the list and find the first (last) block with an address
// less than addr
reverse_iterator i = blocks.rbegin();
while( i != blocks.rend() )
{
if( i->first <= address )
{
// Use the block if address is interior or adjacent to the block
if( (address - i->first) <= i->second.size() )
return i->second[address - i->first];
break;
}
++i;
}
return blocks[address][0];
}
// Return the value at address, or _fill if not set
value_type hex_data::get(address_type address)
{
// Start at the end of the list and find the first (last) block with an address
// less than addr
reverse_iterator i = blocks.rbegin();
while( i != blocks.rend() )
{
if( i->first <= address )
{
// Use the block if address is interior to the block
if( (address - i->first) < i->second.size() )
return i->second[address - i->first];
break;
}
++i;
}
return _fill;
}
// Set the value at address or create a new element using value
void hex_data::set(address_type address, value_type value)
{
if( value == fill() ) // Handle fill values
{
erase(address); // If the address is already set, erase it
return;
}
// Start at the end of the list and find the first (last) block with an address
// less than addr
reverse_iterator i = blocks.rbegin();
while( i != blocks.rend() )
{
if( i->first <= address )
{
// Use the block if address is interior or adjacent to the block
const address_type index = address - i->first;
if( index < i->second.size() )
{
i->second[index] = value;
return;
}
else if( index == i->second.size() )
{
i->second.push_back(value);
return;
}
break;
}
++i;
}
blocks[address].push_back(value); // Otherwise create a new block
}
// Merge adjacent blocks
void hex_data::compact()
{
iterator previous = blocks.begin();
iterator i = previous;
for(++i; i != blocks.end(); ++i)
{
if( (previous->first + previous->second.size()) == i->first )
{
previous->second.insert(previous->second.end(), i->second.begin(), i->second.end());
blocks.erase(i);
i = previous;
}
previous = i;
}
}
// Delete all allocated memory
void hex_data::clear()
{
_fill = 0;
format = HEX_FORMAT_INHX8M;
linear_addr_rec = false;
segment_addr_rec = false;
blocks.clear();
}
// Erase a single element at the given address
void hex_data::erase(address_type address)
{
for(iterator i=blocks.begin(); i!=blocks.end(); ++i)
{
// The blocks are sorted, so if the byte to be deleted is
// before the block it must be a blank address that's either
// before the first block or after any previous blocks.
if( address < i->first )
break;
// Ignore the block if address is past the end of the block
const address_type ope = i->first + i->second.size();
if( address >= ope )
continue;
// address is now guaranteed to be >= i->first and < ope
// Copy trailing portion of the old block to a new block
if( (ope - address) > 1 )
{
const address_type index = address-i->first+1;
blocks[address+1].assign(i->second.begin()+index, i->second.end());
}
// Truncate or delete old block
const address_type size = address - i->first;
if( size )
i->second.resize(size);
else
blocks.erase(i);
break;
}
}
// Erase [first, last]
void hex_data::erase(address_type first, address_type last)
{
if( first > last )
std::swap(first, last);
for(iterator i=blocks.begin(); (i!=blocks.end()) && (first<=last); ++i)
{
const address_type ope = i->first + i->second.size();
if( first >= ope ) // Ignore all blocks with addresses < first
continue;
// The blocks are sorted, so if the first byte to be deleted is
// before the block it must be a blank address that's either
// before the first block or after any previous blocks.
if( first < i->first )
{
if( last < i->first ) // If the entire range is before the
return; // block there's nothing left to do
first = i->first; // Advance to the next non-blank address
}
// first is now guaranteed to be >= i->first and < ope
if( last < ope ) // Entire range is interior
{
// Copy trailing portion of the old block to a new block
if( (ope - last) > 1 )
{
const address_type index = last-i->first+1;
blocks[last+1].assign(i->second.begin()+index, i->second.end());
}
// Truncate or delete old block
const address_type size = first - i->first;
if( size )
i->second.resize(size);
else
blocks.erase(i);
return;
}
else // Truncate block
{
const address_type size = first - i->first;
if( size )
i->second.resize(size);
else
blocks.erase(i--);
first = ope;
}
}
}
hex_data::size_type hex_data::size()
{
size_type s=0;
for(iterator i=blocks.begin(); i!=blocks.end(); ++i)
s += i->second.size();
return s;
}
// Returns the number of populated elements with addresses less than addr
hex_data::size_type hex_data::size_below_addr(address_type addr)
{
size_type s=0;
for(iterator i=blocks.begin(); i!=blocks.end(); ++i)
{
if( (i->first + i->second.size()) < addr)
s += i->second.size();
else if( i->first < addr )
s += addr - i->first;
}
return s;
}
// number of words in [lo, hi)
hex_data::size_type hex_data::size_in_range(address_type lo, address_type hi)
{
size_type s=0;
for(iterator i=blocks.begin(); i!=blocks.end(); ++i)
{
if( i->first < lo )
{
const size_type a = i->first + i->second.size();
if( a >= lo )
s += a - lo;
}
else
{
if( (i->first + i->second.size()) < hi)
s += i->second.size();
else if( i->first < hi )
s += hi - i->first;
}
}
return s;
}
// Return the max address of all of the set words with addresses less than or equal to hi
address_type hex_data::max_addr_below(address_type hi)
{
address_type s=0;
for(iterator i=blocks.begin(); i!=blocks.end(); ++i)
{
if( i->first <= hi)
{
const address_type a = i->first + i->second.size() - 1; //Max address of this block
if( a > s )
s = a;
}
}
if( s > hi )
return hi;
else
return s;
}
// Lowest address
address_type hex_data::min_address() const
{
return blocks.begin()->first;
}
// Highest address
address_type hex_data::max_address() const
{
return blocks.rbegin()->first + blocks.rbegin()->second.size() - 1;
}
//Return true if an element exists at addr
bool hex_data::is_set(address_type addr)
{
// Start at the end of the list and find the first (last) block with an address
// less than addr
reverse_iterator i = blocks.rbegin();
while( (i!=blocks.rend()) && (i->first > addr))
++i;
if( (addr - i->first) > i->second.size() )
return false;
else
return true;
}
// Load from a file
void hex_data::load(const std::string &path)
{
std::ifstream f(path.c_str());
read(f);
}
// Convert a string from hex to binary and append it to a block
uint8_t hex2binary(hex_data::data_container& to, std::string& from)
{
value_type sum = 0, value;
uint8_t character;
bool first = true;
std::string::iterator i = from.begin();
while( i != from.end() )
{
character = *i;
if( (character >= '0') && (character <= '9') )
character -= '0';
else if( (character >= 'A') && (character <= 'Z') )
character -= 'A' - 10;
else if( (character >= 'a') && (character <= 'z') )
character -= 'a' - 10;
else
break; // Bad character
if( first )
value = character << 4;
else
{
value |= character;
to.push_back(value);
sum += value;
}
first = !first;
++i;
}
return sum;
}
// Read data from an input stream
void hex_data::read(std::istream &s)
{
address_type address;
address_type extended_address(0);
std::string line;
data_container buffer;
while( (s.get() == ':') && s.good() )
{
std::getline(s, line); // Read the whole line
if( line.size() <= 10 ) // Ignore truncated lines
break;
buffer.clear();
buffer.reserve(line.size()/2); // Pre-allocate
if( hex2binary(buffer, line) ) // Ignore lines with bad checksums
break;
address = buffer[1];
address = (address << 8) | buffer[2];
unsigned length = buffer[0];
const unsigned type = buffer[3];
value_type* data = &buffer[4];
switch(type)
{
case 0: //Data block
{
address += extended_address;
iterator i = blocks.begin();
for(; i != blocks.end(); ++i ) // Find a block that includes address
{
address_type num = 0;
// If the start of the new block is interior to an existing block...
if( (i->first <= address) && ( (i->first + i->second.size()) > address) )
{
// Store the portion of the new block that overlaps the existing block
const size_type index = address - i->first;
num = i->second.size() - index;
if( num > length )
num = length;
std::copy(data, data+num, &(i->second[index]));
}
// If the end of the new block is interior to an existing block...
if( (address < i->first) && ((address + length) > i->first) )
{
// Create a new block for the non-overlapping portion
num = i->first - address;
if( num > length )
num = length;
blocks[address].assign(data, data+num);
}
length -= num;
address += num;
data += num;
// Bail out early if there's nothing left to do
if( 0 == length )
break;
}
// Handle any leftover bytes
if( length )
blocks[address].assign(data, data+length);
break;
}
case 1: break; // Ignore EOF record
case 2: // Segment address record (INHX32)
if( (0 == address) && (2 == length) )
{
extended_address = buffer[4];
extended_address = (extended_address << 8) | buffer[5];
extended_address <<= 4;
segment_addr_rec = true;
}
break;
case 4: // Linear address record (INHX32)
if( (0 == address) && (2 == length) )
{
extended_address = buffer[4];
extended_address = (extended_address << 8) | buffer[5];
extended_address <<= 16;
linear_addr_rec = true;
}
break;
}
}
}
// Write all data to a file
void hex_data::write(const char *path)
{
std::ofstream ofs(path);
if( !ofs ) // Bail out on bad files
return;
write(ofs);
ofs.close();
}
// Write all data to an output stream
void hex_data::write(std::ostream &os)
{
uint8_t checksum;
uint16_t linear_address(0);
if( !os ) // Bail out on bad streams
return;
os.setf(std::ios::hex, std::ios::basefield); //Set the stream to ouput hex instead of decimal
os.setf(std::ios::uppercase); //Use uppercase hex notation
os.fill('0'); //Pad with zeroes
//If we already know that this is an INHX32M file, start with a segment address record
// otherwise check all of the blocks just to make sure
if( linear_addr_rec )
{
os << INH32M_HEADER << std::endl;
}
else
{
for(iterator i=blocks.begin(); i!=blocks.end(); i++)
{
if(i->first > 0xFFFF) //Check the upper 16 bits
{
linear_addr_rec = true;
os << INH32M_HEADER << std::endl;
break; //Only need to find one
}
}
}
for(iterator i=blocks.begin(); i!=blocks.end(); i++)
{
// Check upper 16 bits of the block address for non-zero,
// which indicates that a segment address record is needed
if( i->first > 0xFFFF )
{
const uint16_t addr(i->first >> 16);
//Has a record for this segment already been emitted?
if( addr != linear_address )
{
//Emit a new segment address record
os << ":02000004";
os.width(4);
os << addr; //Address
// Create a checksum for the linear address record
checksum = 0x06 + addr + (addr >> 8);
checksum = 0x01 + ~checksum;
os.width(2);
// OSX (or maybe GCC), seems unable to handle uint8_t
// arguments to a stream
os << static_cast<uint16_t>(checksum); // Checksum byte
os << std::endl;
linear_address = addr;
}
}
checksum = 0;
os << ':'; //Every line begins with ':'
os.width(2);
os << i->second.size(); //Length
checksum += i->second.size();
os.width(4);
os << static_cast<uint16_t>(i->first); //Address
checksum += static_cast<uint8_t>(i->first); // Low byte
checksum += static_cast<uint8_t>(i->first >> 8); // High byte
os << "00"; //Record type
for(unsigned j=0; j<i->second.size(); ++j) //Store the data bytes, LSB first, ASCII HEX
{
os.width(2);
// OSX (or maybe GCC), seems unable to handle uint8_t
// arguments to a stream
os << static_cast<uint16_t>(i->second[j]);
checksum += i->second[j];
}
checksum = 0x01 + ~checksum;
os.width(2);
// OSX (or maybe GCC), seems unable to handle uint8_t arguments to a stream
os << static_cast<uint16_t>(checksum); // Checksum byte
os << std::endl;
}
os << ":00000001FF\n"; //EOF marker
}
// Make things pretty
// Truncate blocks to a given length as needed
void hex_data::tidy(hex_data::size_type length)
{
for(iterator i=blocks.begin(); i!=blocks.end(); i++)
{
if(i->second.size() > length) //If the block is too long...
{
//Make an interator that points to the first element to copy out of i->second
data_container::iterator k(i->second.begin());
advance(k, length);
// Assign the extra elements to a new block and truncate the original
blocks[i->first + length].assign(k, i->second.end());
i->second.erase(k, i->second.end());
}
}
}
//Compare two sets of hex data
// Return true if every word in hex1 has a corresponding, and equivalent, word in hex2
bool compare(hex_data& hex1, hex_data& hex2, value_type mask, address_type begin, address_type end)
{
//Walk block list from hex1
for( hex_data::iterator i = hex1.begin(); i != hex1.end(); ++i )
{
//Walk the block
address_type addr(i->first);
for( hex_data::data_container::iterator j = i->second.begin(); j != i->second.end(); ++j, ++addr)
{
if( (addr < begin) || (addr > end) )
continue;
//Compare both sides through the given mask
if( ((*j) & mask) != (hex2.get(addr) & mask) )
return false;
}
}
return true;
}
}
+83
View File
@@ -0,0 +1,83 @@
/* Routines for reading/writing Intel INHX8M and INHX32 files
Copyright 2002 Brandon Fosdick (BSD License)
*/
#ifndef INTELHEXH
#define INTELHEXH
#include <iostream>
#include <map>
#include <vector>
//#include <unistd.h>
namespace intelhex
{
#define HEX_FORMAT_INHX8M 0x01
#define HEX_FORMAT_INHX32 0x02
class hex_data;
typedef hex_data container;
typedef uint32_t address_type;
typedef uint8_t value_type;
//The data set that results from parsing a hex file
struct hex_data {
//Each line of the hex file generates a block of memory at a particular address
typedef std::vector<value_type> data_container; //Element container
typedef std::map<address_type, data_container> container; //List of data blocks
typedef container::iterator iterator;
typedef container::reverse_iterator reverse_iterator;
typedef data_container::size_type size_type;
private:
value_type _fill; // Value returned for unset addresses
char format; //Format of the parsed file (necessary?)
bool segment_addr_rec; // Uses/Has a segment address record
bool linear_addr_rec; // Uses/Has a linear address record
container blocks; // List of data blocks
public:
hex_data() : _fill(0), segment_addr_rec(false), linear_addr_rec(false) {}
hex_data(const std::string &s) : _fill(0), segment_addr_rec(false), linear_addr_rec(false)
{
load(s);
}
iterator begin() { return blocks.begin(); }
iterator end() { return blocks.end(); }
void compact(); // Merge adjacent blocks
void clear(); //Delete everything
void erase(address_type); // Erase a single element
void erase(address_type first, address_type last); // Erase [first, last]
value_type fill() { return _fill; }
void fill(value_type f) { _fill = f; }
size_type size();
size_type size_below_addr(address_type);
size_type size_in_range(address_type, address_type); //number of words in [lo, hi)
address_type max_addr_below(address_type);
address_type min_address() const; // Lowest address
address_type max_address() const; // Highest address
bool is_set(address_type);
value_type& operator[](address_type); //Array access operator
value_type get(address_type); // Return the value at address
void set(address_type, value_type); // Set the value at address
void load(const std::string&); // Load from a file
void read(std::istream &); // Read data from an input stream
void write(const char *); //Save hex data to a hex file
void write(std::ostream &); //Write all data to an output stream
void tidy(size_type length); // Make things pretty
};
bool compare(hex_data&, hex_data&, value_type, address_type, address_type);
}
#endif
+287
View File
@@ -0,0 +1,287 @@
#include <string>
#include <iostream>
#include <map>
#include <array>
#include <thread>
#include <chrono>
#include "intelhex.h"
#include "serial/serial.h"
#define ACK 0x79
#define NACK 0x1F
#define CMD_ERASE 0x43
#define CMD_GETID 0x02
#define CMD_WRITE 0x2b
using std::cout, std::endl, std::cerr;
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
#define APPNAME "C:/Users/gritt/Documents/workspace/STM32/Custom-Bootloader/App1/build/App1.hex"
#define COMPORT "COM3"
std::map <uint16_t, std::string> deviceIDs = {
{0x410, "STM32F103RB"},
{0x415, "STM32L152RG"},
{0x417, "STM32L053R8"},
{0x421, "STM32F446RE"},
{0x431, "STM32F411RE"},
{0x433, "STM32F401RE"},
{0x437, "STM32L152RE"},
{0x439, "STM32F302R8"},
{0x438, "STM32F334R8"},
{0x440, "STM32F030R8"},
{0x442, "STM32F091RC"},
{0x446, "STM32F303RE"},
{0x447, "STM32L073RZ"},
{0x448, "STM32F070RB/STM32F072RB"},
{0x458, "STM32F410RB"},
};
u32 computeStm32Crc(u32* buffer, u32 len) {
u32 crc = 0xffffffff;
for (u32 i = 0; i < len; ++i) {
crc ^= buffer[i];
for (u8 j = 0; j < 32; ++j) {
if (crc & 0x80000000) {
crc = (crc << 1) ^ 0x04C11DB7; // Polynomial used in STM32
}
else {
crc = (crc << 1);
}
}
}
return (crc & 0xffffffff);
}
u32 computeStm32Crc_bytes(u8* buffer, u32 len) {
u32 crc = 0xffffffff;
for (u32 i = 0; i < len; ++i) {
u32 data = buffer[i];
crc ^= data;
for (u8 j = 0; j < 32; ++j) {
if (crc & 0x80000000) {
crc = (crc << 1) ^ 0x04C11DB7; // Polynomial used in STM32
}
else {
crc = (crc << 1);
}
}
}
return (crc & 0xffffffff);
}
class Flasher {
public:
Flasher(std::string com) :
timeout (0, 1000, 1, 1000, 1)
{
stm32Serial.setPort(com);
stm32Serial.setBaudrate(115200);
stm32Serial.setTimeout(timeout);
try {
stm32Serial.open();
u32 len = stm32Serial.available();
if (len != 0) {
stm32Serial.read(len);
}
}
catch (...) {
cerr << "Unable to open serial port" << endl;
}
}
~Flasher() {
stm32Serial.close();
}
u32 getDeviceID() {
if (!stm32Serial.isOpen()) {
cerr << "Unable to open serial " << COMPORT << endl;
return 0;
}
std::array <u8, 20> payload;
std::fill(std::begin(payload), std::end(payload), 0);
payload[0] = CMD_GETID;
u32 crc = computeStm32Crc((u32*)payload.data(), 1);
memcpy(payload.data() + 1, &crc, sizeof(u32));
stm32Serial.write(payload.data(), 20);
// read 1 byte (ACK or NACK)
u32 len = stm32Serial.read(payload.data(), 1);
if (len != 1) {
cerr << "Error reading device ID\n" << endl;
return 0;
}
if (payload[0] == ACK) {
len = stm32Serial.read(payload.data(), 2);
if (len == 2) {
u32 deviceID = 0;
memcpy(&deviceID, payload.data(), 2);
return deviceID;
}
}
else {
cerr << "Received NACK on device ID request" << endl;
return 0;
}
}
u8 eraseFlash () {
if (!stm32Serial.isOpen())
return 1;
std::array <u8, 20> payload;
payload[0] = CMD_ERASE;
payload[1] = 0xff;
u32 crc = computeStm32Crc_bytes(payload.data(), 2);
memcpy(payload.data()+2, &crc, sizeof(u32));
stm32Serial.write(payload.data(), 20);
// read 1 byte (ACK or NACK)
// since erase operation can take several seconds, increase read timeout to 50s
timeout.read_timeout_constant = 50000;
stm32Serial.setTimeout(timeout);
u32 len = stm32Serial.read(payload.data(), 1);
if (len == 0) {
cerr << "read timed out" << endl;
return 2;
}
else {
if (payload[0] == ACK) {
cout << "received ACK" << endl;
return 0;
}
else {
cerr << "received NACK" << endl;
return 3;
}
}
}
void flashImage (std::string image) {
intelhex::hex_data program = intelhex::hex_data(APPNAME);
std::array <u8, 20> requestPayload;
std::array <u8, 20> dataPayload;
std::array <u8, 20> readBuffer;
requestPayload[0] = CMD_WRITE;
u32 line = 1;
for (auto& record : program) {
cout << std::dec << (int)line++ << ":Write at " << std::hex << (int) record.first << endl;
// send write request and address
memcpy(requestPayload.data()+1, &record.first,sizeof (u32));
u32 crc = computeStm32Crc_bytes(requestPayload.data(), 5);
memcpy(requestPayload.data()+5, &crc, sizeof(u32));
stm32Serial.write(requestPayload.data(), 20);
u8 len = stm32Serial.read(readBuffer.data(), 1);
if (len == 0 || len==1 && readBuffer[0] == NACK)
return;
// send data
for (u8 i = 0; i < 16; i++) {
if (i < record.second.size())
dataPayload[i] = record.second[i];
else
dataPayload[i] = 0xff;
}
crc = computeStm32Crc((u32*)dataPayload.data(), 4);
memcpy(dataPayload.data()+16, &crc, sizeof(u32));
stm32Serial.write(dataPayload.data(), 20);
len = stm32Serial.read(readBuffer.data(), 1);
if (len == 0) {
cerr << "TIMEOUT" << endl;
return;
}
if (len == 1 && readBuffer[0] == NACK) {
cerr << "NACK" << endl;
return;
}
cout << "Done" << endl;
}
}
private:
// open serial port
serial::Timeout timeout;
serial::Serial stm32Serial;
};
int main() {
Flasher flasher(COMPORT);
u32 devID = flasher.getDeviceID();
cout << "Devide ID: " << devID << endl;
if (devID == 0)
return 1;
std::string device = deviceIDs[devID];
cout << "found " << device << endl;
cout << "Erase flash" << endl;
if (flasher.eraseFlash() != 0) {
cerr << "Error erasing flash" << endl;
return 2;
}
cout << "Done" << endl;
flasher.flashImage(APPNAME);
cout << "IMAGE WRITING DONE" << endl;
return 0;
}