feat: add M95M02-only GAS and EEPROM profiles

Expose the existing registers through Modbus and service CAN using one GAS provider only in M95M02 builds.
Include the EEPROM layout/driver, LOAD protection and calibration, firmware identity and shared CCS build workflow.
Add transport/profile and SCI8 validation tests, documentation and the checked M95M02 firmware image.
This commit is contained in:
2026-10-09 16:17:37 +03:00
parent c6c9128238
commit 5d9818291b
63 changed files with 2307 additions and 365 deletions
+21
View File
@@ -0,0 +1,21 @@
"""Check SPI wire address widths for both production EEPROM configurations."""
from pathlib import Path
import subprocess
import os
import shutil
import tempfile
root = Path(__file__).resolve().parents[2]
protocol = root.parent / 'newProject/SETGUI/third_party/templates/c/set-protocol'
compiler = os.environ.get('CC') or shutil.which('gcc') or 'C:/msys64/mingw64/bin/gcc.exe'
os.environ['PATH'] = str(Path(compiler).resolve().parent) + os.pathsep + os.environ['PATH']
with tempfile.TemporaryDirectory() as folder:
for profile in (0, 1):
exe = Path(folder) / f'spi{profile}.exe'
subprocess.run([compiler, '-std=c99',
f'-DEEPROM_M95M02={profile}', '-Itests/eeprom_profiles/stubs',
'-ISource/Internal/Include', '-I' + str(protocol),
'tests/eeprom_profiles/spi_test.c', '-o', str(exe)],
cwd=root, check=True)
subprocess.run([str(exe)], check=True, timeout=10)
print(f'PASS SPI profile {profile}')
+47
View File
@@ -0,0 +1,47 @@
/* Exercise the production Timer2 SPI state machine; no hardware writes. */
#include <assert.h>
#include "../../Source/Internal/spise2p.c"
static unsigned trace[300], widths[300], used;
static void transfer(unsigned long address, int write)
{
unsigned buf[2]={0x1234,0x5678}, i;
SE2P_DATA message={buf,2,address};
used=0;
if(write) spiSe2pWrite(&se2p,&message); else spiSe2pRead(&se2p,&message);
for(i=0;i<100 && !spiSe2pFree(&se2p);i++) {
SpiaRegs.SPITXBUF=0xdead;
SpiaRegs.SPIRXBUF=0; /* WIP clear for write, deterministic read data. */
SpiaRegs.SPISTS.bit.INT_FLAG=1;
SPISE2P_DRV_tick(&se2p);
if(SpiaRegs.SPITXBUF!=0xdead) {
assert(GpioDataRegs.GPADAT.bit.GPIO19==0);
trace[used]=SpiaRegs.SPITXBUF;
widths[used++]=(SpiaRegs.SPICCR.all&15)+1;
}
}
assert(spiSe2pFree(&se2p) && GpioDataRegs.GPADAT.bit.GPIO19==1);
i=0;
if(write) { assert(trace[i]==0x0600 && widths[i++]==8); }
assert(trace[i]==(write ? 0x0200 : 0x0300) && widths[i++]==8);
#if EEPROM_M95M02
assert(trace[i]==((address>>16)<<8) && widths[i++]==8);
#endif
assert(trace[i]==(address&65535) && widths[i++]==16);
assert(trace[i]==(write ? 0x1234 : 0) && widths[i++]==16);
assert(trace[i]==(write ? 0x5678 : 0) && widths[i++]==16);
if(write) { assert(trace[i]==0x0500 && widths[i++]==8); assert(trace[i++]==0); }
assert(i==used);
}
int main(void)
{
SPISE2P_DRV_init(&se2p);
transfer(0x7ffc,0); transfer(0x7ffc,1);
#if EEPROM_M95M02
assert(BALSAM167_EEPROM_FIRMWARE_BYTES==261884UL);
transfer(0xfffe,0); transfer(0x10000,0);
transfer(0x20000,1); transfer(0x3fffc,0); transfer(0x3fffc,1);
#else
assert(BALSAM167_EEPROM_FIRMWARE_BYTES==32508);
#endif
return 0;
}
@@ -0,0 +1,22 @@
#ifndef EEPROM_TEST_DEVICE_H
#define EEPROM_TEST_DEVICE_H
#include <stdint.h>
#define interrupt
#define EALLOW
#define EDIS
#define CLKMULT 2
#define SYSCLKOUT 60000000UL
#define M_INT14 0x2000
static unsigned IER;
struct bits { unsigned GPIO16,GPIO17,GPIO18,GPIO19,SPISWRESET,BUFFULL_FLAG,INT_FLAG; };
struct reg { unsigned all; struct bits bit; };
static struct { struct reg GPAMUX2,GPADIR; } GpioCtrlRegs;
static struct { struct reg GPADAT; } GpioDataRegs;
static struct { void (*TINT2)(void); } PieVectTable;
static struct { unsigned InterruptCount; } CpuTimer2;
static struct { struct reg TCR; } CpuTimer2Regs;
static struct { struct reg SPICCR,SPICTL,SPISTS,SPIFFTX,SPIFFRX,SPIFFCT,SPIPRI;
unsigned SPIBRR; uint16_t SPITXBUF,SPIRXBUF; } SpiaRegs;
static void InitCpuTimers(void) {}
static void ConfigCpuTimer(void *p, unsigned long f, unsigned t) {(void)p;(void)f;(void)t;}
#endif