forked from SJTU-YONGFU-RESEARCH-GRP/core
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathskid_buffer.v
More file actions
executable file
·135 lines (125 loc) · 5.1 KB
/
skid_buffer.v
File metadata and controls
executable file
·135 lines (125 loc) · 5.1 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
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
/**
* Skid Buffer
*
* This module implements a skid buffer (also known as a ready/valid buffer)
* that provides a one-entry buffer between a producer and consumer using
* ready/valid handshaking. It improves throughput by allowing the producer
* to send data even when the consumer is temporarily not ready.
*
* Key Features:
* - One-entry buffer: Stores one data item
* - Ready/valid protocol: AXI-style handshaking
* - Skid register: Prevents data loss during handshake
* - Zero-cycle latency: Data passes through when consumer is ready
* - Backpressure support: Properly handles consumer backpressure
*
* Skid Buffer Operation:
* - Main register: Holds data currently being consumed
* - Skid register: Holds overflow data when consumer not ready
* - When consumer ready: Data moves from main to consumer
* - When producer sends: Data goes to main (if empty) or skid (if main full)
*
* Handshake Protocol:
* - Producer: Asserts s_valid with s_data when data available
* - Consumer: Asserts m_ready when ready to receive
* - Transfer: Occurs when both s_valid and s_ready are high
*
* Use Cases:
* - Pipeline stages with variable latency
* - Interfaces between different clock domains (with CDC)
* - Flow control in data paths
* - Breaking combinational paths
*
* @param DATA_WIDTH Width of data (default: 8 bits)
*
* Upstream Interface (Producer):
* @input clk Clock signal
* @input rst_n Active-low reset signal
* @input s_valid Source data valid
* @output s_ready Source ready (can accept data)
* @input s_data[DATA_WIDTH-1:0] Source data
*
* Downstream Interface (Consumer):
* @output m_valid Master data valid
* @input m_ready Master ready (can consume data)
* @output m_data[DATA_WIDTH-1:0] Master data
*/
module skid_buffer #(
parameter DATA_WIDTH = 8
) (
input wire clk,
input wire rst_n,
// Upstream interface
input wire s_valid,
output wire s_ready,
input wire [DATA_WIDTH-1:0] s_data,
// Downstream interface
output wire m_valid,
input wire m_ready,
output wire [DATA_WIDTH-1:0] m_data
);
// Internal registers for skid buffer functionality
reg [DATA_WIDTH-1:0] data_reg; // Main data register
reg [DATA_WIDTH-1:0] skid_reg; // Skid register for overflow
reg valid_reg; // Valid flag for main register
reg skid_valid_reg; // Valid flag for skid register
// Control logic
wire push = s_valid && s_ready;
wire pop = m_valid && m_ready;
// ============================================================================
// Ready/Valid Signal Generation
// ============================================================================
// Upstream ready: We can accept new data if:
// 1. Skid register is empty (has space for overflow), OR
// 2. We're popping data (making room in main register)
// This ensures we never lose data from the producer
assign s_ready = !skid_valid_reg || pop;
// ============================================================================
// Downstream Interface
// ============================================================================
// Downstream valid: We have valid data if main register is valid
// This indicates data is ready for the consumer
assign m_valid = valid_reg;
// Downstream data: Output from main register
assign m_data = data_reg;
// Main register update logic
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
valid_reg <= 1'b0;
data_reg <= {DATA_WIDTH{1'b0}};
end else if (pop) begin
// If we're popping data out, check if we have skid data to move in
if (skid_valid_reg) begin
data_reg <= skid_reg;
valid_reg <= 1'b1;
end else if (push) begin
// No skid data, but new data is coming in
data_reg <= s_data;
valid_reg <= 1'b1;
end else begin
// No skid data and no new data, invalidate the register
valid_reg <= 1'b0;
end
end else if (push && !valid_reg) begin
// If not popping but pushing and the main register is empty
data_reg <= s_data;
valid_reg <= 1'b1;
end
end
// Skid register update logic
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
skid_valid_reg <= 1'b0;
skid_reg <= {DATA_WIDTH{1'b0}};
end else if (push && valid_reg && !pop) begin
// If we're pushing but not popping and the main register is full,
// store the incoming data in the skid register
skid_reg <= s_data;
skid_valid_reg <= 1'b1;
end else if (pop && skid_valid_reg) begin
// If we're popping and have skid data, the skid data moves to the main register,
// so the skid register becomes empty
skid_valid_reg <= 1'b0;
end
end
endmodule