If you are learning RTL coding, looking for a useful language reference, or if you just want to see my thoughts on how to write RTL code, I would highly recommend my following two GitHub repositories:
Each tutorial is accompanied by detailed README files, offering insights into the recommended study order and highlighting key points for each section. The provided code examples showcase various approaches to implementing a circuit, often starting with less efficient or incorrect methods that highlight common mistakes. Be sure to read the comments in the code, where I explain my thoughts.
My intent for these tutorials is to stay current with the latest information. As I find time, I will add new topics. As new insights emerge on existing topics, I will update the content. If there’s a specific topic you would like to see covered, feel free to reach out to me directly or add your suggestions in the comments below.
What makes these different from the numerous other tutorials online?
While many tutorials focus on explaining language constructs and their usage, crucial information no doubt, they may inadvertently overlook the impact on the resulting circuit. My tutorials are built around my “design the circuit, then write the code” methodology, for which I’ve shown that similar simulation behaviors can yield vastly different synthesis results.
By advocating for circuit design before code writing, by the time we get to writing code, we already know the type of logic we want to synthesize. In line with this strategy, my tutorials predominantly showcase various ways of writing code for synthesizing these specific types of logic. My approach complements existing tutorials by not delving as deeply into individual coding constructs. Instead, I demonstrate recommended methods for using these constructs to synthesize specific logic types.
It’s essential to learn various constructs, but the key is to ensure your code synthesizes to your intended circuit. Blindly adopting constructs solely based on their simulation behavior may not align with your circuit goals. Always verify that both the simulation behavior and the synthesized circuit match your intentions.
What language do I recommend learning?
Answering this question is outside the scope of this post, but I will be comparing VHDL and SystemVerilog in many future posts. Personally, I use SystemVerilog for most of my projects, but there are features of each language that I wish existed in the other. My tutorials largely mirror each other, so if you know one language and would like to learn another, you can quickly see how to accomplish the same circuit in the other language. I’ve had students that only knew VHDL get hired into senior positions requiring SystemVerilog from this approach.
If you are serious about RTL coding, I recommend starting with VHDL because it protects you at compile time from a lot of potential problems that SystemVerilog allows. VHDL’s extra protection can be annoying, which is why many people prefer SystemVerilog, but the benefits become evident when you realize a day-long debugging session could have been caught by a compiler error in VHDL. However, with sufficient experience in SystemVerilog, you can learn to usually avoid problematic practices that VHDL protects against. Moreover, you can of course combine both in mixed-language designs to get the best of both worlds, albeit with some limitations.
If you plan to seriously pursue verification, absolutely learn SystemVerilog. I exclusively write testbenches in SystemVerilog. Even when I write synthesizable VHDL, I test it with SystemVerilog testbenches. It’s a powerful tool for pursuing comprehensive verification.
Is there a style guide I’d recommend for each language?
No, I don’t prescribe a specific style guide for each language. Despite numerous attempts throughout my career to define a precise style guide, I found myself abandoning each endeavor shortly after initiation. I’ve also explored style guides proposed by others, adopting ideas I resonate with and vehemently opposing others.
The reality is that exceptions to style rules will always exist. I prioritize consistency and ease of visualizing the circuit from the code, and there are various ways to achieve this. It’s crucial to be flexible enough to conform to whatever style your team has adopted, fostering effective collaboration and cohesion within the development process.
My own personal style preferences have evolved over time and will continue to do so. A few times a year, I try new things for a few weeks and see if they are worth adopting.
While I will delve into more detailed posts on my thoughts about coding style, one principle I emphasize is naming signals that synthesize to registers appropriately. Personally, I use an ‘_r’ suffix, but others may prefer a ‘_q’ suffix. The specific naming convention is less crucial than the recognition that a signal is intended to be a register. This practice not only encourages circuit design by determining the number of registers but also aids in timing analysis and optimization by explicitly indicating the register locations.
I hope you find this useful!
Nicely written, Greg.
What are your thoughts on the coding style where all the next_state assignments are alone in an always block, and the outputs are in a separate assignment?
For instance, in mealy_2p, pull all the ‘en’ assignments out and add:
assign en = ( (state_r == COMPUTE) && !ack ) ? 1’b1 : 1’b0;
…and something similar for done.
Perfectly fine to do that. If output is easier to define separately from the state descriptions, I’ll often do it. That example on the tutorial is meant to demonstrate a straightforward translation from the FSM diagram into code, but as long as a different set of code synthesizes to the same thing (or something better), I’ll sometimes use it.
Other parts of the tutorial demonstrate 1, 2, 3, and 4-process models that I’ve seen people use. Some people use 3-process models that separate the next-state and output logic into separate always blocks, with a 3rd for the state register. Your suggestion is basically identical to that, just using an assignment instead of the extra always block.
The output logic and next-state logic are ultimately just combinational logic, so there are numerous coding styles that work.
Thanks.
Also, I’m embarrassed to admit I wasn’t familiar with always_comb and always_ff until I saw this tutorial. I’ve looked them up and will start using them going forward.
Thanks for sharing!