Skip to content
Dmitry edited this page Sep 1, 2022 · 1 revision

Visual Studio Code

Visual Studio Code - популярная свободная кроссплатформенная IDE с открытым исходным кодом. Универсальная, легковесная, модульная, гибкая.
Предоставляет множество возможностей: подсветка синтаксиса и ошибок, подсказки и дополнение кода, сборка, отладка, поддержка git и др.
Кроме того, можно расширять функционал за счёт подключения плагинов.
VS Code можно использовать в том числе и для embedded разработки.


Оглавление


Установка VS Code

Сам VS Code можно установить из deb-пакета.

Установка плагина из .vsix архива:

Ctrl+Shift+P -> Install From VSIX

Сборка проекта

Сборка с существующим готовым makefile:
В tasks.json добавить task-и:

{
    "label": "Makefile",
    "type": "shell",
    "group": "build",
    "command": "make",
    "problemMatcher": [
        "$gcc"
    ],
},
{
    "label": "Makefile clean",
    "type": "shell",
    "group": "build",
    "command": "make",
    "args": ["clean"],
}

Запустить сборку: Ctrl+Shift+B

Можно дополнительно установить плагин "Makefile Tools". С ним запуск make немного удобнее, но сам makefile всё равно должен быть в проекте.

Ссылки Makefile Tools:

Конфигурация C/C++

Конфигурация находится в файле c_cpp_properties.json. Там настраивается платформа, компилятор, стандарт, Includes, Define, IntelliSense и др. Эта конфигурация (файл c_cpp_properties.json) опциональна.

Открыть конфигурацию:

Ctrl+Shift+P -> C/C++: Edit configurations (UI)
или
Ctrl+Shift+P -> C/C++: Edit configurations (JSON)

Можно прописать актуальный "compilerPath", а остальное оставить как есть.

IntelliSense можно настроить под конкретную платформу, которая не обязательно совпадает с host-платформой. Но похоже, платформы RISC-V пока нет.

Ссылки C/C++ configuration:

Отладка проекта

Необходимые плагины:

  • cpptools (C/C++)
  • Native Debug

Debug-конфигурации настраиваются в файле launch.json.

Плагин cpptools (C/C++)

(Плагин Native Debug тоже должен быть установлен и включен)

Взаимодействие между IDE и GDB осуществляется по GDB-MI интерфейсу - "MIMode": "gdb".
Сначала, как обычно нужно запустить OpenOCD. Затем к нему должен подключиться GDB.

Запустить OpenOCD можно двумя способами:

1 способ
Средствами плагина cpptools (более правильный способ)

С помощью настроек в launch.json:

"debugServerPath": "/home/user/tools_riscv/riscv-openocd-0.10.0-2020.12.1-x86_64-linux-ubuntu14/bin/openocd",
"debugServerArgs": "-f /home/user/workspace/riscpoa.cfg",
"miDebuggerServerAddress": "localhost:3333",

Но так не работает! Ошибка при запуске OpenOCD:

Unable to start debugging. Debug server process failed to initialize.
Или
Unable to start debugging. No process is associated with this object.

Выглядит так: Запускается OpenOCD и ждёт на порту :3333, но GDB почему-то не пытается подключаться, затем срабатывает таймаут VSCode. Как будто процесс уже занят OpenOCD, а для OpenOCD и GDB нужны разные процессы.

...        
1: (280) STDERR: Info : starting gdb server for riscv.cpu.0 on 3333
1: (280) STDERR: Info : Listening on port 3333 for gdb connections
    ...Ждёт подключения GDB...
1: (10245) <-logout
1: (10337) Send Event AD7MessageEvent

Это можно увидеть, если включить логгирование:

"logging": {"trace": true, "engineLogging": true, "exceptions": true},

Проблема описана тут:

2 способ
Отдельный task (более простой способ)

В tasks.json делается отдельный task: "startOpenocdHW" или "startOpenocdSpike".

А в launch.json делается настройка:

"preLaunchTask": "startOpenocdHW"
или
"preLaunchTask": "startOpenocdSpike"

Но при этом "miDebuggerServerAddress": "localhost:3333" должно быть убрано. При этом OpenOCD запускается в VSCode-терминале. Но, после того, как GDB-client остановлен (stop), OpenOCD остается запущенным.

Стартовые команды GDB-клиента можно задать с помощью "setupCommands". Можно использовать и команды для GDB-консоли, и команды в MI-формате.

Конфигурация для плагина cpptools (C/C++) в launch.json:

