OpenCores
URL https://opencores.org/ocsvn/zipcpu/zipcpu/trunk

Subversion Repositories zipcpu

[/] [zipcpu/] [trunk/] [sim/] [verilator/] [mpy_tb.cpp] - Blame information for rev 204

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 204 dgisselq
///////////////////////////////////////////////////////////////////////////////
2
//
3
// Filename:    mpy_tb.cpp
4
//
5
// Project:     Zip CPU -- a small, lightweight, RISC CPU soft core
6
//
7
// Purpose:     Bench testing for the multiply ALU instructions used within the
8
//              Zip CPU.  This depends upon the cpuops.v module, but should be
9
//      independent of the internal settings within the module.
10
//
11
//
12
// Creator:     Dan Gisselquist, Ph.D.
13
//              Gisselquist Technology, LLC
14
//
15
///////////////////////////////////////////////////////////////////////////////
16
//
17
// Copyright (C) 2015-2016, Gisselquist Technology, LLC
18
//
19
// This program is free software (firmware): you can redistribute it and/or
20
// modify it under the terms of  the GNU General Public License as published
21
// by the Free Software Foundation, either version 3 of the License, or (at
22
// your option) any later version.
23
//
24
// This program is distributed in the hope that it will be useful, but WITHOUT
25
// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or
26
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
27
// for more details.
28
//
29
// License:     GPL, v3, as defined and found on www.gnu.org,
30
//              http://www.gnu.org/licenses/gpl.html
31
//
32
//
33
///////////////////////////////////////////////////////////////////////////////
34
//
35
//
36
#include <signal.h>
37
#include <time.h>
38
#include <unistd.h>
39
#include <assert.h>
40
 
41
#include <stdlib.h>
42
#include <ctype.h>
43
 
44
#include "verilated.h"
45
#include "Vcpuops.h"
46
 
47
#include "testb.h"
48
#include "cpudefs.h"
49
// #include "twoc.h"
50
 
51
class   CPUOPS_TB : public TESTB<Vcpuops> {
52
public:
53
        // Nothing special to do in a startup.
54
        CPUOPS_TB(void) {}
55
 
56
        // ~CPUOPS_TB(void) {}
57
 
58
        //
59
        // Calls TESTB<>::reset to reset the core.  Makes sure the i_ce line
60
        // is low during this reset.
61
        //
62
        void    reset(void) {
63
                // m_flash.debug(false);
64
                m_core->i_ce = 0;
65
 
66
                TESTB<Vcpuops>::reset();
67
        }
68
 
69
        //
70
        // dbgdump();
71
        //
72
        // Just before the positive edge of every clock, we call this function
73
        // (if the debug flag is set).  This prints out a line of information
74
        // telling us what is going on within the logic, allowing us access
75
        // for debugging purposes to inspect things.
76
        //
77
        // Other than debugging, this isn't necessary for the functioning of the
78
        // test bench.  At the same time, what are you using a test bench for if
79
        // not for debugging?
80
        //
81
        void    dbgdump(void) {
82
                char    outstr[2048], *s;
83
                sprintf(outstr, "Tick %4ld %s%s ",
84
                        m_tickcount,
85
                        (m_core->i_rst)?"R":" ",
86
                        (m_core->i_ce)?"CE":"  ");
87
                switch(m_core->i_op) {
88
                case  0: strcat(outstr, "   SUB"); break;
89
                case  1: strcat(outstr, "   AND"); break;
90
                case  2: strcat(outstr, "   ADD"); break;
91
                case  3: strcat(outstr, "    OR"); break;
92
                case  4: strcat(outstr, "   XOR"); break;
93
                case  5: strcat(outstr, "   LSR"); break;
94
                case  6: strcat(outstr, "   LSL"); break;
95
                case  7: strcat(outstr, "   ASR"); break;
96
                case  8: strcat(outstr, "   MPY"); break;
97
                case  9: strcat(outstr, "LODILO"); break;
98
                case 10: strcat(outstr, "MPYUHI"); break;
99
                case 11: strcat(outstr, "MPYSHI"); break;
100
                case 12: strcat(outstr, "  BREV"); break;
101
                case 13: strcat(outstr, "  POPC"); break;
102
                case 14: strcat(outstr, "   ROL"); break;
103
                case 15: strcat(outstr, "   MOV"); break;
104
                default: strcat(outstr, "UNKWN!"); break;
105
                } s = &outstr[strlen(outstr)];
106
                sprintf(s, "(%x) 0x%08x 0x%08x -> 0x%08x [%x] %s%s",
107
                        m_core->i_op,
108
                        m_core->i_a, m_core->i_b,
109
                        m_core->o_c, m_core->o_f,
110
                        (m_core->o_valid)?"V":" ",
111
                        (m_core->o_busy)?"B":" ");
112
                s = &outstr[strlen(outstr)];
113
 
114
#if(OPT_MULTIPLY==1)
115
                sprintf(s, "1,MPY[][][%016lx]",
116
                        m_core->v__DOT__mpy_result);
117
                s = &outstr[strlen(outstr)];
118
#elif(OPT_MULTIPLY==2)
119
                sprintf(s, "2,MPY[%016lx][%016lx][%016lx]",
120
                        m_core->v__DOT__genblk2__DOT__genblk2__DOT__genblk1__DOT__r_mpy_a_input,
121
                        m_core->v__DOT__genblk2__DOT__genblk2__DOT__genblk1__DOT__r_mpy_b_input,
122
                        m_core->v__DOT__mpy_result);
123
                s = &outstr[strlen(outstr)];
124
#elif(OPT_MULTIPLY==3)
125
                sprintf(s, "3,MPY[%08x][%08x][%016lx], P[%d]",
126
                        m_core->v__DOT__genblk2__DOT__genblk2__DOT__genblk2__DOT__genblk1__DOT__r_mpy_a_input,
127
                        m_core->v__DOT__genblk2__DOT__genblk2__DOT__genblk2__DOT__genblk1__DOT__r_mpy_b_input,
128
                        m_core->v__DOT__genblk2__DOT__genblk2__DOT__genblk2__DOT__genblk1__DOT__r_smpy_result,
129
                        m_core->v__DOT__genblk2__DOT__genblk2__DOT__genblk2__DOT__genblk1__DOT__mpypipe);
130
 
131
#endif
132
 
133
#if(OPT_MULTIPLY != 1)
134
                if (m_core->v__DOT__this_is_a_multiply_op)
135
                        strcat(s, " MPY-OP");
136
#endif
137
                puts(outstr);
138
        }
139
 
140
        //
141
        // tick()
142
        //
143
        // Call this to step the processor.
144
        //
145
        // This is a bit unusual compared to other tick() functions I have in
146
        // my simulators in that there are a lot of calls to eval() with clk==0.
147
        // This is because the multiply logic for OPT_MULTIPLY < 3 depends upon
148
        // it to be valid.  I assume any true Xilinx, or even higher level,
149
        // implementation wouldn't have this problem.
150
        //
151
        void    tick(void) {
152
                bool    debug = false;
153
 
154
                // Insist that we are never both busy and producing a valid
155
                // result at the same time.  One or the other may be true,
156
                // but never both.
157
                assert((!m_core->o_busy)||(!m_core->o_valid));
158
                //
159
                TESTB<Vcpuops>::tick();
160
 
161
                if (debug)
162
                        dbgdump();
163
        }
164
 
165
        //
166
        // clear_ops
167
        //
168
        // Runs enough clocks through the device until it is neither busy nor
169
        // valid.  At this point, the ALU should be thoroughly clear.  Then
170
        // we tick things once more.
171
        //
172
        void    clear_ops(void) {
173
                m_core->i_ce    = 0;
174
                m_core->i_op    = 0;
175
 
176
                do {
177
                        tick();
178
                } while((m_core->o_busy)||(m_core->o_valid));
179
                tick();
180
        }
181
 
182
        //
183
        // This is a fairly generic CPU operation call.  What makes it less
184
        // than generic are two things: 1) the ALU is cleared before any
185
        // new instruction, and 2) the tick count at the end is compared
186
        // against the tick count OPT_MULTIPLY says we should be getting.
187
        // A third difference between this call in simulation and a real
188
        // call within the CPU is that we never set the reset mid-call, whereas
189
        // the CPU may need to do that if a jump is made and the pipeline needs
190
        // to be cleared.
191
        //
192
        unsigned        op(int op, int a, int b) {
193
                // Make sure we start witht he core idle
194
                if (m_core->o_valid)
195
                        clear_ops();
196
 
197
                // Set the arguments to the CPUOPS core to get a multiple
198
                // started
199
                m_core->i_ce    = 1;
200
                m_core->i_op    = op;
201
                m_core->i_a     = a;
202
                m_core->i_b     = b;
203
 
204
                unsigned long now = m_tickcount;
205
 
206
                // Tick once to get it going
207
                tick();
208
 
209
                // Clear the input arguments to the multiply
210
                m_core->i_ce    = 0;
211
                m_core->i_a     = 0;
212
                m_core->i_b     = 0;
213
 
214
                // Wait for the result to be valid
215
                while(!m_core->o_valid)
216
                        tick();
217
 
218
                // Check that we used the number of clock ticks we said we'd
219
                // be using.  OPT_MULTIPLY is *supposed* to be equal to this
220
                // number.
221
                if((m_tickcount - now)!=OPT_MULTIPLY) {
222
                        printf("%ld ticks seen, %d ticks expected\n",
223
                                m_tickcount-now, OPT_MULTIPLY);
224
                        dbgdump();
225
                        printf("TEST-FAILURE!\n");
226
                        closetrace();
227
                        exit(EXIT_FAILURE);
228
                }
229
 
230
                return m_core->o_c;
231
        }
232
 
233
        //
234
        // Here's our testing function.  Pardon the verbosity of the error
235
        // messages within it, but ...  well, hopefully you won't ever encounter
236
        // any of those errors. ;)
237
        //
238
        // The function works by applying the two inputs to all three of the
239
        // multiply functions, MPY, MPSHI, and MPYUHI.  Results are compared
240
        // against a local multiply on the local (host) machine.  If there's
241
        // any mismatch, an error message is printed and the test fails.
242
        void    mpy_test(int a, int b) {
243
                const   int OP_MPY = 0x08, OP_MPYSHI=0xb, OP_MPYUHI=0x0a;
244
                long    ia, ib, sv;
245
                unsigned long   ua, ub, uv;
246
                unsigned        r, s, u;
247
 
248
                clear_ops();
249
 
250
                printf("MPY-TEST: 0x%08x x 0x%08x\n", a, b);
251
 
252
                ia = (long)a; ib = (long)b; sv = ia * ib;
253
                ua = ((unsigned long)a)&0x0ffffffffu;
254
                ub = ((unsigned long)b)&0x0ffffffffu;
255
                uv = ua * ub;
256
 
257
                r = op(OP_MPY, a, b);
258
                s = op(OP_MPYSHI, a, b);
259
                u = op(OP_MPYUHI, a, b);
260
                tick();
261
 
262
                // Let's check our answers, and see if we got the right results
263
                if ((r ^ sv)&0x0ffffffffu) {
264
                        printf("TEST FAILURE(MPY), MPY #1\n");
265
                        printf("Comparing 0x%08x to 0x%016lx\n", r, sv);
266
                        printf("TEST-FAILURE!\n");
267
                        closetrace();
268
                        exit(EXIT_FAILURE);
269
                } if ((r ^ uv)&0x0ffffffffu) {
270
                        printf("TEST FAILURE(MPY), MPY #2\n");
271
                        printf("Comparing 0x%08x to 0x%016lx\n", r, uv);
272
                        printf("TEST-FAILURE!\n");
273
                        closetrace();
274
                        exit(EXIT_FAILURE);
275
                }
276
 
277
                if ((s^(sv>>32))&0x0ffffffffu) {
278
                        printf("TEST FAILURE(MPYSHI), MPY #3\n");
279
                        printf("Comparing 0x%08x to 0x%016lx\n", s, sv);
280
                        printf("TEST-FAILURE!\n");
281
                        closetrace();
282
                        exit(EXIT_FAILURE);
283
                } if ((u^(uv>>32))&0x0ffffffffu) {
284
                        printf("TEST FAILURE(MPYUHI), MPY #4\n");
285
                        printf("Comparing 0x%08x to 0x%016lx\n", u, uv);
286
                        printf("TEST-FAILURE!\n");
287
                        closetrace();
288
                        exit(EXIT_FAILURE);
289
                }
290
        }
291
};
292
 
