Skip to content

Instantly share code, notes, and snippets.

@Kreijstal
Last active August 8, 2024 17:42
Show Gist options
  • Save Kreijstal/ac8dff5de2b771fc6ccde9db433d73d1 to your computer and use it in GitHub Desktop.
Save Kreijstal/ac8dff5de2b771fc6ccde9db433d73d1 to your computer and use it in GitHub Desktop.
verilator windows bug iverilog cosimulation

This compiles and runs on linux it also compiles and runs on windows, you need verilator and iverilog

module and_gate (
input wire a,
input wire b,
output wire y
);
assign y = a & b;
endmodule
`timescale 1ns / 1ps
module and_gate(
input wire a,
input wire b,
output wire y
);
// Use the external VPI function for the AND gate
// If you want to use a Verilog-based AND gate instead, uncomment the assign statement below
assign y = $and_gate(a, b);
// assign y = a & b;
endmodule
`timescale 1ns / 1ps
module and_gate_tb;
// Inputs
reg a;
reg b;
// Outputs
wire y;
// Instantiate the Unit Under Test (UUT)
and_gate uut (
.a(a),
.b(b),
.y(y)
);
// Task for checking the AND gate output
task check_and_gate;
input a_in, b_in, expected_y;
begin
a = a_in;
b = b_in;
#1; // Allow some time for the output to settle
if (y !== expected_y) begin
$display("ERROR: a=%b, b=%b, y=%b (expected %b)", a, b, y, expected_y);
$finish;
end else begin
$display("PASS: a=%b, b=%b, y=%b", a, b, y);
end
end
endtask
initial begin
// Initialize Inputs
a = 0;
b = 0;
// Wait 100 ns for global reset to finish
#100;
// Stimulus and assertions
check_and_gate(0, 0, 0);
check_and_gate(0, 1, 0);
check_and_gate(1, 0, 0);
check_and_gate(1, 1, 1);
// Add some delay before finishing simulation
#10;
$display("All tests passed!");
$finish;
end
endmodule
#include <vpi_user.h>
#include <stdint.h>
#include "and_gate_vpi_wrapper.h"
static void* and_gate_model = NULL;
static PLI_INT32 and_gate_vpi_calltf(PLI_BYTE8* user_data) {
(void)user_data; // Suppress unused parameter warning
vpiHandle systf_handle, args_iter, arg_handle;
s_vpi_value value;
int32_t a, b, y;
if (!and_gate_model) {
and_gate_model = create_and_gate_model();
}
systf_handle = vpi_handle(vpiSysTfCall, NULL);
args_iter = vpi_iterate(vpiArgument, systf_handle);
// Get first argument (a)
arg_handle = vpi_scan(args_iter);
value.format = vpiIntVal;
vpi_get_value(arg_handle, &value);
a = value.value.integer;
// Get second argument (b)
arg_handle = vpi_scan(args_iter);
value.format = vpiIntVal;
vpi_get_value(arg_handle, &value);
b = value.value.integer;
// Set inputs and evaluate the model
set_and_gate_inputs(and_gate_model, a, b);
// Get the output
y = get_and_gate_output(and_gate_model);
// Set the output value
value.format = vpiIntVal;
value.value.integer = y;
vpi_put_value(systf_handle, &value, NULL, vpiNoDelay);
vpi_free_object(args_iter);
return 0;
}
static PLI_INT32 and_gate_vpi_register(void) {
s_vpi_systf_data tf_data;
tf_data.type = vpiSysFunc;
tf_data.sysfunctype = vpiIntFunc;
tf_data.tfname = "$and_gate";
tf_data.calltf = and_gate_vpi_calltf;
tf_data.compiletf = NULL;
tf_data.sizetf = NULL;
tf_data.user_data = NULL;
vpi_register_systf(&tf_data);
return 0;
}
void (*vlog_startup_routines[])(void) = {
(void (*)(void))and_gate_vpi_register,
0
};
#include <verilated.h>
#include "Vand_gate.h"
#include <vpi_user.h>
static Vand_gate* and_gate_model = nullptr;
static int and_gate_vpi_calltf(PLI_BYTE8* user_data) {
vpiHandle systf_handle, args_iter, arg_handle;
s_vpi_value value;
if (!and_gate_model) {
and_gate_model = new Vand_gate;
}
systf_handle = vpi_handle(vpiSysTfCall, NULL);
args_iter = vpi_iterate(vpiArgument, systf_handle);
// Get first argument (a)
arg_handle = vpi_scan(args_iter);
vpi_get_value(arg_handle, &value);
and_gate_model->a = value.value.integer;
// Get second argument (b)
arg_handle = vpi_scan(args_iter);
vpi_get_value(arg_handle, &value);
and_gate_model->b = value.value.integer;
// Evaluate the model
and_gate_model->eval();
// Set the output value
value.format = vpiIntVal;
value.value.integer = and_gate_model->y;
vpi_put_value(systf_handle, &value, NULL, vpiNoDelay);
vpi_free_object(args_iter);
return 0;
}
void and_gate_vpi_register() {
s_vpi_systf_data tf_data;
tf_data.type = vpiSysFunc;
tf_data.sysfunctype = vpiIntFunc;
tf_data.tfname = "$and_gate";
tf_data.calltf = and_gate_vpi_calltf;
tf_data.compiletf = NULL;
tf_data.sizetf = NULL;
tf_data.user_data = NULL;
vpi_register_systf(&tf_data);
}
void (*vlog_startup_routines[])(void) = {
and_gate_vpi_register,
0
};
#include "and_gate_vpi_wrapper.h"
#include "Vand_gate.h"
#include <verilated.h>
extern "C" {
void* create_and_gate_model() {
return new Vand_gate;
}
void destroy_and_gate_model(void* model) {
delete static_cast<Vand_gate*>(model);
}
void set_and_gate_inputs(void* model, int32_t a, int32_t b) {
Vand_gate* and_gate = static_cast<Vand_gate*>(model);
and_gate->a = a;
and_gate->b = b;
and_gate->eval();
}
int32_t get_and_gate_output(void* model) {
Vand_gate* and_gate = static_cast<Vand_gate*>(model);
return and_gate->y;
}
}
// Initialize Verilated modules
static struct VerilatedInitialize {
VerilatedInitialize() {
Verilated::traceEverOn(true);
}
} verilated_initialize;
#include "and_gate_vpi_wrapper.h"
#include "Vand_gate.h"
#include <verilated.h>
extern "C" {
void* create_and_gate_model() {
return new Vand_gate;
}
void destroy_and_gate_model(void* model) {
delete static_cast<Vand_gate*>(model);
}
void set_and_gate_inputs(void* model, int a, int b) {
Vand_gate* and_gate = static_cast<Vand_gate*>(model);
and_gate->a = a;
and_gate->b = b;
and_gate->eval();
}
int get_and_gate_output(void* model) {
Vand_gate* and_gate = static_cast<Vand_gate*>(model);
return and_gate->y;
}
}
// Initialize Verilated modules
static struct VerilatedInitialize {
VerilatedInitialize() {
Verilated::traceEverOn(true);
}
} verilated_initialize;
#ifndef AND_GATE_VPI_WRAPPER_H
#define AND_GATE_VPI_WRAPPER_H
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
void* create_and_gate_model();
void destroy_and_gate_model(void* model);
void set_and_gate_inputs(void* model, int32_t a, int32_t b);
int32_t get_and_gate_output(void* model);
#ifdef __cplusplus
}
#endif
#endif // AND_GATE_VPI_WRAPPER_H
#include "and_gate_vpi_wrapper.h"
#include "Vand_gate.h"
#include <verilated.h>
extern "C" {
void* create_and_gate_model() {
return new Vand_gate;
}
void destroy_and_gate_model(void* model) {
delete static_cast<Vand_gate*>(model);
}
void set_and_gate_inputs(void* model, vpi_int32 a, vpi_int32 b) {
Vand_gate* and_gate = static_cast<Vand_gate*>(model);
and_gate->a = a;
and_gate->b = b;
and_gate->eval();
}
vpi_int32 get_and_gate_output(void* model) {
Vand_gate* and_gate = static_cast<Vand_gate*>(model);
return and_gate->y;
}
}
// Initialize Verilated modules
static struct VerilatedInitialize {
VerilatedInitialize() {
Verilated::traceEverOn(true);
}
} verilated_initialize;
#ifndef AND_GATE_VPI_WRAPPER_H
#define AND_GATE_VPI_WRAPPER_H
#include <vpi_user.h>
#ifdef __cplusplus
extern "C" {
#endif
void* create_and_gate_model();
void destroy_and_gate_model(void* model);
void set_and_gate_inputs(void* model, vpi_int32 a, vpi_int32 b);
vpi_int32 get_and_gate_output(void* model);
#ifdef __cplusplus
}
#endif
#endif // AND_GATE_VPI_WRAPPER_H
VERILATOR = verilator
IVERILOG = iverilog
VVP = vvp
# Detect operating system
ifeq ($(OS),Windows_NT)
DETECTED_OS := Windows
else
DETECTED_OS := $(shell uname -s)
endif
# Set Verilator root based on OS
ifeq ($(DETECTED_OS),Windows)
POSSIBLE_VERILATOR_ROOTS = $(MSYSTEM_PREFIX)/local/share/verilator $(MSYSTEM_PREFIX)/share/verilator
MINGW_DIR = $(MSYSTEM_PREFIX)
IVERILOG_DIR = $(MINGW_DIR)
VPI_INCLUDE_DIR = $(MINGW_DIR)/include/iverilog
else
POSSIBLE_VERILATOR_ROOTS = /usr/local/share/verilator /usr/share/verilator
POSSIBLE_IVERILOG_DIRS = /usr /usr/local
IVERILOG_DIR = $(firstword $(foreach dir,$(POSSIBLE_IVERILOG_DIRS),$(wildcard $(dir)/include/iverilog)))
VPI_INCLUDE_DIR = $(IVERILOG_DIR)
endif
# Find the first existing Verilator root
VERILATOR_ROOT = $(firstword $(foreach dir,$(POSSIBLE_VERILATOR_ROOTS),$(wildcard $(dir))))
ifeq ($(VERILATOR_ROOT),)
$(error Verilator root directory not found. Please install Verilator or set VERILATOR_ROOT manually.)
endif
ifeq ($(DETECTED_OS),Linux)
ifeq ($(IVERILOG_DIR),)
$(error Icarus Verilog directory not found. Please install Icarus Verilog or set IVERILOG_DIR manually.)
endif
endif
VERILATOR_FLAGS = -Wall --cc -CFLAGS -fPIC -CFLAGS -DVL_TIME_CONTEXT
CFLAGS = -Wall -g -I$(VERILATOR_ROOT)/include -I$(VPI_INCLUDE_DIR) -Iobj_dir -fPIC -DVL_TIME_CONTEXT
CXXFLAGS = $(CFLAGS) -std=c++14 -faligned-new
LDFLAGS = -L$(VERILATOR_ROOT)/include -L$(IVERILOG_DIR)/../lib -L./obj_dir
VPI_SRC = and_gate_vpi.c
WRAPPER_SRC = and_gate_vpi_wrapper.cpp
VPI_OUT = and_gate_vpi.vpi
VERILOG_SRC = and_gate.v
VERILOG_PROXY = and_gate_proxy.v
VERILOG_TB = and_gate_tb.v
VVP_EXEC = and_gate_tb.vvp
# Adjust commands for Windows
ifeq ($(DETECTED_OS),Windows)
MKDIR = mkdir
RM = rm -rf
COPY = cp
CC = gcc
CXX = g++
SHARED = -shared
VPI_CFLAGS = -I$(VPI_INCLUDE_DIR)
VPI_LDFLAGS = -L$(MINGW_DIR)/lib -lvpi -lveriuser
else
MKDIR = mkdir -p
RM = rm -rf
COPY = cp
CC = gcc
CXX = g++
SHARED = -shared
VPI_CFLAGS = -I$(VPI_INCLUDE_DIR) -fPIC
VPI_LDFLAGS = -L$(IVERILOG_DIR)/../lib -lvpi -lveriuser
endif
# Debug prints
$(info IVERILOG_DIR = $(IVERILOG_DIR))
$(info VPI_INCLUDE_DIR = $(VPI_INCLUDE_DIR))
.PHONY: all clean run
all: run
build: $(VVP_EXEC)
obj_dir:
$(MKDIR) obj_dir
obj_dir/Vand_gate.cpp: $(VERILOG_SRC) | obj_dir
$(VERILATOR) $(VERILATOR_FLAGS) $< --Mdir obj_dir
obj_dir/Vand_gate__ALL.o: obj_dir/Vand_gate.cpp
$(MAKE) -C obj_dir -f Vand_gate.mk
obj_dir/libverilated.a: obj_dir/Vand_gate.cpp
$(MAKE) -C obj_dir -f Vand_gate.mk
obj_dir/and_gate_vpi_wrapper.o: $(WRAPPER_SRC) and_gate_vpi_wrapper.h obj_dir/Vand_gate.cpp | obj_dir
$(CXX) $(CXXFLAGS) -c $< -o $@
obj_dir/and_gate_vpi.o: $(VPI_SRC) and_gate_vpi_wrapper.h | obj_dir
$(CC) $(CFLAGS) $(VPI_CFLAGS) -c $< -o $@
$(VPI_OUT): obj_dir/and_gate_vpi.o obj_dir/and_gate_vpi_wrapper.o obj_dir/Vand_gate__ALL.o obj_dir/libverilated.a
$(CXX) $(SHARED) -o $@ $^ $(LDFLAGS) $(VPI_LDFLAGS) -lverilated -lpthread -lvpi
$(VVP_EXEC): $(VERILOG_PROXY) $(VERILOG_TB) $(VPI_OUT)
$(IVERILOG) -o $@ -m./$(VPI_OUT) $(VERILOG_PROXY) $(VERILOG_TB)
run: $(VVP_EXEC)
$(VVP) -M. -m$(basename $(VPI_OUT)) $<
clean:
$(RM) obj_dir *.o *.vpi $(VVP_EXEC)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment