Skip to content

Commit 067d94d

Browse files
authored
isochronous_4phase_handshake: Add implementation (#100)
* isochronous_4phase_handshake: Add implementation * Add `isochronous_4phase_handshake` * Fix CHANGELOG * Fix lint problems
1 parent 2d35892 commit 067d94d

File tree

8 files changed

+200
-54
lines changed

8 files changed

+200
-54
lines changed

Bender.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ sources:
3333
- src/exp_backoff.sv
3434
- src/fifo_v3.sv
3535
- src/gray_to_binary.sv
36+
- src/isochronous_4phase_handshake.sv
3637
- src/isochronous_spill_register.sv
3738
- src/lfsr.sv
3839
- src/lfsr_16bit.sv
@@ -104,7 +105,7 @@ sources:
104105
- test/stream_to_mem_tb.sv
105106
- test/sub_per_hash_tb.sv
106107
# Level 1
107-
- test/isochronous_spill_register_tb.sv
108+
- test/isochronous_crossing_tb.sv
108109
- test/stream_omega_net_tb.sv
109110
- test/stream_xbar_tb.sv
110111

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
88

99
### Added
1010
- Add `cc_onehot`
11+
- `isochronous_4phase_handshake`: Isochronous clock domain crossing cutting all paths using a 4-phase handshake.
12+
- Changed `isochronous_spill_register_tb` to `isochronous_crossing_tb` also covering the `isochronous_4phase_handshake`
13+
module.
1114

1215
## 1.22.1 - 2021-06-14
1316
### Fixed

README.md

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,21 +25,22 @@ Please note that cells with status *deprecated* are not to be used for new desig
2525

2626
### Clock Domains and Asynchronous Crossings
2727

28-
| Name | Description | Status | Superseded By |
29-
| ---------------------------- | -------------------------------------------------------------------------------- | ------------ | ------------- |
30-
| `cdc_2phase` | Clock domain crossing using two-phase handshake, with ready/valid interface | active | |
31-
| `cdc_fifo_2phase` | Clock domain crossing FIFO using two-phase handshake, with ready/valid interface | active | |
32-
| `cdc_fifo_gray` | Clock domain crossing FIFO using a gray-counter, with ready/valid interface | active | |
33-
| `edge_detect` | Rising/falling edge detector | active | |
34-
| `edge_propagator` | **ANTONIO ADD DESCRIPTION** | active | |
35-
| `edge_propagator_rx` | **ANTONIO ADD DESCRIPTION** | active | |
36-
| `edge_propagator_tx` | **ANTONIO ADD DESCRIPTION** | active | |
37-
| `isochronous_spill_register` | Isochronous clock domain crossing and full handshake (like `spill_register`) | active | |
38-
| `pulp_sync` | Serial line synchronizer | *deprecated* | `sync` |
39-
| `pulp_sync_wedge` | Serial line synchronizer with edge detector | *deprecated* | `sync_wedge` |
40-
| `serial_deglitch` | Serial line deglitcher | active | |
41-
| `sync` | Serial line synchronizer | active | |
42-
| `sync_wedge` | Serial line synchronizer with edge detector | active | |
28+
| Name | Description | Status | Superseded By |
29+
|--------------------------------|----------------------------------------------------------------------------------|--------------|---------------|
30+
| `cdc_2phase` | Clock domain crossing using two-phase handshake, with ready/valid interface | active | |
31+
| `cdc_fifo_2phase` | Clock domain crossing FIFO using two-phase handshake, with ready/valid interface | active | |
32+
| `cdc_fifo_gray` | Clock domain crossing FIFO using a gray-counter, with ready/valid interface | active | |
33+
| `edge_detect` | Rising/falling edge detector | active | |
34+
| `edge_propagator` | **ANTONIO ADD DESCRIPTION** | active | |
35+
| `edge_propagator_rx` | **ANTONIO ADD DESCRIPTION** | active | |
36+
| `edge_propagator_tx` | **ANTONIO ADD DESCRIPTION** | active | |
37+
| `isochronous_spill_register` | Isochronous clock domain crossing and full handshake (like `spill_register`) | active | |
38+
| `isochronous_4phase_handshake` | Isochronous four-phase handshake. | active | |
39+
| `pulp_sync` | Serial line synchronizer | *deprecated* | `sync` |
40+
| `pulp_sync_wedge` | Serial line synchronizer with edge detector | *deprecated* | `sync_wedge` |
41+
| `serial_deglitch` | Serial line deglitcher | active | |
42+
| `sync` | Serial line synchronizer | active | |
43+
| `sync_wedge` | Serial line synchronizer with edge detector | active | |
4344

4445
### Counters and Shift Registers
4546

src/id_queue.sv

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,8 @@ module id_queue #(
258258
// Set free bit of linked data entry, all other bits are don't care.
259259
linked_data_d[head_tail_q[match_out_idx].head] = '0;
260260
linked_data_d[head_tail_q[match_out_idx].head][0] = 1'b1;
261-
if (head_tail_q[match_out_idx].head == head_tail_q[match_out_idx].tail) begin
261+
if (head_tail_q[match_out_idx].head
262+
== head_tail_q[match_out_idx].tail) begin
262263
oup_ht_popped = 1'b1;
263264
head_tail_d[match_out_idx] = '{free: 1'b1, default: '0};
264265
end else begin
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// Copyright 2020 ETH Zurich and University of Bologna.
2+
//
3+
// Copyright and related rights are licensed under the Solderpad Hardware
4+
// License, Version 0.51 (the "License"); you may not use this file except in
5+
// compliance with the License. You may obtain a copy of the License at
6+
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
7+
// or agreed to in writing, software, hardware and materials distributed under
8+
// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
9+
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
10+
// specific language governing permissions and limitations under the License.
11+
//
12+
// Author: Florian Zaruba <[email protected]>
13+
14+
/// 4-phase handshake between isochronous clock domains
15+
/// (i.e., clock domains which operate on an integer multiple of each other).
16+
///
17+
/// The internals of this modules are similar to a clock-domain crossing except that
18+
/// they do not synchronize the handshake signals as signals can not become metastable (covered by STA).
19+
/// The upstream circuit will only handshake iff the downstream circuit handshaked.
20+
///
21+
/// ## Optionally Passing of Data
22+
///
23+
/// If the passing of data is necessary this should be done out side the module, for example:
24+
/// ```
25+
/// `FFLNR(dst_data_o, src_data_i, (src_valid_i && src_ready_o), src_clk_i)
26+
/// ```
27+
///
28+
/// This module differs to `isochronous_spill_register` that it doesn't buffer any data
29+
/// and only toggles the source handshake once the destination handshake has been toggled.
30+
///
31+
/// # Restrictions
32+
///
33+
/// Source and destination clock domains must be an integer multiple of each other and
34+
/// all timing-paths need to be covered by STA. For example a recommended SDC would be:
35+
///
36+
/// `create_generated_clock dst_clk_i -name dst_clk -source src_clk_i -divide_by 2
37+
///
38+
/// There are _no_ restrictions on which clock domain should be the faster, any integer
39+
/// ratio will work.
40+
41+
`include "common_cells/registers.svh"
42+
43+
module isochronous_4phase_handshake (
44+
input logic src_clk_i,
45+
input logic src_rst_ni,
46+
input logic src_valid_i,
47+
output logic src_ready_o,
48+
input logic dst_clk_i,
49+
input logic dst_rst_ni,
50+
output logic dst_valid_o,
51+
input logic dst_ready_i
52+
);
53+
54+
logic src_req_q, src_ack_q;
55+
logic dst_req_q, dst_ack_q;
56+
57+
// source is making a request
58+
`FFLARN(src_req_q, ~src_req_q, (src_valid_i && src_ready_o), 1'b0, src_clk_i, src_rst_ni)
59+
// "synchronize" the acknowledge into the sending clock-domain
60+
`FFARN(src_ack_q, dst_ack_q, 1'b0, src_clk_i, src_rst_ni)
61+
// source is ready if the request wasn't yet acknowledged
62+
assign src_ready_o = (src_req_q == src_ack_q);
63+
64+
// down-stream circuit is acknowledging the handshake
65+
`FFLARN(dst_ack_q, ~dst_ack_q, (dst_valid_o && dst_ready_i), 1'b0, dst_clk_i, dst_rst_ni)
66+
// "synchronize" the request into the receiving clock domain
67+
`FFARN(dst_req_q, src_req_q, 1'b0, dst_clk_i, dst_rst_ni)
68+
// destination is valid if we didn't yet get acknowledge
69+
assign dst_valid_o = (dst_req_q != dst_ack_q);
70+
71+
// pragma translate_off
72+
// stability guarantees
73+
`ifndef VERILATOR
74+
assert property (@(posedge src_clk_i) disable iff (src_rst_ni)
75+
(src_valid_i && !src_ready_o |=> $stable(src_valid_i))) else $error("src_valid_i is unstable");
76+
assert property (@(posedge dst_clk_i) disable iff (dst_rst_ni)
77+
(dst_valid_o && !dst_ready_i |=> $stable(dst_valid_o))) else $error("dst_valid_o is unstable");
78+
`endif
79+
// pragma translate_on
80+
81+
endmodule

src/isochronous_spill_register.sv

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,16 @@
2727
/// in different clock domains. As we know the static timing relationship between the
2828
/// clock domains we can rely on static timing analysis (STA) to get the sampling windows
2929
/// right and therefore don't need any synchronization.
30+
///
31+
/// # Restrictions
32+
///
33+
/// Source and destination clock domains must be an integer multiple of each other and
34+
/// all timing-paths need to be covered by STA. For example a recommended SDC would be:
35+
///
36+
/// `create_generated_clock dst_clk_i -name dst_clk -source src_clk_i -divide_by 2
37+
///
38+
/// There are _no_ restrictions on which clock domain should be the faster, any integer
39+
/// ratio will work.
3040
module isochronous_spill_register #(
3141
/// Data type of spill register.
3242
parameter type T = logic,
@@ -84,4 +94,18 @@ module isochronous_spill_register #(
8494
assign dst_valid_o = (rd_pointer_q ^ wr_pointer_q) != '0;
8595
assign dst_data_o = mem_q[rd_pointer_q[0]];
8696
end
97+
98+
// pragma translate_off
99+
// stability guarantees
100+
`ifndef VERILATOR
101+
assert property (@(posedge src_clk_i) disable iff (src_rst_ni)
102+
(src_valid_i && !src_ready_o |=> $stable(src_valid_i))) else $error("src_valid_i is unstable");
103+
assert property (@(posedge src_clk_i) disable iff (src_rst_ni)
104+
(src_valid_i && !src_ready_o |=> $stable(src_data_i))) else $error("src_data_i is unstable");
105+
assert property (@(posedge dst_clk_i) disable iff (dst_rst_ni)
106+
(dst_valid_o && !dst_ready_i |=> $stable(dst_valid_o))) else $error("dst_valid_o is unstable");
107+
assert property (@(posedge dst_clk_i) disable iff (dst_rst_ni)
108+
(dst_valid_o && !dst_ready_i |=> $stable(dst_data_o))) else $error("dst_data_o is unstable");
109+
`endif
110+
// pragma translate_on
87111
endmodule
Lines changed: 63 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -11,25 +11,28 @@
1111

1212
// Author: Florian Zaruba <[email protected]>
1313

14-
/// Testbench for the isochronous spill register.
14+
/// Testbench for the isochronous spill register and 4-phase handshake.
1515
/// ## Compilation
1616
///
1717
/// This module needs to be compile with `-timescale "1 ns / 1 ps"` as
1818
/// it doesn't specify an internal timescale.
19-
module isochronous_spill_register_tb #(
19+
module isochronous_crossing_tb #(
2020
parameter int unsigned NumReq = 32'd10000,
21-
parameter time SrcCyclTime = 20ns,
22-
/// Make sure that the clocks are an integer multiple of each other.
23-
parameter time DstCyclTime = SrcCyclTime*2
21+
parameter string DUT = "spill_register",
22+
parameter int unsigned TCK_SRC_MULT = 2,
23+
parameter int unsigned TCK_DST_MULT = 6
2424
);
2525

26+
localparam time CyclTime = 10ns; // smallest possible cycle time
27+
2628
logic src_clk, dst_clk;
2729
logic src_rst_n, dst_rst_n;
2830
logic sim_done;
2931

3032
typedef logic [15:0] payload_t;
3133
// check FIFO
3234
payload_t data_fifo[$];
35+
mailbox #(payload_t) data_mbx = new();
3336

3437
STREAM_DV #(
3538
.payload_t (payload_t)
@@ -45,19 +48,22 @@ module isochronous_spill_register_tb #(
4548

4649
typedef stream_test::stream_driver #(
4750
.payload_t (payload_t),
48-
.TA (SrcCyclTime*0.2),
49-
.TT (SrcCyclTime*0.8)
51+
.TA (TCK_SRC_MULT*CyclTime*0.2),
52+
.TT (TCK_SRC_MULT*CyclTime*0.8)
5053
) stream_driver_in_t;
5154

5255
typedef stream_test::stream_driver #(
5356
.payload_t (payload_t),
54-
.TA (DstCyclTime*0.2),
55-
.TT (DstCyclTime*0.8)
57+
.TA (TCK_DST_MULT*CyclTime*0.2),
58+
.TT (TCK_DST_MULT*CyclTime*0.8)
5659
) stream_driver_out_t;
5760

