hello_world/Makefile: Add base address define. Add compiler define as gcc-8.
[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 // identify bram files to load (if not starting "+[verilator]")
183 // here we can specify any number of files, but at present only
184 // (realistically) two are supported: the bootloader (at address 0x0)
185 // and the second file (linux kernel) at hard-coded 0x500000.
186 // it would be much better to use elf-reading here but that's a lot
187 // of hassle
188 for (int i = 1; i < argc; i++) {
189 char *bram_file = NULL;
190 if (strcmp("-s", argv[i]) == 0) {
191 bram_file = (char*)malloc(128); // okok not freed, i know
192 restore_time = atol(argv[i+1]); // yees, we knoow, check argc
193 restore_model(restore_time, top);
194 sprintf(bram_file, "bram.snapshot.%lld", restore_time);
195 i++;
196 }
197 else if (argv[i][0] != '+') {
198 // assume rest of argument is "+verilator", assume a filename
199 bram_file = argv[i];
200 }
201
202 // mmap the file in private (copy-on-write) mode so that its original
203 // contents are not overwritten
204 if (bram_file != NULL) {
205 unsigned char *fmem = NULL;
206
207 int fd = open(bram_file, O_RDONLY);
208 if (fd < 0) {
209 printf("\n\"%s \" could not open\n", bram_file);
210 exit(1);
211 }
212
213 struct stat statbuf;
214 int err = fstat(fd, &statbuf);
215 if (err < 0) {
216 printf("\n\"%s \" could not stat\n", bram_file);
217 exit(2);
218 }
219
220 fmem = (unsigned char*)mmap(NULL, statbuf.st_size,
221 PROT_READ|PROT_WRITE, MAP_PRIVATE,
222 fd, 0);
223 if (fmem == MAP_FAILED) {
224 printf("Mapping Failed\n");
225 exit(2);
226 }
227 close(fd);
228
229 // copy the file over (bit of a hack, here)
230 mem = _mem;
231 size_t offs = 0x0; // normal start
232 if (i == 2 && restore_time == 0) {
233 offs = 0x600000; // hard-coded offset of the linux binary
234 }
235 printf("loading %s at 0x%x size 0x%x\n", bram_file, offs,
236 statbuf.st_size);
237 memcpy(mem+offs, fmem, statbuf.st_size);
238 munmap(fmem, statbuf.st_size);
239 }
240 }
241 // a file wasn't loaded so the stonking-chunk-o-mem can be free'd
242 if (mem == NULL) {
243 free(_mem);
244 }
245
246 unsigned long long bram_data = 0;
247 unsigned long long bram_data1 = 0; // another clock delay
248 int bram_rd = false;
249
250 // dump file for memory read/write traces [uart takes over stdin/stdout]
251 FILE *dump = fopen("bram.dump", "w");
252
253 // read data is one clock cycle delayed
254 bool next_read = false;
255 unsigned long bram_addr = 0;
256
257 if (restore_time == 0) {
258 // Reset
259 top->ext_rst = 0;
260 for (unsigned long i = 0; i < 5; i++)
261 tick(top, true);
262 top->ext_rst = 1;
263 } else {
264 printf("restored at %lld\n", main_time);
265 }
266
267 unsigned long long bram_do = 0;
268
269 // trace conditions
270 bool traceme = true;
271 int trigger_occurrences = TRIGGER_OCCURENCES;
272 #ifdef TRIGGER_COUNTDOWN
273 int trigger_countdown = TRIGGER_COUNTDOWN;
274 #ifdef TRIGGER_ENABLE
275 traceme = false;
276 #endif
277 #endif // TRIGGER_COUNTDOWN
278
279 while(!Verilated::gotFinish()) {
280
281 // check if a snapshot of the model should be actioned,
282 // otherwise continue counting down
283 if (save_countdown == 0) {
284 fprintf(dump, "snapshot saving at %ld\n", main_time);
285 fflush(dump);
286 save_model(main_time, top);
287 if (mem != NULL) {
288 memdump(main_time, mem, sz);
289 }
290 save_countdown = save_offset-1; // loop for next snapshot
291 } else {
292 --save_countdown;
293 }
294
295 tick(top, traceme);
296
297 // read/write the memory to/from the mmap'd file (if given)
298 if (mem != NULL) {
299 top->bram_do = bram_do;
300 if (top->bram_re ) {
301 bram_do = ((unsigned long long*)mem)[top->bram_addr];
302 }
303 if (top->bram_we) {
304 mem_write(mem, top->bram_addr, top->bram_di, top->bram_sel);
305 }
306 }
307
308 uart_tx(top->uart0_txd);
309 top->uart0_rxd = uart_rx();
310
311 #ifdef BRAM_DEBUG
312 if (top->nia_req) {
313 #ifdef TRIGGER_ENABLE
314 if ((top->nia == TRIGGER_NIA) && (top->insn == TRIGGER_INSN)) {
315 if (trigger_occurrences == 1) {
316 traceme = true;
317 fprintf(dump, "trace trigger enabled\n");
318 }
319 if (trigger_occurrences != 0) {
320 --trigger_occurrences;
321 }
322 }
323 #ifdef TRIGGER_COUNTDOWN
324 // tracing active for only TRIGGER_COUNTDOWN cycles
325 if (traceme) {
326 --trigger_countdown;
327 if (trigger_countdown == 0) {
328 traceme = false;
329 #ifdef TERMINATE_AT_COUNTDOWN
330 break;
331 #endif
332 }
333 }
334 #endif // TRIGGER_COUNTDOWN
335 #endif // TRIGGER_ENABLE
336 fprintf(dump, "pc %16lx insn %8x msr %16lx",
337 top->nia, top->insn, top->msr_o);
338 }
339 #ifdef LDST_DUMP
340 if (top->ldst_req) {
341 if (!top->nia_req) {
342 fprintf(dump, "pc %-16s insn %-8s msr %-16s", "", "", "");
343 }
344 fprintf(dump, " ldst %16lx", top->ldst_addr);
345 }
346 #endif
347 if (top->ldst_req || top->nia_req) {
348 fprintf(dump, "\n");
349 }
350 if (top->bram_we) {
351 fprintf(dump, " " \
352 "wr @ %08x do %16lx sel %02x ",
353 top->bram_addr, top->bram_di, top->bram_sel);
354 ascii_dump((unsigned char*)&top->bram_di, 8, dump);
355 fflush(dump);
356 }
357 // read on one clock delay
358 if (top->bram_re) {
359 fprintf(dump, " " \
360 "rd @ %08x di %16lx sel %02x ",
361 top->bram_addr, bram_do, top->bram_sel);
362 ascii_dump((unsigned char*)&bram_do, 8, dump);
363 fflush(dump);
364 }
365 #endif // BRAM_DEBUG
366 }
367
368 if (mem != NULL) {
369 free(mem);
370 }
371
372 fclose(dump);
373
374 #if VM_TRACE
375 tfp->close();
376 delete tfp;
377 #endif
378
379 delete top;
380 }