Designing and building an embedded Linux computer

Embedding a microprocessor capable of running Linux in an embedded project simplify a lot of tasks and operations and opens up a lot of opportunities to perform advanced tasks. By basing applications upon a full-fledged operating system processes involving file systems, networking, graphical interfaces etc. can relatively simply be implemented by using the appropriate drivers. Development can be done on a higher level and becomes similar to writing applications for modern computers.

Article summary: I designed an electronics circuit with a Microchip Microprocessor and construct it by laying it out on a printed circuit board and ordering the parts. After mounting and soldering all components I compile staged boot-loaders and a custom Linux distribution that the microprocessor runs. I configure the Linux system to automatically connect to my home-network and to setup an SSH server which allows me to connect to it from any local PC.

The complexity of this project does not come from the circuit solving problems such as with my USB headtracker but instead comes from the complexity of simply designing and building a device capable of running Linux and compiling the necessary software to make it work.

emblinux-768x576.jpg

This is the eventual finished and working device.

This post is split into parts.

  1. A short introduction to embedded microprocessors and my choice thereof.

  2. Designing the circuit and the design considerations I’ve dealt with and the compromises I’ve made.

  3. Building the physical device.

  4. Embedded Linux and how a custom Linux distribution was compiled for this specific device.

Part 1. Embedded microprocessor

There are are lot of theoretical differences between a microprocessor and a microcontroller considering the architecture and their part in the composition of a computer system. The modern practical distinction however is pretty much that microprocessors are more powerful computers but requires more external resources. Microcontrollers are instead typically designed to be a relatively cheap and simple to include stand-alone package that is designed for low power and rugged applications. Modern microcontrollers can be quite advanced and the practical difference between using them and microprocessors has gotten smaller.

One of the obvious differences designing a circuit for a microprocessor instead of a microcontroller is the presence of external memory, often multiple required voltage levels, power sequencing requirements and more stringent bypassing requirements. A microcontroller has built in flash memory that one flashes the programs to and simpler built in SRAM to execute from. A microprocessor requires external memory. For example one might instead use an SD-card to store the program data on and external DRAM to execute instructions from.

I chose to base this project on a the Microchip SAM9X60 microprocessor. I chose this microprocessor for two main reasons. The first reason is that the Microchip documentation for embedded Linux applications was relatively easy for me to understand and that the microprocessor is available in a System in Package (SiP) that includes DRAM. Looking ahead at this project the biggest challenge seemed to me to be able to get Linux up and running with the necessary drivers and bootloaders and I greatly appreciate as good documentation as possible. The included DRAM meant the circuit would be simpler to design and less things could go wrong. DRAM is one of the more time consuming and difficult components to include as its high speed demand careful PCB design. I definitely think that my cautious planning paid off as the hardware worked on the first try.

The Microprocessor SiP required 3 different voltage levels, 1.2V , 1.8 V and 3.3 V with a specific sequencing. The sequencing means that the voltages should become available on power-up in a specific order and with specific timings. This is typically achieved by including a Power-Management Integrated Circuit (PMIC). The PMIC can also be used to handle cases such as sleep-mode and not only power sequencing. It also requires an external data storage to read the program data from and I used an SD card for this as it’s simple and convenient to modify its contents.

Part 2. Designing the circuit

The design process was based upon reading the datasheet and looking at any reference designs implementing a similar MPU (none existed for the exact version I chose). I started by implementing the absolute necessities to get it up and running and then implementing the peripherals I wanted.

sam9x60 ballout

The SiP package ballout. Each node in the image corresponds to a tiny conductive ball on the backside of the package. The appropriate connections to these needs to be made. There are a lot of ground connection and power connections denoted by the black- and red colors.

My peripheral requirements were an SD-card connection, three USB-connections (2 host, 1 device), an RJ45 connection, UART/debugging connections and some general purpose input/output pins. I also included an RGB-system LED and some power status-LEDs to simplify eventual troubleshooting.

I used KiCad as the CAD software as I have a lot of experience using it from other projects. It’s free, open source and you can do a lot with it. Below is an exported SVG from one of my schematic pages about the power management.

