RRR Compilation - LEAP-FPGA/leap-documentation GitHub Wiki
In this tutorial, we will examine the operation of the RRR compiler using the STDIO service as an example. STDIO uses RRR to allow hardware to make use of the software-side STDIO library. The code samples shown below are actual examples used in LEAP source, or produced by the RRR compiler. To reduce the burden of understanding, some parts of the code have been removed. Excisions are denoted by ...
.
This tutorial covers only the compilation of RRR. Other tutorials cover:
RRR Programming
RRR Architecture
RRR generates host-side interfaces in the form of C++ classes.
…
#define STDIO_METHOD_ID_Sync 2
…
typedef UINT8 IN_TYPE_Sync;
typedef UINT8 OUT_TYPE_Sync;
…
typedef class STDIO_CLIENT_STUB_CLASS* STDIO_CLIENT_STUB;
class STDIO_CLIENT_STUB_CLASS: public PLATFORMS_MODULE_CLASS
{
…
OUT_TYPE_Sync Sync(UINT8 dummy)
{
UMF_MESSAGE msg = new UMF_MESSAGE_CLASS;
msg→SetLength(1);
msg→SetServiceID(STDIO_SERVICE_ID);
msg→SetMethodID(STDIO_METHOD_ID_Sync);
msg→AppendUINT8(dummy);
…
}
#endif
The RRR compiler generates the above code. The Sync
method accepts a single argument which is marshalled into a unified RRR packet format.
server_stub_STDIO.h
#ifndef __STDIO_SERVER_STUB__ #define __STDIO_SERVER_STUB__ typedef struct { UINT64 data; UINT8 eom; } IN_TYPE_Req; ... typedef class STDIO_SERVER_STUB_CLASS* STDIO_SERVER_STUB; class STDIO_SERVER_STUB_CLASS: public RRR_SERVER_STUB_CLASS, public PLATFORMS_MODULE_CLASS { private: STDIO_SERVER server; public: ... UMF_MESSAGE Request(UMF_MESSAGE req) { #ifdef BYPASS_SERVER_STUB return server->Request(req); #else UMF_MESSAGE resp = NULL; UINT32 methodID = req->GetMethodID(); switch(methodID) { case STDIO_METHOD_ID_Req: { UINT8 eom = req->ExtractUINT8(); UINT64 data = req->ExtractUINT64(); server->Req(data, eom); delete req; break; } ... } } } #endif
The above code is synthesized by the RRR compiler. For compound arguments and return types RRR will generate a C structure. When the software server receives a request from the FPGA it will decode the message header and invoke the appropriate user-defined handler.
RRR produces hardware-side interfaces in the form of Bluespec System Verilog modules, which make use of SoftConnections. As a result, hardware clients and servers may be instantiated anywhere in a user design, without affecting the user interfaces. Client and server interfaces may be instantiated inside the same code. Methods on hardware clients and servers are split-phase, reflecting the non-atomicity of transporting data to and from the host.
Each RRR server links to the root RRR service by way of soft connections.
…
typedef UINT8 IN_TYPE_Sync;
typedef UINT8 OUT_TYPE_Sync;
…
// Complete hardware server interface.
interface ServerStub_STDIO;
method ActionValue#(IN_TYPE_Rsp) acceptRequest_Rsp();
method IN_TYPE_Rsp peekRequest_Rsp();
method ActionValue#(IN_TYPE_Rsp64) acceptRequest_Rsp64();
method IN_TYPE_Rsp64 peekRequest_Rsp64();
method ActionValue#(IN_TYPE_Sync) acceptRequest_Sync();
method IN_TYPE_Sync peekRequest_Sync();
method Action sendResponse_Sync(UINT8 ack);
method ActionValue#(IN_TYPE_SetCondMask) acceptRequest_SetCondMask();
method IN_TYPE_SetCondMask peekRequest_SetCondMask();
endinterface
module [CONNECTED_MODULE] mkServerStub_STDIO (ServerStub_STDIO);
// Soft connection to root RRR handling
Connection_Receive#(UMF_PACKET) link_req <- mkConnection_Receive(“rrr_serv\
er_STDIO_req”);
Connection_Send#(UMF_PACKET) link_resp <- mkConnection_Send(“rrr_server_\
STDIO_resp”);
// Link management logic
Reg#(UMF_METHOD_ID) mid <- mkReg(0);
Integer mid_Rsp = 0;
Integer mid_Rsp64 = 1;
Integer mid_Sync = 2;
Integer numChunks_Sync = (8 % valueOf(UMF_CHUNK_BITS)) == 0 ?
(8 / valueOf(UMF_CHUNK_BITS)) :
(8 / valueOf(UMF_CHUNK_BITS)) + 1;
Integer mid_SetCondMask = 3;
…
// handler for specific RRR method
method ActionValue#(IN_TYPE_Sync) acceptRequest_Sync() if (mid == fromInteg\
er(mid_Sync));
let a <- dem.readAndDelete();
IN_TYPE_Sync retval = unpack(truncate(a));
return retval;
endmethod
…
endmodule
The generated code for the RRR server stub consists of three methods in Bluespec for each hardware method. peekRequest
allows the user code to observe the first request available in the server. The acceptRequest
pops the latest request from the server stub. sendResponse
returns a response to the host. Those methods without a response value do not have a sendResponse
method.
RRR uses a shared marshaller and a shared demarshaller per stub. Thus, only one Request
and one Response
method can potentially be invoked in a cycle. The rules startRequest
, continueRequest
, and continueResponse
in the example above deal with formatting data packets.
`ifndef _STDIO_CLIENT_STUB_ `define _STDIO_CLIENT_STUB_ ... typedef struct { UINT64 data; UINT8 eom; } IN_TYPE_Req deriving (Bits, Eq); interface ClientStub_STDIO; method Action makeRequest_Req(UINT64 data, UINT8 eom); endinterface module [CONNECTED_MODULE] mkClientStub_STDIO (ClientStub_STDIO); Connection_Send#(UMF_PACKET) link_req <- mkConnection_Send("rrr_client_\ STDIO_req"); Connection_Receive#(UMF_PACKET) link_resp <- mkConnection_Receive("rrr_clie\ nt_STDIO_resp"); // No return value means that no Demarshaller is required. MARSHALLER_N#(UMF_CHUNK, Bit#(72)) mar <- mkSimpleMarshallerN(True); ... rule continueRequest (True); UMF_CHUNK chunk = mar.first(); mar.deq(); link_req.send(tagged UMF_PACKET_dataChunk chunk); endrule method Action makeRequest_Req(UINT64 data, UINT8 eom) if (! mar.notEmpty); let req = IN_TYPE_Req { data:data, eom:eom }; UMF_PACKET header = tagged UMF_PACKET_header UMF_PACKET_HEADER { filler: ?, phyChannelPvt: ?, channelID: ?, serviceID: `SERVICE_ID, methodID : fromInteger(mid_Req), numChunks: fromInteger(numChunks_Req) }; link_req.send(header); mar.enq(zeroExtend(pack(req)), fromInteger(numChunks_Req)); endmethod endmodule `endif
Each client method may have up to three synthesized Bluespec methods. makeRequest
sends a request to software. peekResponse
allows user code to examine the next response from the host. acceptResponse
pops the response from the host. Because the Req
method of STDIO has no response from host, the Response
methods and their corresponding support hardware are not included in the client stub.
As with the RRR server, the RRR client also uses a shared marshalling architecture, and is subject to the same parallelism restrictions.