add programmer

This commit is contained in:
Kizarm 2025-02-03 19:18:47 +01:00
parent 65515c7eca
commit b9052ca694
27 changed files with 2102 additions and 0 deletions

View file

@ -0,0 +1,53 @@
TARGET?= ch32v203
TOOL ?= gcc
#TOOL ?= clang
PRJ = example
VPATH = . ./$(TARGET) ./common
BLD = ./build/
DFLAGS = -d
LFLAGS = -g
LDLIBS = -L./$(TARGET)/usbd -lusbd
BFLAGS = --strip-unneeded
CFLAGS = -MMD -Wall -Wno-parentheses -ggdb -fno-exceptions -ffunction-sections -fdata-sections
CFLAGS+= -I. -I./$(TARGET) -I./$(TARGET)/usbd -I./common
DEL = rm -f
# zdrojaky
OBJS = main.o usb_desc.o hack.o
OBJS += intelhex.o linkprotocol.o norflash.o spiblocked.o
include $(TARGET)/$(TOOL).mk
BOBJS = $(addprefix $(BLD),$(OBJS))
all: $(BLD) $(PRJ).elf
# ... atd.
-include $(BLD)*.d
# linker
$(PRJ).elf: $(BOBJS)
-@echo [LD $(TOOL),$(TARGET)] $@
@$(LD) $(LFLAGS) -o $(PRJ).elf $(BOBJS) $(LDLIBS)
-@echo "size:"
@$(SIZE) $(PRJ).elf
-@echo "listing:"
$(DUMP) $(DFLAGS) $(PRJ).elf > $(PRJ).lst
-@echo "OK."
$(COPY) $(BFLAGS) -O binary $(PRJ).elf $(PRJ).bin
# preloz co je potreba
$(BLD)%.o: %.c
-@echo [CC $(TOOL),$(TARGET)] $@
@$(CC) -std=gnu99 -c $(CFLAGS) $< -o $@
$(BLD)%.o: %.cpp
-@echo [CX $(TOOL),$(TARGET)] $@
@$(CXX) -std=c++17 -fno-rtti -c $(CFLAGS) $< -o $@
$(BLD):
mkdir $(BLD)
flash: $(PRJ).elf
minichlink -w $(PRJ).bin flash -b
# vycisti
clean:
$(DEL) $(BLD)* *.lst *.bin *.elf *.map *~
.PHONY: all clean flash run

View file

@ -0,0 +1 @@
../ch32v203/

1
V203F6P6/programmer/common Symbolic link
View file

@ -0,0 +1 @@
../common/

View file

@ -0,0 +1,112 @@
#ifndef ENDIEN_H
#define ENDIEN_H
#include <stdint.h>
/**
@file
@brief Pořadí bytů ve slově.
*/
#if __GNUC__ < 4 // Compiler test
#error "Zkontroluj nastaveni"
#endif
// Pro 16.bit musíme struktury pakovat
#define PACKSTRUCT __attribute__((packed))
#if __thumb__
// Specificky a krátce pro danou architektutu a pokud chceme mít úplnou kontrolu.
static inline uint32_t swab32 (uint32_t p) {
register uint32_t t;
asm volatile ("rev %0, %1":"=r"(t):"0"(p));
return t;
}
#else //__thumb__
// Obecně
static inline uint32_t swab32 (uint32_t p) {
// return __builtin_bswap32 (p); // I takto to jde, ale pro názornost:
return ((p & 0x000000ff) << 24)
| ((p & 0x0000ff00) << 8)
| ((p & 0x00ff0000) >> 8)
| ((p & 0xff000000) >> 24);
}
#endif //__thumb__
// Tohle nebudeme ani specifikovat pro ARM
static inline uint16_t swab16 (uint16_t p) {
return (p << 8) | (p >> 8);
}
// Nedělá nic a ani nijak neotravuje - pouze pro kompatibilitu
static inline uint32_t nosw32 (uint32_t p) {
return p;
}
static inline uint16_t nosw16 (uint16_t p) {
return p;
}
// Specificky podle pořadí bytů - LE je pro ARM i X86 normální.
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#define cpu_to_be32 swab32
#define be32_to_cpu swab32
#define cpu_to_le32 nosw32
#define le32_to_cpu nosw32
#define cpu_to_be16 swab16
#define be16_to_cpu swab16
#define cpu_to_le16 nosw16
#define le16_to_cpu nosw16
#else
#define cpu_to_be32 nosw32
#define be32_to_cpu nosw32
#define cpu_to_le32 swab32
#define le32_to_cpu swab32
#define cpu_to_be16 nosw16
#define be16_to_cpu nosw16
#define cpu_to_le16 swab16
#define le16_to_cpu swab16
#endif
/**
@class ui32be
@brief C++ 32.bit data v BIG Endien.
@class ui32le
@brief C++ 32.bit data v LITTLE Endien.
@class ui16be
@brief C++ 16.bit data v BIG Endien.
@class ui16le
@brief C++ 16.bit data v LITTLE Endien.
*/
class ui32be {
public:
/// Nastavení hodnoty
/// @par p uint32_t hodnota
void set (uint32_t p) {num = cpu_to_be32 (p); };
/// Zjištění hodnoty
/// @return uint32_t hodnota
uint32_t get (void) {return be32_to_cpu (num); };
private:
/// Vlastní data uint32_t - k nim se přímo nedostaneme, jsou privátní
uint32_t num;
}PACKSTRUCT;
class ui32le {
public:
void set (uint32_t p) {num = cpu_to_le32 (p); };
uint32_t get (void) {return le32_to_cpu (num); };
private:
uint32_t num;
}PACKSTRUCT;
/* *******************************************************/
class ui16be {
public:
void set (uint16_t p) {num = cpu_to_be16 (p); };
uint16_t get (void) {return be16_to_cpu (num); };
private:
uint16_t num;
}PACKSTRUCT;
class ui16le {
public:
void set (uint16_t p) {num = cpu_to_le16 (p); };
uint16_t get (void) {return le16_to_cpu (num); };
private:
uint16_t num;
}PACKSTRUCT;
#endif // ENDIEN_H

View file

@ -0,0 +1,21 @@
#include <stdint.h>
#include <stdarg.h>
typedef __SIZE_TYPE__ size_t;
size_t strlen (const char *s) {
size_t l = 0;
while (*s++) l++;
return l;
}
void *memcpy (void *dest, const void *src, size_t n) {
const char *s = (const char *) src;
char *d = (char *) dest;
int i;
for (i=0; i<n; i++) d[i] = s[i];
return dest;
}
void *memset (void *s, int c, size_t n) {
char *p = (char *) s;
int i;
for (i=0; i<n; i++) p[i] = c;
return s;
}

View file

@ -0,0 +1,57 @@
#ifndef HELPERS_H
#define HELPERS_H
#include <stdint.h>
#include "norflash.h"
#include "fifo.h"
#include "baselayer.h"
class MemoryBase {
NorFlash flash;
public:
explicit MemoryBase () noexcept : flash() {}
uint32_t Write (const uint32_t addr, const void * ptr, const uint32_t len) {
return flash.WriteBlock(addr, reinterpret_cast<const uint8_t*>(ptr), len);
}
uint32_t Read (const uint32_t addr, void * ptr, const uint32_t len) {
return flash.ReadBlock(addr, reinterpret_cast<uint8_t*> (ptr), len);
}
void Erase (const uint32_t blok) {
flash.EraseSector(blok);
}
};
class CdcCmd : public BaseLayer {
static constexpr int maxlen = 0x80;
FIFO<char, maxlen> ring;
char line [maxlen];
unsigned index;
public:
explicit CdcCmd () noexcept : BaseLayer(), ring(), index(0u) {}
uint32_t Up(const char * data, const uint32_t len) override {
for (unsigned n=0u; n<len; n++) ring.Write (data [n]);
return len;
}
void SendString (const char * ptr, const uint32_t len) {
unsigned ofs = 0u, res = len;
while (res) {
const unsigned chunk = res > 0x20u ? 0x20u : res;
const unsigned n = Down (ptr + ofs, chunk);
ofs += n;
res -= n;
}
}
char * GetLine (unsigned & len) {
char c;
while (ring.Read(c)) {
line [index++] = c;
if (c == '\n') {
len = index;
index = 0u;
return line;
}
}
len = 0u;
return line;
}
};
#endif // HELPERS_H

View file

