8 #include "Vmicrowatt.h"
10 #include "verilated_vcd_c.h"
11 #include "uart-verilator.h"
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.
18 vluint64_t main_time
= 0;
21 * Called by $time in Verilog
22 * converts to double, to match
25 double sc_time_stamp(void)
34 void tick(Vmicrowatt
*top
, bool dump
)
40 tfp
->dump((double) main_time
);
48 tfp
->dump((double) main_time
);
53 // pretty-print dumped data in ASCII (to help identify strings)
54 static void ascii_dump(unsigned char *data
, int len
, FILE *dump
)
56 for (int i
= 0; i
< len
; i
++) {
65 // save/restore of verilator model to/from file.
66 void save_model(vluint64_t time
, Vmicrowatt
* topp
)
69 sprintf(fname
, "verilator.save.%ld", time
);
71 struct uart_tx_state
*uart
= uart_get_state();
74 os
<< main_time
; // user code must save the timestamp, etc
75 os
.write(uart
, sizeof(*uart
));
79 void restore_model(vluint64_t time
, Vmicrowatt
* topp
)
82 sprintf(fname
, "verilator.save.%ld", time
);
84 struct uart_tx_state uart
;
87 os
.read(&uart
, sizeof(uart
));
92 //save bram memory out to a file. use for snapshots
93 int memdump(vluint64_t time
, unsigned char *mem
, size_t sz
)
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
);
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
);
110 // save-trigger offsets
111 vluint64_t save_offset
= 1000000;
112 vluint64_t save_countdown
= save_offset
-10;
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
,
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
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
161 int main(int argc
, char **argv
)
163 Verilated::commandArgs(argc
, argv
);
164 // init top verilog instance
165 Vmicrowatt
* top
= new Vmicrowatt
;
168 Verilated::traceEverOn(true);
169 tfp
= new VerilatedVcdC
;
171 tfp
->open("microwatt-verilator.vcd");
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
);
180 vluint64_t restore_time
= 0;
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
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
);
197 else if (argv
[i
][0] != '+') {
198 // assume rest of argument is "+verilator", assume a filename
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
;
207 int fd
= open(bram_file
, O_RDONLY
);
209 printf("\n\"%s \" could not open\n", bram_file
);
214 int err
= fstat(fd
, &statbuf
);
216 printf("\n\"%s \" could not stat\n", bram_file
);
220 fmem
= (unsigned char*)mmap(NULL
, statbuf
.st_size
,
221 PROT_READ
|PROT_WRITE
, MAP_PRIVATE
,
223 if (fmem
== MAP_FAILED
) {
224 printf("Mapping Failed\n");
229 // copy the file over (bit of a hack, here)
231 size_t offs
= 0x0; // normal start
232 if (i
== 2 && restore_time
== 0) {
233 offs
= 0x600000; // hard-coded offset of the linux binary
235 printf("loading %s at 0x%x size 0x%x\n", bram_file
, offs
,
237 memcpy(mem
+offs
, fmem
, statbuf
.st_size
);
238 munmap(fmem
, statbuf
.st_size
);
241 // a file wasn't loaded so the stonking-chunk-o-mem can be free'd
246 unsigned long long bram_data
= 0;
247 unsigned long long bram_data1
= 0; // another clock delay
250 // dump file for memory read/write traces [uart takes over stdin/stdout]
251 FILE *dump
= fopen("bram.dump", "w");
253 // read data is one clock cycle delayed
254 bool next_read
= false;
255 unsigned long bram_addr
= 0;
257 if (restore_time
== 0) {
260 for (unsigned long i
= 0; i
< 5; i
++)
264 printf("restored at %lld\n", main_time
);
267 unsigned long long bram_do
= 0;
271 int trigger_occurrences
= TRIGGER_OCCURENCES
;
272 #ifdef TRIGGER_COUNTDOWN
273 int trigger_countdown
= TRIGGER_COUNTDOWN
;
274 #ifdef TRIGGER_ENABLE
277 #endif // TRIGGER_COUNTDOWN
279 while(!Verilated::gotFinish()) {
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
);
286 save_model(main_time
, top
);
288 memdump(main_time
, mem
, sz
);
290 save_countdown
= save_offset
-1; // loop for next snapshot
297 // read/write the memory to/from the mmap'd file (if given)
299 top
->bram_do
= bram_do
;
301 bram_do
= ((unsigned long long*)mem
)[top
->bram_addr
];
304 mem_write(mem
, top
->bram_addr
, top
->bram_di
, top
->bram_sel
);
308 uart_tx(top
->uart0_txd
);
309 top
->uart0_rxd
= uart_rx();
313 #ifdef TRIGGER_ENABLE
314 if ((top
->nia
== TRIGGER_NIA
) && (top
->insn
== TRIGGER_INSN
)) {
315 if (trigger_occurrences
== 1) {
317 fprintf(dump
, "trace trigger enabled\n");
319 if (trigger_occurrences
!= 0) {
320 --trigger_occurrences
;
323 #ifdef TRIGGER_COUNTDOWN
324 // tracing active for only TRIGGER_COUNTDOWN cycles
327 if (trigger_countdown
== 0) {
329 #ifdef TERMINATE_AT_COUNTDOWN
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
);
342 fprintf(dump
, "pc %-16s insn %-8s msr %-16s", "", "", "");
344 fprintf(dump
, " ldst %16lx", top
->ldst_addr
);
347 if (top
->ldst_req
|| top
->nia_req
) {
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
);
357 // read on one clock delay
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
);