A New RTL Called Polished
As aforementioned, I’m creating a new RTL called Polished.
It’s called Polished because it will provide a polished digital logic design experience from start to finish. Polished will emit verilog, but through RTLIL+Yosys, Polished should be able to skip Verilog entirely and emit gate-level netlists, good for place-and-routing.
Polished RTL is part of a larger ecosystem that includes:
- Polished - the RTL
- PolishedVM - Hardware simulation backend.
- Wax - auxiliary libraries for the Polished RTL including things such as Asynchronous Fifos, pipelined multipliers, floating point units, AXI buses, caches, and mesh networks.
- Finish - larger auxiliary components to make a design useful in the real world such as PCIe controllers, Ram controllers, USB controllers, etc.
- Polished Expo - a modern logic visualizer
- TBD - Some tools for inexpensive synthesis and place and routing to a virtual technology node. This is primarily to allow the designer to get a rough estimate on how the design affects fmax and area.
Higher Level View
Below is a diagram of how I anticipate some components of the Polished stack will fit together.
I also explain some of the components in the following sections.
The Polished VM (PVM) contains a single and primary data structure,
namely the Signal. PVM also provides implemented operations on Signals
In particular, stringify is useful in that it allows for emitting value
literals to VCD files.
The graph compiler is necessary to schedule the computation of certain operations in the netlist. The graph compiler must identify atomic blocks whose constituent computations can be re-ordered. Once these blocks have been identified, the graph compiler can form a computation schedule, also marking which computations can safely occur in parallel. Finally, the graph compiler must lower the schedule into ops and primitives supported by PolishedVM.
TestBench Simulation Harness
A simulation harness is needed for simulating hardware. Fortunately, because of the well defined and composable nature of the PolishedVM stack, as well as the expressiveness and sugar provided by Nim-lang itself, the user should be able to write their own simulation harness without much difficulty.
A good RTL testbench experience includes the following:
- synchronous domain advancement
- value inspection with sugar(think signals print their enums)
- exposes modern general purpose programming language paradigms
1 and 2 are easy enough to provide via some coordination between the RTL frontend and the graph compiler. 3 can be provided in its entirety from PolishedVM. The test benches are written in Nim which easily satisfies 4.
Using a combination of 1 - 4, the user should find that writing testbenches comes naturally.
The astute reader may observe that “User Provided Testbench” sits adjacent to the “Emitted Nim”. This is because the testbench links to the emitted nim at compile time, everytime, right before a simulation is executed.
The VCD-Writer is pretty self explanatory. It is dashed as the user may choose to have some simulations not write waveforms.
Marshalled Nim Reader
While waveforms can be stored and read back as VCDs. Since we are working in Nim, it is also possible to marshal nim program state into a file avoiding VCDs entirely, and likely getting better data to space ratio when saving simulation state as a function of time.
I have not yet decided as of July 26, 2021 whether or not Polished Expo will actually support displaying marshalled data, that is why it is dashed in the diagram.
Polished Expo is the visualizer for the polished ecosystem. Not only will it display waveforms, but it will also allow for neat custom visualizations of designs such as a mesh network, and will expose the necessary GUI tools to enable the customizations.
Expo needs to be able to display the value of arbitrary width Signals, and thus depends on PolishedVM for this functionality.
I’ve also been reflecting on the look and feel of Polished. This is what I’ve got so far:
type state = enum start, middle, end var mybus = Io: Input data = Signed(31..0) Input address = Signed(31..0) Output valid = Signed(31..0) var myMod = Module: var io = Io: Input count = Signed(7..0) Output b = Unsigned(7..0) Output c = Signed(1024.Max) Input hash = Signed(127..0, reset=9876546787654348765'bi) Input mystate = state.PolishedEnum() InOut bus = mybus() io.count.sync := io.count + 1 io.b := 3 # the following will fail because we # already assigned to the count's sync domain # count.comb := b