@ -0,0 +1,249 @@
#include <stdio.h>
#include "string.h"
#include "endien.h"
#include "intelhex.h"
static uint8_t hexval (const char c) {
if ('0' <= c && c <= '9')
return (uint8_t) c - '0';
else if ('a' <= c && c <= 'f')
return (uint8_t) c - 'a' + 10;
else return 0;
}
static unsigned to_hex_str (char * str, const uint8_t * data, const unsigned len) {
unsigned result = 0;
const char * hexstr = "0123456789ABCDEF";
for (unsigned n=0; n<len; n++) {
const uint8_t c = data [n];
str [result++] = hexstr [c >> 4];
str [result++] = hexstr [c & 0xF];
}
return result;
}
static uint8_t chk_sum (const uint8_t * data, const unsigned len) {
uint8_t sum = 0;
for (unsigned n=0; n<len; n++) sum += data [n];
return sum;
}
union IhxHeader {
struct {
uint8_t reclen;
ui16be load;
RowTypes rectyp;
}__attribute__((packed));
uint8_t bytes [4];
};
union UlbaAddr {
ui16be addr;
uint8_t bytes [2];
};
union EipAddr {
ui32be addr;
uint8_t bytes [4];
};
IntelHex::IntelHex() noexcept : address(0), offset(0), lenght(0), total_lenght(0), ackofs(0) {
}
RowTypes IntelHex::parseRecord (const char * data, const unsigned int len) {
//printf ("parseRecord: %d %s\r\n", len, data);
if (data[0] != (uint8_t) ':') return badRecord;
uint8_t tmp [(len >> 1) + 1];
uint8_t val = 0;
unsigned k = 0;
for (unsigned n=1; n<len; n++) {
const uint8_t c = data [n];
if (c == '\r') break;
if (c == '\n') break;
val *= 0x10;
val += hexval (c | 0x20);
if ((n & 1) == 0) {
tmp [k++] = val;
val = 0;
}
}
k -= 1;
uint8_t chk = 0;
for (unsigned n=0; n<k; n++) chk += tmp [n];
chk = 0 - chk;
// kontrolní součet
if (chk != tmp[k]) return badRecord;
// chybná délka
if (k < sizeof (IhxHeader)) return badRecord;
IhxHeader hdr;
memcpy (hdr.bytes, tmp, sizeof(IhxHeader));
// chybná délka
if (k != (hdr.reclen + sizeof(IhxHeader))) return badRecord;
//printf ("%d: %d=%d, %d, %02X=%02X\n", len, k, hdr.reclen+4, hdr.rectyp, chk, tmp[k]);
UlbaAddr a1; EipAddr a2;
RowTypes t = hdr.rectyp;
lenght = hdr.reclen;
const uint8_t * start = tmp + sizeof(IhxHeader);
uint32_t tmpadr;
// uint16_t old_ofs = offset;
switch (t) {
case dataRecord:
memcpy (chunk, start, lenght);
offset = hdr.load.get();
// if (old_ofs != offset) printf ("Posun 0x%04X-0x%04X\n", old_ofs, offset);
total_lenght = offset + lenght;
break;
case elaRecord:
total_lenght = 0;
memcpy (a1.bytes, start, lenght);
address = (uint32_t) a1.addr.get() << 16;
offset = hdr.load.get();
break;
case slaRecord:
memcpy (a2.bytes, start, lenght);
address = a2.addr.get();
offset = hdr.load.get();
break;
case ackRecord:
case nakRecord:
ackofs = hdr.load.get();
break;
case eofRecord:
case reqRecord:
case ersRecord:
offset = hdr.load.get();
break;
// Tyhle 2 rekordy nějak fungují, ale je to divné. Bohužel třeba objcopy
// je na nízkých adresách z nějakého důvodu používá. Možná je v tom chyba.
case esaRecord:
total_lenght = 0;
memcpy (a1.bytes, start, lenght);
address = (uint32_t) a1.addr.get() << 4;
offset = hdr.load.get();
break;
case ssaRecord:
memcpy (a2.bytes, start, lenght);
tmpadr = a2.addr.get();
address = (tmpadr >> 12); // CS:IP ? čerti vědí, jak je to myšleno
offset = hdr.load.get();
break;
default:
break;
}
return t;
}
bool IntelHex::CompareAckOffsset() {
//printf ("ofs: %04X=%04X\n", ackofs, offset);
if (ackofs == offset) return true;
return false;
}
void IntelHex::getAddress (uint32_t & adr) {
adr = address;
}
void IntelHex::getLenght (uint32_t & len) {
len = total_lenght;
}
void IntelHex::getOffset (uint32_t & ofs) {
ofs = offset;
}
void IntelHex::WriteData (uint8_t * data) {
memcpy (data + offset, chunk, lenght);
offset += lenght;
}
uint8_t * IntelHex::getDataRow (uint32_t & ofs, uint32_t & len) {
ofs = offset;
len = lenght;
return chunk;
}
void IntelHex::AddOffset (uint32_t len) {
offset += len;
}
uint32_t IntelHex::ElaRecord (char * string, const uint32_t adr) {
offset = 0;
uint32_t result = 0;
string [result++] = ':';
UlbaAddr lba;
IhxHeader hdr;
hdr.load.set(0);
hdr.reclen = sizeof (lba);
hdr.rectyp = elaRecord;
lba.addr.set (adr >> 16);
uint8_t chk = 0;
chk += chk_sum (hdr.bytes, sizeof(IhxHeader));
chk += chk_sum (lba.bytes, sizeof(UlbaAddr ));
chk = 0 - chk;
result += to_hex_str (string + result, hdr.bytes, sizeof(IhxHeader));
result += to_hex_str (string + result, lba.bytes, sizeof(UlbaAddr ));
result += to_hex_str (string + result, & chk, 1);
string [result++] = '\r';
string [result++] = '\n';
string [result] = '\0';
return result;
}
uint32_t IntelHex::SlaRecord (char* string, const uint32_t adr) {
offset = 0;
uint32_t result = 0;
string [result++] = ':';
EipAddr lba;
IhxHeader hdr;
hdr.load.set(0);
hdr.reclen = sizeof (lba);
hdr.rectyp = slaRecord;
lba.addr.set (adr);
uint8_t chk = 0;
chk += chk_sum (hdr.bytes, sizeof(IhxHeader));
chk += chk_sum (lba.bytes, sizeof(EipAddr ));
chk = 0 - chk;
result += to_hex_str (string + result, hdr.bytes, sizeof(IhxHeader));
result += to_hex_str (string + result, lba.bytes, sizeof(EipAddr ));
result += to_hex_str (string + result, & chk, 1);
string [result++] = '\r';
string [result++] = '\n';
string [result] = '\0';
return result;
}
uint32_t IntelHex::DataRecord (char * string, const uint8_t * data, const uint32_t len) {
uint32_t result = 0;
string [result++] = ':';
IhxHeader hdr;
hdr.load.set (offset);
hdr.reclen = len;
hdr.rectyp = dataRecord;
uint8_t chk = 0;
chk += chk_sum (hdr.bytes, sizeof(IhxHeader));
chk += chk_sum (data, len);
chk = 0 - chk;
result += to_hex_str (string + result, hdr.bytes, sizeof(IhxHeader));
if (len) result += to_hex_str (string + result, data, len);
result += to_hex_str (string + result, & chk, 1);
string [result++] = '\r';
string [result++] = '\n';
string [result] = '\0';
return result;
}
uint32_t IntelHex::EofRecord (char * string) {
offset = 0;
const char * eof = ":00000001FF\r\n";
const uint32_t result = strlen (eof);
memcpy (string, eof, result);
string [result] = '\0';
return result;
}
uint32_t IntelHex::BTxRecord (char * string, RowTypes t) {
uint32_t result = 0;
string [result++] = ':';
IhxHeader hdr;
hdr.load.set (offset);
hdr.reclen = 0;
hdr.rectyp = t;
uint8_t chk = 0;
chk += chk_sum (hdr.bytes, sizeof(IhxHeader));
chk = 0 - chk;
result += to_hex_str (string + result, hdr.bytes, sizeof(IhxHeader));
result += to_hex_str (string + result, & chk, 1);
string [result++] = '\r';
string [result++] = '\n';
string [result] = '\0';
//offset += len;
return result;
}

View file

