61 karma · joined July 10, 2022
You're designing a CPU, either for your job or for fun, in your favourite hardware description language. Systemverilog, vhdl, hell maybe you went to Berkeley/ work at sifive and you use chisel. if you aren't familiar with HDLs or digital logic design, you should look into them, it is fun.
to test your CPU, you use a simulator, and give your simulated CPU a program to execute. But shit -- you have a bug, and some instruction is producing the wrong value. It would be really nice to step through the code and see what was happening inside your CPU to pinpoint where the bug is.
At this point, you can tell the simulator to generate a waveform (some call it a dump, maybe there are other names). The waveform will contain every signal inside of your design -- every clock, every control signal, you name it. This is typically a MASSIVE amount of data, and people analyze these waveforms. And worse yet, you just have "raw signals". To find out what PC is being executed, you have to pull up the PC signal in the waveform viewer, and then all of the register signals, and then cross correlate that with the disassembly; in short, you get very intimate information about the design, but organizing that information is the responsibility of the person using the waveform viewer, and it can be tedious.
The insight this tool makes is that all of the data you need to have a gdb-like interface when debugging a simulated CPU is already in the waveform, jpdb just organizes and presents the data in a way that is easy for the developer to parse.
Very unlikely to me that this design would have comparable "raw" performance to a design that implements something closer to tomasulo's algorithm. The assumption that the latency of a load will be a l1 hit is a load bearing abstraction; I can imagine scenarios where this acts as a "double jeopardy" causing scheduling to lock up because the latency was mispredicted, but one could also speculate that isn't important because the workload is already memory bound.
There's an intuition in computer architecture that designs that lean on "static" instruction scheduling mechanisms are less performant than more dynamic mechanisms for general purpose compute, but we've had decades of compiler development since itanium "proved" this. Efficient computer (or whatever their name is) is doing something cool too, it's exciting to see where this will go
While it would be nice to have more elegant support for "modern" codegen in the sv/verilog/vhdl, the real unergonomic experiences are test bench design and integration. The only real options are (for sv, verilog, I have less experience with vhdl): use verilator and write your tb in cpp, use verilator and then write your testbench in cocotb, or you work at a chip design company and use one of the big 3's compilers and maybe you use UVM or cocotb. Verilator and cocotb are okay, but you're crossing a language boundar(ies) and referencing generated code -- it is both mechanical and complex to get any design working with it.
If sus had first class interfaces to create testbenches that could map to UVM or verilator, it would be much more interesting. Spade does some interesting things by having its own package manager, but doesn't (afaik) expose a ton within the language itself
There are clear reasons why a hardware company would use a graph compiler -- they think such an approach is higher performance, and makes tenstorrent look better on price per dollar when compared to competitors (read: nvda).
There is some legitimate criticism of TT here, their hardware is composed or simple blocks that compose into a complex system (5 separate CPUs being programmed per tensix tile, many tiles per chip), and that complexity has to be wrangled in the software stack -- paying that complexity in hardware so there is less of a VLIW model in software might remove a few abstractions.
There are a few reasons to separate build and test systems:
build systems struggle to express constrained random testing, and some build systems even struggle to express directed testing sweeps, which are common patterns in design verification.
The other reason is that testing is often treated as a "leaf node" in a build graph, and it's not possible to describe tests depending on other tests.
Overall, testing is a different problem than building, and circuit design often requires complex testing flows that build systems aren't designed for.
Reproducible seeding, and more generically having "test list arguments" are definitely something we have in our view, and wouldn't be outside the scope of the project
Tagging is an interesting problem -- do you find that you use any querying schemes emerge when trying to run a specific set of tests, or is running a test group named "A" normally suffice? Test tagging should be on the roadmap, thanks for calling it out.
Re:test weighting, unsure what you're describing exactly -- would you like to have a mechanism to describe how many times a "root test" has been duplicated with some sampling of random input parameters?