Добавить протокол Altera Logic и общие клиенты прошивки
This commit is contained in:
@@ -1,11 +1,144 @@
|
||||
# PORTING
|
||||
# Портирование firmware-info
|
||||
|
||||
Аппаратно-зависимый контракт ограничен `firmware_info_config.h`:
|
||||
`FIRMWARE_VERSION_MAJOR`, `FIRMWARE_VERSION_MINOR`, `FIRMWARE_VERSION_PATCH` и
|
||||
необязательный `FIRMWARE_BUILD_ID`. Выберите порт по семейству МК или создайте
|
||||
его копию. Публикация — ответственность транспорта: 12 `uint16_t` для Modbus
|
||||
либо 24 little-endian байта для SETGUI.
|
||||
## Назначение и границы
|
||||
|
||||
При переносе проверьте: поддержку `__DATE__`/`__TIME__`, наличие generated в
|
||||
include path, запуск генератора до компиляции и декодирование build ID как двух
|
||||
ASCII-байтов в каждом логическом слове.
|
||||
Это C-библиотека описания работающего образа: SemVer, дата/время компиляции,
|
||||
8 символов build ID и сериализация контракта v1. Она не обращается к сети,
|
||||
не читает версию из сервера и не записывает Flash. HAL, RTOS, UART и CAN
|
||||
ядру не нужны. Отправку результата выполняет приложение.
|
||||
|
||||
Для размещения `.hex/.bin` в каталоге SETGUI уже существуют
|
||||
[`setprotocol.firmware_publish`](../../python/setprotocol/firmware_publish.py)
|
||||
и [`tools/firmware-publish`](../../tools/firmware-publish/README.md).
|
||||
Связь компонентов и перенос публикации описаны в
|
||||
[`tools/firmware-publish/PORTING.md`](../../tools/firmware-publish/PORTING.md).
|
||||
|
||||
## 1. Файлы и конфигурация
|
||||
|
||||
Пример ниже предполагает `lib/templates` внутри нового проекта:
|
||||
|
||||
```text
|
||||
project/
|
||||
inc/firmware_info_config.h
|
||||
generated/firmware_build_id.h # создаётся до сборки
|
||||
lib/templates/c/firmware-info/
|
||||
src/
|
||||
```
|
||||
|
||||
Добавьте в сборку оба файла:
|
||||
|
||||
```text
|
||||
lib/templates/c/firmware-info/src/firmware_info.c
|
||||
lib/templates/c/firmware-info/src/firmware_info_port.c
|
||||
```
|
||||
|
||||
В include path добавьте `inc`, `generated` и
|
||||
`lib/templates/c/firmware-info/include`. Встроенный `CMakeLists.txt` собирает
|
||||
только ядро и host-тест; `firmware_info_port.c` и каталоги конфигурации нужно
|
||||
добавлять к целевому firmware target самостоятельно.
|
||||
|
||||
Скопируйте `ports/<mcu>/firmware_info_config.template.h` как
|
||||
`inc/firmware_info_config.h`. Есть варианты STM32F1/F4/G4 и К1921ВК028;
|
||||
они не содержат регистров МК. Для другого МК с обычными 8-битными байтами
|
||||
достаточно такого же config:
|
||||
|
||||
```c
|
||||
#ifndef FIRMWARE_INFO_CONFIG_H
|
||||
#define FIRMWARE_INFO_CONFIG_H
|
||||
#define FIRMWARE_VERSION_MAJOR 1U
|
||||
#define FIRMWARE_VERSION_MINOR 2U
|
||||
#define FIRMWARE_VERSION_PATCH 3U
|
||||
#include "firmware_build_id.h"
|
||||
#endif
|
||||
```
|
||||
|
||||
Обязательный include в этом примере позволяет обнаружить пропущенный pre-build.
|
||||
В готовых шаблонах include опциональный через `__has_include`; если заголовок
|
||||
не подключён, порт использует `LOCALDEV`. Для компилятора без `__has_include`
|
||||
используйте явный include. Можно связать три версии с существующими макросами
|
||||
проекта, как это сделано в `KONOR_ds18b20/inc/firmware_info_config.h`.
|
||||
|
||||
## 2. Build ID
|
||||
|
||||
Из корня нового проекта перед компиляцией запустите:
|
||||
|
||||
```powershell
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File "lib/templates/c/firmware-info/tools/make_build_id.ps1" -Repository "." -Output "generated/firmware_build_id.h"
|
||||
```
|
||||
|
||||
Указывайте `-Repository` и `-Output` явно: расположение сабмодуля и рабочий
|
||||
каталог IDE различаются между проектами. `-Repository` должен указывать на
|
||||
исходники прошивки, а не на репозиторий `templates`. Для Keil из каталога `mdk`
|
||||
соответственно используйте `..\lib\templates\...`, `-Repository ".."` и
|
||||
`-Output ".\Generated\firmware_build_id.h"`.
|
||||
|
||||
При доступном Git чистый checkout получает 8 знаков commit, изменения tracked
|
||||
файлов — 7 знаков и `+`. Если commit определить нельзя, используется `NOGIT000`.
|
||||
Новые untracked-файлы текущий генератор при определении dirty не учитывает.
|
||||
Дата/время берутся из `__DATE__`/`__TIME__` при компиляции `firmware_info_port.c`;
|
||||
для выпуска выполняйте полный Rebuild, чтобы не оставить старый объектный файл.
|
||||
|
||||
## 3. Обработчик запроса версии
|
||||
|
||||
Пример функции подготовки полезной нагрузки, вызываемой вашим обработчиком:
|
||||
|
||||
```c
|
||||
#include "firmware_info_port.h"
|
||||
|
||||
firmware_info_status_t app_make_firmware_info(uint8_t *payload, size_t capacity)
|
||||
{
|
||||
firmware_info_t info;
|
||||
firmware_info_status_t status = firmware_info_port_describe(&info);
|
||||
if (status != FIRMWARE_INFO_OK) return status;
|
||||
return firmware_info_to_le_bytes(&info, payload, capacity);
|
||||
}
|
||||
```
|
||||
|
||||
При успехе передайте ровно `FIRMWARE_INFO_PAYLOAD_SIZE` (24) байта в собственный
|
||||
транспорт. При ошибке верните ошибку протокола, а не содержимое буфера.
|
||||
Не отправляйте `sizeof(firmware_info_t)`: структура не является wire format.
|
||||
|
||||
В KONOR обработчик `app_send_firmware_info()` отвечает на
|
||||
`PROTO_MSG_FIRMWARE_INFO = 0x03`, сохраняя sequence запроса. Старый 32-байтовый
|
||||
`DEVICE_INFO` остаётся отдельным сообщением. Для нового протокола сначала
|
||||
согласуйте команду с клиентом: подключение библиотеки само по себе не добавит
|
||||
декодер и отображение версии в GUI.
|
||||
|
||||
Для Modbus вызовите `firmware_info_to_registers()` с массивом из 12 `uint16_t`
|
||||
и разместите его в выбранной карте регистров. Адреса и Modbus-порядок байтов
|
||||
обеспечивает ваш Modbus-стек; LE-буфер в Modbus напрямую не копируйте.
|
||||
|
||||
## 4. Контракт v1
|
||||
|
||||
| Индекс слова | Содержимое |
|
||||
|---|---|
|
||||
| 0 | Версия контракта: 1 |
|
||||
| 1, 2, 3 | major, minor, patch |
|
||||
| 4 | Год |
|
||||
| 5 | `(month << 8) \| day` |
|
||||
| 6 | `(hour << 8) \| minute` |
|
||||
| 7 | Секунды |
|
||||
| 8–11 | По два ASCII-символа build ID: первый в старшем байте слова |
|
||||
|
||||
В LE-представлении младший байт каждого слова идёт первым. Например, build ID
|
||||
`ab` в начале строки даёт слово `0x6162`, но байты `62 61`; декодируйте сначала
|
||||
слово, затем символы из старшего/младшего байта. NUL-терминатор не передаётся.
|
||||
|
||||
Текущая проверка допускает major/minor до 255 и patch до 999. Если в каталоге
|
||||
используется `(major << 16) | (minor << 8) | patch`, ограничьте **все три** части
|
||||
до 255, иначе значения пересекутся. Версия каталога автоматически из C-config
|
||||
не извлекается.
|
||||
|
||||
Для C2000 с 16-битным `char` нельзя считать готовым байтовый порт: отдельно
|
||||
проверьте наличие `uint8_t` и представление октетов в транспорте. Публикация
|
||||
TMS SCI8-файла с ПК поддерживается независимо от переноса этой C-библиотеки.
|
||||
|
||||
## 5. Проверка переноса
|
||||
|
||||
1. Соберите host-тест из `tests/test_firmware_info.c` вместе с ядром либо
|
||||
используйте CMake/CTest. Он проверяет дату, регистры и порядок байтов build ID.
|
||||
2. Соберите целевую прошивку с обоими `.c` и сгенерированным заголовком.
|
||||
3. Запросите версию у устройства: сравните SemVer, build ID, время и 24-байтовый
|
||||
ответ с конкретной сборкой. Проверьте, что прежний `DEVICE_INFO` не изменился.
|
||||
4. Подключите выпуск по инструкции публикатора; сравните версию в config и
|
||||
`firmware-release.cmd` перед Rebuild и `--preflight`.
|
||||
|
||||
@@ -25,3 +25,7 @@ config, а не дублирования ядра.
|
||||
|
||||
KONOR публикует эти 24 байта ответом `FIRMWARE_INFO (0x03)`, сохраняя старый
|
||||
32-байтовый `DEVICE_INFO` без изменений.
|
||||
|
||||
Подробная инструкция с кодом обработчика, форматом ответа и проверкой переноса:
|
||||
[`PORTING.md`](PORTING.md). Для размещения файла прошивки в каталоге SETGUI
|
||||
используется отдельный [общий публикатор](../../tools/firmware-publish/PORTING.md).
|
||||
|
||||
93
c/set-protocol/ALTERA_LOGIC.md
Normal file
93
c/set-protocol/ALTERA_LOGIC.md
Normal file
@@ -0,0 +1,93 @@
|
||||
# Altera Logic: общее ядро и порты
|
||||
|
||||
Потоковый SETCAN/GAS-профиль и общие порты описаны отдельно:
|
||||
[Altera Logic ONLINE](../../doc/setcan/ALTERA_LOGIC_STREAM.md).
|
||||
Ниже — совместимый прежний буферный протокол UART `A5 CMD`.
|
||||
|
||||
Клиент UART-протокола FPGA-анализатора Cyclone IV: 16 каналов, 4096 выборок.
|
||||
Работает с существующим RTL `altera_loganalisator`, версия протокола 1.
|
||||
Ядро C99 не зависит от Qt, ОС, системных часов и динамической памяти.
|
||||
|
||||
Слои: SETGUI → Python ctypes → C99 → Python Qt-порт → COM / USB–UART.
|
||||
GUI передаёт числовые настройки и получает готовый массив; формат пакетов,
|
||||
XOR, endian, последовательность команд, парсер и состояния тайм-аутов находятся в C.
|
||||
|
||||
| Файл | Назначение и зависимости |
|
||||
|---|---|
|
||||
| `include/altera_logic.h` | Публичный ABI, макрос экспорта из `pcan_abi.h` |
|
||||
| `src/altera_logic.c` | Кодек и автомат обмена; только стандартная библиотека C |
|
||||
| `tests/test_altera_logic.c` | Эталонные пакеты RTL, полный захват, ошибки |
|
||||
| `../../python/altera_logic/native.py` | ctypes-модели, загрузка DLL, экспорт CSV; stdlib |
|
||||
| `../../python/altera_logic/qt_port.py` | Асинхронный COM-порт; PySide6 QtSerialPort |
|
||||
|
||||
## Сборка
|
||||
|
||||
Из корня `templates`:
|
||||
|
||||
```powershell
|
||||
python c/set-protocol/tools/build_host.py --output python/altera_logic/native/setprotocol.dll
|
||||
```
|
||||
|
||||
На Linux выходной файл — `python/altera_logic/native/libsetprotocol.so`.
|
||||
Также поддерживается основная CMake-сборка `c/set-protocol`; путь к результату
|
||||
задаётся через `ALTERA_LOGIC_LIBRARY`. Библиотека без символов `la_*`
|
||||
не поддерживается: тихого Python-fallback нет.
|
||||
|
||||
## Контракт порта
|
||||
|
||||
Выделить выровненную память размером `la_context_size()`; все обращения к
|
||||
одному контексту выполняются в одном потоке.
|
||||
|
||||
- `la_init(ctx)` — начать с INFO.
|
||||
- `la_next(ctx, out, capacity)` — получить следующий запрос, либо 0, если отправлять нечего.
|
||||
- `la_feed(ctx, bytes, size)` — передать принятые байты, включая частичные ответы.
|
||||
- `la_tick(ctx, elapsed_ms)` — сообщить прошедшее время; тайм-аут ответа 1000 мс.
|
||||
- `la_start(ctx, divider, mask, value, edge_mask, edge_value)` — настройка и новый захват.
|
||||
- `la_get(ctx, field)` — состояние и прогресс; `la_samples` — завершённая запись.
|
||||
- `la_fail(ctx, LA_IO_ERROR)` — сообщить ошибку транспорта.
|
||||
|
||||
COM: 921600 бод, 8N1, без управления потоком. Одновременно ожидается только
|
||||
один ответ. После ARM STATUS опрашивается каждые 25 мс; ожидание самого
|
||||
триггера не ограничено. После done читаются 64 блока по 64 выборки.
|
||||
Ошибки закрывают сеанс без автоматического повтора ARM. Неверный размер,
|
||||
код команды или XOR прекращает сеанс. При рассинхронизации запроса на FPGA
|
||||
может потребоваться аппаратный RESET_N; переподключение не гарантирует её сброс.
|
||||
|
||||
Клиент намеренно принимает только профиль 16 × 4096 версии 1.
|
||||
Поддержка внешней SDRAM и расширенных адресов сюда пока не входит.
|
||||
Прерывание в GUI закрывает порт ПК: STOP в протоколе FPGA отсутствует.
|
||||
|
||||
## Быстрый старт без платы
|
||||
|
||||
Добавить `templates/python` в PYTHONPATH:
|
||||
|
||||
```python
|
||||
from altera_logic import NativeAnalyzer
|
||||
|
||||
client = NativeAnalyzer()
|
||||
client.reset(demo=True)
|
||||
client.start(divider=49)
|
||||
capture = client.capture()
|
||||
print(len(capture.samples)) # 4096
|
||||
print(capture.sample_rate) # 1_000_000
|
||||
print(capture.trigger_index) # 2048
|
||||
capture.save_csv("demo.csv")
|
||||
```
|
||||
|
||||
Демо создаёт синтетическую запись в C; не моделирует заданные условия триггера.
|
||||
CSV содержит явный признак demo, время относительно триггера и состояния D0…D15.
|
||||
|
||||
## Потребители и проверка
|
||||
|
||||
Используется вкладкой SETGUI «Altera Logic». Для Android/JNI новый потребитель
|
||||
может использовать тот же ABI; интерфейс Android в этой задаче не изменяется.
|
||||
Существующий ABI SETProtocol не изменён, добавлены только символы `la_*`.
|
||||
|
||||
```powershell
|
||||
$env:PYTHONPATH = "$PWD/python"
|
||||
python -m unittest discover -s python/tests -p test_altera_logic.py
|
||||
```
|
||||
|
||||
CMake включает `test_altera_logic` и CTest `altera_logic`.
|
||||
На плате требуется отдельно проверить разводку пинов, RAM-модуль и прошивку;
|
||||
программные проверки не заменяют аппаратную проверку.
|
||||
@@ -6,6 +6,8 @@ set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||
|
||||
# SET protocol v2: управление, телеметрия, CAN segmentation и firmware flow.
|
||||
set(SETPROTOCOL_V2_SOURCES
|
||||
src/altera_logic.c
|
||||
src/altera_stream.c
|
||||
src/set_protocol.c
|
||||
src/set_can.c
|
||||
src/set_firmware.c
|
||||
@@ -79,6 +81,12 @@ endif()
|
||||
option(SETP_BUILD_TESTS "Build host tests" ON)
|
||||
if(SETP_BUILD_TESTS)
|
||||
enable_testing()
|
||||
add_executable(test_altera_logic tests/test_altera_logic.c)
|
||||
target_link_libraries(test_altera_logic PRIVATE setprotocol_static)
|
||||
add_test(NAME altera_logic COMMAND test_altera_logic)
|
||||
add_executable(test_altera_stream tests/test_altera_stream.c)
|
||||
target_link_libraries(test_altera_stream PRIVATE setprotocol_static)
|
||||
add_test(NAME altera_stream COMMAND test_altera_stream)
|
||||
|
||||
add_executable(test_plot tests/test_plot.c)
|
||||
target_link_libraries(test_plot PRIVATE setprotocol_static)
|
||||
|
||||
35
c/set-protocol/include/altera_logic.h
Normal file
35
c/set-protocol/include/altera_logic.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/** Altera logic analyzer UART client. C99, caller-owned memory, no OS/heap.
|
||||
* One request at a time; no automatic retries (ARM is not idempotent).
|
||||
* Initialize aligned storage of la_context_size() bytes, then call la_next,
|
||||
* la_feed and la_tick from one thread. tick receives elapsed milliseconds.
|
||||
*/
|
||||
#ifndef ALTERA_LOGIC_H
|
||||
#define ALTERA_LOGIC_H
|
||||
#include "pcan_abi.h"
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
enum { LA_CONNECTING, LA_READY, LA_CAPTURING, LA_DONE, LA_ERROR };
|
||||
enum { LA_OK, LA_BAD_FRAME, LA_CHECKSUM, LA_DEVICE_ERROR, LA_TIMEOUT,
|
||||
LA_UNSUPPORTED, LA_ARGUMENT, LA_IO_ERROR };
|
||||
enum { LA_STATE, LA_ERROR_CODE, LA_CHANNELS, LA_DEPTH, LA_CLOCK_HZ,
|
||||
LA_FLAGS, LA_TRIGGER_INDEX, LA_READ_COUNT, LA_DIVIDER };
|
||||
PCAN_ABI_API size_t la_context_size(void);
|
||||
PCAN_ABI_API void la_init(void *ctx);
|
||||
/** Return 0 on success. Configuration is immutable throughout this capture. */
|
||||
PCAN_ABI_API int la_start(void *ctx, uint32_t divider, uint32_t mask,
|
||||
uint32_t value, uint32_t edge_mask, uint32_t edge_value);
|
||||
/** Returns 6 when a request is ready, otherwise 0. Capacity must be >=6. */
|
||||
PCAN_ABI_API size_t la_next(void *ctx, uint8_t *out, size_t capacity);
|
||||
PCAN_ABI_API void la_feed(void *ctx, const uint8_t *data, size_t size);
|
||||
PCAN_ABI_API void la_tick(void *ctx, uint32_t elapsed_ms);
|
||||
PCAN_ABI_API void la_fail(void *ctx, uint32_t code);
|
||||
PCAN_ABI_API uint32_t la_get(const void *ctx, uint32_t field);
|
||||
PCAN_ABI_API size_t la_samples(const void *ctx, uint16_t *out, size_t capacity);
|
||||
/** Offline fixture, never communicates with hardware or claims a real trigger. */
|
||||
PCAN_ABI_API void la_demo_init(void *ctx);
|
||||
PCAN_ABI_API int la_demo_capture(void *ctx, uint32_t divider);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
46
c/set-protocol/include/altera_stream.h
Normal file
46
c/set-protocol/include/altera_stream.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/** SETCAN/GAS Altera Logic online stream, shared by CAN and UART.
|
||||
* Caller-owned context; one serialized caller. No heap, OS or Qt dependencies.
|
||||
*/
|
||||
#ifndef ALTERA_STREAM_H
|
||||
#define ALTERA_STREAM_H
|
||||
#include "pcan_abi.h"
|
||||
#include "pcan_frame.h"
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#define LAS_DEVICE_TYPE 6U
|
||||
#define LAS_DEVICE_ID 14U
|
||||
#define LAS_GAS_META 0xFE00U
|
||||
#define LAS_GAS_DATA 0xFF00U
|
||||
#define LAS_CAPACITY 8192U
|
||||
#define LAS_TAG 0x4C01U
|
||||
enum { LAS_COUNT, LAS_PERIOD_NS, LAS_SESSION, LAS_RECEIVED, LAS_MISSING,
|
||||
LAS_DUPLICATES, LAS_INVALID, LAS_IGNORED, LAS_CRC_ERRORS, LAS_HAS_META };
|
||||
PCAN_ABI_API size_t las_context_size(void);
|
||||
PCAN_ABI_API int las_init(void *ctx, uint32_t device_id);
|
||||
PCAN_ABI_API void las_can(void *ctx, uint32_t id, const uint8_t *data, size_t size,
|
||||
uint32_t extended, uint32_t remote);
|
||||
PCAN_ABI_API void las_uart(void *ctx, const uint8_t *data, size_t size);
|
||||
PCAN_ABI_API uint32_t las_get(const void *ctx, uint32_t field);
|
||||
/** Chronological bounded history. breaks[i]=1 marks a discontinuity before i. */
|
||||
PCAN_ABI_API size_t las_snapshot(const void *ctx, uint64_t *indices, uint16_t *samples,
|
||||
uint8_t *breaks, size_t capacity);
|
||||
/** Sparse step trace for rendering; capacity >= 2*LAS_COUNT, move=pen-up. */
|
||||
PCAN_ABI_API size_t las_trace(const void *ctx, uint32_t channel, uint64_t *indices,
|
||||
uint8_t *levels, uint8_t *moves, size_t capacity);
|
||||
PCAN_ABI_API uint32_t las_device_type(void);
|
||||
PCAN_ABI_API uint32_t las_device_id(void);
|
||||
PCAN_ABI_API const char *las_device_name(void);
|
||||
PCAN_ABI_API void las_demo_step(void *ctx, uint32_t count);
|
||||
/** Device-side packet builders; return zero for invalid arguments.
|
||||
* uart=0: 8-byte CAN data + *id; uart=1: complete AA55 transport + *id.
|
||||
* Same builders are used by firmware ports, tests and the demo producer.
|
||||
*/
|
||||
PCAN_ABI_API size_t las_metadata(uint32_t device, uint32_t session, uint32_t period_ns,
|
||||
uint32_t uart, uint32_t *id, uint8_t *out, size_t capacity);
|
||||
PCAN_ABI_API size_t las_data(uint32_t device, uint32_t session, uint32_t index,
|
||||
uint32_t sample, uint32_t uart, uint32_t *id, uint8_t *out, size_t capacity);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
142
c/set-protocol/src/altera_logic.c
Normal file
142
c/set-protocol/src/altera_logic.c
Normal file
@@ -0,0 +1,142 @@
|
||||
#include "altera_logic.h"
|
||||
#include <string.h>
|
||||
typedef struct {
|
||||
uint32_t state, error, clock_hz, elapsed, delay;
|
||||
uint16_t config[5], channels, depth, trigger, count, offset;
|
||||
uint16_t samples[4096];
|
||||
uint8_t command, pending, flags, requested, rx[133];
|
||||
size_t used;
|
||||
} la_context;
|
||||
static uint8_t checksum(const uint8_t *p, size_t n) {
|
||||
uint8_t x = 0; size_t i;
|
||||
for (i = 0; i < n; ++i) x ^= p[i];
|
||||
return x;
|
||||
}
|
||||
static uint16_t u16(const uint8_t *p) {
|
||||
return (uint16_t)((uint16_t)p[0] | ((uint16_t)p[1] << 8));
|
||||
}
|
||||
size_t la_context_size(void) { return sizeof(la_context); }
|
||||
void la_init(void *ctx) {
|
||||
la_context *s = (la_context *)ctx;
|
||||
memset(s, 0, sizeof(*s)); s->command = 1; s->state = LA_CONNECTING;
|
||||
}
|
||||
void la_fail(void *ctx, uint32_t code) {
|
||||
la_context *s = (la_context *)ctx;
|
||||
s->state = LA_ERROR; s->error = code; s->pending = 0; s->command = 0;
|
||||
}
|
||||
int la_start(void *ctx, uint32_t div, uint32_t mask, uint32_t val,
|
||||
uint32_t emask, uint32_t eval) {
|
||||
la_context *s = (la_context *)ctx;
|
||||
if ((s->state != LA_READY && s->state != LA_DONE) ||
|
||||
div > 65535 || mask > 65535 || val > 65535 ||
|
||||
emask > 65535 || eval > 65535 || ((val ^ eval) & mask & emask))
|
||||
return LA_ARGUMENT;
|
||||
s->config[0]=(uint16_t)div; s->config[1]=(uint16_t)mask;
|
||||
s->config[2]=(uint16_t)val; s->config[3]=(uint16_t)emask;
|
||||
s->config[4]=(uint16_t)eval;
|
||||
s->state=LA_CAPTURING; s->error=0; s->flags=0; s->count=0;
|
||||
s->offset=0; s->command=2; s->delay=0;
|
||||
return LA_OK;
|
||||
}
|
||||
size_t la_next(void *ctx, uint8_t *out, size_t capacity) {
|
||||
la_context *s = (la_context *)ctx; uint16_t arg = 0;
|
||||
if (!out || capacity < 6 || !s->command || s->pending || s->delay) return 0;
|
||||
if (s->command >= 2 && s->command <= 6) arg=s->config[s->command-2];
|
||||
if (s->command == 9) {
|
||||
arg=s->offset;
|
||||
s->requested=(uint8_t)((s->depth-s->offset > 64) ? 64 : s->depth-s->offset);
|
||||
}
|
||||
out[0]=0xa5; out[1]=s->command; out[2]=(uint8_t)arg;
|
||||
out[3]=(uint8_t)(arg >> 8); out[4]=(s->command == 9) ? s->requested : 0;
|
||||
out[5]=checksum(out,5);
|
||||
s->pending=1; s->elapsed=0; s->used=0;
|
||||
return 6;
|
||||
}
|
||||
static void complete(la_context *s) {
|
||||
size_t i;
|
||||
if (checksum(s->rx,s->used)) { la_fail(s,LA_CHECKSUM); return; }
|
||||
if (s->rx[2]) { la_fail(s,LA_DEVICE_ERROR); return; }
|
||||
s->pending=0;
|
||||
if (s->command == 1) {
|
||||
s->channels=s->rx[3]; s->depth=u16(s->rx+4);
|
||||
s->clock_hz=(uint32_t)s->rx[6]*1000000U;
|
||||
if (s->channels != 16 || s->depth != 4096 || !s->clock_hz || s->rx[7]!=1) {
|
||||
la_fail(s,LA_UNSUPPORTED); return;
|
||||
}
|
||||
s->state=LA_READY; s->command=0;
|
||||
} else if (s->command < 7) ++s->command;
|
||||
else if (s->command == 7) { s->command=8; s->delay=25; }
|
||||
else if (s->command == 8) {
|
||||
s->flags=s->rx[3]; s->trigger=u16(s->rx+4);
|
||||
if (s->flags & 4) {
|
||||
if (s->trigger >= s->depth || (s->flags & 1)) { la_fail(s,LA_BAD_FRAME); return; }
|
||||
s->command=9;
|
||||
} else s->delay=25;
|
||||
} else {
|
||||
for(i=0; i<s->requested; ++i) s->samples[s->offset+i]=u16(s->rx+4+i*2);
|
||||
s->offset=(uint16_t)(s->offset+s->requested); s->count=s->offset;
|
||||
if(s->offset == s->depth) { s->state=LA_DONE; s->command=0; }
|
||||
}
|
||||
}
|
||||
void la_feed(void *ctx, const uint8_t *data, size_t size) {
|
||||
la_context *s = (la_context *)ctx; size_t i, expected;
|
||||
if (!data) return;
|
||||
for(i=0; i<size; ++i) {
|
||||
if(!s->pending || s->used >= sizeof(s->rx)) { la_fail(s,LA_BAD_FRAME); return; }
|
||||
s->rx[s->used++]=data[i];
|
||||
if(s->rx[0]!=0x5a || (s->used>=2 && s->rx[1]!=(s->command|0x80))) {
|
||||
la_fail(s,LA_BAD_FRAME); return;
|
||||
}
|
||||
if(s->used<3) continue;
|
||||
expected=4;
|
||||
if(!s->rx[2]) {
|
||||
if(s->command==1) expected=9;
|
||||
else if(s->command==8) expected=7;
|
||||
else if(s->command==9) {
|
||||
if(s->used<4) continue;
|
||||
if(s->rx[3]!=s->requested) { la_fail(s,LA_BAD_FRAME); return; }
|
||||
expected=5U+2U*s->requested;
|
||||
}
|
||||
}
|
||||
if(s->used==expected) complete(s);
|
||||
}
|
||||
}
|
||||
void la_tick(void *ctx, uint32_t ms) {
|
||||
la_context *s=(la_context *)ctx;
|
||||
if(s->pending) {
|
||||
if(ms >= 1000U-s->elapsed) la_fail(s,LA_TIMEOUT);
|
||||
else s->elapsed+=ms;
|
||||
}
|
||||
s->delay=(ms >= s->delay) ? 0 : s->delay-ms;
|
||||
}
|
||||
uint32_t la_get(const void *ctx, uint32_t field) {
|
||||
const la_context *s=(const la_context *)ctx;
|
||||
switch(field) {
|
||||
case LA_STATE:return s->state; case LA_ERROR_CODE:return s->error;
|
||||
case LA_CHANNELS:return s->channels; case LA_DEPTH:return s->depth;
|
||||
case LA_CLOCK_HZ:return s->clock_hz; case LA_FLAGS:return s->flags;
|
||||
case LA_TRIGGER_INDEX:return s->trigger; case LA_READ_COUNT:return s->count;
|
||||
case LA_DIVIDER:return s->config[0]; default:return 0;
|
||||
}
|
||||
}
|
||||
size_t la_samples(const void *ctx, uint16_t *out, size_t capacity) {
|
||||
const la_context *s=(const la_context *)ctx;
|
||||
if(s->state!=LA_DONE || !out || capacity<s->count) return 0;
|
||||
memcpy(out,s->samples,s->count*sizeof(uint16_t)); return s->count;
|
||||
}
|
||||
void la_demo_init(void *ctx) {
|
||||
la_context *s=(la_context *)ctx; la_init(s);
|
||||
s->channels=16; s->depth=4096; s->clock_hz=50000000;
|
||||
s->state=LA_READY; s->command=0;
|
||||
}
|
||||
int la_demo_capture(void *ctx, uint32_t divider) {
|
||||
la_context *s=(la_context *)ctx; size_t i, bit;
|
||||
int rc=la_start(s,divider,0,0,0,0); if(rc) return rc;
|
||||
for(i=0;i<4096;++i) {
|
||||
uint16_t v=0;
|
||||
for(bit=0;bit<16;++bit) if(((i/(4U+bit*7U))&1U)!=0) v|=(uint16_t)(1U<<bit);
|
||||
s->samples[i]=v;
|
||||
}
|
||||
s->trigger=2048; s->flags=6; s->count=4096; s->command=0; s->state=LA_DONE;
|
||||
return LA_OK;
|
||||
}
|
||||
158
c/set-protocol/src/altera_stream.c
Normal file
158
c/set-protocol/src/altera_stream.c
Normal file
@@ -0,0 +1,158 @@
|
||||
#include "altera_stream.h"
|
||||
#include "pcan_id.h"
|
||||
#include <string.h>
|
||||
|
||||
typedef struct {
|
||||
pcan_parser_t parser;
|
||||
uint64_t indices[LAS_CAPACITY], next_index;
|
||||
uint16_t samples[LAS_CAPACITY];
|
||||
uint8_t breaks[LAS_CAPACITY];
|
||||
uint32_t head, count, period, session, received, missing, duplicates, invalid, ignored;
|
||||
uint8_t device, metadata, started;
|
||||
} las_context;
|
||||
static uint16_t r16(const uint8_t *p) { return (uint16_t)(p[0]|((uint16_t)p[1]<<8)); }
|
||||
static uint32_t r32(const uint8_t *p) { return (uint32_t)r16(p)|((uint32_t)r16(p+2)<<16); }
|
||||
static void w16(uint8_t *p,uint16_t v) { p[0]=(uint8_t)v;p[1]=(uint8_t)(v>>8); }
|
||||
static void w32(uint8_t *p,uint32_t v) { w16(p,(uint16_t)v);w16(p+2,(uint16_t)(v>>16)); }
|
||||
uint32_t las_device_type(void) { return LAS_DEVICE_TYPE; }
|
||||
uint32_t las_device_id(void) { return LAS_DEVICE_ID; }
|
||||
const char *las_device_name(void) { return "Altera Logic"; }
|
||||
size_t las_context_size(void) { return sizeof(las_context); }
|
||||
int las_init(void *ctx,uint32_t device) {
|
||||
las_context *s=(las_context *)ctx;
|
||||
if(!ctx || device>15) return 0;
|
||||
memset(s,0,sizeof(*s));s->device=(uint8_t)device;pcan_parser_init(&s->parser);return 1;
|
||||
}
|
||||
static void append(las_context *s,uint64_t index,uint16_t value,uint8_t gap) {
|
||||
s->indices[s->head]=index;s->samples[s->head]=value;s->breaks[s->head]=gap;
|
||||
s->head=(s->head+1U)%LAS_CAPACITY;
|
||||
if(s->count<LAS_CAPACITY) ++s->count;
|
||||
}
|
||||
void las_can(void *ctx,uint32_t raw,const uint8_t *p,size_t n,uint32_t extended,uint32_t remote) {
|
||||
las_context *s=(las_context *)ctx;pcan_id_t id;uint32_t index,delta,period,session;
|
||||
if(!extended || remote || raw>PCAN_ID_MASK) { ++s->ignored;return; }
|
||||
pcan_id_unpack(raw,&id);
|
||||
if(id.device_type!=LAS_DEVICE_TYPE || id.device_id!=s->device ||
|
||||
id.route!=PCAN_ROUTE_FROM_DEVICE || id.msg_type!=PCAN_MSG_GAS ||
|
||||
(id.msg_body!=LAS_GAS_META && id.msg_body!=LAS_GAS_DATA)) { ++s->ignored;return; }
|
||||
if(n!=8 || !p) { ++s->invalid;return; }
|
||||
if(id.msg_body==LAS_GAS_META) {
|
||||
period=r32(p+4);session=r16(p+2);
|
||||
if(r16(p)!=LAS_TAG || period==0) { ++s->invalid;return; }
|
||||
if(s->metadata && session==s->session) {
|
||||
if(period!=s->period) ++s->invalid;
|
||||
return;
|
||||
}
|
||||
if(s->metadata && (uint16_t)(session-s->session)>=0x8000U) {
|
||||
++s->duplicates;return;
|
||||
}
|
||||
s->metadata=1;s->session=session;s->period=period;
|
||||
s->count=0;s->head=0;s->started=0;s->next_index=0;
|
||||
return;
|
||||
}
|
||||
if(!s->metadata) { ++s->ignored;return; }
|
||||
if(r16(p)!=s->session) { ++s->ignored;return; }
|
||||
index=r32(p+4);
|
||||
delta=0;
|
||||
if(s->started) {
|
||||
delta=index-(uint32_t)s->next_index;
|
||||
if(delta>=0x80000000U) { ++s->duplicates;return; }
|
||||
s->missing+=delta;
|
||||
} else s->next_index=index;
|
||||
s->next_index+=delta;
|
||||
append(s,s->next_index,r16(p+2),(uint8_t)(!s->started || delta!=0));
|
||||
s->next_index+=1;s->started=1;++s->received;
|
||||
}
|
||||
static void frame_callback(const pcan_frame_t *frame,void *user) {
|
||||
if(frame->flags & (PCAN_FLAG_DIR|PCAN_FLAG_ERR)) {
|
||||
++((las_context *)user)->ignored;return;
|
||||
}
|
||||
las_can(user,frame->id,frame->data,frame->dlc,
|
||||
frame->flags&PCAN_FLAG_IDE,frame->flags&PCAN_FLAG_RTR);
|
||||
}
|
||||
void las_uart(void *ctx,const uint8_t *data,size_t size) {
|
||||
las_context *s=(las_context *)ctx;
|
||||
if(data) pcan_parser_feed(&s->parser,data,size,frame_callback,s);
|
||||
}
|
||||
uint32_t las_get(const void *ctx,uint32_t field) {
|
||||
const las_context *s=(const las_context *)ctx;
|
||||
switch(field) {
|
||||
case LAS_COUNT:return s->count;case LAS_PERIOD_NS:return s->period;
|
||||
case LAS_SESSION:return s->session;case LAS_RECEIVED:return s->received;
|
||||
case LAS_MISSING:return s->missing;case LAS_DUPLICATES:return s->duplicates;
|
||||
case LAS_INVALID:return s->invalid;case LAS_IGNORED:return s->ignored;
|
||||
case LAS_CRC_ERRORS:return s->parser.stats.crc_errors;
|
||||
case LAS_HAS_META:return s->metadata;default:return 0;
|
||||
}
|
||||
}
|
||||
size_t las_snapshot(const void *ctx,uint64_t *indices,uint16_t *samples,uint8_t *breaks,size_t cap) {
|
||||
const las_context *s=(const las_context *)ctx;uint32_t i,pos;
|
||||
if(!indices || !samples || !breaks || cap<s->count) return 0;
|
||||
pos=(s->head+LAS_CAPACITY-s->count)%LAS_CAPACITY;
|
||||
for(i=0;i<s->count;++i) {
|
||||
indices[i]=s->indices[pos];samples[i]=s->samples[pos];breaks[i]=s->breaks[pos];
|
||||
pos=(pos+1U)%LAS_CAPACITY;
|
||||
}
|
||||
return s->count;
|
||||
}
|
||||
size_t las_trace(const void *ctx,uint32_t channel,uint64_t *indices,uint8_t *levels,
|
||||
uint8_t *moves,size_t cap) {
|
||||
const las_context *s=(const las_context *)ctx;uint32_t i,pos;
|
||||
uint64_t previous_x=0;uint8_t previous_y=0;size_t n=0;
|
||||
if(channel>=16 || !indices || !levels || !moves || cap<2U*s->count) return 0;
|
||||
pos=(s->head+LAS_CAPACITY-s->count)%LAS_CAPACITY;
|
||||
for(i=0;i<s->count;++i) {
|
||||
uint64_t x=s->indices[pos];uint8_t y=(uint8_t)((s->samples[pos]>>channel)&1U);
|
||||
if(!i) { indices[n]=x;levels[n]=y;moves[n++]=1; }
|
||||
else if(s->breaks[pos]) {
|
||||
indices[n]=previous_x;levels[n]=previous_y;moves[n++]=0;
|
||||
indices[n]=x;levels[n]=y;moves[n++]=1;
|
||||
} else if(y!=previous_y) {
|
||||
indices[n]=x;levels[n]=previous_y;moves[n++]=0;
|
||||
indices[n]=x;levels[n]=y;moves[n++]=0;
|
||||
}
|
||||
previous_x=x;previous_y=y;pos=(pos+1U)%LAS_CAPACITY;
|
||||
}
|
||||
if(s->count>1) { indices[n]=previous_x;levels[n]=previous_y;moves[n++]=0; }
|
||||
return n;
|
||||
}
|
||||
static size_t packet(uint32_t device,uint16_t body,const uint8_t *data,uint32_t uart,
|
||||
uint32_t *raw,uint8_t *out,size_t cap) {
|
||||
pcan_id_t id;pcan_frame_t frame;
|
||||
if(device>15 || !raw || !out || cap<(uart?PCAN_FRAME_MAX:8U)) return 0;
|
||||
memset(&id,0,sizeof(id));id.device_type=LAS_DEVICE_TYPE;id.device_id=(uint8_t)device;
|
||||
id.priority=PCAN_PRIORITY_STANDARD;id.route=PCAN_ROUTE_FROM_DEVICE;
|
||||
id.msg_type=PCAN_MSG_GAS;id.msg_body=body;
|
||||
memset(&frame,0,sizeof(frame));frame.id=pcan_id_pack(&id);
|
||||
frame.flags=PCAN_FLAG_IDE;frame.dlc=8;memcpy(frame.data,data,8);*raw=frame.id;
|
||||
if(uart) return pcan_frame_encode(&frame,out,cap);
|
||||
memcpy(out,data,8);return 8;
|
||||
}
|
||||
size_t las_metadata(uint32_t device,uint32_t session,uint32_t period,uint32_t uart,
|
||||
uint32_t *id,uint8_t *out,size_t cap) {
|
||||
uint8_t data[8];if(session>65535 || !period) return 0;
|
||||
w16(data,LAS_TAG);w16(data+2,(uint16_t)session);w32(data+4,period);
|
||||
return packet(device,LAS_GAS_META,data,uart,id,out,cap);
|
||||
}
|
||||
size_t las_data(uint32_t device,uint32_t session,uint32_t index,uint32_t sample,uint32_t uart,
|
||||
uint32_t *id,uint8_t *out,size_t cap) {
|
||||
uint8_t data[8];if(session>65535 || sample>65535) return 0;
|
||||
w16(data,(uint16_t)session);w16(data+2,(uint16_t)sample);w32(data+4,index);
|
||||
return packet(device,LAS_GAS_DATA,data,uart,id,out,cap);
|
||||
}
|
||||
void las_demo_step(void *ctx,uint32_t count) {
|
||||
las_context *s=(las_context *)ctx;uint32_t i,bit,idx,raw;uint8_t packet_data[PCAN_FRAME_MAX];size_t n;
|
||||
if(!s->metadata) {
|
||||
n=las_metadata(s->device,1,1000000,1,&raw,packet_data,sizeof(packet_data));
|
||||
las_uart(s,packet_data,n);
|
||||
}
|
||||
if(count>4096) count=4096;
|
||||
for(i=0;i<count;++i) {
|
||||
uint16_t a=0;idx=(uint32_t)s->next_index;
|
||||
for(bit=0;bit<16;++bit) {
|
||||
if((idx/(5U+bit*7U))&1U) a|=(uint16_t)(1U<<bit);
|
||||
}
|
||||
n=las_data(s->device,s->session,idx,a,1,&raw,packet_data,sizeof(packet_data));
|
||||
las_uart(s,packet_data,n);
|
||||
}
|
||||
}
|
||||
72
c/set-protocol/tests/test_altera_logic.c
Normal file
72
c/set-protocol/tests/test_altera_logic.c
Normal file
@@ -0,0 +1,72 @@
|
||||
#include "altera_logic.h"
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
static void response(void *s, uint8_t *data, size_t n) {
|
||||
size_t i; uint8_t sum=0;
|
||||
for(i=0;i<n-1;++i) sum^=data[i]; data[n-1]=sum;
|
||||
/* Serial reads can split at every byte. */
|
||||
for(i=0;i<n;++i) la_feed(s,data+i,1);
|
||||
}
|
||||
static void connect_ok(void *s) {
|
||||
uint8_t p[6], info[]={0x5a,0x81,0,16,0,16,50,1,0xe8};
|
||||
const uint8_t request[]={0xa5,1,0,0,0,0xa4};
|
||||
la_init(s); assert(la_next(s,p,5)==0);
|
||||
assert(la_next(s,p,6)==6 && !memcmp(p,request,6));
|
||||
assert(la_next(s,p,6)==0);
|
||||
response(s,info,sizeof(info)); assert(la_get(s,LA_STATE)==LA_READY);
|
||||
}
|
||||
int main(void) {
|
||||
void *s=calloc(1,la_context_size()); uint8_t p[6], ack[4], status[7], block[133];
|
||||
uint16_t samples[4096]; unsigned int cmd, offset, i;
|
||||
assert(s); connect_ok(s);
|
||||
assert(la_start(s,65536,0,0,0,0)==LA_ARGUMENT);
|
||||
assert(la_start(s,49,1,0,1,1)==LA_ARGUMENT);
|
||||
assert(la_start(s,49,0,0,1,1)==0);
|
||||
for(cmd=2;cmd<=7;++cmd) {
|
||||
assert(la_next(s,p,6)==6 && p[1]==cmd);
|
||||
if(cmd==2) { const uint8_t v[]={0xa5,2,49,0,0,0x96}; assert(!memcmp(p,v,6)); }
|
||||
ack[0]=0x5a; ack[1]=(uint8_t)(cmd|0x80); ack[2]=0; response(s,ack,4);
|
||||
}
|
||||
assert(la_next(s,p,6)==0); la_tick(s,25);
|
||||
assert(la_next(s,p,6)==6 && p[1]==8);
|
||||
{ uint8_t waiting[]={0x5a,0x88,0,1,0,8,0}; response(s,waiting,7); }
|
||||
la_tick(s,60000); /* Waiting for a physical trigger is not a UART timeout. */
|
||||
assert(la_get(s,LA_STATE)==LA_CAPTURING);
|
||||
assert(la_next(s,p,6)==6 && p[1]==8);
|
||||
status[0]=0x5a; status[1]=0x88; status[2]=0; status[3]=6;
|
||||
status[4]=0;status[5]=8;response(s,status,7);
|
||||
for(offset=0;offset<4096;offset+=64) {
|
||||
assert(la_next(s,p,6)==6 && p[1]==9 && p[4]==64);
|
||||
assert((unsigned int)(p[2]|(p[3]<<8))==offset);
|
||||
block[0]=0x5a;block[1]=0x89;block[2]=0;block[3]=64;
|
||||
for(i=0;i<64;++i) { block[4+2*i]=(uint8_t)(offset+i);block[5+2*i]=(uint8_t)((offset+i)>>8); }
|
||||
response(s,block,133);
|
||||
}
|
||||
assert(la_get(s,LA_STATE)==LA_DONE);
|
||||
assert(la_samples(s,samples,4095)==0);
|
||||
assert(la_samples(s,samples,4096)==4096);
|
||||
for(i=0;i<4096;++i) assert(samples[i]==i);
|
||||
assert(la_get(s,LA_TRIGGER_INDEX)==2048);
|
||||
/* Corrupt XOR, wrong command, device error, timeout and extra data. */
|
||||
la_init(s);la_next(s,p,6);
|
||||
{ uint8_t bad[]={0x5a,0x81,0,16,0,16,50,1,0};la_feed(s,bad,9); }
|
||||
assert(la_get(s,LA_ERROR_CODE)==LA_CHECKSUM);
|
||||
la_init(s);la_next(s,p,6);
|
||||
{ uint8_t bad[]={0x5a,0x88};la_feed(s,bad,2); }
|
||||
assert(la_get(s,LA_ERROR_CODE)==LA_BAD_FRAME);
|
||||
la_init(s);la_next(s,p,6);
|
||||
{ uint8_t bad[]={0x5a,0x81,1,0};response(s,bad,4); }
|
||||
assert(la_get(s,LA_ERROR_CODE)==LA_DEVICE_ERROR);
|
||||
la_init(s);la_next(s,p,6);la_tick(s,999);
|
||||
assert(la_get(s,LA_STATE)==LA_CONNECTING);la_tick(s,1);
|
||||
assert(la_get(s,LA_ERROR_CODE)==LA_TIMEOUT && la_next(s,p,6)==0);
|
||||
connect_ok(s);la_feed(s,p,1);assert(la_get(s,LA_ERROR_CODE)==LA_BAD_FRAME);
|
||||
la_init(s);la_next(s,p,6);
|
||||
{ uint8_t bad[]={0x5a,0x81,0,8,0,16,50,1,0};response(s,bad,9); }
|
||||
assert(la_get(s,LA_ERROR_CODE)==LA_UNSUPPORTED);
|
||||
la_demo_init(s);assert(la_demo_capture(s,49)==0);
|
||||
assert(la_get(s,LA_STATE)==LA_DONE && la_samples(s,samples,4096)==4096);
|
||||
free(s);puts("Altera Logic C tests passed");return 0;
|
||||
}
|
||||
62
c/set-protocol/tests/test_altera_stream.c
Normal file
62
c/set-protocol/tests/test_altera_stream.c
Normal file
@@ -0,0 +1,62 @@
|
||||
#include "altera_stream.h"
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
static void meta(void *s,uint32_t session,int uart) {
|
||||
uint8_t data[32];uint32_t id;size_t n,i;
|
||||
n=las_metadata(LAS_DEVICE_ID,session,1000000,(uint32_t)uart,&id,data,sizeof(data));assert(n);
|
||||
assert(id==0x1EE3FE00U);
|
||||
if(uart) for(i=0;i<n;++i) las_uart(s,data+i,1);
|
||||
else las_can(s,id,data,n,1,0);
|
||||
}
|
||||
static void sample(void *s,uint32_t session,uint32_t index,int uart) {
|
||||
uint8_t data[32];uint32_t id;size_t n;
|
||||
n=las_data(LAS_DEVICE_ID,session,index,index&65535U,(uint32_t)uart,&id,data,sizeof(data));assert(n);
|
||||
assert(id==0x1EE3FF00U);
|
||||
if(uart) las_uart(s,data,n);else las_can(s,id,data,n,1,0);
|
||||
}
|
||||
int main(void) {
|
||||
void *can=malloc(las_context_size()),*uart=malloc(las_context_size());
|
||||
uint64_t *indices=malloc(LAS_CAPACITY*sizeof(uint64_t));
|
||||
uint16_t *samples=malloc(LAS_CAPACITY*sizeof(uint16_t));
|
||||
uint8_t *breaks=malloc(LAS_CAPACITY);uint8_t packet[32];uint32_t id;size_t n,i;
|
||||
assert(can&&uart&&indices&&samples&&breaks);
|
||||
assert(las_init(can,14)&&las_init(uart,14));assert(!las_init(can,16));
|
||||
sample(can,1,0,0);assert(las_get(can,LAS_COUNT)==0);
|
||||
meta(can,1,0);meta(uart,1,1);
|
||||
/* Independent golden SETCAN bytes. */
|
||||
n=las_data(14,1,0x12345678U,0x8001,0,&id,packet,sizeof(packet));
|
||||
{ const uint8_t v[]={1,0,1,0x80,0x78,0x56,0x34,0x12};assert(n==8&&!memcmp(v,packet,8)); }
|
||||
for(i=0;i<9000;++i) { sample(can,1,(uint32_t)i,0);sample(uart,1,(uint32_t)i,1); }
|
||||
assert(las_get(can,LAS_COUNT)==8192&&las_get(uart,LAS_COUNT)==8192);
|
||||
assert(las_get(can,LAS_RECEIVED)==9000&&las_get(uart,LAS_RECEIVED)==9000);
|
||||
assert(las_snapshot(can,indices,samples,breaks,LAS_CAPACITY)==8192);
|
||||
assert(indices[0]==808&&indices[8191]==8999&&samples[8191]==8999);
|
||||
assert(las_snapshot(uart,indices,samples,breaks,LAS_CAPACITY)==8192);
|
||||
assert(indices[0]==808&&indices[8191]==8999&&samples[8191]==8999);
|
||||
sample(can,1,8999,1);assert(las_get(can,LAS_DUPLICATES)==1); /* second transport duplicate */
|
||||
sample(can,1,9003,0);assert(las_get(can,LAS_MISSING)==3);
|
||||
las_snapshot(can,indices,samples,breaks,LAS_CAPACITY);assert(breaks[8191]);
|
||||
/* Repeated metadata must not erase rolling history. */
|
||||
meta(can,1,1);assert(las_get(can,LAS_COUNT)==8192);
|
||||
meta(can,2,0);assert(las_get(can,LAS_COUNT)==0);
|
||||
sample(can,1,9004,1);assert(las_get(can,LAS_COUNT)==0);
|
||||
meta(can,1,1);assert(las_get(can,LAS_SESSION)==2);
|
||||
sample(can,2,0xffffffffU,0);sample(can,2,0,1);
|
||||
las_snapshot(can,indices,samples,breaks,LAS_CAPACITY);
|
||||
assert(indices[0]==0xffffffffULL&&indices[1]==0x100000000ULL&&!breaks[1]);
|
||||
/* Reject wrong address, standard ID, RTR and short payload. */
|
||||
n=las_data(14,2,1,0,0,&id,packet,sizeof(packet));assert(n==8);
|
||||
las_can(can,id,packet,8,0,0);las_can(can,id,packet,8,1,1);
|
||||
las_can(can,id^(1U<<20),packet,8,1,0);las_can(can,id,packet,7,1,0);
|
||||
assert(las_get(can,LAS_COUNT)==2&&las_get(can,LAS_INVALID)==1);
|
||||
n=las_data(14,2,1,1,1,&id,packet,sizeof(packet));packet[n-1]^=0x80;
|
||||
las_uart(can,packet,n);assert(las_get(can,LAS_CRC_ERRORS)==1);
|
||||
sample(can,2,1,1);assert(las_get(can,LAS_COUNT)==3); /* recovery after CRC */
|
||||
assert(!las_metadata(14,65536,1000,0,&id,packet,sizeof(packet)));
|
||||
assert(!las_metadata(14,1,0,0,&id,packet,sizeof(packet)));
|
||||
assert(!las_data(14,1,0,65536,0,&id,packet,sizeof(packet)));
|
||||
free(can);free(uart);free(indices);free(samples);free(breaks);
|
||||
puts("Altera SETCAN/CAN/UART stream tests passed");return 0;
|
||||
}
|
||||
@@ -20,6 +20,8 @@ INCLUDE = ROOT / "include"
|
||||
JNI_INCLUDES: list[Path] = []
|
||||
SOURCES = [
|
||||
ROOT / "src" / name for name in (
|
||||
"altera_logic.c",
|
||||
"altera_stream.c",
|
||||
"set_protocol.c", "set_can.c", "set_firmware.c", "set_telemetry.c", "set_plot.c", "set_trends.c", "set_spectrum.c",
|
||||
"balsam_can.c", "set_crc.c", "periph28335.c", "tms2812.c", "gui_catalog.c", "gui_frame.c", "pcan_abi.c", "pcan_crc.c",
|
||||
"pcan_frame.c", "pcan_id.c", "pcan_link.c", "pcan_ring.c",
|
||||
|
||||
Reference in New Issue
Block a user