Generate README.md, add clock dividers and limiters, load configs from WS client, fix stream per-segment subscriptions according to memory projections mechanism
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -7,3 +7,4 @@ build
|
||||
devices
|
||||
.cache
|
||||
*.bin
|
||||
inc/flatbuf_autogen
|
||||
|
||||
10
Makefile
10
Makefile
@@ -11,7 +11,7 @@ LIBS_HEADERS=deps/ $(OPENSSL_INCLUDE)
|
||||
SYSTEM_INCLUDES=-isystem deps/flatcc/include/ -isystem $(PROTO_INC_DIR)/
|
||||
STATIC_LIBS=crypto
|
||||
STANDART=c23
|
||||
OPTIMIZE=-O3
|
||||
OPTIMIZE=-Og
|
||||
TARGET=main
|
||||
|
||||
FLATCC = deps/flatcc/bin/flatcc
|
||||
@@ -48,7 +48,7 @@ LFLAGS=$(OPTIMIZE) -g $(PEDANTIC_FLAGS) $(DEFINES) $(STATICLIBS_FLAGS) -flto -fu
|
||||
OBJECTS = $(patsubst $(SRC_DIR)/%.c, $(BUILD_DIR)/%.o, $(C_SOURCES))
|
||||
|
||||
DEP = $(filter %.d, $(OBJECTS:.o=.d))
|
||||
$(info $(DEP))
|
||||
#$(info $(DEP))
|
||||
|
||||
OBJECTS += $(LIBS)
|
||||
vpath %.c $(sort $(dir $(C_SOURCES)))
|
||||
@@ -56,11 +56,11 @@ vpath %.c $(sort $(dir $(C_SOURCES)))
|
||||
|
||||
all: build
|
||||
|
||||
build: date deps Dir proto target compile_commands
|
||||
build: date deps Dir proto python-proto target compile_commands
|
||||
|
||||
rebuild: clean | build
|
||||
|
||||
$(info $(DEP))
|
||||
#$(info $(DEP))
|
||||
|
||||
-include $(DEP)
|
||||
|
||||
@@ -92,7 +92,7 @@ $(PROTO_STAMP): $(FBS_SOURCES) | ProtoDir BuildDir
|
||||
|
||||
BuildDir:
|
||||
@mkdir -p $(BUILD_DIR)
|
||||
$(shell mkdir -p $(dir $(OBJECTS)))
|
||||
@$(shell mkdir -p $(dir $(OBJECTS)))
|
||||
|
||||
SrcDir:
|
||||
@mkdir -p $(SRC_DIR)
|
||||
|
||||
250
README.md
Normal file
250
README.md
Normal file
@@ -0,0 +1,250 @@
|
||||
# hmmmm
|
||||
|
||||
**Модульный эмулятор микроконтроллеров с сетевым интерфейсом**
|
||||
|
||||
hmmmm — это эмулятор микроконтроллеров, который позволяет эмулировать AVR и другие архитектуры с возможностью управления через WebSocket. Эмулятор поддерживает динамическую загрузку устройств, композицию сложных устройств и потоковую передачу изменений памяти в реальном времени.
|
||||
|
||||
## Возможности
|
||||
|
||||
- **Эмуляция AVR-микроконтроллеров** — поддержка ~99 инструкций AVR
|
||||
- **Модульная архитектура** — устройства загружаются как динамические библиотеки (.so)
|
||||
- **Композиция устройств** — создание сложных устройств через TOML-конфигурацию
|
||||
- **WebSocket интерфейс** — управление эмуляцией и получение данных в реальном времени
|
||||
- **Потоковая передача** — подписка на изменения памяти (чтение/запись)
|
||||
- **Аутентификация** — SHA-512 токен с временным окном (TOTP-подобный)
|
||||
- **Сегментированная память** — PS (программная), GP_REGS, IO_REGS, DS (данные)
|
||||
|
||||
## Структура проекта
|
||||
|
||||
```
|
||||
hmmmm/
|
||||
├── src/ # Исходный код эмулятора
|
||||
│ ├── main.c # WebSocket сервер, главный цикл
|
||||
│ ├── hmmmm.c # Загрузка динамических библиотек
|
||||
│ ├── config.c # Парсинг TOML-конфигураций
|
||||
│ └── proto/ # Обработчики WebSocket-протокола
|
||||
├── inc/ # Заголовочные файлы
|
||||
├── devices/ # Реализации эмулируемых устройств
|
||||
│ ├── avr_generic/ # Универсальный эмулятор AVR
|
||||
│ └── avr_gpio/ # GPIO устройство
|
||||
├── flatbuffers/ # FlatBuffers схемы протокола
|
||||
├── deps/ # Внешние зависимости
|
||||
├── hmmmm_scripts/ # Python скрипты и утилиты
|
||||
└── Makefile # Основной файл сборки
|
||||
```
|
||||
|
||||
## Требования
|
||||
|
||||
- **GCC** с поддержкой C23
|
||||
- **OpenSSL** (для SHA-512)
|
||||
- **Git** (для клонирования зависимостей)
|
||||
- **Python 3.12+** (опционально, для скриптов)
|
||||
|
||||
## Сборка
|
||||
|
||||
### 1. Инициализация зависимостей
|
||||
|
||||
```bash
|
||||
# Клонирование и сборка внешних зависимостей
|
||||
make deps
|
||||
```
|
||||
|
||||
Это клонирует следующие зависимости в `deps/`:
|
||||
- `tomlc99` — TOML парсер
|
||||
- `ptQueue` — lock-free очередь
|
||||
- `wsServer` — WebSocket сервер
|
||||
- `flatcc` — FlatBuffers runtime для C
|
||||
- `flatbuffers` — Google FlatBuffers (flatc компилятор)
|
||||
|
||||
### 2. Генерация FlatBuffers кода
|
||||
|
||||
```bash
|
||||
# Генерация C заголовков из .fbs схем
|
||||
make proto
|
||||
|
||||
# Генерация Python bindings (опционально)
|
||||
make python-proto
|
||||
```
|
||||
|
||||
### 3. Сборка эмулятора
|
||||
|
||||
```bash
|
||||
# Основная сборка
|
||||
make build
|
||||
|
||||
# Полная пересборка
|
||||
make rebuild
|
||||
|
||||
# Очистка
|
||||
make clean
|
||||
```
|
||||
|
||||
После успешной сборки исполняемый файл будет находиться в `out/main.elf`.
|
||||
|
||||
## Быстрый старт
|
||||
|
||||
### 1. Подготовка конфигурации
|
||||
|
||||
Создайте TOML-файл с конфигурацией эмулятора:
|
||||
|
||||
```toml
|
||||
# example.toml
|
||||
[dev]
|
||||
avr = "devices/avr_generic/AVRrc.toml"
|
||||
|
||||
[clock]
|
||||
limiter = 16000000 # 16 MHz
|
||||
|
||||
[mem]
|
||||
# Настройка памяти
|
||||
```
|
||||
|
||||
### 2. Запуск эмулятора
|
||||
|
||||
```bash
|
||||
./out/main.elf example.toml
|
||||
```
|
||||
|
||||
Эмулятор запустит WebSocket сервер на порту **8181**.
|
||||
|
||||
### 3. Подключение клиента
|
||||
|
||||
Используйте Python-клиент для подключения:
|
||||
|
||||
```bash
|
||||
cd ../hmmmm_client_py
|
||||
poetry install
|
||||
poetry run python -m model.client
|
||||
```
|
||||
|
||||
## Конфигурация
|
||||
|
||||
### TOML-конфигурация устройства
|
||||
|
||||
Пример конфигурации AVR устройства:
|
||||
|
||||
```toml
|
||||
[device]
|
||||
type = "avr_generic"
|
||||
libpath = "devices/avr_generic/AVRrc_build/device.so"
|
||||
firmware = "test.bin"
|
||||
|
||||
[memory]
|
||||
ps_size = 65536 # Program space
|
||||
ds_size = 4096 # Data space
|
||||
io_regs = 256 # I/O registers
|
||||
gp_regs = 32 # General purpose registers
|
||||
```
|
||||
|
||||
### Композиция устройств
|
||||
|
||||
Сложные устройства создаются через композицию:
|
||||
|
||||
```toml
|
||||
[dev]
|
||||
core = "avr_generic.toml"
|
||||
gpio_a = "avr_gpio.toml"
|
||||
|
||||
[clock]
|
||||
limiter = 16000000
|
||||
|
||||
[mem.intercept.PORTA]
|
||||
base_at = "core"
|
||||
point_at = "gpio_a"
|
||||
addr = 0
|
||||
seg = "reg_io"
|
||||
```
|
||||
|
||||
## Протокол
|
||||
|
||||
Эмулятор использует **FlatBuffers over WebSocket** для обмена сообщениями.
|
||||
|
||||
### Типы сообщений
|
||||
|
||||
#### Клиент → Сервер:
|
||||
- **AuthRequest** — аутентификация (SHA-512 токен + timestamp)
|
||||
- **ExecCtrlMessage** — управление эмуляцией (старт/стоп/сброс)
|
||||
- **MemReadRequest/MemWriteRequest** — операции с памятью
|
||||
- **StreamRegRequest** — подписка на изменения памяти
|
||||
|
||||
#### Сервер → Клиент:
|
||||
- **AuthResponse** — подтверждение аутентификации (seat_id)
|
||||
- **ExecNotifyMessage** — уведомления о состоянии эмуляции
|
||||
- **MemReadResponse** — данные из памяти
|
||||
- **StreamDataPush** — потоковые изменения памяти
|
||||
|
||||
### Аутентификация
|
||||
|
||||
```python
|
||||
import hashlib
|
||||
import time
|
||||
|
||||
def generate_auth(access_token: str) -> bytes:
|
||||
now = int(time.time())
|
||||
prepared = f'{access_token}{now // 30}'
|
||||
return hashlib.sha512(prepared.encode()).digest()
|
||||
```
|
||||
|
||||
Токен действителен в течение 30 секунд.
|
||||
|
||||
## API устройств
|
||||
|
||||
Каждое устройство должно экспортировать интерфейс `device_lib_t`:
|
||||
|
||||
```c
|
||||
typedef struct {
|
||||
uint8_t devType;
|
||||
device_public_context_t* (*init)(void*, char* errbuf);
|
||||
uint8_t (*makeDeviceTick)(device_public_context_t* devInfo);
|
||||
void* extendedHandlers;
|
||||
void* (*parseSpecsFromConfig)(const conf_dev_t* devConf, char* errbuf);
|
||||
void (*freeSpecs)(void* specs);
|
||||
void (*freeDevMem)(device_mem_t* mem);
|
||||
void (*fillSmartReadSpecs)(void* specs, smart_read_spec_t*, uint64_t);
|
||||
void (*fillSmartWriteSpecs)(void* specs, smart_write_spec_t*, uint64_t);
|
||||
} device_lib_t;
|
||||
```
|
||||
|
||||
## Отладка
|
||||
|
||||
### Генерация compile_commands.json
|
||||
|
||||
Для интеграции с clangd:
|
||||
|
||||
```bash
|
||||
./.gen_compile_commands.sh
|
||||
```
|
||||
|
||||
### Логирование
|
||||
|
||||
Эмулятор выводит отладочную информацию в stdout. Для подробного логирования можно модифицировать `src/main.c`.
|
||||
|
||||
### GDB
|
||||
|
||||
```bash
|
||||
gdb ./out/main.elf
|
||||
(gdb) run example.toml
|
||||
```
|
||||
|
||||
## Производительность
|
||||
|
||||
- **Тактовая частота** — до 16 MHz (эмулируемая)
|
||||
- **WebSocket** — асинхронная отправка сообщений
|
||||
- **Lock-free очереди** — эффективный межпоточный обмен
|
||||
- **FlatBuffers** — сериализация с нулевым копированием
|
||||
|
||||
## Известные ограничения
|
||||
|
||||
- Абсолютные пути в конфигурационных файлах (требует замены на относительные)
|
||||
- Хардкод токена аутентификации (требует выноса в ENV)
|
||||
- Отсутствие graceful shutdown (обработка SIGINT/SIGTERM)
|
||||
- Нет unit-тестов для основного кода
|
||||
|
||||
## Лицензия
|
||||
|
||||
Проект разработан @nikto_b. Все права защищены.
|
||||
|
||||
## Контакты
|
||||
|
||||
- Документация: https://about.hmmmm.nikto-b.ru/
|
||||
- Автор: @nikto_b
|
||||
2
deps/flatbuffers
vendored
2
deps/flatbuffers
vendored
Submodule deps/flatbuffers updated: 4e582b0c1d...e223d69b36
@@ -72,7 +72,7 @@ device_mem_t* genDevMem(device_specs_t* devSpec, char* errbuf)
|
||||
void* rawCells = (void*)malloc(memTotalSize);
|
||||
if (rawCells == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to allocate raw memory buf");
|
||||
sprintf(errbuf, "unable to allocate raw memory buf %lu bytes", memTotalSize);
|
||||
free(devMem);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -41,7 +41,8 @@ table DeviceEntry {
|
||||
// Inline device configuration — either base or compose.
|
||||
config: DeviceConfig;
|
||||
|
||||
clock: DeviceClockConfig;
|
||||
//clock: DeviceClockConfig;
|
||||
clock_divider: uint64 = 1;
|
||||
overrides: [MemSegOverride];
|
||||
}
|
||||
|
||||
|
||||
60
glob.toml
60
glob.toml
@@ -1,60 +0,0 @@
|
||||
[dev]
|
||||
core = "/home/nikto_b/Documents/baum/hmmmm/devices/avr_generic/AVRrc.toml"
|
||||
# gpio_a = "/home/nikto_b/Documents/baum/hmmmm/devices/avr_gpio/gpio.toml"
|
||||
|
||||
[config.core.mem.ps]
|
||||
len = 128
|
||||
|
||||
|
||||
[clock]
|
||||
limiter = 16000000
|
||||
|
||||
[clock.core]
|
||||
src = ""
|
||||
divider = 1
|
||||
|
||||
[clock.gpio_a]
|
||||
src = "core"
|
||||
divider = 2
|
||||
|
||||
|
||||
# [mem.projection.gpio_a.ext_reg_io]
|
||||
# base_at = "core"
|
||||
# base_seg = "reg_io"
|
||||
# projection_shift = 0
|
||||
|
||||
|
||||
[mem.intercept.PORTA]
|
||||
base_at = "core"
|
||||
point_at = "gpio_a"
|
||||
addr = 0
|
||||
seg = "reg_io"
|
||||
|
||||
|
||||
[mem.intercept.PINA]
|
||||
base_at = "core"
|
||||
point_at = "gpio_a"
|
||||
base_addr = 1
|
||||
point_addr = 1
|
||||
base_seg = "reg_io"
|
||||
point_seg = "ext_reg_io"
|
||||
|
||||
[mem.setup.core.ps]
|
||||
filepath = "/home/nikto_b/Documents/avr_selftests/test.bin"
|
||||
|
||||
|
||||
[mem.setup.core.ds]
|
||||
filepath = "/dev/urandom"
|
||||
overflow_behaviour = "ignore"
|
||||
|
||||
# - почему си (адресная арифметика, отсутствие мешающих фичей языка, широкое распространение среди разработчеков-железячников)
|
||||
# - подход к модульности (всё базовое устройство, запускаем комбинированные устройства из базовых и комбинированных)
|
||||
# - структура базового модуля эмулятора (какие есть варианты полей конфига)
|
||||
# - структура комбинированного модуля эмулятора (какие есть варианты полей конфига)
|
||||
# - структура комбинированного модуля эмулятора (какие конфиг вырождается в эмулятор)
|
||||
# - процесс инициализации модуля (6 этапов инициализации)
|
||||
# - процесс исполнения модуля (чисто дергаем "makeTick" у каждой dlib'ы)
|
||||
# - реализация перехватчиков доступа (пример с sbi, диаграмма с тестовым перехватчиком)
|
||||
# - реализация проецирования памяти (диаграммы с колбасами)
|
||||
# - тестовая прошивка (селфтест сложения-вычитания)
|
||||
# - пример исполнения (?)
|
||||
Submodule hmmmm_scripts updated: 9cc7b36cbb...1bcda2881a
@@ -2,6 +2,7 @@
|
||||
#define __BASE_DEVICE_H__
|
||||
|
||||
#include "hmmmm.h"
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct {
|
||||
void* specs;
|
||||
@@ -9,6 +10,7 @@ typedef struct {
|
||||
device_public_context_t* ctx;
|
||||
uint64_t clockCycleLimit;
|
||||
uint64_t clockCycleCounter;
|
||||
uint64_t clockDivider;
|
||||
} device_handle_t;
|
||||
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define __CONTEXT_H__
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "ptQueue/inc/ptQueue.h"
|
||||
#include "wsServer/include/ws.h"
|
||||
@@ -30,11 +31,13 @@ typedef struct {
|
||||
OutgoingBuffers* outBufs;
|
||||
DeviceSegStreamReg** deviceStreamRegs;
|
||||
uint8_t** devicesMem;
|
||||
size_t** devicesMemSegAddrs;
|
||||
size_t devicesCount;
|
||||
void** deviceHandles; // device_handle_t** — dynamically loaded devices
|
||||
void* interceptCtxs; // contiguous intercept_ctx_t array (freed as single block)
|
||||
size_t interceptCtxCount;
|
||||
flatcc_builder_t stream_builder;
|
||||
uint64_t simRateLimit;
|
||||
} EmulContext;
|
||||
|
||||
|
||||
|
||||
@@ -12,7 +12,9 @@
|
||||
typedef struct {
|
||||
ClientContext* clientContext;
|
||||
uint32_t regId;
|
||||
uint32_t segId;
|
||||
uint64_t startAddr;
|
||||
uint64_t startGlobalAddr;
|
||||
uint64_t segLen;
|
||||
uint8_t mode;
|
||||
} StreamReg;
|
||||
|
||||
@@ -69,6 +69,7 @@ device_handle_t* openBaseDevice(conf_dev_t* devConf, char* errbuf)
|
||||
ret->specs = devSpecs;
|
||||
ret->clockCycleCounter = 0;
|
||||
ret->clockCycleLimit = 0;
|
||||
ret->clockDivider = 1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ device_handle_t** openComposeDevice(compose_dev_conf_t* conf, char* errbuf)
|
||||
for(size_t i = 0; i < devIdx; i++)
|
||||
{
|
||||
conf_dev_t* devConf = conf->baseConfigs[i];
|
||||
char intErrbuf[1024] = {0};
|
||||
char intErrbuf[255] = {0};
|
||||
device_handle_t* devHandle = openBaseDevice(devConf, intErrbuf);
|
||||
if(devHandle == NULL)
|
||||
{
|
||||
|
||||
52
src/main.c
52
src/main.c
@@ -245,17 +245,17 @@ void mockDevice1(uint8_t* mem, uint64_t* readAddrs, size_t* readAddrsLen, uint64
|
||||
|
||||
}
|
||||
|
||||
void dispatchStreamSegment(EmulContext* emulContext, StreamReg* reg, uint8_t* mem)
|
||||
void dispatchStreamSegment(EmulContext* emulContext, StreamReg* reg, device_mem_t* mem)
|
||||
{
|
||||
size_t mlen = 0;
|
||||
uint8_t* msg = fb_build_stream_data_push(
|
||||
&emulContext->stream_builder,
|
||||
UINT64_MAX, reg->regId, *emulContext->clockCounter,
|
||||
mem + reg->startAddr, reg->segLen, &mlen);
|
||||
(uint8_t*)(((uint64_t)(mem->cells[reg->segId])) + (uint64_t)reg->startAddr), reg->segLen, &mlen);
|
||||
dispatchOutgoingMessage(emulContext->outBufs, reg->clientContext->clientId, msg, mlen);
|
||||
}
|
||||
|
||||
void dispatchMemAccessNotifications(EmulContext* emulContext, DeviceSegStreamReg* deviceRegs, uint8_t* mem, uint64_t* addrs, size_t addrsLen, uint8_t mode)
|
||||
void dispatchMemAccessNotifications(EmulContext* emulContext, DeviceSegStreamReg* deviceRegs, device_mem_t* mem, uint64_t* addrs, size_t addrsLen, uint8_t mode)
|
||||
{
|
||||
if(deviceRegs->regCount == 0)
|
||||
{
|
||||
@@ -274,7 +274,7 @@ void dispatchMemAccessNotifications(EmulContext* emulContext, DeviceSegStreamReg
|
||||
StreamReg* reg = &deviceRegs->regs[regIdx];
|
||||
if(reg->mode == mode)
|
||||
{
|
||||
if(reg->startAddr <= addr && reg->startAddr + reg->segLen >= addr)
|
||||
if(reg->startGlobalAddr <= addr && reg->startGlobalAddr + reg->segLen >= addr)
|
||||
{
|
||||
uint8_t isDuplicate = 0;
|
||||
for(size_t j = 0; j < dispatchRegsCnt; j++)
|
||||
@@ -307,6 +307,14 @@ void dispatchMemAccessNotifications(EmulContext* emulContext, DeviceSegStreamReg
|
||||
free(dispatchRegs);
|
||||
}
|
||||
|
||||
|
||||
uint64_t getCurrentUsec()
|
||||
{
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv,NULL);
|
||||
return (1000000 * (uint64_t)tv.tv_sec) + (uint64_t)tv.tv_usec;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
(void)argc; (void)argv;
|
||||
@@ -320,6 +328,7 @@ int main(int argc, char** argv)
|
||||
size_t deviceCount = 0;
|
||||
DeviceSegStreamReg** deviceStreamRegs = NULL;
|
||||
uint8_t** devicesMem = NULL;
|
||||
size_t** devicesMemSegmentAddrs = NULL;
|
||||
void** deviceHandlesArr = NULL;
|
||||
|
||||
uint8_t emulState = EMUL_STATE_STILL;
|
||||
@@ -366,11 +375,13 @@ int main(int argc, char** argv)
|
||||
&outBufs,
|
||||
deviceStreamRegs,
|
||||
devicesMem,
|
||||
devicesMemSegmentAddrs,
|
||||
deviceCount,
|
||||
deviceHandlesArr,
|
||||
NULL, /* interceptCtxs */
|
||||
0, /* interceptCtxCount */
|
||||
{0} /* stream_builder — initialized below */
|
||||
{0}, /* stream_builder — initialized below */
|
||||
0,
|
||||
};
|
||||
if (flatcc_builder_init(&emulContext.stream_builder)) {
|
||||
panic("flatcc_builder_init failed\n");
|
||||
@@ -409,6 +420,8 @@ int main(int argc, char** argv)
|
||||
|
||||
uint8_t clients_try_timer = 100;
|
||||
|
||||
uint64_t lastTickAt = getCurrentUsec();
|
||||
|
||||
while(1)
|
||||
{
|
||||
ClientRegistrationEvent* payload = regQueueTail->payload;
|
||||
@@ -443,7 +456,7 @@ int main(int argc, char** argv)
|
||||
}
|
||||
while(readReqIdx == newWriteIdx)
|
||||
{
|
||||
my_sleep(1000);
|
||||
my_sleep(100000);
|
||||
readReqIdx = atomic_load(&outBufs.readRequestIdx);
|
||||
}
|
||||
atomic_store(&outBufs.currWritingIdx, newWriteIdx);
|
||||
@@ -451,22 +464,35 @@ int main(int argc, char** argv)
|
||||
}
|
||||
}
|
||||
|
||||
if(emulState == EMUL_STATE_EXEC && emulContext.devicesCount > 0)
|
||||
uint64_t now = emulContext.simRateLimit > 0? getCurrentUsec() : 0;
|
||||
|
||||
if(emulState == EMUL_STATE_EXEC && emulContext.devicesCount > 0 && (emulContext.simRateLimit == 0 || now - lastTickAt >= 1000000 / emulContext.simRateLimit))
|
||||
{
|
||||
for (size_t di = 0; di < emulContext.devicesCount; di++)
|
||||
{
|
||||
device_handle_t* dev = (device_handle_t*)emulContext.deviceHandles[di];
|
||||
device_mem_t* devMem = dev->ctx->deviceMem;
|
||||
devMem->memreadLen = 0;
|
||||
devMem->memwriteLen = 0;
|
||||
|
||||
dev->lib->makeDeviceTick(dev->ctx);
|
||||
if (clockCounter % dev->clockDivider == 0)
|
||||
{
|
||||
// printf("clock device %lu\n", di);
|
||||
device_mem_t* devMem = dev->ctx->deviceMem;
|
||||
devMem->memreadLen = 0;
|
||||
devMem->memwriteLen = 0;
|
||||
|
||||
dev->lib->makeDeviceTick(dev->ctx);
|
||||
|
||||
dispatchMemAccessNotifications(&emulContext, emulContext.deviceStreamRegs[di], devMem, devMem->memreadCellAddrs, (size_t)devMem->memreadLen, STREAM_MODE_READ);
|
||||
dispatchMemAccessNotifications(&emulContext, emulContext.deviceStreamRegs[di], devMem, devMem->memwriteCellAddrs, (size_t)devMem->memwriteLen, STREAM_MODE_WRITE);
|
||||
}
|
||||
|
||||
dispatchMemAccessNotifications(&emulContext, emulContext.deviceStreamRegs[di], devMem->rawCells, devMem->memreadCellAddrs, (size_t)devMem->memreadLen, STREAM_MODE_READ);
|
||||
dispatchMemAccessNotifications(&emulContext, emulContext.deviceStreamRegs[di], devMem->rawCells, devMem->memwriteCellAddrs, (size_t)devMem->memwriteLen, STREAM_MODE_WRITE);
|
||||
}
|
||||
|
||||
clockCounter++;
|
||||
|
||||
if(emulContext.simRateLimit > 0)
|
||||
{
|
||||
lastTickAt = getCurrentUsec();
|
||||
}
|
||||
}
|
||||
else if(!utilizedFlag)
|
||||
{
|
||||
|
||||
@@ -128,13 +128,13 @@ void handleAllClients(EmulContext* emulContext)
|
||||
if (fbmsg != NULL) {
|
||||
*emulContext->utilizedFlag = 1;
|
||||
|
||||
if (hmmmm_ClientMessage_verify_as_root(fbmsg->data, fbmsg->size) == 0) {
|
||||
// if (hmmmm_ClientMessage_verify_as_root(fbmsg->data, fbmsg->size) == 0) {
|
||||
hmmmm_ClientMessage_table_t cm =
|
||||
hmmmm_ClientMessage_as_root(fbmsg->data);
|
||||
handleIncomingMessage(cm, ctx, emulContext);
|
||||
} else {
|
||||
printf("client %lu: dropped malformed FlatBuffer\n", ctx->clientId);
|
||||
}
|
||||
// } else {
|
||||
// printf("client %lu: dropped malformed FlatBuffer\n", ctx->clientId);
|
||||
// }
|
||||
|
||||
free(fbmsg->data);
|
||||
free(fbmsg);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "proto/handlers/config.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@@ -122,19 +123,35 @@ static int load_devices_recursive(
|
||||
if (!dc) return -1;
|
||||
|
||||
device_handle_t* dev = openBaseDevice(dc, errbuf);
|
||||
freeConf(dc); // freeConf() already calls free(dc)
|
||||
if (!dev) return -1;
|
||||
if (!dev) {
|
||||
freeConf(dc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
const uint64_t clockDivider = hmmmm_config_DeviceEntry_clock_divider(entry);
|
||||
if(clockDivider > 0)
|
||||
{
|
||||
dev->clockDivider = clockDivider;
|
||||
}
|
||||
|
||||
|
||||
|
||||
dev->lib->fillSmartReadSpecs(dev->specs, NULL, 0);
|
||||
dev->lib->fillSmartWriteSpecs(dev->specs, NULL, 0);
|
||||
|
||||
dev->ctx = dev->lib->init(dev->specs, errbuf);
|
||||
char initErrBuf[256];
|
||||
|
||||
dev->ctx = dev->lib->init(dev->specs, initErrBuf);
|
||||
if (!dev->ctx) {
|
||||
snprintf(errbuf, 1024, "error while loading device %lu: %s", st->count, initErrBuf);
|
||||
closeBaseDevice(dev);
|
||||
free(dev);
|
||||
freeConf(dc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
freeConf(dc);
|
||||
|
||||
size_t idx = st->count;
|
||||
st->handles[idx] = dev;
|
||||
|
||||
@@ -471,6 +488,7 @@ static void free_old_config(EmulContext* emulContext)
|
||||
emulContext->devicesMem = NULL;
|
||||
emulContext->deviceStreamRegs = NULL;
|
||||
emulContext->devicesCount = 0;
|
||||
emulContext->simRateLimit = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -522,6 +540,8 @@ void handleConfigCtrlMessage(
|
||||
return;
|
||||
}
|
||||
|
||||
const uint64_t simRateLimit = hmmmm_config_EmulationConfig_sim_rate_limit(econf);
|
||||
|
||||
// Free old config
|
||||
free_old_config(emulContext);
|
||||
|
||||
@@ -591,10 +611,12 @@ void handleConfigCtrlMessage(
|
||||
// Allocate new arrays for EmulContext
|
||||
size_t dc = st.count;
|
||||
emulContext->devicesCount = dc;
|
||||
emulContext->simRateLimit = simRateLimit;
|
||||
|
||||
emulContext->deviceHandles = calloc(dc, sizeof(void*));
|
||||
emulContext->devicesMem = calloc(dc, sizeof(uint8_t*));
|
||||
emulContext->deviceStreamRegs = calloc(dc, sizeof(DeviceSegStreamReg*));
|
||||
emulContext->deviceHandles = calloc(dc, sizeof(void*));
|
||||
emulContext->devicesMem = calloc(dc, sizeof(uint8_t*));
|
||||
emulContext->devicesMemSegAddrs = calloc(dc, sizeof(size_t*));
|
||||
emulContext->deviceStreamRegs = calloc(dc, sizeof(DeviceSegStreamReg*));
|
||||
|
||||
if (!emulContext->deviceHandles || !emulContext->devicesMem ||
|
||||
!emulContext->deviceStreamRegs) {
|
||||
@@ -619,16 +641,36 @@ void handleConfigCtrlMessage(
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < dc; i++) {
|
||||
for (size_t i = 0; i < dc; i++)
|
||||
{
|
||||
emulContext->deviceHandles[i] = st.handles[i];
|
||||
emulContext->devicesMem[i] = st.handles[i]->ctx->deviceMem->rawCells;
|
||||
|
||||
emulContext->devicesMemSegAddrs[i] = calloc(st.seg_counts[i], sizeof(size_t));
|
||||
|
||||
for(size_t seg_id = 0; seg_id < st.seg_counts[i]; seg_id++)
|
||||
{
|
||||
|
||||
// size_t segShift = (size_t)(st.handles[i]->ctx->deviceMem->cells[seg_id]) - (size_t)(st.handles[i]->ctx->deviceMem->cells[0]);
|
||||
size_t segShift = st.handles[i]->ctx->deviceMem->memsegShifts[seg_id];
|
||||
emulContext->devicesMemSegAddrs[i][seg_id] = segShift;
|
||||
}
|
||||
|
||||
DeviceSegStreamReg* dsr = calloc(1, sizeof(DeviceSegStreamReg));
|
||||
if (!dsr) { emulContext->deviceStreamRegs[i] = NULL; continue; }
|
||||
if (!dsr)
|
||||
{
|
||||
emulContext->deviceStreamRegs[i] = NULL;
|
||||
continue;
|
||||
}
|
||||
dsr->allocatedSize = 4;
|
||||
dsr->regCount = 0;
|
||||
dsr->regs = calloc(4, sizeof(StreamReg));
|
||||
if (!dsr->regs) { free(dsr); emulContext->deviceStreamRegs[i] = NULL; continue; }
|
||||
if (!dsr->regs)
|
||||
{
|
||||
free(dsr);
|
||||
emulContext->deviceStreamRegs[i] = NULL;
|
||||
continue;
|
||||
}
|
||||
emulContext->deviceStreamRegs[i] = dsr;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include "proto/dial.h"
|
||||
#include "mem_reader.h"
|
||||
|
||||
#define DEVICE_MEM_SIZE ((size_t)(256 * 1024))
|
||||
// #define DEVICE_MEM_SIZE ((size_t)(256 * 1024))
|
||||
|
||||
|
||||
void handleIncomingMemMessage(
|
||||
@@ -40,10 +40,13 @@ void handleIncomingMemMessage(
|
||||
printf("[MEM/READ] invalid device %u\n", dev_id);
|
||||
return;
|
||||
}
|
||||
if ((size_t)offset + (size_t)length > DEVICE_MEM_SIZE) {
|
||||
printf("[MEM/READ] out of bounds\n");
|
||||
return;
|
||||
}
|
||||
|
||||
offset = (uint32_t)(((size_t)offset) + emulContext->devicesMemSegAddrs[dev_id][seg_id]);
|
||||
|
||||
// if ((size_t)offset + (size_t)length > DEVICE_MEM_SIZE) {
|
||||
// printf("[MEM/READ] out of bounds\n");
|
||||
// return;
|
||||
// }
|
||||
|
||||
const uint8_t* base = emulContext->devicesMem[dev_id];
|
||||
size_t out_len;
|
||||
@@ -73,10 +76,10 @@ void handleIncomingMemMessage(
|
||||
printf("[MEM/WRITE] invalid device %u\n", dev_id);
|
||||
return;
|
||||
}
|
||||
if ((size_t)offset + data_len > DEVICE_MEM_SIZE) {
|
||||
printf("[MEM/WRITE] out of bounds\n");
|
||||
return;
|
||||
}
|
||||
// if ((size_t)offset + data_len > DEVICE_MEM_SIZE) {
|
||||
// printf("[MEM/WRITE] out of bounds\n");
|
||||
// return;
|
||||
// }
|
||||
|
||||
uint8_t* base = emulContext->devicesMem[dev_id];
|
||||
memcpy(base + offset, data, data_len);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "proto/handlers/stream.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "streamed.h"
|
||||
@@ -104,11 +105,15 @@ void handleIncomingStreamMessage(
|
||||
deviceRegs->allocatedSize = new_size;
|
||||
}
|
||||
|
||||
uint64_t globalOffset = (((size_t)offset) + emulContext->devicesMemSegAddrs[dev_id][seg_id]);
|
||||
|
||||
uint32_t reg_id = ctx->streamRegIterator++;
|
||||
StreamReg* reg = &deviceRegs->regs[deviceRegs->regCount++];
|
||||
reg->clientContext = ctx;
|
||||
reg->regId = reg_id;
|
||||
reg->segId = seg_id;
|
||||
reg->startAddr = (uint64_t)offset;
|
||||
reg->startGlobalAddr = globalOffset;
|
||||
reg->segLen = (uint64_t)length;
|
||||
reg->mode = (uint8_t)mode;
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "proto/handlers.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "panic.h"
|
||||
#include "my_mutex.h"
|
||||
#include "proto/enums.h"
|
||||
@@ -112,10 +113,20 @@ void onWsMessage(
|
||||
}
|
||||
|
||||
// Verify the FlatBuffer before queuing
|
||||
if (hmmmm_ClientMessage_verify_as_root(msg, (size_t)size)) {
|
||||
printf("Client %lu sent invalid FlatBuffer, dropping\n", client);
|
||||
return;
|
||||
}
|
||||
// if (hmmmm_ClientMessage_verify_as_root(msg, size)) {
|
||||
// printf("Client %lu sent invalid FlatBuffer, dropping\n", client);
|
||||
|
||||
// FILE *write_ptr;
|
||||
|
||||
// write_ptr = fopen("/tmp/last_failed_packet.bin","wb"); // w for write, b for binary
|
||||
// NULL_GUARD(write_ptr);
|
||||
|
||||
// fwrite(msg,size,1,write_ptr); // write 10 bytes from our buffer
|
||||
// // free(write_ptr);
|
||||
// fclose(write_ptr);
|
||||
|
||||
// return;
|
||||
// }
|
||||
|
||||
// Copy bytes — the WS buffer is only valid for this callback's duration
|
||||
FbMessage* fbmsg = malloc(sizeof(FbMessage));
|
||||
|
||||
Reference in New Issue
Block a user