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

Subversion Repositories zipcpu

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

Details | Compare with Previous | View Log

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

powered by: WebSVN 2.1.0

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