URL
https://opencores.org/ocsvn/zipcpu/zipcpu/trunk
Subversion Repositories zipcpu
Compare Revisions
- This comparison shows the changes necessary to convert path
/zipcpu
- from Rev 196 to Rev 197
- ↔ Reverse comparison
Rev 196 → Rev 197
/trunk/bench/cpp/Makefile
58,6 → 58,16
# debugging output) if successful. To actually run this test, |
# you'll need to run ./div_tb (no arguments necessary). |
# |
# mpy_tb |
# A raw test bench to test the multiply instructions within the |
# cpuops (ALU) unit separate from the rest of the CPU. For more |
# details, look at the usage statement wtihin mpy_tb. |
# |
# zipmmu_tb |
# Like div_tb, this is another raw component test bench. In this |
# case, zipmmu_tb tests whether or not the MMU works when |
# separated from the rest of the CPU. |
# |
# pdump |
# zippy_tb can be configured to produce a profile output that is |
# very useful when debugging the Dhrystone benchmark. (It is |
97,15 → 107,17
# |
################################################################################ |
# |
all: zippy_tb pdump div_tb |
all: zippy_tb pdump div_tb mpy_tb |
|
CXX := g++ |
FLAGS := -Wall -Og -g |
ZASM := ../../sw/zasm |
RTLD := ../../rtl |
INCS := -I$(RTLD)/obj_dir/ -I$(RTLD) -I/usr/share/verilator/include -I$(ZASM) |
VERILATOR_ROOT ?= $(shell bash -c 'verilator -V|grep VERILATOR_ROOT | head -1 | sed -e " s/^.*=\s*//"') |
VROOT := $(VERILATOR_ROOT) |
INCS := -I$(RTLD)/obj_dir/ -I$(RTLD) -I$(VROOT)/include -I$(ZASM) |
SOURCES := zippy_tb.cpp memsim.cpp twoc.cpp $(ZASM)/zopcodes.cpp $(ZASM)/zparser.cpp |
VLIB := /usr/share/verilator/include/verilated.cpp |
VLIB := $(VROOT)/include/verilated.cpp |
RAWLIB := $(VLIB) $(RTLD)/obj_dir/Vzipsystem__ALL.a |
LIBS := $(RAWLIB) -lncurses -lelf |
TESTF := $(ZASM)/z.out |
118,6 → 130,12
div_tb: div_tb.cpp twoc.cpp $(VLIB) $(RTLD)/obj_dir/Vdiv__ALL.a testb.h |
$(CXX) $(FLAGS) $(INCS) div_tb.cpp twoc.cpp $(VLIB) $(RTLD)/obj_dir/Vdiv__ALL.a -o $@ |
|
mpy_tb: mpy_tb.cpp twoc.cpp $(VLIB) $(RTLD)/obj_dir/Vcpuops__ALL.a testb.h |
$(CXX) $(FLAGS) $(INCS) mpy_tb.cpp twoc.cpp $(VLIB) $(RTLD)/obj_dir/Vcpuops__ALL.a -o $@ |
|
zipmmu_tb: zipmmu_tb.cpp $(VLIB) ../rtl/obj_dir/Vzipmmu_tb__ALL.a |
$(CXX) $(FLAGS) $(INCS) -I ../rtl/obj_dir zipmmu_tb.cpp $(VLIB) ../rtl/obj_dir/Vzipmmu_tb__ALL.a -o $@ |
|
pdump: pdump.cpp $(ZASM)/zopcodes.cpp $(ZASM)/zparser.cpp |
pdump: $(ZASM)/zopcodes.h $(ZASM)/zparser.h testb.h twoc.cpp |
$(CXX) $(FLAGS) $(INCS) pdump.cpp $(ZASM)/zopcodes.cpp $(ZASM)/zparser.cpp twoc.cpp -lelf -o $@ |
140,4 → 158,4
|
.PHONY: clean |
clean: |
rm ./zippy_tb pdump div_tb |
rm ./zippy_tb pdump div_tb mpy_tb |
/trunk/bench/cpp/memsim.cpp
47,6 → 47,7
; |
m_len = nxt; m_mask = nxt-1; |
m_mem = new BUSW[m_len]; |
m_nxt_ack = 0; |
} |
|
MEMSIM::~MEMSIM(void) { |
83,13 → 84,25
const unsigned char wb_stb, const unsigned char wb_we, |
const BUSW wb_addr, const BUSW wb_data, |
unsigned char &o_ack, unsigned char &o_stall, BUSW &o_data) { |
o_ack = m_nxt_ack; |
o_data= m_nxt_data; |
m_nxt_data = wb_data; |
o_stall= 0; |
if ((wb_cyc)&&(wb_stb)&&(clk)) { |
if (wb_we) |
m_mem[wb_addr & m_mask] = wb_data; |
o_ack = 1; |
o_stall= 0; |
o_data = m_mem[wb_addr & m_mask]; |
m_nxt_ack = 1; |
m_nxt_data = m_mem[wb_addr & m_mask]; |
// o_ack = 1; |
|
{ |
extern FILE *gbl_dbgfp; |
if (gbl_dbgfp) { |
if (wb_we) fprintf(gbl_dbgfp, "MEMSIM::MEM[%08x] = %08x\n", wb_addr&m_mask, wb_data); |
else |
fprintf(gbl_dbgfp, "MEMSIM::BUS = MEM[%08x] = %08x\n", wb_addr&m_mask, m_nxt_data); |
} |
} |
/* |
printf("MEMBUS -- ACK %s 0x%08x - 0x%08x\n", |
(wb_we)?"WRITE":"READ", |
96,7 → 109,7
wb_addr, o_data); |
*/ |
} else if (clk) { |
o_ack = 0; |
m_nxt_ack = 0; |
o_stall = 0; |
} |
} |
/trunk/bench/cpp/memsim.h
46,6 → 46,8
typedef unsigned char uchar; |
|
BUSW *m_mem, m_len, m_mask; |
int m_nxt_ack; |
BUSW m_nxt_data; |
|
|
MEMSIM(const unsigned int nwords); |
/trunk/bench/cpp/mpy_tb.cpp
0,0 → 1,342
/////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: mpy_tb.cpp |
// |
// Project: Zip CPU -- a small, lightweight, RISC CPU soft core |
// |
// Purpose: Bench testing for the multiply ALU instructions used within the |
// Zip CPU. This depends upon the cpuops.v module, but should be |
// independent of the internal settings within the module. |
// |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
/////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// by the Free Software Foundation, either version 3 of the License, or (at |
// your option) any later version. |
// |
// This program is distributed in the hope that it will be useful, but WITHOUT |
// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or |
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
// for more details. |
// |
// License: GPL, v3, as defined and found on www.gnu.org, |
// http://www.gnu.org/licenses/gpl.html |
// |
// |
/////////////////////////////////////////////////////////////////////////////// |
// |
// |
#include <signal.h> |
#include <time.h> |
#include <unistd.h> |
#include <assert.h> |
|
#include <stdlib.h> |
#include <ctype.h> |
|
#include "verilated.h" |
#include "Vcpuops.h" |
|
#include "testb.h" |
#include "cpudefs.h" |
// #include "twoc.h" |
|
class CPUOPS_TB : public TESTB<Vcpuops> { |
public: |
// Nothing special to do in a startup. |
CPUOPS_TB(void) {} |
|
// ~CPUOPS_TB(void) {} |
|
// |
// Calls TESTB<>::reset to reset the core. Makes sure the i_ce line |
// is low during this reset. |
// |
void reset(void) { |
// m_flash.debug(false); |
m_core->i_ce = 0; |
|
TESTB<Vcpuops>::reset(); |
} |
|
// |
// dbgdump(); |
// |
// Just before the positive edge of every clock, we call this function |
// (if the debug flag is set). This prints out a line of information |
// telling us what is going on within the logic, allowing us access |
// for debugging purposes to inspect things. |
// |
// Other than debugging, this isn't necessary for the functioning of the |
// test bench. At the same time, what are you using a test bench for if |
// not for debugging? |
// |
void dbgdump(void) { |
char outstr[2048], *s; |
sprintf(outstr, "Tick %4ld %s%s ", |
m_tickcount, |
(m_core->i_rst)?"R":" ", |
(m_core->i_ce)?"CE":" "); |
switch(m_core->i_op) { |
case 0: strcat(outstr, " SUB"); break; |
case 1: strcat(outstr, " AND"); break; |
case 2: strcat(outstr, " ADD"); break; |
case 3: strcat(outstr, " OR"); break; |
case 4: strcat(outstr, " XOR"); break; |
case 5: strcat(outstr, " LSR"); break; |
case 6: strcat(outstr, " LSL"); break; |
case 7: strcat(outstr, " ASR"); break; |
case 8: strcat(outstr, " MPY"); break; |
case 9: strcat(outstr, "LODILO"); break; |
case 10: strcat(outstr, "MPYUHI"); break; |
case 11: strcat(outstr, "MPYSHI"); break; |
case 12: strcat(outstr, " BREV"); break; |
case 13: strcat(outstr, " POPC"); break; |
case 14: strcat(outstr, " ROL"); break; |
case 15: strcat(outstr, " MOV"); break; |
default: strcat(outstr, "UNKWN!"); break; |
} s = &outstr[strlen(outstr)]; |
sprintf(s, "(%x) 0x%08x 0x%08x -> 0x%08x [%x] %s%s", |
m_core->i_op, |
m_core->i_a, m_core->i_b, |
m_core->o_c, m_core->o_f, |
(m_core->o_valid)?"V":" ", |
(m_core->o_busy)?"B":" "); |
s = &outstr[strlen(outstr)]; |
|
#if(OPT_MULTIPLY==1) |
sprintf(s, "1,MPY[][][%016lx]", |
m_core->v__DOT__mpy_result); |
s = &outstr[strlen(outstr)]; |
#elif(OPT_MULTIPLY==2) |
sprintf(s, "2,MPY[%016lx][%016lx][%016lx]", |
m_core->v__DOT__genblk2__DOT__genblk2__DOT__genblk1__DOT__r_mpy_a_input, |
m_core->v__DOT__genblk2__DOT__genblk2__DOT__genblk1__DOT__r_mpy_b_input, |
m_core->v__DOT__mpy_result); |
s = &outstr[strlen(outstr)]; |
#elif(OPT_MULTIPLY==3) |
sprintf(s, "3,MPY[%08x][%08x][%016lx], P[%d]", |
m_core->v__DOT__genblk2__DOT__genblk2__DOT__genblk2__DOT__genblk1__DOT__r_mpy_a_input, |
m_core->v__DOT__genblk2__DOT__genblk2__DOT__genblk2__DOT__genblk1__DOT__r_mpy_b_input, |
m_core->v__DOT__genblk2__DOT__genblk2__DOT__genblk2__DOT__genblk1__DOT__r_smpy_result, |
m_core->v__DOT__genblk2__DOT__genblk2__DOT__genblk2__DOT__genblk1__DOT__mpypipe); |
|
#endif |
|
#if(OPT_MULTIPLY != 1) |
if (m_core->v__DOT__this_is_a_multiply_op) |
strcat(s, " MPY-OP"); |
#endif |
puts(outstr); |
} |
|
// |
// tick() |
// |
// Call this to step the processor. |
// |
// This is a bit unusual compared to other tick() functions I have in |
// my simulators in that there are a lot of calls to eval() with clk==0. |
// This is because the multiply logic for OPT_MULTIPLY < 3 depends upon |
// it to be valid. I assume any true Xilinx, or even higher level, |
// implementation wouldn't have this problem. |
// |
void tick(void) { |
bool debug = true; |
|
m_core->i_clk = 0; |
m_core->eval(); |
if (debug) |
dbgdump(); |
#if(OPT_MULTIPLY == 1) |
m_core->i_clk = 0; |
m_core->eval(); |
#endif |
assert((!m_core->o_busy)||(!m_core->o_valid)); |
TESTB<Vcpuops>::tick(); |
m_core->i_clk = 0; |
m_core->eval(); |
} |
|
// |
// clear_ops |
// |
// Runs enough clocks through the device until it is neither busy nor |
// valid. At this point, the ALU should be thoroughly clear. Then |
// we tick things once more. |
// |
void clear_ops(void) { |
m_core->i_ce = 0; |
m_core->i_op = 0; |
|
do { |
tick(); |
} while((m_core->o_busy)||(m_core->o_valid)); |
tick(); |
} |
|
// |
// This is a fairly generic CPU operation call. What makes it less |
// than generic are two things: 1) the ALU is cleared before any |
// new instruction, and 2) the tick count at the end is compared |
// against the tick count OPT_MULTIPLY says we should be getting. |
// A third difference between this call in simulation and a real |
// call within the CPU is that we never set the reset mid-call, whereas |
// the CPU may need to do that if a jump is made and the pipeline needs |
// to be cleared. |
// |
unsigned op(int op, int a, int b) { |
if (m_core->o_valid) |
clear_ops(); |
m_core->i_ce = 1; |
m_core->i_op = op; |
m_core->i_a = a; |
m_core->i_b = b; |
|
unsigned long now = m_tickcount; |
|
tick(); |
m_core->i_ce = 0; |
m_core->i_a = 0; |
m_core->i_b = 0; |
|
while(!m_core->o_valid) |
tick(); |
if(1) { |
if((m_tickcount - now)!=OPT_MULTIPLY) { |
printf("%ld ticks seen, %d ticks expected\n", |
m_tickcount-now, OPT_MULTIPLY); |
dbgdump(); |
} assert((m_tickcount - now)==OPT_MULTIPLY); |
} |
return m_core->o_c; |
} |
|
// |
// Here's our testing function. Pardon the verbosity of the error |
// messages within it, but ... well, hopefully you won't ever encounter |
// any of those errors. ;) |
// |
// The function works by applying the two inputs to all three of the |
// multiply functions, MPY, MPSHI, and MPYUHI. Results are compared |
// against a local multiply on the local (host) machine. If there's |
// any mismatch, an error message is printed and the test fails. |
void mpy_test(int a, int b) { |
const int OP_MPY = 0x08, OP_MPYSHI=0xb, OP_MPYUHI=0x0a; |
long ia, ib, sv; |
unsigned long ua, ub, uv; |
unsigned r, s, u; |
|
clear_ops(); |
|
printf("MPY-TEST: 0x%08x x 0x%08x\n", a, b); |
|
ia = (long)a; ib = (long)b; sv = ia * ib; |
ua = ((unsigned long)a)&0x0ffffffffu; |
ub = ((unsigned long)b)&0x0ffffffffu; |
uv = ua * ub; |
|
r = op(OP_MPY, a, b); |
s = op(OP_MPYSHI, a, b); |
u = op(OP_MPYUHI, a, b); |
tick(); |
|
if ((r ^ sv)&0x0ffffffffu) { |
printf("TEST FAILURE(MPY), MPY #1\n"); |
printf("Comparing 0x%08x to 0x%016lx\n", r, sv); |
printf("TEST-FAILURE!\n"); |
exit(EXIT_FAILURE); |
} if ((r ^ uv)&0x0ffffffffu) { |
printf("TEST FAILURE(MPY), MPY #2\n"); |
printf("Comparing 0x%08x to 0x%016lx\n", r, uv); |
printf("TEST-FAILURE!\n"); |
exit(EXIT_FAILURE); |
} |
|
if ((s^(sv>>32))&0x0ffffffffu) { |
printf("TEST FAILURE(MPYSHI), MPY #3\n"); |
printf("Comparing 0x%08x to 0x%016lx\n", s, sv); |
printf("TEST-FAILURE!\n"); |
exit(EXIT_FAILURE); |
} if ((u^(uv>>32))&0x0ffffffffu) { |
printf("TEST FAILURE(MPYUHI), MPY #4\n"); |
printf("Comparing 0x%08x to 0x%016lx\n", u, uv); |
printf("TEST-FAILURE!\n"); |
exit(EXIT_FAILURE); |
} |
} |
}; |
|
void usage(void) { |
printf("USAGE: mpy_tb [a b]\n"); |
printf("\n"); |
printf( |
"The test is intended to be run with no arguments. When run in this fashion,\n" |
"a series of multiplcation tests will be conducted using all three multiply\n" |
"instructions. Any test failure will terminate the program with an exit\n" |
"condition. Test success will terminate with a clear test condition. \n" |
"During the test, you may expect a large amount of debug output to be\n" |
"produced. This is a normal part of testing. For the meaning of the debug\n" |
"output, please consider the source code. The last line of the debug output,\n" |
"however, will always include either the word \"FAIL\" or \"SUCCESS\"\n" |
"depending on whether the test succeeds or fails.\n\n" |
"If the two arguments a and b are given, they will be interpreted according\n" |
"to the form of strtol, and the test will only involve testing those two\n" |
"parameters\n\n"); |
} |
|
int main(int argc, char **argv) { |
// Setup verilator |
Verilated::commandArgs(argc, argv); |
// Now, create a test bench. |
CPUOPS_TB *tb = new CPUOPS_TB(); |
int rcode = EXIT_SUCCESS; |
|
// Get us started by a couple of clocks past reset. This isn't that |
// unreasonable, since the CPU needs to load up the pipeline before |
// any first instruction will be executed. |
tb->reset(); |
tb->tick(); |
tb->tick(); |
tb->tick(); |
|
// Look for options, such as '-h'. Trap those here, and produce a usage |
// statement. |
if ((argc > 1)&&(argv[1][0]=='-')&&(isalpha(argv[1][1]))) { |
usage(); |
exit(EXIT_SUCCESS); |
} |
|
if (argc == 3) { |
// Were we given enough arguments to run a user-specified test? |
tb->mpy_test( |
strtol(argv[1], NULL, 0), |
strtol(argv[2], NULL, 0)); |
} else { |
// Otherwise we run through a canned set of tests. |
tb->mpy_test(0,0); |
tb->mpy_test(-1,0); |
tb->mpy_test(-1,-1); |
tb->mpy_test(1,-1); |
tb->mpy_test(1,0); |
tb->mpy_test(0,1); |
tb->mpy_test(1,1); |
|
for(int a=0; ((a&0xfff00000)==0); a+=137) |
tb->mpy_test(139, a); |
|
for(int a=0; ((a&0x80000000)==0); a+=0x197e2) |
tb->mpy_test(0xf97e27ab, a); |
} |
|
printf("SUCCESS!\n"); |
exit(rcode); |
} |
|
/trunk/bench/cpp/zippy_tb.cpp
792,7 → 792,7
printw("%8s",""); |
mvprintw(ln-1, 48, "%s%s%s ", |
(m_core->v__DOT__thecpu__DOT__alu_valid)?"A" |
:((m_core->v__DOT__thecpu__DOT__doalu__DOT__genblk2__DOT__r_busy)?"a":" "), |
:((m_core->v__DOT__thecpu__DOT__doalu__DOT__r_busy)?"a":" "), |
(m_core->v__DOT__thecpu__DOT__div_valid)?"D" |
:((m_core->v__DOT__thecpu__DOT__div_busy)?"d":" "), |
(m_core->v__DOT__thecpu__DOT__div_valid)?"F" |