Skip to content

Commit

Permalink
Merge pull request #71 from Ben1152000/feature/tests
Browse files Browse the repository at this point in the history
Rewrite tests to use unittest module
  • Loading branch information
Ben1152000 authored Apr 17, 2023
2 parents 2cdb762 + 18abf7f commit d741274
Show file tree
Hide file tree
Showing 20 changed files with 287 additions and 258 deletions.
21 changes: 14 additions & 7 deletions PACKAGE.md
Original file line number Diff line number Diff line change
@@ -1,28 +1,35 @@

## Instructions for how to update the version of the sootty package of PyPI:

1) Update the version number in `setup.py`.
2) Run this command to package the new version into the `dist` directory:
1. Update the version number in `setup.py`.
2. Run this command to package the new version into the `dist` directory:

<!-- -->

python3 setup.py sdist bdist_wheel


3) Run to check that the package contains the correct files:
3. Run to check that the package contains the correct files:

<!-- -->

tar tzf dist/sootty-<version>.tar.gz

4) Run to check whether the package will correctly render to PyPI:
4. Run to check whether the package will correctly render to PyPI:

<!-- -->

twine check dist/*

5) Upload the newly created files to PyPI:
5. Upload the newly created files to PyPI:

<!-- well hey there sailor! don't delete these comments because they actually affect the markdown rendering :-) -->
<!-- -->

twine upload dist/sootty-<version>.tar.gz dist/sootty-<version>-py3-none-any.whl

## Install project locally

python3 -m pip install .

## Run all unit tests

python3 -m unittest test
61 changes: 38 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,70 +1,78 @@
# Sootty

The process of designing hardware requires a graphical interaction mechanism, so that the designer can view the results of their hardware and understand how it functions on a low level. Currently, this is done using waveform viewers like Verdi and GTKWave, which are standalone programs that allow the designer to view and interact with the waveform. However, the process of interaction can be slow, inefficient, and difficult to automate. The goal of this project is to create a waveform viewer that can be used within the command line, enabling the designer to quickly view and debug hardware designs. Sootty is a terminal-based waveform viewer which takes advantage of modern terminals’ capabilities to give a full graphical display of the users’ waveforms.
Sootty is a graphical command-line tool for visualizing waveforms from hardware simulations, as a substitute for waveform visualizers like Verdi and GTKWave. It is designed with a focus on being lightweight and easy to use, and takes advantage of modern terminals’ capabilities to provide a clean graphical display. Current features include:
- Customizable display style
- Search and highlight events using a simple query language

<img width="979" alt="Screen Shot 2022-03-02 at 12 46 28 PM" src="https://user-images.githubusercontent.com/8484201/156447563-438b9763-5429-46f0-aa3f-7b5ad9fe6609.png">
And here's an example of how sootty can be used directly from the terminal:

Example of Sootty used in the terminal.
<img width="979" alt="Screenshot of sootty in action" src="https://raw.githubusercontent.com/Ben1152000/sootty/master/image/screenshot.png">

## How to use
## Getting Started

This library can be installed from PyPi:
1. Install sootty using pip:

```bash
python3 -m pip install sootty
pip install sootty
```

To use, run:
2. Display a waveform file to the terminal:

```bash
sootty "waveform.vcd" -o > image.svg
```

with a Value Change Dump (VCD) or Extended VCD (EVCD) file to produce an svg waveform diagram. Optional arguments include:
- `-b | --break FORMULA` Specify the formula for the points in time to be highlighted.
- `--btable` Print the wire value table at breakpoints to `stdout` (`-b` is required).
Optional arguments include:
- `-s | --start FORMULA` Specify the start of the window.
- `-e | --end FORMULA` Specify the end of the window.
- `-h | --help` Show the help message and exit.
- `-l | --length N` Specify the number of ticks in the window (mutually exclusive with `-e`).
- `-o | --output` Print to `stdout` rather than display on terminal.
- `-o | --output` Print svg data to `stdout` rather than display on terminal.
- `-w | --wires LIST` Comma-separated list of wires to include in the visualization (default to all wires).
- `-b | --break FORMULA` Specify the formula for the points in time to be highlighted.
- `-r | --radix N` Display values in radix N (default 10).
- `-S | --save SAVENAME` Saves current query for future reuse.
- `-R | --reload SAVENAME` Loads a saved query. Requires query name as string.
- `-s | --start FORMULA` Specify the start of the window.
- `-w | --wires LIST` Comma-separated list of wires to include in the visualization (default to all wires).
- `--btable` Print the wire value table at breakpoints to `stdout` (`-b` is required).

*Note: For more detailed information on the query language, check out [syntax.md](syntax.md)

### Examples

Display all wires starting at time 4 and ending at wire `clk`'s tenth tick:
Below are some more examples that take advantage of some of the features sootty has to offer:

- Display all wires starting at time 4 and ending at wire `clk`'s tenth tick:

```bash
sootty "example/example3.vcd" -s "time 4" -e "acc clk == const 10" -w "clk,rst_n,pc,inst"
```

Display wires `Data` and `D1` for 8 units of time starting when `Data` is equal to 20:
- Display wires `Data` and `D1` for 8 units of time starting when `Data` is equal to 20:

```bash
sootty "example/example1.vcd" -l 8 -s "Data == const 20" -w "D1,Data"
```

Saving a query for future use:
- Saving a query for future use:

```bash
sootty "example/example2.vcd" -s "rdata && wdata == const 22" -l 10 -w "rdata, wdata" -S "save.txt"
```

Reloading a saved query:
- Reloading a saved query:

```bash
sootty -R "save.txt"
```

Add breakpoints at time 9, 11, and 16 - 17 and print wire values at breakpoints:
- Add breakpoints at time 9, 11, and 16 - 17 and print wire values at breakpoints:

```bash
sootty "example/example5.evcd" -b "time 9 || time 11 || after time 15 && before time 18" --btable
```

How to run in python (using the repl):
### Running with python

Sootty can also be run from within a python program:

```python
from sootty import WireTrace, Visualizer, Style
Expand All @@ -86,14 +94,21 @@ with open('myevcd.evcd', 'rb') as evcd_stream:
vcd_stream.write(vcd_reader.read())
```

You can view and modify the save files for the queries in the `~/.config/sootty/save` directory.
*Note: You can view and modify the save files for the queries in the `~/.config/sootty/save` directory.*

## Dependencies

As of the current release, Sootty can only display images in certain terminals with builtin graphics support. This currently includes the following terminal replacements:

- [iTerm2](https://iterm2.com/)
- [kitty](https://sw.kovidgoyal.net/kitty/)

The following external dependencies are also needed to properly display images within the terminal:

- [viu](https://github.com/atanunq/viu)

```bash
# From source
# From source (rust package manager)
cargo install viu
# MacOS
brew install viu
Expand All @@ -110,4 +125,4 @@ You can view and modify the save files for the queries in the `~/.config/sootty/

## Contributing

If you are interested in contributing to this project, feel free to take a look at the existing issues. We also have a project idea listed for Google Summer of Code 2022 on the FOSSI website: https://www.fossi-foundation.org/gsoc22-ideas#enhancing-the-sootty-terminal-based-graphical-waveform-viewer
If you are interested in contributing to this project, feel free to take a look at the existing issues and submit a PR. Beginners are encouraged to focus on issues with the "good first issue" label. This project has also been involved with Google Summer of Code through the [FOSSi Foundation](https://www.fossi-foundation.org/). Check out our project idea for GSoC '23: https://www.fossi-foundation.org/gsoc23-ideas#enhancing-the-sootty-terminal-based-graphical-waveform-viewer
Binary file added image/screenshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes.
2 changes: 2 additions & 0 deletions scripts/run_tests.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
python3 -m pip install .
python3 -m unittest discover
2 changes: 1 addition & 1 deletion sootty/visualizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ def to_svg(
if length is None:
length = wiretrace.length()

if wires: # include all wires if empty list provided
if wires is not None: # include all wires if empty list provided
wires = (
None
if len(wires) == 0
Expand Down
File renamed without changes.
6 changes: 6 additions & 0 deletions test/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from .test_all import TestStringMethods
from .test_general import TestGeneral
from .test_limits import TestLimits
from .test_pyrtl import TestPyrtl
from .test_style import TestStyle
from .test_wires import TestWires
11 changes: 11 additions & 0 deletions test/test_all.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from sootty.storage import Wire

import unittest

class TestStringMethods(unittest.TestCase):

def test_upper(self):
self.assertEqual('foo'.upper(), 'FOO')

if __name__ == '__main__':
unittest.main()
43 changes: 43 additions & 0 deletions test/test_general.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import re, sys
from subprocess import call, Popen, STDOUT, PIPE
from sootty import WireTrace, Visualizer, VectorImage, Style

import unittest


class TestGeneral(unittest.TestCase):

def test_svg_output(self):
wiretrace = WireTrace.from_vcd("example/example1.vcd")

assert type(wiretrace) == WireTrace

image = Visualizer().to_svg(wiretrace, start=0, length=8)

pattern = r"(?:<\?xml\b[^>]*>[^<]*)?(?:<!--.*?-->[^<]*)*(?:<svg|<!DOCTYPE svg)\b"
prog = re.compile(pattern, re.DOTALL)
assert prog.match(image.source) is not None

image.display()


def test_scope(self):
wiretrace = WireTrace().from_vcd("example/example2.vcd")
assert type(wiretrace) == WireTrace

# def print_group(group, depth=0):
# for wire in group.wires:
# print('\t' * depth + wire.name)
# for group in group.groups:
# print('\t' * depth + group.name + ':')
# print_group(group, depth + 1)

# print_group(wiretrace.root)

assert wiretrace.num_wires() == 26
image = Visualizer().to_svg(wiretrace, start=0, length=8)
image.display()


if __name__ == "__main__":
unittest.main()
30 changes: 30 additions & 0 deletions test/test_limits.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from sootty.parser import parser

import unittest


class TestLimits(unittest.TestCase):
def test_parse_1(self):
self.assertEqual(
str(parser.parse("a + b & c - d + const 1").pretty("\t")),
"&\n\t+\n\t\twire\ta\n\t\twire\tb\n\t+\n\t\t-\n\t\t\twire\tc\n\t\t\twire\td\n\t\tconst\t1\n",
)

def test_parse_2(self):
self.assertEqual(
str(
parser.parse(
"after (acc clk == const 5) & ready & value & (3 next data == const 64)"
).pretty("\t")
),
"&\n\t&\n\t\t&\n\t\t\tafter\n\t\t\t\t==\n\t\t\t\t\tacc\n\t\t\t\t\t\twire\tclk\n\t\t\t\t\tconst\t5\n\t\t\twire\tready\n\t\twire\tvalue\n\t==\n\t\tnext\n\t\t\t3\n\t\t\twire\tdata\n\t\tconst\t64\n",
)

def test_parse_3(self):
self.assertEqual(
str(parser.parse("D1 & D2").pretty("\t")), "&\n\twire\tD1\n\twire\tD2\n"
)


if __name__ == "__main__":
unittest.main()
112 changes: 112 additions & 0 deletions test/test_pyrtl.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import pyrtl
from sootty import WireTrace, Visualizer

import unittest


class TestPyrtl(unittest.TestCase):

def test_counter(self):
pyrtl.reset_working_block()

i = pyrtl.Input(3, "i")
counter = pyrtl.Register(3, "counter")
o = pyrtl.Output(3, "o")

update = counter + i
counter.next <<= update
o <<= counter

sim = pyrtl.Simulation()
sim.step_multiple({"i": [1, 1, 1, 1, 1]})
sim.tracer.render_trace()

wiretrace = WireTrace.from_pyrtl(sim.tracer)
Visualizer().to_svg(wiretrace).display()

print(pyrtl.working_block())


def test_alu(self):
pyrtl.reset_working_block()

LW = 0
SW = 1
BEQ = 2
RT = 3
AND = 0
OR = 1
ADD = 2
SUB = 3

def ALU(ctrl, a, b):
result = pyrtl.WireVector(16)
zero = pyrtl.WireVector(1)

with pyrtl.conditional_assignment:
with ctrl == AND:
result |= a & b
with ctrl == OR:
result |= a | b
with ctrl == ADD:
result |= a + b
with ctrl == SUB:
result |= a - b

zero <<= result == 0
return result, zero

def ALUControl(op, func):
ctrl = pyrtl.WireVector(2, "ctrl")
with pyrtl.conditional_assignment:
with op == LW:
ctrl |= 2
with op == SW:
ctrl |= 2
with op == BEQ:
ctrl |= 3
with op == RT:
with func == 0:
ctrl |= AND
with func == 1:
ctrl |= OR
with func == 2:
ctrl |= ADD
with func == 3:
ctrl |= SUB
return ctrl

op = pyrtl.Input(2, "op")
a = pyrtl.Input(16, "a")
b = pyrtl.Input(16, "b")
func = pyrtl.Input(2, "func")

r = pyrtl.Output(16, "result")
z = pyrtl.Output(1, "zero")

ctl = ALUControl(op, func)
(
r_o,
z_o,
) = ALU(ctl, a, b)
r <<= r_o
z <<= z_o

sim_inputs = {
"op": [0] * 2 + [1] * 2 + [2] * 2 + [3] * 2,
"a": [2, 1] * 4,
"b": [1, 0, 1, 0] * 2,
"func": [0] * 6 + [0, 1],
}

sim = pyrtl.Simulation()

sim.step_multiple(sim_inputs)
sim.tracer.render_trace(trace_list=["op", "func", "a", "b", "result", "zero"])

wiretrace = WireTrace.from_pyrtl(sim.tracer)
Visualizer().to_svg(wiretrace).display()


if __name__ == "__main__":
unittest.main()
Loading

0 comments on commit d741274

Please sign in to comment.