sed Part IV · Module 9 · RTL and Filelist Maintenance
Safe RTL Refactoring Boundaries
Chapter 9.2 renamed an identifier as carefully as sed allows: audited first, word boundaries, a file list derived from content, and a compile at the end. This chapter is about what that still cannot reach. The difference is not skill with regular expressions — it is that sed matches characters while a compiler resolves meaning, and in a hardware language the same characters mean different things depending on where they sit. The same rename from the last chapter produces four distinct outcomes on one small tree. One of them stops the build, which is the good case. The other three compile perfectly and are wrong.
Advanced20 min readsedPart IVRTLRefactoringTool Selection
Part IV · Module 9 · Chapter 9.3 · Safe RTL Refactoring Boundaries
1. Characters Versus Meaning
sed decides what to change by looking at characters. A compiler decides what things are by resolving them: this identifier is a port of this module, that one is a macro, this run of characters is inside a string literal, that one is a comment.
Those two views agree most of the time, which is why Chapter 9.2's procedure works on most renames. This chapter is about the cases where they disagree, and the uncomfortable property they share: sed is not wrong in any of them. It matched what you asked for. The mismatch is between what you asked for and what you meant.
2. One Rename, Four Outcomes
Everything below uses the tree from Chapter 9.2 and the same rename — axi_awvalid to m_axi_awvalid, with word boundaries, applied across the files that contain it.
The baseline is that the design builds:
iverilog -g2012 -I work -o /tmp/alu.out work/alu.sv
iverilog -g2012 -I work -o /tmp/fifo.out work/fifo.sv
iverilog -g2012 -I work -o /tmp/mu.out work/macro_user.svThree commands, no output, exit status 0 each.
3. The Four Blind Spots
A fifth case — a macro — is not on the diagram because it is not a misreading of one piece of text. It is the identifier being reachable under a second name, and it is the one the compiler catches.
4. The Loud One — a Macro
Apply the rename to the SystemVerilog sources:
sed -i '' 's/[[:<:]]axi_awvalid[[:>:]]/m_axi_awvalid/g' work/*.sv
iverilog -g2012 -I work -o /tmp/mu.out work/macro_user.svwork/macro_user.sv:3: error: Unable to bind wire/reg/memory `axi_awvalid' in `macro_user'
work/macro_user.sv:3: error: Unable to elaborate r-value: axi_awvalid
2 error(s) during elaboration.Exit status 2. The port became m_axi_awvalid; the header still says:
cat -n work/defs.svh 1 `define AXI_VLD m_axi_awvalid
2 `define DBG 1— after it was included in the rename. Before that it read `define AXI_VLD axi_awvalid, and line 3 of macro_user.sv referenced a signal that no longer existed.
This is the case to be grateful for. A macro is an indirection the compiler must resolve, so when the two halves disagree, elaboration fails loudly with a file and a line. Chapter 9.2 covered the mechanics; the point here is the category: a boundary that breaks the build is not the dangerous kind.
What makes macros genuinely hard is only partly this. `define AXI_VLD axi_awvalid is a simple alias, so renaming both sides works. A macro that builds an identifier — `define PORT(n) axi_n_valid — constructs names that never appear as text anywhere, and no amount of searching will find them.
5. The Silent One — Scope
Here is the second module:
cat -n work/fifo.sv 1 module fifo (
2 input logic clk,
3 input logic m_axi_awvalid
4 );
5 // NOTE: m_axi_awvalid here is a LOCAL port, unrelated to alu's
6 initial $display("fifo: m_axi_awvalid wired");
7 endmoduleThat is after the rename. Before it, line 3 declared a port called axi_awvalid — a completely separate signal that happens to share a name with alu's, as the comment on line 5 was there to tell you.
iverilog -g2012 -I work -o /tmp/fifo.out work/fifo.svExit status 0. It compiles. Of course it does — renaming a port and its uses consistently within a module is always valid. The design is still correct; it is just not the change you asked for.
sed cannot know these are different signals, because they are the same characters. Identity in a hardware language comes from scope — which module, which block, which generate branch — and scope is a property the compiler builds and a text matcher does not have.
6. The Other Silent Ones — Strings and Comments
Both are visible in the fifo.sv listing above, and both compiled.
The string. Line 6 was $display("fifo: axi_awvalid wired") and is now $display("fifo: m_axi_awvalid wired"). The design is unchanged; what the simulation prints is not.
That matters more than it sounds, because log text is frequently load-bearing. A regression checker that greps for a known message, a scoreboard that parses $display output, a waveform annotation, a message a verification engineer searches for by hand — all of them key on the literal text. The rename silently moved the goalposts, and the failure arrives later as "the checker stopped finding its line", with nothing connecting it to a signal rename from last week.
The comment. Line 5 said axi_awvalid here is a LOCAL port and now says m_axi_awvalid. Harmless to the build, and it is now documentation describing a signal under a name that — in this module — is at best confusing.
The comment case is the mildest and the most common. It is usually what you want: if the signal is renamed, references to it in prose probably should be too. The trouble is that sed cannot distinguish a comment about this signal from a comment that merely mentions the string, so it is right by luck rather than by analysis.
7. The Decision Rule
A rename is a sed job when all three of these hold, and each is one command:
1. the identifier is unique — no other name shares its prefix
grep -oh 'name[a-z_0-9]*' -r rtl/ | sort -u expect one line
2. it has no macro alias
grep -rn '`define.*\bname\b' rtl/ expect nothing
3. it appears in no string you depend on
grep -rn '"[^"]*name' rtl/ read every hitIf any of them fails, you are in one of two situations:
- A syntax-aware tool. An HDL-aware editor or linter that resolves scope can rename a signal properly. If you have one, this is not a sed job and never was.
- A compile-and-review loop. Run the rename, compile every target, read the entire diff rather than skimming it, and treat each string and comment hit as a decision rather than a side effect. That is defensible for a small tree and does not scale to a large one.
And a fourth case worth naming: do not rename it. A signal name that is merely unfashionable, in a block that is stable and verified, is not worth the risk this chapter describes. The question "what does this change buy" deserves an answer before the question "how do I make the change".
8. What This Does Not Apply To
The boundaries here are properties of source code: scope, macros, strings, comments. They exist because RTL is a language with a compiler.
The rest of Part IV mostly is not that. A simulation log, a synthesis report, a timing report — these are tool output. They are flat, line-oriented, generated rather than authored, and nobody compiles them. That puts them back in exactly the territory Chapter 9.1's filelist occupied, and it is why sed remains the right tool for them.
Module 10 — Simulation and Verification Automation starts there, and the distinction is worth carrying: editing what a tool reads needs this chapter's caution; editing what a tool wrote usually does not.
9. Think It Through
1. Why is the macro failure the best of the four?
Because it stops the build with a file and a line number. The compiler has to resolve a macro, so a half-applied rename is a contradiction it cannot ignore — exit 2, immediately. The other three compile cleanly, which means the only thing standing between them and a release is somebody reading a diff.
2. Two modules have a signal with the same name. Why can sed not keep them apart?
Because they are the same characters. Identity in a hardware language comes from scope, and scope is something a compiler constructs by parsing the design. A text matcher has no notion of which module it is inside — it has a line, and the line looks identical in both files.
3. Your rename compiled everywhere and a regression started failing a week later. What is the likely cause?
A string. Section 6's $display text changed, so anything keying on that literal — a checker, a log scraper, a grep in a run script — stopped matching. It compiles because the design is fine; the breakage is in the agreement between the design's output and something that reads it.
4. Write the three checks that say whether a rename is a sed job.
Uniqueness: grep -oh 'name[a-z_0-9]*' -r rtl/ | sort -u should return one line. No macro alias: grep -rn '`define.*name' rtl/ should return nothing. No load-bearing strings: grep -rn '"[^"]*name' rtl/ should return nothing you depend on. All three pass, and Chapter 9.2's procedure is sufficient.
5. Can you write a pattern that skips comments?
For whole-line comments, yes — a negated address. For a trailing comment on a line of code, no: the line is half code and half prose, and sed's unit is the line. Strings are harder still, because recognising one means tracking quote state, which is parsing. Chapter 8.4's third question sends parsing to a parser.
6. Which parts of Part IV does this chapter's caution apply to?
The parts that edit source. A simulation log, a synthesis report and a timing report are tool output — flat, generated, uncompiled — and they have none of these properties, which is why the rest of Part IV can use sed freely. The rule is whether something compiles what you are editing.
10. Summary
- sed matches characters; a compiler resolves meaning. Every boundary here is a place where the two disagree, and in none of them is sed wrong.
- One rename on one small tree produced four different outcomes.
- A macro breaks the build —
Unable to bind wire/reg/memory 'axi_awvalid', exit 2. This is the good case: loud, located, unmissable. A macro that constructs an identifier is worse, because the name never appears as text. - Scope fails silently. A second module's identically-named port was renamed too;
iverilogexit 0. The result is a valid design that is not the one you asked for, and nothing records it. - A string fails silently and changes behaviour.
$display("fifo: axi_awvalid wired")now prints a different name, so anything keying on that text stops matching — later, with no link back to the rename. - A comment fails silently and harmlessly, and is usually what you wanted — by luck, not analysis.
- You cannot exclude strings and comments with a pattern. Whole-line comments yield to an address; a trailing comment does not, and a string needs parsing.
- Three checks decide it: the identifier is unique, has no macro alias, and appears in no string you depend on. One command each.
- If a check fails: a syntax-aware tool, or a compile-and-review loop, or do not rename it at all.
- This caution applies to source, not to tool output — which is why the rest of Part IV is sed territory again.
That closes Module 9 and the source-editing half of Part IV. You can migrate a filelist, rename an identifier about as well as a text tool permits, and say precisely where that stops. Module 10 — Simulation and Verification Automation moves from what the tools read to what they write: updating run scripts across VCS, Xcelium and Questa, and turning UVM logs and whole regressions into something comparable.
Where this fits
Part of the sed curriculum.
