rust_hdl/lib.rs
1//! ** Write FPGA Firmware using Rust! **
2//!
3//!
4//! RustHDL is a crate that allows you to write FPGA firmware using Rust!
5//! Specifically, `rust-hdl` compiles a subset of Rust down to Verilog so that
6//! you can synthesize firmware for your FPGA using standard tools. It also
7//! provides tools for simulation, verification, and analysis along with strongly
8//! typed interfaces for making sure your design works before heading to the bench.
9//! The workflow is very similar to GPU programming. You write everything in Rust,
10//! including an update `kernel` that compiles down onto the hardware. You can simulate
11//! and verify everything from the safety and comfort of your Rust environment, and
12//! then head over to standard synthesis tools to get files that program your FPGA.
13//!
14//! ## Links
15//!
16//! You may want:
17//!
18//! - [API Documentation](https://docs.rs/rust-hdl/latest/rust_hdl/)
19//! - [GitHub](https://github.com/samitbasu/rust-hdl)
20//! - [Home Page](https://rust-hdl.org)
21//!
22//! ## Features
23//! * Safe - have Rust check the validity of your firmware with
24//! strongly typed interfaces at **compile** time, as well as at
25//! run time, synthesis, and on the device.
26//! * Fast - Run simulations of your designs straight from your
27//! Rust code, with pretty good simulation performance.
28//! * Readable - RustHDL outputs Verilog code for synthesis and
29//! implementation, and goes through some effort to make sure that
30//! code is readable and understandable, in case you need to resolve
31//! timing issues or other conflicts.
32//! * Reusable - RustHDL supports templated firmware for parametric
33//! use, as well as a simple composition model based on structs.
34//! * Batteries Included - RustHDL includes a set of basic firmware
35//! widgets that provide FIFOs, RAMs and ROMs, Flip flops, SPI components,
36//! PWMs etc, so you can get started quickly.
37//! * Free - Although you can use RustHDL to wrap existing IP cores,
38//! all of the RustHDL code and firmware is open source and free to use (as in speech and beer).
39//! * Tested - RustHDL has been used to write firmware that is shipping in commercial products.
40//! This includes quite complicated designs that use nearly all of a moderately sized FPGA,
41//! and take advantage of specialized hardware in the FPGAs themselves.
42//!
43//! ## Quickstart
44//!
45//! The definitive example in FPGA firmware land is a simple LED blinker. This typically
46//! involves a clock that is fed to the FPGA with a pre-defined frequency, and an output
47//! signal that can control an LED. Because we don't know what FPGA we are using, we will
48//! do this in simulation first. We want a blink that is 250 msec long every second, and
49//! our clock speed is (a comically slow) 10kHz. Here is a minimal working Blinky! example:
50//!
51//! ```
52//! use std::time::Duration;
53//! use rust_hdl::prelude::*;
54//!
55//! const CLOCK_SPEED_HZ : u64 = 10_000;
56//!
57//! #[derive(LogicBlock)] // <- This turns the struct into something you can simulate/synthesize
58//! struct Blinky {
59//! pub clock: Signal<In, Clock>, // <- input signal, type is clock
60//! pulser: Pulser, // <- sub-circuit, a widget that generates pulses
61//! pub led: Signal<Out, Bit>, // <- output signal, type is single bit
62//! }
63//!
64//! impl Default for Blinky {
65//! fn default() -> Self {
66//! Self {
67//! clock: Default::default(),
68//! pulser: Pulser::new(CLOCK_SPEED_HZ, 1.0, Duration::from_millis(250)),
69//! led: Default::default(),
70//! }
71//! }
72//! }
73//!
74//! impl Logic for Blinky {
75//! #[hdl_gen] // <- this turns the update function into an HDL Kernel that can be turned into Verilog
76//! fn update(&mut self) {
77//! // v-- write to the .next member v-- read from .val() method
78//! self.pulser.clock.next = self.clock.val();
79//! self.pulser.enable.next = true.into();
80//! self.led.next = self.pulser.pulse.val();
81//! }
82//! }
83//!
84//! fn main() {
85//! // v--- build a simple simulation (1 testbench, single clock)
86//! let mut sim = simple_sim!(Blinky, clock, CLOCK_SPEED_HZ, ep, {
87//! let mut x = ep.init()?;
88//! wait_clock_cycles!(ep, clock, x, 4*CLOCK_SPEED_HZ);
89//! ep.done(x)
90//! });
91//!
92//! // v--- construct the circuit
93//! let mut uut = Blinky::default();
94//! // v--- run the simulation, with the output traced to a .vcd file
95//! sim.run_to_file(Box::new(uut), 5 * SIMULATION_TIME_ONE_SECOND, "blinky.vcd").unwrap();
96//! vcd_to_svg("/tmp/blinky.vcd","images/blinky_all.svg",&["uut.clock", "uut.led"], 0, 4_000_000_000_000).unwrap();
97//! vcd_to_svg("/tmp/blinky.vcd","images/blinky_pulse.svg",&["uut.clock", "uut.led"], 900_000_000_000, 1_500_000_000_000).unwrap();
98//! }
99//! ```
100//!
101//! Running the above (a release run is highly recommended) will generate a `vcd` file (which is
102//! a trace file for FPGAs and hardware in general). You can open this using e.g., `gtkwave`.
103//! If you have, for example, an Alchitry Cu board you can generate a bitstream for this exampling
104//! with a single call. It's a little more involved, so we will cover that in the detailed
105//! documentation. It will also render that `vcd` file into an `svg` you can view with an ordinary
106//! web browser. This is the end result showing the entire simulation:
107//! 
108//! Here is a zoom in showing the pulse to the LED
109//! 
110//!
111//! The flow behind RustHDL is the following:
112//!
113//! - Circuits are modelled using simple `struct`s, composed of other circuit elements and
114//! signal wires that interconnect them.
115//! - A `#[derive(LogicBlock)]` annotation on the struct adds autogenerated code needed by
116//! RustHDL.
117//! - You `impl Logic` on your `struct`, and provide the `fn update(&mut self)` method, which
118//! is the HDL update kernel.
119//! - That gets annotated with a `#[hdl_gen]` attribute to generate HDL from the Rust code
120//! - You can then simulate and synthesize your design - either in software, or by using an
121//! appropriate BSP and toolchain.
122//!
123//! The rest is detail. Some final things to keep in mind.
124//!
125//! - RustHDL is a strict subset of Rust. The `rustc` compiler must be satisfied with your
126//! design first. That means types, exhaustive enum matching, etc.
127//! - The goal is to eliminate a class of mistakes that are easy to make in other HDLs with
128//! checks taking place at compile time, via static analysis at run time, and then with
129//! testbenches.
130//! - Although the performance can always be improved, RustHDL is pretty fast, especially in
131//! release mode.
132//!
133//! ## Types
134//!
135//! There are a couple of key types you should be comfortable with to use RustHDL. The first
136//! is the [Bits](core::bits::Bits) type, which provides a compile-time arbitrary width bit vector.
137//!
138//! ### Representing bits
139//!
140//! The [Bits](core::bits::Bits) type is a `Copy` enabled type that you can construct from integers,
141//! from the `Default` trait, or from other `Bits`. Mostly, it is meant to stay out of your way
142//! and behave like a `u32`.
143//!
144//! ```
145//! # use rust_hdl::prelude::*;
146//! let x: Bits<50> = Default::default();
147//! ```
148//! This will construct a length 50 bit vector that is initialized to all `0`.
149//!
150//! You can also convert from literals into bit vectors using the [From] and [Into] traits,
151//! provided the literals are of the `u64` type.
152//!
153//! ```
154//! # use rust_hdl::prelude::*;
155//! let x: Bits<50> = 0xBEEF.into();
156//! ```
157//!
158//! In some cases, Rust complains about literals, and you may need to provide a suffix:
159//! ```
160//! # use rust_hdl::prelude::*;
161//! let x: Bits<50> = 0xDEAD_BEEF_u64.into();
162//! ```
163//! However, in most cases, you can leave literals suffix-free, and Rust will automatically
164//! determine the type from the context.
165//!
166//! You can construct a larger constant using the [bits] function. If you have a literal of up to
167//! 128 bits, it provides a functional form
168//! ```
169//! # use rust_hdl::prelude::*;
170//! let x: Bits<200> = bits(0xDEAD_BEEE); // Works for up to 128 bit constants.
171//! ```
172//!
173//! There is also the [ToBits] trait, which is implemented on the basic unsigned integer types.
174//! This trait allows you to handily convert from different integer values
175//!
176//! ```
177//! # use rust_hdl::prelude::*;
178//! let x: Bits<10> = 32_u8.to_bits();
179//! ```
180//!
181//! ### Operations
182//!
183//! The [Bits](core::bits::Bits) type supports a subset of operations that can be synthesized in
184//! hardware. You can perform
185//!
186//! * Addition between `Bits` of the same size using the `+` operator
187//! * Subtraction between `Bits` of the same size using the `-` operator
188//! * Bitwise logical `AND` between `Bits` of the same size using the `&` operator
189//! * Bitwise logical `OR` between `Bits` of the same size using the `|` operator
190//! * Bitwise logical `XOR` (Exclusive Or) between `Bits` of the same size using the `^` operator
191//! * Bitwise comparisons for equality between `Bits` of the same size using `==` and `!=` operators
192//! * Unsigned comparisons (e.g., `>,>=,<,<=`) between `Bits` of the same size - these are
193//! always treated as unsigned values for comparison purposes.
194//! * Shift left using the `<<` operator
195//! * Shift right (no sign extension!) using the '>>' operator
196//! * Bitwise logical `NOT` using the `!` prefix operator
197//!
198//! These should feel natural when using RustHDL, as expressions follow Rust's rules (and not Verilog's).
199//! For example:
200//! ```rust
201//! # use rust_hdl::prelude::*;
202//! let x: Bits<32> = 0xDEAD_0000_u32.to_bits();
203//! let y: Bits<32> = 0x0000_BEEF_u32.to_bits();
204//! let z = x + y;
205//! assert_eq!(z, 0xDEAD_BEEF_u32.to_bits());
206//! ```
207//!
208//! You can, of course, construct expressions of arbitrary complexity using parenthesis, etc.
209//! The only real surprise may be at synthesis time, when you try to fit the expression onto hardware.
210//!
211//! ### Signal Type
212//!
213//! *Signals are software abstractions to represent physical wires*. The [Signal](core::signal::Signal)
214//!type is generic over a couple of parameters. The first is meant to indicate the driver of the wire.
215//! In RustHDL, every wire must have exactly one driver. It is the hardware equivalent of the
216//! single writer principle. You can have as many readers as you want, but only one writer. Unfortunately,
217//! there are some subtleties here, and declaring ownership of a wire using the type system is
218//! imperfect. Instead, we settle for a signalling mechanism of _intent_. So you mark a
219//! signal as how you intend to use it in your logic. For example, consider the following circuit:
220//!
221//! ```rust
222//! # use rust_hdl::prelude::*;
223//!
224//! pub struct My8BitAdder {
225//! pub input_1: Signal<In, Bits<8>>,
226//! pub input_2: Signal<In, Bits<8>>,
227//! pub output: Signal<Out, Bits<8>>,
228//! }
229//! ```
230//! In this case, the fields of the adder circuit are marked as `pub` so they can be accessed from
231//! outside the circuit. The [Direction](core::signal::Direction) argument to the [Signal] indicates
232//! how the given circuit intends to utilize the various wires. In this case, `input_1` and `input_2`
233//! should be considered inputs, and `output` is, obviously, an output. As such, `My8BitAdder` is
234//! promising you that it will drive the `output` wire. If it fails to actually do so (by leaving
235//! it undriven), then you will get an error when you try to use it in a design.
236//!
237//! *RustHDL does not allow undriven nets*. They are treated similarly to uninitialized memory in Rust.
238//! You _must_ drive every net in the design. Furthermore, you can have only one driver for each
239//! net. These two principles are core to RustHDL!
240//!
241//! The second part of a [Signal] is that it is _typed_. In general, the type signature is meant
242//! to convey something about the nature of the data being stored or passed. In the case of
243//! `My8BitAdder`, it doesn't say much - only that the input is an unsigned 8-bit value. But
244//! the types can be more complicated, including collections of signals running in multiple
245//! directions (as is typical for a bus or other complex interface).
246//!
247//! Signals can also be bidirectional with the [InOut](core::signal::Direction::InOut) designation.
248//! But you typically can only use these types of signals at the edge of your device. More on that
249//! elsewhere.
250//!
251//! The definition of [Signal] also indicates how it should be used. [Signal]'s cannot be
252//! assigned to using usual semantics.
253//! ```
254//! # use rust_hdl::prelude::*;
255//!
256//! #[derive(Clone, Debug)]
257//! pub struct Signal<D: Direction, T: Synth> {
258//! pub next: T,
259//! pub changed: bool,
260//! // Internal details omitted
261//!# dir: std::marker::PhantomData<D>,
262//! }
263//!```
264//!
265//! To change (drive) the value of a signal, you assign to the `next` field. To read the
266//! value of the signal (i.e. to get it's current state without driving it), you use the `val()` method.
267//! This is in keeping with the idea that you treat the signal differently if you want to drive
268//! it to some value, versus if you want to read it, as in hardware, these are different functions.
269//! In most cases, you will read from `val()` of the input signals, and write to the `.next` of the
270//! output signal. For example, in the `My8BitAdder` example, you would read from `input_1.val()`
271//! and from `input_2.val()`, and write to `output.next`. Like this:
272//!
273//! ```
274//! # use rust_hdl::prelude::*;
275//!
276//! pub struct My8BitAdder {
277//! pub input_1: Signal<In, Bits<8>>,
278//! pub input_2: Signal<In, Bits<8>>,
279//! pub output: Signal<Out, Bits<8>>,
280//! }
281//!
282//! impl Logic for My8BitAdder {
283//! fn update(&mut self) {
284//! self.output.next = self.input_1.val() + self.input_2.val();
285//! }
286//! }
287//!
288//! ```
289//!
290//! In general, this is the pattern to follow. However, there are some exceptions. Sometimes,
291//! you will want a "scratchpad" for holding intermediate results in a longer expression.
292//! For example, suppose you want to logically OR a bunch of values together, but want to
293//! logically shift them into different positions before doing so. Let us assume you have
294//! a logical block that looks like this:
295//!
296//! ```
297//! # use rust_hdl::prelude::*;
298//!
299//! pub struct OrStuff {
300//! pub val1: Signal<In, Bit>,
301//! pub val2: Signal<In, Bits<4>>,
302//! pub val3: Signal<In, Bits<2>>,
303//! pub val4: Signal<In, Bit>,
304//! pub combined: Signal<Out, Bits<8>>,
305//! pad: Signal<Local, Bits<8>>,
306//! }
307//! ```
308//!
309//! In this case, the `pad` field (which is private to the logic) has a direction of `Local`,
310//! which means it can be used to write and read from in the same circuit, as _long as you write first_!
311//! Hence, you can do something like this in the `update` method
312//!
313//! ```
314//! # use rust_hdl::prelude::*;
315//!
316//!# pub struct OrStuff {
317//!# pub val1: Signal<In, Bit>,
318//!# pub val2: Signal<In, Bits<4>>,
319//!# pub val3: Signal<In, Bits<2>>,
320//!# pub val4: Signal<In, Bit>,
321//!# pub combined: Signal<Out, Bits<8>>,
322//!# pad: Signal<Local, Bits<8>>,
323//!# }
324//!
325//! impl Logic for OrStuff {
326//! fn update(&mut self) {
327//! self.pad.next = 0.into(); // Write first
328//! self.pad.next = self.pad.val() | bit_cast::<8,1>(self.val1.val().into()); // Now we can read and write to it
329//! self.pad.next = self.pad.val() | (bit_cast::<8,4>(self.val2.val()) << 1);
330//! self.pad.next = self.pad.val() | (bit_cast::<8,2>(self.val3.val()) << 5);
331//! self.pad.next = self.pad.val() | (bit_cast::<8,1>(self.val4.val().into()) << 7);
332//! self.combined.next = self.pad.val();
333//! }
334//! }
335//! ```
336//!
337//! You can understand this behavior by either "folding" all of the expressions into a single
338//! long expression (i.e., by eliminating `self.pad` altogether) and just assigning the output
339//! to an expression consisting of the various inputs OR-ed together. Nonetheless, it is
340//! handy to be able to compute intermediate values and read them back elsewhere in the code.
341//!
342//! * Note that `.next` should _never_ appear on the right hand side of an expression! *
343//!
344//! The following code will fail to compile, because once we try to derive HDL from the result,
345//! RustHDL realizes it makes no sense.
346//!
347//! ```compile_fail
348//! # use rust_hdl::prelude::*;
349//!# pub struct OrStuff {
350//!# pub val1: Signal<In, Bit>,
351//!# pub val2: Signal<In, Bits<4>>,
352//!# pub val3: Signal<In, Bits<2>>,
353//!# pub val4: Signal<In, Bit>,
354//!# pub combined: Signal<Out, Bits<8>>,
355//!# pad: Signal<Local, Bits<8>>,
356//!# }
357//! impl Logic for OrStuff {
358//! #[hdl_gen]
359//! fn update(&mut self) {
360//! self.pad.next = 0.into(); // Write first
361//! self.pad.next = self.pad.next | bit_cast::<8,1>(self.val1.val().into()); // Fails! Can only write to .next
362//! self.combined.next = self.pad.val();
363//! }
364//! }
365//!```
366//!
367//! Detecting the case in which you fail to write to a signal before reading from it is more complicated
368//! and must be done a run time. The macro processor is not sophisticated enough to detect that case at the moment.
369//! However, it can be found when your logic is checked for correctness by the static analyzer.
370//!
371//! Normally, the Verilog code generator or the Simulation engine will statically check your design for you.
372//! However, you can also check the design yourself using the [check_all](core::check_error::check_all)
373//! function. Here is an example of that check being run on a logic block that attempts to write
374//! to an input signal being passed into the block. The example panics because
375//!
376//!```should_panic
377//! # use rust_hdl::prelude::*;
378//!
379//! #[derive(LogicBlock, Default)]
380//! struct BadActor {
381//! pub in1: Signal<In, Bit>,
382//! pub in2: Signal<In, Bit>,
383//! pub out1: Signal<Out, Bit>,
384//! }
385//!
386//! impl Logic for BadActor {
387//! #[hdl_gen]
388//! fn update(&mut self) {
389//! // This is definitely not OK
390//! self.in1.next = true;
391//! // This is fine
392//! self.out1.next = self.in2.val();
393//! }
394//! }
395//!
396//! // This will panic with an error of CheckError::WritesToInputs, pointing to self.in1
397//! check_all(&BadActor::default()).unwrap()
398//! ```
399//!
400//! ## Traits
401//!
402//! There is only one trait that you typically need to implement to get things to work in RustHDL
403//! with the simulation and synthesis frameworks. That is the [Logic](core::logic::Logic) trait.
404//! Although you will rarely (if ever) need to implement the methods themselves, here is the
405//! full definition of the trait:
406//!```
407//! # use rust_hdl::prelude::*;
408//!
409//! pub trait Logic {
410//! fn update(&mut self);
411//! fn connect(&mut self) {}
412//! fn hdl(&self) -> Verilog {
413//! Verilog::Empty
414//! }
415//! fn timing(&self) -> Vec<TimingInfo> {
416//! vec![]
417//! }
418//! }
419//! ```
420//!
421//! The methods are quite simple:
422//!
423//! * `update` - this updates the state of the logical block based on the inputs and internal state.
424//! In general, this is where the action of the logical block takes place.
425//! * `connect` - this is where we claim whatever signals we drive, by calling `connect` on them.
426//! * `hdl` - this method returns the Verilog description for our logical block in the form of
427//! an [Verilog](core::ast::Verilog) enum.
428//! * `timing` - this is where specific timing exceptions or requirements are expressed for the
429//! logical block.
430//!
431//! In almost all cases, you will use the `#[derive(LogicBlock)]` macro to derive all of the traits from
432//! your own `update` method, written in Rust. If we revisit the `Blinky` example, note that
433//! we only provided the `update` method, with an attribute of `#[hdl_gen]`, which in turn
434//! generated the remaining trait implementations:
435//! ```
436//! # use std::time::Duration;
437//! # use rust_hdl::prelude::*;
438//! # use rust_hdl::docs::vcd2svg::vcd_to_svg;
439//!
440//! # const CLOCK_SPEED_HZ : u64 = 10_000;
441//!
442//! #[derive(LogicBlock)]
443//! struct Blinky {
444//! pub clock: Signal<In, Clock>,
445//! pulser: Pulser,
446//! pub led: Signal<Out, Bit>,
447//! }
448//!
449//! # impl Default for Blinky {
450//! # fn default() -> Self {
451//! # Self {
452//! # clock: Default::default(),
453//! # pulser: Pulser::new(CLOCK_SPEED_HZ, 1.0, Duration::from_millis(250)),
454//! # led: Default::default(),
455//! # }
456//! # }
457//! # }
458//!
459//! impl Logic for Blinky {
460//! #[hdl_gen]
461//! fn update(&mut self) {
462//! self.pulser.clock.next = self.clock.val();
463//! self.pulser.enable.next = true.into();
464//! self.led.next = self.pulser.pulse.val();
465//! }
466//! }
467//!```
468//!
469//! There are a couple of other traits that RustHDL uses that you should be aware of.
470//!
471//! - `Synth` - this trait is provided on types that can be represented in hardware, i.e. as
472//! a set of bits. You will probably not need to implement this trait yourself, but if you
473//! need some special type representation `Foo`, and `impl Synth for Foo`, then RustHDL will
474//! be able to generate Verilog code for it.
475//! - `Block` - this trait is needed on any `struct` that is a composition of circuit elements
476//! (pretty much every struct used to model a circuit). This should be auto-derived.
477//! - `Logic` - Sometimes, you will need to override the default implementations of the `Logic`
478//!trait. In those cases, (when you are providing a custom simulation model, or wrapping a
479//!black box Verilog routine), you will need to `impl` the other methods.
480//!
481//! ## The Synthesizable Subset of Rust and the HDL Kernel
482//!
483//! RustHDL uses procedural macros to define a subset of the Rust language that can be used to
484//! describe actual hardware. That subset is known as the synthesizable subset of Rust. It is
485//! quite limited because the end result is translated into Verilog and ultimately into hardware
486//! configuration for the FPGA.
487//!
488//! - The HDL kernel must be valid Rust! If you remove the `#[hdl_gen]` attribute, the code
489//! must still be accepted by `rustc`! That means you must satisfy the type constraints, the
490//! private nature of the struct fields, etc. This is one of the major benefits of RustHDL. It
491//! takes code that is already been checked by `rustc` and then converts it into HDL.
492//!
493//! So this will _clearly_ fail to compile.
494//!
495//! ```compile_fail
496//! # use rust_hdl::prelude::*;
497//!
498//! struct Foo {
499//! bar: Signal<Out, Bits<4>>
500//! }
501//!
502//! impl Logic for Foo {
503//! #[hdl_gen]
504//! fn update(&mut self) {
505//! self.bar.next = "Oy!"; // Type issue here...
506//! }
507//! }
508//! ```
509//!
510//! - The `#[hdl_gen]` attribute can only be applied to a function (aka HDL Kernel) that
511//! takes `&mut self` as an argument. In almost all cases, you will write something like:
512//!
513//! ```
514//!# use rust_hdl::prelude::*;
515//!
516//! struct Foo {}
517//!
518//! impl Logic for Foo {
519//! #[hdl_gen]
520//! fn update(&mut self) {
521//! // Put your synthesizable subset of Rust here...
522//! }
523//! }
524//! ```
525//!
526//! - The body of the `update` function must be a single block, consisting of statements.
527//! Local definitions and items are not allowed in HDL kernels. The following, for example, will
528//!fail. This is an example of valid Rust that is not allowed in an HDL kernel.
529//!
530//!```compile_fail
531//! # use rust_hdl::prelude::*;
532//!
533//! struct Foo {}
534//!
535//! impl Logic for Foo {
536//! #[hdl_gen]
537//! fn update (&mut self) {
538//! // Fails because local items are not allowed in HDL kernels.
539//! let x = 32;
540//! }
541//! }
542//!```
543//!
544//! - Assignments are allowed as long as you follow the rules about signals. Types are
545//! still enforced by Rust.
546//! - Indexed assignments are currently not supported
547//! - Signal assignments must be to either `.next` or `.next.field` if the signal is struct based.
548//!
549//! So valid assignments will be of the form `self.<signal>.next = <expr>`, or for structure-valued
550//! signals.
551//!
552//! - Expressions support accessing fields of a signal
553//! - Binary operations supported are `+`, `-`, `*`, `&&`, `||`, `^`, `&`, `|`, `<<`, `>>`, `==`, `<`, `<=`, `!=`, `>`, `>=`
554//! In general, binary operations require that both arguments are of the same type (e.g. bitwidth) or one of the
555//! arguments will be a literal.
556//! ```rust
557//! # use rust_hdl::prelude::*;
558//!
559//! struct Foo {
560//! pub sig1: Signal<In, Bits<4>>,
561//! pub sig2: Signal<In, Bits<4>>,
562//! pub sig3: Signal<Out, Bits<4>>,
563//! }
564//!
565//! impl Logic for Foo {
566//! #[hdl_gen]
567//! fn update(&mut self) {
568//! self.sig3.next = self.sig1.val() + 4; // Example of binop with a literal
569//! self.sig3.next = self.sig1.val() ^ self.sig2.val(); // Example of a binop with two bitvecs
570//! }
571//! }
572//! ```
573//!
574//! - Unary operations supported are `-` and `!`
575//! The `-` operator is only supported for `Signed` types. Otherwise, it makes no sense. If
576//! you want to compute the 2's complement of an unsigned value, you need to do so explicitly.
577//! The `!` operator will flip all of the bits in the bitvector.
578//! - Conditionals (`if`) are supported
579//!```rust
580//! # use rust_hdl::prelude::*;
581//!
582//! struct Foo {
583//! pub sig1: Signal<In, Bit>,
584//! pub sig2: Signal<Out, Bits<2>>,
585//! pub sig3: Signal<In, Bits<2>>,
586//! pub sig4: Signal<Out, Bits<2>>,
587//! }
588//!
589//! impl Logic for Foo {
590//! #[hdl_gen]
591//! fn update(&mut self) {
592//! self.sig2.next = 0.into(); // Latch prevention!
593//! // Straight `if`s are supported, but beware of latches!
594//! // This `if` statement would generate a latch if not for
595//! // the unconditional assign to `sig2`
596//! if self.sig1.val() {
597//! self.sig2.next = 1.into();
598//! }
599//! // You can use `else` clauses also
600//! if self.sig1.val() {
601//! self.sig2.next = 1.into();
602//! } else {
603//! self.sig2.next = 2.into();
604//! }
605//! // Nesting and chaining are also fine
606//! if self.sig3.val() == 0 {
607//! self.sig4.next = 3.into();
608//! } else if self.sig3.val() == 1 {
609//! self.sig4.next = 2.into();
610//! } else {
611//! self.sig4.next = 0.into(); // <- Fall through else prevents latch
612//! }
613//! }
614//! }
615//! ```
616//! - Literals (provided they implement the `Synth` trait) are supported. In most cases, you
617//! can used un-suffixed literals (like `1` or `0xDEAD`) as add `.into()`.
618//! - Function calls - RustHDL kernels support a very limited number of function calls, all of
619//! which are ignored in HDL at the moment (they are provided to make `rustc` happy)
620//! - `bit_cast`
621//! - `signed_bit_cast`
622//! - `unsigned_cast`
623//! - `bits`
624//! - `Bits`
625//! - `Type::join` and `Type::link` used to link and join logical interfaces...
626//! - Method calls - Kernels support the following limited set of method calls
627//! - `get_bits` - extract a (fixed width) set of bits from a bit vector
628//! - `get_bit` - extract a single bit from a bit vector
629//! - `replace_bit` - replace a single bit in a bit vector
630//! - `all` - true if all the bits in the bit vector are true
631//! - `any` - true if any of the bits in the bit vector are true
632//! - `xor` - true if the number of ones in the bit vector is odd
633//! - `val`, `into`, `index`, `to_bits` - ignored in HDL kernels
634//! ```rust
635//! # use rust_hdl::prelude::*;
636//!
637//! struct Foo {
638//! pub sig1: Signal<In, Bits<8>>,
639//! pub sig_index: Signal<In, Bits<3>>,
640//! pub sig2: Signal<Out, Bit>,
641//! pub sig3: Signal<Out, Bits<3>>,
642//! pub sig4: Signal<Out, Bit>,
643//! }
644//!
645//! impl Logic for Foo {
646//! #[hdl_gen]
647//! fn update(&mut self) {
648//! self.sig2.next = self.sig1.val().get_bit(self.sig_index.val().index()); // <- Selects specified bit out of sig1
649//! self.sig3.next = self.sig1.val().get_bits::<3>(self.sig_index.val().index()); // Selects 3 bits starting at index `sig_index`
650//! // Notice that here we have an output on both the left and right side of the assignment
651//! // That is fine as long we we write to `.next` before we read from `.val`.
652//! self.sig4.next = self.sig3.val().all(); // True if sig3 is all true
653//! }
654//! }
655//! ```
656//! - Matches - Kernels support matching with literals or identifiers
657//! Matches are used for state machines and implementing ROMs.
658//! For now, `match` is a statement, not an expression! Maybe that will be fixed in a future
659//! version of RustHDL, but for now, the value of the `match` is ignored.
660//! Here is an example of a `match` for a state machine:
661//! ```rust
662//! # use rust_hdl::prelude::*;
663//! # use rust_hdl::widgets::prelude::*;
664//!
665//! #[derive(Copy, Clone, PartialEq, Debug, LogicState)]
666//! enum State {
667//! Idle,
668//! Running,
669//! Paused,
670//! }
671//!
672//!
673//! struct Foo {
674//! pub start: Signal<In, Bit>,
675//! pub pause: Signal<In, Bit>,
676//! pub stop: Signal<In, Bit>,
677//! pub clock: Signal<In, Clock>,
678//! state: DFF<State>,
679//! }
680//!
681//! impl Logic for Foo {
682//! #[hdl_gen]
683//! fn update(&mut self) {
684//! dff_setup!(self, clock, state); // <- setup the DFF
685//! match self.state.q.val() {
686//! State::Idle =>
687//! if self.start.val() {
688//! self.state.d.next = State::Running;
689//! }
690//! State::Running =>
691//! if self.pause.val() {
692//! self.state.d.next = State::Paused;
693//! }
694//! State::Paused =>
695//! if !self.pause.val() {
696//! self.state.d.next = State::Running;
697//! }
698//! }
699//! if self.stop.val() {
700//! self.state.d.next = State::Idle;
701//! }
702//! }
703//! }
704//! ```
705//! - Macros - some macros are supported in kernels
706//! - `println` - this is converted into a comment in the generated HDL
707//! - `comment` - also a comment
708//! - `assert` - converted to a comment
709//! - `dff_setup` - setup a DFF - this macro is converted into the appropriate HDL
710//! - `clock` - clock a set of components - this macro is also converted into the appropriate HDL
711//! - Loops - `for` loops are supported for code generation
712//! - In software parlance, all `for` loops are unrolled at compile time, so they must be of the form `for <ident> in <const>..<const>`.
713//! A simple example to consider is a parameterizable mux.
714//!
715//! ```rust
716//! # use rust_hdl::prelude::*;
717//!
718//! // Mux from N separate signals, using A address bits
719//! // For fun, it's also generic over the width of the
720//! // signals being muxed. So there are 3 generics here:
721//! // - D - the type of those signals
722//! // - N - the number of signals being muxed
723//! // - A - the number of address bits (check that 2^A >= N)
724//! struct Mux<D: Synth, const N: usize, const A: usize> {
725//! pub input_lines: [Signal<In, D>; N],
726//! pub select: Signal<In, Bits<A>>,
727//! pub outsig: Signal<Out, D>,
728//! fallback: Constant<D>,
729//! }
730//!
731//! // The impl for this requires a for loop
732//! impl<D: Synth, const N: usize, const A: usize> Logic for Mux<D, N, A> {
733//! #[hdl_gen]
734//! fn update(&mut self) {
735//! self.outsig.next = self.fallback.val();
736//! for i in 0..N {
737//! if self.select.val().index() == i {
738//! self.outsig.next = self.input_lines[i].val();
739//! }
740//! }
741//! }
742//! }
743//! ```
744//! RustHDL is still pretty restrictive about arrays and loops. You can still do great stuff though.
745//!
746//! Since an example is instructive, here is the HDL kernel for a nontrivial circuit (the `SPIMaster`),
747//! annotated to demonstrate the various valid bits of syntax. It's been heavily redacted to make
748//! it easier to read.
749//!
750//! ```
751//! # use rust_hdl::prelude::*;
752//! # use rust_hdl::widgets::prelude::*;
753//!# #[derive(Copy, Clone, PartialEq, Debug, LogicState)]
754//!# enum SPIState {
755//!# Idle,
756//!# Dwell,
757//!# LoadBit,
758//!# MActive,
759//!# SampleMISO,
760//!# MIdle,
761//!# Finish,
762//!# }
763//!# #[derive(Copy, Clone)]
764//!# pub struct SPIConfig {
765//!# pub clock_speed: u64,
766//!# pub cs_off: bool,
767//!# pub mosi_off: bool,
768//!# pub speed_hz: u64,
769//!# pub cpha: bool,
770//!# pub cpol: bool,
771//!# }
772//!# #[derive(LogicInterface, Default)]
773//!# #[join = "SPIWiresSlave"]
774//!# pub struct SPIWiresMaster {
775//!# pub mosi: Signal<Out, Bit>,
776//!# pub miso: Signal<In, Bit>,
777//!# pub msel: Signal<Out, Bit>,
778//!# pub mclk: Signal<Out, Bit>,
779//!# }
780//!# #[derive(LogicInterface, Default)]
781//!# #[join = "SPIWiresMaster"]
782//!# pub struct SPIWiresSlave {
783//!# pub mosi: Signal<In, Bit>,
784//!# pub miso: Signal<Out, Bit>,
785//!# pub msel: Signal<In, Bit>,
786//!# pub mclk: Signal<In, Bit>,
787//!# }
788//! // Note - you can use const generics in HDL definitions and kernels!
789//! #[derive(LogicBlock)]
790//! struct SPIMaster<const N: usize> {
791//! // The `pub` members are the ones you can access from other circuits.
792//! // These form the official interface of the circuit
793//! pub clock: Signal<In, Clock>,
794//! pub bits_outbound: Signal<In, Bits<16>>,
795//! pub data_outbound: Signal<In, Bits<N>>,
796//! // snip...
797//!# pub data_inbound: Signal<Out, Bits<N>>,
798//!# pub start_send: Signal<In, Bit>,
799//!# pub transfer_done: Signal<Out, Bit>,
800//!# pub continued_transaction: Signal<In, Bit>,
801//!# pub busy: Signal<Out, Bit>,
802//!# pub wires: SPIWiresMaster, // <-- This is a LogicInterface type
803//! // These are private, so they can only be accessed by internal code
804//! register_out: DFF<Bits<N>>,
805//! register_in: DFF<Bits<N>>,
806//! state: DFF<SPIState>,
807//! strobe: Strobe<32>,
808//! pointer: DFF<Bits<16>>,
809//! // snip...
810//!# pointerm1: Signal<Local, Bits<16>>,
811//!# clock_state: DFF<Bit>,
812//!# done_flop: DFF<Bit>,
813//!# msel_flop: DFFWithInit<Bit>,
814//!# mosi_flop: DFF<Bit>,
815//!# continued_save: DFF<Bit>,
816//! // Computed constants need to be stored in a special Constant field member
817//! cs_off: Constant<Bit>,
818//! mosi_off: Constant<Bit>,
819//!# cpha: Constant<Bit>,
820//!# cpol: Constant<Bit>,
821//! }
822//!
823//!impl<const N: usize> Logic for SPIMaster<N> {
824//! #[hdl_gen]
825//! fn update(&mut self) {
826//! // Setup the internals - for Latch avoidance, each digital flip flop
827//! // requires setup - it needs to be clocked, and it needs to connect
828//! // the output and input together, so that the input is driven.
829//! // This macro simply declutters the code a bit and makes it easier to read.
830//! dff_setup!(
831//! self,
832//! clock,
833//! // | equivalent to `self.register_out.clock.next = self.clock.val();`
834//! // v-- `self.register_out.d.next = self.register_out.q.val();`
835//! register_out,
836//! register_in,
837//! state,
838//! pointer,
839//!# clock_state,
840//!# done_flop,
841//!# msel_flop,
842//!# mosi_flop,
843//!# continued_save
844//! );
845//! // This macro is shorthand for `self.strobe.next = self.clock.val();`
846//! clock!(self, clock, strobe);
847//! // These are just standard assignments... Nothing too special.
848//! // Note that `.next` is on the LHS, and `.val()` on the right...
849//! self.strobe.enable.next = true;
850//! self.wires.mclk.next = self.clock_state.q.val();
851//!# self.wires.mosi.next = self.mosi_flop.q.val();
852//! self.wires.msel.next = self.msel_flop.q.val();
853//! self.data_inbound.next = self.register_in.q.val();
854//!# self.transfer_done.next = self.done_flop.q.val();
855//!# self.done_flop.d.next = false;
856//! self.pointerm1.next = self.pointer.q.val() - 1;
857//!# self.busy.next = true;
858//! // The `match` is used to model state machines
859//! match self.state.q.val() {
860//! SPIState::Idle => {
861//! self.busy.next = false;
862//! self.clock_state.d.next = self.cpol.val();
863//! if self.start_send.val() {
864//! // Capture the outgoing data in our register
865//! self.register_out.d.next = self.data_outbound.val();
866//! self.state.d.next = SPIState::Dwell; // Transition to the DWELL state
867//! self.pointer.d.next = self.bits_outbound.val(); // set bit pointer to number of bit to send (1 based)
868//! self.register_in.d.next = 0.into(); // Clear out the input store register
869//! self.msel_flop.d.next = !self.cs_off.val(); // Activate the chip select
870//! self.continued_save.d.next = self.continued_transaction.val();
871//! } else {
872//! if !self.continued_save.q.val() {
873//! self.msel_flop.d.next = self.cs_off.val(); // Set the chip select signal to be "off"
874//! }
875//! }
876//! self.mosi_flop.d.next = self.mosi_off.val(); // Set the mosi signal to be "off"
877//! }
878//! SPIState::Dwell => {
879//! if self.strobe.strobe.val() {
880//! // Dwell timeout has reached zero
881//! self.state.d.next = SPIState::LoadBit; // Transition to the loadbit state
882//! }
883//! }
884//! SPIState::LoadBit => {
885//! // Note in this statement that to use the pointer register as a bit index
886//! // into the `register_out` DFF, we need to convert it with `index()`.
887//! if self.pointer.q.val().any() {
888//! // We have data to send
889//! self.mosi_flop.d.next = self
890//! .register_out
891//! .q
892//! .val()
893//! .get_bit(self.pointerm1.val().index()); // Fetch the corresponding bit out of the register
894//!# self.pointer.d.next = self.pointerm1.val(); // Decrement the pointer
895//! self.state.d.next = SPIState::MActive; // Move to the hold mclock low state
896//! self.clock_state.d.next = self.cpol.val() ^ self.cpha.val();
897//! } else {
898//! self.mosi_flop.d.next = self.mosi_off.val(); // Set the mosi signal to be "off"
899//! self.clock_state.d.next = self.cpol.val();
900//! self.state.d.next = SPIState::Finish; // No data, go back to idle
901//! }
902//! }
903//! SPIState::MActive => {
904//! if self.strobe.strobe.val() {
905//! self.state.d.next = SPIState::SampleMISO;
906//! }
907//! }
908//!# SPIState::SampleMISO => {}
909//!# SPIState::MIdle => {}
910//!# SPIState::Finish => {}
911//! }
912//! }
913//!}
914//! ```
915//!
916//! ## Enums
917//!
918//! In keeping with Rust's strongly typed model, you can use enums (not sum types) in your HDL,
919//! provided you derive the `LogicState` trait for them. This makes your code much easier to
920//! read and debug, and `rustc` will make sure you don't do anything illegal with your
921//! enums.
922//!
923//! ```rust
924//! # use rust_hdl::prelude::*;
925//!
926//! #[derive(Copy, Clone, PartialEq, Debug, LogicState)]
927//! enum State {
928//! Idle,
929//! Running,
930//! Paused,
931//! }
932//! ```
933//!
934//! Using enums for storing things like state has several advantages:
935//! - RustHDL will automatically calculate the minimum number of bits needed to store the
936//! enum in e.g., a register.
937//!
938//! For example, we can create a Digital Flip Flop (register) of value `State` from the next
939//! example, and RustHDL will convert this into a 2 bit binary register.
940//!
941//! ```rust
942//! # use rust_hdl::prelude::*;
943//! # use rust_hdl::widgets::prelude::*;
944//!
945//! #[derive(Copy, Clone, PartialEq, Debug, LogicState)]
946//! enum State {
947//! Idle,
948//! Sending,
949//! Receiving,
950//! Done,
951//! }
952//!
953//! struct Foo {
954//! dff: DFF<State>, // <-- This is a 2 bit DFF
955//! }
956//! ```
957//!
958//! Now imagine we add another state in the future to our state machine - say `Pending`:
959//!
960//! ```rust
961//! # use rust_hdl::prelude::*;
962//! # use rust_hdl::widgets::prelude::*;
963//!
964//! #[derive(Copy, Clone, PartialEq, Debug, LogicState)]
965//! enum State {
966//! Idle,
967//! Sending,
968//! Receiving,
969//! Pending,
970//! Done,
971//! }
972//!
973//! struct Foo {
974//! dff: DFF<State>, // <-- This is now a 3 bit DFF!
975//! }
976//! ```
977//! RustHDL will _automatically_ choose a 3-bit representation.
978//!
979//! - RustHDL will ensure that assignments to `enum`-valued signals are valid at all times
980//!
981//! The strong type guarantees ensure you cannot assign arbitrary values to `enum` valued
982//! signals, and the namespaces ensure that there is no ambiguity in assignment. This example
983//! won't compile, since `On` without the name of the `enum` means nothing, and `State1` and
984//! `State2` are separate types. They cannot be assigned to one another.
985//!
986//! ```compile_fail
987//! # use rust_hdl::prelude::*;
988//!
989//! #[derive(Copy, Clone, PartialEq, Debug, LogicState)]
990//! enum State1 {
991//! On,
992//! Off,
993//! }
994//!
995//! #[derive(Copy, Clone, PartialEq, Debug, LogicState)]
996//! enum State2 {
997//! Off,
998//! On,
999//! }
1000//!
1001//! struct Foo {
1002//! pub sig_in: Signal<In, State1>,
1003//! pub sig_out: Signal<Out, State2>,
1004//! }
1005//!
1006//! impl Logic for Foo {
1007//! #[hdl_gen]
1008//! fn update(&mut self) {
1009//! self.sig_out.next = On; // << This won't work either.
1010//! self.sig_out.next = self.sig_in.val(); // << Won't compile
1011//! }
1012//! }
1013//! ```
1014//!
1015//! If for some reason, you needed to translate between enums, use a `match`:
1016//!
1017//! ```rust
1018//! # use rust_hdl::prelude::*;
1019//!
1020//! # #[derive(Copy, Clone, PartialEq, Debug, LogicState)]
1021//! # enum State1 {
1022//! # On,
1023//! # Off,
1024//! # }
1025//! # #[derive(Copy, Clone, PartialEq, Debug, LogicState)]
1026//! # enum State2 {
1027//! # Off,
1028//! # On,
1029//! # }
1030//! #
1031//! # struct Foo {
1032//! # pub sig_in: Signal<In, State1>,
1033//! # pub sig_out: Signal<Out, State2>,
1034//! # }
1035//! #
1036//! impl Logic for Foo {
1037//! #[hdl_gen]
1038//! fn update(&mut self) {
1039//! match self.sig_in.val() {
1040//! State1::On => self.sig_out.next = State2::On,
1041//! State1::Off => self.sig_out.next = State2::Off,
1042//! }
1043//! }
1044//! }
1045//! ```
1046//!
1047//! ## Interfaces
1048//!
1049//! One area you will encouter as your circuits become more complex is that the interfaces
1050//! to those circuits will become increasingly complicated. To demonstrate, suppose you
1051//! have a circuit that consumes a sequence of 16-bit integers via a FIFO interface. The
1052//! circuit has some flow control signals because it cannot consume them every clock
1053//! cycle (For Reasons). Suppose also that you have a data producer circuit that will
1054//! produce 16-bit integers and you want to connect these two together. A natural
1055//! FIFO interface would look like this
1056//!
1057//! ```rust
1058//!# use rust_hdl::prelude::*;
1059//! struct MyFIFO {
1060//! pub data_to_fifo: Signal<In, Bits<16>>,
1061//! pub write: Signal<In, Bits<16>>,
1062//! pub full: Signal<Out, Bit>,
1063//! pub overflow: Signal<Out, Bit>,
1064//! }
1065//!
1066//! struct DataWidget {
1067//! pub data_to_fifo: Signal<Out, Bits<16>>,
1068//! pub write: Signal<Out, Bits<16>>,
1069//! pub full: Signal<In, Bit>,
1070//! pub overflow: Signal<In, Bit>,
1071//! }
1072//!
1073//! struct Foo {
1074//! producer: DataWidget,
1075//! consumer: MyFIFO,
1076//! }
1077//! ```
1078//!
1079//! Now, we want to connect the output of the DataWidget (all 4 signals!) to the corresponding
1080//! signals on `MyFIFO`. Keep in mind that the order of assignment is irrelevant, but which
1081//! signal appears on the LHS vs RHS _is_ important. In the `impl Logic` block for `Foo`,
1082//! our HDL kernel will look like this:
1083//!```rust
1084//!# use rust_hdl::prelude::*;
1085//!# struct MyFIFO {
1086//!# pub data_to_fifo: Signal<In, Bits<16>>,
1087//!# pub write: Signal<In, Bits<16>>,
1088//!# pub full: Signal<Out, Bit>,
1089//!# pub overflow: Signal<Out, Bit>,
1090//!# }
1091//!#
1092//!# struct DataWidget {
1093//!# pub data_to_fifo: Signal<Out, Bits<16>>,
1094//!# pub write: Signal<Out, Bits<16>>,
1095//!# pub full: Signal<In, Bit>,
1096//!# pub overflow: Signal<In, Bit>,
1097//!# }
1098//!#
1099//!# struct Foo {
1100//!# producer: DataWidget,
1101//!# consumer: MyFIFO,
1102//!# }
1103//!impl Logic for Foo {
1104//! #[hdl_gen]
1105//! fn update(&mut self) {
1106//! self.consumer.data_to_fifo.next = self.producer.data_to_fifo.val();
1107//! self.consumer.write.next = self.producer.write.val();
1108//! self.producer.full.next = self.consumer.full.val();
1109//! self.producer.overflow.next = self.consumer.overflow.val();
1110//! }
1111//!}
1112//!```
1113//! This is basically boilerplate at this point, and typing that in and getting it right
1114//! is error prone and tedious. Fortunately, RustHDL can help! RustHDL includes the
1115//! concept of an `Interface`, which is basically a bus. An `Interface` is generally a
1116//! pair of structs that contain signals of complementary directions and a `#[derive]`
1117//! macro that autogenerates a bunch of boilerplate. To continue on with our previous
1118//! example, we could define a pair of `struct`s for the write interface of the FIFO
1119//!
1120//! ```rust
1121//! # use rust_hdl::prelude::*;
1122//! #[derive(LogicInterface)] // <- Note the LogicInterface, not LogicBlock
1123//! #[join = "MyFIFOWriteSender"] // <- Name of the "mating" interface
1124//! struct MyFIFOWriteReceiver {
1125//! pub data_to_fifo: Signal<In, Bits<16>>,
1126//! pub write: Signal<In, Bit>,
1127//! pub full: Signal<Out, Bit>,
1128//! pub overflow: Signal<Out, Bit>,
1129//! }
1130//!
1131//! #[derive(LogicInterface)] // <- Also here
1132//! #[join = "MyFIFOWriteReceiver"] // <- Name of the "mating" interface
1133//! struct MyFIFOWriteSender {
1134//! pub data_to_fifo: Signal<Out, Bits<16>>,
1135//! pub write: Signal<Out, Bit>,
1136//! pub full: Signal<In, Bit>,
1137//! pub overflow: Signal<In, Bit>
1138//! }
1139//! ```
1140//!The names of the fields must match, the types of the fields must also match, and the directions
1141//! of the signals must be complementary. So in general:
1142//!
1143//! - Each field in struct `A` must have a matching named field in struct `B`
1144//! - The types of those fields must match
1145//! - The direction of those signals must be opposite
1146//! - Order of the fields is immaterial
1147//! - The `join` attribute tells the compiler which interface to mate to this one.
1148//!
1149//! So what can we do with our shiny new interfaces? Plenty of stuff. First, lets
1150//! rewrite our FIFO circuit and data producer to use our new interfaces.
1151//!
1152//! ```rust
1153//! # use rust_hdl::prelude::*;
1154//!# #[derive(LogicInterface)] // <- Note the LogicInterface, not LogicBlock
1155//!# #[join = "MyFIFOWriteSender"] // <- Name of the "mating" interface
1156//!# struct MyFIFOWriteReceiver {
1157//!# pub data_to_fifo: Signal<In, Bits<16>>,
1158//!# pub write: Signal<In, Bit>,
1159//!# pub full: Signal<Out, Bit>,
1160//!# pub overflow: Signal<Out, Bit>,
1161//!# }
1162//!# #[derive(LogicInterface)] // <- Also here
1163//!# #[join = "MyFIFOWriteReceiver"] // <- Name of the "mating" interface
1164//!# struct MyFIFOWriteSender {
1165//!# pub data_to_fifo: Signal<Out, Bits<16>>,
1166//!# pub write: Signal<Out, Bit>,
1167//!# pub full: Signal<In, Bit>,
1168//!# pub overflow: Signal<In, Bit>
1169//!# }
1170//! struct MyFIFO {
1171//! // The write interface to the FIFO - now only one line!
1172//! pub write_bus: MyFIFOWriteReceiver,
1173//! }
1174//!
1175//! struct DataWidget {
1176//! // The output interface from the DataWidget!
1177//! pub data_out: MyFIFOWriteSender,
1178//! }
1179//! ```
1180//!
1181//! That is significantly less verbose! So what happens to our
1182//! `impl Logic for Foo`? Well, RustHDL autogenerates 2 methods for each `LogicInterface`. The first
1183//! one is called `join`. And it, well, joins the interfaces.
1184//!
1185//! ```rust
1186//! # use rust_hdl::prelude::*;
1187//!# #[derive(LogicInterface)] // <- Note the LogicInterface, not LogicBlock
1188//!# #[join = "MyFIFOWriteSender"] // <- Name of the "mating" interface
1189//!# struct MyFIFOWriteReceiver {
1190//!# pub data_to_fifo: Signal<In, Bits<16>>,
1191//!# pub write: Signal<In, Bit>,
1192//!# pub full: Signal<Out, Bit>,
1193//!# pub overflow: Signal<Out, Bit>,
1194//!# }
1195//!# #[derive(LogicInterface)] // <- Also here
1196//!# #[join = "MyFIFOWriteReceiver"] // <- Name of the "mating" interface
1197//!# struct MyFIFOWriteSender {
1198//!# pub data_to_fifo: Signal<Out, Bits<16>>,
1199//!# pub write: Signal<Out, Bit>,
1200//!# pub full: Signal<In, Bit>,
1201//!# pub overflow: Signal<In, Bit>
1202//!# }
1203//!# struct MyFIFO {
1204//!# // The write interface to the FIFO - now only one line!
1205//!# pub write_bus: MyFIFOWriteReceiver,
1206//!# }
1207//!# struct DataWidget {
1208//!# pub data_out: MyFIFOWriteSender,
1209//!# }
1210//!# struct Foo {
1211//!# producer: DataWidget,
1212//!# consumer: MyFIFO,
1213//!# }
1214//! impl Logic for Foo {
1215//! #[hdl_gen]
1216//! fn update(&mut self) {
1217//! // Excess verbosity eliminated!!
1218//! MyFIFOWriteSender::join(&mut self.producer.data_out, &mut self.consumer.write_bus);
1219//! }
1220//! }
1221//! ```
1222//!
1223//! This is exactly equivalent to our previous 4 lines of hand crafted code, but is now automatically
1224//! generated _and_ synthesizable. But wait! There is more. RustHDL also generates a `link`
1225//! method, which allows you to _forward_ a bus from one point to another. If you think in terms
1226//! gendered cables, a `join` is a cable with a Male connector on one end and a Female connector
1227//! on the other. A `link` is a cable that is either Male to Male or Female to Female. Links
1228//! are useful when you want to forward an interface to an interior component of a circuit, but
1229//! hide that interior component from the outside world. For example, lets suppose that
1230//! `DataWidget` doesn't actually produce the 16-bit samples. Instead, some other FPGA component
1231//! or circuit generates the 16-bit samples, and `DataWidget` just wraps it along with some
1232//! other control logic. So in fact, our `DataWidget` has an internal representation that looks
1233//! like this
1234//!```rust
1235//! # use rust_hdl::prelude::*;
1236//! # use rust_hdl::widgets::prelude::*;
1237//! # struct MyFIFOWriteSender{}
1238//! struct DataWidget {
1239//! pub data_out: MyFIFOWriteSender,
1240//! secret_guy: CryptoGenerator,
1241//! running: DFF<Bit>,
1242//! }
1243//!
1244//! struct CryptoGenerator {
1245//! pub data_out: MyFIFOWriteSender,
1246//! // secret stuff!
1247//! }
1248//!```
1249//!
1250//! In this example, the `DataWidget` wants to present the outside world that it is a `MyFIFOWriteSender`
1251//! interface, and that it can produce 16-bit data values. But the real work is being done internally
1252//! by the `secret_guy`. The manual way to do this would be to connect up the signals manually. Again,
1253//! paying attention to which signal is an input (for `DataWidget`), and which is an output.
1254//!
1255//! ```rust
1256//! # use rust_hdl::prelude::*;
1257//! # use rust_hdl::widgets::prelude::*;
1258//!# #[derive(LogicInterface)] // <- Note the LogicInterface, not LogicBlock
1259//!# #[join = "MyFIFOWriteSender"] // <- Name of the "mating" interface
1260//!# struct MyFIFOWriteReceiver {
1261//!# pub data_to_fifo: Signal<In, Bits<16>>,
1262//!# pub write: Signal<In, Bit>,
1263//!# pub full: Signal<Out, Bit>,
1264//!# pub overflow: Signal<Out, Bit>,
1265//!# }
1266//!# #[derive(LogicInterface)] // <- Also here
1267//!# #[join = "MyFIFOWriteReceiver"] // <- Name of the "mating" interface
1268//!# struct MyFIFOWriteSender {
1269//!# pub data_to_fifo: Signal<Out, Bits<16>>,
1270//!# pub write: Signal<Out, Bit>,
1271//!# pub full: Signal<In, Bit>,
1272//!# pub overflow: Signal<In, Bit>
1273//!# }
1274//!# struct DataWidget {
1275//!# pub data_out: MyFIFOWriteSender,
1276//!# secret_guy: CryptoGenerator,
1277//!# running: DFF<Bit>,
1278//!# }
1279//!# struct CryptoGenerator {
1280//!# pub data_out: MyFIFOWriteSender,
1281//!# // secret stuff!
1282//!# }
1283//! impl Logic for DataWidget {
1284//! #[hdl_gen]
1285//! fn update(&mut self) {
1286//! // Yawn...
1287//! self.data_out.data_to_fifo.next = self.secret_guy.data_out.data_to_fifo.val();
1288//! self.data_out.write.next = self.secret_guy.data_out.write.val();
1289//! self.secret_guy.data_out.full.next = self.data_out.full.val();
1290//! self.secret_guy.data_out.overflow.next = self.data_out.overflow.val();
1291//! }
1292//! }
1293//! ```
1294//!
1295//! In these instances, you can use the `link` method instead. The syntax is
1296//! `Interface::link(&mut self.outside, &mut self.inside)`, where `outside` is the
1297//! side of the interface going out of the circuit, and `inside` is the side of the interface
1298//! inside of the circuit. Hence, our interface can be `forwarded` or `linked` with a single line
1299//! like so:
1300//! ```rust
1301//! # use rust_hdl::prelude::*;
1302//! # use rust_hdl::widgets::prelude::*;
1303//!# #[derive(LogicInterface)] // <- Note the LogicInterface, not LogicBlock
1304//!# #[join = "MyFIFOWriteSender"] // <- Name of the "mating" interface
1305//!# struct MyFIFOWriteReceiver {
1306//!# pub data_to_fifo: Signal<In, Bits<16>>,
1307//!# pub write: Signal<In, Bit>,
1308//!# pub full: Signal<Out, Bit>,
1309//!# pub overflow: Signal<Out, Bit>,
1310//!# }
1311//!# #[derive(LogicInterface)] // <- Also here
1312//!# #[join = "MyFIFOWriteReceiver"] // <- Name of the "mating" interface
1313//!# struct MyFIFOWriteSender {
1314//!# pub data_to_fifo: Signal<Out, Bits<16>>,
1315//!# pub write: Signal<Out, Bit>,
1316//!# pub full: Signal<In, Bit>,
1317//!# pub overflow: Signal<In, Bit>
1318//!# }
1319//!# struct DataWidget {
1320//!# pub data_out: MyFIFOWriteSender,
1321//!# secret_guy: CryptoGenerator,
1322//!# running: DFF<Bit>,
1323//!# }
1324//!# struct CryptoGenerator {
1325//!# pub data_out: MyFIFOWriteSender,
1326//!# // secret stuff!
1327//!# }
1328//! impl Logic for DataWidget {
1329//! #[hdl_gen]
1330//! fn update(&mut self) {
1331//! // Tada!
1332//! MyFIFOWriteSender::link(&mut self.data_out, &mut self.secret_guy.data_out);
1333//! }
1334//! }
1335//! ```
1336//!
1337//! As a parting note, you can make interfaces generic across types. Here, for example
1338//! is the FIFO interface used in the High Level Synthesis library in RustHDL:
1339//! ```rust
1340//! # use rust_hdl::prelude::*;
1341//!#[derive(Clone, Debug, Default, LogicInterface)]
1342//! #[join = "FIFOWriteResponder"]
1343//! pub struct FIFOWriteController<T: Synth> {
1344//! pub data: Signal<Out, T>,
1345//! pub write: Signal<Out, Bit>,
1346//! pub full: Signal<In, Bit>,
1347//! pub almost_full: Signal<In, Bit>,
1348//! }
1349//!
1350//! #[derive(Clone, Debug, Default, LogicInterface)]
1351//! #[join = "FIFOWriteController"]
1352//! pub struct FIFOWriteResponder<T: Synth> {
1353//! pub data: Signal<In, T>,
1354//! pub write: Signal<In, Bit>,
1355//! pub full: Signal<Out, Bit>,
1356//! pub almost_full: Signal<Out, Bit>,
1357//! }
1358//! ```
1359//!
1360//! You can then use any synthesizable type for the data bus, and keep the control signals
1361//! as single bits! Neat, eh? 🦑
1362//!
1363//! ## Simulation
1364//!
1365//! Now that you have a shiny new circuit implemented as a struct, what do you do with it?
1366//! Typically, in hardware design, the first thing you do (after static analysis) is to simulate
1367//! the circuit. Simulation allows you to verify the proper behavior of the circuit in software
1368//! _before_ heading over to the bench to test on the physical hardware. There is a saying
1369//! in hardware design "success in simulation is necessary, but not sufficient for correct operation".
1370//! Or something like that.
1371//!
1372//! In any case, RustHDL makes it easy to simulate your designs by allowing you to create and write
1373//! complex test benches in Rust instead of in an HDL like Verilog or VHDL. Furthermore, the
1374//! simulator is built in, so you do not need to use external tools for simulation. Occasionally,
1375//! you may need to or want to simulate using external tools. Currently, RustHDL can't help
1376//! much there. You can convert your design to Verilog and then import it into standard
1377//! simulation tools, but the testbench won't go with the design. Maybe in the future...
1378//!
1379//! The simulator that is built into RustHDL is pretty basic, and easy to use. To use it, you
1380//! need a circuit to simulate. Let's create a basic 8 bit adder with a clocked register for
1381//! the output (and no carry):
1382//! ```rust
1383//! use rust_hdl::prelude::*; // <- shorthand to bring in all definitions
1384//!
1385//! // v--- Required by RustHDL
1386//! #[derive(LogicBlock, Default)]
1387//! struct MyAdder {
1388//! pub sig_a: Signal<In, Bits<8>>,
1389//! pub sig_b: Signal<In, Bits<8>>,
1390//! pub sig_sum: Signal<Out, Bits<8>>,
1391//! pub clock: Signal<In, Clock>,
1392//! my_reg: DFF<Bits<8>>,
1393//! }
1394//!
1395//! impl Logic for MyAdder {
1396//! #[hdl_gen] // <--- don't forget this
1397//! fn update(&mut self) {
1398//! // Setup the DFF.. this macro is handy to prevent latches
1399//! dff_setup!(self, clock, my_reg);
1400//! self.my_reg.d.next = self.sig_a.val() + self.sig_b.val();
1401//! self.sig_sum.next = self.my_reg.q.val();
1402//! }
1403//! }
1404//! ```
1405//!
1406//! At this point, we can convert `MyAdder` into Verilog and use a standard toolchain to generate
1407//! a bitfile. However, we want to verify that the circuit operates properly. The simplest way
1408//! to do that would be to feed it a vector of random inputs, and make sure that the output
1409//! matches the sum of the inputs. Setting up a simulation can be a little verbose, so there
1410//! is a handy macro [simple_sim!] that works if you have only a single (top level) clock,
1411//! and only need one test bench.
1412//!
1413//! ** An aside on ownership **
1414//! We haven't talked about the borrow checker much. And that is because by and large, RustHDL
1415//! does not use references. So how do the testbenches work? The key points for those of you
1416//! familiar with Rust are:
1417//! - The circuit must be [Send]. All RustHDL components are [Send].
1418//! - The simulation uses a [Box] to hold the circuit.
1419//! - Each testbench runs in it's own thread.
1420//! - The circuit is moved to each testbench as it runs via the endpoint.
1421//! - The testbench then updates the circuit inputs, and checks outputs. It is the
1422//! sole owner of the circuit at this point.
1423//! - The techbench then passes the circuit back to the simulation (moves) along with some
1424//! indication of when it needs to see it again.
1425//! - If a testbench is complete, it signals that it does not need to see the circuit again.
1426//! - When all testbenches are complete (or any of them report an error), the simulation
1427//! halts.
1428//!
1429//! It takes a little getting used to, but the design allows you to write concurrent testbenches
1430//! without worrying about shared mutable state.
1431//!
1432//! So back to our adder. The testbench should look something like this
1433//! - set input A to some known value x
1434//! - set input B to some known value y
1435//! - wait a clock cycle
1436//! - check that the output matches the sum x + y
1437//! - loop until complete.
1438//!
1439//! Here is a complete example:
1440//! ```rust
1441//!# use rust_hdl::prelude::*; // <- shorthand to bring in all definitions
1442//!#
1443//!# // v--- Required by RustHDL
1444//!# #[derive(LogicBlock, Default)]
1445//!# struct MyAdder {
1446//!# pub sig_a: Signal<In, Bits<8>>,
1447//!# pub sig_b: Signal<In, Bits<8>>,
1448//!# pub sig_sum: Signal<Out, Bits<8>>,
1449//!# pub clock: Signal<In, Clock>,
1450//!# my_reg: DFF<Bits<8>>,
1451//!# }
1452//!#
1453//!# impl Logic for MyAdder {
1454//!# #[hdl_gen] // <--- don't forget this
1455//!# fn update(&mut self) {
1456//!# // Setup the DFF.. this macro is handy to prevent latches
1457//!# dff_setup!(self, clock, my_reg);
1458//!# self.my_reg.d.next = self.sig_a.val() + self.sig_b.val();
1459//!# self.sig_sum.next = self.my_reg.q.val();
1460//!# }
1461//!# }
1462//! use rand::{thread_rng, Rng};
1463//! use std::num::Wrapping;
1464//! // Build a set of test cases for the circuit. Use Wrapping to emulate hardware.
1465//! let test_cases = (0..512)
1466//! .map(|_| {
1467//! let a_val = thread_rng().gen::<u8>();
1468//! let b_val = thread_rng().gen::<u8>();
1469//! let c_val = (Wrapping(a_val) + Wrapping(b_val)).0;
1470//! (
1471//! a_val.to_bits::<8>(),
1472//! b_val.to_bits::<8>(),
1473//! c_val.to_bits::<8>(),
1474//! )
1475//! })
1476//! .collect::<Vec<_>>();
1477//! // The clock speed doesn't really matter here. So 100MHz is fine.
1478//! let mut sim = simple_sim!(MyAdder, clock, 100_000_000, ep, {
1479//! let mut x = ep.init()?; // Get the circuit
1480//! for test_case in &test_cases {
1481//! // +-- This should look familiar. Same rules as for HDL kernels
1482//! // v Write to .next, read from .val()
1483//! x.sig_a.next = test_case.0;
1484//! x.sig_b.next = test_case.1;
1485//! // Helpful macro to delay the simulate by 1 clock cycle (to allow the output to update)
1486//! wait_clock_cycle!(ep, clock, x);
1487//! // You can use any standard Rust stuff inside the testbench.
1488//! println!(
1489//! "Test case {:x} + {:x} = {:x} (check {:x})",
1490//! test_case.0,
1491//! test_case.1,
1492//! x.sig_sum.val(),
1493//! test_case.2
1494//! );
1495//! // The `sim_assert_eq` macro stops the simulation gracefully.
1496//! sim_assert_eq!(ep, x.sig_sum.val(), test_case.2, x);
1497//! }
1498//! // When the testbench is complete, call done on the endpoint, and pass the circuit back.
1499//! ep.done(x)
1500//! });
1501//! // Run the simulation - needs a boxed circuit, and a maximum duration.
1502//! sim.run(MyAdder::default().into(), sim_time::ONE_MILLISECOND)
1503//! .unwrap();
1504//! ```
1505//!
1506//! The above should write the following to your console (your numbers will be different)
1507//!
1508//!```bash
1509//!Test case 5d + 98 = f5 (check f5)
1510//!Test case 3b + 44 = 7f (check 7f)
1511//!Test case 5d + b0 = 0d (check 0d)
1512//!Test case f8 + 38 = 30 (check 30)
1513//!Test case 73 + b5 = 28 (check 28)
1514//!Test case 1b + e5 = 00 (check 00)
1515//!Test case c1 + 89 = 4a (check 4a)
1516//! etc...
1517//!```
1518//!
1519//! You can also generate a trace of the circuit using the `vcd` (Value Change Dump) format, and
1520//! read the output using `gtkwave` or some other `vcd` viewer. RustHDL includes a simple
1521//! `vcd` renderer for convenience, but its pretty basic, and mostly for creating documentation
1522//! examples. It does have the advantage of being callable directly from your testbench in case
1523//! you need some visual verification of your circuit.
1524//!
1525//! We can make a one line change to our previous example, and generate a `vcd`.
1526//!
1527//! ```rust
1528//!# use rust_hdl::prelude::*; // <- shorthand to bring in all definitions
1529//!#
1530//!# // v--- Required by RustHDL
1531//!# #[derive(LogicBlock, Default)]
1532//!# struct MyAdder {
1533//!# pub sig_a: Signal<In, Bits<8>>,
1534//!# pub sig_b: Signal<In, Bits<8>>,
1535//!# pub sig_sum: Signal<Out, Bits<8>>,
1536//!# pub clock: Signal<In, Clock>,
1537//!# my_reg: DFF<Bits<8>>,
1538//!# }
1539//!#
1540//!# impl Logic for MyAdder {
1541//!# #[hdl_gen] // <--- don't forget this
1542//!# fn update(&mut self) {
1543//!# // Setup the DFF.. this macro is handy to prevent latches
1544//!# dff_setup!(self, clock, my_reg);
1545//!# self.my_reg.d.next = self.sig_a.val() + self.sig_b.val();
1546//!# self.sig_sum.next = self.my_reg.q.val();
1547//!# }
1548//!# }
1549//!# use rand::{thread_rng, Rng};
1550//!# use std::num::Wrapping;
1551//!# // Build a set of test cases for the circuit. Use Wrapping to emulate hardware.
1552//!# let test_cases = (0..512)
1553//!# .map(|_| {
1554//!# let a_val = thread_rng().gen::<u8>();
1555//!# let b_val = thread_rng().gen::<u8>();
1556//!# let c_val = (Wrapping(a_val) + Wrapping(b_val)).0;
1557//!# (
1558//!# a_val.to_bits::<8>(),
1559//!# b_val.to_bits::<8>(),
1560//!# c_val.to_bits::<8>(),
1561//!# )
1562//!# })
1563//!# .collect::<Vec<_>>();
1564//!# // The clock speed doesn't really matter here. So 100MHz is fine.
1565//!# let mut sim = simple_sim!(MyAdder, clock, 100_000_000, ep, {
1566//!# let mut x = ep.init()?; // Get the circuit
1567//!# for test_case in &test_cases {
1568//!# // +-- This should look familiar. Same rules as for HDL kernels
1569//!# // v Write to .next, read from .val()
1570//!# x.sig_a.next = test_case.0;
1571//!# x.sig_b.next = test_case.1;
1572//!# // Helpful macro to delay the simulate by 1 clock cycle (to allow the output to update)
1573//!# wait_clock_cycle!(ep, clock, x);
1574//!# // You can use any standard Rust stuff inside the testbench.
1575//!# println!(
1576//!# "Test case {:x} + {:x} = {:x} (check {:x})",
1577//!# test_case.0,
1578//!# test_case.1,
1579//!# x.sig_sum.val(),
1580//!# test_case.2
1581//!# );
1582//!# // The `sim_assert_eq` macro stops the simulation gracefully.
1583//!# sim_assert_eq!(ep, x.sig_sum.val(), test_case.2, x);
1584//!# }
1585//!# // When the testbench is complete, call done on the endpoint, and pass the circuit back.
1586//!# ep.done(x)
1587//!# });
1588//! // Run the simulation - needs a boxed circuit, and a maximum duration.
1589//! sim.run_to_file(
1590//! MyAdder::default().into(),
1591//! sim_time::ONE_MILLISECOND,
1592//! &vcd_path!("my_adder.vcd"),
1593//! )
1594//! .unwrap();
1595//! vcd_to_svg(
1596//! &vcd_path!("my_adder.vcd"),
1597//! "images/my_adder.svg",
1598//! &[
1599//! "uut.clock",
1600//! "uut.sig_a",
1601//! "uut.sig_b",
1602//! "uut.my_reg.d",
1603//! "uut.my_reg.q",
1604//! "uut.sig_sum",
1605//! ],
1606//! 0,
1607//! 100 * sim_time::ONE_NANOSECOND,
1608//! )
1609//! .unwrap()
1610//! ```
1611//! The result of that simulation is here.
1612//! 
1613//! Note that the digital flip flop copies it's input from `d` to `q` on the leading edge of the clock.
1614//!
1615//! ## Generating Verilog
1616//!
1617//! At some point, you will want to generate Verilog so you can send your design to other
1618//! tools. This is pretty simple. You call [generate_verilog] and pass it a reference
1619//! to the circuit in question. The [generate_verilog] function will check your circuit,
1620//! and then return a string that contains the Verilog equivalent. It's pretty simple.
1621//!
1622//! Here is an example. We will reuse the `MyAdder` circuit from the testbench section,
1623//! but this time, generate the Verilog for the circuit instead of simulating it.
1624//!
1625//! ```rust
1626//! use rust_hdl::prelude::*; // <- shorthand to bring in all definitions
1627//!
1628//! // v--- Required by RustHDL
1629//! #[derive(LogicBlock, Default)]
1630//! struct MyAdder {
1631//! pub sig_a: Signal<In, Bits<8>>,
1632//! pub sig_b: Signal<In, Bits<8>>,
1633//! pub sig_sum: Signal<Out, Bits<8>>,
1634//! pub clock: Signal<In, Clock>,
1635//! my_reg: DFF<Bits<8>>,
1636//! }
1637//!
1638//! impl Logic for MyAdder {
1639//! #[hdl_gen] // <--- don't forget this
1640//! fn update(&mut self) {
1641//! // Setup the DFF.. this macro is handy to prevent latches
1642//! dff_setup!(self, clock, my_reg);
1643//! self.my_reg.d.next = self.sig_a.val() + self.sig_b.val();
1644//! self.sig_sum.next = self.my_reg.q.val();
1645//! }
1646//! }
1647//!
1648//! let mut uut = MyAdder::default();
1649//! uut.connect_all();
1650//! println!("{}", generate_verilog(&uut));
1651//! ```
1652//!
1653//! You should get the following generated code in your console:
1654//! ```verilog
1655//! module top(sig_a,sig_b,sig_sum,clock);
1656//!
1657//! // Module arguments
1658//! input wire [7:0] sig_a;
1659//! input wire [7:0] sig_b;
1660//! output reg [7:0] sig_sum;
1661//! input wire clock;
1662//!
1663//! // Stub signals
1664//! reg [7:0] my_reg$d;
1665//! wire [7:0] my_reg$q;
1666//! reg my_reg$clock;
1667//!
1668//! // Sub module instances
1669//! top$my_reg my_reg(
1670//! .d(my_reg$d),
1671//! .q(my_reg$q),
1672//! .clock(my_reg$clock)
1673//! );
1674//!
1675//! // Update code
1676//! always @(*) begin
1677//! my_reg$clock = clock;
1678//! my_reg$d = my_reg$q;
1679//! my_reg$d = sig_a + sig_b;
1680//! sig_sum = my_reg$q;
1681//! end
1682//!
1683//! endmodule // top
1684//!
1685//!
1686//! module top$my_reg(d,q,clock);
1687//!
1688//! // Module arguments
1689//! input wire [7:0] d;
1690//! output reg [7:0] q;
1691//! input wire clock;
1692//!
1693//! // Update code (custom)
1694//! initial begin
1695//! q = 8'h0;
1696//! end
1697//!
1698//! always @(posedge clock) begin
1699//! q <= d;
1700//! end
1701//!
1702//! endmodule // top$my_reg
1703//! ```
1704//!
1705//! A few things about the Verilog generated.
1706//! - It is hierarchical (scoped) by design. The scopes mimic the scopes inside the RustHDL circuit.
1707//! That makes it easy to map the Verilog back to the RustHDL code if needed when debugging.
1708//! - The code is readable and formatted.
1709//! - The names correspond to the names in RustHDL, which makes it easy to see the details of the logic.
1710//! - RustHDL (at least for this trivial example) is a pretty thin wrapper around Verilog. That's
1711//! good for compatibility with tooling.
1712//!
1713//! While most FPGAs will require you to use a proprietary and closed source toolchain to synthesize
1714//! your design, you can use the open source [Yosys] compiler (if you have it installed) to
1715//! check your designs. For that, you can use the [yosys_validate] function, which runs the Verilog
1716//! through some checks and reports on potential errors. At the moment, [Yosys] is far more
1717//! thorough in it's checking than RustHDL, so I highly recommend you install it and use the
1718//! [yosys_validate] function on your generated Verilog.
1719//!
1720//! ## Struct valued signals
1721//!
1722//! We have seen how Enums and Interfaces can help make your code more compact and readable. There
1723//! is another abstraction you can use to simplify your code. Interfaces allow you to group together
1724//! signals that are logically related into a named bundle (like a bus). You can also group
1725//! together `bits` into a logically related bundle that can be treated as a single entity.
1726//! While this is supported in RustHDL, it's not frequently that useful. Nonetheless.
1727//!
1728//! Suppose you have a set of signals to your circuit that all travel in the same direction,
1729//! but are different widths. Any maybe some of the elements are enums. Something like this
1730//!
1731//! ```rust
1732//!# use rust_hdl::prelude::*;
1733//! struct Foo {
1734//! pub in_red: Signal<In, Bits<5>>,
1735//! pub in_green: Signal<In, Bits<8>>,
1736//! pub in_blue: Signal<In, Bits<8>>,
1737//! pub in_alpha: Signal<In, Bits<6>>,
1738//! }
1739//! ```
1740//!
1741//! Instead, we can define a struct and annotate it with [LogicStruct], which makes it into a
1742//! type that can be used for a signal.
1743//! ```rust
1744//!# use rust_hdl::prelude::*;
1745//! #[derive(Default, PartialEq, LogicStruct, Copy, Clone, Debug)]
1746//! struct Color {
1747//! pub red: Bits<5>,
1748//! pub green: Bits<8>,
1749//! pub blue: Bits<8>,
1750//! pub alpha: Bits<6>,
1751//! }
1752//!
1753//! struct Foo {
1754//! pub in_color: Signal<In, Color>,
1755//! pub local_color: Signal<Local, Color>,
1756//! pub out_color: Signal<Out, Color>,
1757//! }
1758//!
1759//! impl Logic for Foo {
1760//! #[hdl_gen]
1761//! fn update(&mut self) {
1762//! self.local_color.next = self.in_color.val(); // Copy the struct as a single atom
1763//! // v-- Extract a single field using standard `.field` notation
1764//! if self.local_color.val().alpha.get_bit(4) {
1765//! self.local_color.next.red = 16.into(); // Assign to a single field of the struct
1766//! }
1767//! self.out_color.next = self.local_color.val();
1768//! }
1769//! }
1770//! ```
1771//!
1772//! From within the HDL kernel, you can access the fields of the struct as you normally would. You can
1773//! also assign entire structs to one another, as well as individual fields of a struct. The generated
1774//! Verilog is messy, and I don't use struct valued signals much. But if you need to use them they are
1775//! there.
1776//!
1777//! ## Loops and Arrays
1778//!
1779//! A frequently useful feature of hardware is to be able to handle a variable number of
1780//! inputs or outputs based on some parameter. Examples might include:
1781//! - A processing stage with a variable number of passes
1782//! - A mux with a variable number of inputs
1783//! - A bank of identical state machines, where the number of banks is variable
1784//!
1785//! In all of these cases, the tool to reach for is an array in RustHDL. Including an array
1786//! of subcircuits is pretty simple. You simply use a static sized array (via a `const generic`
1787//! parameter) or a `vec`. Here is an example of a circuit that contains a configurable number
1788//! of subcircuits, each of which is an instance of the `Pulser` circuit (a standard RustHDL
1789//! widget)
1790//! ```rust
1791//! # use rust_hdl::prelude::*;
1792//!
1793//! struct PulserSet<const N: usize> {
1794//! pub outs: Signal<Out, Bits<N>>,
1795//! pub clock: Signal<In, Clock>,
1796//! pulsers: [Pulser; N]
1797//! }
1798//! ```
1799//!
1800//! In this case, as long as the members of the array implement `Block` (i.e., are circuits),
1801//! everything will work as expected, including simulation and synthesis.
1802//!
1803//! Frequently, though, having an array of subcircuits means you need a way to loop over them
1804//! in order to do something useful with their inputs or outputs. Loops are were software-centric
1805//! thinking can get you into trouble very quickly. In hardware, it's best to think of loops
1806//! in terms of unrolling. A `for` loop in RustHDL does not actually loop over anything in
1807//! the hardware. Rather it is a way of repeating a block of code multiple times with a varying
1808//! parameter.
1809//!
1810//! So the `impl Logic` HDL kernel of the [PulserSet] example above might look something like this:
1811//! ```
1812//! # use rust_hdl::prelude::*;
1813//! # struct PulserSet<const N: usize> {
1814//! # pub outs: Signal<Out, Bits<N>>,
1815//! # pub clock: Signal<In, Clock>,
1816//! # pulsers: [Pulser; N]
1817//! # }
1818//! impl<const N: usize> Logic for PulserSet<N> {
1819//! #[hdl_gen]
1820//! fn update(&mut self) {
1821//! // Connect all the clocks & enable them all
1822//! for i in 0..N {
1823//! self.pulsers[i].clock.next = self.clock.val();
1824//! self.pulsers[i].enable.next = true.into();
1825//! }
1826//! // Connect the outputs...
1827//! self.outs.next = 0.into();
1828//! for i in 0..N {
1829//! self.outs.next = self.outs.val().replace_bit(i, self.pulsers[i].pulse.val());
1830//! }
1831//! }
1832//! }
1833//! ```
1834//!
1835//! Note that we are both reading and writing from `self.outs` in the same kernel, but we write
1836//! first, which makes it OK. Reading first would make this latching behavior, and RustHDL (or
1837//! `yosys`) would throw a fit.
1838//!
1839//! You can do some basic manipulations with the index (like using `3*i+4`, for example), but
1840//! don't get carried away. Those expressions are evaluated by the HDL kernel generator and
1841//! it has a limited vocab.
1842//!
1843//! ## High Level Synthesis
1844//!
1845//! RustHDL supports it's own version of High Level Synthesis (HLS). Normally, this is some kind
1846//! of strange drag-and-drop based entry or visual programming paradigm. Worse, still, it could
1847//! be some kind of macro meta-language that you build complex designs out of using a graphical
1848//! editor.
1849//!
1850//! But that is not the case here! RustHDL's HLS approach is much simpler. Essentially,
1851//! even though [Interfaces] are so great, you may not want to use them. So the core widgets,
1852//! like the [AsynchronousFIFO] do not use Interfaces. That leads to some pretty gnarly
1853//! circuit definitions. Here is the [AsynchronousFIFO] for example:
1854//! ```rust
1855//! # use rust_hdl::prelude::*;
1856//! pub struct AsynchronousFIFO<D: Synth, const N: usize, const NP1: usize, const BLOCK_SIZE: u32> {
1857//! // Read interface
1858//! pub read: Signal<In, Bit>,
1859//! pub data_out: Signal<Out, D>,
1860//! pub empty: Signal<Out, Bit>,
1861//! pub almost_empty: Signal<Out, Bit>,
1862//! pub underflow: Signal<Out, Bit>,
1863//! pub read_clock: Signal<In, Clock>,
1864//! pub read_fill: Signal<Out, Bits<NP1>>,
1865//! // Write interface
1866//! pub write: Signal<In, Bit>,
1867//! pub data_in: Signal<In, D>,
1868//! pub full: Signal<Out, Bit>,
1869//! pub almost_full: Signal<Out, Bit>,
1870//! pub overflow: Signal<Out, Bit>,
1871//! pub write_clock: Signal<In, Clock>,
1872//! pub write_fill: Signal<Out, Bits<NP1>>,
1873//! // Internals ommitted...
1874//! }
1875//! ```
1876//! Using an [AsynchronousFIFO] requires up to 14 separate signals! With mixed directions and types!
1877//! Fortunately, there is an HLS wrapper type you can use instead. It's _much_ simpler
1878//! ```rust
1879//! # use rust_hdl::prelude::*;
1880//! pub struct AsyncFIFO<T: Synth, const N: usize, const NP1: usize, const BLOCK_SIZE: u32> {
1881//! pub bus_write: FIFOWriteResponder<T>,
1882//! pub write_clock: Signal<In, Clock>,
1883//! pub bus_read: FIFOReadResponder<T>,
1884//! pub read_clock: Signal<In, Clock>,
1885//! fifo: AsynchronousFIFO<T, N, NP1, BLOCK_SIZE>,
1886//! }
1887//!```
1888//!
1889//! In this case, it has only 4 signals, and using it is also much easier. You can use the
1890//! [FIFOWriteResponder] and [FIFOWriteController] busses to send and receive data from the
1891//! asynchronous fifo. Even better is the fact that this HLS construct is just a thin wrapper
1892//! around the [AsynchronousFIFO], so when you synthesize it, or plot signals, there is nothing
1893//! extra under the hood.
1894//!
1895//! The HLS library also includes a sort of System-on-chip model in case you want to use it.
1896//! It allows you to connect a set of widgets to a single controller, and route data to them
1897//! over a fixed bus using a very simple protocol. It won't replace AXI or WishBone, but it
1898//! can be used to build some pretty complicated designs and remain readable. The test cases
1899//! are a good place to look for some runnable examples of the different SoC widgets.
1900//!
1901//! ## Wrapping IP Cores
1902//!
1903//! Occasionally in RustHDL, you will need to wrap an external IP core or logic primitive supported
1904//! by your hardware, but that is not supported directly in RustHDL. There best method for wrapping
1905//! Verilog code is to use the [Wrapper](core::ast::Wrapper) struct and provide your own implementation
1906//! of the `hdl` method for your logic.
1907//!
1908//! Here is a minimal example of a clock buffer primitive (that takes a differential clock input
1909//! and provides a single ended clock output). The Verilog module declaration for the clock buffer is
1910//! simply:
1911//! ```verilog
1912//! module IBUFDS(I, B, O);
1913//! input I;
1914//! input B;
1915//! output O;
1916//! endmodule
1917//! ```
1918//!
1919//! Since the implementation of this device is built into the FPGA (it is a hardware primitive),
1920//! the module definition is enough for the toolchain to construct the device. Here is a
1921//! complete example of a wrapped version of this for use in RustHDL.
1922//!
1923//!```rust
1924//! # use rust_hdl::prelude::*;
1925//!
1926//! #[derive(LogicBlock, Default)]
1927//! pub struct ClockDriver {
1928//! pub clock_p: Signal<In, Clock>,
1929//! pub clock_n: Signal<In, Clock>,
1930//! pub sys_clock: Signal<Out, Clock>,
1931//! }
1932//!
1933//! impl Logic for ClockDriver {
1934//! // Our simulation simply forwards the positive clock to the system clock
1935//! fn update(&mut self) {
1936//! self.sys_clock.next = self.clock_p.val();
1937//! }
1938//! // RustHDL cannot determine what signals are driven based on the declaration
1939//! // alone. This method identifies `sys_clock` as being driven by the internal
1940//! // logic of the device.
1941//! fn connect(&mut self) {
1942//! self.sys_clock.connect();
1943//! }
1944//! // Normally the `hdl` method is generated by the `derive` macro. But in this
1945//! // case we implement it ourselves to wrap the Verilog code.
1946//! fn hdl(&self) -> Verilog {
1947//! Verilog::Wrapper(Wrapper {
1948//! code: r#"
1949//! // This is basically arbitrary Verilog code that lives inside
1950//! // a scoped module generated by RustHDL. Whatever IP cores you
1951//! // use here must have accompanying core declarations in the
1952//! // cores string, or they will fail verification.
1953//! //
1954//! // In this simple case, we remap the names here
1955//! IBUFDS ibufds_inst(.I(clock_p), .B(clock_n), .O(sys_clock));
1956//!
1957//! "#.into(),
1958//! // Some synthesis tools (like [Yosys] need a blackbox declaration so they
1959//! // can process the Verilog if they do not have primitives in their
1960//! // libraries for the device. Other toolchains will strip these out.
1961//! cores: r#"
1962//! (* blackbox *)
1963//! module IBUFDS(I, B, O);
1964//! input I;
1965//! input B;
1966//! output O;
1967//! endmodule"#.into(),
1968//! })
1969//! }
1970//! }
1971//! ```
1972//!
1973
1974#![warn(missing_docs)]
1975
1976///! Tools for documenting RustHDL designs, including the generation of SVGs from simulation waveforms.
1977pub mod docs;
1978///! A series of High Level Synthesis blocks used to build System-on-Chip designs quickly.
1979pub use rust_hdl_hls as hls;
1980///! Prelude module defines common symbols to make importing RustHDL easier.
1981pub mod prelude;
1982///! The core RustHDL module. Defines variable width bits, signals, logical blocks, etc.
1983pub use rust_hdl_core as core;
1984///! A set of routines for dealing with FPGA specific pieces. Either tools for synthesis, or
1985/// logic circuits that are specific to an FPGA family.
1986#[cfg(feature = "fpga")]
1987pub use rust_hdl_fpga_support as fpga;
1988///! Support for the OpalKelly devices (including HDL components and the FrontPanel API)
1989#[cfg(feature = "ok")]
1990pub use rust_hdl_ok_core as ok;
1991#[cfg(feature = "ok")]
1992pub use rust_hdl_ok_frontpanel_sys as frontpanel;
1993///! Module that contains all code related to simulating RustHDL designs in Rust (i.e., without
1994///! an external Verilog simulator).
1995pub use rust_hdl_sim as sim;
1996///! A set of core widgets useful for FPGA based designs, all written in RustHDL. This includes
1997///! elements such as Digital Flip Flops, Block RAMs, ROMs, FIFOs, SDRAM controllers, SPI controllers
1998///! I2C controllers, FIR filters, etc.
1999pub use rust_hdl_widgets as widgets;
2000
2001#[test]
2002fn doc_sim() {
2003 use crate::prelude::*; // <- shorthand to bring in all definitions
2004 use rand::{thread_rng, Rng};
2005 use std::num::Wrapping;
2006
2007 // v--- Required by RustHDL
2008 #[derive(LogicBlock, Default)]
2009 struct MyAdder {
2010 pub sig_a: Signal<In, Bits<8>>,
2011 pub sig_b: Signal<In, Bits<8>>,
2012 pub sig_sum: Signal<Out, Bits<8>>,
2013 pub clock: Signal<In, Clock>,
2014 my_reg: DFF<Bits<8>>,
2015 }
2016
2017 impl Logic for MyAdder {
2018 #[hdl_gen] // <--- don't forget this
2019 fn update(&mut self) {
2020 // Setup the DFF.. this macro is handy to prevent latches
2021 dff_setup!(self, clock, my_reg);
2022 self.my_reg.d.next = self.sig_a.val() + self.sig_b.val();
2023 self.sig_sum.next = self.my_reg.q.val();
2024 }
2025 }
2026
2027 let test_cases = (0..512)
2028 .map(|_| {
2029 let a_val = thread_rng().gen::<u8>();
2030 let b_val = thread_rng().gen::<u8>();
2031 let c_val = (Wrapping(a_val) + Wrapping(b_val)).0;
2032 (
2033 a_val.to_bits::<8>(),
2034 b_val.to_bits::<8>(),
2035 c_val.to_bits::<8>(),
2036 )
2037 })
2038 .collect::<Vec<_>>();
2039 let mut sim = simple_sim!(MyAdder, clock, 100_000_000, ep, {
2040 let mut x = ep.init()?; // Get the circuit
2041 for test_case in &test_cases {
2042 x.sig_a.next = test_case.0;
2043 x.sig_b.next = test_case.1;
2044 wait_clock_cycle!(ep, clock, x);
2045 println!(
2046 "Test case {:x} + {:x} = {:x} (check {:x})",
2047 test_case.0,
2048 test_case.1,
2049 x.sig_sum.val(),
2050 test_case.2
2051 );
2052 sim_assert_eq!(ep, x.sig_sum.val(), test_case.2, x);
2053 }
2054 ep.done(x)
2055 });
2056 /* sim.run(MyAdder::default().into(), sim_time::ONE_MILLISECOND)
2057 .unwrap();*/
2058 sim.run_to_file(
2059 MyAdder::default().into(),
2060 sim_time::ONE_MILLISECOND,
2061 &vcd_path!("my_adder.vcd"),
2062 )
2063 .unwrap();
2064 vcd_to_svg(
2065 &vcd_path!("my_adder.vcd"),
2066 "/tmp/my_adder.svg",
2067 &[
2068 "uut.clock",
2069 "uut.sig_a",
2070 "uut.sig_b",
2071 "uut.my_reg.d",
2072 "uut.my_reg.q",
2073 "uut.sig_sum",
2074 ],
2075 0,
2076 100 * sim_time::ONE_NANOSECOND,
2077 )
2078 .unwrap()
2079}
2080
2081#[test]
2082fn doc_vlog() {
2083 use crate::prelude::*;
2084
2085 #[derive(Copy, Clone, PartialEq, Debug, LogicState)]
2086 enum State {
2087 Idle,
2088 Sending,
2089 Receiving,
2090 Done,
2091 }
2092
2093 #[derive(LogicBlock, Default)]
2094 struct Foo {
2095 clock: Signal<In, Clock>,
2096 dff: DFF<State>, // <-- This is a 2 bit DFF
2097 }
2098
2099 impl Logic for Foo {
2100 #[hdl_gen]
2101 fn update(&mut self) {
2102 dff_setup!(self, clock, dff);
2103 match self.dff.q.val() {
2104 State::Idle => {
2105 self.dff.d.next = State::Sending;
2106 }
2107 State::Sending => {
2108 self.dff.d.next = State::Receiving;
2109 }
2110 State::Receiving => {
2111 self.dff.d.next = State::Done;
2112 }
2113 State::Done => {
2114 self.dff.d.next = State::Idle;
2115 }
2116 }
2117 }
2118 }
2119
2120 let mut uut = Foo::default();
2121 uut.connect_all();
2122 let vlog = generate_verilog(&uut);
2123 println!("{}", vlog);
2124}
2125
2126#[test]
2127fn doc_struct_valued() {
2128 use crate::prelude::*;
2129 #[derive(Default, PartialEq, LogicStruct, Copy, Clone, Debug)]
2130 struct Color {
2131 pub red: Bits<5>,
2132 pub green: Bits<8>,
2133 pub blue: Bits<8>,
2134 pub alpha: Bits<6>,
2135 }
2136
2137 struct Foo {
2138 pub in_color: Signal<In, Color>,
2139 pub local_color: Signal<Local, Color>,
2140 pub out_color: Signal<Out, Color>,
2141 }
2142
2143 impl Logic for Foo {
2144 #[hdl_gen]
2145 fn update(&mut self) {
2146 self.local_color.next = self.in_color.val(); // Copy the struct as a single atom
2147 // v-- Extract a single field using standard `.field` notation
2148 if self.local_color.val().alpha.get_bit(4) {
2149 self.local_color.next.red = 16.into(); // Assign to a single field of the struct
2150 }
2151 self.out_color.next = self.local_color.val();
2152 }
2153 }
2154}
2155
2156#[test]
2157fn doc_verilog_demo() {
2158 use crate::prelude::*; // <- shorthand to bring in all definitions
2159
2160 // v--- Required by RustHDL
2161 #[derive(LogicBlock, Default)]
2162 struct MyAdder {
2163 pub sig_a: Signal<In, Bits<8>>,
2164 pub sig_b: Signal<In, Bits<8>>,
2165 pub sig_sum: Signal<Out, Bits<8>>,
2166 pub clock: Signal<In, Clock>,
2167 my_reg: DFF<Bits<8>>,
2168 }
2169
2170 impl Logic for MyAdder {
2171 #[hdl_gen] // <--- don't forget this
2172 fn update(&mut self) {
2173 // Setup the DFF.. this macro is handy to prevent latches
2174 dff_setup!(self, clock, my_reg);
2175 self.my_reg.d.next = self.sig_a.val() + self.sig_b.val();
2176 self.sig_sum.next = self.my_reg.q.val();
2177 }
2178 }
2179
2180 let mut uut = MyAdder::default();
2181 uut.connect_all();
2182 println!("{}", generate_verilog(&uut));
2183}