Geek went Freak!

Ubuntu: Cross-compile baremetal Cortex-M assembly program

In this post, we will cross-compile a small baremetal program for ARM processor on an Ubuntu machine.

ARM cross-compile toolchain

First step is to install the ARM cross-compiler toolchain. Luckily Ubuntu already has it in its software repository. Execute the following command in the terminal to install ARM EABI compatible tool chain:

sudo apt install gcc-arm-none-eabi

Check the version of the installed compiler using the following command:

arm-none-eabi-gcc --version

Sample baremetal program

Now, we need a sample baremetal program to compile. I have choosen a very simple assembly program.

startup.S

.global _start
_start:
  B _reset /* Reset */
  B . /* Undefined */
  B . /* SWI */
  B . /* Prefetch Abort */
  B . /* Data Abort */
  B . /* reserved */
  B . /* IRQ */
  B . /* FIQ */

_reset:
  mov r1, #10
  ldr r0, =0x20000000
  str r1, [r0]
  ldr r2, [r0]
  B .

Assemble

Lets assemble the assembly file using GCC assembler.

arm-none-eabi-as -mcpu=cortex-m3 -g startup.S -o startup.out

Link

Finally lets link the object file startup.out generated by the assembler.

arm-none-eabi-ld -Ttext=0x0 -o startup.elf startup.out

Note: Since the program is very simple, I haven’t used any linker script here.

-Ttext=0x0 option instructs the linker to use 0x0 as the starting address of the instructions.

Ubuntu: Emulate baremetal Cortex-M program

In this post, we will emulate a baremetal program for Cortex-M on Ubuntu PC.

Installation

We will need

  1. QEMU emulator for ARM
  2. GDB

Fortunately both of them are available through Ubuntu software repository.

Install them using the following command:

sudo apt install qemu-system-arm
sudo apt install gdb-arm-none-eabi

Emulation

We will use QEMU for emulation. GDB is used to control and inspect QEMU.

Launch QEMU

qemu-system-arm -monitor stdio -machine lm3s811evb -cpu cortex-m3 -s -S -kernel startup.elf
  • -monitor stdio
    Access QEMU HMI monitor from terminal
  • -machine lm3s811evb -cpu cortex-m3
    Select machine lm3s811evb and CPU cortex-m3
  • -s
    Start GDB server on localhost:1234
  • -S
    Don’t start execution. This is used so we can start and control execution from GDB
  • -kernel startup.elf
    The executable file to execute

Launch GDB client

arm-none-eabi-gdb startup.elf

You should now be in GDB interactive console.

Connect to QEMU

Lets connect to GDB server hosted by QEMU from the GDB client

target remote localhost:1234
load

Run the program

continue

Inspect

Press <Ctrl-c> to stop execution.

Check registers

In lines 13, 14 and 16, we update registers r1, r0 and r2 respectively. They should hold values 0x20000000, 10 and 10 respectively.

info reg r0 r1 r2

Should print:

r0 0x20000000 536870912
r1 0xa 10
r2 0xa 10

Check memory

We write value 10 to memory address 0x20000000. Lets check if that worked correctly:

x/4wx 0x20000000

0x20000000: 0x0000000a 0x00000000 0x00000000 0x00000000

STM32F: Calculating APB clock frequency (PCLKx)

The clock frequency of APB is determined through a long sequence of prescaling and selecting as shown in the image below:

APB clock source flow

Note: In this post, external oscillator and PLL are used to select SYSCLK.

Term Explanation
HSE External clock frequency
PLLM PLL division factor
PLLN PLL multiplication factor
PLLP SYSCLK division factor
HPRE AHB prescaler
PPREx APBx prescaler

PLL

$$tex fVCO = \frac{HSE}{PLLM} * PLLN tex$$

SYSCLK

$$tex SYSCLK = \frac{fVCO}{PLLP} tex$$

AHB clock

$$tex HCLK = \frac{SYSCLK}{HPRE} tex$$

APB clock

$$tex PCLKx = \frac{HCLK}{PPREx} tex$$

An example

Lets consider an external oscillator of frequency 16MHz. Lets say we need a SYSCLK and HCLK of 168MHz.

>> HPRE = 1

This leaves us with,

$$tex \frac{PLLN}{PLLM * PLLP} = \frac{SYSCLK}{HSE} tex$$
$$tex \frac{PLLN}{PLLM * PLLP} = 10.5 tex$$

We can settle with the following values:

>> PLLN = 336
>> PLLM = 16
>> PLLP = 2  

Now, for a PCLKx of 42MHz, we can pick,

>> PPREx = 4  

STM32F: What is PCLK and fPCLK

Couple of peripherals like SPI and UART derive their clock from the fPCLK. So what clock is PCLK and what frequency is fPCLK?

PCLKx is the clock of the corresponding APB peripheral X. For example:

Clock Bus
PCLK1 APB1
PCLK2 APB2

Note: Similarly, HCLKx is the clock of the corresponding AHB peripheral X. For example:

Clock Bus
HCLK1 AHB1
HCLK2 AHB2

So, when SPI2 says it derives its clock for fPCLK, what it means is the clock of its APB bus. In STM32F407, SPI2 is on APB1. So this makes its fPCLK fPCLK1.

