{"id":3668,"date":"2026-07-15T11:31:08","date_gmt":"2026-07-15T03:31:08","guid":{"rendered":"http:\/\/manufacturing.wiki\/?p=3668"},"modified":"2026-07-15T11:31:08","modified_gmt":"2026-07-15T03:31:08","slug":"specification-for-writing-comments-for-field-programmable-gate-array-code","status":"publish","type":"post","link":"http:\/\/manufacturing.wiki\/index.php\/2026\/07\/15\/specification-for-writing-comments-for-field-programmable-gate-array-code\/","title":{"rendered":"Specification for Writing Comments for Field Programmable Gate Array Code"},"content":{"rendered":"\n<h1 class=\"wp-block-heading\">FPGA Code Commenting Standards: Writing Comments That Actually Help You Six Months Later<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Nobody reads comments when they write them. That is the truth. You write a comment, move on, and the next time you see it is when something breaks at 3 AM and you are staring at your own code wondering what you were thinking. Good comments do not explain what the code does \u2014 they explain why it does it that way. Bad comments restate the obvious and rot over time, becoming lies that actively mislead.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This guide covers how to write comments in HDL that survive contact with reality. Not the textbook kind of commenting. The kind that saves you from debugging your own past self.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The Real Purpose of Comments in HDL<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Most coding standards treat comments as documentation for other people. That is backwards. In FPGA work, the primary audience for your comments is you, six months from now, when the original context has evaporated from your brain.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Code tells you what happens. Comments tell you why it happens that way, what was tried before, and what constraints forced the decision. If a comment does not answer at least one of those three questions, delete it. It is noise.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A common trap: commenting every line of code. That is not documentation, that is clutter. If your code needs a comment on every line to be understood, the code itself is the problem. Refactor the code instead of explaining it.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What Belongs in a Header Comment<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Every module needs a header comment. Not a one-line description \u2014 a block that answers specific questions.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>verilog1\/\/=============================================================================\n2\/\/ Module: uart_tx\n3\/\/\n4\/\/ Description:\n5\/\/   UART transmitter with 8N1 framing, 1 stop bit, no parity.\n6\/\/   Output holds idle high until tx_start is asserted for one clock cycle.\n7\/\/\n8\/\/ Design Notes:\n9\/\/   - Baud rate generator uses a fractional divider to hit 115200 exactly\n10\/\/     at 50 MHz input clock. Rounding error is 0.16%, well within spec.\n11\/\/   - tx_busy is registered to avoid glitches on the output pin.\n12\/\/   - This module replaces the original blocking FSM version (rev 1.2)\n13\/\/     because the blocking version caused timing violations at 200 MHz.\n14\/\/\n15\/\/ Author: j.doe\n16\/\/ Date: 2025-11-14\n17\/\/ Revision History:\n18\/\/   1.3 - Fixed metastability on tx_start input (2026-01-20)\n19\/\/   1.2 - Original blocking FSM (removed due to timing)\n20\/\/   1.1 - Initial version\n21\/\/=============================================================================\n22module uart_tx (\n23  input  wire       clk,\n24  input  wire       rst_n,\n25  input  wire       tx_start,\n26  input  wire &#91;7:0] tx_data,\n27  output reg        tx_out,\n28  output reg        tx_busy\n29);\n30<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This header does five things. It names the module. It describes the function in one sentence. It records design decisions that are not obvious from the code. It documents revision history with dates and reasons. It gives you a reason to care about each line when you come back later.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The revision history is the part most people skip. It is also the part you will thank yourself for when a field report says &#8220;version 1.2 has a bug&#8221; and you need to know exactly what changed between 1.2 and 1.3.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Inline Comments: The Rules That Matter<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Explain Non-Obvious Choices, Not Obvious Actions<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Bad inline comment:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>verilog1<strong>always @<\/strong>(posedge clk) begin  \/\/ On clock edge\n2  if (rst_n) begin           \/\/ If reset is low\n3    counter &lt;= 0;            \/\/ Reset counter to zero\n4  end\n5end\n6<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This comment says nothing. The code is self-explanatory. Delete all three comments.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Good inline comment:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>verilog1<strong>always @<\/strong>(posedge clk) begin\n2  if (rst_n) begin\n3    counter &lt;= 0;\n4  end else if (counter == 99) begin  \/\/ 99 not 100: off-by-one fix for 100 Hz tick\n5    counter &lt;= 0;\n6  end else begin\n7    counter &lt;= counter + 1;\n8  end\n9end\n10<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The comment explains why 99 instead of 100. That is the kind of detail that vanishes from memory in a week. Write it down.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Flag Workarounds and Temporary Fixes<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">FPGA designs accumulate hacks. A timing fix that uses an extra register. A workaround for a tool bug. A constraint that should not be needed but is. These need comments because they look like mistakes to anyone reading the code later.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>verilog1\/\/ Workaround: tool infers a latch here if we do not assign a default.\n2\/\/ This was reported as bug #4471 in the 2024.2 release. Fixed in 2025.1.\n3\/\/ Remove this comment and the default assignment when upgrading tools.\n4<strong>always @<\/strong>(*) begin\n5  state = IDLE;  \/\/ Default assignment prevents latch inference\n6  case (state)\n7    IDLE:  if (start) state = RUN;\n8    RUN:   if (done)  state = IDLE;\n9  endcase\n10end\n11<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The comment tells a future reader three things. This is a workaround, not intended behavior. It is tied to a specific tool version. It has an expiration date. That is invaluable context.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Mark TODO and FIXME Tags Consistently<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Use TODO for things you know need to be done. Use FIXME for things that are broken. Do not mix them up.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>verilog1\/\/ TODO: Replace this with a proper FIFO when area budget allows (tracked in JIRA-142)\n2reg &#91;7:0] data_buffer &#91;0:15];  \/\/ 16-deep buffer, max throughput 1 word\/cycle\n3\n4\/\/ FIXME: This path fails timing at 150 MHz. Adding a pipeline register breaks\n5\/\/ the protocol. Need to talk to the system team about relaxing the spec.\n6assign data_valid = (state == ACTIVE) &amp;&amp; !fifo_empty;\n7<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">TODO items are intentional debt. FIXME items are broken things you know about. Both are searchable. If your editor supports it, configure a task list that scans for TODO and FIXME across all HDL files. You will be surprised how many of these accumulate.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Commenting Testbenches Differently<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Testbenches are not synthesis code. They are throwaway scripts that verify correct behavior. But &#8220;throwaway&#8221; does not mean &#8220;uncommented.&#8221; A testbench without comments is a puzzle you have to solve every time you run it.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Describe the Test Scenario, Not the Stimulus<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Do not write:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>verilog1\/\/ Set reset high\n2rst_n = 1;\n3\/\/ Wait 10 cycles\n4repeat(10) @(posedge clk);\n5\/\/ Set reset low\n6rst_n = 0;\n7<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Write:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>verilog1\/\/ Release reset after 10 clock cycles to let PLL lock\n2rst_n = 1;\n3repeat(10) @(posedge clk);\n4rst_n = 0;\n5\n6\/\/ Apply a burst of 64 bytes with random gaps to stress the FIFO\n7for (int i = 0; i &lt; 64; i++) begin\n8  @(posedge clk);\n9  tx_data = $random;\n10  tx_valid = 1;\n11  tx_start = 1;\n12  @(posedge clk);\n13  tx_start = 0;\n14  \/\/ Random gap of 1-5 cycles between transfers\n15  repeat($urandom_range(1, 5)) @(posedge clk);\n16end\n17<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The second version tells you what the test is actually checking \u2014 FIFO behavior under bursty traffic with variable gaps. The first version just tells you what signals toggle, which is obvious from the code.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Annotate Expected Behavior in Assertions<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">If your testbench uses assertions, comment what each one is catching:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>verilog1\/\/ Bus should never be driven by both master and slave at the same time\n2assert property (@(posedge clk) !(m_valid &amp;&amp; s_valid))\n3  else $error(\"Bus contention detected at time %0t\", $time);\n4\n5\/\/ Response must come within 4 cycles of a request, or the timeout counter fires\n6assert property (@(posedge clk) (req |-&gt; ##&#91;1:4] resp))\n7  else $error(\"Response timeout at time %0t\", $time);\n8<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The comment explains the protocol rule. The assertion enforces it. Together they make the test self-documenting.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What Never Needs a Comment<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Some things are so standard that commenting them adds noise.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Standard port names like&nbsp;<code>clk<\/code>,&nbsp;<code>rst_n<\/code>,&nbsp;<code>valid<\/code>,&nbsp;<code>ready<\/code>&nbsp;do not need comments. Everyone knows what they mean.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Obvious bit slicing like&nbsp;<code>data[7:0]<\/code>&nbsp;does not need a comment saying &#8220;lower 8 bits of data.&#8221;<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A state machine with named states like&nbsp;<code>IDLE<\/code>,&nbsp;<code>RUN<\/code>,&nbsp;<code>DONE<\/code>&nbsp;does not need a comment per state unless the transition logic is unusual.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If you find yourself writing a comment to explain something that any HDL engineer would recognize instantly, stop. The comment is not helping. It is just taking up screen space.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Maintaining Comments Over Time<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Comments rot. A comment that was accurate when written becomes misleading after a code change. The only way to prevent this is to treat comments as part of the code change.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">When you modify a module, read every comment in that module. Ask: is this still true? If the answer is no, update the comment or delete it. A stale comment is worse than no comment. It actively misleads.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Make this a code review checklist item. &#8220;Did the comments get updated?&#8221; should be as standard as &#8220;did the tests pass?&#8221; If your team does not enforce this, your comments will become a graveyard of outdated assumptions.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">A Commenting Template You Can Steal<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">If you do not want to think about this every time you start a new module, copy this template and fill it in:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>verilog1\/\/=============================================================================\n2\/\/ Module: &#91;NAME]\n3\/\/\n4\/\/ Function:\n5\/\/   &#91;One sentence. What does this module do?]\n6\/\/\n7\/\/ Interface:\n8\/\/   Inputs:  &#91;list with brief purpose]\n9\/\/   Outputs: &#91;list with brief purpose]\n10\/\/\n11\/\/ Design Notes:\n12\/\/   - &#91;Why was it implemented this way?]\n13\/\/   - &#91;What are the known limitations?]\n14\/\/   - &#91;What was tried before and why did it fail?]\n15\/\/\n16\/\/ Timing:\n17\/\/   - &#91;Critical paths and how they were handled]\n18\/\/\n19\/\/ Revision History:\n20\/\/   &#91;date] - &#91;what changed and why]\n21\/\/=============================================================================\n22<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Fill it in once when you write the module. Update it when you change the module. Your future self will spend less time archeology and more time building.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">ChipApex is a global distributor of electronic components: ICs, semiconductors, passives &amp; interconnects. Source active &amp; obsolete parts with wholesale pricing, fast RFQ response, and worldwide delivery.Official website address:chipapex.com<\/p>\n","protected":false},"excerpt":{"rendered":"<p>FPGA Code Commenting Standards: Writing Comments That A &hellip;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-3668","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"http:\/\/manufacturing.wiki\/index.php\/wp-json\/wp\/v2\/posts\/3668","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/manufacturing.wiki\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/manufacturing.wiki\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/manufacturing.wiki\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/manufacturing.wiki\/index.php\/wp-json\/wp\/v2\/comments?post=3668"}],"version-history":[{"count":1,"href":"http:\/\/manufacturing.wiki\/index.php\/wp-json\/wp\/v2\/posts\/3668\/revisions"}],"predecessor-version":[{"id":3669,"href":"http:\/\/manufacturing.wiki\/index.php\/wp-json\/wp\/v2\/posts\/3668\/revisions\/3669"}],"wp:attachment":[{"href":"http:\/\/manufacturing.wiki\/index.php\/wp-json\/wp\/v2\/media?parent=3668"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/manufacturing.wiki\/index.php\/wp-json\/wp\/v2\/categories?post=3668"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/manufacturing.wiki\/index.php\/wp-json\/wp\/v2\/tags?post=3668"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}