You are on page 1of 24

Clinux on NIOS2 with custom hardware and

kernel module
Johan Granath
Applied Electronics for Embedded Systems
AGSTU School of Higher Vocational Education
Malmo, Sweden
jg@int80h.se
February 18, 2013

Abstract
This document describes how to set up a NIOS2-CPU system with MMU for
use with Clinux. It includes a simple custom hardware written in VHDL,
connected to the Avalon bus, and a kernel module to be able to interface with
the custom hardware from user-space. The custom hardware controls some
LEDs on a Altera development board (DE2-115), and should be fairly simple
to adapt to other circumstances.

Contents
1 Specification

2 QSys - building the system


2.1 OnChip RAMs . . . . . . . . .
2.2 CPU . . . . . . . . . . . . . . .
2.3 SDRAM . . . . . . . . . . . . .
2.4 UP clocks . . . . . . . . . . . .
2.5 JTAG UART . . . . . . . . . .
2.6 Timer . . . . . . . . . . . . . .
2.7 MMC SPI . . . . . . . . . . . .
2.8 UART . . . . . . . . . . . . . .
2.9 Custom LED component . . . .
2.9.1 Create new component .
2.10 Set properties of led component
2.11 Finishing the system . . . . . .
2.12 Connecting wires . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

4
4
5
5
6
7
7
7
8
9
9
11
11
11

3 Clinux - installation and configuration


3.1 Cloning the repositories . . . . . . . . .
3.2 Preparing the dts . . . . . . . . . . . . .
3.3 Configuring Clinux . . . . . . . . . . .
3.4 Building Clinux . . . . . . . . . . . . .
3.5 Loading Clinux onto the board . . . .
3.5.1 SD-card as root file system . . .
3.5.2 Boot from memory . . . . . . . .

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

13
13
14
15
18
18
18
18

4 Custom hardware setup


4.1 Kernel configuration . . . . . . . .
4.2 Add device files to root file system
4.3 Adding the source . . . . . . . . .
4.4 Building the kernel . . . . . . . . .
4.5 Testing the driver . . . . . . . . . .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

20
20
20
20
20
20

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

A LED component VHDL code

21

B LED component kernel driver C code

22

Specification

The system should be based on NIOS2, more specifically the NIOS2(f) variant.
It should use a MMU (Memory Management Unit) to be able to facilitate virtual
memory in Clinux. The following tasks are going to be implemented:
Build a NIOS2-system with MMU and peripherals in QSys
Build a custom hardware component that controls some LEDs, make it
Avalon-ready
Build and configure Clinux for NIOS2 with MMU
Write a kernel driver (module) to control the LED-component.
The hardware used in this project is Alteras DE2-115 board. However it should
be pretty easy to adapt to other development boards in the DE* range.

QSys - building the system

In this project QSys is used instead of SOPC builder. QSys is the new way
of building NIOS2-based systems. The following features and components are
required for this particular setup:
NIOS2(f) CPU with MMU
OnChip RAM (MMU)
SDRAM
JTAG-UART
UART
Timer
UP Clocks
MMC SPI (SD-card)
Custom LED component
Please note that the above specifies the requirements for this particular
project. Clinux is able to function without some of the above (i.e custom
hardware, UART, MMC SPI).

2.1

OnChip RAMs

Two OnChip RAMs are used. One for the reset- and exception vectors and one
for the MMU TLB. The reset/exception OnChip RAM was made 4096 bytes
big and the MMU TLB OnChip 1024 bytes. Note that the MMU TLB OnChip
RAM is a dual-port RAM. See figure 1 (reset/exception) and 2 (MMU TLB).

Figure 1: OnChip RAM

Figure 2: OnChip RAM TLB

2.2

CPU

The CPU is the fast variant (NIOS2(f)). Check that MMU is used and make
sure that the reset-, exception vectors and TLB are located at their respective
OnChip RAM. Connect data master to s1-port and instruction master to s2-port
on TLB OnChip RAM. Also connect the instruction master and data master to
port s1 of the regular OnChip RAM. See figure 3.