293
void    usage(void) {
294
        printf("USAGE: mpy_tb [a b]\n");
295
        printf("\n");
296
        printf(
297
"The test is intended to be run with no arguments.  When run in this fashion,\n"
298
"a series of multiplcation tests will be conducted using all three multiply\n"
299
"instructions.  Any test failure will terminate the program with an exit\n"
300
"condition.  Test success will terminate with a clear test condition.  \n"
301
"During the test, you may expect a large amount of debug output to be\n"
302
"produced.  This is a normal part of testing.  For the meaning of the debug\n"
303
"output, please consider the source code.  The last line of the debug output,\n"
304
"however, will always include either the word \"FAIL\" or \"SUCCESS\"\n"
305
"depending on whether the test succeeds or fails.\n\n"
306
"If the two arguments a and b are given, they will be interpreted according\n"
307
"to the form of strtol, and the test will only involve testing those two\n"
308
"parameters\n\n");
309
}
310
 
311
int     main(int argc, char **argv) {
312
        // Setup verilator
313
        Verilated::commandArgs(argc, argv);
314
        // Now, create a test bench.
315
        CPUOPS_TB       *tb = new CPUOPS_TB();
316
        int     rcode = EXIT_SUCCESS;
317
        // tb->opentrace("mpy_tb.vcd");
318
 
319
        // Get us started by a couple of clocks past reset.  This isn't that
320
        // unreasonable, since the CPU needs to load up the pipeline before
321
        // any first instruction will be executed.
322
        tb->reset();
323
        tb->tick();
324
        tb->tick();
325
        tb->tick();
326
 
327
        // Look for options, such as '-h'.  Trap those here, and produce a usage
328
        // statement.
329
        if ((argc > 1)&&(argv[1][0]=='-')&&(isalpha(argv[1][1]))) {
330
                usage();
331
                exit(EXIT_SUCCESS);
332
        }
333
 
334
        if (argc == 3) {
335
                // Were we given enough arguments to run a user-specified test?
336
                tb->mpy_test(
337
                        strtol(argv[1], NULL, 0),
338
                        strtol(argv[2], NULL, 0));
339
        } else {
340
                // Otherwise we run through a canned set of tests.
341
                tb->mpy_test(0,0);
342
                tb->mpy_test(-1,0);
343
                tb->mpy_test(-1,-1);
344
                tb->mpy_test(1,-1);
345
                tb->mpy_test(1,0);
346
                tb->mpy_test(0,1);
347
                tb->mpy_test(1,1);
348
 
349
                for(int a=0; ((a&0xfff00000)==0); a+=137)
350
                        tb->mpy_test(139, a);
351
 
352
                for(int a=0; ((a&0x80000000)==0); a+=0x197e2)
353
                        tb->mpy_test(0xf97e27ab, a);
354
        }
355
 
356
        printf("SUCCESS!\n");
357
        exit(rcode);
358
}
359
 

powered by: WebSVN 2.1.0

© copyright 1999-2022 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.