Brainathon: Summary

7 views
Skip to first unread message

David Pineau

unread,
Nov 10, 2013, 12:49:22 PM11/10/13
to rathaxe...@googlegroups.com
Hello everyone !

This was a brain-grilling week end ! We had so much to talk about and
re-design (as well as designing new features), that we still have a
few things to discuss at a later date (We need to finish a few things
before then).

So as a summary here are the subjects that we have talked about:
- New entry points for the code generation:
* Main entry point updated to remove hard-coded things, and to
improve flexibility: The driver's generation model.
* Secondary entry point added: The provided callbacks which may
not appear in the front-end
- New way to setup the configuration parameters: Calling a sequence
from the configuration, for setting a config parameter at run-time
- New features:
* Function templates (prospective, yet to be actually designed)
* Subtypes (For the device's integration in the backend as well as
managing types of Collections)
- Reminder about How to write a register
- Typing system (We had a little explanation about where Lionel is,
and he will write a clear description and explanation at a later date)

Left to discuss/detail:
- Typing system, once the design has been chosen (after ongoing
experimentations)
- Way to manage the Backend Library (a few experiments are going on,
since the out-of-the-box graphdb isn't available without a DB server,
and we wish for a local disk storage)
- Generation Model Internals + Database representation of the backend
which depends on the two previous items.


Sorry for the way the mail is (or not) structured, but seeing as the
subjects are not really all directly linked, and that there is no
logic linking them as a suite, You will have to go through the whole
mail ;)
Anyways, the summary follows the order of the mail, if you aren't
brave enough to read the results of our ramblings !


//
// Entry points for the code generations
//

New way to get the starting point of the generation:
- Interface "Driver" contains a "model" parameter use to specialize it.
- the Driver interface may be specialized for LKM, UserAgent (user
daemon, for Mac or for micro-kernels), etc...
- The specialized Driver interface provides the skeleton of the code
to generate in a with clause: "with Driver::model == LKM { ... }"

Thus the configuration provides the model to use for the generation,
which will be used as a starting point. This way, the old "global
with" disappears, and we have a better way to describe how to generate
the whole thing. Here is an explainatory sample:

"""
interface Driver : Builtin
{
provided pointcut Driver::Includes();
provided pointcut Driver::Types();
provided pointcut Driver::Prototypes();
provided pointcut Driver::Data();
provided pointcut Driver::Code();

provided parameter Symbol model; /* The driver model to use */
required Callback Init()
{
...
}
required Callback Exit()
{
...
}
}

// in backend
with Driver::model == LKM
{
${pointcut Driver::Includes()};
${pointcut Driver::Types()};
${pointcut Driver::Prototypes()};
${pointcut Driver::Data()};
${pointcut Driver::Code()};
}



// And then, front-end side in the config':
configuration {
driver::model = LKM;
...
}
"""

Then, setting the configuration's Driver::model to LKM will use the
with block I just wrote to generate the whole code's skeleton.
It looks quite close to what we had before, but we can take out the
hard-coded bits, which is a great satisfaction !


//
// Secondary code generation entry point
//
Secondary entry points: Provided callbacks
Let's set a bit of context; we have a specific need, following an
example, which describes a specific case, and that we will probably
often cross on our path:
Imagine a PC Linux environment: In this case, the PCI bus will have a
known address, and you will use it to manipulate the PCI Library and
access to your BARs.
Now imagine an embedded Arm Linux. The PCI bus does not have a
known-in-advance address, from a coder's point of view.
For this, the kernel has a description file telling him the address of
each bus etc... Thus, we need to have some kind of specific code to do
the retrieval of the bus's address through the kernel.
In some cases, this discover "function" will be called by the driver,
but in some cases we will need a new Callback called by the kernel for
the retrieval of the PCI BUS's address.

