惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Apple Machine Learning Research
Apple Machine Learning Research
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
S
Security @ Cisco Blogs
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Cisco Talos Blog
Cisco Talos Blog
Cyberwarzone
Cyberwarzone
SecWiki News
SecWiki News
Webroot Blog
Webroot Blog
L
LINUX DO - 最新话题
V
Vulnerabilities – Threatpost
T
Troy Hunt's Blog
Cloudbric
Cloudbric
L
LINUX DO - 热门话题
Google DeepMind News
Google DeepMind News
H
Heimdal Security Blog
S
Schneier on Security
NISL@THU
NISL@THU
The Hacker News
The Hacker News
Attack and Defense Labs
Attack and Defense Labs
A
Arctic Wolf
V2EX - 技术
V2EX - 技术
Security Latest
Security Latest
AWS News Blog
AWS News Blog
Scott Helme
Scott Helme
W
WeLiveSecurity
S
Secure Thoughts
Y
Y Combinator Blog
GbyAI
GbyAI
H
Hackread – Cybersecurity News, Data Breaches, AI and More
博客园 - Franky
量子位
人人都是产品经理
人人都是产品经理
雷峰网
雷峰网
K
Kaspersky official blog
P
Privacy & Cybersecurity Law Blog
T
Tenable Blog
The GitHub Blog
The GitHub Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
J
Java Code Geeks
Vercel News
Vercel News
Recent Commits to openclaw:main
Recent Commits to openclaw:main
Schneier on Security
Schneier on Security
云风的 BLOG
云风的 BLOG
小众软件
小众软件
Engineering at Meta
Engineering at Meta
宝玉的分享
宝玉的分享
C
CERT Recently Published Vulnerability Notes
Security Archives - TechRepublic
Security Archives - TechRepublic
C
CXSECURITY Database RSS Feed - CXSecurity.com
P
Palo Alto Networks Blog

Lobsters

Lunacy | Red Vice CIFSwitch: a non-universal Linux local root vulnerability RIPE NCC session fixation: poaching logins with an Atlas probe GNOME 2.20 but its Web Components Agentic Search for Context Engineering – Leonie Monigatti Garnix is shutting down [not OC] akashina.tngl.sh/jjc Concerning Emacs (and Jazz) Nitpicking the shell history scene in ‘Tron: Legacy’ What's cooking on SourceHut? Q2 2026 The tenth OpenPGP email summit Package managers that package package managers Clojure on Fennel part three: parsing WordPress at 23 Finding Miscompiles for Fun, Not Profit GitHub - creusot-rs/creusot: Creusot helps you prove your Rust code is correct. Announcing Rust 1.96.0 | Rust Blog A Love Letter to Neovim sqlite AGENTS.md Am I a Bad Friend? CSS vs. JavaScript • Josh W. Comeau Erlang Ecosystem Foundation - Supporting the BEAM community A brief note about slot access cost in Common Lisp Keyboard latency probe Rethinking the GNOME clipboard issues Tech Notes: Theseus: translating win32 to wasm Fast is better than slow Content-addressed Rust builds (or, what kache actually caches) Intent to Prototype: Embedding API Canada’s Bill C-22 and the security cost of collecting more data 5 PostgreSQL locking behaviors that trip people up okmij.org Stop advertising in your commits! | AksDev GitHub - mplsllc/macsurf: A modern web browser for Classic Mac OS 9 PowerPC. Real CSS3, ES5 JavaScript, native HTTPS — built with CodeWarrior on the Carbon API. Introducing DoomBench - Can Your Data Stack Run DOOM? What are some of your favourite developer tools? Building a Scalable Ingestion Pipeline with Temporal (Part 1) Converting shallow Git bundles into normal repositories Are you a member of any professional associations? What is a harmonic? An interactive comic about additive synthesis How Virtual Tables Work in the Itanium C++ ABI Using SwiftUI to Build a Mac-assed App in 2026 Rust (and Slint) on a jailbroken Kindle. ~jack/lambda-on-lambda - Serverless Haskell on AWS - sourcehut git Human proof for FOSS contributions Extremely simple internet radio controlled via IRC Announcing BABLR Splitting Konsole views from Helix to run tools | AksDev GitHub - yugr/rust-slides Serving files over HTTP three ways: synchronous, epoll, and io_uring update docs with information about building with build.py (#979) · astral-sh/python-build-standalone@c9c40c5 A Simple Makefile Tutorial On C extensions, portability, and alternative compilers Switching to Colemak | Pedro Alves Just How Bad Was The Intel IAPX432? Nix's Substituter List Is Not a Routing Table Accelerating copy_if using SIMD Lambda on Lambda: Serverless Haskell on AWS | Blog Announcing feed-repeat v1.0 Scaling Akvorado BMP RIB with sharding EYG news: A host of CLI improvements, new guides and new effects The social contract of writing JS Crossword C array types are weird; and related topics Flatpak will depend on systemd – OSnews Migrating from Go to Rust | corrode Rust Consulting A portentous reunion Vivado Licensing Options How my minimal, memory-safe Go rsync steers clear of vulnerabilities the entropy layer of a wavelet codec, on its own GitHub - nferhat/fht-compositor: A dynamic tiling Wayland compositor. Debian SE Linux and PinTheft Does bulk memmove speed up std::remove_if? (No.) 声明式部分更新 | Blog | Chrome for Developers Fully in-browser container builds Dianne Skoll's Web Site - Remind The Architecture of Open Source Applications (Volume 1)Berkeley DB Pardon MIE? - ironPeak Blog “Long-Term Support” doesn’t mean what you think Jira IS Turing-Complete May I recommend thinking of Emacs as your Fortress of Solitude hershey Floodgap Gopher-HTTP gateway gopher://thelambdalab.xyz/1cuneiforth/ HP QuickWeb, Singular And Pointless That one time I used Go panics for flow control A new suite of modern tools coming for editing and publishing RFCs From the Tabletop… The Digital Antiquarian Building a Host-Tuned GCC to Make GCC Compile Faster Are we self-sovereign PKI yet? Claw Patrol: an open-source security firewall for agents | Deno Revised^7 Report on Scheme, Large: Procedural Fascicle Draft is now public A Network Allow-List Won't Stop Exfiltration — André Graf From AFSK to Goertzel – µArt.cz Software For My New Home Server Introducing Neptune: Direct3D virtualization for QEMU AI Agent Bankrupted Their Operator While Trying to Scan DN42 - Lan Tian @ Blog mimalloc: A new, high-performance, scalable memory allocator for the modern era Making wl_shm fast The Soul of Maintaining a New Machine - Third Draft | Books in Progress What is Git made of?
Back to the Building Blocks’ Building Blocks
cs.cornell.e · 2026-05-27 · via Lobsters