5861
stream_driver_in_t in_driver = new(dut_in);
5962
stream_driver_out_t out_driver = new(dut_out);
6063

64+
int unsigned handshake_mst = 0;
65+
int unsigned handshake_slv = 0;
66+
6167
// Generate Stream data
6268
initial begin : proc_stream_master
6369
automatic payload_t test_data;
@@ -68,9 +74,10 @@ module isochronous_spill_register_tb #(
6874
for (int unsigned i = 0; i < NumReq; i++) begin
6975
test_data = payload_t'($urandom());
7076
stall_cycles = $urandom_range(0, 5);
71-
data_fifo.push_back(test_data);
77+
handshake_mst++;
7278
repeat (stall_cycles) @(posedge src_clk);
7379
in_driver.send(test_data);
80+
data_mbx.put(test_data);
7481
end
7582
end
7683

@@ -87,12 +94,14 @@ module isochronous_spill_register_tb #(
8794
stall_cycles = $urandom_range(0, 5);
8895
repeat (stall_cycles) @(posedge dst_clk);
8996
out_driver.recv(actual);
90-
expected = data_fifo.pop_front();
97+
data_mbx.get(expected);
98+
handshake_slv++;
9199
assert(expected === actual) else $error("expected: %h, actual: %0h", expected, actual);
92100
num_tested++;
93101
end
94102
repeat (50) @(posedge dst_clk);
95103
sim_done = 1'b1;
104+
assert(handshake_mst == handshake_slv) else $error("Amount of handshakes differed.");
96105
end
97106