We thought about a front/back end hybrid, but that would be too
confusing, and may require too much from the driver's (rtx) writer
(like writing a discovery callback that he has no knowledge of, and
doesn't want to know about neither). Thus, we opted for a similar
solution to my previous point: a configuration parameter.

This configuration parameter would indicate the flavor to use, and
the associated code being a "provided callback" would be forcibly
weaved into the final code as long as it can be selected with the
configuration (It is a callback, there is no use-base selection in
this case). Of course, this provided callback would be defined in an
extension of the original interface.

Good points:
- The writer of the driver doesn't need any knowledge of any specific
environment setup.
- The writer of the backend knows his environment and can do things properly
- The guy who wants to generate the driver knows his target
environment, and through the documentations knows what he must use in
the configuration.
- No code correlation or mix between what should be in the front-end
or the back-end. For instance, a callback-based PCI discovery might
simply be a sequence template that generates code almost silently

Bad points: Well, it's a hidden code (from a front-end point of view)
that will be generated but apart this, not much is bad.

So here is the code sample:
"""
interface Bus
{
...
}

extend interface Bus on Bus::type=PCI
{
required parameter Builtin::String flavor;
}

extend interface Bus on Bus::type=PCI, Bus::PCI::flavor="callback"
{
provided callback Cb_discover()
{
provided chunk Driver::code();
provided chunk Driver::callbacks();
}
}

with Bus::type=PCI, Bus::PCI::flavor="callback"
{
template callback Cb_discover()
{
chunk Driver::code()
{
int cb_discover() {}
}

chunk Driver::callbacks()
{
&cb_discover
}
}
}

"""


//
// Sequences called by the configuration
//
In the same state of mind, a configuration value may have to be
retrieved at runtime, instead of compile-time. For instance, you may
not know in advance the adress of the PCI Bus to give to the PCI
library in order to retrieve your Bank Adress Register's adress.

The idea is simply to use a provided sequence from the configuration
block, such as:

"""
configuration {
Bus::type = PCI;
Bus::address = Bus::get_adress();
}
"""

//
// New (not yet fully specified/designed) features : Function
templates and Subtypes
//
Through Louis' attempt to implement the e1000 driver with rathaxes, a
few lacking features were identified, and here we try to address those
issues.

First, the impossibility to return a value from a sequence. Indeed,
the sequences are a kind of procedure, and thus, cannot return a
value. The error management was planned to be done through the
on_error blocks attached to each chunk, and some compiler magic. The
fact is that sometimes, we need a more functional way of returning
values, and we are now seriously thinking about getting no only
"Sequences and Callbacks" into rathaxes, but also functions. This will
be specified when the new typing system will be decided and announced.

variables
The second issue, is a matter of encapsulating datas between multiple
subsystems and sometimes with some data coming from the front-end.
As louis experimented on rathaxes, we will sometimes need to include a
data from an underlying layer (think underlaying subsystem). Up until
now, we've done it through a pointcut within the type's declaration,
leading to issues where we didn't know the actual name of the field,
which led to the upper layer requiring knowledge about the underlying
one.

Thus, we propose "subtype" feature, which would be written so:

"""
interface Upper {
provided abstract Upper::Context {
required subtype Contained;
}
provided template sequence Upper::SomeTemplateSequence() {
provided chunk SomeInterface::SomeChunk();
}
}

interface Lower : Upper {
provided type Lower::Context {
provided subtype Contained;
}
}

with {
template type Upper::Context
{
chunk ::decl {
${Upper::Context::Contained} sub_field;
}
}

template sequence Upper::SomeTemplateSequence()
{
chunk SomeInterface::SomeChunk()
{
${Upper::Context::Contained} temp;
}
}
}
"""

Or, including the subtype from the front-end (since this is also a
required case, when the data is defined by the algorithms required by
the driver) :

"""
device e1000
inherit Upper::Context::Contained // Optional,
depends Algo, Builtin, Ethernet, Net
{
//
// Every "variable" declaration would be autregomatically
"wrapped" into a front-end type which would then become the subtype
//
}
"""

Sadly, this only seems manages the one-to-one types relationships, and
we may need to manages some parametrized types such as collections.
Here is how we see it (using the same mechanism):

"""
interface Algo
{
provided abstract Queue
{
required subtype Collection;
}

provided abstract Tree
{
required subtype Collection;
}

provided abstract Semaphore
{
required subtype Resource;
}
}

interface Resources
{
provided type MemPage
{
provided subtype Collection; // Here, "Collection" is a
concept, to say that any collection can embbed this type.
provided subtype Resource;
}
}


// Then, Using it from front-end...
Memory is variable Tree<MemPage>;


// Or using it from the back-end....
${Tree<MemPage>} memtree;

"""

The idea is that all typing is to be resolved at type instanciation,
and we'd have a kind of mangling to manage multiples
Queues/Trees/Collections over differents subtypes.
On another note, a type providing subtypes for multiples abstracts
may only be instantiated for one at a time (all provided do not
require to be resolved, there are merely explicit possibilities).




//
// Reminder about how to write a register
//
For the sake of staying up-to-date, here is a reminder about how we
plan to write registers (modified a bit through this brainathon)

"""
device e1000
...
{
// Name is type at base_addr
Puf is descriptor at ${Bus::base_adress}
{
// Name is type subtype Flag Mapping(...) at offset
Nia is register byte R like(.**.01..) at 0
{
...
}
}

// Name is type QualifiedTypeName
Tah is variable Builtin::Word;
Teh is variable Builtin::String;
bleh is variable Puf::Nia; // Reference the register Nia
described in the descriptor Puf
}

"""

With the ongoing work, we also imagined an evolution possibility,
going toward Type Inference in the front-end.
For instance, you could write "tmp = Nia;", which would be translated
as a read operation of the Nia register to a temporary variable of the
same type.
A few similar syntaxes may become easy to manage with the whole typing system.



//
// Typing system
//
The typing system is currently in its exploratory stages, and will be
described and explained by Lionel when its design is deemed worthy to
be the backbone of the new rathaxes compiler.





// END

That will be all for this time, folks ! I hope the reading was
interesting for you, and as usual don't mind telling us any suggestion
or remark you'd have !

--
David Pineau,
Developer R&D at Scality

David Amsallem

unread,
Nov 11, 2013, 3:02:04 PM11/11/13
to rathaxe...@googlegroups.com

Very interesting. Thanks.

--
--
ML Rathaxes
www.rathaxes.org

---
You received this message because you are subscribed to the Google Groups "Rathaxes Development List" group.
To unsubscribe from this group and stop receiving emails from it, send an email to rathaxes-deve...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
Reply all
Reply to author
Forward
0 new messages