If there are hardware engineers who love Verilog, I haven’t met them. Almost universally, the attitude toward Verilog seems to be that it’s frustrating, ridiculous, error-prone, and the only pragmatic choice.

Verilog is inescapable because it is the input format to essentially every EDA tool. Its centrality means that it is a de facto intermediate representation implementing for every other HDL: even if you prefer Bluespec, Chisel, Amaranth, or Spade, they all have to compile to Verilog to interact with the rest of the hardware world.

I am worried that Verilog’s flaws will be the cause of a new wave of hardware bugs. There is an analogy to the problems with C and C++: as designing custom hardware becomes more popular, we risk allowing a dangerous HDL to proliferate and fester in the same way that memory-unsafe programming languages have in software.

I don’t know yet what the analog of memory safety is for hardware bugs, or even if there will be a single dominant defect category. Footguns abound in Verilog, though, so there are many good candidates. This post makes the case that we should invest in better understanding the problems with Verilog so that future HDLs can avoid them.

On Building Blocks

As an American programming languages nerd, I believe that “Back to the Building Blocks” was one of the most exciting developments of the last decade. It’s a 2024 report from the White House Office of the National Cyber Director that makes the case for memory safety. It calls for critical infrastructure to move on from memory-unsafe languages like C and C++, and it even mentions Rust by name as a promising alternative.

“Back to the Building Blocks” didn’t break new ground: by 2024, it was obvious to all reasonable people that memory safety was a huge problem. It was exciting because Joe Biden was saying the same things that we had all been saying for years.1 I’m not a very patriotic person, but my heart soars like a majestic bald eagle when I read stuff like this in a government report:

Despite rigorous code reviews as well as other preventive and detective controls, up to 70 percent of security vulnerabilities in memory unsafe languages patched and assigned a CVE designation are due to memory safety issues.

And I can hear the national anthem playing when the report says:

For new products, choosing to build in a memory safe programming language is an early architecture decision that can deliver significant security benefits. Even for existing codebases, where a complete rewrite of code is more challenging, there are still paths toward adopting memory safe programming languages by taking a hybrid approach.

God bless America. “Back to the Building Blocks” distills a hard-to-refute syllogism along these lines:

  1. Correctness matters.
  2. There exist large classes of bugs with similar root causes.
  3. Some languages lead to a higher frequency of these bug classes than other languages.
  4. We should therefore see these bugs as the “fault” of the language, not the programmer.
  5. Languages that are harder to use but dramatically reduce the frequency of these bugs may be worth it.

In the original report, this “Building Blocks” argument was about C. But I believe the same reasoning applies to Verilog.

