-
Notifications
You must be signed in to change notification settings - Fork 3
LFR Primer
LFR is a hardware description language (HDL) that allows the designer to synthesize the microfluidic designs automatically from a text-based description of the hardware architecture.
A programming language helps you write the program while an HDL enables you to write what the computer's capability is.
Here's a quick learning guide for writing LFR code
Every architecture description starts with a module; modules are meant to describe the architecture of the device; they can be nested, instantiated, and reused multiple times to describe a single design. However, one needs to have at least 1 module to describe the top level of the design. Ignore <input>, <output> , <control>
for now.
module XXX(<input> , <output>, <control>);
Body of the description goes here.
endmodule
As you have seen above, when describing a module, one must describe the inputs and outputs to the module. LFR currently has three kinds of I/O types:
Type | Description |
---|---|
finput | Describes all the fluids/flows that will go into the module |
foutput | Describes all the fluids/flows that will go out of the module |
control | Describes all the control signals that regulate the flow of the liquid |
So to declare the type of the I/O you're providing, it would be good to first declare the I/O variables in the module header and then explicitly declare the type associated with the variable first thing in the body of the module. Here's an example of such an explicit declaration:
module one_to_one(in, out);
finput in;
foutput out;
...
Today, during the demo, we are only going to support connections and not interactions. That means that you would be able to describe how the fluid networks can scale connect. Some examples of what you can create are shown in the presentation. Here's how we go about doing it.
To connect any two fluid-objects, we need to use the assign
statement. What the assign
statement does is that it creates a relationship that tells the LFR compiler that the fluid-objects, storage-objects and I/O-objects are connected with each other in the designs. This is the most fundamental way of describing these relationships. Here's an example that builds on the simple I/O example shown earlier.
module one_to_one(in, out);
finput in;
foutput out;
// The assign operator is making the connection here
assign out = in;
endmodule
Now that you have succeeded in making the connections, the next step is to create more intricate connections. To do that, you can create fluid
objects that can act as intermediate variables that abstract the complexity in the design process. Here's an example that builds on top of the previous example:
module one_to_one(in, out);
finput in;
foutput out;
fluid temp1;
fluid temp2;
assign temp1 = in;
assign temp2 = temp1;
assign out = in;
endmodule
One of the things that went into designing LFR was that it's intended to help you scale basic designs into something big without having you redo any of the architecture. So to scale up your designs, you need to convert your fluid-objects (or the I/O, storage elements wherever necessary) using vectors.
Creating vectors is super easy in LFR because all of the declared objects are by default vectors in the eyes of the compiler. Hence, all you need to do is to give a dimension to each of the objects that you're defining. The line fluid temp1;
would now become fluid [0:7] temp1;
. Here's an example that builds on the previous design:
module tree_bus(in, out);
finput in;
foutput out;
fluid [0:8] temp1;
fluid [0:8] temp2;
// We extend the base case to
// vectors having similar sizes
assign temp1[0:8] = in;
assign temp2[0:8] = temp1[0:8];
assign out = temp2[0:8];
endmodule