refactoring programmer

This commit is contained in:
Kizarm 2025-02-04 16:32:08 +01:00
parent b9052ca694
commit b9a3524daf
9 changed files with 284 additions and 364 deletions

View file

@ -16,7 +16,11 @@ class MemoryBase {
return flash.ReadBlock(addr, reinterpret_cast<uint8_t*> (ptr), len);
}
void Erase (const uint32_t blok) {
flash.EraseSector(blok);
/* Flash používá 24-bitovou adresu počátku bloku, zde máme k dispozici
* jen 16-bitový offset. Takže to jen natáhneme 256 krát. Software
* tomu musí odpovídat.
* */
flash.EraseSector(blok << 8);
}
};

View file

@ -6,7 +6,8 @@
/*********************************************************************************
* 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.
* binární soubory. Dodělat Intel hex není problém, ale nepotřebuji to.
* Umí to jen nor flash se stránkou 4096 bytů, předpokládaná velikost je 32Mbit.
* Software je dost surový, bez kontrol, prostě jen aby to trochu fungovalo.
**********************************************************************************/
static GpioClass led (GPIOB, 8);

View file

@ -7,7 +7,8 @@ MFLAGS = -o $(PR)
all: $(PR)
OBJECTS = main.o usart.o baud.o linkprotocol.o intelhex.o
OBJECTS = main.o usart.o baud.o programmer.o intelhex.o
#linkprotocol.o
%.o: %.c
$(CC) -c $(CFLAGS) $< -o $@

View file

@ -1,75 +0,0 @@
#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

@ -1,122 +0,0 @@
#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

@ -1,60 +0,0 @@
#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