@ -0,0 +1,138 @@
#ifndef INTELHEX_H
#define INTELHEX_H
#include <stdint.h>
/// Maximální délka rekordu
static const unsigned MaxHexRecord = 0x80u;
enum RowTypes : uint8_t {
// defined by Intel
dataRecord = 0, //!< datový paket
eofRecord, //!< konec dat
esaRecord, //!< X86/16.bit CS/IP počáteční adresa
ssaRecord, //!< X86/16.bit CS/IP startovací adresa
elaRecord, //!< počáteční adresa 32-bit (horních 16.bit)
slaRecord, //!< startovací adresa 32-bit
// ... defined by BT protocol
// všechny budou používat jen offset pro přenos dat.
ackRecord, //!< offset - představuje offset předchozích dat
nakRecord, //!< stejně jako předchozí
reqRecord, //!< Request for data - offset značí odkud vzhledem k počáteční adrese
ersRecord, //!< Erase - po blocích, offset je číslo bloku (závisí na typu flash)
badRecord, //!< použito pro detekci chyb
};
/// @enum RowTypes
/// Enumerace datových typů Intel-hex formátu
/**
* @class IntelHex
* @brief Hepler pro parsování a vytváření paketů Intel HEX formátu.
*
* Prvních 6 typů (#RowTypes) je definováno ve specifikaci např.
* <a href="https://cs.wikipedia.org/wiki/Intel_HEX">zde</a>. A protože je tam dost volného
* místa pro rozšíření, jsou zde dodefinovány další typy použité pro komunikaci. Je to tak
* jednodušší než vymýšlet nějaký vlastní formát pro potvrzování a jiné. Protokol je znakový,
* poměrně robustní, na druhou stranu dost velku režii.
*
* Ukázalo sem že úzkým hrdlem komunikace je čekání na potvrzování, takže se režie poněkud zastírá,
* ale potvrzování být musí pokud to trochu spolehlivě fungovat.
* */
class IntelHex {
public:
explicit IntelHex() noexcept;
/**
* @brief Základní funkce parseru
*
* @param data vstupní řádek (paket)
* @param len jeho délka (např. strlen(data))
* @return RowTypes - to se pak zpracovává dál v nadřazené části např. LinkProtocol
*/
RowTypes parseRecord (const char * data, const unsigned len);
/**
* @brief Nastavení adresy
*
* Buď počáteční nebo starovací.
*
* @param adr odkaz na proměnnou, kam bude adresa nastavena
*/
void getAddress (uint32_t & adr);
/**
* @brief Nastavení délky
* @param len odkaz na proměnnou, kam bude délka nastavena
*/
void getLenght (uint32_t & len);
/**
* @brief Nastavení ofsetu
* @param ofs odkaz na proměnnou, kam bude ofset nastaven
*/
void getOffset (uint32_t & ofs);
/**
* @brief Zápis dat z příchozího dataRecord
*
* @param data kam se zapíší
*/
void WriteData (uint8_t * data);
/**
* @brief Posun ofsetu kvůli udržení konzistence
*
* @param len o kolik se posunout
*/
void AddOffset (uint32_t len);
/**
* @brief Vytvoření dataRecord
*
* @param string kam se record zapíše
* @param data pure data rekordu
* @param len a jejich délka
* @return uint32_t kolik bylo zapsáno do string
*/
uint32_t DataRecord (char * string, const uint8_t * data, const uint32_t len);
/**
* @brief Vytvoření elaRecord
*
* @param string kam se record zapíše
* @param adr počáteční adresa
* @return uint32_t kolik znaků bylo zapsáno do string
*/
uint32_t ElaRecord (char * string, const uint32_t adr);
/**
* @brief Vytvoření slaRecord
*
* @param string kam se record zapíše
* @param adr startovací adresa
* @return uint32_t kolik znaků bylo zapsáno do string
*/
uint32_t SlaRecord (char * string, const uint32_t adr);
/**
* @brief Vytvoření eofRecord
*
* @param string kam se record zapíše
* @return uint32_t kolik znaků bylo zapsáno do string
*/
uint32_t EofRecord (char * string);
/**
* @brief Vytvoření BT rekordů ackRecord, nakRecord, reqRecord, ersRecord
*
* @param string kam se record zapíše
* @param t definuj typ rekordu
* @return uint32_t kolik znaků bylo zapsáno do string
*/
uint32_t BTxRecord (char * string, RowTypes t = ackRecord);
/// pomocná funkce
bool CompareAckOffsset ();
/// pomocná funkce
uint8_t * getDataRow (uint32_t & ofs, uint32_t & len);
protected:
private:
uint32_t address;
uint32_t offset;
uint32_t lenght;
uint32_t total_lenght;
uint32_t ackofs;
uint8_t chunk [MaxHexRecord];
};
#endif // INTELHEX_H

View file

@ -0,0 +1,75 @@
#include <string.h>
#include "helpers.h"
#include "linkprotocol.h"
#define trace(...)
LinkProtocol::LinkProtocol(CdcCmd & g, MemoryBase & b) noexcept : cdccmd(g), ihx(), driver(b),
begin_addr(0), start_addr(0), lenght (0) {
}
void LinkProtocol::ParseLine (const char * line) {
const unsigned readen = strlen (line);
RowTypes t = ihx.parseRecord (line, readen);
switch (t) {
case dataRecord: AcquireDataRecord (); break;
case elaRecord: AcquireElaRecord (); break;
case slaRecord: AcquireSlaRecord (); break;
case eofRecord: AcquireEofRecord (); break;
case reqRecord: AcquireReqRecord (); break;
case ersRecord: AcquireErsRecord (); break;
default:
trace ("BAD record 0x%02X\r\n", t);
break;
}
}
void LinkProtocol::AcquireDataRecord() {
uint32_t ofs, len;
uint8_t * ptr = ihx.getDataRow (ofs, len);
uint32_t res = driver.Write (begin_addr + ofs, ptr, len);
if (res) {
SendAck();
ihx.AddOffset (res); // posun offsetu az po ack
return;
}
SendAck (false);
}
// První paket, který musí přijít při každé akci.
void LinkProtocol::AcquireElaRecord() {
ihx.getAddress (begin_addr);
trace("Begin = 0x%08X\r\n", begin_addr);
SendAck();
}
typedef void (*pHandler) (void);
void LinkProtocol::AcquireSlaRecord() {
ihx.getAddress (start_addr);
trace("Start = 0x%08X\r\n", start_addr);
}
void LinkProtocol::AcquireEofRecord() {
ihx.getLenght (lenght);
trace ("Lenght = %d\r\n", lenght);
SendAck();
}
void LinkProtocol::AcquireReqRecord() {
static constexpr unsigned chunk_size = 0x20;
uint8_t data [chunk_size];
uint32_t res, len, ofs;
len = chunk_size;
ihx.getOffset (ofs);
//trace ("AcquireReqRecord: %04X, len=%d\r\n", ofs, len);
res = driver.Read (begin_addr + ofs, data, len);
res = ihx.DataRecord(strbuf, data, res);
cdccmd.SendString (strbuf, res);
}
void LinkProtocol::AcquireErsRecord() {
uint32_t block;
ihx.getOffset (block);
driver.Erase (block);
SendAck();
}
void LinkProtocol::SendAck (bool ok) {
RowTypes t = ok ? ackRecord : nakRecord;
const uint32_t r = ihx.BTxRecord (strbuf, t);
cdccmd.SendString (strbuf, r);
}

View file

@ -0,0 +1,55 @@
#ifndef LINKPROTOCOL_H
#define LINKPROTOCOL_H
#include <stdint.h>
#include "intelhex.h"
class CdcCmd;
class MemoryBase;
/**
* @class LinkProtocol
* @brief Protokol po CDC.
*
* Vychází z formátu IntelHex. komunikaci začíná PC, modul odpovídá. První co musí PC (vždy na začátku konunikace !) poslat
* je paket ElaRecord (4), který určuje počáteční adresu - podle modul vybere potřebný ovladač paměti. Formy komunikace :
* -# Mazání obsahu flash. Probíhá po jednotlivých blocích.
* - PC -> MODUL : ersRecord (v poli offset číslo bloku relativně k počátku typu paměti)
* - MODUL -> PC : ackRecord (v poli offset číslo bloku relativně k počátku typu paměti)
* -# Zápis dat do paměti.
* - PC -> MODUL : dataRecord (max. 32 bytů)
* - MODUL -> PC : ackRecord nebo nakRecord pokud je cosi špatně
* -# Čtení dat z paměti.
* - PC -> MODUL : reqRecord (v poli offset odkud chci číst)
* - MODUL -> PC : dataRecord - pokud nulovou délku, další data nejsou. Posílána je délka 32 bytů.
* -# Spuštění uživatelského programu.
* - PC -> MODUL : slaRecord
* - bez odpovědi - je to jen pro uživatelské testy a může to špatně skončit. Vlastně je to přidáno
* jen proto, že to jde.
* */
class LinkProtocol {
static constexpr unsigned strbuflen = 0x80u;
public:
explicit LinkProtocol(CdcCmd & g, MemoryBase & b) noexcept;
void ParseLine (const char * line);
protected:
void AcquireDataRecord ();
void AcquireElaRecord ();
void AcquireSlaRecord ();
void AcquireEofRecord ();
void AcquireReqRecord ();
void AcquireErsRecord ();
void SendAck (bool ok=true);
private:
CdcCmd & cdccmd;
IntelHex ihx;
MemoryBase & driver;
uint32_t begin_addr;
uint32_t start_addr;
uint32_t lenght;
char strbuf [strbuflen];
};
#endif // LINKPROTOCOL_H

View file

@ -0,0 +1,32 @@
#include "system.h"
#include "gpio.h"
#include "cdc_class.h"
#include "linkprotocol.h"
#include "helpers.h"
/*********************************************************************************
* Do mluvícího teploměru bylo potřeba nějak dostat data do externí flash.
* Zde je základ programátoru, je to recyklovaný kód, takže zatím to umí jen
* binární soubory do 64KiB, ale patrně nebude problém dopsat to na větší délku.
* Software je dost surový, bez kontrol, prostě jen aby to trochu fungovalo.
**********************************************************************************/
static GpioClass led (GPIOB, 8);
static cdc_class cdc;
static MemoryBase mem;
static CdcCmd cmd;
static LinkProtocol lnk (cmd, mem);
int main () {
led << true;
cdc.init ();
cmd += cdc;
unsigned pass = 0u, command;
for (;;) {
char * res = cmd.GetLine(command);
if (command == 0u) continue;
lnk.ParseLine(res);
pass += 1u;
const bool b = pass & 1u;
led << b;
}
return 0;
}

View file