@android:id vs @id

@id

This refers to the ids defined in the application, either in layouts itself using @+id/<id-nam> or in ids.xml. These ids are exposed to Java through R.id.

@android:id

This namespace contains builtin ids from android itself. These ids are exposed to Java through android.R.id.

Ids in Android layout

The layouts are designed in an XML backed layout editor. When the project is built, android tools compile the XML layouts and other resources into corresponding Java code that can later be inflated.

Each view in the layout must have an unique Id to be able to reference them. Here are some scenarios where giving an unique Id to a view in Layout is required:

  1. To cross reference one view from another view in the layout

  2. To query for the view and get a reference to it from Java code

Id namespace

In android, the Ids have no namespacing. Ids from all layouts are clumped into the same R.id namespace.

android.R.id namespace

android.R.id is namespace through which android exposes the internal Ids. These Ids are defined by/in android itself.

Define and Id in Layout

<TextView
    android:id="@+id/txtCounter"
    android:text="TextView"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_weight="1" />

Note: The + following @ means that android should create an Id with the name txtCounter if it doesn’t exist already. If such Id exists, the existing Id is used.

Define Ids in ids.xml

One can manually define the required Ids in ids.xml using item tag with type="id". The name property defines the name of the defined id.

<item name="txtCounter" type="id"/>

The Id can be later used in layouts and Java.

<TextView
    android:id="@id/txtCounter"
    android:text="TextView"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_weight="1" />
findViewById(R.id.txtCounter);

Loading sklearn datasets

Datasets or toy datasets, as sklearn calls it, reside in sklearn.datasets package.

A dataset can be loaded by using sklearn.datasets.load_*() function.

In this post, let us consider iris dataset. iris dataset can be loaded using sklearn.datasets.load_iris().

By default sklearn provides datasets as sklearn.datasets.base.Bunch.

from sklearn.datasets import load_iris
irisData = load_iris()
print(type(irisData))

The Bunch structure is convenient since it holds data, target, feature_names and target_names.data and target fields are both numpy.ndarray containing independent and dependent variables respectively.

from sklearn.datasets import load_iris
irisData = load_iris()
print(type(irisData))
print(type(irisData.data), type(irisData.target))
print(irisData.feature_names)
print(irisData.target_names)
print(irisData.data)
print(irisData.target)

sklearn datasets’ load methods can also provide the features and targets directly as numpy.ndarray by using the return_X_y argument.

from sklearn.datasets import load_iris
irisData = load_iris(return_X_y=True)
print(irisData[0])
print(irisData[1])

Loading statsmodels datasets

statsmodels comes with some sample datasets built-in. In this tutorial, we are going to learn how to use datasets in statsmodels.

The built-in datasets are available in package statsmodels.api.datasets.

In this tutorial lets explore statsmodels.api.datasets.fair.

One can load data from the datasets either as numpy.recarray or pandas.core.frame.DataFrame.

statsmodels.api.datasets.fair.load().data provides data as numpy.recarray.

statsmodels.api.datasets.fair.load_pandas().data provides data as pandas.core.frame.DataFrame.

The following code will display the dataset as table in ipython notebook.

import statsmodels.api as sm
dta = sm.datasets.fair.load_pandas().data
dta

Windows: build C/C++ code from command line

To build C/C++ code from command line, the cl.exe binary must be in the path environment variable.

Lets try to compile a single code:

int add(int a, int b) {
  return a + b;
}

int main(int argc, char const *argv[]) {
  int temp = add(5, 15);
  return 0;
}
cl.exe simple.c

That would throw the following error:

LINK : fatal error LNK1104: cannot open file ‘LIBCMT.lib’

Opps!

The reason is, just having the cl binary in the path isn’t enough. cl expects some configuration from the environment variables. This can be setup by executing the following command:

"C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7/Tools/VsDevCmd.bat"

Now, the compilation should be smooth:

cl.exe simple.c

Note: This only compiles to 32-bit binaries.

LPC810: UART baudrate configuration

This post is about UART baudrate configuration in LPC810.

FRG and BRG can be used to derive the desired baudrate.

Block diagram

Setup BRG

BRG should produce output clock rate 16 times the desired baudrate. The input clock to BRG is BASECLK.

$$tex BRGVAL = \frac{BASECLK}{16 * Baudrate} tex$$
//Setup BRG
LPC_USART0->BRG = MAINCLK / (16 * aBaudRate);

Setup FRG

Output clock from FRG is common for all UART peripherals.

$$tex UARTFRGMUL = \frac{FRGINCLK*(UARTFRGDIV+1)}{16 * Baudrate * BRGVAL} - (UARTFRGDIV+1) tex$$

It is easier, if we set UARTFRGDIV to 255.

//Set up FRG
LPC_SYSCON->UARTFRGDIV = 0xFF;
LPC_SYSCON->UARTFRGMULT = ((MAINCLK * 256) / (16 * aBaudRate * LPC_USART0->BRG))
    - 256;

Setup clock to FRG

$$tex UARTDIV = \frac{MAINCLK}{FRGINCLK} tex$$
//Setup clock to FRG
LPC_SYSCON->UARTCLKDIV = UARTCLKDIV;