Add yosys/verilator support

Add a microwatt-verilator target that simulates the
ghdl -> yosys -> verilog -> verilator path. A good test of
ghdl/yosys synthesis.

Because the everything is run through synthesis, the instruction
image is baked into the build via the RAM_INIT_FILE generic.

Signed-off-by: Anton Blanchard <anton@linux.ibm.com>
pull/179/head
Anton Blanchard 5 years ago committed by Anton Blanchard
parent 6692f0db4f
commit 3e8a6a8fc2

@ -60,6 +60,15 @@ all: microwatt.bit
microwatt.json: $(VHDL_FILES) microwatt.json: $(VHDL_FILES)
$(YOSYS) -m $(GHDLSYNTH) -p "ghdl --std=08 $(GHDL_IMAGE_GENERICS) $(GHDL_TARGET_GENERICS) $(VHDL_FILES) -e toplevel; synth_ecp5 -json $@" $(YOSYS) -m $(GHDLSYNTH) -p "ghdl --std=08 $(GHDL_IMAGE_GENERICS) $(GHDL_TARGET_GENERICS) $(VHDL_FILES) -e toplevel; synth_ecp5 -json $@"


microwatt.v: $(VHDL_FILES)
$(YOSYS) -m $(GHDLSYNTH) -p "ghdl --std=08 $(GHDL_IMAGE_GENERICS) $(GHDL_TARGET_GENERICS) $(VHDL_FILES) -e toplevel; write_verilog $@"

microwatt-verilator: microwatt.v verilator/microwatt-verilator.cpp verilator/uart-verilator.c
#verilator -O3 -Wall --assert --cc microwatt.v --exe verilator/microwatt-verilator.cpp verilator/uart-verilator.c -o $@ -Wno-CASEOVERLAP -Wno-UNOPTFLAT #--trace
verilator -O3 --assert --cc microwatt.v --exe verilator/microwatt-verilator.cpp verilator/uart-verilator.c -o $@ -Wno-CASEOVERLAP -Wno-UNOPTFLAT #--trace
make -C obj_dir -f Vmicrowatt.mk
@cp -f obj_dir/microwatt-verilator microwatt-verilator

microwatt_out.config: microwatt.json $(LPF) microwatt_out.config: microwatt.json $(LPF)
$(NEXTPNR) --json $< --lpf $(LPF) --textcfg $@ $(NEXTPNR_FLAGS) --package $(PACKAGE) $(NEXTPNR) --json $< --lpf $(LPF) --textcfg $@ $(NEXTPNR_FLAGS) --package $(PACKAGE)



@ -0,0 +1,83 @@
#include <stdlib.h>
#include "Vmicrowatt.h"
#include "verilated.h"
#include "verilated_vcd_c.h"

/*
* Current simulation time
* This is a 64-bit integer to reduce wrap over issues and
* allow modulus. You can also use a double, if you wish.
*/
vluint64_t main_time = 0;

/*
* Called by $time in Verilog
* converts to double, to match
* what SystemC does
*/
double sc_time_stamp(void)
{
return main_time;
}

#if VM_TRACE
VerilatedVcdC *tfp;
#endif

void tick(Vmicrowatt *top)
{
top->ext_clk = 1;
top->eval();
#if VM_TRACE
if (tfp)
tfp->dump((double) main_time);
#endif
main_time++;

top->ext_clk = 0;
top->eval();
#if VM_TRACE
if (tfp)
tfp->dump((double) main_time);
#endif
main_time++;
}

void uart_tx(unsigned char tx);
unsigned char uart_rx(void);

int main(int argc, char **argv)
{
Verilated::commandArgs(argc, argv);

// init top verilog instance
Vmicrowatt* top = new Vmicrowatt;

#if VM_TRACE
// init trace dump
Verilated::traceEverOn(true);
tfp = new VerilatedVcdC;
top->trace(tfp, 99);
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;

while(!Verilated::gotFinish()) {
tick(top);

uart_tx(top->uart0_txd);
top->uart0_rxd = uart_rx();
}

#if VM_TRACE
tfp->close();
delete tfp;
#endif

delete top;
}

@ -0,0 +1,254 @@
#include <signal.h>
#include <poll.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <termios.h>
#include <stdlib.h>

/* Should we exit simulation on ctrl-c or pass it through? */
#define EXIT_ON_CTRL_C

#define CLOCK 50000000L
#define BAUD 115200
/* Round to nearest */
#define BITWIDTH ((CLOCK+(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;

/*
* Return an error if the transition is not close enough to the start or
* the end of an expected bit.
*/
static bool is_error(unsigned long bits)
{
double e = 1.0 * tx_countbits / BITWIDTH;

if ((e <= (1.0-error)) && (e >= error))
return true;

return false;
}

void uart_tx(unsigned char tx)
{
switch (tx_state) {
case IDLE:
if (tx == 0) {
tx_state = START_BIT;
tx_countbits = BITWIDTH;
tx_bits = 0;
tx_byte = 0;
}
break;

case START_BIT:
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;
break;
}
}

if (tx_countbits == 0) {
tx_state = BITS;
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;
}

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;
break;
}
}

if (tx_countbits == 0) {
if (tx_bits == 8) {
tx_state = STOP_BIT;
}
tx_countbits = BITWIDTH;
}
break;

case STOP_BIT:
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;
break;
}
/* Go straight to idle */
write(STDOUT_FILENO, &tx_byte, 1);
tx_state = IDLE;
}

if (tx_countbits == 0) {
write(STDOUT_FILENO, &tx_byte, 1);
tx_state = IDLE;
}
break;

case ERROR:
tx_countbits--;
if (tx_countbits == 0) {
tx_state = IDLE;
}

break;
}

tx_prev = tx;
}

static struct termios oldt;

static void disable_raw_mode(void)
{
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
}

static void enable_raw_mode(void)
{
static bool initialized = false;

if (!initialized) {
static struct termios newt;

tcgetattr(STDIN_FILENO, &oldt);
newt = oldt;
cfmakeraw(&newt);
#ifdef EXIT_ON_CTRL_C
newt.c_lflag |= ISIG;
#endif
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
initialized = true;
atexit(disable_raw_mode);
}
}

static int nonblocking_read(unsigned char *c)
{
int ret;
unsigned long val = 0;
struct pollfd fdset[1];

enable_raw_mode();

memset(fdset, 0, sizeof(fdset));

fdset[0].fd = STDIN_FILENO;
fdset[0].events = POLLIN;

ret = poll(fdset, 1, 0);
if (ret == 0)
return false;

ret = read(STDIN_FILENO, &val, 1);
if (ret != 1) {
fprintf(stderr, "%s: read of stdin returns %d\n", __func__, ret);
exit(1);
}

if (ret == 1) {
*c = val;
return true;
} else {
return false;
}
}

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 uart_rx(void)
{
unsigned char c;

switch (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;
}
}

break;

case START_BIT:
rx_countbits--;
if (rx_countbits == 0) {
rx_state = BITS;
rx_countbits = BITWIDTH;
rx = 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;
} else {
rx = (rx_char >> rx_bit) & 1;
}
rx_countbits = BITWIDTH;
}
break;

case STOP_BIT:
rx_countbits--;
if (rx_countbits == 0) {
rx_state = IDLE;
}
break;
}

return rx;
}
Loading…
Cancel
Save