-
Notifications
You must be signed in to change notification settings - Fork 14
/
StackExample.bsv
345 lines (274 loc) · 9.12 KB
/
StackExample.bsv
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
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
/*
* Copyright 2015 Matthew Naylor
* All rights reserved.
*
* This software was developed by SRI International and the University of
* Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237
* ("CTSRD"), as part of the DARPA CRASH research programme.
*
* This software was developed by SRI International and the University of
* Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-11-C-0249
* ("MRC2"), as part of the DARPA MRC research programme.
*
* This software was developed by the University of Cambridge Computer
* Laboratory as part of the Rigorous Engineering of Mainstream
* Systems (REMS) project, funded by EPSRC grant EP/K008528/1.
*
* @BERI_LICENSE_HEADER_START@
*
* Licensed to BERI Open Systems C.I.C. (BERI) under one or more contributor
* license agreements. See the NOTICE file distributed with this work for
* additional information regarding copyright ownership. BERI licenses this
* file to you under the BERI Hardware-Software License, Version 1.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
*
* http://www.beri-open-systems.org/legal/license-1-0.txt
*
* Unless required by applicable law or agreed to in writing, Work distributed
* under the 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.
*
* @BERI_LICENSE_HEADER_END@
*/
import Vector :: *;
import BRAMCore :: *;
import BlueCheck :: *;
import StmtFSM :: *;
import Clocks :: *;
///////////////
// Interface //
///////////////
interface Stack#(numeric type n, type a);
method Action push(a x);
method Action pop;
method a top;
method Bool isEmpty;
method Action clear;
method Bit#(n) size;
endinterface
//////////////////
// Specfication //
//////////////////
/* Make a stack with space for 2^n elements of type a */
module mkStackSpec (Stack#(n, a))
provisos(Bits#(a, b), Add#(1, m, TExp#(n)));
Reg#(Vector#(TExp#(n), a)) stk <- mkReg(newVector());
Reg#(Bit#(n)) num <- mkReg(0);
method Action push(a x);
num <= num+1;
stk <= cons(x, init(stk));
endmethod
method Action pop if (num > 0);
num <= num-1;
stk <= append(tail(stk), cons(?, nil));
endmethod
method a top if (num > 0);
return head(stk);
endmethod
method Bool isEmpty;
return (num==0);
endmethod
method Action clear;
num <= 0;
endmethod
method Bit#(n) size = num;
endmodule
////////////////////
// Implementation //
////////////////////
/*
A block RAM based stack implementation.
We want push and pop to be single-cycle operations. In the case of
pop, we cannot get the value out of memory until the next cycle so
must fetch it speculatively:
* when popping, we speculatively read the next item from block RAM
so that it's ready on the data bus for the next pop;
* when pushing, we request the value we are writing (using the
WRITE_FIRST semantics of block RAMs) so that it's also ready
on the data bus for the next pop.
We assume the block RAMs have a WRITE_FIRST semantics even though this
is not stated in the documentation! (I can see it in the Verilog
though.)
Simultaneous pushing and popping (with a pop before push semantics) is
useful but not supported by this module. Could be easily done using
DWires though.
*/
/* A stack with a capacity of 2^n elements of type a */
module mkBRAMStack (Stack#(n, a))
provisos(Bits#(a, b));
/* Create the block RAM */
BRAM_PORT#(Bit#(n), a) ram <- mkBRAMCore1(2**valueOf(n), False);
/* Create the stack pointer */
Reg#(Bit#(n)) sp <- mkReg(0);
/* The top stack element is stored in a register */
Reg#(a) topReg <- mkRegU;
method Action push(a x);
/* Update top of stack */
topReg <= x;
/* Push the old top of stack to block RAM and speculate next pop */
ram.put(True, sp, topReg);
/* Increment stack pointer */
sp <= sp+1;
endmethod
method Action pop if (sp > 0);
/* Update top of stack */
topReg <= ram.read;
/* Speculate that another pop is coming soon */
ram.put(False, sp-1, ?); // INCORRECT
//ram.put(False, sp-2, ?); // CORRECT
/* Decrement stack pointer */
sp <= sp-1;
endmethod
method a top if (sp > 0);
return topReg;
endmethod
method Bool isEmpty;
return (sp == 0);
endmethod
method Action clear;
sp <= 0;
endmethod
method Bit#(n) size = sp;
endmodule
/////////////////////////
// Equivalence testing //
/////////////////////////
module [BlueCheck] checkStack ();
/* Specification instance */
Stack#(8, Bit#(4)) spec <- mkStackSpec();
/* Implmentation instance */
Stack#(8, Bit#(4)) imp <- mkBRAMStack();
equiv("pop" , spec.pop , imp.pop);
equiv("push" , spec.push , imp.push);
equiv("isEmpty", spec.isEmpty, imp.isEmpty);
equiv("top" , spec.top , imp.top);
endmodule
// Reset version, for iterative deepening
module [BlueCheck] checkStackWithReset#(Reset r) ();
/* Specification instance */
Stack#(8, Bit#(4)) spec <- mkStackSpec(reset_by r);
/* Implmentation instance */
Stack#(8, Bit#(4)) imp <- mkBRAMStack(reset_by r);
equiv("clear" , spec.clear , imp.clear);
equiv("pop" , spec.pop , imp.pop);
equiv("push" , spec.push , imp.push);
equiv("isEmpty", spec.isEmpty, imp.isEmpty);
equiv("top" , spec.top , imp.top);
endmodule
// Similar to above, but with classification
module [BlueCheck] checkStackWithResetAndClassify#(Reset r) ();
/* Specification instance */
Stack#(8, Bit#(4)) spec <- mkStackSpec(reset_by r);
/* Implmentation instance */
Stack#(8, Bit#(4)) imp <- mkBRAMStack(reset_by r);
/* Test data classifier */
Classifier classifySmall <- mkClassifier("small");
/* Monitor max stack size */
Reg#(Bit#(8)) maxSize <- mkReg(0);
PulseWire resetMaxSize <- mkPulseWire;
rule updateMaxSize;
if (resetMaxSize) maxSize <= 0;
else maxSize <= max(maxSize, spec.size);
endrule
pre("", resetMaxSize.send);
equiv("pop" , spec.pop , imp.pop);
equiv("push" , spec.push , imp.push);
equiv("isEmpty", spec.isEmpty, imp.isEmpty);
equiv("top" , spec.top , imp.top);
equiv("clear" , spec.clear , imp.clear);
post("", classifySmall(maxSize <= 3));
endmodule
module [Module] testStack ();
blueCheck(checkStack);
endmodule
// Synthesisable version
module [Module] testStackSynth (JtagUart);
let uart <- blueCheckSynth(checkStack);
return uart;
endmodule
// Iterative deepening version
module [Module] testStackID ();
Clock clk <- exposeCurrentClock;
MakeResetIfc r <- mkReset(0, True, clk);
blueCheckID(checkStackWithReset(r.new_rst), r);
endmodule
// Iterative deepening version (with extra options)
module [Module] testStackIDCustom ();
Clock clk <- exposeCurrentClock;
MakeResetIfc r <- mkReset(0, True, clk);
// Customise default BlueCheck parameters
BlueCheck_Params params = bcParamsID(r);
params.wedgeDetect = True;
params.id.initialDepth = 3;
function incr(x) = x+1;
params.id.incDepth = incr;
params.numIterations = 25;
params.id.testsPerDepth = 100;
// Generate checker
Stmt s <- mkModelChecker(checkStackWithReset(r.new_rst), params);
mkAutoFSM(s);
endmodule
// Iterative deepening version & classification
module [Module] testStackIDClassify ();
Clock clk <- exposeCurrentClock;
MakeResetIfc r <- mkReset(0, True, clk);
blueCheckID(checkStackWithResetAndClassify(r.new_rst), r);
endmodule
// Synthesisable & iterative deepening version
module [Module] testStackIDSynth (JtagUart);
Clock clk <- exposeCurrentClock;
MakeResetIfc r <- mkReset(0, True, clk);
let uart <- blueCheckIDSynth(checkStackWithReset(r.new_rst), r);
return uart;
endmodule
///////////////////////
// Algebraic testing //
///////////////////////
/* A block RAM based stack: test against algebraic specification. */
module [BlueCheck] checkStackAlgWithReset#(Reset r) ();
/* Instances */
Stack#(8, Bit#(8)) s1 <- mkBRAMStack(reset_by r);
Stack#(8, Bit#(8)) s2 <- mkBRAMStack(reset_by r);
/* This function allows us to make assertions in the properties */
Ensure ensure <- getEnsure;
Stmt prop1 =
seq
s1.clear; s2.clear;
ensure(s1.isEmpty);
endseq;
function Stmt prop2(Bit#(8) x) =
seq
s1.push(x); s2.push(x);
ensure(!s1.isEmpty);
endseq;
function Stmt prop3(Bit#(8) x) =
seq
s1.push(x);
s1.pop;
endseq;
function Stmt prop4(Bit#(8) x) =
seq
s1.push(x); s2.push(x);
ensure(s1.top == x);
endseq;
/* Equivalences */
equiv("pop", s1.pop, s2.pop);
equiv("push", s1.push, s2.push);
equiv("isEmpty", s1.isEmpty, s2.isEmpty);
equiv("top", s1.top, s2.top);
/* Properties */
prop("prop1", prop1);
prop("prop2", prop2);
prop("prop3", prop3);
prop("prop4", prop4);
endmodule
module [Module] testStackAlgID ();
Clock clk <- exposeCurrentClock;
MakeResetIfc r <- mkReset(0, True, clk);
blueCheckID(checkStackAlgWithReset(r.new_rst), r);
endmodule
module [Module] testStackAlg ();
blueCheck(checkStackAlgWithReset(noReset));
endmodule