Skip to main content

Wrapping IP Cores

Occasionally in RustHDL, you will need to wrap an external IP core or logic primitive supported by your hardware, but that is not supported directly in RustHDL. There best method for wrapping Verilog code is to use the Wrapper struct and provide your own implementation of the hdl method for your logic.

Here is a minimal example of a clock buffer primitive (that takes a differential clock input and provides a single ended clock output). The Verilog module declaration for the clock buffer is simply:

module IBUFDS(I, B, O);
input I;
input B;
output O;
endmodule

Since the implementation of this device is built into the FPGA (it is a hardware primitive), the module definition is enough for the toolchain to construct the device. Here is a complete example of a wrapped version of this for use in RustHDL.

# use rust_hdl::prelude::*;

#[derive(LogicBlock, Default)]
pub struct ClockDriver {
pub clock_p: Signal<In, Clock>,
pub clock_n: Signal<In, Clock>,
pub sys_clock: Signal<Out, Clock>,
}

impl Logic for ClockDriver {
// Our simulation simply forwards the positive clock to the system clock
fn update(&mut self) {
self.sys_clock.next = self.clock_p.val();
}
// RustHDL cannot determine what signals are driven based on the declaration
// alone. This method identifies `sys_clock` as being driven by the internal
// logic of the device.
fn connect(&mut self) {
self.sys_clock.connect();
}
// Normally the `hdl` method is generated by the `derive` macro. But in this
// case we implement it ourselves to wrap the Verilog code.
fn hdl(&self) -> Verilog {
Verilog::Wrapper(Wrapper {
code: r#"
// This is basically arbitrary Verilog code that lives inside
// a scoped module generated by RustHDL. Whatever IP cores you
// use here must have accompanying core declarations in the
// cores string, or they will fail verification.
//
// In this simple case, we remap the names here
IBUFDS ibufds_inst(.I(clock_p), .B(clock_n), .O(sys_clock));

"#.into(),
// Some synthesis tools (like [Yosys] need a blackbox declaration so they
// can process the Verilog if they do not have primitives in their
// libraries for the device. Other toolchains will strip these out.
cores: r#"
(* blackbox *)
module IBUFDS(I, B, O);
input I;
input B;
output O;
endmodule"#.into(),
})
}
}