blob: 83a374ea824f540b5887469364dc882e62417516 [file] [log] [blame]
Design of Newt System
1. Introduction
The newt tool allows for building and distributing embedded projects. It's
goal is to make it simple for developers to create a source project and
manage that project, with an embedded twist. In embedded systems, the core
operating system is often built and distributed along with system libraries
and applications. Additionally, there is a fair amount of complexity in
managing driver interfaces, board support packages, and both development and
production builds. Right now, all of this is done for every project, and
everybody does it in a different way.
Newt introduces the following core concepts:
Repository
The base directory of your embedded software. A repository can contain
multiple projects, and reflect multiple end products. It is meant to be
a logical collection of your source code.
Project
Projects represent the individual build configurations of your embedded
system. The project files are what dictate the resulting binary that is
generated.
Package
Packages represent libraries that are distributed. Packages have multiple
types (e.g. "bsp", "library", "os") that represent their function within
the newt system.
In order to bring sanity to your embedded life (lonelieness and cat obsession
left as an exercise for the user), Newt allows you to:
Build architecture specific binaries
Newt contains the ability to manage and build for multiple different CPU
architectures, project definitions and board support packages.
Manage your drivers, board support and libraries
Newt provides an infrastructure that helps you manage board capabilities,
and drivers based upon what low-level features your libraries and projects
use.
Distribute Binaries
Newt provides the ability to generate signed firmware images, suitable for
booting with n-boot. Flash can also take n-boot images + project images
and create a full flash map.
Distribute Source
Newt makes it easy to distribute and download libraries. By providing a
clear interface to the lower layer drivers, and dependency checking across
both architecture and board support package: newt makes it easy to
develop reusable software components across embedded projects.
Test
Newt provides a test framework that allows you to easily define regression
and unit tests, and have them run automatically.
2. Usage
To get started with newt, first create a new repository:
$ newt create repo <fw>
This creates a new repository in the directory specified by <fw>.
The repository has the following contents:
$ newt repo create test_repo_1
Repo test_repo_1 successfully created!
$ cd test_repo_1/
$ tree
.
├── compiler
├── hw
│   └── bsp
├── libs
├── project
└── repo.yml
The arch/ directory contains the architecture specific information for the
new repository. By default, the sim architecture and compiler is installed:
which allows you to build new projects and software on your native OS and
try it out.
The newt tool also creates a directory .<fw> -- this contains the
environment and configuration information for the current project. More
information about this directory is contained in Appendix A.
Once you have a repo, you can enter that repo, and begin composing your
project.
The first step to composing a project is to setup a build environment and
board support package for that product. There are a set of board support
packages and architectures available at the following URL:
http://www.github.com/XXX.
Let's start with the board support package for the STM32-E407, which is a
test board for the ARM Cortex-M4, provided by Olimex
(https://www.olimex.com/Products/ARM/ST/STM32-E407/open-source-hardware).
To install the Olimex board support package, cd into the <fw> directory
and type:
$ newt install bsp http://www.github.com/XXX/STM32-E407
Downloading Board Support Definition from
http://www.github.com/XXX/STM32-E407 ... ok
Board Support Definition STM32-E407 requires ARM architecture to be
installed (http://www.github.com/XXX/ARM), is that OK? [Y/n] y
Downloading ARM architecture support ... ok
Successfully installed BSP for STM32-E407!
$
The "install bsp" command goes out and installs the board support package
defined by the URL provided in the link. In this case, the bsp is defined
on github. Once the newt tool downloads the BSP, it notices that the BSP
requires the ARM architecture to be installed, and its currently not in the
workspace. Newt will then download the definiton from the github URL, and
install the necessary architecture information.
After the dependency to the compiler has been fufilled, the bsp support
packages are installed into the working directory. After the command
has finished, the following directory structure should exist:
$ tree
.
└── arch/
└── sim/
└── compiler/
└── compiler.yml
└── arm/
└── compiler/
└── arm-gcc
└── bin
└── arm-gcc
└── arm-as
└── .. <snip> ..
└── compiler.yml
└── include/
└── rt_CMSIS.h
└── mcu/
└── stm32/
└── arch_gpio.c
└── arch_gpio.h
└── .. <snip> ..
└── bsp
└── stm32-e407
└── layout
└── debug
└── stm32-e407.lds
└── prod
└── stm32-e407.lds
└── stm32-e407.yml
└── bsp_flash_layout.h
└── bsp_boot.s
└── .. <snip> ..
└── .<fw>
└── <fw>.yml
└── <fw>.db
As you can see, a couple of new directories were created:
arch/arm
Definition files specific to the ARM architecture. This is directory
contains generic definition files across multiple MCU architectures.
arch/arm/mcu/stm32
This directory contains definition files specific to the
STM32 MCU. This includes things like SPI and UART definitions.
arch/arm/compiler
The compiler for the ARM architecture definition. This includes an
up-to-date, tested version of the arm-gcc compiler. By default Newt uses
this compiler, however it can be overridden to use a system compiler as
well.
bsp/stm32-e407
This directory contains the board support files specific to the
STM32-E407 development board.
bsp/stm32-e407/layout
This contains the memory layouts for build layouts, which can often be
different for debug and production builds (e.g. run out of SRAM for debug
and Flash for prod.)
The next step is to create a project that will build on the test board. Let's
say the first project is "blink_leds" a project that will load onto the
STM32-E407 and blink LED1.
To create the project, in the main directory enter:
$ newt create project blink_leds
Creating project blink_leds in project/ ... ok
Creating project scaffolding for blink_leds ... ok
$
This will create the following directory structure:
$ tree
..<snip>.. (same as previous step)
└── project
└── blink_leds
└── blink_leds.yml
└── blink_leds.c
$
The file blink_leds.c will contain base scaffolding in order to build:
#include <newt/core.h>
int
main(int argc, char **argv)
{
while (1) { /* your code here */ }
return (0);
}
In order to blink LEDs, the next step is to install the LED package. The LED
package contains a standard API definition for controlling board LEDs, and
the architecture and board specific definitions for LEDs.
$ newt install driver http://www.github.com/XXX/led-driver
Downloading driver ... ok
Checking for necessary bsp support, bsps found: sim, STM32-E407 ... ok
Installing driver ... ok
$
Installing the driver will create the following directories in the root
directory of your project:
$ tree
..<snip>.. (same as previous step)
└── drivers
└── led-driver
└── led-driver.yml
└── src
└── led-driver.c
└── include
└── led-driver.h
└── bsp
└── stm32-e407
└── stm32-e407.c
└── stm32-e407.h
This driver will then be accessible in your project. The next step is to
enable it in your project definition, in order to do that, enter:
$ newt project blink_leds use driver led-driver
Enabling led-driver in blink_leds project.
Now edit project/blink_leds/blink_leds.c, and write the following code:
#include <newt/core.h>
#include <led_driver/led_driver.h>
int
main(int argc, char **argv)
{
int toggle = 0;
while (1) {
if ((toggle++ % 2) == 0) {
led_off(LED_DRIVER_LED1);
} else {
led_on(LED_DRIVER_LED1);
}
}
return (0);
}
Once this file is edited, you can now build your project. In order to
build an image that can be loaded on the board, create a build definition:
$ newt build_def create blink_leds_arm_build
$ newt build_def edit blink_leds_arm_build set bsp=stm32-e407
$ newt build_def edit blink_leds_arm_build set project=blink_leds
$ newt build_def edit blink_leds_arm_build set layout=debug
This build definition is stored in the project build definition, and can now
be referenced with the newt build command:
$ newt build blink_leds_arm_build
Building project blink_leds with bsp stm32-e407, layout debug ... ok
$
In order to see the output of the build, check the bin/ directory in the base
directory of your repo:
$ cd bin; tree
└── bin
└── blink_leds
└── stm32-e407
└── blink_leds.elf
└── blink_leds.map
In order to load these files onto your board using GDB, please see the
Debugger Support section of this document.
3. Packages
Newt distributes libraries in the form of source packages, making it easy to
bundle and reuse source code across multiple repositories and projects.
Outside of the core architecture & board specific setup, all source code in
a newt project should be encapsulated within a package.
A package has the following directory structure:
$ cd pkg/os; tree
└── os
└── os.yml
└── include
└── arch
└── sim
└── os_arch.h
└── arm
└── os_arch.h
└── os
└── os_mutex.h
└── os_sem.h
└── os.h
└── src
└── arch
└── sim
└── os_arch_sim.c
└── arm
└── os_arch_arm.c
└── os_mutex.c
└── os_sched.c
└── test
└── os_mutex_test
└── os_mutex_test.c
Building a Package
In order to build a package, either:
a) Include it in a project definition, in which case it will be built as a
library and linked with the main application.
b) Build it with target "test" in which case the specified regression test
will be executed. If no regression test is specified, all regression tests
will be run.
Running a Package's Unit Tests
To run a package's unit tests, cd into the package directory and run newt build
test:
$ cd pkg/os
$ newt build test
Building regression tests in OS
Building OS ... ok
Building Regression Tests ... ok
Running test os_mutex_test ... ok
$
Including a Package in a Project file