forked from bespoke-silicon-group/basejump_stl
-
Notifications
You must be signed in to change notification settings - Fork 0
/
bsg_nonsynth_dpi_to_fifo.sv
274 lines (231 loc) · 9.76 KB
/
bsg_nonsynth_dpi_to_fifo.sv
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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
// bsg_nonsynth_dpi_to_fifo: A FIFO Interface for transmitting FIFO
// data from C/C++ into simulation via DPI
//
// Parameters:
//
// width_p: is the bit-width of the FIFO interface. Must be a power
// of 2 and divisible by 8, i.e. a ctype.
//
// debug_p: is the intial value to set on debug_o and to control
// debug messages. The bsg_dpi_debug() DPI function can be used to
// control messages at runtime, but this allows it to be set in
// the initial block, before any runtime functions can be called.
//
// Functions:
//
// init(): Initialize this FIFO DPI Interface. Init must be called
// before any other function
//
// bsg_dpi_debug(input bit): Enable or disable BSG DBGINFO messages
// from this module and set the debug_o output bit.
//
// width(): Return the bit-width of data_i
//
// fini(): De-Initialize this FIFO DPI Interface. This must be
// called before $finish is called in the testbench.
//
// bsg_dpi_fifo_tx(input bit [width_p-1:0] data_i): Write data_i to the FIFO
// interface and set v_o. If the consumer is ready (ready_i === 1)
// this function will return 1 to indicate that the consumer
// accepted the data. Ifthe consumer is not ready (ready_i === 0)
// this function will return 0 to indicate that the consumer did
// not accept the data.
//
// If the data is not accepted by the consumer FIFO, the host
// C/C++ program MUST call this method again on the next cycle.
//
// If the data is not accepted by the consumer FIFO, the host
// C/C++ program MUST call this this method with the same
// arguments (i.e. data_i should remain constant across calls).
//
// bsg_dpi_fifo_tx() CAN ONLY be called after the positive edge of clk_i is
// evaluated.
//
// bsg_dpi_fifo_tx() CAN ONLY be called only once per clk_i cycle.
//
// Violating any of these constraints this will cause $fatal to be
// called to indicate a protocol violation.
//
// For safe operation of this interface use the bsg_nonsynth_fifo_to_dpi
// class provided in bsg_nonsynth_fifo.hpp header.
`include "bsg_defines.sv"
module bsg_nonsynth_dpi_to_fifo
#(
parameter `BSG_INV_PARAM(width_p )
,parameter bit debug_p = 0
)
(
input clk_i
, input reset_i
, output logic v_o
, output logic [width_p-1:0] data_o
, input ready_i
, output bit debug_o);
// This bit tracks whether initialize has been called. If data is
// sent and recieved before init() is called, then this module will
// call $fatal
bit init_r = 0;
// This bit checks whether bsg_dpi_fifo_tx() has been called multiple times in a
// cycle.
bit tx_r = 0;
// Check if width_p is a ctype width. call $fatal, if not.
if (!(width_p == 32'd8 || width_p == 32'd16 || width_p == 32'd32 || width_p == 32'd64 || width_p == 32'd128)) begin
$fatal(1, "BSG ERROR (%M): width_p of %d is not supported. Must be a power of 2 and divisible by 8", width_p);
end
// Print module parameters to the console and set the intial debug
// value
initial begin
debug_o = debug_p;
$display("BSG INFO: bsg_nonsynth_dpi_to_fifo (initial begin)");
$display("BSG INFO: Instantiation: %M");
$display("BSG INFO: width_p = %d", width_p);
$display("BSG INFO: debug_p = %d", debug_p);
end
// This checks that fini was called before $finish
final begin
if (init_r === 1)
$fatal(1, "BSG ERROR (%M): fini() was not called before $finish");
end
export "DPI-C" function bsg_dpi_init;
export "DPI-C" function bsg_dpi_fini;
export "DPI-C" function bsg_dpi_debug;
export "DPI-C" function bsg_dpi_width;
export "DPI-C" function bsg_dpi_fifo_tx;
export "DPI-C" function bsg_dpi_fifo_is_window;
// Set or unset the debug_o output bit. If a state change occurs
// (0->1 or 1->0) then module will print DEBUG ENABLED / DEBUG
// DISABLED. No messages are printed if a state change does not
// occur.
function void bsg_dpi_debug(input bit switch_i);
if(!debug_o & switch_i)
$display("BSG DBGINFO (%M@%t): DEBUG ENABLED", $time);
else if (debug_o & !switch_i)
$display("BSG DBGINFO (%M@%t): DEBUG DISABLED", $time);
debug_o = switch_i;
endfunction
// Silly, but useful.
function int bsg_dpi_width();
return width_p;
endfunction
// Initialize this FIFO DPI Interface
function void bsg_dpi_init();
if(debug_o)
$display("BSG DBGINFO (%M@%t): bsg_dpi_init() called", $time);
if(init_r)
$fatal(1, "BSG ERROR (%M): bsg_dpi_init() already called");
init_r = 1;
endfunction
// Terminate this FIFO DPI Interface
function void bsg_dpi_fini();
if(debug_o)
$display("BSG DBGINFO (%M@%t): bsg_dpi_fini() called", $time);
if(~init_r)
$fatal(1, "BSG ERROR (%M): bsg_dpi_fini() already called");
init_r = 0;
endfunction
// bsg_dpi_fifo_tx(input logic [width_p-1:0] data_i) -- Provide data_i
// to the FIFO interface on data_o and set v_o. When the consumer
// is ready (ready_i == 1) this function will return 1 to indicate
// that the consumer accepted the data. When the consumer is not
// ready this function will return 0 to indicate that the consumer
// did not accept the data.
//
// If the data is not consumed, the host C/C++ program MUST call
// this method again on the next cycle with the same arguments
// (i.e. data_i should remain constant across calls). Not doing
// this will cause $fatal to be called to indicate a protocol
// violation because v_o will return to 0 in the next cycle.
//
// bsg_dpi_fifo_tx() MUST be called after the positive edge of clk_i is
// evaluated. It MUST be called only once per cycle. Failure will
// cause an error and a call to $fatal.
// We set v_o_n so that we can signal a read to the producer on the
// NEXT positive edge. v_o_n flows to v_o on the negative edge of
// clk_i
logic v_o_n;
// Same as above, but with data_o.
logic [width_p-1:0] data_o_n;
// We track the "last" data_o and last ready_i values to detect
// protocol violations. These are captured on the positive edge of
// the clock
reg [width_p-1:0] data_o_r;
reg ready_i_r;
// We track the polarity of the current edge so that we can call
// $fatal when $rx is called during the wrong phase of clk_i.
reg edgepol;
always @(posedge clk_i or negedge clk_i) begin
edgepol <= clk_i;
end
function bit bsg_dpi_fifo_tx(input bit [width_p-1:0] data_bi);
if(init_r === 0) begin
$fatal(1, "BSG ERROR (%M): bsg_dpi_fifo_tx() called before init()");
end
if(reset_i === 1) begin
$fatal(1, "BSG ERROR (%M): bsg_dpi_fifo_tx() called while reset_i === 1");
end
if(tx_r !== 0) begin
$fatal(1, "BSG ERROR (%M): bsg_dpi_fifo_tx() called multiple times in a clk_i cycle");
end
if(clk_i === 0) begin
$fatal(1, "BSG ERROR (%M): bsg_dpi_fifo_tx() must be called when clk_i == 1");
end
if(edgepol === 0) begin
$fatal(1, "BSG ERROR (%M): bsg_dpi_fifo_tx() must be called after the positive edge of clk_i has been evaluated");
end
if((ready_i_r === 0 & v_o === 1) & !(data_bi === data_o_r)) begin
$fatal(1, "BSG ERROR (%M): bsg_dpi_fifo_tx() argument data_bi must be constant across calls/cycles when the consumer is not ready");
end
// These will flow to their respective outputs on the next
// negative clock edge.
v_o_n = '1;
data_o_n = data_bi;
tx_r = 1;
if(debug_o)
$display("BSG DBGINFO (%M@%t): bsg_dpi_fifo_tx() called -- ready_i: %b, data_o_n: 0x%x",
$time, ready_i, data_bi);
return (ready_i === 1);
endfunction
// The function bsg_dpi_fifo_is_window returns true if the interface is
// in a valid time-window to call bsg_dpi_fifo_tx()
function bit bsg_dpi_fifo_is_window();
return (~tx_r & clk_i & edgepol & ~reset_i);
endfunction
// We set v_o and data_o on a negative clock edge so that it is
// seen on the next positive edge. v_o_n and data_o_n hold the "next"
// values for v_o and data_o.
//
// We proactively clear v_o by setting v_o_n to 0 in case ready_i ==
// 1 on the positive edge. If ready_i == 1, and we don't set v_o_n
// here, then the user will have to call tx again to clear v_o even
// though they aren't sending data. If ready_i === 0 then the user
// MUST call tx again and v_o will be set to 1 again, otherwise
// $fatal will be called because dropping v_o without ready_i === 1
// is a protocol violation.
//
// If the user wants to send NEW data after ready_i === 1, they
// will call bsg_dpi_fifo_tx() and v_o will be set to 1 again.
always @(negedge clk_i) begin
// If the user fails to call bsg_dpi_fifo_tx() AGAIN (v_o_n === 0) after a
// data beat was not accepted (v_o == 1 && ready_i == 0) that is
// a protocol error.
if(v_o_n === 0 & (v_o === 1 & ready_i_r === 0)) begin
$fatal(1, "BSG ERROR: bsg_dpi_fifo_tx() was not called again on the cycle after the consumer was not ready");
end
data_o <= data_o_n;
v_o <= v_o_n;
v_o_n = 0;
end
// Save the last ready_i and data_o values for protocol checking
// and reset tx_r to 0 to.
always @(posedge clk_i) begin
ready_i_r <= ready_i;
data_o_r <= data_o;
// reset v_o to 0 so that negedge clk assertions aren't triggered.
v_o <= v_o_n;
tx_r = 0;
if(debug_o)
$display("BSG DBGINFO (%M@%t): posedge clk_i -- reset_i: %b v_o: %b ready_i: %b data_i: 0x%x",
$time, reset_i, v_o, ready_i, data_o);
end
endmodule // bsg_nonsynth_dpi_to_fifo
`BSG_ABSTRACT_MODULE(bsg_nonsynth_dpi_to_fifo)