Files
Balsam_167/tests/gas167/gas167_test.c
Andrey 5d9818291b 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.
2026-10-09 16:17:37 +03:00

197 lines
7.9 KiB
C

#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "gas167.h"
#include "ump_protocol.h"
#include "ump_tms28335.h"
#include "include/balsam167_eeprom_block.h"
#define ANSWER_LEN 128U
#define MAX_SEND_LENGTH 400U
#define CNTRL_ADDR 16U
#define CMD_MODBUS_3 3U
#define CMD_MODBUS_6 6U
#define dsk_LOAD 6
#define LOBYTE(v) ((v) & 255U)
#define HIBYTE(v) (((v) >> 8) & 255U)
#define UMP_CAN_MAX_WORDS UMP_MAX_WORDS
#define ump_can_prepare ump_tms28335_prepare
typedef struct { int all; } WORDE;
#define Modbus ((WORDE *)modbus)
typedef struct {
uint16_t RS_Header[8], buffer[MAX_SEND_LENGTH];
unsigned flag_TIMEOUT_to_Send;
} RS_DATA;
int modbus[129], Desk = dsk_LOAD;
static unsigned captures, spi_reads, sent, irq_depth, legacy_reads;
static unsigned __disable_interrupts(void) {
unsigned saved = irq_depth;
++irq_depth;
return saved;
}
static void __restore_interrupts(unsigned saved) { irq_depth = saved; }
void ump_log_rs(void) { ++captures; }
unsigned int ump_log_read(unsigned int address) { ++legacy_reads; return address; }
int ump_log_write(unsigned int address, unsigned int value) { return 1; }
static unsigned get_crc_16(unsigned seed, const uint16_t *data, unsigned size) {
assert(seed == 0xFFFFU);
return ump_crc(data, size);
}
static void RS_Send(RS_DATA *rs, uint16_t *data, unsigned size) {
assert(irq_depth == 0);
sent = size;
}
static void Seeprom_read(unsigned long word, uint16_t *data, unsigned bytes) {
unsigned i;
assert(irq_depth == 0); /* The Timer2 SPI driver must retain interrupts. */
++spi_reads;
for (i = 0; i < bytes/2; ++i) data[i] = (uint16_t)(0xBEEFU+i);
}
static unsigned Load_protection_prepare(unsigned fn, unsigned a, unsigned n,
uint16_t *out, unsigned *error) {
out[0] = (uint16_t)a; out[1] = (uint16_t)n; *error = 0;
return 2;
}
struct mock_reg { uint32_t all; };
static struct {
struct mock_reg CANTA, CANAA, CANTRR, CANTRS;
} ECanaRegs;
static struct {
struct { struct mock_reg MDH, MDL; } MBOX2;
} ECanaMboxes;
static struct { unsigned long InterruptCount; } CpuTimer1;
#include "production_routes.inc"
static void request(RS_DATA *rs, unsigned fn, unsigned a, unsigned n) {
unsigned crc;
memset(rs, 0, sizeof(*rs));
rs->RS_Header[0] = CNTRL_ADDR; rs->RS_Header[1] = fn;
rs->RS_Header[2] = a >> 8; rs->RS_Header[3] = a & 255U;
rs->RS_Header[4] = n >> 8; rs->RS_Header[5] = n & 255U;
crc = ump_crc(rs->RS_Header, 6);
rs->RS_Header[6] = crc & 255U; rs->RS_Header[7] = crc >> 8;
sent = 0;
if (fn == 3) ReceiveCommandModbus3(rs);
else ReceiveCommandModbus6(rs);
assert(irq_depth == 0 && rs->flag_TIMEOUT_to_Send);
assert(ump_crc(rs->buffer, rs->buffer[1] & 128U ? 5 :
(fn == 6 ? 8 : 5+rs->buffer[2])) == 0);
}
static void can_request(unsigned fn, unsigned a, unsigned n) {
memset(&ECanaRegs, 0, sizeof(ECanaRegs));
memset(&ECanaMboxes, 0, sizeof(ECanaMboxes));
ump_can_waiting = 0; ump_can_length = 0; ump_can_index = 0;
ump_can_pending = 1; ump_can_tag = 0x0100U | fn;
ump_can_address = a; ump_can_value = n;
UMP_CAN_service();
assert(irq_depth == 0 && !ump_can_pending);
assert(ECanaRegs.CANTRS.all == 4U);
}
int main(void) {
RS_DATA rs;
unsigned i;
for (i = 0; i < 129; ++i) modbus[i] = (int)i;
modbus[128] = 0xD00D;
/* Exercise the real native handlers in both builds. */
request(&rs, 6, 1, 0xFEDC);
assert(modbus[1] == 0xFEDC && sent == 10);
request(&rs, 3, 0, 2);
assert(rs.buffer[2] == 4 && rs.buffer[5] == 0xFE && rs.buffer[6] == 0xDC);
request(&rs, 6, 0x52, 0);
assert(rs.buffer[1] == 0x86 && rs.buffer[2] == 3 && modbus[0x52] == 0x52);
request(&rs, 6, 0x7C, 9);
assert(rs.buffer[2] == 2 && modbus[0x7C] == 0x7C);
/* Existing UMP and protection routing must still work. */
request(&rs, 3, 0x1000, 2);
assert(rs.buffer[1] == 3 && rs.buffer[3] == 0x10 && legacy_reads == 2);
request(&rs, 3, 0x1300, 2);
assert(rs.buffer[1] == 3 && rs.buffer[3] == 0x13);
#if EEPROM_M95M02
{
uint16_t out[124];
unsigned error, count;
for (i = 0; i < 65536U; ++i)
assert(gas167_contains((uint16_t)i) ==
((i >= 0x5000U && i <= 0x507FU) || (i >= 0x5F00U && i <= 0x5F07U)));
assert(gas167_prepare(3, 0x5000, 124, 0, out, &error) == 124 && !error);
assert(out[1] == 0xFEDC && out[123] == 123);
count = gas167_prepare(3, 0x507F, 2, 0, out, &error);
assert(count == 2 && error == 2 && modbus[128] == 0xD00D);
assert(gas167_prepare(3, 0x5000, 0, 0, out, &error) == 2 && error == 3);
assert(gas167_prepare(3, 0x5000, 125, 0, out, &error) == 2 && error == 3);
assert(gas167_prepare(3, 0x5000, 1, 1, out, &error) == 2 && error == 3);
assert(gas167_prepare(3, 0xFFFF, 2, 0, out, &error) == 2 && error == 2);
assert(gas167_prepare(16, 0x5000, 1, 0, out, &error) == 2 && error == 1);
assert(gas167_prepare(6, 0x5080, 7, 0, out, &error) == 2 && error == 2);
assert(gas167_prepare(6, 0x5052, 60001, 0, out, &error) == 2 && error == 3);
assert(gas167_prepare(6, 0x5052, 60000, 0, out, &error) == 2 && !error);
assert(modbus[0x52] == 60000);
assert(gas167_prepare(3, 0x5000, 1, 0, 0, &error) == 0);
assert(gas167_prepare(6, 0x5000, 1, 0, out, 0) == 0 && modbus[0] == 0);
}
request(&rs, 3, 0x5000, 2);
assert(sent == 9 && rs.buffer[5] == 0xFE && rs.buffer[6] == 0xDC);
request(&rs, 3, 0x5000, 124);
assert(sent == 253 && rs.buffer[2] == 248);
request(&rs, 3, 0x5F00, 8);
assert(sent == 21 && rs.buffer[3] == 0x47 && rs.buffer[4] == 0x41);
assert(rs.buffer[5] == 0x53 && rs.buffer[6] == 0x31);
assert(rs.buffer[10] == 167 && rs.buffer[18] == 1);
request(&rs, 6, 0x5F00, 0);
assert(sent == 5 && rs.buffer[2] == 2);
request(&rs, 3, 0x5F07, 2);
assert(rs.buffer[2] == 2);
request(&rs, 6, 0x5052, 0);
assert(rs.buffer[2] == 3 && modbus[0x52] == 60000);
request(&rs, 6, 0x507D, 9);
assert(rs.buffer[2] == 2 && modbus[0x7D] == 0x7D);
request(&rs, 6, 0x507F, 0x80);
assert(sent == 8 && rs.buffer[2] == 0x50 && rs.buffer[3] == 0x7F);
assert(captures == 1 && modbus[127] == 0x80);
can_request(6, 0x5001, 0xFEDC);
assert(modbus[1] == 0xFEDC && ECanaMboxes.MBOX2.MDL.all == 0x5001FEDCUL);
assert(ECanaMboxes.MBOX2.MDH.all == 0x1F000100UL);
can_request(3, 0x5001, 2);
assert(ECanaMboxes.MBOX2.MDL.all == 0xFEDC0002UL);
can_request(3, 0x5000, 124);
assert(ump_can_length == 124 && ump_can_index == 2);
modbus[2] = 777; /* All fragments must retain the original snapshot. */
for (i = 1; i < 62; ++i) {
ECanaRegs.CANTA.all = 4;
UMP_CAN_service();
assert(ump_can_index == 2*(i+1));
if (i == 1) assert(ECanaMboxes.MBOX2.MDL.all == 0x00020003UL);
}
modbus[2] = 2;
can_request(3, 0x5000, 3);
assert(ECanaMboxes.MBOX2.MDH.all == 0x1F000103UL);
can_request(6, 0x507C, 9);
assert((ECanaMboxes.MBOX2.MDH.all & 255U) == 2 && modbus[0x7C] == 0x7C);
can_request(6, 0x507F, 0x40);
assert(captures == 2 && modbus[127] == 0x40);
can_request(6, 0x5F00, 9);
assert((ECanaMboxes.MBOX2.MDH.all & 255U) == 2);
request(&rs, 3, 0x8000, 0x8001);
#else
request(&rs, 3, 0x5000, 2);
assert(sent == 5 && rs.buffer[2] == 2);
request(&rs, 6, 0x5001, 9);
assert(rs.buffer[2] == 2 && modbus[1] == 0xFEDC);
request(&rs, 3, 0x5000, 124);
assert(sent == 5 && rs.buffer[2] == 2);
request(&rs, 3, 0x5F00, 8);
assert(rs.buffer[2] == 2);
can_request(6, 0x5001, 9);
assert((ECanaMboxes.MBOX2.MDH.all & 255U) == 2 && modbus[1] == 0xFEDC);
assert(captures == 0);
request(&rs, 3, 0x8000, 1);
#endif
assert(spi_reads == 1 && rs.buffer[3] == 0xBE && rs.buffer[4] == 0xEF);
assert(modbus[128] == 0xD00D);
printf("PASS GAS167 profile %d: production RTU/CAN routes, guards, CRC and legacy services\n", EEPROM_M95M02);
return 0;
}