@ -1,118 +1,27 @@
#include <signal.h>
#include <cstdio>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <cstdlib>
#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;
}
#include "programmer.h"
int main(int argc, char *argv[]) {
if (argc < 3) {
printf("Usage:\r\t$ %s r|w|v file.bin\n", argv[0]);
printf("Usage:\r\t$ %s r|w|v file.bin [numblocks]\n", argv[0]);
return 0;
}
loop = true;
signal(SIGINT, sig_handler);
UsartClass usart ("/dev/serial/by-id/usb-Kizarm_Labs._Flash_Programmer_0001-if00", 9600);
Programmer cmd;
cmd += usart;
cmd.Flush();
if (argv[1][0] == 'r') {
read_flash_binary (argv[2]);
long len = 16;
if (argc > 3) {
len = strtol (argv[3], nullptr, 10);
}
cmd.read_flash_binary (argv[2], len);
} else if (argv[1][0] == 'w') {
write_flash_binary(argv[2]);
cmd.write_flash_binary(argv[2]);
} else if (argv[1][0] == 'v') {
verify_flash_binary(argv[2]);
cmd.verify_flash_binary(argv[2]);
} else {
printf("bad parameter %s\n", argv[1]);
}

View file

@ -0,0 +1,216 @@
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "programmer.h"
#define trace printf
Programmer::Programmer() : BaseLayer(), ring(), ihx(),
line_len(0u), line_index(0u), begin_addr(0u), file_len(0u) {
file_pool = new uint8_t [MaxPages * PageSize];
memory_pool = new uint8_t [MaxPages * PageSize];
}
Programmer::~Programmer() {
delete [] memory_pool;
delete [] file_pool;
}
void Programmer::Flush() {
char c;
while (ring.Read(c));
}
uint32_t Programmer::Up(const char * data, const uint32_t len) {
for (unsigned n=0u; n<len; n++) ring.Write (data [n]);
return len;
}
void Programmer::read_flash_binary(const char * name, const unsigned int blen) {
file_len = blen * PageSize;
if (file_len > (MaxPages * PageSize)) {
fprintf(stderr, "read request too long - %d\n", file_len);
}
printf("read %d bytes...\n", file_len);
read_flash(file_len);
FILE * out = fopen(name, "w");
int r = fwrite(memory_pool, 1, file_len, out);
printf("writen %d bytes to %s\n", r, name);
fclose(out);
}
void Programmer::verify_flash_binary(const char * name) {
if (!open_file_for_read(name)) return;
printf("verify %d bytes...\n", file_len);
read_flash(file_len);
unsigned err = 0u;
for (unsigned n=0u; n<file_len; n++) {
if (file_pool[n] != memory_pool[n]) {
fprintf(stderr, "err at 0x%06X: 0x%02X != 0x%02X\n", n, file_pool[n], memory_pool[n]);
err += 1u;
}
}
if (err) printf("verify errors: %d\n", err);
else printf("flash OK\n");
}
void Programmer::write_flash_binary(const char * name) {
if (!open_file_for_read(name)) return;
uint32_t blocks = file_len / PageSize;
if (file_len % PageSize) blocks += 1u;
erase_blocks (blocks);
begin_addr = 0u;
StartOperation(begin_addr);
unsigned ofs = 0u, rem = file_len, chunk;
while (true) {
chunk = rem > 0x20 ? 0x20 : rem;
unsigned rl = ihx.DataRecord(strbuf, file_pool + begin_addr + ofs, chunk);
SendString(strbuf, rl);
while (!GetLine());
RowTypes t = ihx.parseRecord(line, line_len);
if (t != ackRecord) fprintf(stderr, "BAD WriteOperation 0x%X\n", begin_addr + ofs);
ihx.AddOffset(chunk);
ofs += chunk;
rem -= chunk;
printf("write at 0x%04X \r", begin_addr + ofs);
fflush(stdout);
if (ofs >= ihxPage) {
begin_addr += ihxPage;
StartOperation(begin_addr);
ofs = 0u;
}
if (rem == 0u) break;
}
printf("\n");
}
void Programmer::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;
}
}
bool Programmer::open_file_for_read(const char * name) {
struct stat prop;
const int r = stat (name, & prop);
if (r) {
fprintf(stderr, "file %s not exists.", name);
return false;
}
const unsigned flen = prop.st_size;
if (flen > (MaxPages * PageSize)) {
fprintf(stderr, "verify/write request too long - %d\n", flen);
return false;
}
FILE * in = fopen (name, "r");
if (!in) {
fprintf(stderr, "file %s not open for read.", name);
return false;
}
const int l = fread (file_pool, 1, flen, in);
printf("%s: readen = %d bytes\n", name, l);
fclose(in);
file_len = flen;
return true;
}
bool Programmer::GetLine() {
char c;
while (ring.Read(c)) {
line [line_index++] = c;
if (c == '\n') {
line_len = line_index;
/*
fprintf(stdout, "RX");
int r = fwrite (line, 1, line_len, stdout); (void) r;
fflush (stdout);
*/
line_index = 0u;
return true;
}
}
return false;
}
void Programmer::ParseLine() {
RowTypes t = ihx.parseRecord (line, line_len);
switch (t) {
case dataRecord: AcquireDataRecord (); break;
case reqRecord: AcquireReqRecord (); break;
default:
trace ("BAD record 0x%02X\r\n", t);
break;
}
}
void Programmer::StartOperation(const unsigned addr) {
uint32_t res = ihx.ElaRecord(strbuf, addr);
SendString (strbuf, res);
while (!GetLine());
RowTypes t = ihx.parseRecord(line, line_len);
if (t != ackRecord) fprintf(stderr, "BAD StartOperation\n");
}
void Programmer::AcquireDataRecord() {
uint32_t ofs, len;
uint8_t * ptr = ihx.getDataRow (ofs, len);
memcpy (memory_pool + begin_addr + ofs, ptr, len);
if (len) {
ihx.AddOffset (len); // posun offsetu az po ack
return;
}
}
void Programmer::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);
memcpy (data, memory_pool + begin_addr + ofs, len);
res = ihx.DataRecord (strbuf, data, len);
SendString (strbuf, res);
}
void Programmer::read_flash(const unsigned len) {
StartOperation(begin_addr);
while (true) {
const uint32_t res = ihx.BTxRecord (strbuf, reqRecord);
SendString (strbuf, res);
while (!GetLine());
ParseLine();
uint32_t ofs;
ihx.getOffset(ofs);
printf("read flash at 0x%X \r", begin_addr + ofs);
fflush(stdout);
if (ofs >= ihxPage) {
begin_addr += ihxPage;
StartOperation(begin_addr);
ofs = 0u;
}
if ((begin_addr + ofs) >= len) break;
}
printf("\n");
}
void Programmer::Erase(const unsigned int n) {
uint32_t ofs, addr = n * 0x10u; // adresa zmenšena 256x
ihx.getOffset(ofs);
ihx.AddOffset(addr - ofs);
uint32_t res = ihx.BTxRecord (strbuf, ersRecord);
SendString (strbuf, res);
}
void Programmer::erase_blocks(const unsigned int blocks) {
StartOperation(0u);
for (unsigned n=0u; n<blocks; n++) {
Erase(n);
while (!GetLine());
RowTypes t = ihx.parseRecord(line, line_len);
if (t != ackRecord) fprintf(stderr, "BAD EraseOperation %d\n", n);
printf("erase block %d \r", n+1);
fflush(stdout);
}
printf("\n");
}

View file

@ -0,0 +1,46 @@
#ifndef PROGRAMMER_H
#define PROGRAMMER_H
#include "cstdint"
#include "baselayer.h"
#include "fifo.h"
#include "intelhex.h"
/**
*/
class Programmer : public BaseLayer {
static constexpr unsigned MaxStrLen = 1024u;
static constexpr unsigned MaxPages = 1024u;
static constexpr unsigned PageSize = 0x1000u;
static constexpr unsigned ihxPage = 0x10000u;
FIFO<char, MaxStrLen> ring;
IntelHex ihx;
char strbuf[MaxStrLen];
char line [MaxStrLen];
unsigned line_len, line_index;
unsigned begin_addr;
unsigned file_len;
uint8_t * file_pool;
uint8_t * memory_pool;
public:
explicit Programmer ();
virtual ~Programmer ();
uint32_t Up (const char * data, const uint32_t len) override;
void Flush ();
void read_flash_binary (const char * name, const unsigned blen = 16u);
void verify_flash_binary (const char * name);
void write_flash_binary (const char * name);
protected:
bool open_file_for_read (const char * name);
void SendString (const char * ptr, const uint32_t len);
bool GetLine ();
void ParseLine ();
void StartOperation (const unsigned addr);
void AcquireDataRecord ();
void AcquireReqRecord ();
void Erase (const unsigned n);
void read_flash (const unsigned len);
void erase_blocks (const unsigned blocks);
};
#endif // PROGRAMMER_H