#include <stdlib.h>
-#include "Vtoplevel.h"
+#include <stdio.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <time.h>
+#include "Vmicrowatt.h"
#include "verilated.h"
#include "verilated_vcd_c.h"
+#include "uart-verilator.h"
/*
* Current simulation time
VerilatedVcdC *tfp;
#endif
-void tick(Vtoplevel *top)
+void tick(Vmicrowatt *top, bool dump)
{
top->ext_clk = 1;
top->eval();
#if VM_TRACE
- if (tfp)
+ if (tfp && dump)
tfp->dump((double) main_time);
#endif
main_time++;
top->ext_clk = 0;
top->eval();
#if VM_TRACE
- if (tfp)
+ if (tfp && dump)
tfp->dump((double) main_time);
#endif
main_time++;
}
-void uart_tx(unsigned char tx);
-unsigned char uart_rx(void);
+// pretty-print dumped data in ASCII (to help identify strings)
+static void ascii_dump(unsigned char *data, int len, FILE *dump)
+{
+ for (int i = 0; i < len; i++) {
+ if (isalnum(data[i]))
+ putc(data[i], dump);
+ else
+ putc('.', dump);
+ }
+ putc('\n', dump);
+}
+
+// save/restore of verilator model to/from file.
+void save_model(vluint64_t time, Vmicrowatt* topp)
+{
+ char fname[128];
+ sprintf(fname, "verilator.save.%ld", time);
+ VerilatedSave os;
+ struct uart_tx_state *uart = uart_get_state();
+
+ os.open(fname);
+ os << main_time; // user code must save the timestamp, etc
+ os.write(uart, sizeof(*uart));
+ os << *topp;
+}
+
+void restore_model(vluint64_t time, Vmicrowatt* topp)
+{
+ char fname[128];
+ sprintf(fname, "verilator.save.%ld", time);
+ VerilatedRestore os;
+ struct uart_tx_state uart;
+ os.open(fname);
+ os >> main_time;
+ os.read(&uart, sizeof(uart));
+ os >> *topp;
+ uart_restore(&uart);
+}
+
+//save bram memory out to a file. use for snapshots
+int memdump(vluint64_t time, unsigned char *mem, size_t sz)
+{
+ char fname[128];
+ sprintf(fname, "bram.snapshot.%ld", time);
+ int fd = open(fname, O_RDWR | O_CREAT, (mode_t)0600);
+ lseek(fd, sz, SEEK_SET);
+ write(fd, "", 1);
+ void *map = mmap(NULL, sz, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
+ lseek(fd, sz, SEEK_SET);
+ memcpy(map, mem, sz);
+ msync(map, sz, MS_SYNC);
+ munmap(map, sz);
+ ftruncate(fd, sz);
+ close(fd);
+ return 0;
+}
+
+// save-trigger offsets
+vluint64_t save_offset = 1000000;
+vluint64_t save_countdown = save_offset-10;
+
+// write (masked by sel) to internal mem offset by bram_addr line
+static void mem_write(unsigned char *mem,
+ unsigned long bram_addr, unsigned long long bram_di,
+ int bram_sel)
+{
+ unsigned char *mat = &(mem[bram_addr*8]); // 64-bit (8 byte) wide
+ unsigned char *data_in = (unsigned char*)&bram_di; // treat as bytes
+ for (int i = 0; i < 8; i++) {
+ if (bram_sel & (1<<i)) { // for each selected byte
+ mat[i] = data_in[i]; // write it to memory
+ }
+ }
+}
+
+#define BRAM_DEBUG
+
+// sigh yes, all these should be runtime commandline options
+#define TRIGGER_ENABLE
+#define TRIGGER_OCCURENCES 1
+#define TRIGGER_COUNTDOWN 20000
+//#define TERMINATE_AT_COUNTDOWN
+//#define TRIGGER_NIA 0xa580
+//#define TRIGGER_INSN 0xe8628008
+//#define TRIGGER_NIA 0x900
+//#define TRIGGER_INSN 0x7db243a6
+//define TRIGGER_NIA 0x603348
+//#define TRIGGER_INSN 0x7c842a14
+//#define TRIGGER_NIA 0xc00000000042f788
+//#define TRIGGER_INSN 0x7c0802a6
+//#define TRIGGER_INSN 0x7c842a14
+//#define TRIGGER_NIA 0xc00000000042f788
+//#define TRIGGER_INSN 0x7c0802a6
+//#define TRIGGER_NIA 0xc000000000434fc0
+//#define TRIGGER_INSN 0x4bddc159
+//#define TRIGGER_NIA 0x300
+//#define TRIGGER_INSN 0x7db243a6
+//#define TRIGGER_NIA 0xc0000000000d0cbc
+//#define TRIGGER_INSN 0xf93e0000
+#define TRIGGER_NIA 0xc00000000011e46c
+#define TRIGGER_INSN 0x60000000
+//#define TRIGGER_NIA 0xc000000000077b90
+//#define TRIGGER_NIA 0xc0000000000cf600
+//#define TRIGGER_INSN 0x38210040
+//#define TRIGGER_INSN 0xb3be000a
+//#define TRIGGER_NIA 0x335c
+//#define TRIGGER_INSN 0x3c400001
int main(int argc, char **argv)
{
Verilated::commandArgs(argc, argv);
-
// init top verilog instance
- Vtoplevel* top = new Vtoplevel;
-
+ Vmicrowatt* top = new Vmicrowatt;
#if VM_TRACE
// init trace dump
Verilated::traceEverOn(true);
tfp->open("microwatt-verilator.vcd");
#endif
- // Reset
- top->ext_rst = 0;
- for (unsigned long i = 0; i < 5; i++)
- tick(top);
- top->ext_rst = 1;
+ // allocate a stonking chunk of memory but don't use it yet
+ unsigned char *_mem = NULL; // gets deleted if not required
+ unsigned char *mem = NULL; // the actual memory (only set on file load)
+ size_t sz = 0x10000000;
+ _mem = (unsigned char*)malloc(sz);
+ memset(_mem, sz, 0);
+ vluint64_t restore_time = 0;
+
+ // identify bram files to load (if not starting "+[verilator]")
+ // here we can specify any number of files, but at present only
+ // (realistically) two are supported: the bootloader (at address 0x0)
+ // and the second file (linux kernel) at hard-coded 0x500000.
+ // it would be much better to use elf-reading here but that's a lot
+ // of hassle
+ for (int i = 1; i < argc; i++) {
+ char *bram_file = NULL;
+ if (strcmp("-s", argv[i]) == 0) {
+ bram_file = (char*)malloc(128); // okok not freed, i know
+ restore_time = atol(argv[i+1]); // yees, we knoow, check argc
+ restore_model(restore_time, top);
+ sprintf(bram_file, "bram.snapshot.%lld", restore_time);
+ i++;
+ }
+ else if (argv[i][0] != '+') {
+ // assume rest of argument is "+verilator", assume a filename
+ bram_file = argv[i];
+ }
+
+ // mmap the file in private (copy-on-write) mode so that its original
+ // contents are not overwritten
+ if (bram_file != NULL) {
+ unsigned char *fmem = NULL;
+
+ int fd = open(bram_file, O_RDONLY);
+ if (fd < 0) {
+ printf("\n\"%s \" could not open\n", bram_file);
+ exit(1);
+ }
+
+ struct stat statbuf;
+ int err = fstat(fd, &statbuf);
+ if (err < 0) {
+ printf("\n\"%s \" could not stat\n", bram_file);
+ exit(2);
+ }
+
+ fmem = (unsigned char*)mmap(NULL, statbuf.st_size,
+ PROT_READ|PROT_WRITE, MAP_PRIVATE,
+ fd, 0);
+ if (fmem == MAP_FAILED) {
+ printf("Mapping Failed\n");
+ exit(2);
+ }
+ close(fd);
+
+ // copy the file over (bit of a hack, here)
+ mem = _mem;
+ size_t offs = 0x0; // normal start
+ if (i == 2 && restore_time == 0) {
+ offs = 0x600000; // hard-coded offset of the linux binary
+ }
+ printf("loading %s at 0x%x size 0x%x\n", bram_file, offs,
+ statbuf.st_size);
+ memcpy(mem+offs, fmem, statbuf.st_size);
+ munmap(fmem, statbuf.st_size);
+ }
+ }
+ // a file wasn't loaded so the stonking-chunk-o-mem can be free'd
+ if (mem == NULL) {
+ free(_mem);
+ }
+
+ unsigned long long bram_data = 0;
+ unsigned long long bram_data1 = 0; // another clock delay
+ int bram_rd = false;
+
+ // dump file for memory read/write traces [uart takes over stdin/stdout]
+ FILE *dump = fopen("bram.dump", "w");
+
+ // read data is one clock cycle delayed
+ bool next_read = false;
+ unsigned long bram_addr = 0;
+
+ if (restore_time == 0) {
+ // Reset
+ top->ext_rst = 0;
+ for (unsigned long i = 0; i < 5; i++)
+ tick(top, true);
+ top->ext_rst = 1;
+ } else {
+ printf("restored at %lld\n", main_time);
+ }
+
+ unsigned long long bram_do = 0;
+
+ // trace conditions
+ bool traceme = true;
+ int trigger_occurrences = TRIGGER_OCCURENCES;
+#ifdef TRIGGER_COUNTDOWN
+ int trigger_countdown = TRIGGER_COUNTDOWN;
+#ifdef TRIGGER_ENABLE
+ traceme = false;
+#endif
+#endif // TRIGGER_COUNTDOWN
while(!Verilated::gotFinish()) {
- tick(top);
+
+ // check if a snapshot of the model should be actioned,
+ // otherwise continue counting down
+ if (save_countdown == 0) {
+ fprintf(dump, "snapshot saving at %ld\n", main_time);
+ fflush(dump);
+ save_model(main_time, top);
+ if (mem != NULL) {
+ memdump(main_time, mem, sz);
+ }
+ save_countdown = save_offset-1; // loop for next snapshot
+ } else {
+ --save_countdown;
+ }
+
+ tick(top, traceme);
+
+ // read/write the memory to/from the mmap'd file (if given)
+ if (mem != NULL) {
+ top->bram_do = bram_do;
+ if (top->bram_re ) {
+ bram_do = ((unsigned long long*)mem)[top->bram_addr];
+ }
+ if (top->bram_we) {
+ mem_write(mem, top->bram_addr, top->bram_di, top->bram_sel);
+ }
+ }
uart_tx(top->uart0_txd);
top->uart0_rxd = uart_rx();
+
+#ifdef BRAM_DEBUG
+ if (top->nia_req) {
+#ifdef TRIGGER_ENABLE
+ if ((top->nia == TRIGGER_NIA) && (top->insn == TRIGGER_INSN)) {
+ if (trigger_occurrences == 1) {
+ traceme = true;
+ fprintf(dump, "trace trigger enabled\n");
+ }
+ if (trigger_occurrences != 0) {
+ --trigger_occurrences;
+ }
+ }
+#ifdef TRIGGER_COUNTDOWN
+ // tracing active for only TRIGGER_COUNTDOWN cycles
+ if (traceme) {
+ --trigger_countdown;
+ if (trigger_countdown == 0) {
+ traceme = false;
+#ifdef TERMINATE_AT_COUNTDOWN
+ break;
+#endif
+ }
+ }
+#endif // TRIGGER_COUNTDOWN
+#endif // TRIGGER_ENABLE
+ fprintf(dump, "pc %16lx insn %8x msr %16lx",
+ top->nia, top->insn, top->msr_o);
+ }
+#ifdef LDST_DUMP
+ if (top->ldst_req) {
+ if (!top->nia_req) {
+ fprintf(dump, "pc %-16s insn %-8s msr %-16s", "", "", "");
+ }
+ fprintf(dump, " ldst %16lx", top->ldst_addr);
+ }
+#endif
+ if (top->ldst_req || top->nia_req) {
+ fprintf(dump, "\n");
+ }
+ if (top->bram_we) {
+ fprintf(dump, " " \
+ "wr @ %08x do %16lx sel %02x ",
+ top->bram_addr, top->bram_di, top->bram_sel);
+ ascii_dump((unsigned char*)&top->bram_di, 8, dump);
+ fflush(dump);
+ }
+ // read on one clock delay
+ if (top->bram_re) {
+ fprintf(dump, " " \
+ "rd @ %08x di %16lx sel %02x ",
+ top->bram_addr, bram_do, top->bram_sel);
+ ascii_dump((unsigned char*)&bram_do, 8, dump);
+ fflush(dump);
+ }
+#endif // BRAM_DEBUG
}
+ if (mem != NULL) {
+ free(mem);
+ }
+
+ fclose(dump);
+
#if VM_TRACE
tfp->close();
delete tfp;
#include <stdio.h>
#include <termios.h>
#include <stdlib.h>
+#include "uart-verilator.h"
/* Should we exit simulation on ctrl-c or pass it through? */
#define EXIT_ON_CTRL_C
/* Round to nearest */
#define BITWIDTH ((CLK_FREQUENCY+(BAUD/2))/BAUD)
-/*
- * Our UART uses 16x oversampling, so at 50 MHz and 115200 baud
- * each sample is: 50000000/(115200*16) = 27 clock cycles. This
- * means each bit is off by 0.47% so for 8 bits plus a start and
- * stop bit the errors add to be 4.7%.
- */
-static double error = 0.05;
-
-enum state {
- IDLE, START_BIT, BITS, STOP_BIT, ERROR
-};
-
-static enum state tx_state = IDLE;
-static unsigned long tx_countbits;
-static unsigned char tx_bits;
-static unsigned char tx_byte;
-static unsigned char tx_prev;
+static struct uart_tx_state uart;
/*
* Return an error if the transition is not close enough to the start or
*/
static bool is_error(unsigned long bits)
{
- double e = 1.0 * tx_countbits / BITWIDTH;
+ double e = 1.0 * uart.tx_countbits / BITWIDTH;
- if ((e <= (1.0-error)) && (e >= error))
+ if ((e <= (1.0-uart.error)) && (e >= uart.error))
return true;
return false;
void uart_tx(unsigned char tx)
{
- switch (tx_state) {
+ switch (uart.tx_state) {
case IDLE:
if (tx == 0) {
- tx_state = START_BIT;
- tx_countbits = BITWIDTH;
- tx_bits = 0;
- tx_byte = 0;
+ uart.tx_state = START_BIT;
+ uart.tx_countbits = BITWIDTH;
+ uart.tx_bits = 0;
+ uart.tx_byte = 0;
}
break;
case START_BIT:
- tx_countbits--;
+ uart.tx_countbits--;
if (tx == 1) {
- if (is_error(tx_countbits)) {
- printf("START_BIT error %ld %ld\n", BITWIDTH, tx_countbits);
- tx_countbits = BITWIDTH*2;
- tx_state = ERROR;
+ if (is_error(uart.tx_countbits)) {
+ printf("START_BIT error %ld %ld\n", BITWIDTH, uart.tx_countbits);
+ uart.tx_countbits = BITWIDTH*2;
+ uart.tx_state = ERROR;
break;
}
}
- if (tx_countbits == 0) {
- tx_state = BITS;
- tx_countbits = BITWIDTH;
+ if (uart.tx_countbits == 0) {
+ uart.tx_state = BITS;
+ uart.tx_countbits = BITWIDTH;
}
break;
case BITS:
- tx_countbits--;
- if (tx_countbits == BITWIDTH/2) {
- tx_byte = tx_byte | (tx << tx_bits);
- tx_bits = tx_bits + 1;
+ uart.tx_countbits--;
+ if (uart.tx_countbits == BITWIDTH/2) {
+ uart.tx_byte = uart.tx_byte | (tx << uart.tx_bits);
+ uart.tx_bits = uart.tx_bits + 1;
}
- if (tx != tx_prev) {
- if (is_error(tx_countbits)) {
- printf("BITS error %ld %ld\n", BITWIDTH, tx_countbits);
- tx_countbits = BITWIDTH*2;
- tx_state = ERROR;
+ if (tx != uart.tx_prev) {
+ if (is_error(uart.tx_countbits)) {
+ printf("BITS error %ld %ld\n", BITWIDTH, uart.tx_countbits);
+ uart.tx_countbits = BITWIDTH*2;
+ uart.tx_state = ERROR;
break;
}
}
- if (tx_countbits == 0) {
- if (tx_bits == 8) {
- tx_state = STOP_BIT;
+ if (uart.tx_countbits == 0) {
+ if (uart.tx_bits == 8) {
+ uart.tx_state = STOP_BIT;
}
- tx_countbits = BITWIDTH;
+ uart.tx_countbits = BITWIDTH;
}
break;
case STOP_BIT:
- tx_countbits--;
+ uart.tx_countbits--;
if (tx == 0) {
- if (is_error(tx_countbits)) {
- printf("STOP_BIT error %ld %ld\n", BITWIDTH, tx_countbits);
- tx_countbits = BITWIDTH*2;
- tx_state = ERROR;
+ if (is_error(uart.tx_countbits)) {
+ printf("STOP_BIT error %ld %ld\n", BITWIDTH, uart.tx_countbits);
+ uart.tx_countbits = BITWIDTH*2;
+ uart.tx_state = ERROR;
break;
}
/* Go straight to idle */
- write(STDOUT_FILENO, &tx_byte, 1);
- tx_state = IDLE;
+ write(STDOUT_FILENO, &uart.tx_byte, 1);
+ uart.tx_state = IDLE;
}
- if (tx_countbits == 0) {
- write(STDOUT_FILENO, &tx_byte, 1);
- tx_state = IDLE;
+ if (uart.tx_countbits == 0) {
+ write(STDOUT_FILENO, &uart.tx_byte, 1);
+ uart.tx_state = IDLE;
}
break;
case ERROR:
- tx_countbits--;
- if (tx_countbits == 0) {
- tx_state = IDLE;
+ uart.tx_countbits--;
+ if (uart.tx_countbits == 0) {
+ uart.tx_state = IDLE;
}
break;
}
- tx_prev = tx;
+ uart.tx_prev = tx;
}
static struct termios oldt;
}
}
-static enum state rx_state = IDLE;
-static unsigned char rx_char;
-static unsigned long rx_countbits;
-static unsigned char rx_bit;
-static unsigned char rx = 1;
-
/* Avoid calling poll() too much */
#define RX_INTERVAL 10000
static unsigned long rx_sometimes;
{
unsigned char c;
- switch (rx_state) {
+ switch (uart.rx_state) {
case IDLE:
if (rx_sometimes++ >= RX_INTERVAL) {
rx_sometimes = 0;
if (nonblocking_read(&c)) {
- rx_state = START_BIT;
- rx_char = c;
- rx_countbits = BITWIDTH;
- rx_bit = 0;
- rx = 0;
+ uart.rx_state = START_BIT;
+ uart.rx_char = c;
+ uart.rx_countbits = BITWIDTH;
+ uart.rx_bit = 0;
+ uart.rx = 0;
}
}
break;
case START_BIT:
- rx_countbits--;
- if (rx_countbits == 0) {
- rx_state = BITS;
- rx_countbits = BITWIDTH;
- rx = rx_char & 1;
+ uart.rx_countbits--;
+ if (uart.rx_countbits == 0) {
+ uart.rx_state = BITS;
+ uart.rx_countbits = BITWIDTH;
+ uart.rx = uart.rx_char & 1;
}
break;
case BITS:
- rx_countbits--;
- if (rx_countbits == 0) {
- rx_bit = rx_bit + 1;
- if (rx_bit == 8) {
- rx = 1;
- rx_state = STOP_BIT;
+ uart.rx_countbits--;
+ if (uart.rx_countbits == 0) {
+ uart.rx_bit = uart.rx_bit + 1;
+ if (uart.rx_bit == 8) {
+ uart.rx = 1;
+ uart.rx_state = STOP_BIT;
} else {
- rx = (rx_char >> rx_bit) & 1;
+ uart.rx = (uart.rx_char >> uart.rx_bit) & 1;
}
- rx_countbits = BITWIDTH;
+ uart.rx_countbits = BITWIDTH;
}
break;
case STOP_BIT:
- rx_countbits--;
- if (rx_countbits == 0) {
- rx_state = IDLE;
+ uart.rx_countbits--;
+ if (uart.rx_countbits == 0) {
+ uart.rx_state = IDLE;
}
break;
}
- return rx;
+ return uart.rx;
+}
+
+struct uart_tx_state * uart_get_state(void)
+{
+ return &uart;
+}
+
+void uart_restore(struct uart_tx_state *new_state)
+{
+ memcpy(&uart, new_state, sizeof(struct uart_tx_state));
}