{
    //"preLaunchTask": "startOpenocdHW",
    "preLaunchTask": "startOpenocdSpike",

    "name": "cpptools Debug",
    "type": "cppdbg",
    "request": "launch",
    "cwd": "${workspaceFolder}",
    "program": "${workspaceFolder}/Debug/${workspaceFolderBasename}.elf",
    "MIMode": "gdb",
    "miDebuggerPath": "/home/user/eclipse_4.4.1/TOOLS/riscv64-unknown-elf-toolchain-10.2.0-2020.12.8-x86_64-linux-ubuntu14/bin/riscv64-unknown-elf-gdb",
    "miDebuggerArgs": "${workspaceFolder}/Debug/${workspaceFolderBasename}.elf --silent",
    "stopAtEntry": true,
    "stopAtConnect": true,
    "postDebugTask": "Termitate all tasks",

    "setupCommands": [
        {
            "description": "Enable pretty-printing for GDB",
            "text": "-enable-pretty-printing",
            "ignoreFailures": false
        },
        {
            "description": "...",
            "text": "-target-select remote localhost:3333",
            "ignoreFailures": false
        },
        {
            "description": "...",
            "text": "monitor reset halt",
            "ignoreFailures": false
        },
        {
            "description": "...",
            "text": "load",
            "ignoreFailures": false
        },
        {
            "description": "break main",
            "text": "break main",
            "ignoreFailures": false
        },
    ]
}

После подключения и запуска GDB, внизу во вкладке DEBUG CONSOLE доступна GDB-консоль. Видимо это не CLI CGB console, а MI-GDB. Перед каждой командой GDB-консоли нужно использовать "-exec", что неудобно. Пример: -exec p/x $pc

Дизассемблер:
Контекстное меню -> Open Disassembly View

Непонятно как смотреть память (скорее всего никак).

Registers:
Отображаются GPR, FP, CSR. fp-регистры отображаются также неправильно, как в eclipse, потому что это вопрос к GDB. Но в регистры почему-то нельзя писать вручную - ошибка "cannot be assigned to".

Если ставить breakpoint в GDB-консоли, то он не появляется в окне Breakpoints.

Отображаются шагание по asm-коду, но breakpoint-ы на asm-коде вручную не ставятся.

Не работает прерывание программы, когда программа исполняется в крутилке!

Плагин Native Debug

(Плагин cpptools тоже должен быть установлен и включен)

Тут не предусмотрено возможности запустить GDB-server (OpenOCD) средствами самого плагина, поэтому OpenOCD запускается с помощью настройки "preLaunchTask": "startOpenocd..." через task.
При этом OpenOCD запускается в VSCode-терминале. Но, после того, как GDB-client остановлен (stop/disconnect), OpenOCD остается запущенным.

В остальном всё получилось подключить по-штатному.

Конфигурация для плагина Native Debug в launch.json:

{
    //"preLaunchTask": "startOpenocdHW",
    "preLaunchTask": "startOpenocdSpike",

    "name": "Native Debug",
    "type": "gdb",
    "request": "attach",
    "gdbpath": "/home/user/eclipse_4.4.1/TOOLS/riscv64-unknown-elf-toolchain-10.2.0-2020.12.8-x86_64-linux-ubuntu14/bin/riscv64-unknown-elf-gdb",
    "debugger_args": ["--silent"],
    "cwd": "${workspaceRoot}",
    "executable": "${workspaceFolder}/Debug/${workspaceFolderBasename}.elf",
    "remote": true,
    "target": "localhost:3333",
    "postDebugTask": "Termitate all tasks",

    "autorun": [
        "monitor reset halt",
        "load",
        "break main",
    ]
}

Непонятно как смотреть регистры.

Непонятно как смотреть дизассемблер и память (скорее всего никак).

Во вкладке DEBUG CONSOLE доступна GDB-консоль. Она работает нормально, без "-exec".

Отображаются шагание по asm-коду.

Нормально работает прерывание программы, когда программа исполняется в крутилке.

Проблемы с breakpoint:
Если ставить breakpoint в GDB-консоли, то он не появляется в окне Breakpoints. В Си-коде breakpoint-ы ставятся вручную только если включен плагин cpptools. В asm-коде breakpoint-ы вручную не ставятся вообще.

Ссылки Debug

Запуск OpenOCD + Hardware (task)

В tasks.json делается отдельный task: "startOpenocdHW".

А в launch.json делается настройка:

"preLaunchTask": "startOpenocdHW"

Task в tasks.json для запуска OpenOCD+Hardware:

{
    "label": "startOpenocdHW",
    "type": "shell",
    "isBackground": true,
    "command": "/home/user/eclipse_4.4.1/TOOLS/riscv-openocd-0.10.0-2020.12.1-x86_64-linux-ubuntu14/bin/openocd",
    "args": ["-f", "${workspaceFolder}/scripts/riscpoa.cfg"],
}

Запуск OpenOCD + Spike (фоновые task-процессы)