98107
// stop the simulation
@@ -104,18 +113,20 @@ module isochronous_spill_register_tb #(
104113

105114
// Clock Generation
106115
initial begin
116+
$display("Simulating %d", DUT);
107117
src_clk = 1'b0;
108-
forever begin
109-
src_clk = ~src_clk;
110-
#(SrcCyclTime / 2);
111-
end
112-
end
113-
114-
initial begin
115118
dst_clk = 1'b0;
116119
forever begin
117-
dst_clk = ~dst_clk;
118-
#(DstCyclTime / 2);
120+
fork
121+
forever begin
122+
src_clk = ~src_clk;
123+
#((CyclTime * TCK_SRC_MULT) / 2);
124+
end
125+
forever begin
126+
dst_clk = ~dst_clk;
127+
#((CyclTime * TCK_DST_MULT) / 2);
128+
end
129+
join
119130
end
120131
end
121132

@@ -140,19 +151,36 @@ module isochronous_spill_register_tb #(
140151
dst_rst_n = 1'b1;
141152
end
142153

143-
isochronous_spill_register #(
144-
.T (payload_t)
145-
) i_isochronous_spill_register (
146-
.src_clk_i (dut_in.clk_i),
147-
.src_rst_ni (src_rst_n),
148-
.src_valid_i (dut_in.valid),
149-
.src_ready_o (dut_in.ready),
150-
.src_data_i (dut_in.data),
151-
.dst_clk_i (dut_out.clk_i),
152-
.dst_rst_ni (dst_rst_n),
153-
.dst_valid_o (dut_out.valid),
154-
.dst_ready_i (dut_out.ready),
155-
.dst_data_o (dut_out.data)
156-
);
157-
154+
if (DUT == "spill_register") begin
155+
isochronous_spill_register #(
156+
.T (payload_t)
157+
) i_isochronous_spill_register (
158+
.src_clk_i (dut_in.clk_i),
159+
.src_rst_ni (src_rst_n),
160+
.src_valid_i (dut_in.valid),
161+
.src_ready_o (dut_in.ready),
162+
.src_data_i (dut_in.data),
163+
.dst_clk_i (dut_out.clk_i),
164+
.dst_rst_ni (dst_rst_n),
165+
.dst_valid_o (dut_out.valid),
166+
.dst_ready_i (dut_out.ready),
167+
.dst_data_o (dut_out.data)
168+
);
169+
end if (DUT == "4phase_handshake") begin
170+
isochronous_4phase_handshake
171+
isochronous_4phase_handshake (
172+
.src_clk_i (dut_in.clk_i),
173+
.src_rst_ni (src_rst_n),
174+
.src_valid_i (dut_in.valid),
175+
.src_ready_o (dut_in.ready),
176+
.dst_clk_i (dut_out.clk_i),
177+
.dst_rst_ni (dst_rst_n),
178+
.dst_valid_o (dut_out.valid),
179+
.dst_ready_i (dut_out.ready)
180+
);
181+
182+
always_ff @(posedge dut_in.clk_i)
183+
if (dut_in.valid & dut_in.ready)
184+
dut_out.data <= dut_in.data;
185+
end
158186
endmodule