The Claim (Weak and Strong Forms)

I’ll state my thesis in a weak form and a strong form; you can pick which level you want to consider. The weak form is:

Verilog causes lots of bugs.

And the strong form is:

The next “Building Blocks” crisis will happen in hardware, and it will be Verilog’s fault.

In other words: at the same moment that we’re beginning to get a handle on memory safety, we’re producing a lot more Verilog code. So the next scourge of avoidable bugs could occur in hardware. Better HDLs could dramatically reduce the frequency of these bugs.

Verilog’s problems are not new, but until now, hardware design methodologies have attenuated their harm. The traditional way to develop hardware—the kind of process that big CPU vendors use—mitigates Verilog’s problems in part by expending a ridiculous amount of resources on verification. This observation is hard to justify with concrete evidence, but consider this somewhat dubious report from an industry consortium that claims that, in CPU design projects, the ratio of verification engineers to design engineers is 5:1. A terrible HDL matters less when you have a safety net like that.

But today, more people want to design custom, application-specific hardware. Specialized, lower-volume hardware projects will not (and should not) use the same engineering process as Apple’s next iPhone SoC. The emerging long tail of cheaper, lighter-weight hardware design projects will be more vulnerable to Verilog’s problems.

Some Cheap Shots at Verilog

The main point of this post is to call for systematic study of Verilog’s implications for hardware correctness, not to pick on specific flaws I personally love to hate. But I can’t resist shooting a few fish in this barrel.

The root of Verilog’s problems is that it was not designed for implementing hardware. It was originally developed as a DSL for writing event-based simulators of digital logic. Later, logic synthesis tools repurposed Verilog for generating real netlists. Many problems with Verilog stem from the confusing boundaries between simulation and implementation:

  • There is an ill-defined “synthesizable” subset. Tools can’t agree on what this subset is, but we can all agree that not all of Verilog can be sensibly translated into hardware.
  • Verilog requires load-bearing linters. Serious hardware design shops pay for extremely expensive commercial tools that keep their engineers within the Verilog subset that their toolchain can handle.

To get concrete, let’s look at three specific footguns in Verilog: inferred latches, the semantics of the “don’t care” value, and the absence of cycle-level timing information. (As a warning, the latter is a self-serving complaint that motivates some research I co-authored.)

Inferred Latches

Verilog, people say, uses the register-transfer level (RTL) abstraction. If this was the first thing I knew about Verilog, I would assume it works like this: the language has special, built-in constructs for state elements like latches, registers, and memories. You write a Verilog program by describing how to compute the values of all state elements on cycle n+1 based on their values on cycle n. In an RTL language, it seems obvious that all the registers should be explicit and clearly separated from stateless combinational logic.

Because of Verilog’s roots in event-based simulation, that’s not how it works. Instead, variables might be stateful and might be stateless depending on how they are used. For example, here’s one way to write a 32-bit latch in Verilog:

module latch (
  input  wire        en,
  input  wire [31:0] data_in,
  output reg  [31:0] data_out
);
  always @(*) begin
    if (en)
      data_out = data_in;
  end
endmodule

There’s an input data port, an output data port, and a 1-bit enable signal that decides whether the latch should take on a new value. The Verilog semantics say that data_out must be stateful because it is assigned conditionally. That is, because data_out doesn’t get assigned on cycles when en == 0, it keeps its old value—implying that it requires a stateful circuit for its implementation. Verilog engineers call this an “inferred latch.”2

The footgun here is that it’s disturbingly easy to accidentally create a latch. Consider this contrived implementation of an XOR gate:

