Forgot to remove dissasembly file.
[microwatt.git] / verilator / microwatt-verilator.cpp
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <sys/mman.h>
4 #include <sys/stat.h>
5 #include <fcntl.h>
6 #include <unistd.h>
7 #include <time.h>
8 #include "Vmicrowatt.h"
9 #include "verilated.h"
10 #include "verilated_vcd_c.h"
11 #include "uart-verilator.h"
12
13 /*
14 * Current simulation time
15 * This is a 64-bit integer to reduce wrap over issues and
16 * allow modulus. You can also use a double, if you wish.
17 */
18 vluint64_t main_time = 0;
19
20 /*
21 * Called by $time in Verilog
22 * converts to double, to match
23 * what SystemC does
24 */
25 double sc_time_stamp(void)
26 {
27 return main_time;
28 }
29
30 #if VM_TRACE
31 VerilatedVcdC *tfp;
32 #endif
33
34 void tick(Vmicrowatt *top, bool dump)
35 {
36 top->ext_clk = 1;
37 top->eval();
38 #if VM_TRACE
39 if (tfp && dump)
40 tfp->dump((double) main_time);
41 #endif
42 main_time++;
43
44 top->ext_clk = 0;
45 top->eval();
46 #if VM_TRACE
47 if (tfp && dump)
48 tfp->dump((double) main_time);
49 #endif
50 main_time++;
51 }
52
53 // pretty-print dumped data in ASCII (to help identify strings)
54 static void ascii_dump(unsigned char *data, int len, FILE *dump)
55 {
56 for (int i = 0; i < len; i++) {
57 if (isalnum(data[i]))
58 putc(data[i], dump);
59 else
60 putc('.', dump);
61 }
62 putc('\n', dump);
63 }
64
65 // save/restore of verilator model to/from file.
66 void save_model(vluint64_t time, Vmicrowatt* topp)
67 {
68 char fname[128];
69 sprintf(fname, "verilator.save.%ld", time);
70 VerilatedSave os;
71 struct uart_tx_state *uart = uart_get_state();
72
73 os.open(fname);
74 os << main_time; // user code must save the timestamp, etc
75 os.write(uart, sizeof(*uart));
76 os << *topp;
77 }
78
79 void restore_model(vluint64_t time, Vmicrowatt* topp)
80 {
81 char fname[128];
82 sprintf(fname, "verilator.save.%ld", time);
83 VerilatedRestore os;
84 struct uart_tx_state uart;
85 os.open(fname);
86 os >> main_time;
87 os.read(&uart, sizeof(uart));
88 os >> *topp;
89 uart_restore(&uart);
90 }
91
92 //save bram memory out to a file. use for snapshots
93 int memdump(vluint64_t time, unsigned char *mem, size_t sz)
94 {
95 char fname[128];
96 sprintf(fname, "bram.snapshot.%ld", time);
97 int fd = open(fname, O_RDWR | O_CREAT, (mode_t)0600);
98 lseek(fd, sz, SEEK_SET);
99 write(fd, "", 1);
100 void *map = mmap(NULL, sz, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
101 lseek(fd, sz, SEEK_SET);
102 memcpy(map, mem, sz);
103 msync(map, sz, MS_SYNC);
104 munmap(map, sz);
105 ftruncate(fd, sz);
106 close(fd);
107 return 0;
108 }
109
110 // save-trigger offsets
111 vluint64_t save_offset = 1000000;
112 vluint64_t save_countdown = save_offset-10;
113
114 // write (masked by sel) to internal mem offset by bram_addr line
115 static void mem_write(unsigned char *mem,
116 unsigned long bram_addr, unsigned long long bram_di,
117 int bram_sel)
118 {
119 unsigned char *mat = &(mem[bram_addr*8]); // 64-bit (8 byte) wide
120 unsigned char *data_in = (unsigned char*)&bram_di; // treat as bytes
121 for (int i = 0; i < 8; i++) {
122 if (bram_sel & (1<<i)) { // for each selected byte
123 mat[i] = data_in[i]; // write it to memory
124 }
125 }
126 }
127
128 #define BRAM_DEBUG
129
130 // sigh yes, all these should be runtime commandline options
131 //#define TRIGGER_ENABLE
132 #define TRIGGER_OCCURENCES 1
133 #define TRIGGER_COUNTDOWN 20000
134 //#define TERMINATE_AT_COUNTDOWN
135 //#define TRIGGER_NIA 0xa580
136 //#define TRIGGER_INSN 0xe8628008
137 //#define TRIGGER_NIA 0x900
138 //#define TRIGGER_INSN 0x7db243a6
139 //define TRIGGER_NIA 0x603348
140 //#define TRIGGER_INSN 0x7c842a14
141 //#define TRIGGER_NIA 0xc00000000042f788
142 //#define TRIGGER_INSN 0x7c0802a6
143 //#define TRIGGER_INSN 0x7c842a14
144 //#define TRIGGER_NIA 0xc00000000042f788
145 //#define TRIGGER_INSN 0x7c0802a6
146 //#define TRIGGER_NIA 0xc000000000434fc0
147 //#define TRIGGER_INSN 0x4bddc159
148 //#define TRIGGER_NIA 0x300
149 //#define TRIGGER_INSN 0x7db243a6
150 //#define TRIGGER_NIA 0xc0000000000d0cbc
151 //#define TRIGGER_INSN 0xf93e0000
152 #define TRIGGER_NIA 0xc00000000011e46c
153 #define TRIGGER_INSN 0x60000000
154 //#define TRIGGER_NIA 0xc000000000077b90
155 //#define TRIGGER_NIA 0xc0000000000cf600
156 //#define TRIGGER_INSN 0x38210040
157 //#define TRIGGER_INSN 0xb3be000a
158 //#define TRIGGER_NIA 0x335c
159 //#define TRIGGER_INSN 0x3c400001
160
161 int main(int argc, char **argv)
162 {
163 Verilated::commandArgs(argc, argv);
164 // init top verilog instance
165 Vmicrowatt* top = new Vmicrowatt;
166 #if VM_TRACE
167 // init trace dump
168 Verilated::traceEverOn(true);
169 tfp = new VerilatedVcdC;
170 top->trace(tfp, 99);
171 tfp->open("microwatt-verilator.vcd");
172 #endif
173
174 // allocate a stonking chunk of memory but don't use it yet
175 unsigned char *_mem = NULL; // gets deleted if not required
176 unsigned char *mem = NULL; // the actual memory (only set on file load)
177 size_t sz = 0x10000000;
178 _mem = (unsigned char*)malloc(sz);
179 memset(_mem, sz, 0);
180 vluint64_t restore_time = 0;
181
182 bool traceme = false; // true: dump to trace file, false: don't
183 // identify bram files to load (if not starting "+[verilator]")
184 // here we can specify any number of files, but at present only
185 // (realistically) two are supported: the bootloader (at address 0x0)
186 // and the second file (linux kernel) at hard-coded 0x500000.
187 // it would be much better to use elf-reading here but that's a lot
188 // of hassle
189 for (int i = 1; i < argc; i++) {
190 char *bram_file = NULL;
191 if (strcmp("-h", argv[i]) == 0) {
192 printf("---Microwatt-verilator binary.---\n");
193 printf("Line args:\n");
194 printf("-h <- shows this help string\n");
195 printf("-d <- enable trace dumping\n");
196 printf("-s [NUMBER] <- start sim from snapshot\n");
197 printf("+verilator+[CMD] <- pass in verilator runtime commands,\
198 see the verilator wiki for more info.\n");
199 printf("first arg <- binary file to load into bram\n");
200 printf("second arg <- linux binary to load to 0x600000 (a hack)\n");
201 exit(1);
202 }
203 else if (strcmp("-d", argv[i]) == 0) {
204 printf("Trace dump enabled. NOTE: Generates *LARGE* files!\n");
205 traceme = true;
206 }
207 else if (strcmp("-s", argv[i]) == 0) {
208 bram_file = (char*)malloc(128); // okok not freed, i know
209 restore_time = atol(argv[i+1]); // yees, we knoow, check argc
210 restore_model(restore_time, top);
211 sprintf(bram_file, "bram.snapshot.%lld", restore_time);
212 i++;
213 }
214 else if (argv[i][0] != '+') {
215 // assume rest of argument is "+verilator", assume a filename
216 bram_file = argv[i];
217 }
218
219 // mmap the file in private (copy-on-write) mode so that its original
220 // contents are not overwritten
221 if (bram_file != NULL) {
222 unsigned char *fmem = NULL;
223
224 int fd = open(bram_file, O_RDONLY);
225 if (fd < 0) {
226 printf("\n\"%s \" could not open\n", bram_file);
227 exit(1);
228 }
229
230 struct stat statbuf;
231 int err = fstat(fd, &statbuf);
232 if (err < 0) {
233 printf("\n\"%s \" could not stat\n", bram_file);
234 exit(2);
235 }
236
237 fmem = (unsigned char*)mmap(NULL, statbuf.st_size,
238 PROT_READ|PROT_WRITE, MAP_PRIVATE,
239 fd, 0);
240 if (fmem == MAP_FAILED) {
241 printf("Mapping Failed\n");
242 exit(2);
243 }
244 close(fd);
245
246 // copy the file over (bit of a hack, here)
247 mem = _mem;
248 size_t offs = 0x0; // normal start
249 if (i == 2 && restore_time == 0) {
250 offs = 0x600000; // hard-coded offset of the linux binary
251 }
252 printf("loading %s at 0x%x size 0x%x\n", bram_file, offs,
253 statbuf.st_size);
254 memcpy(mem+offs, fmem, statbuf.st_size);
255 munmap(fmem, statbuf.st_size);
256 }
257 }
258 // a file wasn't loaded so the stonking-chunk-o-mem can be free'd
259 if (mem == NULL) {
260 free(_mem);
261 }
262
263 unsigned long long bram_data = 0;
264 unsigned long long bram_data1 = 0; // another clock delay
265 int bram_rd = false;
266
267 // dump file for memory read/write traces [uart takes over stdin/stdout]
268 FILE *dump = fopen("bram.dump", "w");
269
270 // read data is one clock cycle delayed
271 bool next_read = false;
272 unsigned long bram_addr = 0;
273
274 if (restore_time == 0) {
275 // Reset
276 top->ext_rst = 0;
277 for (unsigned long i = 0; i < 5; i++)
278 tick(top, true);
279 top->ext_rst = 1;
280 } else {
281 printf("restored at %lld\n", main_time);
282 }
283
284 unsigned long long bram_do = 0;
285
286 // trace conditions
287 int trigger_occurrences = TRIGGER_OCCURENCES;
288 #ifdef TRIGGER_COUNTDOWN
289 int trigger_countdown = TRIGGER_COUNTDOWN;
290 #ifdef TRIGGER_ENABLE
291 traceme = false;
292 #endif
293 #endif // TRIGGER_COUNTDOWN
294
295 while(!Verilated::gotFinish()) {
296
297 // check if a snapshot of the model should be actioned,
298 // otherwise continue counting down
299 if (save_countdown == 0) {
300 fprintf(dump, "snapshot saving at %ld\n", main_time);
301 fflush(dump);
302 save_model(main_time, top);
303 if (mem != NULL) {
304 memdump(main_time, mem, sz);
305 }
306 save_countdown = save_offset-1; // loop for next snapshot
307 } else {
308 --save_countdown;
309 }
310
311 tick(top, traceme);
312
313 // read/write the memory to/from the mmap'd file (if given)
314 if (mem != NULL) {
315 top->bram_do = bram_do;
316 if (top->bram_re ) {
317 bram_do = ((unsigned long long*)mem)[top->bram_addr];
318 }
319 if (top->bram_we) {
320 mem_write(mem, top->bram_addr, top->bram_di, top->bram_sel);
321 }
322 }
323
324 uart_tx(top->uart0_txd);
325 top->uart0_rxd = uart_rx();
326
327 #ifdef BRAM_DEBUG
328 if (top->nia_req) {
329 #ifdef TRIGGER_ENABLE
330 if ((top->nia == TRIGGER_NIA) && (top->insn == TRIGGER_INSN)) {
331 if (trigger_occurrences == 1) {
332 traceme = true;
333 fprintf(dump, "trace trigger enabled\n");
334 }
335 if (trigger_occurrences != 0) {
336 --trigger_occurrences;
337 }
338 }
339 #ifdef TRIGGER_COUNTDOWN
340 // tracing active for only TRIGGER_COUNTDOWN cycles
341 if (traceme) {
342 --trigger_countdown;
343 if (trigger_countdown == 0) {
344 traceme = false;
345 #ifdef TERMINATE_AT_COUNTDOWN
346 break;
347 #endif
348 }
349 }
350 #endif // TRIGGER_COUNTDOWN
351 #endif // TRIGGER_ENABLE
352 fprintf(dump, "pc %16lx insn %8x msr %16lx",
353 top->nia, top->insn, top->msr_o);
354 }
355 #ifdef LDST_DUMP
356 if (top->ldst_req) {
357 if (!top->nia_req) {
358 fprintf(dump, "pc %-16s insn %-8s msr %-16s", "", "", "");
359 }
360 fprintf(dump, " ldst %16lx", top->ldst_addr);
361 }
362 #endif
363 if (top->ldst_req || top->nia_req) {
364 fprintf(dump, "\n");
365 }
366 if (top->bram_we) {
367 fprintf(dump, " " \
368 "wr @ %08x do %16lx sel %02x ",
369 top->bram_addr, top->bram_di, top->bram_sel);
370 ascii_dump((unsigned char*)&top->bram_di, 8, dump);
371 fflush(dump);
372 }
373 // read on one clock delay
374 if (top->bram_re) {
375 fprintf(dump, " " \
376 "rd @ %08x di %16lx sel %02x ",
377 top->bram_addr, bram_do, top->bram_sel);
378 ascii_dump((unsigned char*)&bram_do, 8, dump);
379 fflush(dump);
380 }
381 #endif // BRAM_DEBUG
382 }
383
384 if (mem != NULL) {
385 free(mem);
386 }
387
388 fclose(dump);
389
390 #if VM_TRACE
391 tfp->close();
392 delete tfp;
393 #endif
394
395 delete top;
396 }