Skip to content

Commit

Permalink
Merge pull request #15 from OrbitNTNU/8-add-support-for-displaying-en…
Browse files Browse the repository at this point in the history
…ums-in-gtkwave

Add some support for automatically displaying enums in gtkwave.
  • Loading branch information
Eskilrl authored Feb 23, 2024
2 parents 30c95d3 + b236dfb commit 3227202
Show file tree
Hide file tree
Showing 11 changed files with 202 additions and 5 deletions.
4 changes: 3 additions & 1 deletion .github/workflows/makefile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,6 @@ jobs:
- uses: actions/checkout@v3

- name: Docker test
run: make
run: |
python3 -m pip install -r requirements.txt
make
72 changes: 72 additions & 0 deletions enumparse.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import pycparser
import os
import argparse
import math

argparser = argparse.ArgumentParser()
argparser.add_argument('projectname', help='The name of the project')
argparser.add_argument('-f', '--project-folder', help='Project folder location', default='.')
args = argparser.parse_args()

INPUTFILE = args.project_folder + '/obj_dir/V' + args.projectname + '.h'
OUTPUTFOLDER = args.project_folder + '/enums'
try:
with open(INPUTFILE, 'r') as f:
lines = f.readlines()
except FileNotFoundError:
print('Not found:', INPUTFILE)
print('Verilator not run?')
exit(1)



in_enum = False
enumfile = ""

for line in lines:
if not in_enum and "enum" in line:
in_enum = True
if in_enum:
enumfile += line
if in_enum and "};" in line:
in_enum = False

parser = pycparser.c_parser.CParser()
ast = parser.parse(enumfile, filename='<none>')

def get_enums():
return ast.ext

def get_enum_statename_only(name):
# Verilator names the states like so:
# Top__DOT__State
# We only want the 'State' part
return name.split('__DOT__')[-1]

def get_enum_highest_value(enum):
return int(
enum.type.values.enumerators[-1].value.value[:-1]
)

def get_enum_value_binary_encoded(value, bits):
value = int(value[:-1])
return "{0:b}".format(value).zfill(bits)

def enum2txt(enum, bits):
txt = ""
name = get_enum_statename_only(enum.type.name)
for e in enum.type.values.enumerators:
txt += get_enum_value_binary_encoded(e.value.value, bits) + ' ' + e.name + '\n'
return txt, name

enums = get_enums()

if not os.path.exists(OUTPUTFOLDER):
os.mkdir(OUTPUTFOLDER)

for enum in enums:
highest = get_enum_highest_value(enum)
bits = int(math.log2(highest)) + 1
(enumcfgfile, name) = enum2txt(enum, bits)
with open (OUTPUTFOLDER + '/' + name + '.gtkw', 'w') as f:
f.write(enumcfgfile)
1 change: 1 addition & 0 deletions examples/States/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
enums/
13 changes: 13 additions & 0 deletions examples/States/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# How to add states to waveform

1. In your design (.sv file), create an enum in the same way as in the `States.sv` file. Remember the `/* verilator public */` comment, as `Verilator` will pick up on this and generate a C++ equivalent enum for you.
2. Run the `enumparse.py` Python script. This parses the C++ enum and generates a file that adheres to gtkwave's configuration files. The script will output one file per enum in your code under the `enums` folder. (In this project, the python script is automatically run with `make`.)
3. Select the signal you want to correlate with your enums. Make sure it is *blue*! `Data format` -> `Translate Filter File` -> `Enable and Select`. See Figure 1.
4. `Add filter to list` -> Add correct `.gtkw` file -> Make sure it is *blue* -> `Ok`. See Figure 2.
5. Once this is done, you can save the configuration (CTRL + S) and reuse it later (`gtkwave <tracefile> -a <config file>`). It is fine to track the config file on git if the signals included are somewhat stable.

Figure 1:
![Figure1](./docs/gtkwave-1.png)

Figure 2:
![Figure2](./docs/gtkwave-2.png)
22 changes: 22 additions & 0 deletions examples/States/States.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
module States (
input[31:0] in,
output[1:0] state_out
);
typedef enum logic [1:0] { ZERO, LOW, HIGH, MAX } HighLow /* verilator public */;
typedef enum logic [2:0] { INIT, DATA_RX, DATA_TX, STOPBIT, RESET } UART /* verilator public */;
HighLow state = ZERO;

