Monday, January 21, 2013

Defining a good language Kernel

When you design a new language you have the ultimate freedom of defining how your language should look like. There are plenty of language favors available, each with their own set of advantages and disadvantages. As the language designer it is your ultimate goal to create a language that can easily be understood, learned and used. It should make common tasks easy but also allow more complicated cases.

A strictly procedural syntax seemed a bit to plain for me, a functional one too far away from the hardware. So my decision went for the so far most successful way, the object oriented way. But I did not try to create an academic overhead by forcing a full OOP way on the programmer. Instead I designed the expression syntax to be equivalent to the C/Java expressions and added a few OOP concepts where it made sense. This allows most programmers to get an idea of what is going on quite quickly. There are two additions to the known expression: Bit accesses designated with curly braces and concatenation of numbers with #.

Most statements also look quite a bit like regular C/Java code. But I had to add a few other concepts. Interfaces, generators and variable bit width integer and registers are among them. I will quickly explain them here and get into more details in a later post.

Interfaces are kind of like binding contracts for developers. If an interface is declared, the implementation has to have all those ports in this exact way that they are declared. This is really useful for communicating an API when the implementation is not known, does not exist yet or is in another language. For example if you want to instantiate VHDL entities, you have to declare their interface, which you can then instantiate. It is up to the developer to ensure that the interface matches the VHDL implementation. Another example are generators...

Generators are a very powerful concept to automatically generate code for you from a short description. To declare a generator multiple things are required. An interface which the generated unit will have to obey, parameters, which specify whatever code should be generated and an optional piece of code. The last part is what makes generators so powerful, you can essentially embed another language with it. Take the bus generator for example. From a few lines of code, it can generate you a whole lot of PSHDL code, C code and even documentation. But other use cases might include a processor with C code, a language for statemachines, a C to hardware generator Etc.

What I was not able to transfer over to PSHDL however is the semantic of C, a fully sequentially control flow as it is described with C. Instead, PSHDL took the path of other HDLs by having everything in parallel. If you need sequential execution, you will have to code a state machine. This is the tribute of programming FPGAs vs. programming CPUs that you have to pay. In hardware things are pipelined and running in parallel. The equivalent of functions are rather other ip cores, but to wire them up efficiently, the sequential paradigm had to go. To create efficient pipelines and state machines registers came into play. They allow you to create really fast and robust synchronous logic. This why they are an integral part of PSHDL. While on CPUs the width of int is chosen to be the most efficient for the given architecture, on an FPGA the developer has to decide how many bits his variable should have. Make it too big and expensive routing and fabric resources are wasted and timing will start to degrade.

No comments:

Post a Comment