Guide to MCU

Материал из Animus
Перейти к навигацииПерейти к поиску

MCU (Microcontroller Unit) — программируемые микроконтроллеры, позволяющие игрокам создавать автоматизированные устройства с произвольной логикой. MCU используют полноценный интерпретатор 32-битного процессора RISC-V с расширениями IMFZicsrZicntrZbbZba и уровнями привилегий M+U.

Обзор

MCU представляют собой компактные вычислительные платы, которые можно программировать с помощью загрузки ELF-файлов через JTAG-программатор. К MCU подключаются различные модули расширения через PCI-слоты, позволяя взаимодействовать с окружающим миром: синтезировать речь, отправлять радиосигналы, выводить отладочную информацию и многое другое.

Ключевые особенности

  • Реальная архитектура RISC-V — код компилируется стандартными тулчейнами (GCC, Clang)
  • Модульная система — расширение функциональности через PCI-модули
  • Реалистичная термодинамика — перегрев, троттлинг, аварийное отключение
  • Разгон — возможность превысить штатные частоты с риском нестабильности
  • Радиационная устойчивость — различные уровни защиты для работы вблизи суперматерии

Базовое использование

Начало работы

  1. Получите MCU — изготовьте на принтере схем или найдите готовую плату
  2. Установите батарею — используйте любую совместимую ячейку питания
  3. Подключите модули — вставьте нужные PCI-модули в плату
  4. Загрузите прошивку — используйте JTAG-программатор для загрузки ELF-файла
  5. Включите плату — используйте команду "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)

Для разгона:

  1. Переключите OC-джампер отвёрткой
  2. Установите частоту выше штатного максимума (до 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";
    // ...
};

Компиляция

  1. Установите Zig (версия 0.15.2)
  2. Клонируйте SDK: git clone https://github.com/igorsaux/mcu-sdk
  3. Соберите пример: zig build
  4. Загрузите zig-out/bin/firmware.elf через JTAG-программатор

Отладка

  • Используйте Serial Terminal для вывода отладочной информации
  • Проверяйте Sensors для мониторинга температуры и потребления
  • Используйте sdk.boot_info для получения информации о системе

Ограничения

  • Максимальный размер ELF-файла ограничен конфигурацией сервера
  • Доступ к памяти вне разрешённых регионов вызывает trap
  • Некоторые syscall'ы имеют cooldown (TTS, Signaler)
  • RAM не сохраняется между перезагрузками

НачинающимИнтересноеПрофессииРуководства

Ролевая игра

Руководство по отыгрышу ролиРуководство по отыгрышу роли для продвинутыхРуководство по заполнению окна Relations и созданию связейПсихология убийстваПсихологические заболеванияЗнания персонажа

Режимы игры

Агент СиндикатаОперативник СиндикатаСнаряжение СиндикатаРеволюцияКультВампирВолшебникГенокрадКосмический Ниндзя

Инженерное дело

Руководство инженераРуководство атмосферного техникаИскусство взломаКонструированиеТехнологии связиПродвинутое конструирование

Медицинские руководства

Медицина ХирургияВирусология ХимияМедицинский справочникРадиация

Научно-исследовательские проекты

ИсследованияРабота с газамиКсенобиологияКсеноархеологияРобототехникаКсеноботаникаИнтегрированные схемы

Служба безопасности

Руководство службы безопасностиСвод космических законовОружие

Прочее

Как грамотно писатьПравильная работа с документамиОфициальные бланки документов НТКак готовить еду ОниксаРуководство по напиткам

Руководства для желающих помочь

Учимся программировать в BYONDРисуем спрайты