Figure 3: CPU settings

2.3

SDRAM

To be able to function, Clinux needs some RAM. We make use of the SDRAM
on the DE2-115 board. It has 128MB of SDRAM. It should be possible with

less SDRAM (even SRAM) but at least 8MB is needed for a extremely minimal
system. More RAM than that is recommended. Instanciate a SDRAM controller. Then choose custom and fill in the values as seen below in figure 4 and
5. Please note that these settings are for the DE2-115 SDRAM, other settings
apply if you use another board.

Figure 4: SDRAM profile

Figure 5: SDRAM timing


Also, export the wires from the SDRAM component. We will later connect
them to the SDRAM controller on the board.

2.4

UP clocks

This core will set up the clock that is needed for the SDRAM to function
correctly. It is found within Alteras University Program (UP). Instanciate the
core and select your board and then SDRAM clock. See figure 6.

Figure 6: UP clocks
Worth noting here is that the UP Clocks core provides us with a system
clock as well. Use this clock (sys clk) to all other components except the UP
Clocks component itself. I.e the other components should be clocked on sys clk
from UP Clocks instead of directly from the 50Mhz clock provided by the board.

2.5

JTAG UART

This component is used for connecting to Clinux. The terminal so to speak.


This project also includes a RS232 UART since the JTAG UART can be a bit
flaky (it sends random characters when not supposed to, atleast on my system).
Nevertheless, instanciate a JTAG UART and connect IRQ 1 to the CPU.

2.6

Timer

Clinux needs a timer to function. This timer needs to be full-featured. See


figure 7. Connect the IRQ to the CPU.

Figure 7: Timer settings

2.7

MMC SPI

This component is optional, but is needed if the system needs to make permanent changes to the filesystem. It is basically a component that will make
it possible to use a filesystem on a SD-card (or MMC) as root filesystem in

Clinux. Instanciate the component (called SPI 3-wire) and make changes according to figure 8.

Figure 8: MMC (SD-card) SPI 3-wire


Also, export the wires (Click to export). We will later connect them to the
SD-card controller. Connect IRQ 2 to the CPU.

2.8

UART

For a more reliable terminal than JTAG UART, a real UART via RS232 will
be used. This component is also optional. Start by instanciating a UART and
make changes according to figure 9.

Figure 9: UART RS323


Export the wires and connect IRQ 3 to the CPU.

2.9

Custom LED component

The LED component is a proof-of-concept component, it has no real value except


for educational purposes. Its functionality is to take input from the Avalon bus
and output those values on the LEDG LEDs on the DE2-115 board. It is of
course also optional, and not needed for Clinux to operate. However if the
reader is about to add some custom component to a Clinux system, these
guidelines might be useful. First, the component needs to be written in VHDL.
See appendix A for the VHDL code.
Next, it needs to be imported in QSys.
Create a directory in your project directory called ip\led component\HDL.
Next put the source code there (i.e led.vhd).
2.9.1

Create new component

In QSys choose File-New component. Fill in the boxes as seen in figure 10.

Figure 10: New component settings


Then add the led.vhd VHDL file and click Analyse Synthesis Files. See
figure 11.

Figure 11: New component files


Then select the corresponding Avalon signals for each signal in the top-level
hierarchy. Note that LEDG has to be set to conduit. See figure 12.

Figure 12: New component signals


Finally, check that the clocks and reset signals are present (associated clock/reset).
10

See figure 13.

Figure 13: New component clocks/reset


Then press finish. The component should be visible and the possiblility to
instanciate it will be present. Instanciate it and connect s1 to data master, and
also export the wires, which we will connect later.

2.10

Set properties of led component

To make our kernel driver recognize the led component it needs to have correct properties in the DeviceTree. This can be accomplished by editing the
led hw.tcl. Add the following sections to it.
set_module_assignment embeddedsw.dts.vendor "ALTR"
set_module_assignment embeddedsw.dts.name "led"
set_module_assignment embeddedsw.dts.group "led"

