Porting Raspberry Pi Driver Framework Circle to Custom Operating System

Click the card below to follow Arm Technology Academy

This article is selected from the Jishu Community column “Embedded Inn”, authored by RC. This article will briefly describe the process of porting the Circle driver framework to a custom microkernel operating system as a user-mode driver process.

Original link:

https://stdrc.cc/post/2021/01/22/porting-circle/

1. Introduction

What is Circle?

Circle is a bare-metal Raspberry Pi driver framework written in C++ by a developer known as rsta2. It supports almost all existing versions of Raspberry Pi and can drive most devices on the Raspberry Pi, including the SD card controller, wired and wireless network cards, GPIO, USB controllers, and some common USB devices.

Some of these device drivers are written by rsta2 based on references from Linux or other bare-metal implementations, and some are directly ported from other systems. Each driver is encapsulated as a C++ class, which can be instantiated and used as needed. For scenarios where you want to write a custom operating system on the Raspberry Pi and quickly drive some devices, porting the entire Circle framework is a very worthwhile option. However, it should be noted that Circle’s open-source license is GPLv3, which is infectious, and it should be used as a non-essential component of the system.

What does this article do?

This article will briefly describe the process of porting the Circle driver framework to a custom microkernel operating system (actually a lab project), as a user-mode driver process.

Since this article only briefly records the porting process and summarizes experiences, it will not delve into every specific detail. If you happen to have similar needs, in addition to referring to this article, you will also need to specifically study the Circle code and analyze it according to your specific circumstances.

2. Determine Requirements

First, do not blindly port; first determine which device drivers you need from Circle. For example, if you only need USB-related drivers, you can ignore whether WLAN is incompatible during the porting process.

Then run some samples provided by Circle to confirm that Circle can indeed meet your needs.

3. Porting Basic Parts

This part includes some very necessary components, such as mailbox access; many drivers need to obtain information or request resources through the mailbox. Once this part is ported, you can obtain machine information at startup and light up the LED.

Specifically, you need to do the following:

1. Modify Rules.mk and Makefile to remove any boot-related code.

2. Re-implement some modules, removing places that require privileged instructions, and use the functionality provided by user-mode libraries instead:

  • assert: Do not actually shut down or restart on assert failure, can exit;

  • interrupt: Initially, you can simply print some content, no need to really implement;

  • logger: Change to printf output;

  • new: Use malloc for allocation;

  • sysinit: Provide a dummy implementation;

  • timer: Use nanosleep to implement SimpleusDelay;

  • memory: CMemorySystem class can be removed directly, initially there will be places using CMemorySystem::GetCoherentPage;

  • Change to use the system-provided interface for allocating physically contiguous non-cacheable memory.

3. When the Circle process starts, directly map the physical address peripheral area to the current process’s virtual address space, so that it will not require changes to the addresses used in Circle for MMIO access to peripherals.

After some debugging, you can light up the LED, output logs to stdout, and then exit (you need to write the appropriate kernel.cpp, refer to samples), which means that simple MMIO is already working.

4. Driving the Screen

This step also requires re-implementing some modules:

  • synchronize: Mainly related to cache flushing operations;

  • bcmframebuffer: After requesting a frame buffer from the GPU, it needs to be mapped to the current process’s virtual address.

After some debugging, you can output content to the screen via HDMI.

5. Simulating Timer Implementation

For some slightly more complex drivers, such as USB and WLAN, they rely on the timer to obtain the current tick, so the timer module needs to be re-implemented.

Specifically, you can create a new thread in CTimer::Initialize, calling CTimer::InterruptHandler every 10ms (using functions like nanosleep), and almost no other code needs to be changed. In addition, you also need to implement CTimer::GetClockTicks to get the current tick count.

After some debugging, the output logs can contain the current time, indicating that the timer implementation is basically correct.

6. Resolving a Series of Memory-Related Issues

Many drivers need to allocate memory for DMA while running, and also need to access this memory within the process to read and write data interacting with peripherals. Therefore, this memory needs to be accessible via virtual addresses, while also being able to obtain physical addresses, and must be physically contiguous and non-cacheable. malloc cannot meet this requirement, as malloc only guarantees that the allocated memory’s virtual addresses are contiguous, but does not guarantee physical address contiguity and cannot be configured as non-cacheable.

To solve this problem, the kernel needs to provide system calls for allocating physically contiguous and non-cacheable memory and mapping it to the virtual address space, and then Circle can use these system calls to re-implement memory-related modules. In fact, this was roughly implemented earlier, but at this step, it is necessary to ensure the correctness of the implementation. Modifications to Circle mainly involve the definitions and usages of CMemorySystem::GetCoherentPage, DMA_BUFFER, BUS_ADDRESS, new(HEAP_DMA).

7. User-mode Interrupt Handling

For devices like USB and WLAN that need to use interrupts to notify the operating system of specific events, you also need to implement the previously dummy interrupt module.

First, the kernel needs to provide the ability for specific user-mode processes to handle specific IRQs. Specifically, the driver process needs to be able to register a function as a user-mode handler for a specific IRQ number through a system call, and then the kernel will call this function to handle the IRQ when it is received.

Next, re-implement the interrupt module, changing CInterruptSystem::ConnectIRQ to use the aforementioned system call for registering IRQ handler functions. Functions that are temporarily not needed can be left unimplemented, such as those related to FIQ.

8. Driving USB

After the important parts of Timer, DMA buffer, and interrupts are ported, it becomes relatively easy to drive the USB controller, which can then detect and drive USB keyboards, mice, storage devices, serial converters, etc. For Raspberry Pi 3, it can also drive wired network cards (LAN7800).

9. Other Drivers

So far, most of the runtime environment required for driving has been ported. The subsequent porting work mainly depends on specific needs. For example, if a network protocol stack is needed, the sched module needs to be re-implemented, which includes thread abstraction, scheduling, thread synchronization mechanisms, etc.

Recommended Reading

  • From Linux on Arm to Windows on Arm

  • The Knowledge Structure of a Qualified Digital IC Design Engineer

  • What is CMSIS-RTOS?

Porting Raspberry Pi Driver Framework Circle to Custom Operating System
Porting Raspberry Pi Driver Framework Circle to Custom Operating System
Porting Raspberry Pi Driver Framework Circle to Custom Operating System
Porting Raspberry Pi Driver Framework Circle to Custom Operating System

Long press to recognize the QR code to join the group chat: Arm Technology Academy Reader Group; if the group QR code is invalid, please directly add Miss Jishu WeChat (aijishu20) to invite to join the group.

Follow Arm Technology Academy

Porting Raspberry Pi Driver Framework Circle to Custom Operating System Click below “Read the Original“, to read more articles related to the Embedded Inn column.

Leave a Comment

×