-
Notifications
You must be signed in to change notification settings - Fork 195
Expand file tree
/
Copy pathisochronous_spill_register.sv
More file actions
106 lines (100 loc) · 4.56 KB
/
isochronous_spill_register.sv
File metadata and controls
106 lines (100 loc) · 4.56 KB
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
// Copyright 2020 ETH Zurich and University of Bologna.
//
// Copyright and related rights are licensed under the Solderpad Hardware
// License, Version 0.51 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
// or agreed to in writing, software, hardware and materials distributed under
// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
//
// Florian Zaruba <zarubaf@iis.ee.ethz.ch>
`include "common_cells/registers.svh"
`include "common_cells/assertions.svh"
/// A register with handshakes that completely cuts any combinatorial paths
/// between the input and output in isochronous clock domains.
///
/// > Definition of isochronous: In telecommunication, an isochronous signal is a signal
/// > in which the time interval separating any two significant instants is equal to the
/// > unit interval or a multiple of the unit interval.
///
/// The source and destination clock domains must be derived from the same clock
/// but can vary in frequency by a constant factor (e.g., double the frequency).
///
/// The module is basically a two deep dual-clock fifo with read and write pointers
/// in different clock domains. As we know the static timing relationship between the
/// clock domains we can rely on static timing analysis (STA) to get the sampling windows
/// right and therefore don't need any synchronization.
///
/// # Restrictions
///
/// Source and destination clock domains must be an integer multiple of each other and
/// all timing-paths need to be covered by STA. For example a recommended SDC would be:
///
/// `create_generated_clock dst_clk_i -name dst_clk -source src_clk_i -divide_by 2
///
/// There are _no_ restrictions on which clock domain should be the faster, any integer
/// ratio will work.
module isochronous_spill_register #(
/// Data type of spill register.
parameter type T = logic,
/// Make this spill register transparent.
parameter bit Bypass = 1'b0
) (
/// Clock of source clock domain.
input logic src_clk_i,
/// Active low async reset in source domain.
input logic src_rst_ni,
/// Source input data is valid.
input logic src_valid_i,
/// Source is ready to accept.
output logic src_ready_o,
/// Source input data.
input T src_data_i,
/// Clock of destination clock domain.
input logic dst_clk_i,
/// Active low async reset in destination domain.
input logic dst_rst_ni,
/// Destination output data is valid.
output logic dst_valid_o,
/// Destination is ready to accept.
input logic dst_ready_i,
/// Destination output data.
output T dst_data_o
);
// Don't generate the spill register.
if (Bypass) begin : gen_bypass
assign dst_valid_o = src_valid_i;
assign src_ready_o = dst_ready_i;
assign dst_data_o = src_data_i;
// Generate the spill register
end else begin : gen_isochronous_spill_register
/// Read/write pointer are one bit wider than necessary.
/// We implicitly capture the full and empty state with the second bit:
/// If all but the topmost bit of `rd_pointer_q` and `wr_pointer_q` agree, the
/// FIFO is in a critical state. If the topmost bit is equal, the FIFO is
/// empty, otherwise it is full.
logic [1:0] rd_pointer_q, wr_pointer_q;
// Advance write pointer if we pushed a new item into the FIFO. (Source clock domain)
`FFLARN(wr_pointer_q, wr_pointer_q+1, (src_valid_i && src_ready_o), '0, src_clk_i, src_rst_ni)
// Advance read pointer if downstream consumed an item. (Destination clock domain)
`FFLARN(rd_pointer_q, rd_pointer_q+1, (dst_valid_o && dst_ready_i), '0, dst_clk_i, dst_rst_ni)
T [1:0] mem_d, mem_q;
`FFL(mem_q, mem_d, (src_valid_i && src_ready_o), '0, src_clk_i, src_rst_ni)
always_comb begin
mem_d = mem_q;
mem_d[wr_pointer_q[0]] = src_data_i;
end
assign src_ready_o = (rd_pointer_q ^ wr_pointer_q) != 2'b10;
assign dst_valid_o = (rd_pointer_q ^ wr_pointer_q) != '0;
assign dst_data_o = mem_q[rd_pointer_q[0]];
end
// stability guarantees
`ifndef COMMON_CELLS_ASSERTS_OFF
`ASSERT(src_valid_unstable, src_valid_i && !src_ready_o |=> $stable(src_valid_i),
src_clk_i, !src_rst_ni, "src_valid_i is unstable")
`ASSERT(dst_valid_unstable, dst_valid_o && !dst_ready_i |=> $stable(dst_valid_o),
dst_clk_i, !dst_rst_ni, "dst_valid_o is unstable")
`endif
endmodule