Showing posts with label C99. Show all posts
Showing posts with label C99. Show all posts

Tuesday, 28 January 2020

Progress

The toolchain (compiler, assembler) for the Robin SoC is really shaping up now, so i started creating a test suite consisting of several functions commonly found in libc.

Even though the C compiler isn't anywhere near completion, it does now produce code that might be ugly but is capable of producing good enough assembly.

The goal of this all is to create a proper test suite of executable programs that can be used to check if future changes in the cpu still perform as designed.

Compiler status


The compiler now supports most control structures (for, while, if/else, break, continue, return) except switch.

It supports char and int data types including pointers and arrays but not yet structs or unions. Some work to support floats is underway (see below).
Variables can be automatic (local) or static (file scope).

Most unary and binary operators are supported including the ternary  ?: operator, pointer dereferencing (*) and function calls, but not the address of operator (&).

Type checking however is weak (almost non existent to be honest 😁) and the assembly code it produces is far from optimal but it works. Storage specifiers like static and volatile are completely ignored.

Implemented functions


The C standard library is rather large and although I have no intention to implement more than a small subset, these functions do provide a good example of realistic functionality which is why I have chosen it as a test vehicle.

The implementation is done from scratch and of course targeted at just the Robin SoC, which makes life a lot easier because a full blown portable libc is humongous.

The current status (with links) is shown below; more functions will probably follow soon, especially low level functions to implement a (bare bones) soft float library.

From string.h


These functions are mainly implemented to support the integer and float conversion functions in stdlib.h but are of course useful on their own as well.

strlen.c
strchr.c
strreverse.c

From stdio.h


File based functions are a long way off still but some basic output over the serial interface is provided here. Later some input functions will be provided as well.

putchar.c
print.c (this one is not actually in libc, it just prints a string)

From stdlib.h


In order to test the float functions later, we absolutely need some basic conversion functions so I implemented those first. Note that the float functions currently just support basic decimal fractions, scientific notation (2.3E-9) is not (yet) supported. They do work with standard ieee float32 numbers but no rigorous compliance is attempted with regard to rounding etc.

atoi.c
ftoi.c
itoa.c
itof.c

Conclusion


These functions need to be thoroughly tested before they can actually be used as a proper test suite for the hardware but I feel we have started quite well.

Sunday, 12 January 2020

Compiler

Assembler is nice but to get a feel how well the SoC design fits day to day programming tasks I started crafting a small C compiler.

I probably should call it a compiler for a 'C-like language' because it implements a tiny subset of C, just enough to implement some basic functions. Currently it supports int and char as well as pointers and you can define and call functions. Control structures are limited to while, if/else and return but quite a few binary and unary operators have been implemented already.

Because the compiler is based on the pycparser module that can recognize the full C99 spec it will be rather straight forward to implement missing features.

Pain points


Even for the small string manipulation functions it quickly becomes clear that additional instructions for the CPU would be welcome. The biggest benefit would probably be to have:

  • Conditional branch instructions with a larger offset than just one byte.  
Even small functions may exceed offsets of just -128 to 127 bytes so this is a must have.

  • Pop/push instructions.

Currently implemented as two instructions, one to change the stack pointer and another to load or store the register. This approach makes it possible to use any register as a stack pointer but for compiled c we need just one.

  • Better byte loading.

If we load a byte into a register we often have to zero it out before load it. This way we can easily change just the lower byte of the flags register but otherwise it is less convenient.

  • alu operation to convert an int to a boolean

This would greatly reduce the overhead in expressiins involving && and ||

Plenty of room for improvement here 😁

CPU design

The CPU design as currently implemented largely follows the diagram shown below. It features a 16 x 32bit register file and 16 bit instructi...