diff --git a/enumparse.py b/enumparse.py new file mode 100644 index 0000000..3cf6f82 --- /dev/null +++ b/enumparse.py @@ -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='') + +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) diff --git a/examples/States/.gitignore b/examples/States/.gitignore new file mode 100644 index 0000000..cb8c728 --- /dev/null +++ b/examples/States/.gitignore @@ -0,0 +1 @@ +enums/ \ No newline at end of file diff --git a/examples/States/README.md b/examples/States/README.md new file mode 100644 index 0000000..ea69936 --- /dev/null +++ b/examples/States/README.md @@ -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 -a `). 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) diff --git a/examples/States/States.sv b/examples/States/States.sv new file mode 100644 index 0000000..93d672d --- /dev/null +++ b/examples/States/States.sv @@ -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 diff --git a/examples/States/docs/gtkwave-1.png b/examples/States/docs/gtkwave-1.png new file mode 100644 index 0000000..d962633 Binary files /dev/null and b/examples/States/docs/gtkwave-1.png differ diff --git a/examples/States/docs/gtkwave-2.png b/examples/States/docs/gtkwave-2.png new file mode 100644 index 0000000..6a0ed54 Binary files /dev/null and b/examples/States/docs/gtkwave-2.png differ diff --git a/examples/States/main.cpp b/examples/States/main.cpp new file mode 100644 index 0000000..9de2493 --- /dev/null +++ b/examples/States/main.cpp @@ -0,0 +1,66 @@ +#include "VStates.h" +#include "verilated.h" + +// Needed for waveform generation +#include + +#include +#include +#include + +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; +} diff --git a/examples/States/makefile b/examples/States/makefile new file mode 100644 index 0000000..4ff506b --- /dev/null +++ b/examples/States/makefile @@ -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) diff --git a/verilator.mk b/verilator.mk index 5f424f1..2e527cf 100644 --- a/verilator.mk +++ b/verilator.mk @@ -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 @@ -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) @@ -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