module funny_xor (
  input   wire [1:0] in_bits,
  output  reg        out
);
  always @(*) begin
    if (in_bits == 2'b00)
      out = 1'b0;
    else if (in_bits == 2'b01)
      out = 1'b1;
    else if (in_bits == 2'b10)
      out = 1'b1;
    else if (in_bits == 2'b11)
      out = 1'b0;
  end
endmodule

This module takes its two inputs on a single 2-bit port, in_bits. This circuit is stateless because our else if cascade covers all the cases: out is assigned in every situation.

But what happens if you forget one of these cases? For example, let’s delete these two lines:

else if (in_bits == 2'b10)
  out = 1'b1;

The module is now stateful, and the hardware implementation requires a latch circuit. Spooky.

Nonsense Semantics for X

HDLs typically need a way to represent don’t-care values, usually written as X. A little like undefined behavior (in a good way), X lets you convey to the optimizer that your specification only covers certain cases, and that it’s free to do what it wants in others.

While X is a good and useful idea, Verilog’s semantics for it don’t make sense. Here’s how it should work: X represents a bit that might be 0 or 1, and we don’t know which.3 By that definition, it would make sense that X would propagate through Verilog’s math operators, like this:

module optimism1;
  reg [31:0] in;
  reg [31:0] out;

  initial begin
    in = 32'bx;
    $display("in  = %d", in);

    out = in * 2 + 4;
    $display("out = %d", out);
  end
endmodule

And indeed, simulating this module shows that out is also a don’t care value, just like in:

$ iverilog optimism1.v && ./a.out
in  =          x
out =          x

All is right with the world. Verilog also has a ternary operator, and we can try using X in its condition:

out = (in * 2 + 4) > 42 ? 32'b1 : 32'b0;

Gratefully, the result of an undefined condition is itself X:

$ iverilog optimism2.v && ./a.out
in  =          x
out =          X

Because out might be 1 and it might be 0, it’s sensible that our simulator reports is value is X. Let’s go one step farther and rewrite that ternary operator using the long-form if equivalent:

if ((in * 2 + 4) > 42)
  out = 32'b1;
else
  out = 32'b0;

And try simulating again:

$ iverilog optimism3.v && ./a.out
in  =          x
out =          0

Wat.

This footgun has a name: X-optimism. That’s the standard behavior for Verilog: if treats X as false, which incorrectly makes conditional assignments appear defined when they are actually unknown. Tragically, this behavior was apparently intentional:

This optimistic behavior of if-else was a deliberate decision by Moorby. He realized that in such a procedural context, evaluating both sides of the if-else expression could be enormously complicated. Reducing optimism requires execution time in the simulator.

Feel free to call me a PL nerd for saying this, but I don’t think simulation performance is a good justification for broken semantics.

Unsurprisingly, researchers have explored how X optimism can lead to sneaky security problems because your simulator lies to you about what how final circuit will behave.

Timing Trouble

This last footgun is a little different because it’s the problem we have tried to address in some of our recent research on safer HDLs (and it’s borrowed from that paper). It’s not just a Verilog thing; it’s a danger in every major HDL I know of. It’s also my best guess at this point for a bug category that’s analogous to memory safety problems in software.

Let’s say you already have a multiplier and an adder:

module Mul32 (
  input  wire [31:0] in_a,
  input  wire [31:0] in_b,
  output reg  [31:0] out,
);
  // ...
endmodule

module Add32 (
  input  wire [31:0] in_a,
  input  wire [31:0] in_b,
  output reg  [31:0] out
);
  // ...
endmodule

Suppose you want to combine these two functional units into an ALU that can both add and multiply. We’ll add a one-bit signal, op, that picks which FU to use:

module alu (
  input  wire [31:0] in_a,
  input  wire [31:0] in_b,
  input  wire        op,
  output reg  [31:0] out
);
  wire [31:0] add_out;
  wire [31:0] mul_out;

  Add32 add (.in_a(in_a), .in_b(in_b), .out(add_out));
  Mul32 mul (.in_a(in_a), .in_b(in_b), .out(mul_out));

  always @(*) begin
    out = (op == 1'b0) ? add_out : mul_out;
  end
endmodule

This module instantiates an adder add and a multiplier mul, and it uses Verilog’s ternary operator to multiplex the output based on op.

This implementation works fine if add and mul are both combinational. More realistically, though, a 32-bit multiplier will probably be sequential and pipelined. If that’s the case, this code is probably incorrect: it has implicitly assumed that Add32 and Mul32 have the same timing behavior.

Here’s the problem: there is nothing in the module signatures for the functional units that tells us that’s the case. The interfaces for Add32 and Mul32 are identical because they can only describe the physical shape of the ports, not the timing. Even if Add32 is combinational and Mul32 takes 3 cycles, their Verilog signatures would remain identical. The only way to know how to use a Verilog module correctly is to read the comments (or to look directly at the source code).

If you’re curious about this particular problem, please take a look at our paper about Filament, an HDL with a type system that enforces timing safety.4

The Building Blocks’ Building Blocks

If low-level systems like compilers are the building blocks for the software industry, then hardware design tools are the building blocks for the building blocks. The tower of system abstractions needs a strong foundation, and Verilog is a cracked and crumbling concrete slab.

We must work toward a post-Verilog future. We need to invest more in modern HDLs, and we need toolchains that can support these superior languages without going through Verilog as the least common denominator.

  1. I choose to believe that Biden wrote “Back to the Building Blocks” all by himself, and I am not interested in evidence to the contrary. 

  2. It would be reasonable to guess that the reg keyword means that data_out is stored in a register. That’s not the case. reg declares mutable variables that can be either combinational or stateful, depending on how they’re used. 

  3. In lattice terms, X should work like a top element. 

  4. Please direct all Filament fan mail to Rachit, who led that work.