2.11

Finishing the system

To finish, select Assign Base Addresses and press finish.

2.12

Connecting wires

For this project a BDF (Block Diagram) will connect the wires to the respective
controllers on the board. This is just a matter of reading the DE2-115 manual.
See figure 14.

11

Figure 14: Schematic pin connections

12

Clinux - installation and configuration

To be able to build Clinux the following are required:


Linux build box (Fedora 17 in our case)
make version 3.81 or less
Clinux distribution
Toolchain (precompiled)
This chapter explains installing and configuring Clinux without the custom
hardware. We want to make sure it runs before we make those changes.

3.1

Cloning the repositories

There is a community git server that has all of the prerequisites. It is located
at http://sopc.et.ntust.edu.tw/. Start by cloning the git repositories that
we need.
1. Clone the trunk branch of Clinux.
git clone -b trunk git://sopc.et.ntust.edu.tw/git/uClinux-dist.git
2. Clone the toolchain (MMU variant).
git clone git://sopc.et.ntust.edu.tw/git/toolchain-mmu.git
3. Clone the linux-2.6 repository.
git clone git://sopc.et.ntust.edu.tw/git/linux-2.6.git
4. We also need the sopc2dts tool.
git clone git://sopc.et.ntust.edu.tw/git/tools.git
Next, check that the correct branches are used. For Clinux, trunk should
be used, for toolchain-mmu, master is used and for linux-2.6, nios2 is used.
# cd uClinux-dist
# git branch
* trunk
...
# cd toolchain-mmu
# git branch
* master
...
# cd linux-2.6
# git branch
* nios2

13

3.2

Preparing the dts

DeviceTree is a way of describing the hardware in an embedded system. This


information is passed to the kernel during build, so it is needed before we start
to build Clinux. QSys provides a .sopcinfo file which has to be converted
to a .dts (DeviceTree). A boardinfo file can be created for later use. Read
more about boardinfo at http://www.alterawiki.com/wiki/Sopc2dts. Use the
sopc2dts utility with the GUI feature.
$ java -jar sopc2dts.jar --gui
Next, select Choose file and open your .sopcinfo file from QSys. See figure
15.

Figure 15: sopc2dts GUI


We have to add a mmc-spi-slot to the dts. Select Boardinfo, SPI and
then select your SPI component. Next click Add to add a mmc-spi-slot.
See figure 16.

14

Figure 16: sopc2dts GUI


Finally, select Output and save your dts file.

3.3

Configuring Clinux

Before building the kernel some changes are needed. The first thing that needs
to be done is to set the PATH environment variable so that the build system finds
the toolchain.
# export PATH=$PATH:/path/to/nios2/toolchain/bin
Some other changes in the kernel configuration is needed.
Set start of SDRAM memory (CONFIG MEM BASE)
Compile and link DTB into kernel image (CONFIG DTB SOURCE BOOL)
Set DTB file to be used above (CONFIG DTB SOURCE)
Set MMU support (CONFIG MMU)
Set kernel commandline (CONFIG CMDLINE)
Set link address offset (CONFIG BOOT LINK OFFSET)
Set SPI support (if MMC SPI is used) (CONFIG SPI)
Set SPI Altera support (if MMC SPI is used) (CONFIG SPI ALTERA)
Set MMC support (if MMC SPI is used) (CONFIG MMC)
Set MMC over SPI support (if MMC SPI is used) (CONFIG MMC SPI)

15

These changes can be made with make menuconfig. Please note that make
version 3.81 or less has to be used! The first configuration menu is for setting
platform and vendor. Choose libc - none here. See figure 15 and 16.

Figure 17: Vendor/Platform selection

Figure 18: Libc/Kernel configuration selection

16

If the applications shipped with Clinux needs to change, select Customize


Application/Library Settings. For the purpose of this document, the default application settings are used. If Customize Kernel Settings were chosen, the following menu is available (figure 17).

Figure 19: Kernel configuration