always @(in) begin
if (in == 0)
state = ZERO;
else if (in <= 'h80000000)
state = LOW;
else if (in == 'hFFFFFFFF)
state = MAX;
else
state = HIGH;
end

assign state_out = state;

endmodule
Binary file added examples/States/docs/gtkwave-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/States/docs/gtkwave-2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
66 changes: 66 additions & 0 deletions examples/States/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#include "VStates.h"
#include "verilated.h"

// Needed for waveform generation
#include <verilated_vcd_c.h>

#include <cassert>
#include <vector>
#include <iostream>

int main(int argc, char** argv) {
VStates* top = new VStates;
Verilated::commandArgs(argc, argv);

// For waveform generation
unsigned long tickcount = 0;
Verilated::traceEverOn(true);

// Initialization of trace
VerilatedVcdC *trace = new VerilatedVcdC;
top->trace(trace, 99);
trace->open("trace.vcd");

struct inout {
const uint32_t in;
VStates::States__DOT__HighLow out;
} inout[] = {
/**
* The enum is generated by Verilator from using
*
* /* verilator public *\/
*
* behind the typedef enum declaration. See the States.sv code.
*
*/
{ .in = 0x00000000, .out = VStates::States__DOT__HighLow::ZERO, },
{ .in = 0x00000001, .out = VStates::States__DOT__HighLow::LOW, },
{ .in = 0x00000010, .out = VStates::States__DOT__HighLow::LOW, },
{ .in = 0x7FFFFFFF, .out = VStates::States__DOT__HighLow::LOW, },
{ .in = 0x80000000, .out = VStates::States__DOT__HighLow::LOW, },
{ .in = 0x80000001, .out = VStates::States__DOT__HighLow::HIGH, },
{ .in = 0x80000002, .out = VStates::States__DOT__HighLow::HIGH, },
{ .in = 0x87236211, .out = VStates::States__DOT__HighLow::HIGH, },
{ .in = 0xFFFFFFFE, .out = VStates::States__DOT__HighLow::HIGH, },
{ .in = 0xFFFFFFFF, .out = VStates::States__DOT__HighLow::MAX, },
};

for (int i = 0; i < sizeof(inout) / sizeof(inout[0]); i++) {
if (Verilated::gotFinish()) break;

top->in = inout[i].in;

top->eval();
assert(top->state_out == inout[i].out);

trace->dump(10*tickcount++);
}

trace->dump(10*tickcount++);

trace->close();
delete trace;

delete top;
return 0;
}
15 changes: 15 additions & 0 deletions examples/States/makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
VERILATOR_ROOT := ../..
PROJECT_NAME := States
SOURCES := *.sv
SIMFILES := *.cpp
ENUMPARSE := ../../enumparse.py

include $(VERILATOR_ROOT)/verilator.mk

all:: enums

clean::
rm -rf enums

enums:
python3 $(ENUMPARSE) $(PROJECT_NAME)
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pycparser
13 changes: 9 additions & 4 deletions verilator.mk
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ HASHTAG := \#
# Just doing ${MODULE_FOLDER}/${PROJECT_NAME} is not sufficient, because there
# might be folders between the module folder and project folder. Like in the
# Hierachy example.
#
# The cryptographic code is extracted from:
# https://unix.stackexchange.com/questions/392528/extract-sub-directory-path-from-partially-known-directory
PATH_FROM_ROOT_TO_SRC = $(shell p=$(shell pwd); g=$${p${HASHTAG}${HASHTAG}*/${MODULE_FOLDER}}; echo $$g)

VERILATOR := $(VERILATOR_ROOT)/verilator-docker.sh
Expand All @@ -20,7 +23,7 @@ VERILATOR_ARGS_EMULATOR := --cc --build --exe --trace
VERILATOR_EMULATOR := obj_dir/V$(PROJECT_NAME)
PARSER ?= $(VERILATOR_ROOT)/default_parser.sh

all: run
all:: run

lint: $(SOURCES) $(GENERATED)
./$(VERILATOR) $(VERILATOR_ARGS) $(VERILATOR_ARGS_LINT) $(SOURCES) --top-module $(PROJECT_NAME)
Expand All @@ -29,12 +32,14 @@ $(VERILATOR_EMULATOR): lint | $(SOURCES) $(GENERATED)
./$(VERILATOR) $(VERILATOR_ARGS) $(VERILATOR_ARGS_EMULATOR) $(SIMFILES) $(SOURCES) > /dev/null

# https://stackoverflow.com/questions/17757039/equivalent-of-pipefail-in-dash-shell
run: $(VERILATOR_EMULATOR)
@mkfifo named_pipe
run:: $(VERILATOR_EMULATOR)
@if [[ ! -e named_pipe ]]; then \
mkfifo named_pipe; \
fi
@tee output.txt < named_pipe &
@./$(VERILATOR_EMULATOR) > named_pipe; ./$(PARSER) $(PROJECT_NAME) $$? output.txt
@rm named_pipe

clean:
clean::
@rm -rf obj_dir
@rm -f named_pipe

0 comments on commit 3227202

Please sign in to comment.