gateware

A collection of little open source FPGA hobby projects
git clone http://frotz.net/git/gateware.git
Log | Files | Refs | README

testbench.cpp (6448B)


      1 /* Copyright 2014 Brian Swetland <swetland@frotz.net>
      2  *
      3  * Licensed under the Apache License, Version 2.0 (the "License");
      4  * you may not use this file except in compliance with the License.
      5  * You may obtain a copy of the License at
      6  *
      7  *     http://www.apache.org/licenses/LICENSE-2.0
      8  *
      9  * Unless required by applicable law or agreed to in writing, software
     10  * distributed under the License is distributed on an "AS IS" BASIS,
     11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12  * See the License for the specific language governing permissions and
     13  * limitations under the License.
     14  */
     15 
     16 /* reusable verilator testbench driver
     17  * - expects the top module to be testbench(clk);
     18  * - provides clk to module
     19  * - handles vcd tracing if compiled with TRACE
     20  * - allows tracefilename to be specified via -o
     21 */
     22 
     23 #include <stdio.h>
     24 #include <stdlib.h>
     25 #include <string.h>
     26 #include <sys/types.h>
     27 #include <unistd.h>
     28 #include <fcntl.h>
     29 
     30 #include "Vtestbench.h"
     31 #include "verilated.h"
     32 #include <verilated_vcd_c.h>
     33 
     34 #ifdef SDRAM
     35 #include "sim-sdram.h"
     36 #endif
     37 
     38 static unsigned memory[65536];
     39 
     40 void dpi_mem_write(int addr, int data) {
     41 	//fprintf(stdout,"WR %08x = %08x\n", addr, data);
     42 	memory[addr & 0xFFFF] = data;
     43 }
     44 
     45 void dpi_mem_read(int addr, int *data) {
     46 	//fprintf(stdout,"RD %08x = %08x\n", addr, memory[addr & 0xFFFF]);
     47 	*data = (int) memory[addr & 0xFFFF];
     48 }
     49 
     50 int dpi_mem_read2(int addr) {
     51 	//fprintf(stdout,"Rd %08x = %08x\n", addr, memory[addr & 0xFFFF]);
     52 	return (int) memory[addr & 0xFFFF];
     53 }
     54 
     55 void loadmem(const char *fn) {
     56 	unsigned a = 0;
     57 	FILE *fp = fopen(fn, "r");
     58 	char buf[128];
     59 	memset(memory, 0xaa, sizeof(memory));
     60 	if (fp == NULL) {
     61 		fprintf(stderr, "warning: cannot load memory from '%s'\n", fn);
     62 		return;
     63 	}
     64 	while (fgets(buf, 128, fp) != NULL) {
     65 		unsigned n;
     66 		char *x = buf;
     67 		while (isspace(*x)) x++;
     68 		if (*x == '#') continue;
     69 		if ((x[0] == '/') && (x[1] == '/')) continue;
     70 		n = 0;
     71 		if (x[0] == '.') {
     72 			x++;
     73 			while (isdigit(*x)) {
     74 				n <<= 1;
     75 				if (*x == '1') {
     76 					n |= 1;
     77 				}
     78 				x++;
     79 			}
     80 		} else {
     81 			sscanf(x, "%x", &n);
     82 		}
     83 		//fprintf(stderr,"mem[%08x] = %08x\n",a,n);
     84 		memory[a++] = n;
     85 		if (a == 4096) break;
     86 	}
     87 }
     88 
     89 #ifdef VGA
     90 #define FRAME_W 800
     91 #define FRAME_H 524
     92 #define FRAME_TICKS (FRAME_W * FRAME_H)
     93 #define FRAME_BYTES (FRAME_W * FRAME_H * 3)
     94 
     95 static unsigned vga_ticks = 0;
     96 static unsigned vga_frames = 0;
     97 static unsigned char vga_data[2][FRAME_BYTES];
     98 static unsigned vga_active;
     99 
    100 static int vga_tick(int hs, int vs, int fr, int red, int grn, int blu) {
    101 	if (fr) {
    102 		//fprintf(stderr, "VGA: frame=%u active=%u ticks=%u\n",
    103 		//	vga_frames, vga_active, vga_ticks);
    104 		if (vga_ticks < FRAME_TICKS) {
    105 			fprintf(stderr, "VGA: frame too small: %u ticks\n", vga_ticks);
    106 		} else if (vga_ticks > FRAME_TICKS) {
    107 			fprintf(stderr, "VGA: frame too large: %u ticks\n", vga_ticks);
    108 		} else if (memcmp(vga_data[vga_active], vga_data[!vga_active], FRAME_BYTES)) {
    109 			char tmp[256];
    110 			sprintf(tmp, "frame%04d.ppm", vga_frames);
    111 			int fd = open(tmp, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    112 			if (fd < 0) {
    113 				fprintf(stderr, "VGA: cannot write '%s'\n", tmp);
    114 			} else {
    115 				sprintf(tmp, "P6\n%u %u 15\n", FRAME_W, FRAME_H);
    116 				write(fd, tmp, strlen(tmp));
    117 				write(fd, vga_data[vga_active], FRAME_BYTES);
    118 				close(fd);
    119 			}
    120 			vga_active = !vga_active;
    121 		} else {
    122 			//fprintf(stderr, "VGA: frame %u did not change\n", vga_frames);
    123 		}
    124 		memset(vga_data[vga_active], 0xff, FRAME_BYTES);
    125 		vga_ticks = 0;
    126 		vga_frames++;
    127 		if (vga_frames == 5) {
    128 			return -1;
    129 		}
    130 	}
    131 	if (vga_ticks < FRAME_TICKS) {
    132 		unsigned char* pixel = vga_data[vga_active] + vga_ticks * 3;
    133 		if (hs == 0) {
    134 			pixel[0] = 0xf;
    135 			pixel[1] = 0x8;
    136 			pixel[2] = 0x0;
    137 		} else if (vs == 0) {
    138 			pixel[0] = 0xf;
    139 			pixel[1] = 0x0;
    140 			pixel[2] = 0xf;
    141 		} else {
    142 			pixel[0] = red;
    143 			pixel[1] = grn;
    144 			pixel[2] = blu;
    145 		}
    146 	}
    147 	vga_ticks++;
    148 	return 0;
    149 }
    150 #endif
    151 
    152 #ifdef TRACE
    153 static vluint64_t now = 0;
    154 
    155 double sc_time_stamp() {
    156 	return now;
    157 }
    158 #endif
    159 
    160 int main(int argc, char **argv) {
    161 	const char *vcdname = "trace.vcd";
    162 	const char *memname = NULL;
    163 	int fd;
    164 
    165 	while (argc > 1) {
    166 		if (!strcmp(argv[1], "-trace")) {
    167 #ifdef TRACE
    168 			if (argc < 3) {
    169 				fprintf(stderr,"error: -trace requires argument\n");
    170 				return -1;
    171 			}
    172 			vcdname = argv[2];
    173 			argv += 2;
    174 			argc -= 2;
    175 			continue;
    176 #else
    177 			fprintf(stderr,"error: no trace support\n");
    178 			return -1;
    179 #endif
    180 		} else if (!strcmp(argv[1], "-dump")) {
    181 			if (argc < 3) {
    182 				fprintf(stderr, "error: -dump requires argument\n");
    183 				return -1;
    184 			}
    185 			memname = argv[2];
    186 			argv += 2;
    187 			argc -= 2;
    188 		} else if (!strcmp(argv[1], "-load")) {
    189 			if (argc < 3) {
    190 				fprintf(stderr, "error: -load requires argument\n");
    191 				return -1;
    192 			}
    193 			loadmem(argv[2]);
    194 			argv += 2;
    195 			argc -= 2;
    196 		} else {
    197 			break;
    198 		}
    199 	}
    200 
    201 #ifdef SDRAM
    202 	sim_sdram_init();
    203 #endif
    204 
    205 	Verilated::commandArgs(argc, argv);
    206 	Verilated::debug(0);
    207 	Verilated::randReset(2);
    208 
    209 	Vtestbench *testbench = new Vtestbench;
    210 	testbench->clk = 1;
    211 
    212 // first tick, line up with gtk's vert lines
    213 	testbench->eval();
    214 
    215 #ifdef TRACE
    216 	Verilated::traceEverOn(true);
    217 	VerilatedVcdC* tfp = new VerilatedVcdC;
    218 	testbench->trace(tfp, 99);
    219 	tfp->open(vcdname);
    220 	tfp->dump(now);
    221 #define SAVETRACE() tfp->dump(now)
    222 #else
    223 #define SAVETRACE() do {} while (0)
    224 #endif
    225 
    226 	int oops = 0;
    227 	while (!(testbench->done | testbench->error | oops)) { //Verilated::gotFinish()) {
    228 		now += 5;
    229 		testbench->clk = 0;
    230 		testbench->eval();
    231 		SAVETRACE();
    232 
    233 		now += 5;
    234 		testbench->clk = 1;
    235 #ifdef SDRAM
    236 		unsigned ctl =
    237 			(testbench->sdram_ras_n << 2) |
    238 			(testbench->sdram_cas_n << 1) |
    239 			(testbench->sdram_we_n << 0);
    240 		unsigned out = 0;
    241 		oops = sim_sdram(ctl, testbench->sdram_addr, testbench->sdram_data_o, &out);
    242 		testbench->sdram_data_i = out;
    243 #endif
    244 		testbench->eval();
    245 		SAVETRACE();
    246 #ifdef VGA
    247 		if (vga_tick(testbench->vga_hsync, testbench->vga_vsync,
    248 			     testbench->vga_frame, testbench->vga_red,
    249 			     testbench->vga_grn, testbench->vga_blu)) {
    250 			break;
    251 		}
    252 #endif
    253 	}
    254 
    255 	int status = testbench->error ? -1 : 0;
    256 	fprintf(stderr, "%s: %s\n", argv[0], testbench->error ? "FAIL" : "PASS");
    257 
    258 #ifdef TRACE
    259 	tfp->close();
    260 #endif
    261 	testbench->final();
    262 	delete testbench;
    263 
    264 	if (memname != NULL) {
    265 		fd = open(memname, O_WRONLY | O_CREAT | O_TRUNC, 0640);
    266 		if (fd < 0) {
    267 			fprintf(stderr, "cannot open '%s' for writing\n", memname);
    268 			return -1;
    269 		}
    270 		write(fd, memory, sizeof(memory));
    271 		close(fd);
    272 	}
    273 	return status;
    274 }
    275