Guide to MCU
MCU (Microcontroller Unit) — программируемые микроконтроллеры, позволяющие игрокам создавать автоматизированные устройства с произвольной логикой. MCU используют полноценный интерпретатор 32-битного процессора RISC-V с расширениями IMFZicsrZicntrZbbZba и уровнями привилегий M+U.
Обзор
MCU представляют собой компактные вычислительные платы, которые можно программировать с помощью загрузки ELF-файлов через JTAG-программатор. К MCU подключаются различные модули расширения через PCI-слоты, позволяя взаимодействовать с окружающим миром: синтезировать речь, отправлять радиосигналы, выводить отладочную информацию и многое другое.
Ключевые особенности
- Реальная архитектура RISC-V — код компилируется стандартными тулчейнами (GCC, Clang)
- Модульная система — расширение функциональности через PCI-модули
- Реалистичная термодинамика — перегрев, троттлинг, аварийное отключение
- Разгон — возможность превысить штатные частоты с риском нестабильности
- Радиационная устойчивость — различные уровни защиты для работы вблизи суперматерии
Базовое использование
Начало работы
- Получите MCU — изготовьте на принтере схем или найдите готовую плату
- Установите батарею — используйте любую совместимую ячейку питания
- Подключите модули — вставьте нужные PCI-модули в плату
- Загрузите прошивку — используйте JTAG-программатор для загрузки ELF-файла
- Включите плату — используйте команду "Turn On" в контекстном меню
Управление
| Действие | Инструмент | Описание |
|---|---|---|
| Загрузка прошивки | JTAG-программатор | Загружает ELF-файл в память MCU (плата должна быть выключена) |
| Диагностика | Debugger Probe | Выводит полный дамп регистров процессора (PC, x0-x31, f0-f31, CSR и др.) |
| Настройка частоты | Мультитул | Позволяет задать рабочую частоту в допустимых пределах |
| Переключение OC | Отвёртка | Включает/выключает режим разгона |
| Ремонт радиационных повреждений | Нанопаста | Восстанавливает повреждённую полупроводниковую структуру, снижая накопленный TID |
| Блокировка прошивки | Сварочный аппарат | НЕОБРАТИМО прожигает OTP-предохранитель, запрещая перепрограммирование |
| Установка батареи | Батарея (в руке) | Вставляет источник питания |
| Установка модуля | PCI-модуль (в руке) | Подключает модуль расширения |
Контекстное меню
- Turn On — включить плату
- Turn Off — выключить плату
- Eject Battery — извлечь батарею
- Eject Module — извлечь выбранный PCI-модуль
Модели MCU
Стандартная линейка (Nanotrasen Cybernetics)
| Модель | RAM | Частота | PCI | Особенности |
|---|---|---|---|---|
| NCR-1000 | 64 KB | 0.25–2 МГц | 4 | Базовая модель для повседневных задач |
| NCR-2000 | 256 KB | 0.5–4 МГц | 8 | Удвоенная память, популярна в промышленности |
| NCR-4000 Pro | 1 MB | 1–8 МГц | 16 | Профессиональный уровень, лёгкая защита от радиации |
Энергоэффективная линейка (Whisper)
| Модель | RAM | Частота | PCI | Особенности |
|---|---|---|---|---|
| Whisper-LP8 | 32 KB | 0.125–1 МГц | 2 | Ультранизкое потребление, идеален для датчиков |
| Whisper-LP16 | 64 KB | 0.1–1.5 МГц | 4 | Расширенная автономность |
| Whisper-LP32i | 128 KB | 0.25–2 МГц | 6 | Промышленный класс, повышенная радиационная защита |
Оверклокерская линейка (Cybersun Fury)
| Модель | RAM | Частота | OC предел | PCI | Особенности |
|---|---|---|---|---|---|
| Fury-S1 Starter | 64 KB | 0.75–3 МГц | 4.5 МГц | 4 | Начальный уровень, защита RAM при OC |
| Fury-X1 | 256 KB | 1–4 МГц | 6 МГц | 8 | Разблокированные множители |
| Fury-X2 Extreme | 1 MB | 2–8 МГц | 12 МГц | 16 | Флагман линейки, требует серьёзного охлаждения |
Линейка Fury поставляется с включённым режимом разгона и защитой RAM. Превышение штатных частот значительно увеличивает тепловыделение и может привести к нестабильности!
Гибкая линейка (Flex)
| Модель | RAM | Частота | PCI | Особенности |
|---|---|---|---|---|
| Flex-V1 | 64 KB | 0.1–4 МГц | 6 | Широкий диапазон частот |
| Flex-V2 Pro | 256 KB | 0.125–6 МГц | 12 | Адаптивные системы |
| Flex-V3 Max | 768 KB | 0.0625–8 МГц | 18 | Максимальная гибкость, защита от EMP |
Специализированные модели
| Модель | RAM | Частота | PCI | Особенности |
|---|---|---|---|---|
| Aegis-H1 | 128 KB | 0.5–2 МГц | 4 | 90% защита от радиации, работает у суперматерии |
| RetroTech Z80-NT | 32 KB | 0.25–0.75 МГц | 2 | Ностальгия для энтузиастов, минимальное потребление |
PCI-модули
Text-to-Speech Module
Синтезирует речь из текста. MCU может "говорить" в игровой чат.
Serial Terminal Module
Терминал для отладки и взаимодействия с MCU. Открывается при использовании модуля в руке (при подключении к MCU). Позволяет просматривать вывод программы и отправлять данные.
Signaler Module
Радиомодуль для отправки и приёма сигналов. Позволяет удалённо активировать другие устройства или получать команды.
Инструменты
JTAG-программатор
Используется для загрузки ELF-файлов в память MCU. Плата должна быть выключена для программирования.
Debugger Probe
Компактный аппаратный отладчик для диагностики микроконтроллеров. Позволяет читать состояние регистров работающего MCU:
- Статус — Program Counter (PC), счётчик циклов, счётчик инструкций, уровень привилегий
- Общие регистры — x0-x31 (целочисленные регистры RISC-V)
- Регистры с плавающей точкой — f0-f31
- FCSR — регистр состояния FPU (режим округления, флаги исключений)
- Таймеры — mtime, mtimecmp
- CSR регистры — mscratch, mepc, mtval, mcause, mtvec
- Прерывания — состояние MIE и MIP (software, timer, external)
- Идентификация — mvendorid, marchid, mimpid, mhartid
Незаменим для низкоуровневой отладки и диагностики проблем с прошивкой.
Термальная система
MCU генерируют тепло при работе. Система охлаждения учитывает:
- Частоту работы — выше частота = больше тепла
- Загрузку процессора — активные вычисления греют сильнее
- Окружающую среду — в вакууме охлаждение практически отсутствует
- Разгон — потребление растёт квадратично относительно превышения частоты
Температурные пороги
Каждая модель имеет свои пороги (указаны типичные значения):
| Порог | Температура | Эффект |
|---|---|---|
| Троттлинг | ~65°C | Частота снижается вдвое |
| Аварийное отключение | ~90°C | MCU выключается |
| Повреждение | ~95°C | Начинается повреждение памяти |
При включённом режиме разгона (OC) аварийное отключение при перегреве отключено — плата продолжит работать до физического повреждения!
Индикация состояния
При осмотре MCU:
- Зелёный LED — нормальная работа
- Оранжевый мигающий LED — троттлинг активен
- Красный мигающий LED — требуется охлаждение перед запуском
- LED выключен — плата не работает
Разгон (Overclocking)
Для разгона:
- Переключите OC-джампер отвёрткой
- Установите частоту выше штатного максимума (до 150% от max)
Риски разгона
- Повышенное энергопотребление — батарея разряжается быстрее
- Увеличенное тепловыделение — требуется внешнее охлаждение
- Нестабильность памяти — случайное повреждение данных в RAM
- Отсутствие защиты от перегрева — плата не выключится автоматически
Модели без флага oc_ram_protection будут испытывать случайные ошибки памяти при разгоне!
Радиация
MCU подвержены воздействию ионизирующего излучения:
Мгновенные эффекты (при работе)
- SEU (Single Event Upset) — случайное изменение битов в памяти
- SEL (Single Event Latchup) — короткое замыкание с аварийным отключением и искрами
Накопительный эффект (TID)
Total Ionizing Dose накапливается даже при выключенной плате:
| Уровень TID | Эффект |
|---|---|
| 50% лимита | Лёгкое обесцвечивание, ×1.5 к вероятности ошибок |
| 75% лимита | Заметное обесцвечивание, ×2 к вероятности ошибок |
| 100% лимита | Необратимый выход из строя |
Защита от радиации
| Модель | Защита | TID лимит |
|---|---|---|
| Стандартные | 0–5% | 100 |
| Whisper-LP32i | 50% | 250 |
| Aegis-H1 | 90% | 1000 |
Ремонт радиационных повреждений
Накопленный TID можно снизить с помощью нанопасты. Каждое применение восстанавливает часть повреждённой полупроводниковой структуры, "залечивая" радиационно-индуцированные дефекты в оксидных слоях.
- При отсутствии повреждений — нанопаста не требуется
- При серьёзных повреждениях — может потребоваться несколько применений
- При полном восстановлении — устройство возвращается к нормальной работе
Нанопаста не поможет, если MCU уже вышел из строя (TID достиг 100% лимита).
EMP
Электромагнитные импульсы повреждают MCU в зависимости от силы импульса и уровня защиты (emp_hardening):
| Модель | Защита EMP |
|---|---|
| Базовые модели | 0 (уязвимы) |
| NCR-4000 Pro, Whisper-LP | 1 |
| Whisper-LP32i | 2 |
| Aegis-H1 | 4 |
При превышении защиты — необратимый выход из строя.
Физические повреждения
MCU разрушаются от:
- Попадания снарядов
- Взрывов
- Падения/ударов
- Расплавления (экстремальные температуры)
При разрушении батарея и модули могут уцелеть (50% шанс) и разлететься в стороны.
Защита прошивки
OTP (One-Time Programmable) предохранитель можно прожечь сварочным аппаратом:
Это действие НЕОБРАТИМО! После прожига MCU невозможно перепрограммировать.
Используется для:
- Защиты критических систем от саботажа
- Предотвращения несанкционированных модификаций
- Создания "запечатанных" устройств
Изготовление
Все MCU и модули изготавливаются на принтере схем (Imprinter) и протолате. Требуемые технологии и материалы зависят от сложности устройства.
Советы
- Начинайте с NCR-1000 или Whisper-LP8 для изучения системы
- Всегда тестируйте прошивку перед прожигом OTP
- Для работы у суперматерии используйте только Aegis-H1
- Serial Terminal незаменим для отладки
- Debugger Probe поможет диагностировать проблемы на уровне регистров
- В вакууме MCU перегревается почти мгновенно — обеспечьте атмосферу
- Линейка Flex идеальна для устройств с переменной нагрузкой
- Держите нанопасту под рукой при работе в зонах с повышенной радиацией
Разработка прошивок
Эта секция предназначена для разработчиков, желающих создавать собственные прошивки для MCU.
Архитектура
MCU используют 32-битный процессор RISC-V со следующими расширениями:
| Расширение | Описание |
|---|---|
| I | Базовый целочисленный набор инструкций |
| M | Умножение и деление |
| F | Операции с плавающей точкой одинарной точности |
| Zicsr | Инструкции для работы с CSR (Control and Status Registers) |
| Zicntr | Базовые счётчики производительности |
| Zbb | Базовые битовые манипуляции |
| Zba | Адресные битовые манипуляции |
Уровни привилегий: M (Machine) + U (User).
SDK
Официальный SDK написан на языке Zig и доступен на GitHub:
https://github.com/igorsaux/mcu-sdk
SDK содержит:
- Определения всех memory-mapped регистров
- Драйверы для всех PCI-устройств
- Утилиты для работы с таймерами, DMA, PRNG
- Примеры использования всех модулей
Карта памяти
| Адрес | Размер | Описание |
|---|---|---|
0x0000_1000 |
16 байт | Boot Info (частота CPU, размер RAM) |
0x0000_2000 |
8 байт | Сенсоры (температура, энергопотребление, флаги) |
0x0200_0000 |
— | CLINT (таймер, прерывания) |
0x0C00_2000 |
1 байт | PRNG (аппаратный генератор случайных чисел) |
0x1000_0000 |
— | UART |
0x1000_1000 |
— | DMA контроллер |
0x1000_2000 |
— | PCI контроллер |
0x2000_0000 |
— | Адресное пространство PCI-устройств |
0x8000_0000 |
varies | RAM (начало) |
Структуры данных
Boot Info
pub const BootInfo = extern struct {
cpu_frequency: u64, // Частота CPU в Hz
ram_size: u32, // Размер RAM в байтах
free_ram_start: u32, // Начало свободной RAM
};
Sensors
pub const Sensors = extern struct {
pub const Flags = packed struct(u8) {
overheat: bool = false, // Температура >= shutdown_temp
throttled: bool = false, // Частота снижена
_pad: u6 = 0,
};
temperature: i16 = 0, // Температура в °C
power_usage: u16 = 0, // Потребление в mWh/мин
flags: Flags = .{},
};
PCI-устройства
Обнаружение устройств
pub const Pci = extern struct {
pub const MAX_DEVICES: usize = 18;
pub const DeviceType = enum(u8) {
none = 0,
tts = 1,
serial_terminal = 2,
signaler = 3,
_,
};
pub const Entry = extern struct {
address: u32 = 0, // MMIO адрес устройства
len: u32 = 0, // Размер адресного пространства
ty: DeviceType = .none,
};
};
Для поиска устройства по типу:
inline fn findDevice(comptime T: type, comptime pci_type: sdk.Pci.DeviceType) ?T {
for (&sdk.pci.status().entries, 0..) |entry, slot| {
if (entry.ty == pci_type) {
return .{ .entry = entry, .slot = @intCast(slot) };
}
}
return null;
}
Text-to-Speech (TTS)
pub const Tts = extern struct {
pub const BUFFER_SIZE: usize = 128;
// Проверка готовности
pub inline fn isReady(this: *volatile Tts) bool;
// Произнести текст (предварительно записанный через DMA)
pub inline fn say(this: *volatile Tts) void;
// Подтвердить обработку события
pub inline fn ack(this: *volatile Tts) void;
};
Serial Terminal
pub const SerialTerminal = extern struct {
pub const INPUT_BUFFER_SIZE: usize = 1024;
pub const OUTPUT_BUFFER_SIZE: usize = 1024;
// Количество байт во входном буфере
pub inline fn len(this: *volatile SerialTerminal) u16;
// Отправить выходной буфер
pub inline fn flush(this: *volatile SerialTerminal) void;
// Включить прерывание при получении данных
// this.interrupts().on_new_data = true;
};
Signaler
pub const Signaler = extern struct {
// Установить частоту и код
pub inline fn set(this: *volatile Signaler, frequency: u16, code: u8) void;
// Отправить сигнал
pub inline fn send(this: *volatile Signaler) void;
// Проверка готовности (есть cooldown между отправками и установкой частоты с кодом)
pub inline fn ready(this: *volatile Signaler) bool;
// Включить прерывания
// this.interrupts().on_pulse = true; // При получении сигнала
// this.interrupts().on_ready = true; // Когда готов к отправке
};
DMA
DMA позволяет эффективно передавать данные между RAM и устройствами:
// Чтение из устройства в RAM sdk.dma.read(channel, device_address, destination_slice); // Запись из RAM в устройство sdk.dma.write(channel, device_address, source_slice); // Заполнение паттерном sdk.dma.fill(channel, device_address, pattern_slice, total_length); // Заполнение одним байтом (memset) sdk.dma.memset(channel, device_address, byte_value, length);
CLINT (таймер)
// Прочитать текущее время в тиках const ticks = sdk.clint.readMtime(); // Прочитать текущее время в наносекундах const ns = sdk.clint.readMtimeNs(); // Установить прерывание через N тиков sdk.clint.interruptAfter(ticks); // Установить прерывание через N наносекунд sdk.clint.interruptAfterNs(nanoseconds); // Ожидание прерывания (энергосбережение) sdk.arch.wfi();
PRNG
Аппаратный генератор случайных чисел:
// Получить один случайный байт const byte = sdk.prng.status().value; // Использовать как std.Random интерфейс const random = sdk.prng.interface(); const value = random.int(u32);
Пример: Sub-GHz передатчик
Разберём пример прошивки, реализующей интерактивный терминал для управления Signaler-модулем.
Структура программы
1. Обёртки для PCI-устройств
const SerialTerminal = struct {
entry: sdk.Pci.Entry,
slot: u8 = 0,
pub inline fn mmio(this: SerialTerminal) *volatile sdk.SerialTerminal {
return @ptrFromInt(this.entry.address);
}
};
const Signaler = struct {
entry: sdk.Pci.Entry,
slot: u8 = 0,
pub inline fn mmio(this: Signaler) *volatile sdk.Signaler {
return @ptrFromInt(this.entry.address);
}
};
Каждое устройство хранит свой PCI Entry и номер слота. Метод mmio() возвращает указатель на memory-mapped регистры устройства.
2. Инициализация
pub fn main() void {
// Поиск необходимых устройств
const terminal = findDevice(SerialTerminal, .serial_terminal) orelse return;
const signaler = findDevice(Signaler, .signaler) orelse return;
var shell = Shell.init(terminal, signaler);
shell.run();
}
Если устройства не найдены — программа завершается.
3. Главный цикл
pub fn run(this: *Shell) void {
// Включаем внешние прерывания
sdk.arch.Mie.setMeie();
// Подписываемся на события
this.terminal.mmio().interrupts().on_new_data = true;
this.signaler.mmio().interrupts().on_pulse = true;
this.printBanner();
while (true) {
// Проверяем события терминала
if (this.terminal.mmio().lastEvent()) |event| {
this.terminal.mmio().ack();
if (event.ty == .new_data) {
this.handleInput();
}
}
// Проверяем события сигналера
if (this.signaler.mmio().lastEvent()) |event| {
this.signaler.mmio().ack();
if (event.ty == .pulse) {
// Получен входящий сигнал!
this.print("\n");
this.printBox("SIGNAL RECEIVED", C.bgreen);
this.printPrompt();
this.flush();
}
}
// Ждём следующего прерывания (экономия энергии)
sdk.arch.wfi();
}
}
Вызов wfi() (Wait For Interrupt) переводит CPU в режим ожидания, значительно снижая энергопотребление.
4. Обработка ввода
fn handleInput(this: *Shell) void {
const bytes = this.terminal.mmio().len();
var input_buffer: [sdk.SerialTerminal.INPUT_BUFFER_SIZE]u8 = undefined;
// Читаем данные через DMA
sdk.dma.read(this.terminal.slot, 0, input_buffer[0..bytes]);
var input = input_buffer[0..bytes];
// Убираем trailing newlines
while (input.len > 0 and (input[input.len - 1] == '\n' or input[input.len - 1] == '\r')) {
input = input[0 .. input.len - 1];
}
this.executeCommand(input);
}
5. Отправка сигнала
fn cmdSend(this: *Shell) void {
if (this.cur_freq == 0) {
this.print("No frequency configured\n");
return;
}
// Проверяем cooldown
if (!this.signaler.mmio().ready()) {
this.print("Cooldown, wait...\n");
return;
}
// Устанавливаем параметры и отправляем
this.signaler.mmio().set(this.cur_freq, this.cur_code);
this.signaler.mmio().send();
this.send_count += 1;
}
6. Entry Point
comptime {
_ = sdk.utils.EntryPoint(.{
.stack_size = std.math.pow(u32, 2, 14), // 16 KB стека
});
}
Макрос EntryPoint генерирует стартовый код, настраивает стек и вызывает main().
ANSI-цвета в терминале
Serial Terminal поддерживает ANSI escape-последовательности:
const C = struct {
const reset = "\x1b[0m";
const bold = "\x1b[1m";
const dim = "\x1b[2m";
const orange = "\x1b[38;5;208m";
const green = "\x1b[32m";
const red = "\x1b[31m";
// ...
};
Компиляция
- Установите Zig (версия 0.15.2)
- Клонируйте SDK:
git clone https://github.com/igorsaux/mcu-sdk - Соберите пример:
zig build - Загрузите
zig-out/bin/firmware.elfчерез JTAG-программатор
Отладка
- Используйте Serial Terminal для вывода отладочной информации
- Проверяйте Sensors для мониторинга температуры и потребления
- Используйте
sdk.boot_infoдля получения информации о системе
Ограничения
- Максимальный размер ELF-файла ограничен конфигурацией сервера
- Доступ к памяти вне разрешённых регионов вызывает trap
- Некоторые syscall'ы имеют cooldown (TTS, Signaler)
- RAM не сохраняется между перезагрузками