chore(merge): объедини pysetterminal с main

Сохрани актуальный генератор firmware identity с экспортом всей папки сборки и временем UTC+3 при объединении веток.
This commit is contained in:
2026-10-09 11:04:32 +03:00
77 changed files with 48505 additions and 65 deletions
+8
View File
@@ -149,6 +149,14 @@ if(SETP_BUILD_TESTS)
add_executable(test_periph28335 tests/test_periph28335.c)
target_link_libraries(test_periph28335 PRIVATE setprotocol_static)
add_test(NAME shared_periph28335 COMMAND test_periph28335)
add_executable(test_periph28335_eeprom tests/test_periph28335_eeprom.c)
target_link_libraries(test_periph28335_eeprom PRIVATE setprotocol_static)
if(MSVC)
target_compile_options(test_periph28335_eeprom PRIVATE /UNDEBUG)
else()
target_compile_options(test_periph28335_eeprom PRIVATE -UNDEBUG)
endif()
add_test(NAME periph28335_eeprom COMMAND test_periph28335_eeprom)
add_executable(test_tms2812 tests/test_tms2812.c)
target_link_libraries(test_tms2812 PRIVATE setprotocol_static)
add_test(NAME shared_tms2812 COMMAND test_tms2812)
+6
View File
@@ -1,5 +1,11 @@
# SETProtocol
`periph28335_eeprom_*` — read-only reader физической EEPROM Бальзам-167
(AT25256B, 32 КиБ), со стабильным C ABI и портом
`python/protocan/periph28335_eeprom.py`. Разметка платы задана в
`include/balsam167_eeprom_layout.h`; PEEK, CRC и порядок байтов реализованы
в `src/periph28335.c`. Проверка полного диапазона — `test_periph28335_eeprom`.
Универсальные сервисы эмуляторов: [адресное пространство, API и порт новой платы](docs/EMULATOR_SERVICES.md).
`set_regmap` объединяет УМП, 2812, IGBT и DAC за общим API для RTU, CAN и других оболочек.
@@ -0,0 +1,44 @@
/* Private PM35 FC03 EEPROM window; not standard Modbus register quantities.
* AT25256B keeps its existing 0x8000..0xBFFF / count 1..120 requests.
* M95M02 uses four 64-KiB windows: count bit15 marks the profile, bits9:8
* select bank 0..3, bits7:0 carry word count. Address 0x8000..0xFFFF is
* the word offset within a bank. Every request is self-contained; no global
* bank register can be changed by another client. Old firmware rejects the
* marked quantity. Neither profile silently reads the other memory type.
*/
#ifndef BALSAM167_EEPROM_BLOCK_H
#define BALSAM167_EEPROM_BLOCK_H
#include "balsam167_eeprom_layout.h"
#define BALSAM167_EEPROM_RTU_BASE 0x8000U
#define BALSAM167_EEPROM_BLOCK_WORDS 120U
#define BALSAM167_EEPROM_M95_MARKER 0x8000U
#define BALSAM167_EEPROM_WRITE_M95 0x44U
static unsigned long balsam167_eeprom_capacity(unsigned profile)
{
return profile == 0 ? 32768UL : (profile == 1 ? 262144UL : 0UL);
}
static unsigned long balsam167_eeprom_block_word(unsigned address, unsigned count)
{
return ((unsigned long)(count & 0x0300U) << 7) + (address - 0x8000U);
}
static unsigned balsam167_eeprom_block_valid_profile(unsigned fn, unsigned address,
unsigned count, unsigned profile)
{
unsigned n = count & 255U;
unsigned long word;
if (fn != 3 || address < 0x8000U || !n || n > 120U) return 0;
if (profile == 0) return count == n && address < 0xC000U && n <= 0xC000U-address;
if (profile != 1 || (count & 0xFC00U) != 0x8000U) return 0;
word = balsam167_eeprom_block_word(address, count);
return n <= 0x10000UL-address && word+n <= 131072UL;
}
static unsigned balsam167_eeprom_block_valid(unsigned fn, unsigned address, unsigned count)
{
#if EEPROM_M95M02
return balsam167_eeprom_block_valid_profile(fn, address, count, 1);
#else
return fn == 3 && address >= 0x8000U && address < 0xC000U &&
count && count <= 120U && count <= 0xC000U-address;
#endif
}
#endif
@@ -0,0 +1,29 @@
/* Board-specific layout shared by firmware, linker and host tools.
* Fitted part: AT25256B, top marking 5EBL = 256 Kbit = 32768 OCTETS.
* ATMLH516 is a manufacturer/date code, not a 512-Kbit density code.
* Numeric defines intentionally have no C suffix: TI linker also includes this.
*/
#ifndef BALSAM167_EEPROM_LAYOUT_H
#define BALSAM167_EEPROM_LAYOUT_H
/* EEPROM_M95M02 is selected by the board configuration, never autodetected
* by writing test patterns. Keep numeric constants usable by the TI linker. */
#define BALSAM167_AT25256_BYTES 0x8000
#define BALSAM167_M95M02_BYTES 0x40000
#ifndef EEPROM_M95M02
#define EEPROM_M95M02 0
#endif
#if EEPROM_M95M02
#define BALSAM167_EEPROM_BYTES BALSAM167_M95M02_BYTES
#define BALSAM167_EEPROM_PAGE_BYTES 256
#else
#define BALSAM167_EEPROM_BYTES BALSAM167_AT25256_BYTES
#define BALSAM167_EEPROM_PAGE_BYTES 64
#endif
#define BALSAM167_EEPROM_PARAM_WORDS 129
#define BALSAM167_EEPROM_WORDS (BALSAM167_EEPROM_BYTES / 2)
/* Preserve the historical end-exclusive parameter boundary. The final word
* remains unused; moving it would break existing saved configurations. */
#define BALSAM167_EEPROM_PARAMS_END_WORD (BALSAM167_EEPROM_WORDS - 1)
#define BALSAM167_EEPROM_PARAMS_START_WORD (BALSAM167_EEPROM_PARAMS_END_WORD - BALSAM167_EEPROM_PARAM_WORDS)
#define BALSAM167_EEPROM_FIRMWARE_BYTES (BALSAM167_EEPROM_PARAMS_START_WORD * 2)
#endif
+22
View File
@@ -82,6 +82,25 @@ PCAN_ABI_API size_t pcan_abi_balsam_register_name(
/* PM35/TMS320F28335 direct RS232/485 register terminal. */
PCAN_ABI_API uint16_t pcan_abi_periph28335_crc16(
const uint8_t *data, size_t size);
/* Caller-owned EEPROM reader state: uint32_t[4], 16 bytes, see periph28335.h.
* Exported independently of PM67: the EEPROM routing is specific to 167. */
PCAN_ABI_API int pcan_abi_periph28335_eeprom_begin(void *state,
uint32_t controller, uint32_t start, uint32_t length);
PCAN_ABI_API size_t pcan_abi_periph28335_eeprom_request(const void *state,
uint8_t *output, size_t capacity);
PCAN_ABI_API int pcan_abi_periph28335_eeprom_accept(void *state,
const uint8_t *reply, size_t size, uint8_t *output, size_t capacity);
PCAN_ABI_API uint32_t pcan_abi_periph28335_eeprom_capacity(void);
PCAN_ABI_API uint32_t pcan_abi_periph28335_eeprom_capacity_profile(unsigned profile);
PCAN_ABI_API int pcan_abi_periph28335_eeprom_begin_profile(void *r, uint32_t c, uint32_t a, uint32_t n, unsigned profile);
PCAN_ABI_API size_t pcan_abi_periph28335_eeprom_block_expected_profile(const void *r, unsigned profile);
PCAN_ABI_API size_t pcan_abi_periph28335_eeprom_block_request_profile(const void *r, uint8_t *o, size_t n, unsigned profile);
PCAN_ABI_API int pcan_abi_periph28335_eeprom_block_accept_profile(void *r, const uint8_t *p, size_t n, uint8_t *o, size_t cap, unsigned profile);
PCAN_ABI_API size_t pcan_abi_periph28335_eeprom_write_profile(uint32_t controller, uint32_t destination, uint32_t source, uint32_t length, unsigned profile, uint8_t *out, size_t cap);
PCAN_ABI_API size_t pcan_abi_periph28335_eeprom_block_expected(const void *state);
PCAN_ABI_API size_t pcan_abi_periph28335_eeprom_block_request(const void *state, uint8_t *out, size_t cap);
PCAN_ABI_API int pcan_abi_periph28335_eeprom_block_accept(void *state, const uint8_t *reply, size_t size, uint8_t *out, size_t cap);
PCAN_ABI_API size_t pcan_abi_periph28335_append_crc(
const uint8_t *payload, size_t payload_size,
uint8_t *output, size_t output_size);
@@ -110,6 +129,9 @@ PCAN_ABI_API const char *pcan_abi_periph28335_command_name(
/* PM67/TMS320F2812 legacy memory upload. */
PCAN_ABI_API size_t pcan_abi_tms2812_build_fixed(uint8_t controller, uint8_t command,
const uint8_t *payload, size_t payload_size, uint8_t *output, size_t output_size);
PCAN_ABI_API int pcan_abi_tms2812_bus_step(int write, unsigned step, unsigned tetrad, unsigned reg,
uint16_t data, uint32_t *address, uint16_t *value, uint8_t *command);
PCAN_ABI_API size_t pcan_abi_tms2812_build_memory(uint8_t controller, uint8_t command,
uint32_t word_address, uint16_t value, uint8_t *output, size_t output_size);
PCAN_ABI_API size_t pcan_abi_tms2812_build_test(uint8_t controller, uint16_t duration,
+41
View File
@@ -16,6 +16,7 @@
#include <stddef.h>
#include <stdint.h>
#include "balsam167_eeprom_layout.h"
#ifdef __cplusplus
extern "C" {
@@ -25,6 +26,46 @@ extern "C" {
#define PERIPH28335_REQUEST_SIZE 8U
#define PERIPH28335_COMMAND_COUNT 17U
/* Balsam-167 fitted AT25256B (marking 5EBL): 256 Kbit, NOT 256 Kbyte.
* All reader positions below count physical EEPROM octets, not C28x words.
* Four uint32_t fields make the caller-owned state stable for ctypes/JNI. */
#define PERIPH28335_EEPROM_BYTES BALSAM167_EEPROM_BYTES
typedef struct {
uint32_t controller;
uint32_t start;
uint32_t length;
uint32_t received;
} periph28335_eeprom_reader;
/* No writes or capacity probes. Odd starts/lengths are allowed: the core reads
* the containing 16-bit word and returns only the requested physical octets. */
int periph28335_eeprom_begin(periph28335_eeprom_reader *reader,
uint32_t controller, uint32_t start, uint32_t length);
size_t periph28335_eeprom_request(const periph28335_eeprom_reader *reader,
uint8_t *output, size_t capacity);
/* Returns 1/2 decoded octets, or a negative status; errors do not advance state.
* PEEK payload = little-endian data WORD + timer WORD, not four memory bytes.
* SPI16 shifts the data word high-octet first, so physical EEPROM order is
* response[3], response[2]. This differs from a C28x little-endian RAM dump. */
int periph28335_eeprom_accept(periph28335_eeprom_reader *reader,
const uint8_t *reply, size_t size, uint8_t *output, size_t capacity);
/* FC03 block reader uses the same state/physical byte range as legacy PEEK.
* expected() is the exact response length. No state advance on CRC/size error. */
size_t periph28335_eeprom_block_expected(const periph28335_eeprom_reader *r);
size_t periph28335_eeprom_block_request(const periph28335_eeprom_reader *r, uint8_t *out, size_t cap);
int periph28335_eeprom_block_accept(periph28335_eeprom_reader *r,
const uint8_t *reply, size_t size, uint8_t *out, size_t cap);
/* Profile 0 = AT25256B, 1 = M95M02; stable additional API for every host port.
* State remains four uint32_t fields. M95 requires block mode, no PEEK fallback. */
uint32_t periph28335_eeprom_capacity_profile(unsigned profile);
int periph28335_eeprom_begin_profile(periph28335_eeprom_reader *r, uint32_t c, uint32_t a, uint32_t n, unsigned profile);
size_t periph28335_eeprom_block_expected_profile(const periph28335_eeprom_reader *r, unsigned profile);
size_t periph28335_eeprom_block_request_profile(const periph28335_eeprom_reader *r, uint8_t *o, size_t n, unsigned profile);
int periph28335_eeprom_block_accept_profile(periph28335_eeprom_reader *r, const uint8_t *p, size_t n, uint8_t *o, size_t cap, unsigned profile);
size_t periph28335_eeprom_write_profile(uint32_t controller, uint32_t destination, uint32_t source, uint32_t length, unsigned profile, uint8_t *out, size_t cap);
typedef enum {
PERIPH28335_OK = 0,
PERIPH28335_ERROR_ARGUMENT = -1,
+1 -1
View File
@@ -12,7 +12,7 @@
extern "C" {
#endif
#define SET_SPECTRUM_MAX 16384U
#define SET_SPECTRUM_MAX 2097152U
enum { SET_WINDOW_RECT, SET_WINDOW_HANN, SET_WINDOW_HAMMING, SET_WINDOW_BLACKMAN, SET_WINDOW_FLATTOP };
enum { SET_FILTER_NONE, SET_FILTER_LOW_PASS, SET_FILTER_HIGH_PASS, SET_FILTER_BAND_PASS, SET_FILTER_NOTCH };
enum { SET_SPECTRUM_OK, SET_SPECTRUM_SHORT, SET_SPECTRUM_INVALID, SET_SPECTRUM_TIMING,
+3
View File
@@ -36,6 +36,9 @@ typedef enum {
TMS2812_ERROR_CAPACITY = -6
} tms2812_status_t;
int tms2812_bus_step(int write, unsigned step, unsigned tetrad, unsigned reg,
uint16_t data, uint32_t *address, uint16_t *value, uint8_t *command);
uint16_t tms2812_crc16(const uint8_t *data, size_t size);
size_t tms2812_build_fixed_request(uint8_t controller, uint8_t command,
+4
View File
@@ -17,6 +17,10 @@ extern "C" {
#define UMP_BASE 0x1000U
#define UMP_DATA 0x1010U
#define UMP_LIVE 0x1100U
/* Read-only running firmware identity, same 12-word v1 contract as Climate.
* Kept outside logger control/data and the EEPROM-backed legacy parameters. */
#define UMP_FIRMWARE_INFO 0x1200U
#define UMP_FIRMWARE_INFO_WORDS 12U
#define UMP_MARKER 0x1F00U
#define UMP_MAX_WORDS 124U
#define UMP_RTU_CAPACITY 253U
@@ -8,7 +8,17 @@
extern unsigned int ump_log_read(unsigned int address);
extern int ump_log_write(unsigned int address, unsigned int value);
static uint16_t read_word(void *context, uint16_t address)
{ (void)context; return (uint16_t)ump_log_read(address); }
{
(void)context;
/* Optional board binding to the common firmware-info static port. Older
* consumers still link without it and return contract=0 (unknown version).
* This hook changes neither PM67 CAN nor the saved parameter layout. */
#ifdef UMP_FIRMWARE_INFO_READ
if (address >= UMP_FIRMWARE_INFO && address < UMP_FIRMWARE_INFO + UMP_FIRMWARE_INFO_WORDS)
return UMP_FIRMWARE_INFO_READ(address - UMP_FIRMWARE_INFO);
#endif
return (uint16_t)ump_log_read(address);
}
static int write_word(void *context, uint16_t address, uint16_t value)
{ (void)context; return ump_log_write(address,value); }
unsigned ump_tms28335_prepare(unsigned fn, uint16_t a, uint16_t n,
+37
View File
@@ -101,6 +101,17 @@ uint16_t pcan_abi_periph28335_crc16(const uint8_t *data, size_t size)
return periph28335_crc16_modbus(data, size);
}
int pcan_abi_periph28335_eeprom_begin(void *state,
uint32_t controller, uint32_t start, uint32_t length)
{ return periph28335_eeprom_begin((periph28335_eeprom_reader *)state, controller, start, length); }
size_t pcan_abi_periph28335_eeprom_request(const void *state, uint8_t *out, size_t cap)
{ return periph28335_eeprom_request((const periph28335_eeprom_reader *)state, out, cap); }
int pcan_abi_periph28335_eeprom_accept(void *state, const uint8_t *reply,
size_t size, uint8_t *out, size_t cap)
{ return periph28335_eeprom_accept((periph28335_eeprom_reader *)state, reply, size, out, cap); }
uint32_t pcan_abi_periph28335_eeprom_capacity(void)
{ return PERIPH28335_EEPROM_BYTES; }
size_t pcan_abi_periph28335_append_crc(
const uint8_t *payload, size_t payload_size,
uint8_t *output, size_t output_size)
@@ -368,3 +379,29 @@ int pcan_abi_gui_parser_stats(const void *parser_storage, uint32_t *frames,
if (stray_bytes != NULL) *stray_bytes = parser->stats.stray_bytes;
return 1;
}
size_t pcan_abi_periph28335_eeprom_block_expected(const void *s)
{ return periph28335_eeprom_block_expected((const periph28335_eeprom_reader *)s); }
size_t pcan_abi_periph28335_eeprom_block_request(const void *s, uint8_t *out, size_t cap)
{ return periph28335_eeprom_block_request((const periph28335_eeprom_reader *)s,out,cap); }
int pcan_abi_periph28335_eeprom_block_accept(void *s, const uint8_t *reply, size_t size, uint8_t *out, size_t cap)
{ return periph28335_eeprom_block_accept((periph28335_eeprom_reader *)s,reply,size,out,cap); }
uint32_t pcan_abi_periph28335_eeprom_capacity_profile(unsigned p)
{ return periph28335_eeprom_capacity_profile(p); }
int pcan_abi_periph28335_eeprom_begin_profile(void *s, uint32_t c, uint32_t a, uint32_t n, unsigned p)
{ return periph28335_eeprom_begin_profile((periph28335_eeprom_reader *)s,c,a,n,p); }
size_t pcan_abi_periph28335_eeprom_block_expected_profile(const void *s, unsigned p)
{ return periph28335_eeprom_block_expected_profile((const periph28335_eeprom_reader *)s,p); }
size_t pcan_abi_periph28335_eeprom_block_request_profile(const void *s, uint8_t *o, size_t n, unsigned p)
{ return periph28335_eeprom_block_request_profile((const periph28335_eeprom_reader *)s,o,n,p); }
int pcan_abi_periph28335_eeprom_block_accept_profile(void *s, const uint8_t *r, size_t n, uint8_t *o, size_t cap, unsigned p)
{ return periph28335_eeprom_block_accept_profile((periph28335_eeprom_reader *)s,r,n,o,cap,p); }
size_t pcan_abi_periph28335_eeprom_write_profile(uint32_t c, uint32_t d, uint32_t s, uint32_t n, unsigned p, uint8_t *o, size_t cap)
{ return periph28335_eeprom_write_profile(c,d,s,n,p,o,cap); }
int pcan_abi_tms2812_bus_step(int write, unsigned step, unsigned tetrad, unsigned reg,
uint16_t data, uint32_t *address, uint16_t *value, uint8_t *command)
{
return tms2812_bus_step(write, step, tetrad, reg, data, address, value, command);
}
+137
View File
@@ -9,6 +9,143 @@
#include <string.h>
#include "set_crc.h"
#include "balsam167_eeprom_block.h"
static int eeprom_reader_valid(const periph28335_eeprom_reader *r, unsigned profile)
{
return r && r->controller >= 1U && r->controller <= 255U &&
r->start < balsam167_eeprom_capacity(profile) && r->length &&
r->length <= balsam167_eeprom_capacity(profile) - r->start && r->received < r->length;
}
int periph28335_eeprom_begin_profile(periph28335_eeprom_reader *r,
uint32_t controller, uint32_t start, uint32_t length, unsigned profile)
{
if (!r) return PERIPH28335_ERROR_ARGUMENT;
/* Invalidate first: a rejected new range must not leave an old read active. */
memset(r, 0, sizeof(*r));
if (!controller || controller > 255U || start >= balsam167_eeprom_capacity(profile) ||
!length || length > balsam167_eeprom_capacity(profile) - start)
return PERIPH28335_ERROR_RANGE;
r->controller = controller; r->start = start; r->length = length;
return PERIPH28335_OK;
}
size_t periph28335_eeprom_request(const periph28335_eeprom_reader *r,
uint8_t *output, size_t capacity)
{
uint8_t payload[6];
uint32_t word;
if (!eeprom_reader_valid(r, 0)) return 0;
word = (r->start + r->received) / 2U;
payload[0] = (uint8_t)r->controller; payload[1] = 56U; /* CMD_PEEK only */
/* 167 routes words 0..0x3fff to SPI EEPROM. The old terminal's
* 0x10000000 memory selector is NOT implemented by this firmware. */
payload[2] = (uint8_t)word; payload[3] = (uint8_t)(word >> 8);
payload[4] = 0; payload[5] = 0;
return periph28335_append_crc(payload, sizeof(payload), output, capacity);
}
int periph28335_eeprom_accept(periph28335_eeprom_reader *r,
const uint8_t *reply, size_t size, uint8_t *output, size_t capacity)
{
unsigned odd, count;
if (!eeprom_reader_valid(r, 0) || !reply || !output) return PERIPH28335_ERROR_ARGUMENT;
/* The UART layer may strip two legacy zero-padding bytes after CRC. */
if (size != 8U && size != 10U) return PERIPH28335_ERROR_LENGTH;
if (periph28335_crc16_modbus(reply, 8U) != 0) return PERIPH28335_ERROR_CRC;
if (reply[0] != r->controller || reply[1] != 56U) return PERIPH28335_ERROR_HEADER;
odd = (unsigned)((r->start + r->received) & 1U);
count = 2U - odd;
if (count > r->length - r->received) count = (unsigned)(r->length - r->received);
if (capacity < count) return PERIPH28335_ERROR_CAPACITY;
output[0] = reply[odd ? 2U : 3U];
if (count == 2U) output[1] = reply[2];
r->received += count;
return (int)count;
}
/* One common codec for all host ports. Physical EEPROM bytes already have
* network-word order; only trim an odd first/last byte, never swap them. */
static unsigned eeprom_block_words(const periph28335_eeprom_reader *r, unsigned profile)
{
uint32_t n;
if (!eeprom_reader_valid(r, profile)) return 0;
n = (r->length - r->received + ((r->start + r->received) & 1U) + 1U) / 2U;
/* A response cannot cross a bank boundary; trim the last block there. */
if (n > 32768U - ((r->start+r->received)/2U % 32768U))
n = 32768U - ((r->start+r->received)/2U % 32768U);
return (unsigned)(n > BALSAM167_EEPROM_BLOCK_WORDS ? BALSAM167_EEPROM_BLOCK_WORDS : n);
}
size_t periph28335_eeprom_block_expected_profile(const periph28335_eeprom_reader *r, unsigned profile)
{
unsigned n = eeprom_block_words(r, profile);
return n ? 5U + 2U*n : 0;
}
size_t periph28335_eeprom_block_request_profile(const periph28335_eeprom_reader *r, uint8_t *out, size_t cap, unsigned profile)
{
uint8_t p[6];
unsigned n = eeprom_block_words(r, profile), a, quantity;
uint32_t word;
if (!n) return 0;
word = (r->start + r->received) / 2U;
a = 0x8000U + (unsigned)(word % 32768U);
quantity = n | (profile ? (0x8000U | (unsigned)(word/32768U)*256U) : 0U);
if (!balsam167_eeprom_block_valid_profile(3,a,quantity,profile)) return 0;
p[0]=(uint8_t)r->controller; p[1]=3;
p[2]=(uint8_t)(a>>8); p[3]=(uint8_t)a; p[4]=(uint8_t)(quantity>>8); p[5]=(uint8_t)quantity;
return periph28335_append_crc(p,6,out,cap);
}
int periph28335_eeprom_block_accept_profile(periph28335_eeprom_reader *r,
const uint8_t *reply, size_t size, uint8_t *out, size_t cap, unsigned profile)
{
unsigned n=eeprom_block_words(r, profile), odd, count;
if (!n || !reply || !out) return PERIPH28335_ERROR_ARGUMENT;
if (size != 5U+2U*n) return PERIPH28335_ERROR_LENGTH;
if (periph28335_crc16_modbus(reply,size)) return PERIPH28335_ERROR_CRC;
/* Common address 16 is answered by the board's own Mode, 5 or 6. */
if ((reply[0] != r->controller && !(r->controller==16 && (reply[0]==5 || reply[0]==6))) ||
reply[1]!=3 || reply[2]!=2U*n) return PERIPH28335_ERROR_HEADER;
odd=(unsigned)((r->start+r->received)&1U);
count=2U*n-odd;
if (count>r->length-r->received) count=(unsigned)(r->length-r->received);
if (cap<count) return PERIPH28335_ERROR_CAPACITY;
memcpy(out,reply+3+odd,count);
r->received+=count;
return (int)count;
}
/* Keep all existing exported symbols and the 16-byte reader ABI unchanged. */
int periph28335_eeprom_begin(periph28335_eeprom_reader *r, uint32_t c, uint32_t a, uint32_t n)
{ return periph28335_eeprom_begin_profile(r,c,a,n,0); }
size_t periph28335_eeprom_block_expected(const periph28335_eeprom_reader *r)
{ return periph28335_eeprom_block_expected_profile(r,0); }
size_t periph28335_eeprom_block_request(const periph28335_eeprom_reader *r, uint8_t *o, size_t n)
{ return periph28335_eeprom_block_request_profile(r,o,n,0); }
int periph28335_eeprom_block_accept(periph28335_eeprom_reader *r, const uint8_t *p, size_t n, uint8_t *o, size_t cap)
{ return periph28335_eeprom_block_accept_profile(r,p,n,o,cap,0); }
uint32_t periph28335_eeprom_capacity_profile(unsigned profile)
{ return (uint32_t)balsam167_eeprom_capacity(profile); }
/* Legacy EXTEND carries word addresses and octet lengths. A distinct M95
* subcommand makes a wrong-profile write fail closed on either firmware. */
size_t periph28335_eeprom_write_profile(uint32_t controller, uint32_t destination,
uint32_t source, uint32_t length, unsigned profile, uint8_t *out, size_t cap)
{
uint8_t p[16]; unsigned i;
uint32_t capacity = periph28335_eeprom_capacity_profile(profile);
uint32_t limit = capacity ? capacity-(BALSAM167_EEPROM_PARAM_WORDS+1U)*2U : 0;
if (!controller || controller>255U || !length || length>199993U ||
destination>=limit/2U || length>limit-destination*2U) return 0;
p[0]=(uint8_t)controller; p[1]=60;
for(i=0;i<4;i++) {
p[2+i]=(uint8_t)(destination>>(8*i));
p[6+i]=(uint8_t)(source>>(8*i));
p[10+i]=(uint8_t)(length>>(8*i));
}
p[14]=(uint8_t)(profile ? BALSAM167_EEPROM_WRITE_M95 : 4); p[15]=0;
return periph28335_append_crc(p,16,out,cap);
}
typedef struct {
const char *project;
+13
View File
@@ -137,3 +137,16 @@ int tms2812_decode_upload_response(
memcpy(output, &data[2], byte_count);
return TMS2812_OK;
}
int tms2812_bus_step(int write, unsigned step, unsigned tetrad, unsigned reg,
uint16_t data, uint32_t *address, uint16_t *value, uint8_t *command)
{
if (!address || !value || !command) return TMS2812_ERROR_ARGUMENT;
if ((write != 0 && write != 1) || step > 1U || tetrad > 14U || reg > 15U)
return TMS2812_ERROR_RANGE;
*command = (!write && step == 1U) ? TMS2812_CMD_PEEK : TMS2812_CMD_POKE;
*address = write ? (step == 0U ? 0x200AU : 0x200BU) : (step == 0U ? 0x200BU : 0x200FU);
*value = write && step == 0U ? data : (!write && step == 1U ? 0U :
(uint16_t)((write ? 0x8000U : 0U) | (tetrad << 4) | reg));
return TMS2812_OK;
}
+5
View File
@@ -12,6 +12,11 @@ unsigned ump_validate(unsigned fn, uint16_t a, uint16_t n, int can)
{
if (fn == 6) return a >= UMP_BASE && a <= UMP_BASE+3 ? 0 : 2;
if (fn != 3) return 1;
/* CAN reads the whole immutable block (six fragments); RTU may read slices.
* Check subtraction only after the lower bound, avoiding uint16 wraparound. */
if (a >= UMP_FIRMWARE_INFO && a < UMP_FIRMWARE_INFO + UMP_FIRMWARE_INFO_WORDS)
return can ? ((a == UMP_FIRMWARE_INFO && n == UMP_FIRMWARE_INFO_WORDS) ? 0 : 2)
: ((n && n <= UMP_FIRMWARE_INFO + UMP_FIRMWARE_INFO_WORDS - a) ? 0 : 2);
if (can) return ((a == UMP_BASE && n == 16) ||
(a == UMP_DATA && (n == 40 || n == 80 || n == 120 || n == 42 || n == 84)) ||
(a == UMP_LIVE && n == 124)) ? 0 : 2;
@@ -0,0 +1,156 @@
#include "periph28335.h"
#include <assert.h>
#include "balsam167_eeprom_block.h"
#include <string.h>
static void response(uint8_t *out, unsigned high, unsigned low)
{
/* Reply is low data byte, high data byte, then an unrelated timer.
* Nonzero timer detects accidentally copying four memory bytes per PEEK. */
uint8_t payload[] = {16,56,0,0,0xef,0xbe};
payload[2]=(uint8_t)low; payload[3]=(uint8_t)high;
assert(periph28335_append_crc(payload,6,out,10)==8);
out[8]=out[9]=0;
}
static void blocks(void)
{
periph28335_eeprom_reader r;
uint8_t req[8], reply[245], out[240];
unsigned start, transactions, n, a, i, old;
int got;
assert(!balsam167_eeprom_block_valid(6,0x8000,1));
assert(!balsam167_eeprom_block_valid(3,0xbfff,2));
assert(!balsam167_eeprom_block_valid(3,0xffff,120));
assert(!balsam167_eeprom_block_valid(3,0x8000,121));
assert(!balsam167_eeprom_block_valid(3,0x8000,0));
assert(balsam167_eeprom_block_valid(3,0xbfff,1));
for(start=0;start<2;start++) {
assert(!periph28335_eeprom_begin(&r,16,start,32768-start));
transactions=0;
while(r.received<r.length) {
assert(periph28335_eeprom_block_request(&r,req,8)==8);
assert(!periph28335_crc16_modbus(req,8));
a=((unsigned)req[2]*256+req[3]-0x8000)*2;
n=req[5]; assert(n && n<=120 && a+2*n<=32768);
reply[0]=5; reply[1]=3; reply[2]=(uint8_t)(2*n);
for(i=0;i<2*n;i++) reply[3+i]=(uint8_t)(a+i);
assert(periph28335_append_crc(reply,3+2*n,reply,sizeof reply)==5+2*n);
old=r.received;
reply[4]^=1;
assert(periph28335_eeprom_block_accept(&r,reply,5+2*n,out,240)==PERIPH28335_ERROR_CRC);
assert(r.received==old); reply[4]^=1;
assert(periph28335_eeprom_block_accept(&r,reply,5+2*n,out,0)==PERIPH28335_ERROR_CAPACITY);
assert(r.received==old);
got=periph28335_eeprom_block_accept(&r,reply,5+2*n,out,240);
assert(got>0);
for(i=0;i<(unsigned)got;i++) assert(out[i]==(uint8_t)(start+old+i));
transactions++;
}
assert(transactions==137);
assert(!periph28335_eeprom_block_request(&r,req,8));
}
}
static void profiles(void)
{
periph28335_eeprom_reader r;
uint8_t req[18], reply[245], out[240];
unsigned n, i, q, a, bankmask=0;
uint32_t pos, before;
assert(periph28335_eeprom_capacity_profile(0)==32768);
assert(periph28335_eeprom_capacity_profile(1)==262144);
assert(periph28335_eeprom_capacity_profile(2)==0);
assert(periph28335_eeprom_begin_profile(&r,16,262143,2,1)<0);
assert(periph28335_eeprom_begin_profile(&r,16,0,2,2)<0);
assert(!balsam167_eeprom_block_valid_profile(3,0xffff,0x8078,1));
assert(!balsam167_eeprom_block_valid_profile(3,0x8000,0x8401,1));
assert(!balsam167_eeprom_block_valid_profile(3,0x8000,0x8001,0));
assert(!balsam167_eeprom_block_valid_profile(3,0x8000,1,1));
assert(balsam167_eeprom_block_valid_profile(3,0xffff,0x8301,1));
/* Full 256-KiB dump from an odd start exercises every bank transition,
* first/last byte trimming, CRC failure and exact physical byte order. */
assert(!periph28335_eeprom_begin_profile(&r,16,1,262143,1));
while(r.received<r.length) {
assert(periph28335_eeprom_block_request_profile(&r,req,8,1)==8);
assert(!periph28335_crc16_modbus(req,8));
a=req[2]*256U+req[3]; q=req[4]*256U+req[5]; n=q&255U;
assert(balsam167_eeprom_block_valid_profile(3,a,q,1));
pos=balsam167_eeprom_block_word(a,q)*2UL;
assert(pos == (r.start+r.received)/2*2);
bankmask |= 1U << ((q>>8)&3U);
assert(periph28335_eeprom_block_expected_profile(&r,1)==5+2*n);
reply[0]=5; reply[1]=3; reply[2]=(uint8_t)(n*2);
for(i=0;i<n*2;i++) reply[3+i]=(uint8_t)((pos+i)^((pos+i)>>16));
periph28335_append_crc(reply,3+n*2,reply,sizeof(reply));
before=r.received; reply[4]^=1;
assert(periph28335_eeprom_block_accept_profile(&r,reply,5+2*n,out,240,1)==PERIPH28335_ERROR_CRC);
assert(r.received==before); reply[4]^=1;
assert(periph28335_eeprom_block_accept_profile(&r,reply,5+2*n,out,240,1)>0);
for(i=0;i<r.received-before;i++) {
pos=1+before+i;
assert(out[i]==(uint8_t)(pos^(pos>>16)));
}
}
assert(bankmask==15);
assert(!periph28335_eeprom_block_request_profile(&r,req,8,1));
assert(periph28335_eeprom_write_profile(5,0,0x200000,33514,0,req,18)==0);
assert(periph28335_eeprom_write_profile(5,0,0x200000,33514,1,req,18)==18);
assert(req[14]==0x44 && req[15]==0 && !periph28335_crc16_modbus(req,18));
assert(!periph28335_eeprom_write_profile(5,0x1ff7e,0,2,1,req,18));
assert(!periph28335_eeprom_write_profile(5,0xffffffff,0,2,1,req,18));
assert(periph28335_eeprom_write_profile(5,0,0,32438,0,req,18)==18 && req[14]==4);
}
int main(void)
{
periph28335_eeprom_reader reader;
blocks();
profiles();
uint8_t request[8], reply[10], data[2];
unsigned i;
assert(sizeof(reader)==16);
assert(periph28335_eeprom_begin(&reader,16,0,0)==PERIPH28335_ERROR_RANGE);
assert(periph28335_eeprom_begin(&reader,0,0,2)==PERIPH28335_ERROR_RANGE);
assert(periph28335_eeprom_begin(&reader,16,0x8000,1)==PERIPH28335_ERROR_RANGE);
assert(periph28335_eeprom_begin(&reader,16,0x7fff,2)==PERIPH28335_ERROR_RANGE);
assert(periph28335_eeprom_begin(&reader,16,1,0xffffffffUL)==PERIPH28335_ERROR_RANGE);
assert(periph28335_eeprom_request(&reader,request,8)==0);
assert(periph28335_eeprom_begin(&reader,16,1,3)==0);
assert(periph28335_eeprom_request(&reader,request,7)==0);
assert(periph28335_eeprom_request(&reader,request,8)==8);
assert(memcmp(request,"\x10\x38\0\0\0\0",6)==0);
response(reply,0x12,0x34);
assert(periph28335_eeprom_accept(&reader,reply,8,data,2)==1 && data[0]==0x34);
assert(reader.received==1);
assert(periph28335_eeprom_request(&reader,request,8)==8 && request[2]==1);
reply[2]^=1;
assert(periph28335_eeprom_accept(&reader,reply,8,data,2)==PERIPH28335_ERROR_CRC);
assert(reader.received==1);
response(reply,0x56,0x78);
assert(periph28335_eeprom_accept(&reader,reply,8,data,1)==PERIPH28335_ERROR_CAPACITY);
assert(reader.received==1);
assert(periph28335_eeprom_accept(&reader,reply,10,data,2)==2);
assert(data[0]==0x56 && data[1]==0x78 && reader.received==3);
assert(periph28335_eeprom_request(&reader,request,8)==0);
/* Every requested word of a complete dump remains in the EEPROM window.
* In particular, neither the RAM route nor selector 0x10000000 is used. */
assert(periph28335_eeprom_begin(&reader,16,0,32768)==0);
for (i=0;i<16384;i++) {
assert(periph28335_eeprom_request(&reader,request,8)==8);
assert(request[1]==56 && request[4]==0 && request[5]==0);
assert(((unsigned)request[3]*256+request[2])==i);
assert(periph28335_crc16_modbus(request,8)==0);
response(reply,i>>8,i);
assert(periph28335_eeprom_accept(&reader,reply,8,data,2)==2);
assert(data[0]==(i>>8) && data[1]==(i&255));
}
assert(reader.received==32768 && periph28335_eeprom_request(&reader,request,8)==0);
assert(periph28335_eeprom_begin(&reader,16,0x7fff,1)==0);
assert(periph28335_eeprom_request(&reader,request,8)==8);
assert(request[2]==0xff && request[3]==0x3f);
response(reply,0x9a,0xbc);
assert(periph28335_eeprom_accept(&reader,reply,8,data,2)==1 && data[0]==0xbc);
return 0;
}
+13
View File
@@ -18,6 +18,19 @@ static int test_terminal(void)
uint8_t reply[] = {0x0a, 0x38, 0x65, 0xa5, 0, 0, 0xaf, 0x9b, 0, 0};
const uint8_t test_prefix[] = {7, 66, 25, 0, 18, 0};
size_t size;
uint32_t bus_address;
uint16_t bus_value;
uint8_t bus_command;
if (tms2812_bus_step(1, 0, 3, 5, 0x1234, &bus_address, &bus_value, &bus_command) ||
bus_address != 0x200AU || bus_value != 0x1234 || bus_command != 57) return 20;
if (tms2812_bus_step(1, 1, 3, 5, 0x1234, &bus_address, &bus_value, &bus_command) ||
bus_address != 0x200BU || bus_value != 0x8035 || bus_command != 57) return 21;
if (tms2812_bus_step(0, 0, 3, 5, 0, &bus_address, &bus_value, &bus_command) ||
bus_address != 0x200BU || bus_value != 0x35 || bus_command != 57) return 22;
if (tms2812_bus_step(0, 1, 3, 5, 0, &bus_address, &bus_value, &bus_command) ||
bus_address != 0x200FU || bus_command != 56) return 23;
if (tms2812_bus_step(0, 0, 15, 0, 0, &bus_address, &bus_value, &bus_command) != TMS2812_ERROR_RANGE ||
tms2812_bus_step(0, 2, 0, 0, 0, &bus_address, &bus_value, &bus_command) != TMS2812_ERROR_RANGE) return 24;
if (tms2812_build_memory_request(10, 0, 0, 0, output, sizeof output) != 8U ||
memcmp(output, ping, sizeof ping) != 0) return 10;
if (tms2812_build_memory_request(10, TMS2812_CMD_POKE, 0x12345678UL, 0xABCD,