POWER_MANAGEMENT-POWER-MANAGEMENT.svg

The power management schematic page.

I created a schematic symbol for the microprocessor package using the datasheet and split it into different submodules to organized the connections. This makes the schematic a lot more readable as there are a lot of connections. The microprocessor connections are therefore split between multiple schematic pages.

As can be seen in the schematic-page above the power supply is quite simple. I generated the different voltage rails not by using a PMIC but instead by using linear voltage regulators. I did this after carefully reading the datasheet about the power sequencing requirements and I noticed it was possible to satisfy the requirements using linear voltage regulators instead of a PMIC. This is absolutely not recommended but works for a simple hobby project like mine, the benefit is that a lot less components is needed and it’s significantly cheaper. That said both the datasheet recommends a PMIC and reference designs implement a PMIC.

The power supply is implemented as two chained linear voltage regulators. One main regulator that converts 5 V from USB power to a 3.3 V rail. The 3.3 V rail is then used to convert voltages to 1.8 V and 1.2 V using a dual-output linear voltage regulator.

MPU_CONFIG-MPU-CONFIG.svg

The clock management schematic page.

The schematic-page above shows the connection to the crystal oscillator I use to supply the processor with a stable frequency that can be used to generate all other system clocks using a PLL (phase locked loop). I used a 16 MHz crystal as this is a convenient frequency to generate all system clocks. For example USB 2.0 runs with a 48 MHz clock which is simply generated by multiplying the 16 MHz base-frequency here by 3. The choice of crystal oscillator frequency is a bit arbitrary and many frequencies will work. A more robust alternative to a crystal oscillator is using a MEMS-device and is what all the reference designs used.

Note that the choice of not using a PMIC and the specific oscillator frequency are both choices that needs to be reflected when compiling the bootloaders and Linux.

PCB_CONNECTORS-PCB-CONNECTORS.svg

The interface schematic page.

The last page of the schematic is shown above and shows most of the peripheral connections. As I wanted an ethernet connection I needed to include an ethernet PHY. It is the most complicated component outside of the microprocessor package. The SD-card connections, USB and RJ45 (ethernet) are all high-speed connections and one needs to consider impedance matching and length-matching the traces. If RAM was an external component those would need to be impedance- and length matched as well. I’ve included notes about the appropriate impedances for single-ended and differential traces.

I will note that the one critical error I made I did here. The RJ45 connector I used does not work with my choice of ethernet PHY. I had a naive understanding of the RJ45 connector which is quite complicated and uses magnetics to transfer the data between the cable and the connector. My mistake was using a cheap RJ45 connector with no center taps on the internal transformers. A proper connector is quite expensive compared to other components and I tried to keep cost down too much.

The workflow designing the schematic is pretty simple. It did take a few days but mostly consisted of referencing the datasheet how to implement the appropriate peripherals.

Part 3. Laying out the circuit board

To me this is the most fun part of any electronics project. After designing everything it finally starts to materialize to an actual circuit. Laying out all the components and connecting all traces efficiently is a fun puzzle!

I used a 4-layer board. The two inner layers were only used as power- and ground planes, the two outer layers we’re used for the other component connections.

sam9x60_pcb.png

PCB board layout.

The image above shows the layout I finalized on. The two inner layers are not shown. The red colors are front-side copper traces and pads and the green colors for the back side. The small gray circles are vias which are traces that pass through the circuit board to connect to other layers. The yellow circles with black centers are pins that pass through the board, such as header connectors. Most components are surface mounted.

Some traces are a bit wavy, for example to the SD-card connector. This is done to make all traces the same length which means that electrical signals will take roughly as long to propagate through them and is a good practice when designing high speed traces.

sam9x60_pcb_power_planes.png

PCB power planes.

The image above shows the power plane in magenta. It is split in four parts as there are three different power rails excluding ground, 5V, 3.3 V, 1.8 V and 1.2 V. Most of the circuit uses 3.3 V and it uses the most space followed by 5 V. The smaller areas close the the microprocessor in the center are the 1.8 V and 1.2 V power planes.

