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}