rust_hdl_widgets/tristate.rs
1use rust_hdl_core::prelude::*;
2
3/// Tristate Buffer
4///
5/// Most FPGAs do not support internal tristate logic. Instead, the compilers turn tristate
6/// logic into a combination of a pair of signals (one in, one out) and an enable line. However,
7/// the real world definitely needs tristate logic, and there are usually dedicated buffers
8/// on the FPGA that can drive a tristate line using a pin that is appropriately configured.
9///
10/// Most FPGA toolchains can infer the tristate buffer when it's at the edge of the design. So
11/// when you need a tristate buffer, you can use this struct. Note that it is generic over
12/// the signals being tristated. So you can include a set of different tristate buffers with
13/// a single entity.
14///
15/// ```rust
16/// # use rust_hdl_core::prelude::*;
17/// # use rust_hdl_widgets::prelude::*;
18///
19/// // An example of a simple tristate 8-bit bus
20/// #[derive(LogicInterface, Default)]
21/// struct EightBitBus {
22/// bus: Signal<InOut, Bits<8>>,
23/// }
24/// ```
25#[derive(LogicBlock, Default)]
26pub struct TristateBuffer<D: Synth> {
27 /// The tristated signals come out of this pin. This should be a top level signal in your design.
28 pub bus: Signal<InOut, D>,
29 /// When asserted (true), the bus will attempt to drive `write_data` to the pins.
30 pub write_enable: Signal<In, Bit>,
31 /// The data to write to the bus. Ignored when `write_enable` is not active (high).
32 pub write_data: Signal<In, D>,
33 /// The read back from the bus. When `write_enable` is false, then this signal represents
34 /// the external signals driving the FPGA pins. For FPGA, this is likely equal to `write_data`
35 /// when `write_enable` is true.
36 pub read_data: Signal<Out, D>,
37}
38
39impl<D: Synth> Logic for TristateBuffer<D> {
40 fn update(&mut self) {
41 if self.write_enable.val() {
42 self.bus.next = self.write_data.val();
43 }
44 self.read_data.next = self.bus.val();
45 self.bus.set_tristate_is_output(self.write_enable.val());
46 }
47
48 fn connect(&mut self) {
49 self.bus.connect();
50 self.read_data.connect();
51 }
52
53 fn hdl(&self) -> Verilog {
54 Verilog::Custom(format!(
55 "\
56 assign bus = write_enable ? write_data : {WIDTH}'bz;
57always @(*) read_data = bus;",
58 WIDTH = D::BITS
59 ))
60 }
61}
62
63#[test]
64fn test_tristate_synthesizes() {
65 let mut uut = TristateBuffer::<Bits<8>>::default();
66 uut.connect_all();
67 let vlog = generate_verilog(&uut);
68 yosys_validate("tristate", &vlog).unwrap()
69}