Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .github/workflows/general.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@ jobs:

- name: Setup Dart
uses: dart-lang/setup-dart@v1
with:
sdk: 3.6.2 # downgrade pending https://github.com/dart-lang/dartdoc/issues/3996

- name: Setup Node
uses: actions/setup-node@v4
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
[![License](https://img.shields.io/badge/License-BSD--3-blue)](https://github.com/intel/rohd/blob/main/LICENSE)
[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](https://github.com/intel/rohd/blob/main/CODE_OF_CONDUCT.md)

ROHD (pronounced like "road") is a framework for describing and verifying hardware in the Dart programming language.
ROHD (pronounced like "road") is a silicon-proven framework for describing and verifying hardware in the Dart programming language.

## Documentation

Expand Down
24 changes: 12 additions & 12 deletions doc/tutorials/chapter_6/00_combinational_logic.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,39 +85,39 @@ If.block([

### Add the condition inside the conditional Block

`Iff(condition, then: [])`: `Iff` Statement, if condition is matched, then execute the `then` condition.
`Iff(condition, [])`: `Iff` Statement, if condition is matched, then execute the `then` condition.

```dart
// template
If.block([
Iff(condition, then: [
Iff(condition, [
// You can wrap condition here
]),
]); // If.block

// example
If.block([
Iff(a.eq(0), then: [
Iff(a.eq(0), [
sum < 0
]),
]); // If.block
```

`ElseIf(condition, then: [])`: `ElseIf` Statement, if the condition in `Iff` is not matched, its will skip and look for next condition in `ElseIf` condition, then execute the `then`.
`ElseIf(condition, [])`: `ElseIf` Statement, if the condition in `Iff` is not matched, its will skip and look for next condition in `ElseIf` condition, then execute the `then`.

```dart
// template
If.block([
Iff(condition, then: []), // If statement
ElseIf(condition, then: []) // Else If Statement
Iff(condition, []), // If statement
ElseIf(condition, []) // Else If Statement
]); // If.block

// example
If.block([
Iff(a.eq(0), then: [
Iff(a.eq(0), [
sum < 1
]), // If statement
ElseIf(b.eq(0), then: [
ElseIf(b.eq(0), [
sum < 0
]) // Else If Statement
]); // If.block
Expand All @@ -127,17 +127,17 @@ If.block([

```dart
If.block([
Iff(condition, then: []), // If statement
ElseIf(condition, then: []), // Else If Statement
Iff(condition, []), // If statement
ElseIf(condition, []), // Else If Statement
Else([]) // execute this
]); // If.block

// example
If.block([
Iff(a.eq(0), then: [
Iff(a.eq(0), [
sum < 0
]), // If statement
ElseIf(b.eq(0), then: [
ElseIf(b.eq(0), [
sum < 1
]), // Else If Statement
Else([
Expand Down
4 changes: 2 additions & 2 deletions doc/user_guide/_docs/A07-bus-ranges-and-swizzling.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ var a = Logic(width:8),
e = Logic(width: 9);


// assign b to the bottom 3 bits of a
// assign b to be driven by the bottom 3 bits of a
b <= a.slice(2,0);

// assign d to the top bit of a
// assign d to be driven by the top bit of a
d <= a[7];

// construct e by swizzling bits from b, c, and d
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ last_modified_at: 2024-6-3
toc: true
---

Many of the basic built-in gates in Dart implement custom behavior. An implementation of the `NotGate` is shown below as an example. There is different syntax for functions which can be inlined versus those which cannot (the `~` can be inlined). In this case, the `InlineSystemVerilog` mixin is used, but if it were not inlineable, you could use the `SystemVerilog` mixin instead. Note that it is mandatory to provide an initial value computation when the module is first created for non-sequential modules.
Many of the basic built-in gates in ROHD implement custom behavior. An implementation of the `NotGate` is shown below as an example. There is different syntax for functions which can be inlined versus those which cannot (the `~` can be inlined). In this case, the `InlineSystemVerilog` mixin is used, but if it were not inlineable, you could use the `SystemVerilog` mixin instead. Note that it is mandatory to provide an initial value computation when the module is first created for non-sequential modules.

```dart
/// A gate [Module] that performs bit-wise inversion.
Expand Down
53 changes: 42 additions & 11 deletions doc/user_guide/_docs/B02-comparisons-with-alternatives.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,21 @@
title: "Comparison with Alternatives"
permalink: /docs/comparison-with-alternatives/
excerpt: "Comparison with Alternatives"
last_modified_at: 2024-01-04
last_modified_at: 2025-03-19
toc: true
---

There are a lot of options for developing hardware. This section briefly discusses popular alternatives to ROHD and some of their strengths and weaknesses.
There are a lot of options for developing hardware. This section briefly discusses some popular alternatives to ROHD and some of their strengths and weaknesses.

There is a conceptual difference between a *compiled hardware language* and an *embedded generator framework*. ROHD falls in the latter category because it is embedded into a software language (Dart) and is fundamentally a package or library for modelling hardware, simulating it, and generating outputs (e.g. SystemVerilog). By contrast, compiled hardware languages (e.g. VHDL, SystemVerilog) are independent langauges, and a compiler stack reads the code and converts it into some synthesizable/simulatable representation. There are benefits and downsides to each approach, and some solutions are a mixture between these.

- **Language expressiveness:** A compiled hardware language can add specific syntax that can make it easier and more natural to describe hardware, whereas an embedded framework would need to remain within the bounds of the programming language. However, compiled hardware languages can become limiting in their expressiveness, requiring additional language features to expand capabilities. Meanwhile, an embedded framework has the full software language available to control hardware construction.
- **Source alignment:** In a compiled language, it is reasonable to expect that the *names of variables* would have an impact on *generated outputs*. For example, if you name a `logic abc` for a register in SystemVerilog, you might reasonably expect a compiled netlist to still have that name `abc` somewhere in it. In a generator framework (with no [reflection](https://en.wikipedia.org/wiki/Reflective_programming)), you'd need to explicitly name a signal with a `String` for the name to show up in a generated output. The trade-off here is succinctness vs. flexibility. You can potentially write less repetitive code for simple descriptions in a compiled language, but you have greater flexibility and control in the generator framework for more configurable and complex designs.
- **Optimization and predictability:** A compiler can spend time optimizing/lowering a design which could potentially give you better outcomes in synthesis, simulation, etc. However, the more optimization there is, the harder it is to map results back to the original source code or predict how a change in the source will affect the output. For at least the near future, many engineers are still concerned with inspecting the generated outputs, so readability is important. More importantly, in ASIC development just before tape-out, there may be tiny bug fixes needed to the design that are done by hand rather than with full re-synthesis. If a small change in the source code can have a large or unpredictable set of changes in the final hardware, then these small manual edits become impossible. Additionally, a lot of the optimization for simulation and synthesis in standard EDA tools, once it's in SystemVerilog, is optimized for non-bit-blasted representations. Most compiler stacks are not producing good enough optimizations to outperform what those standard EDA tools already do, so the optimizations may not be adding a lot of value.
- **Algorithm Abstraction**: Some compiled languages (e.g. High-Level Synthesis) actually compile some algorithmic intent and constraints into a performant implementation, for example with automatic pipelining. In practice, this kind of approach requires re-validating the generated outputs since that's where the actual cycle-accurate hardware exists. Generator frameworks, by contrast, achieve abstraction via composition: automating the way you compose and construct pieces of hardware provides a layer of abstraction for building more complex designs. The generator framework approach grounds the designer in a hardware mindset instead of a more detached algorithmic one.
- **Determination of synthesizability:** Compiled languages usually still have a capability of doing some "generation" or parameterization that do not represent actual hardware operations (e.g. `generate` in SystemVerilog). They also might have pure-software constructs for verification purposes that are neither synthesizable nor generation-time compatible (e.g. SystemVerilog classes). This can create a blurry barrier for developers where it's unclear which language constructs can be used to represent or control generation of hardware, and which ones are non-synthesizable. A generator framework can eliminate such blurriness: hardware objects represent hardware, and software constructs used to generate those objects do not.
- **Automation Development:** In a compiled language, developing a new EDA tool often requires either/both parsing the language and/or generating the language. This is a substantial barrier for tool development. Even more concerning is that any new language advancement can only be leveraged once all required tools are able to handle it. This is a serious problem that often forces engineers to leverage only the subset of language features which all tools support. This also instills an aversion in developers to use new language features since things may work initially in one context, but then a tool incompatibility downstream may force a rewrite later. By contrast, a generator language can *reduce* barriers to EDA development since the object model already exists and generation is already handled.
- **Ecosystem:** A development ecosystem with editors, static analysis tools, reusable packages, documentation, and community is extremely valuable. Building a thriving ecosystem is very difficult, and the relative size of the hardware development community compared to that of software development only makes this more challenging. Even industry standards like SystemVerilog and VHDL have a very limited ecosystem (compared to Python, Dart, etc.). While compiled languages are on their own, embedded frameworks instantly gain the ecosystem of the underlying language, which is a huge advantage.

### SystemVerilog

Expand All @@ -15,26 +25,31 @@
- SystemVerilog is old, verbose, and limited, which makes code more bug-prone
- Integration of IPs at SOC level with SystemVerilog is very difficult and time-consuming.
- Validation collateral is hard to develop, debug, share, and reuse when it is written in SystemVerilog.
- Building requires building packages with proper `include ordering based on dependencies, ordering of files read by compilers in .f files, correctly specifiying order of package and library dependencies, and correct analysis and elaboration options. This is an area that drains many engineers' time debugging.
- Build and simulation are dependent on expensive EDA vendor tools or incomplete open-source alternatives. Every tool has its own intricacies, dependencies, licensing, switches, etc. and different tools may synthesize or simulate the same code in a functionally inequivalent way.
- Building requires building packages and libraries with proper `include ordering based on dependencies, ordering of files read by compilers in .f files, correctly specifiying order of package and library dependencies, and correct analysis and elaboration options. This is an area that drains many engineers' time debugging.
- Build and simulation are dependent on expensive EDA vendor tools or incomplete open-source alternatives. Every tool has its own intricacies, dependencies, licensing, switches, etc. and different tools may synthesize or simulate the same code in a functionally different way.
- Designing configurable and flexible modules in pure SystemVerilog usually requires parameterization, compile-time defines, and "generate" blocks, which can be challenging to use, difficult to debug, and restrictive on approaches.
- People often rely on perl scripts to bridge the gap for iteratively generating more complex hardware or stitching together large numbers of modules.
- Testbenches are, at the end of the day, software. SystemVerilog is arguably a terrible programming language, since it is primarily focused at hardware description, which makes developing testbenches excessively challenging. Basic software quality-of-life features are missing in SystemVerilog.
- Engineers often rely on perl or python scripts to bridge the gap for iteratively generating more complex hardware or stitching together large numbers of modules.
- Testbenches are, at the end of the day, software. SystemVerilog is arguably a poor programming language, since it is primarily focused at hardware description, which makes developing testbenches excessively challenging. Basic software quality-of-life features are missing in SystemVerilog.
- Mitigating the problem by connecting to other languages through DPI calls (e.g. C++ or SystemC) has it's own complexities with extra header files, difficulty modelling parallel execution and edge events, passing callbacks, etc.
- UVM throws macros and boilerplate at the problem, which doesn't resolve the underlying limitations.
- [UVM](https://en.wikipedia.org/wiki/Universal_Verification_Methodology) throws macros and boilerplate at the problem, which doesn't resolve the underlying limitations.

ROHD aims to enable all the best parts of SystemVerilog, while completely eliminating each of the above issues. Build is automatic and part of Dart, packages and files can just be imported as needed, no vendor tools are required, hardware can be constructed using all available software constructs, and Dart is a fully-featured modern software language with modern features.

You can read more about SystemVerilog here: <https://en.wikipedia.org/wiki/SystemVerilog>
You can read more about SystemVerilog here: <https://en.wikipedia.org/wiki/SystemVerilog>.

VHDL is another of the most popular HDLs, with many similar characteristics to Verilog <https://en.wikipedia.org/wiki/VHDL>.

### Chisel

Chisel is a domain specific language (DSL) built on top of [Scala](https://www.scala-lang.org/), which is built on top of the Java virtual machine (JVM). The goals of Chisel are somewhat aligned with the goals of ROHD. Chisel can also convert to SystemVerilog.

- The syntax of Scala (and thus Chisel) is probably less familiar-feeling to most hardware engineers, and it can be more verbose than ROHD with Dart.
- Scala and the JVM are arguably less user friendly to debug than Dart code.
- Scala and the JVM are arguably less user-friendly to debug than Dart code.
- Chisel is focused mostly on the hardware *designer* rather than the *validator*. Many of the design choices for the language are centered around making it easier to parameterize and synthesize logic. ROHD was created with validators in mind.
- Chisel generates logic that's closer to a netlist than what a similar implementation in SystemVerilog would look like. This can make it difficult to debug or validate generated code. ROHD generates structurally similar SystemVerilog that looks close to how you might write it.
- Chisel does not have a native hardware simulator in the same way that ROHD does. A variety of simulation approaches exist for Chisel. Some operate on the intermediate representations between the source code into the compiler stack. Most teams rely on other simulators (e.g. Verilator) to simulate the generated SystemVerilog, which leaves validation to the most of the same problems as verifying any other SystemVerilog design.
- Chisel has some amount of code reflection, meaning the structure of the generator code you write in Chisel (e.g. variable names) has an impact on the generated output. Conversely, in ROHD, the Dart code written is completely independent of the model which the code generates. This means that sometimes simpler designs can be a little more succinct in Chisel, but ROHD excels at scaling configurability.
- Parameterization and configuration of hardware in Chisel is often determined prior to module construction, similar to how SystemVerilog does it. In ROHD, you can dynamically determine port widths, module contents, etc. based on introspecting the signals connected to it (or anything else). This provides a lot more flexibility and reusability for hardware developed with ROHD.

Read more about Chisel here: <https://www.chisel-lang.org/>

Expand All @@ -43,8 +58,8 @@
There have been a number of attempts to create a HDL on top of Python, but it appears the MyHDL is one of the most mature options. MyHDL has many similar goals to ROHD, but chose to develop in Python instead of Dart. MyHDL can also convert to SystemVerilog.

- MyHDL uses "generators" and decorators to help model concurrent behavior of hardware, which is arguably less user-friendly and intuitive than async/await and event based simulation in ROHD.
- While Python is a great programming langauge for the right purposes, some language features of Dart make it better for representing hardware. Above is already mentioned Dart's isolates and async/await, which don't exist in the same way in Python. Dart is statically typed with null safety while Python is dynamically typed, which can make static analysis (including intellisense, type safety, etc.) more challenging in Python. Python can also be challenging to scale to large programs without careful architecting.
- Python is inherently slower to execute than Dart.
- While Python is a great programming langauge for the right purposes, some language features of Dart make it better for representing hardware. Above is already mentioned Dart's asynchronous programming capabilities, which don't exist in the same way in Python. Dart is statically typed with null safety while Python is dynamically typed, which can make static analysis (including IDE integration, type safety, etc.) more challenging in Python. Python can also be challenging to scale to large programs without careful architecting.
- Python is generally slower to execute than Dart.
- MyHDL has support for cosimulation via VPI calls to SystemVerilog simulators.

Read more about MyHDL here: <http://www.myhdl.org/>
Expand Down Expand Up @@ -74,6 +89,7 @@
PyMTL is another attempt at creating an HDL in Python. It is developed at Cornell University and the third version (PyMTL 3) is currently in Beta. PyMTL aims to resolve a lot of the same things as ROHD, but with Python. It supports conversion to SystemVerilog and simulation.

- The Python language trade-offs described in the above section on MyHDL apply to PyMTL as well.
- The general approach is similar to Chisel, described above, but with Python.

Read more about PyMTL here: <https://github.com/pymtl/pymtl3> or <https://pymtl3.readthedocs.io/en/latest/>

Expand All @@ -84,3 +100,18 @@
The cosimulation capabilities of cocotb are gratefully leveraged within the [ROHD Cosim](https://github.com/intel/rohd-cosim) package for cosimulation with SystemVerilog simulators.

Read more about cocotb here: <https://github.com/cocotb/cocotb> or <https://docs.cocotb.org/en/stable/>


Check failure on line 104 in doc/user_guide/_docs/B02-comparisons-with-alternatives.md

View workflow job for this annotation

GitHub Actions / Run Checks

Multiple consecutive blank lines

doc/user_guide/_docs/B02-comparisons-with-alternatives.md:104 MD012/no-multiple-blanks Multiple consecutive blank lines [Expected: 1; Actual: 2] https://github.com/DavidAnson/markdownlint/blob/v0.29.0/doc/md012.md
### Spade

### PipelineC

### Sus

### DFiant HDL

### BlueSpec

### Clash

### Lava?

Check failure on line 117 in doc/user_guide/_docs/B02-comparisons-with-alternatives.md

View workflow job for this annotation

GitHub Actions / Run Checks

Files should end with a single newline character

doc/user_guide/_docs/B02-comparisons-with-alternatives.md:117:9 MD047/single-trailing-newline Files should end with a single newline character https://github.com/DavidAnson/markdownlint/blob/v0.29.0/doc/md047.md
Loading
Loading