1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
use crate::core::prelude::*;
use crate::dff_setup;
use crate::widgets::prelude::{TristateBuffer, DFF};

#[derive(LogicBlock)]
pub struct EdgeTristateBufferDelayed<T: Synth> {
    pub to_pin: Signal<In, T>,
    pub from_pin: Signal<Out, T>,
    pub output_enable: Signal<In, Bit>,
    pub clk: Signal<In, Clock>,
    pub reset: Signal<In, Reset>,
    pub pin: Signal<InOut, T>,
    dff_out: DFF<T>,
    dff_in: DFF<T>,
    buffer: TristateBuffer<T>,
    _delay: u8,
}

impl<T: Synth> EdgeTristateBufferDelayed<T> {
    pub fn new(delay: u8) -> Self {
        Self {
            to_pin: Default::default(),
            from_pin: Default::default(),
            output_enable: Default::default(),
            clk: Default::default(),
            reset: Default::default(),
            pin: Default::default(),
            dff_out: Default::default(),
            dff_in: Default::default(),
            buffer: Default::default(),
            _delay: delay,
        }
    }
}

fn wrapper_once(delay: u8) -> String {
    format!(
        r##"
    wire bb_to_pin;
    wire bb_from_pin_a;
    wire bb_from_pin_z;

    OFS1P3DX obuf(.D(to_pin), .CD(reset), .SP(1'b1), .SCLK(clk), .Q(bb_to_pin));
    IFS1P3DX ibuf(.D(bb_from_pin_z), .CD(reset), .SP(1'b1), .SCLK(clk), .Q(from_pin));
    BB bb(.I(bb_to_pin), .O(bb_from_pin_a), .B(pin), .T(~output_enable));

    defparam dg.DEL_VALUE = {delay_from_pin};
    defparam dg.DEL_MODE = "USER_DEFINED";
    DELAYG dg(.A(bb_from_pin_a),.Z(bb_from_pin_z));
"##,
        delay_from_pin = delay
    )
}

fn wrapper_multiple(count: usize, delay: u8) -> String {
    let bufs = (0..count)
        .map(|x| {
            format!(
                r#"
    OFS1P3DX obuf_{x}(.D(to_pin[{x}]), .CD(reset), .SP(1'b1), .SCLK(clk), .Q(bb_to_pin[{x}]));
    IFS1P3DX ibuf_{x}(.D(bb_from_pin_z[{x}]), .CD(reset), .SP(1'b1), .SCLK(clk), .Q(from_pin[{x}]));
    BB bb_{x}(.I(bb_to_pin[{x}]), .O(bb_from_pin_a[{x}]), .B(pin[{x}]), .T(~output_enable));

    defparam dg_from_pin_{x}.DEL_VALUE = {delay_from_pin};
    defparam dg_from_pin_{x}.DEL_MODE = "USER_DEFINED";
    DELAYG dg_from_pin_{x}(.A(bb_from_pin_a[{x}]),.Z(bb_from_pin_z[{x}]));
        "#,
                x = x,
                delay_from_pin = delay
            )
        })
        .collect::<Vec<_>>()
        .join("\n");
    format!(
        r##"
wire [{B}:0] bb_to_pin;
wire [{B}:0] bb_from_pin_a;
wire [{B}:0] bb_from_pin_z;

{bufs}
    "##,
        B = count,
        bufs = bufs
    )
}

impl<T: Synth> Logic for EdgeTristateBufferDelayed<T> {
    fn update(&mut self) {
        dff_setup!(self, clk, reset, dff_out, dff_in);
        self.buffer.write_enable.next = self.output_enable.val();
        self.dff_in.d.next = self.buffer.read_data.val();
        self.dff_out.d.next = self.to_pin.val();
        self.buffer.write_data.next = self.dff_out.q.val();
        self.from_pin.next = self.dff_in.q.val();
        Signal::<InOut, T>::link(&mut self.pin, &mut self.buffer.bus);
    }
    fn connect(&mut self) {
        self.dff_out.clock.connect();
        self.dff_in.clock.connect();
        self.dff_out.reset.connect();
        self.dff_in.reset.connect();
        self.buffer.write_enable.connect();
        self.dff_in.d.connect();
        self.dff_out.d.connect();
        self.buffer.write_data.connect();
        self.from_pin.connect();
    }
    fn hdl(&self) -> Verilog {
        Verilog::Wrapper(Wrapper {
            code: if T::BITS == 1 {
                wrapper_once(self._delay).to_string()
            } else {
                wrapper_multiple(T::BITS, self._delay)
            },
            cores: r##"
(* blackbox *)
module IFS1P3DX(input D, input SP, input SCLK, input CD, output Q);
endmodule

(* blackbox *)
module OFS1P3DX(input D, input SP, input SCLK, input CD, output Q);
endmodule

(* blackbox *)
module BB(input I, input T, output O, inout B);
endmodule

(* blackbox *)
module DELAYG(input A, output Z);
parameter DEL_MODE = "USER_DEFINED";
parameter DEL_VALUE = 0;
endmodule

            "##
            .into(),
        })
    }
}

#[test]
fn test_edge_buffer_synthesizes() {
    let mut uut = TopWrap::new(EdgeTristateBufferDelayed::<Bits<8>>::new(10));
    uut.uut.output_enable.connect();
    uut.uut.to_pin.connect();
    uut.uut.clk.connect();
    uut.uut.reset.connect();
    uut.uut.pin.connect();
    uut.connect_all();
    std::fs::write("edge_tristate_buffer.v", generate_verilog(&uut)).unwrap();
    yosys_validate("edge_tristate_buffer_delayed", &generate_verilog(&uut)).unwrap();
}