Here all the changes has to be made. The Start of SDRAM Memory needs
to be set to whatever memory location QSys chose for the SDRAM. Have a look
in your DTS to find out. The kernel commandline can be one of two possible
settings. It depends on if we want it to boot from SD-card or from memory, a
third alternative is also possible - to use JTAG UART as console.
1. To boot from memory with UART as serial console.
debug console=ttyAL0,115200
2. To boot from SD-card with UART as serial console.
debug console=ttyAL0,115200 root=/dev/mmcblk0 rootdelay=2
3. To boot from memory with JTAG UART as serial console.
debug console=ttyJ0,115200
The other settings also have to be made. To search and find where in the
configuration menu tree a specific configuration option is, use / to search after
a CONFIG * option. Then do the appropriate changes.

17

3.4

Building Clinux

After the configuration is done, the build can start. Hopefully without any
compilation errors. If you get compilation errors, there is most likely some
package missing on your build machine. These packages are confirmed to be
needed (Fedora 17).
git-all git-gui make gcc ncurses-devel bison byacc flex \
gawk gettext ccache zlib-devel gtk2-devel lzo-devel \
pax-utilslibglade2-devel uboot-tools
To start the build (again with make version 3.81 or less):
# make
...
Kernel: arch/nios2/boot/zImage is ready
...
When done, the above text is printed. This means your build was successful.

3.5

Loading Clinux onto the board

This can be done in two ways. Since we have configured MMC SPI support the
initramfs can be copied onto a SD-card, then use that as a root file system.
3.5.1

SD-card as root file system

The initramfs is located in the relative path (in uClinux-dist directory)


linux-2.6/usr/initramfs data.cpio. First the SD-card has to be prepared
to be able to copy the contents onto it. Please note that /dev/sdb could be
different in your system.
$
$
$
$
$

mke2fs /dev/sdb
mount /dev/sdb /mnt
cd /mnt
cat /path/to/initramfs_data.cpio | cpio -id
umount /mnt

The SD-card is now ready to be booted from. Proceed to Boot from memory.
3.5.2

Boot from memory

First, the board has to be loaded with the .sof file from Quartus.
$ nios2-configure-sof /path/to/soffile.sof
Next, we have to load the kernel into the memory.
$ nios2-download /path/to/zImage
To be able to see some output, use a terminal program, like minicom (or nios2terminal if you chose JTAG UART as serial console). Note that ttyUSB0 could
be different in your system.
$ minicom -D /dev/ttyUSB0
18

You should see Clinux boot up.


...
Welcome to
____ _ _
/ __| ||_|
_
_| | | | _ ____ _
_ _ _
| | | | | | || | _ \| | | |\ \/ /
| |_| | |__| || | | | | |_| |/
\
| ___\____|_||_|_| |_|\____|\_/\_/
| |
|_|
For further information check:
http://www.uclinux.org/

BusyBox v1.18.4 (2013-02-13 19:40:16 CET) hush - the humble shell


Enter help for a list of built-in commands.
root:/>

19

Custom hardware setup

Now that we have a functioning Clinux system up and running, its possible to
add support for the custom hardware that we added in QSys (LED component).
We will do some changes to the kernel configuration and and a kernel driver for
this component.

4.1

Kernel configuration

There is a few changes that has to be made to include the code into the kernel
build.
1. Add option to Kconfig in linux-2.6\source\drivers\misc
config LED
tristate "LED custom hardware"
help
LED custom hardware
2. Add object file to Makefile in linux-2.6\source\drivers\misc
obj-$(CONFIG_LED) += led.o

4.2

Add device files to root file system

The kernel driver will make use of a device file to communicate with the hardware, so a device file needs to be created. Add the following line to vendors\Altera\nios2
\device table.txt
/dev/led

4.3

666

250

Adding the source

The source of the device driver needs to be put in linux-2.6\source\drivers\misc,


and be called led.c. See source code in appendix B.

4.4

Building the kernel