test/simulate.sh

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ set -e
1616

1717
bender script vsim -t test > compile.tcl
1818

19-
"$VSIM" -c -do 'source compile.tcl; quit'
19+
"$VSIM" -c -quiet -do 'source compile.tcl; quit'
2020

2121
call_vsim() {
2222
echo "run -all" | "$VSIM" "$@" | tee vsim.log 2>&1
@@ -29,7 +29,7 @@ for tb in cdc_2phase_tb fifo_tb graycode_tb id_queue_tb popcount_tb stream_regis
2929
done
3030

3131
for depth in 0 1 2; do
32-
call_vsim stream_to_mem_tb -GBufDepth=$depth -coverage -voptargs="+acc +cover=bcesfx"
32+
call_vsim stream_to_mem_tb -gBufDepth=$depth -coverage -voptargs="+acc +cover=bcesfx"
3333
done
3434

3535
for num in 1 4 7; do
@@ -51,3 +51,10 @@ for radix in 2 4 8; do
5151
done
5252
done
5353
done
54+
55+
for dut in "spill_register" "4phase_handshake"; do
56+
for clk in 1,1 1,2 2,1 1,4 5,1 3,6 8,4; do
57+
IFS=',' read src_clk dst_clk <<< "${clk}"
58+
call_vsim isochronous_crossing_tb -gDUT=$dut -gTCK_SRC_MULT=$src_clk -gTCK_DST_MULT=$dst_clk
59+
done
60+
done

0 commit comments

Comments
 (0)