I ordered this PCB from a PCB manufacturer and the components I used in my design from Mouser. I built this circuit in my small electronics lab at home. A component that worried me was the microprocessor. All those small conductive balls underneath its package need to make an electrical connection to the circuit board when soldered. There are 233 of them with only 0.4 mm between each ball. My solution to soldering this was using a stencil with cut-outs where solder should be applied and applying solder-paste to this stencil.

stenciled PCB

Small amounts of solder-paste applied to each exposed copper pad using a stencil.

I bought a small cooktop to melt the solder paste after mounting the components. The cooktop would heat up the whole circuit board from underneath and was my best bet for making good connections.

bgasoldering.jpg

My setup for soldering the board. The unfinished circuit board is lying on top of the cooktop.

This method worked out great. When the paste started to melt the surface-tension of the melted solder pulled the microprocessor into alignment. By nudging the processor slightly with tweezers as the solder was melted I could see the surface tension pulling it back into position. This meant that it likely was making a good connection and was aligned correctly. The microprocessor did solder without any problems. Some smaller components such as resistors and capacitors needed some touching up but in all everything went well. The back side needed soldering to and was done using a soldering iron.

Part 4. Embedded Linux

Booting the device is done in three stages. To run Linux the device needs to be correctly configured in advance, that is we need to prepare for example the system clock and DRAM.

The first stage of booting is done automatically by the device when powered on. There is a section in the silicon that hard-codes a bootloader program. This program looks for valid boot images by parsing through media peripherals. Valid images are any binaries which begin with eight ARM exception vectors. For more information about the exception vectors check my other post where I write my own bootloader for an ARM-device. As I use an SD card with the boot images on this program passes control to the next stage bootloader.

The second stage of booting is through a program called AT91-bootstrap. Here the system is configured. By patching the source code one can change system-specific parameters, such as the frequency of the external oscillator, how much DRAM is present and in how many banks etc. I do think it’s possible to boot Linux directly from this bootloader but it’s more common to first pass control to a third bootloader called U-boot and it’s what I do.

Through these steps Linux is booted. The compilation and configuration of the bootloaders and the custom Linux distribution was done through the Yocto Project. The bootloader source code and Linux source code are pulled from the respective repositories, my custom patches were applied to the source code and the images were compiled. My patches consisted of changing device parameters such as DRAM, frequencies etc. and which additional kernel modules I needed and changing the device tree source files to reflect my hardware setup (e.g. no Power Management Controller, which pins the system LED is connected to etc). Since I used the wrong RJ45 connector I included drivers for a USB wifi-card so I could connect to the internet through that instead.

I manually created a suitable Linux filesystem (that is directories such as /tmp, /var, /etc and /home mounted each mounted with proper filesystems) on the SD-card and used Busybox to supply any utilities and programs I wanted such as systemd but also all ubiquitous programs present on any Linux system such as “ls” or “vi”. I used a UART-connection to access the terminal and standard output.

The video below shows the device booting after I press the reset-button. Linux has successfully booted when the blue light is pulsating. I use my laptop to send- and receive the UART data. That way I can interface with the device using my laptop and run commands such as ls.

A short video of my Linux device booting and running commands.

I also wrote some systemd-services that ran at startup that initialized the USB wifi-card by loading the kernel module drivers and configuring it. I also configured a basic ssh-server. After this the device automatically connected to my home wifi-network at power-on and makes itself available through the ssh-server.

usbwifi-rotated-e1623579464209.jpg

The device running Linux and using an external USB wifi-card to allow connections through SSH.

This meant I didn’t need the UART connections and could just interface with the device using SSH. After this I configured a Samba server so I could use it for sharing files in my home network.

Moving forward I would like to use what I’ve learned to create something more complicated. I feel confident creating circuits running embedded Linux and would like to design something having a connected screen with a graphical user interface. I think there could be some fun had with robot-projects done using a more powerful microprocessor running embedded Linux. I’ve already scouted parts I would like to use for a continuation of embedded Linux projects but the current Global Chip Shortage has made obtaining them difficult. Even for this project I had to use a microprocessor version with less DRAM than I’d like simply because the wait for higher-spec parts was too long.