To include the kernel driver, the kernel needs to be rebuilt. In the configuration
choose the CONFIG LED option and save your changes. The kernel should now
include the code in the build.
$ make menuconfig
$ make

4.5

Testing the driver

If the build was successful, the driver should be able to output some values on
the LEDG LEDs from user-space. To test the function type in the following.
$ echo 1 > /dev/led
The LEDG should now show 1, i.e the first LED should be lit.
20

LED component VHDL code

library ieee;
use ieee.std_logic_1164.all;
entity led is
port(
clock_50
reset_n
avalon_cs_n
avalon_addr
avalon_write_n
avalon_din
LEDG
);
end entity led;

:
:
:
:
:
:
:

in std_logic;
in std_logic;
in std_logic;
in std_logic_vector(0 downto 0);
in std_logic;
in std_logic_vector(7 downto 0);
out std_logic_vector(7 downto 0)

architecture led_rtl of led is


begin
led_p : process(clock_50, reset_n)
begin
if reset_n = 0 then
LEDG <= (others => 0);
elsif rising_edge(clock_50) then
if avalon_cs_n = 0 then
if avalon_write_n = 0 and avalon_addr = "0" then
LEDG <= avalon_din;
end if;
end if;
end if;
end process led_p;
end architecture led_rtl;

21

LED component kernel driver C code

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

<linux/kernel.h>
<linux/module.h>
<linux/mod_devicetable.h>
<linux/platform_device.h>
<linux/of_device.h>
<linux/of_address.h>
<linux/fs.h>
<linux/ioport.h>
<asm/uaccess.h>
<asm/io.h>

MODULE_AUTHOR("Johan Granath");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("LEDG[7..0] driver for Altera DE2-115");
MODULE_SUPPORTED_DEVICE("none");
#define LED_MAJOR 250
static ssize_t led_write(struct file *, const char *, size_t, loff_t *);
static char led_str[3];
int *led_mem=0;
static struct of_device_id led_of_match[] __devinitdata =
{
{ .compatible = "ALTR,led-1.0", },
{}
};
MODULE_DEVICE_TABLE(of, led_of_match);
static struct file_operations fops_led =
{
.write = led_write,
};
static ssize_t led_write(struct file *fp, const char *buf, size_t len, \
loff_t *offset)
{
int not_copied, led_value;
not_copied=copy_from_user(led_str, buf, len);
led_str[len] = \0;
led_value = (int) simple_strtoul(led_str, NULL, 10);
iowrite8(led_value, led_mem);
return len-not_copied;
}

22

static int __devinit led_drv_probe(struct platform_device *op)


{
struct resource *res;
if(!of_match_device(led_of_match, &op->dev))
return -ENODEV;
res = platform_get_resource(op, IORESOURCE_MEM, 0);
if(!res)
return -ENODEV;
if(!request_mem_region(res->start, resource_size(res), "led"))
return -ENOMEM;
led_mem = of_iomap(op->dev.of_node, 0);
if(!led_mem)
return -ENOMEM;
if(register_chrdev(LED_MAJOR, "led", &fops_led))
{
printk("register_chrdev: led failed\n");
return -EIO;
}
return 0;
}
static int __devinit led_drv_remove(struct platform_device *op)
{
struct resource *res;
res = platform_get_resource(op, IORESOURCE_MEM, 0);
if(!res)
return -ENODEV;
release_mem_region(res->start, resource_size(res));
return 0;
}
static struct platform_driver led_platform_driver =
{
.probe = led_drv_probe,
.remove = led_drv_remove,
.driver =
{
.name = "led",
.owner = THIS_MODULE,
.of_match_table = led_of_match,

23

},
};
static int __init mod_init(void)
{
int ret;
ret = platform_driver_register(&led_platform_driver);
return ret;
}
static void __exit mod_exit(void)
{
platform_driver_unregister(&led_platform_driver);
unregister_chrdev(LED_MAJOR, "led");
}
module_init(mod_init);
module_exit(mod_exit);

24

You might also like