diff --git a/V203F6P6/programmer/Makefile b/V203F6P6/programmer/Makefile new file mode 100644 index 0000000..191bf70 --- /dev/null +++ b/V203F6P6/programmer/Makefile @@ -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 diff --git a/V203F6P6/programmer/ch32v203 b/V203F6P6/programmer/ch32v203 new file mode 120000 index 0000000..7650c85 --- /dev/null +++ b/V203F6P6/programmer/ch32v203 @@ -0,0 +1 @@ +../ch32v203/ \ No newline at end of file diff --git a/V203F6P6/programmer/common b/V203F6P6/programmer/common new file mode 120000 index 0000000..8332399 --- /dev/null +++ b/V203F6P6/programmer/common @@ -0,0 +1 @@ +../common/ \ No newline at end of file diff --git a/V203F6P6/programmer/endien.h b/V203F6P6/programmer/endien.h new file mode 100644 index 0000000..b0f2353 --- /dev/null +++ b/V203F6P6/programmer/endien.h @@ -0,0 +1,112 @@ +#ifndef ENDIEN_H +#define ENDIEN_H +#include +/** + @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 diff --git a/V203F6P6/programmer/hack.c b/V203F6P6/programmer/hack.c new file mode 100644 index 0000000..391b7bf --- /dev/null +++ b/V203F6P6/programmer/hack.c @@ -0,0 +1,21 @@ +#include +#include +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 +#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(ptr), len); + } + uint32_t Read (const uint32_t addr, void * ptr, const uint32_t len) { + return flash.ReadBlock(addr, reinterpret_cast (ptr), len); + } + void Erase (const uint32_t blok) { + flash.EraseSector(blok); + } +}; + +class CdcCmd : public BaseLayer { + static constexpr int maxlen = 0x80; + FIFO 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 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 diff --git a/V203F6P6/programmer/intelhex.cpp b/V203F6P6/programmer/intelhex.cpp new file mode 100644 index 0000000..d68c737 --- /dev/null +++ b/V203F6P6/programmer/intelhex.cpp @@ -0,0 +1,249 @@ +#include +#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> 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> 1) + 1]; + uint8_t val = 0; + unsigned k = 0; + for (unsigned n=1; n> 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; +} diff --git a/V203F6P6/programmer/intelhex.h b/V203F6P6/programmer/intelhex.h new file mode 100644 index 0000000..e469104 --- /dev/null +++ b/V203F6P6/programmer/intelhex.h @@ -0,0 +1,138 @@ +#ifndef INTELHEX_H +#define INTELHEX_H +#include + +/// 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ř. + * zde. 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 má 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 má 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 má 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 diff --git a/V203F6P6/programmer/linkprotocol.cpp b/V203F6P6/programmer/linkprotocol.cpp new file mode 100644 index 0000000..84e5cc4 --- /dev/null +++ b/V203F6P6/programmer/linkprotocol.cpp @@ -0,0 +1,75 @@ +#include +#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); +} diff --git a/V203F6P6/programmer/linkprotocol.h b/V203F6P6/programmer/linkprotocol.h new file mode 100644 index 0000000..89bc4c1 --- /dev/null +++ b/V203F6P6/programmer/linkprotocol.h @@ -0,0 +1,55 @@ +#ifndef LINKPROTOCOL_H +#define LINKPROTOCOL_H + +#include +#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 ní 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 má 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 diff --git a/V203F6P6/programmer/main.cpp b/V203F6P6/programmer/main.cpp new file mode 100644 index 0000000..5c6ed14 --- /dev/null +++ b/V203F6P6/programmer/main.cpp @@ -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; +} diff --git a/V203F6P6/programmer/norflash.cpp b/V203F6P6/programmer/norflash.cpp new file mode 100644 index 0000000..aa7adaa --- /dev/null +++ b/V203F6P6/programmer/norflash.cpp @@ -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 +#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 diff --git a/V203F6P6/programmer/software/Makefile b/V203F6P6/programmer/software/Makefile new file mode 100644 index 0000000..d6d9ceb --- /dev/null +++ b/V203F6P6/programmer/software/Makefile @@ -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 *~ + diff --git a/V203F6P6/programmer/software/baud.cpp b/V203F6P6/programmer/software/baud.cpp new file mode 100644 index 0000000..97c6012 --- /dev/null +++ b/V203F6P6/programmer/software/baud.cpp @@ -0,0 +1,14 @@ +#include +#include +#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); +} diff --git a/V203F6P6/programmer/software/endien.h b/V203F6P6/programmer/software/endien.h new file mode 100644 index 0000000..b0f2353 --- /dev/null +++ b/V203F6P6/programmer/software/endien.h @@ -0,0 +1,112 @@ +#ifndef ENDIEN_H +#define ENDIEN_H +#include +/** + @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 diff --git a/V203F6P6/programmer/software/helpers.h b/V203F6P6/programmer/software/helpers.h new file mode 100644 index 0000000..1403965 --- /dev/null +++ b/V203F6P6/programmer/software/helpers.h @@ -0,0 +1,75 @@ +#ifndef HELPERS_H +#define HELPERS_H +#include +#include +#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(ptr); + for (unsigned n=0; n(ptr); + for (unsigned n=0; n 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 +#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> 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> 1) + 1]; + uint8_t val = 0; + unsigned k = 0; + for (unsigned n=1; n> 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; +} diff --git a/V203F6P6/programmer/software/intelhex.h b/V203F6P6/programmer/software/intelhex.h new file mode 100644 index 0000000..d2a3c8c --- /dev/null +++ b/V203F6P6/programmer/software/intelhex.h @@ -0,0 +1,138 @@ +#ifndef INTELHEX_H +#define INTELHEX_H +#include + +/// 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ř. + * zde. 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 má 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 má 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 má 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 diff --git a/V203F6P6/programmer/software/linkprotocol.cpp b/V203F6P6/programmer/software/linkprotocol.cpp new file mode 100644 index 0000000..47be27c --- /dev/null +++ b/V203F6P6/programmer/software/linkprotocol.cpp @@ -0,0 +1,122 @@ +#include +#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; +} diff --git a/V203F6P6/programmer/software/linkprotocol.h b/V203F6P6/programmer/software/linkprotocol.h new file mode 100644 index 0000000..8b04cdc --- /dev/null +++ b/V203F6P6/programmer/software/linkprotocol.h @@ -0,0 +1,60 @@ +#ifndef LINKPROTOCOL_H +#define LINKPROTOCOL_H + +#include +#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 ní 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 má 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 diff --git a/V203F6P6/programmer/software/main.cpp b/V203F6P6/programmer/software/main.cpp new file mode 100644 index 0000000..c765098 --- /dev/null +++ b/V203F6P6/programmer/software/main.cpp @@ -0,0 +1,121 @@ +#include +#include +#include +#include +#include +#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 +#include +#include +#include +#include +#include +#include +#include +#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); + } +} diff --git a/V203F6P6/programmer/software/usart.h b/V203F6P6/programmer/software/usart.h new file mode 100644 index 0000000..813c63f --- /dev/null +++ b/V203F6P6/programmer/software/usart.h @@ -0,0 +1,32 @@ +#ifndef USARTCLASS_H +#define USARTCLASS_H + +#include +#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 diff --git a/V203F6P6/programmer/spiblocked.cpp b/V203F6P6/programmer/spiblocked.cpp new file mode 100644 index 0000000..0d48b88 --- /dev/null +++ b/V203F6P6/programmer/spiblocked.cpp @@ -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; +} + + diff --git a/V203F6P6/programmer/spiblocked.h b/V203F6P6/programmer/spiblocked.h new file mode 100644 index 0000000..852c818 --- /dev/null +++ b/V203F6P6/programmer/spiblocked.h @@ -0,0 +1,12 @@ +#ifndef SPIBLOCKED_H +#define SPIBLOCKED_H +#include + +class SpiBlocked { +public: + explicit SpiBlocked () noexcept; + uint8_t ReadWriteByte (const uint8_t data) const; + void ChipSelect (const bool on) const; +}; + +#endif // SPIBLOCKED_H diff --git a/V203F6P6/programmer/usb_desc.cpp b/V203F6P6/programmer/usb_desc.cpp new file mode 100644 index 0000000..76dcebb --- /dev/null +++ b/V203F6P6/programmer/usb_desc.cpp @@ -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 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(LangDescr); +const uint8_t * USBD_StringVendor = reinterpret_cast(&str_1); +const uint8_t * USBD_StringProduct = reinterpret_cast(&str_2); +const uint8_t * USBD_StringSerial = reinterpret_cast(&str_3); +