@ -0,0 +1,82 @@
#include "string.h"
#include "norflash.h"
extern "C" {
extern void delay_init ();
extern void Delay_Ms (const unsigned dly);
};
union FlashCommandHeader {
struct {
FLASH_COMMANDS cmd : 8;
uint32_t adr : 24; // adresa je BIG ENDIEN - vyšší byte vystupuje po SPI dříve (MSB FIRST).
}__attribute__((packed));
uint8_t bytes [4];
}__attribute__((packed));
static inline uint32_t be_set_24 (const uint32_t p) {
return ((p & 0x000000ff) << 16)
| ((p & 0x0000ff00) << 0)
| ((p & 0x00ff0000) >> 16);
}
static constexpr unsigned FlashBlockLen = 0x1000u; // 4KiB = 4096 B
NorFlash::NorFlash() noexcept : spi() {
delay_init();
}
unsigned int NorFlash::ReadBlock(const unsigned int addr, uint8_t * data, const unsigned int len) {
FlashCommandHeader header;
header.cmd = FLASH_4READ;
header.adr = be_set_24(addr);
spi.ChipSelect(true);
for (unsigned n=0u; n<sizeof(header); n++) spi.ReadWriteByte(header.bytes[n]);
for (unsigned n=0u; n<len; n++) data [n] = spi.ReadWriteByte(0xff);
spi.ChipSelect(false);
return len;
}
unsigned int NorFlash::WriteBlock(const unsigned int addr, const uint8_t * data, const unsigned int len) {
FlashCommandHeader header;
header.cmd = FLASH_4PP;
header.adr = be_set_24(addr);
SingleCommand(FLASH_WREN);
spi.ChipSelect(true);
for (unsigned n=0u; n<sizeof(header); n++) spi.ReadWriteByte(header.bytes[n]);
for (unsigned n=0u; n<len; n++) spi.ReadWriteByte(data [n]);
spi.ChipSelect(false);
if (!WaitForReady()) return 0;
return len;
}
bool NorFlash::EraseSector(const unsigned int addr) {
FlashCommandHeader header;
header.cmd = FLASH_4SE;
header.adr = be_set_24(addr);
SingleCommand(FLASH_WREN);
spi.ChipSelect(true);
for (unsigned n=0u; n<sizeof(header); n++) spi.ReadWriteByte(header.bytes[n]);
spi.ChipSelect(false);
if (!WaitForReady(400u)) return false;
return true;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////
inline bool NorFlash::SingleCommand(const FLASH_COMMANDS cmd) const {
uint8_t tmp = cmd;
spi.ChipSelect(true);
spi.ReadWriteByte (tmp);
spi.ChipSelect(false);
return true;
}
inline uint8_t NorFlash::ReadStatus(const FLASH_COMMANDS cmd) const {
uint8_t buf [2] = {cmd, 0};
spi.ChipSelect(true);
for (unsigned n=0u; n<2u; n++) buf[n] = spi.ReadWriteByte(buf[n]);
spi.ChipSelect(false);
return buf[1];
}
bool NorFlash::WaitForReady(const unsigned int ms) const {
uint8_t sr;
for (unsigned n=0; n<ms; n++) {
Delay_Ms (1u);
sr = ReadStatus(FLASH_RDSR1);
if ((sr & 1u) == 0u) return true;
}
return false;
}

View file

@ -0,0 +1,40 @@
#ifndef NORFLASH_H
#define NORFLASH_H
#include <stdint.h>
#include "spiblocked.h"
/// Enumerace povelů pro SPI FLASH.
enum FLASH_COMMANDS : uint8_t {
/* single byte commands */
FLASH_WRDI = 0x04, // nepoužito
FLASH_WREN = 0x06,
FLASH_RDID = 0x9F,
FLASHRSTEN = 0x66, // nepoužito
FLASH__RST = 0x99, // nepoužito
FLASH__RES = 0xAB, // release from power down, nepoužito
FLASH__DPD = 0xB9, // power down, nepoužito
// multi - byte
FLASH_RDSR1 = 0x05,
// dále se mohou povely lišit
FLASH_RDSR2 = 0x35, // nepoužito
FLASH_4READ = 0x03,
FLASH_4PP = 0x02,
FLASH_4SE = 0x20,
};
class NorFlash {
SpiBlocked spi;
public:
explicit NorFlash () noexcept;
unsigned ReadBlock (const unsigned addr, uint8_t * data, const unsigned len);
unsigned WriteBlock (const unsigned addr, const uint8_t * data, const unsigned len);
bool EraseSector (const unsigned addr);
protected:
/// Jednoduchý příkaz
bool SingleCommand (const FLASH_COMMANDS cmd) const;
/// Načtení registru STATUS1 nebo STATUS2 pomocí FLASH_RDSRx
uint8_t ReadStatus (const FLASH_COMMANDS cmd) const;
/// Čekání na dokončení operace zápisu / mazání
bool WaitForReady (const unsigned ms = 5u) const;
};
#endif // NORFLASH_H

View file

@ -0,0 +1,20 @@
PR = programmer
CC = gcc
CX = g++
VPATH = .
CFLAGS = -Os -Wall -I. -I../common
MFLAGS = -o $(PR)
all: $(PR)
OBJECTS = main.o usart.o baud.o linkprotocol.o intelhex.o
%.o: %.c
$(CC) -c $(CFLAGS) $< -o $@
%.o: %.cpp
$(CX) -std=c++17 -c $(CFLAGS) $< -o $@
$(PR): $(OBJECTS)
$(CX) $(MFLAGS) $(OBJECTS) -lpthread
clean:
rm -f *.o *~

View file

@ -0,0 +1,14 @@
#include <asm/termbits.h>
#include <sys/ioctl.h>
#include "usart.h"
void UsartClass::setBaud (int baud) {
struct termios2 tio;
ioctl(fd, TCGETS2, &tio);
tio.c_cflag &= ~CBAUD;
tio.c_cflag |= BOTHER;
tio.c_ispeed = baud;
tio.c_ospeed = baud;
ioctl(fd, TCSETS2, &tio);
}

View file

@ -0,0 +1,112 @@
#ifndef ENDIEN_H
#define ENDIEN_H
#include <stdint.h>
/**
@file
@brief Pořadí bytů ve slově.
*/
#if __GNUC__ < 4 // Compiler test
#error "Zkontroluj nastaveni"
#endif
// Pro 16.bit musíme struktury pakovat
#define PACKSTRUCT __attribute__((packed))
#if __thumb__
// Specificky a krátce pro danou architektutu a pokud chceme mít úplnou kontrolu.
static inline uint32_t swab32 (uint32_t p) {
register uint32_t t;
asm volatile ("rev %0, %1":"=r"(t):"0"(p));
return t;
}
#else //__thumb__
// Obecně
static inline uint32_t swab32 (uint32_t p) {
// return __builtin_bswap32 (p); // I takto to jde, ale pro názornost:
return ((p & 0x000000ff) << 24)
| ((p & 0x0000ff00) << 8)
| ((p & 0x00ff0000) >> 8)
| ((p & 0xff000000) >> 24);
}
#endif //__thumb__
// Tohle nebudeme ani specifikovat pro ARM
static inline uint16_t swab16 (uint16_t p) {
return (p << 8) | (p >> 8);
}
// Nedělá nic a ani nijak neotravuje - pouze pro kompatibilitu
static inline uint32_t nosw32 (uint32_t p) {
return p;
}
static inline uint16_t nosw16 (uint16_t p) {
return p;
}
// Specificky podle pořadí bytů - LE je pro ARM i X86 normální.
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#define cpu_to_be32 swab32
#define be32_to_cpu swab32
#define cpu_to_le32 nosw32
#define le32_to_cpu nosw32
#define cpu_to_be16 swab16
#define be16_to_cpu swab16
#define cpu_to_le16 nosw16
#define le16_to_cpu nosw16
#else
#define cpu_to_be32 nosw32
#define be32_to_cpu nosw32
#define cpu_to_le32 swab32
#define le32_to_cpu swab32
#define cpu_to_be16 nosw16
#define be16_to_cpu nosw16
#define cpu_to_le16 swab16
#define le16_to_cpu swab16
#endif
/**
@class ui32be
@brief C++ 32.bit data v BIG Endien.
@class ui32le
@brief C++ 32.bit data v LITTLE Endien.
@class ui16be
@brief C++ 16.bit data v BIG Endien.
@class ui16le
@brief C++ 16.bit data v LITTLE Endien.
*/
class ui32be {
public:
/// Nastavení hodnoty
/// @par p uint32_t hodnota
void set (uint32_t p) {num = cpu_to_be32 (p); };
/// Zjištění hodnoty
/// @return uint32_t hodnota
uint32_t get (void) {return be32_to_cpu (num); };
private:
/// Vlastní data uint32_t - k nim se přímo nedostaneme, jsou privátní
uint32_t num;
}PACKSTRUCT;
class ui32le {
public:
void set (uint32_t p) {num = cpu_to_le32 (p); };
uint32_t get (void) {return le32_to_cpu (num); };
private:
uint32_t num;
}PACKSTRUCT;
/* *******************************************************/
class ui16be {
public:
void set (uint16_t p) {num = cpu_to_be16 (p); };
uint16_t get (void) {return be16_to_cpu (num); };
private:
uint16_t num;
}PACKSTRUCT;
class ui16le {
public:
void set (uint16_t p) {num = cpu_to_le16 (p); };
uint16_t get (void) {return le16_to_cpu (num); };
private:
uint16_t num;
}PACKSTRUCT;
#endif // ENDIEN_H

View file

@ -0,0 +1,75 @@
#ifndef HELPERS_H
#define HELPERS_H
#include <stdint.h>
#include <stdio.h>
#include "fifo.h"
#include "baselayer.h"
class MemoryBase {
unsigned lenght;
unsigned char * data;
public:
explicit MemoryBase (const unsigned len) noexcept : lenght(len) {
data = new unsigned char [lenght];
}
~MemoryBase () { delete [] data; }
uint32_t getLenght () { return lenght; }
uint8_t * getData () { return data; }
uint32_t Write (const uint32_t addr, const void * ptr, const uint32_t len) {
printf("Readen at 0x%04X \r", addr);
fflush(stdout);
const uint8_t * dptr = reinterpret_cast<const uint8_t*>(ptr);
for (unsigned n=0; n<len; n++) data [addr + n] = dptr [n];
return len;
}
uint32_t Read (const uint32_t addr, void * ptr, const uint32_t len) {
uint8_t * data = reinterpret_cast<uint8_t*>(ptr);
for (unsigned n=0; n<len; n++) data[n] = n;
return len; }
void Erase (const uint32_t blok) {}
};
class CdcCmd : public BaseLayer {
static constexpr int maxlen = 0x80;
FIFO<char, maxlen> ring;
char line [maxlen];
unsigned index;
public:
explicit CdcCmd () noexcept : BaseLayer(), ring(), index(0u) {}
uint32_t Up(const char * data, const uint32_t len) override {
for (unsigned n=0u; n<len; n++) ring.Write (data [n]);
return len;
}
void SendString (const char * ptr, const uint32_t len) {
/*
fprintf(stdout, "TX");
int r = fwrite (ptr, 1, len, stdout); (void) r;
fflush (stdout);
*/
unsigned ofs = 0u, res = len;
while (res) {
const unsigned n = Down (ptr + ofs, res);
ofs += n;
res -= n;
}
}
char * GetLine (unsigned & len) {
char c;
while (ring.Read(c)) {
line [index++] = c;
if (c == '\n') {
len = index;
/*
fprintf(stdout, "RX");
int r = fwrite (line, 1, index, stdout); (void) r;
fflush (stdout);
*/
index = 0u;
return line;
}
}
len = 0u;
return line;
}
};
#endif // HELPERS_H

View file

@ -0,0 +1,249 @@
#include <stdio.h>
#include "string.h"
#include "endien.h"
#include "intelhex.h"
static uint8_t hexval (const char c) {
if ('0' <= c && c <= '9')
return (uint8_t) c - '0';
else if ('a' <= c && c <= 'f')
return (uint8_t) c - 'a' + 10;
else return 0;
}
static unsigned to_hex_str (char * str, const uint8_t * data, const unsigned len) {
unsigned result = 0;
const char * hexstr = "0123456789ABCDEF";
for (unsigned n=0; n<len; n++) {
const uint8_t c = data [n];
str [result++] = hexstr [c >> 4];
str [result++] = hexstr [c & 0xF];
}
return result;
}
static uint8_t chk_sum (const uint8_t * data, const unsigned len) {
uint8_t sum = 0;
for (unsigned n=0; n<len; n++) sum += data [n];
return sum;
}
union IhxHeader {
struct {
uint8_t reclen;
ui16be load;
RowTypes rectyp;
}__attribute__((packed));
uint8_t bytes [4];
};
union UlbaAddr {
ui16be addr;
uint8_t bytes [2];
};
union EipAddr {
ui32be addr;
uint8_t bytes [4];
};
IntelHex::IntelHex() noexcept : address(0), offset(0), lenght(0), total_lenght(0), ackofs(0) {
}
RowTypes IntelHex::parseRecord (const char * data, const unsigned int len) {
//printf ("parseRecord: %d %s\r\n", len, data);
if (data[0] != (uint8_t) ':') return badRecord;
uint8_t tmp [(len >> 1) + 1];
uint8_t val = 0;
unsigned k = 0;
for (unsigned n=1; n<len; n++) {
const uint8_t c = data [n];
if (c == '\r') break;
if (c == '\n') break;
val *= 0x10;
val += hexval (c | 0x20);
if ((n & 1) == 0) {
tmp [k++] = val;
val = 0;
}
}
k -= 1;
uint8_t chk = 0;
for (unsigned n=0; n<k; n++) chk += tmp [n];
chk = 0 - chk;
// kontrolní součet
if (chk != tmp[k]) return badRecord;
// chybná délka
if (k < sizeof (IhxHeader)) return badRecord;
IhxHeader hdr;
memcpy (hdr.bytes, tmp, sizeof(IhxHeader));
// chybná délka
if (k != (hdr.reclen + sizeof(IhxHeader))) return badRecord;
//printf ("%d: %d=%d, %d, %02X=%02X\n", len, k, hdr.reclen+4, hdr.rectyp, chk, tmp[k]);
UlbaAddr a1; EipAddr a2;
RowTypes t = hdr.rectyp;
lenght = hdr.reclen;
const uint8_t * start = tmp + sizeof(IhxHeader);
uint32_t tmpadr;
// uint16_t old_ofs = offset;
switch (t) {
case dataRecord:
memcpy (chunk, start, lenght);
offset = hdr.load.get();
// if (old_ofs != offset) printf ("Posun 0x%04X-0x%04X\n", old_ofs, offset);
total_lenght = offset + lenght;
break;
case elaRecord:
total_lenght = 0;
memcpy (a1.bytes, start, lenght);
address = (uint32_t) a1.addr.get() << 16;
offset = hdr.load.get();
break;
case slaRecord:
memcpy (a2.bytes, start, lenght);
address = a2.addr.get();
offset = hdr.load.get();
break;
case ackRecord:
case nakRecord:
ackofs = hdr.load.get();
break;
case eofRecord:
case reqRecord:
case ersRecord:
offset = hdr.load.get();
break;
// Tyhle 2 rekordy nějak fungují, ale je to divné. Bohužel třeba objcopy
// je na nízkých adresách z nějakého důvodu používá. Možná je v tom chyba.
case esaRecord:
total_lenght = 0;
memcpy (a1.bytes, start, lenght);
address = (uint32_t) a1.addr.get() << 4;
offset = hdr.load.get();
break;
case ssaRecord:
memcpy (a2.bytes, start, lenght);
tmpadr = a2.addr.get();
address = (tmpadr >> 12); // CS:IP ? čerti vědí, jak je to myšleno
offset = hdr.load.get();
break;
default:
break;
}
return t;
}
bool IntelHex::CompareAckOffsset() {
//printf ("ofs: %04X=%04X\n", ackofs, offset);
if (ackofs == offset) return true;
return false;
}
void IntelHex::getAddress (uint32_t & adr) {
adr = address;
}
void IntelHex::getLenght (uint32_t & len) {
len = total_lenght;
}
void IntelHex::getOffset (uint32_t & ofs) {
ofs = offset;
}
void IntelHex::WriteData (uint8_t * data) {
memcpy (data + offset, chunk, lenght);
offset += lenght;
}
uint8_t * IntelHex::getDataRow (uint32_t & ofs, uint32_t & len) {
ofs = offset;
len = lenght;
return chunk;
}
void IntelHex::AddOffset (uint32_t len) {
offset += len;
}
uint32_t IntelHex::ElaRecord (char * string, const uint32_t adr) {
offset = 0;
uint32_t result = 0;
string [result++] = ':';
UlbaAddr lba;
IhxHeader hdr;
hdr.load.set(0);
hdr.reclen = sizeof (lba);
hdr.rectyp = elaRecord;
lba.addr.set (adr >> 16);
uint8_t chk = 0;
chk += chk_sum (hdr.bytes, sizeof(IhxHeader));
chk += chk_sum (lba.bytes, sizeof(UlbaAddr ));
chk = 0 - chk;
result += to_hex_str (string + result, hdr.bytes, sizeof(IhxHeader));
result += to_hex_str (string + result, lba.bytes, sizeof(UlbaAddr ));
result += to_hex_str (string + result, & chk, 1);
string [result++] = '\r';
string [result++] = '\n';
string [result] = '\0';
return result;
}
uint32_t IntelHex::SlaRecord (char* string, const uint32_t adr) {
offset = 0;
uint32_t result = 0;
string [result++] = ':';
EipAddr lba;
IhxHeader hdr;
hdr.load.set(0);
hdr.reclen = sizeof (lba);
hdr.rectyp = slaRecord;
lba.addr.set (adr);
uint8_t chk = 0;
chk += chk_sum (hdr.bytes, sizeof(IhxHeader));
chk += chk_sum (lba.bytes, sizeof(EipAddr ));
chk = 0 - chk;
result += to_hex_str (string + result, hdr.bytes, sizeof(IhxHeader));
result += to_hex_str (string + result, lba.bytes, sizeof(EipAddr ));
result += to_hex_str (string + result, & chk, 1);
string [result++] = '\r';
string [result++] = '\n';
string [result] = '\0';
return result;
}
uint32_t IntelHex::DataRecord (char * string, const uint8_t * data, const uint32_t len) {
uint32_t result = 0;
string [result++] = ':';
IhxHeader hdr;
hdr.load.set (offset);
hdr.reclen = len;
hdr.rectyp = dataRecord;
uint8_t chk = 0;
chk += chk_sum (hdr.bytes, sizeof(IhxHeader));
chk += chk_sum (data, len);
chk = 0 - chk;
result += to_hex_str (string + result, hdr.bytes, sizeof(IhxHeader));
if (len) result += to_hex_str (string + result, data, len);
result += to_hex_str (string + result, & chk, 1);
string [result++] = '\r';
string [result++] = '\n';
string [result] = '\0';
return result;
}
uint32_t IntelHex::EofRecord (char * string) {
offset = 0;
const char * eof = ":00000001FF\r\n";
const uint32_t result = strlen (eof);
memcpy (string, eof, result);
string [result] = '\0';
return result;
}
uint32_t IntelHex::BTxRecord (char * string, RowTypes t) {
uint32_t result = 0;
string [result++] = ':';
IhxHeader hdr;
hdr.load.set (offset);
hdr.reclen = 0;
hdr.rectyp = t;
uint8_t chk = 0;
chk += chk_sum (hdr.bytes, sizeof(IhxHeader));
chk = 0 - chk;
result += to_hex_str (string + result, hdr.bytes, sizeof(IhxHeader));
result += to_hex_str (string + result, & chk, 1);
string [result++] = '\r';
string [result++] = '\n';
string [result] = '\0';
//offset += len;
return result;
}

View file

@ -0,0 +1,138 @@
#ifndef INTELHEX_H
#define INTELHEX_H
#include <stdint.h>
/// Maximální délka rekordu
static const unsigned MaxHexRecord = 0x80;
enum RowTypes : uint8_t {
// defined by Intel
dataRecord = 0, //!< datový paket
eofRecord, //!< konec dat
esaRecord, //!< X86/16.bit CS/IP počáteční adresa
ssaRecord, //!< X86/16.bit CS/IP startovací adresa
elaRecord, //!< počáteční adresa 32-bit (horních 16.bit)
slaRecord, //!< startovací adresa 32-bit
// ... defined by BT protocol
// všechny budou používat jen offset pro přenos dat.
ackRecord, //!< offset - představuje offset předchozích dat
nakRecord, //!< stejně jako předchozí
reqRecord, //!< Request for data - offset značí odkud vzhledem k počáteční adrese
ersRecord, //!< Erase - po blocích, offset je číslo bloku (závisí na typu flash)
badRecord, //!< použito pro detekci chyb
};
/// @enum RowTypes
/// Enumerace datových typů Intel-hex formátu
/**
* @class IntelHex
* @brief Hepler pro parsování a vytváření paketů Intel HEX formátu.
*
* Prvních 6 typů (#RowTypes) je definováno ve specifikaci např.
* <a href="https://cs.wikipedia.org/wiki/Intel_HEX">zde</a>. A protože je tam dost volného
* místa pro rozšíření, jsou zde dodefinovány další typy použité pro komunikaci. Je to tak
* jednodušší než vymýšlet nějaký vlastní formát pro potvrzování a jiné. Protokol je znakový,
* poměrně robustní, na druhou stranu dost velku režii.
*
* Ukázalo sem že úzkým hrdlem komunikace je čekání na potvrzování, takže se režie poněkud zastírá,
* ale potvrzování být musí pokud to trochu spolehlivě fungovat.
* */
class IntelHex {
public:
explicit IntelHex() noexcept;
/**
* @brief Základní funkce parseru
*
* @param data vstupní řádek (paket)
* @param len jeho délka (např. strlen(data))
* @return RowTypes - to se pak zpracovává dál v nadřazené části např. LinkProtocol
*/
RowTypes parseRecord (const char * data, const unsigned len);
/**
* @brief Nastavení adresy
*
* Buď počáteční nebo starovací.
*
* @param adr odkaz na proměnnou, kam bude adresa nastavena
*/
void getAddress (uint32_t & adr);
/**
* @brief Nastavení délky
* @param len odkaz na proměnnou, kam bude délka nastavena
*/
void getLenght (uint32_t & len);
/**
* @brief Nastavení ofsetu
* @param ofs odkaz na proměnnou, kam bude ofset nastaven
*/
void getOffset (uint32_t & ofs);
/**
* @brief Zápis dat z příchozího dataRecord
*
* @param data kam se zapíší
*/
void WriteData (uint8_t * data);
/**
* @brief Posun ofsetu kvůli udržení konzistence
*
* @param len o kolik se posunout
*/
void AddOffset (uint32_t len);
/**
* @brief Vytvoření dataRecord
*
* @param string kam se record zapíše
* @param data pure data rekordu
* @param len a jejich délka
* @return uint32_t kolik bylo zapsáno do string
*/
uint32_t DataRecord (char * string, const uint8_t * data, const uint32_t len);
/**
* @brief Vytvoření elaRecord
*
* @param string kam se record zapíše
* @param adr počáteční adresa
* @return uint32_t kolik znaků bylo zapsáno do string
*/
uint32_t ElaRecord (char * string, const uint32_t adr);
/**
* @brief Vytvoření slaRecord
*
* @param string kam se record zapíše
* @param adr startovací adresa
* @return uint32_t kolik znaků bylo zapsáno do string
*/
uint32_t SlaRecord (char * string, const uint32_t adr);
/**
* @brief Vytvoření eofRecord
*
* @param string kam se record zapíše
* @return uint32_t kolik znaků bylo zapsáno do string
*/
uint32_t EofRecord (char * string);
/**
* @brief Vytvoření BT rekordů ackRecord, nakRecord, reqRecord, ersRecord
*
* @param string kam se record zapíše
* @param t definuj typ rekordu
* @return uint32_t kolik znaků bylo zapsáno do string
*/
uint32_t BTxRecord (char * string, RowTypes t = ackRecord);
/// pomocná funkce
bool CompareAckOffsset ();
/// pomocná funkce
uint8_t * getDataRow (uint32_t & ofs, uint32_t & len);
protected:
private:
uint32_t address;
uint32_t offset;
uint32_t lenght;
uint32_t total_lenght;
uint32_t ackofs;
uint8_t chunk [MaxHexRecord];
};
#endif // INTELHEX_H

View file

@ -0,0 +1,122 @@
#include <string.h>
#include "helpers.h"
#include "linkprotocol.h"
#define trace(...)
LinkProtocol::LinkProtocol(CdcCmd & g, MemoryBase & b) noexcept : cdccmd(g), ihx(), driver(b),
begin_addr(0), start_addr(0), lenght (0) {
}
void LinkProtocol::ParseLine (const char * line) {
const unsigned readen = strlen (line);
RowTypes t = ihx.parseRecord (line, readen);
switch (t) {
case dataRecord: AcquireDataRecord (); break;
case elaRecord: AcquireElaRecord (); break;
case slaRecord: AcquireSlaRecord (); break;
case eofRecord: AcquireEofRecord (); break;
case reqRecord: AcquireReqRecord (); break;
case ersRecord: AcquireErsRecord (); break;
default:
trace ("BAD record 0x%02X\r\n", t);
break;
}
}
void LinkProtocol::AcquireDataRecord() {
uint32_t ofs, len;
uint8_t * ptr = ihx.getDataRow (ofs, len);
uint32_t res = driver.Write (begin_addr + ofs, ptr, len);
if (res) {
//SendAck();
ihx.AddOffset (res); // posun offsetu az po ack
return;
}
//SendAck (false);
}
// První paket, který musí přijít při každé akci.
void LinkProtocol::AcquireElaRecord() {
ihx.getAddress (begin_addr);
trace("Begin = 0x%08X\r\n", begin_addr);
//driver = GetDriverByAddress (begin_addr);
//if (driver) driver->On (begin_addr);
SendAck();
}
typedef void (*pHandler) (void);
void LinkProtocol::AcquireSlaRecord() {
ihx.getAddress (start_addr);
trace("Start = 0x%08X\r\n", start_addr);
}
void LinkProtocol::AcquireEofRecord() {
ihx.getLenght (lenght);
trace ("Lenght = %d\r\n", lenght);
SendAck();
}
void LinkProtocol::AcquireReqRecord() {
static constexpr unsigned chunk_size = 0x10;
uint8_t data [chunk_size];
uint32_t res = 0x10, len, ofs;
len = chunk_size;
ihx.getOffset (ofs);
//trace ("AcquireReqRecord: %04X, len=%d\r\n", ofs, len);
res = driver.Read (begin_addr + ofs, data, len);
res = ihx.DataRecord (strbuf, data, res);
cdccmd.SendString (strbuf, res);
}
void LinkProtocol::AcquireErsRecord() {
uint32_t block;
ihx.getOffset (block);
driver.Erase (block);
SendAck();
}
void LinkProtocol::SendAck (bool ok) {
RowTypes t = ok ? ackRecord : nakRecord;
const uint32_t r = ihx.BTxRecord (strbuf, t);
cdccmd.SendString (strbuf, r);
}
////////////////////////////////////////////////////////
void LinkProtocol::ReadMem() {
uint32_t res = ihx.BTxRecord (strbuf, reqRecord);
cdccmd.SendString (strbuf, res);
}
bool LinkProtocol::isEOF(const unsigned len) {
uint32_t ofs;
ihx.getOffset(ofs);
if (ofs < len) return false;
return true;
}
bool LinkProtocol::StartOperation() {
uint32_t res = ihx.ElaRecord(strbuf, 0u);
cdccmd.SendString (strbuf, res);
return true;
}
bool LinkProtocol::Erase(const unsigned int n) {
uint32_t ofs, addr = n * 0x1000u;
ihx.getOffset(ofs);
ihx.AddOffset(addr - ofs);
uint32_t res = ihx.BTxRecord (strbuf, ersRecord);
cdccmd.SendString (strbuf, res);
return true;
}
bool LinkProtocol::WriteMem(const uint8_t * data, const unsigned int len) {
unsigned ofs = 0u, rem = len, chunk;
for (;;) {
unsigned value;
char * res = cdccmd.GetLine(value);
if (value == 0u) continue;
RowTypes t = ihx.parseRecord(res, value);
if (t != ackRecord) return false;
if (rem == 0u) break;
chunk = rem > 0x20 ? 0x20 : rem;
unsigned rl = ihx.DataRecord(strbuf, data + ofs, chunk);
printf("Write at 0x%04X \r", ofs);
fflush(stdout);
ihx.AddOffset(chunk);
cdccmd.SendString(strbuf, rl);
ofs += chunk;
rem -= chunk;
}
return true;
}

View file

@ -0,0 +1,60 @@
#ifndef LINKPROTOCOL_H
#define LINKPROTOCOL_H
#include <stdint.h>
#include "intelhex.h"
class CdcCmd;
class MemoryBase;
/**
* @class LinkProtocol
* @brief Protokol po CDC.
*
* Vychází z formátu IntelHex. komunikaci začíná PC, modul odpovídá. První co musí PC (vždy na začátku konunikace !) poslat
* je paket ElaRecord (4), který určuje počáteční adresu - podle modul vybere potřebný ovladač paměti. Formy komunikace :
* -# Mazání obsahu flash. Probíhá po jednotlivých blocích.
* - PC -> MODUL : ersRecord (v poli offset číslo bloku relativně k počátku typu paměti)
* - MODUL -> PC : ackRecord (v poli offset číslo bloku relativně k počátku typu paměti)
* -# Zápis dat do paměti.
* - PC -> MODUL : dataRecord (max. 32 bytů)
* - MODUL -> PC : ackRecord nebo nakRecord pokud je cosi špatně
* -# Čtení dat z paměti.
* - PC -> MODUL : reqRecord (v poli offset odkud chci číst)
* - MODUL -> PC : dataRecord - pokud nulovou délku, další data nejsou. Posílána je délka 32 bytů.
* -# Spuštění uživatelského programu.
* - PC -> MODUL : slaRecord
* - bez odpovědi - je to jen pro uživatelské testy a může to špatně skončit. Vlastně je to přidáno
* jen proto, že to jde.
* */
class LinkProtocol {
static constexpr unsigned strbuflen = 0x80u;
public:
explicit LinkProtocol(CdcCmd & g, MemoryBase & b) noexcept;
void ParseLine (const char * line);
bool StartOperation ();
void ReadMem ();
bool WriteMem (const uint8_t * data, const unsigned len);
bool Erase (const unsigned n);
bool isEOF (const unsigned len);
protected:
void AcquireDataRecord ();
void AcquireElaRecord ();
void AcquireSlaRecord ();
void AcquireEofRecord ();
void AcquireReqRecord ();
void AcquireErsRecord ();
void SendAck (bool ok=true);
private:
CdcCmd & cdccmd;
IntelHex ihx;
MemoryBase & driver;
uint32_t begin_addr;
uint32_t start_addr;
uint32_t lenght;
char strbuf [strbuflen];
};
#endif // LINKPROTOCOL_H

View file

@ -0,0 +1,121 @@
#include <signal.h>
#include <cstdio>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "usart.h"
#include "fifo.h"
#include "linkprotocol.h"
#include "helpers.h"
static constexpr unsigned FlashBlockSize = 0x1000u;
volatile bool loop = false;
void sig_handler (int) {
loop = false;
}
static UsartClass usart ("/dev/serial/by-id/usb-Kizarm_Labs._Flash_Programmer_0001-if00", 9600);
static MemoryBase mem (0x400u * FlashBlockSize); // 4MiB flash
static CdcCmd cmd;
static LinkProtocol lnk (cmd, mem);
static void read_flash (const unsigned len) {
lnk.StartOperation();
while (loop) {
unsigned value;
char * res = cmd.GetLine(value);
if (value == 0u) continue;
lnk.ParseLine(res);
if (lnk.isEOF(len)) break;
lnk.ReadMem();
}
}
static void erase_blocks (const unsigned num = 1) {
unsigned block = 0u;
lnk.StartOperation();
while (loop) {
unsigned value;
char * res = cmd.GetLine(value);
if (value == 0u) continue;
if (block >= num) break;
lnk.ParseLine(res);
lnk.Erase (block);
printf("Erasing blok %d \r", block);
fflush(stdout);
block += 1u;
}
}
static uint8_t * open_file_for_read (const char * name, unsigned & len) {
struct stat prop;
const int r = stat (name, & prop);
if (r) return nullptr;
const unsigned flen = prop.st_size;
FILE * in = fopen (name, "r");
uint8_t * data = new uint8_t [flen];
const int l = fread (data, 1, flen, in);
printf("readen = %d bytes\n", l);
fclose(in);
len = flen;
return data;
}
static void read_flash_binary (const char * name) {
const unsigned flen = 0x10000u;
printf("Read data\n");
read_flash (flen);
FILE * out = fopen(name,"w");
int r = fwrite(mem.getData(), 1, flen, out);
(void) r;
fclose(out);
}
static void write_flash_binary (const char * name) {
unsigned flen = 0u;
uint8_t * data = open_file_for_read(name, flen);
if (!data) return;
printf("Write data\n");
uint32_t blocks = flen / FlashBlockSize;
if (flen % FlashBlockSize) blocks += 1u;
erase_blocks (blocks);
lnk.StartOperation();
lnk.WriteMem (data, flen);
delete [] data;
}
static void verify_flash_binary (const char * name) {
unsigned flen = 0u;
uint8_t * data = open_file_for_read(name, flen);
if (!data) return;
printf("Verify data\n");
read_flash (flen);
uint8_t * fdata = mem.getData();
unsigned ok = 0;
for (unsigned n=0u; n<flen; n++) {
if (fdata[n] != data[n]) {
printf("err at %04X - %02X != %02X\n", n, fdata[n], data[n]);
ok += 1u;
}
}
if (ok == 0u) printf("\nFlash OK\n");
else printf("\nTotal errors = %d\n", ok);
delete [] data;
}
int main(int argc, char *argv[]) {
if (argc < 3) {
printf("Usage:\r\t$ %s r|w|v file.bin\n", argv[0]);
return 0;
}
loop = true;
signal(SIGINT, sig_handler);
cmd += usart;
if (argv[1][0] == 'r') {
read_flash_binary (argv[2]);
} else if (argv[1][0] == 'w') {
write_flash_binary(argv[2]);
} else if (argv[1][0] == 'v') {
verify_flash_binary(argv[2]);
} else {
printf("bad parameter %s\n", argv[1]);
}
return 0;
}

View file

@ -0,0 +1,85 @@
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdint.h>
#include <termios.h>
#include <sys/ioctl.h>
#include "usart.h"
UsartClass::UsartClass (const char * name, const int baudrate) : BaseLayer() {
id = name;
running = false;
fd = ::open (id, O_RDWR);
if (fd < 0) return;
timeout = 20'000'000 / baudrate; // cca 2 násobek pro 1 byte
struct termios LineFlags;
int attr = tcgetattr (fd, &LineFlags);
if (attr) {
printf ("Nelze zjistit parametry portu %s\r\n", name);
::close (fd);
return;
}
// nastaveni komunikacnich parametru do struct termios
LineFlags.c_iflag = IGNPAR /* | IXON | IXOFF*/; // ignoruj chyby parity
LineFlags.c_oflag = 0; // normalni nastaveni
LineFlags.c_cflag = CS8 | CREAD | CLOCAL; // 8-bit, povol prijem
LineFlags.c_lflag = 0; // Raw input bez echa
LineFlags.c_cc [VMIN] = 1; // minimalni pocet znaku pro cteni
LineFlags.c_cc [VTIME] = 1; // read timeout 0.1 s
tcsetattr (fd, TCSANOW, &LineFlags);
fcntl (fd, F_SETFL, 0);
int flag = TIOCM_DTR;
ioctl(fd, TIOCMBIS, & flag);
setBaud(baudrate);
printf ("Port %s opened (%d) at %d Bd\r\n", id, fd, baudrate);
usleep (10000);
running = true;
pthread_create (&rc, NULL, UsartClass::UsartHandler, this);
}
UsartClass::~UsartClass() {
running = false;
pthread_cancel (rc);
int flag = TIOCM_DTR;
ioctl(fd, TIOCMBIC, & flag);
::close (fd);
printf ("\nPort %s closed\r\n", id);
}
uint32_t UsartClass::Up (const char * data, uint32_t len) {
return BaseLayer::Up (data, len);
}
uint32_t UsartClass::Down (const char * data, uint32_t len) {
if (!running) return 0;
const unsigned maxchunk = 32; // USB bulk len (64) je moc
unsigned offset = 0, remain = len;
while (remain) {
const unsigned chunk = remain > maxchunk ? maxchunk : remain;
const unsigned writn = ::write (fd, data + offset, chunk);
// Počkat je nejjednodušší prevence zahlcení USB (USART má omezenou rychlost)
// usleep (timeout * writn); // závisí na baud rate
offset += writn;
remain -= writn;
}
return offset;
}
void UsartClass::ReadLoop (void) {
int n;
while (running) {
// Nutno číst po jednom nebo použít timer, jinak to po prvním čtení zdechne.
n = ::read (fd, rxbuf, 1);
if (!n) continue;
Up (rxbuf, n);
}
}

View file

@ -0,0 +1,32 @@
#ifndef USARTCLASS_H
#define USARTCLASS_H
#include <pthread.h>
#include "../common/baselayer.h"
static constexpr unsigned BUFLEN = 1024u;
// Bottom
class UsartClass : public BaseLayer {
public:
UsartClass (const char* name, const int baudrate);
uint32_t Up (const char* data, uint32_t len);
uint32_t Down(const char* data, uint32_t len);
virtual ~UsartClass ();
protected:
void ReadLoop (void);
static void* UsartHandler (void* p) {
UsartClass* inst = (UsartClass*) p;
inst->ReadLoop();
return nullptr;
};
void setBaud (int baud);
public:
bool running;
private:
const char * id; //!< Identifikátor třídy pro ladění
char rxbuf[BUFLEN];
int fd;
int timeout;
pthread_t rc;
};
#endif // USARTCLASS_H

View file

@ -0,0 +1,66 @@
#include "system.h"
#include "spiblocked.h"
enum SPICLK : uint32_t {
FPCLK_2 = 0u, // 72 MHz
FPCLK_4, // 36 MHz
FPCLK_8, // 18 MHz
FPCLK_16, // 9 MHz
FPCLK_32, // 4.5 MHz
FPCLK_64, // 2.25 MHz
FPCLK_128, // 1.125 MHz
FPCLK_256, // 0.5625 MHz
};
static constexpr unsigned FM = 3u; // 50 MHz
static void InitPins () noexcept {
// PA4 - NSS, PA5 - SCK, PA6 - MISO, PA7 - MOSI
GPIOA.CFGLR.modify([](GPIOA_Type::CFGLR_DEF & r) -> uint32_t {
r.B.MODE4 = FM;
r.B.CNF4 = 0u; // gen push - pull
r.B.MODE5 = FM;
r.B.CNF5 = 2u; // alt push - pull
r.B.MODE6 = 0u; // input mode
r.B.CNF6 = 1u; // floating
r.B.MODE7 = FM;
r.B.CNF7 = 2u; // alt push - pull
return r.R;
});
// AFIO - default
GPIOA.BSHR.B.BS4 = SET;
}
SpiBlocked::SpiBlocked() noexcept {
RCC.APB2PCENR.modify([](RCC_Type::APB2PCENR_DEF & r) -> uint32_t {
r.B.SPI1EN = SET;
r.B.IOPAEN = SET;
//r.B.AFIOEN = SET;
return r.R;
});
InitPins();
RCC.APB2PRSTR.B.SPI1RST = SET;
RCC.APB2PRSTR.B.SPI1RST = RESET;
SPI1.CTLR1.modify([](SPI1_Type::CTLR1_DEF & r) -> uint32_t {
r.B.CPHA = RESET;
r.B.CPOL = RESET;
r.B.MSTR = SET; // master
r.B.DFF = RESET; // 8 bit
r.B.SSM = SET; // software
r.B.SSI = SET; // !!! netuším proč, ale jinak se nenastaví MSTR a SPE
r.B.LSBFIRST = RESET;
r.B.BR = FPCLK_64;
return r.R;
});
SPI1.CRCR.R = 7u;
SPI1.CTLR1.B.SPE = SET;
}
uint8_t SpiBlocked::ReadWriteByte(const uint8_t data) const {
while (SPI1.STATR.B.TXE == RESET);
SPI1.DATAR.B.DATAR = data;
while (SPI1.STATR.B.RXNE == RESET);
return SPI1.DATAR.B.DATAR;
}
void SpiBlocked::ChipSelect(const bool on) const {
if (on) GPIOA.BSHR.B.BR4 = SET;
else GPIOA.BSHR.B.BS4 = SET;
}

View file

@ -0,0 +1,12 @@
#ifndef SPIBLOCKED_H
#define SPIBLOCKED_H
#include <stdint.h>
class SpiBlocked {
public:
explicit SpiBlocked () noexcept;
uint8_t ReadWriteByte (const uint8_t data) const;
void ChipSelect (const bool on) const;
};
#endif // SPIBLOCKED_H

View file

@ -0,0 +1,80 @@
/********************************** (C) COPYRIGHT *******************************
* File Name : usb_desc.c
* Author : WCH
* Version : V1.0.0
* Date : 2022/08/20
* Description : usb device descriptor,configuration descriptor,
* string descriptors and other descriptors.
*********************************************************************************
* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
* Attention: This software (modified or not) and binary are used for
* microcontroller manufactured by Nanjing Qinheng Microelectronics.
*******************************************************************************/
#include "usb_desc.h"
/* Device Descriptor */
const uint8_t USBD_DeviceDescriptor[] =
{
0x12, // bLength
0x01, // bDescriptorType (Device)
0x10, 0x01, // bcdUSB 1.10
0x02, // bDeviceClass
0x00, // bDeviceSubClass
0x00, // bDeviceProtocol
DEF_USBD_UEP0_SIZE, // bMaxPacketSize0 64
(uint8_t)DEF_USB_VID, (uint8_t)(DEF_USB_VID >> 8), // idVendor 0x1A86
(uint8_t)DEF_USB_PID, (uint8_t)(DEF_USB_PID >> 8), // idProduct 0x5537
DEF_IC_PRG_VER, 0x00, // bcdDevice 0.01
0x01, // iManufacturer (String Index)
0x02, // iProduct (String Index)
0x03, // iSerialNumber (String Index)
0x01, // bNumConfigurations 1
};
/* Configuration Descriptor */
const uint8_t USBD_ConfigDescriptor[] =
{
/* Configure descriptor */
0x09, 0x02, 0x43, 0x00, 0x02, 0x01, 0x00, 0x80, 0x32,
/* Interface 0 (CDC) descriptor */
0x09, 0x04, 0x00, 0x00, 0x01, 0x02, 0x02, 0x01, 0x00,
/* Functional Descriptors */
0x05, 0x24, 0x00, 0x10, 0x01,
/* Length/management descriptor (data class interface 1) */
0x05, 0x24, 0x01, 0x00, 0x01,
0x04, 0x24, 0x02, 0x02,
0x05, 0x24, 0x06, 0x00, 0x01,
/* Interrupt upload endpoint descriptor */
0x07, 0x05, 0x81, 0x03, (uint8_t)DEF_USBD_ENDP1_SIZE, (uint8_t)( DEF_USBD_ENDP1_SIZE >> 8 ), 0x01,
/* Interface 1 (data interface) descriptor */
0x09, 0x04, 0x01, 0x00, 0x02, 0x0A, 0x00, 0x00, 0x00,
/* Endpoint descriptor */
0x07, 0x05, 0x02, 0x02, (uint8_t)DEF_USBD_ENDP2_SIZE, (uint8_t)( DEF_USBD_ENDP2_SIZE >> 8 ), 0x00,
/* Endpoint descriptor */
0x07, 0x05, 0x83, 0x02, (uint8_t)DEF_USBD_ENDP3_SIZE, (uint8_t)( DEF_USBD_ENDP3_SIZE >> 8 ), 0x00,
};
#define DEF_STRDESC(p,n) w_text<(sizeof(p)>>1)>n={sizeof(n)-2u,3u,{p}}
template<const unsigned N> struct w_text {
uint8_t len, typ;
const char16_t str [N];
};
static const DEF_STRDESC((u"Kizarm Labs."), str_1);
static const DEF_STRDESC((u"Flash Programmer"),str_2);
static const DEF_STRDESC((u"0001"), str_3);
/* Language Descriptor */
static const uint8_t LangDescr[] = {
0x04, 0x03, 0x09, 0x04
};
const uint8_t * USBD_StringLangID = reinterpret_cast<const uint8_t *>(LangDescr);
const uint8_t * USBD_StringVendor = reinterpret_cast<const uint8_t *>(&str_1);
const uint8_t * USBD_StringProduct = reinterpret_cast<const uint8_t *>(&str_2);
const uint8_t * USBD_StringSerial = reinterpret_cast<const uint8_t *>(&str_3);