Traits
There is only one trait that you typically need to implement to get things to work in RustHDL with the simulation and synthesis frameworks. That is the Logic trait. Although you will rarely (if ever) need to implement the methods themselves, here is the full definition of the trait:
pub trait Logic {
fn update(&mut self);
fn connect(&mut self) {}
fn hdl(&self) -> Verilog {
Verilog::Empty
}
fn timing(&self) -> Vec<TimingInfo> {
vec![]
}
}
The methods are quite simple:
update
- this updates the state of the logical block based on the inputs and internal state. In general, this is where the action of the logical block takes place.connect
- this is where we claim whatever signals we drive, by callingconnect
on them.hdl
- this method returns the Verilog description for our logical block in the form of an Verilog enum.timing
- this is where specific timing exceptions or requirements are expressed for the logical block.
In almost all cases, you will use the #[derive(LogicBlock)]
macro to derive all of the traits from your own update
method, written in Rust.
If we revisit the Blinky
example, note that
we only provided the update
method, with an attribute of #[hdl_gen]
, which in turn
generated the remaining trait implementations:
#[derive(LogicBlock)]
struct Blinky {
pub clock: Signal<In, Clock>,
pulser: Pulser,
pub led: Signal<Out, Bit>,
}
impl Logic for Blinky {
#[hdl_gen]
fn update(&mut self) {
self.pulser.clock.next = self.clock.val();
self.pulser.enable.next = true.into();
self.led.next = self.pulser.pulse.val();
}
}
There are a couple of other traits that RustHDL uses that you should be aware of.
Synth
- this trait is provided on types that can be represented in hardware, i.e. as a set of bits. You will probably not need to implement this trait yourself, but if you need some special type representationFoo
, andimpl Synth for Foo
, then RustHDL will be able to generate Verilog code for it.Block
- this trait is needed on anystruct
that is a composition of circuit elements (pretty much every struct used to model a circuit). This should be auto-derived.Logic
- Sometimes, you will need to override the default implementations of theLogic
trait. In those cases, (when you are providing a custom simulation model, or wrapping a black box Verilog routine), you will need toimpl
the other methods.
The main need for implementing a trait is when you want to represent some logic block that has "magic" internals. Like a RAM block or some special SerDes circuitry.