Для работы с симулятором Spike нужно чтобы были одновременно запущены процессы Spike и OpenOCD.

Чтобы запускать несколько task-ов нужно использовать настройки "dependsOn" и "dependsOrder". "dependsOn" содержит список task-ов, от которых зависит текущий task. "dependsOrder" содержит способ выполнения task-ов - последовательно или параллельно.

При запуске нескольких фоновых task-процессов каждый такой task обязательно должен иметь "problemMatcher", чтобы можно было отследить, когда он "выполнится" - то есть когда изменяться вывод в терминал.
Простейший "problemMatcher":

"problemMatcher": {
    "pattern":[
        {
            "regexp": ".",
            "file": 1,
            "location": 2,
            "message": 3,
        }
    ],
    "background": {
        "activeOnStart": false,
        "beginsPattern": ".",
        "endsPattern": ".",
    },
}

Запуск Spike и OpenOCD:
Spike и OpenOCD оба запускаются фоново.
Есть три task-а: "startOpenocdSpike", "_startSpike" и "_startOpenocdSpike". "startOpenocdSpike" - головной task, зависит от task-ов "_startSpike" и "_startOpenocdSpike", и запускается в launch.json из "preLaunchTask".

Task-и в tasks.json для запуска OpenOCD+Spike:

{
    "label": "_startSpike",
    "type": "shell",
    "isBackground": true,
    "options": {
        "env": {
            "LD_LIBRARY_PATH": "/home/user/eclipse_4.4.1/TOOLS/riscv_tui_12.12.18/lib/"
        }
    },
    "command": "/home/user/eclipse_4.4.1/TOOLS/riscv_tui_12.12.18/bin/spike",
    "args": ["--rbb-port=9824", "-m0xc000000:0x90000000", "-H", "${workspaceFolder}/Debug/${workspaceFolderBasename}.elf"],
    "problemMatcher": {
        "pattern":[
            {
                "regexp": ".",
                "file": 1,
                "location": 2,
                "message": 3,
            }
        ],
        "background": {
            "activeOnStart": false,
            "beginsPattern": ".",
            "endsPattern": ".",
        },
    },
},
{
    "label": "_startOpenocdSpike",
    "type": "shell",
    "isBackground": true,
    "command": "/home/user/eclipse_4.4.1/TOOLS/riscv-openocd-0.10.0-2020.12.1-x86_64-linux-ubuntu14/bin/openocd",
    "args": ["-f", "${workspaceFolder}/scripts/spike.cfg"],
    "problemMatcher": {
        "pattern":[
            {
                "regexp": ".",
                "file": 1,
                "location": 2,
                "message": 3,
            }
        ],
        "background": {
            "activeOnStart": false,
            "beginsPattern": ".",
            "endsPattern": ".",
        },
    },
},
{
    "label": "startOpenocdSpike",
    "type": "shell",
    "isBackground": true,
    "command": "echo Start Spike and OpenOCD",
    "dependsOn": ["_startSpike", "_startOpenocdSpike"],
    "dependsOrder": "sequence",
    "problemMatcher": {
        "pattern":[
            {
                "regexp": ".",
                "file": 1,
                "location": 2,
                "message": 3,
            }
        ],
        "background": {
            "activeOnStart": false,
            "beginsPattern": ".",
            "endsPattern": ".",
        },
    },
}

Ссылки tasks:

Остановить все task-процессы

(Подходит и для OpenOCD - при работе с hardware, и для OpenOCD+Spike - при работе с симулятором Spike)

При завершении debug-сессии, после того, как GDB-client остановлен (stop/disconnect), task-процессы OpenOCD (при работе с hardware) или OpenOCD+Spike (при работе с симулятором Spike) остаются запущеными.
Их можно останавливать руками в терминале по Ctrl+C, но это не очень удобно.

Можно автоматически убивать эти task-процессы по завершению Debug-сессии (stop/disconnect) - по "postDebugTask".

Чтобы убить все task-процессы нужно выполнить внутреннюю VSCode команду "command:workbench.action.tasks.terminate" с аргументом "terminateAll".

В tasks.json добавить отдельный task:

{
    "label": "Termitate all tasks",
    "type": "shell",
    "command": "echo ${input:terminate}",
}

Также в tasks.json добавить секцию "inputs":

"inputs": [
    {
        "id": "terminate",
        "type": "command",
        "command": "workbench.action.tasks.terminate",
        "args": "terminateAll"
    }
]

А в launch.json сделать настройку:

"postDebugTask": "Termitate all tasks"

Похоже, что такое сочетание task+input - единственный способ выполнить внутреннюю VSCode команду "command:..." (не shell-команду) из task.

Visual Studio Code Variables Reference