@type @plugin(ram_bool_v0, ram, 0, 8, 16, 2, 128, 64);
RAM Boolean v0 Plugin
The ram_bool_v0
plugin provides private indexed array access when using bit-vectors in Boolean ZK.
Types
The ram
type is a plugin type for a privately indexable array.
It uses the following plugin parameters.
-
Plugin Name:
ram_bool_v0
-
Type name:
ram
-
Number: type index for the index and element type
-
Number: number of bits in a RAM index
-
Number: number of bits in a RAM element
-
Number: number of allocations
-
Number: total size of all allocations (in elements)
-
Number: maximum size concurrently allocated buffers
The first three parameters, type and bit-counts, should be self explanatory. The latter three are there to aid in allocation schemes which a backend may utilize. For example, if a program had two allocations, both with 64 elements but one is free’d before the other allocated, the type could be declared as such. For the sake of the example, we’ll use type 0, 8-bits of index precision, and 16-bits of element precision.
If, instead, both were allocated before one was free’d, the concurrent allocation size would be the same as the total.
@type @plugin(ram_bool_v0, ram, 0, 8, 16, 2, 128, 128);
Operations
The following operations are used with RAM.
init
-
Create a new RAM buffer with specified size.
read
-
Retrieve and return the element at a specified index.
write
-
overwrite the value at the specified index.
Here is an example declaration of each.
// index/element is type 0, buffer is type 1, again 8-bit index and 16-bit element. // initialize a RAM buffer. Each element is given the input value as a start value. // The size is a constant given in the plugin binding. // buffer fill @function(ram_init_64, @out: 1:1, @in: 0:16) // size @plugin(ram_bool_v0, init, 64); // Read an element from the RAM buffer. // out buffer index @function(ram_read, @out: 0:16, @in: 1:1, 0:8) @plugin(ram_bool_v0, read); // Write an element into the RAM buffer. // buffer index element #function(ram_write, @in: 1:1, 0:8, 0:16) @plugin(ram_bool_v0, read);
init
Initialize a new RAM buffer.
For a given buffer type b
, index type i
, index bit-width iw
, size s
, and element bit-width ew
, the following parameters are used.
Note that i
must represent a field with prime 2.
-
Output
b:1
: The newly created buffer -
Input
i:ew
: A value copied into each buffer element
-
Plugin Name:
ram_bool_v0
-
Operation:
init
-
Number: the size of the buffer.
read
Read an element from the buffer.
For a given buffer type b
, index type i
, index bit-width iw
, size s
, and element bit-width ew
, the following parameters are used.
Note that i
must represent a field with prime 2.
Although the read
operation does not have a parameter for the size, if the index exceeds the size declared when the buffer was initialized, an @assert_zero
failure occurs.
If this failure occurs, the backend may assign to the output value an unspecified value.
-
Output
i:ew
: output value -
Input
b:1
: the buffer -
Input
i:iw
: the index
-
Plugin Name:
ram_bool_v0
-
Operation:
read
write
write an element into the buffer.
For a given buffer type b
, index type i
, index bit-width iw
, size s
, and element bit-width ew
, the following parameters are used.
Note that i
must represent a field with prime 2.
Although the write
operation does not have a parameter for the size, if the index exceeds the size declared when the buffer was initialized, an @assert_zero
failure occurs.
If this failure occurs, the backend may assign an unspecified value to the indexed element.
-
Input
b:1
: the buffer -
Input
i:iw
: the index -
Input
i:ew
: the input value
-
Plugin Name:
ram_bool_v0
-
Operation:
write
Implementing the ram_bool_v0
Plugin
The RAM plugin is likely the trickiest plugin to implement, because it utilizes "IR Plugin Types". That means that an IR type will describe wires which aren’t arithmeic or boolean values. Instead, wires in the RAM buffer type carry RAM buffers.
This means that in addition to implementing operations for init
, read
, and write
, you need a Buffer_T
template to take the place of Wire_T
, and you need a wtk::TypeBackend<Number, Buffer_T>
to allow NAILS to interact with your Buffer_T
.
Fortunately, whether you choose to implement your own RAM, or use the fallback implementation, WizToolKit has you covered.
Both the arithmetic RAM interface and fallback implementation reside in the #include <wtk/plugins/BooleanRAM.h>
header.
Implementing your own RAM Plugin
To implement your RAM plugin, you’ll need to start with your Buffer_T
type.
This type must follow the same rules as any other wire template used with NAILS: it must be default and move constructible.
Of course, you will also need a Wire_T
for the buffer’s private indices and elements.
Next you’ll need to implement a wtk::TypeBackend<Number_T, Buffer_T>
.
Ordinarily, this has callbacks for @add
and @mul
gates, however, such operations are nonsensical with RAM.
Instead, subclass the wtk::plugins::BoolRAMBackend<Number_T, Buffer_T, Wire_T>
, which indicates to NAILS that such gates are unsupported (and implements no-ops in the unused callbacks).
You must provide a constructor with the wire’s type and backend, along with the bit-widths, and implement the check()
method to indicate if proof errors occurred.
struct MyBoolRAMBackend
: public wtk::plugins::BoolRAMBackend <Number, MyBuffer, MyWire>
{
MyRAMBackend(wtk::type_idx type, wtk::TypeBackend<Number, MyWire>* backend,
wtk::wire_idx idx_bits, wtk::wire_idx elt_bits)
: wtk::plugins::BoolRAMBackend<Number, MyBuffer, MyWire>(
type, backend, idx_bits, elt_bits);
bool failure = false;
// Returns false if any errors are encountered, true otherwise
bool check() override
{
return !this->failure;
// alternatively, report failures through this->wireBackend->assertZero()
// and always return true;
}
};
You’ll need to implement the three RAM operations: init
, read
, and write
.
These have convenient superclasses which handle signature and argument checking and provide easy-to-implement callbacks.
Within each operation, the Wire_T*
parameters are list pointers with index or element bit-width.
The bit-widths, and the backend, may be accessed as attributes of the operation→ramBackend()
helper.
struct MyRAMInit
: public wtk::plugins::RAMInitOperation<Number, MyBuffer, MyWire>
{
void init(wtk::wire_idx const size,
MyBuffer* const buffer, MyWire const* const fill) override
{
/**
* Instantiate the buffer with the stated size. Then assign each
* element the fill value
*/
// fill has length
this->ramBackend()->elementBits;
}
};
struct MyRAMRead
: public wtk::plugins::RAMReadOperation<Number, MyBuffer, MyWire>
{
void read(MyWire* const out,
MyBuffer* const buffer, MyWire const* const idx) override
{
/* implement: out = buffer[idx]; */
// out has bit-length
this->ramBackend()->elementBits;
// idx has bit-length
this->ramBackend()->indexBits;
}
};
struct MyRAMWrite
: public wtk::plugins::RAMReadOperation<Number, MyBuffer, MyWire>
{
void write(MyBuffer* const buffer,
MyWire const* const idx, MyWire const* const in) override
{
/* implement: obuffer[idx] = in; */
// idx has bit-length
this->ramBackend()->indexBits;
// in has bit-length
this->ramBackend()->elementBits;
}
};
Lastly, you’ll want to implement the wtk::plugins::BoolRAMPlugin<Number_T, Buffer_T, Wire_T>
superclass, which is essentially a factory for your operations.
Fallback RAM Plugin
WizToolKit provides a buffer, wtk::plugins::FallbackBoolRAMBuffer<Wire_T>
, backend, wtk::plugins::FallbackBoolRAMBackend<Number_T, Wire_T>
, and plugin, wtk::plugins::FallbackBoolRAMPlugin<Number_T, Wire_T>
, the latter two using the afformentioned buffer to fill the middle Buffer_T
template.
Instantiating the RAM Plugin
To instantiate an instance of the RAM plugin, allocate both a plugin object and a RAM backend object. Pass the plugin object to your plugins manager and the backend to both the plugins manager and NAILS interpreter.
Although there is no Boolean RAM demo at this time, you can check out the Arithmetic RAM sample instead.