← Back to blogs

Why computer architecture should be taught before HDL.

A note on why hardware description languages make more sense after understanding architecture and execution.

This is probably one of my strongest opinions about how digital design is taught: computer architecture should come before hardware description languages. HDL is often introduced as “hardware programming,” but that framing is misleading because it skips the engineering reason hardware exists in the first place. Verilog or SystemVerilog can describe a circuit, but the language does not tell you why that circuit should exist, why it should be organised in a particular way, or which physical trade-offs make one design better than another.

RTL is not an abstraction layer over software. It is an abstraction layer over physical digital systems. Without some understanding of computer architecture, students begin by describing structures whose purpose they do not yet understand. They learn how to write an always_ff block before they understand why state must be separated from combinational logic. They learn how to build a pipeline register before understanding why combinational depth limits frequency. They learn how to construct a cache controller before understanding what problem locality is solving.

Computer architecture defines how information moves through a machine. HDL merely formalises that movement.

When students begin directly with Verilog or SystemVerilog, they quickly become familiar with syntax, non-blocking assignments, sensitivity lists, finite-state machines, synthesis rules and simulation semantics. With enough practice, they can produce circuits that compile, simulate and even synthesise correctly. Yet many still struggle to explain why a processor needs a forwarding network, why branch prediction exists, why register renaming improves parallelism, or why a cache hierarchy can dominate the performance of an otherwise capable execution core.

This happens because implementation is being taught before intent. A student learns what a multiplexer looks like in RTL without first understanding why multiple values are competing for the same destination. They learn how to encode a controller without understanding the datapath it is controlling. They learn how to divide logic into modules without understanding the information dependencies that should determine those module boundaries.

To me, architecture is the computational contract, while RTL is its structural realisation. Architecture defines the visible behaviour of a system and the major mechanisms required to support it. RTL decides how those mechanisms are represented as registers, combinational paths, control signals, queues, arbiters and state machines. The architecture says what must happen. The microarchitecture decides how it will happen. RTL then expresses that microarchitecture precisely enough for tools to transform it into gates.

A processor is fundamentally a constrained information-transport machine. Instructions, operands, addresses, predictions, metadata and control signals all move through structures with finite capacity and non-zero delay. Every architectural decision is therefore an optimisation problem involving latency, throughput, locality, dependency resolution, energy consumption and the physical movement of bits.

Consider pipelining. It is usually introduced as the act of splitting instruction execution into stages. That description is technically correct, but it misses the engineering point. Pipelining reduces the amount of combinational work performed between registers, which can shorten the critical path and allow a higher clock frequency. At the same time, it allows multiple instructions to occupy different stages of the processor concurrently, increasing throughput.

But pipelining also creates new problems. Instructions now overlap. Results may not be available when later instructions need them. Branch outcomes may arrive after younger instructions have already entered the pipeline. Pipeline registers consume power and area. Deeper pipelines can increase misprediction penalties. What initially appears to be a simple partitioning technique becomes a network of trade-offs involving timing, hazards, speculation and recovery.

Without architectural context, a student may implement pipeline registers and hazard logic as isolated exercises. With architectural context, the same student understands that forwarding exists because waiting for every result to reach the register file would unnecessarily serialize dependent instructions. Stalling exists because some dependencies cannot be resolved in time. Branch prediction exists because the fetch unit must continue choosing instruction addresses before the actual branch outcome is known.

The same principle applies to caches. A cache is not simply a memory array with tags. It is a response to the enormous difference between processor speed and main-memory latency. It exploits temporal and spatial locality to reduce the average cost of memory access. Its organisation reflects architectural choices about line size, capacity, associativity, replacement, write policy, coherence and miss handling.

If caches are taught only as RTL structures, students may understand how to compare tags and select ways, but not why increasing associativity can reduce conflict misses while increasing hit latency and access energy. They may know how to write a replacement-policy state machine without understanding why replacement behaviour matters. They may implement a blocking cache without appreciating the throughput advantage of supporting multiple outstanding misses.

Architecture provides the reason. RTL provides the mechanism.

This is also why I think beginners spend too much time thinking in modules instead of dataflow. Hardware is not composed because modules exist. Modules exist because information must move, transform, synchronise and eventually satisfy timing constraints. A module boundary is useful only when it reflects a meaningful division of responsibility, state ownership, timing or reuse.

Starting from modules can encourage a software-like way of thinking. A beginner may imagine that one module “calls” another module or that statements execute sequentially because they appear sequentially in the source file. But hardware does not execute source code in that way. The source describes relationships between signals, registers and logic that exist concurrently.

A better starting question is not, “Which modules should I create?” It is, “What information exists, where is it stored, where must it go, when must it arrive and what resources does it compete for?” Once those questions are answered, useful module boundaries often emerge naturally.

Suppose an instruction needs to move from fetch to decode, from decode to rename, from rename to scheduling and from scheduling to execution. At every point, the relevant architectural questions are about dataflow. What metadata travels with the instruction? Which stage owns its current state? What happens when the next stage is full? Can the instruction be replayed? Can it be invalidated after a branch misprediction? Which structures must be updated when it completes?

Those questions determine the design long before a single line of RTL is written. The HDL representation comes later, after the movement and ownership of information have been understood.

Timing is another reason architecture should come first. Beginners are often told that hardware is concurrent, but concurrency alone is not the difficult concept. The real difficulty is understanding concurrency under timing constraints. Signals propagate through combinational paths. Registers capture values only at clock boundaries. Different operations require different numbers of cycles. Shared resources create contention. Backpressure prevents data from advancing. Incorrect assumptions about timing produce circuits that are logically plausible but architecturally broken.

A queue, for example, is not merely an array with read and write pointers. Its architecture must define what happens when reads and writes occur simultaneously, whether it supports multiple enqueues or dequeues per cycle, how full and empty conditions are detected, whether entries may be bypassed and how ordering is preserved. The RTL is a consequence of those behavioural decisions.

An arbiter is not merely a case statement. It represents a policy for allocating a scarce resource. Fixed priority may be simple and fast but can starve low-priority requesters. Round-robin arbitration improves fairness but requires state. Age-based arbitration can preserve ordering but may require wider comparison logic. The architecture of the policy determines the circuit that must be written.

Even something as basic as a register file becomes more meaningful when viewed architecturally. How many reads and writes must it support per cycle? Are reads synchronous or asynchronous? What happens when the same register is read and written simultaneously? Can a design tolerate banking conflicts? Is the structure implemented as flip-flops, SRAM macros or distributed storage? The answer affects performance, area, timing and physical implementation.

This architectural way of thinking becomes even more important in modern processor design. Contemporary cores are not simple sequences of fetch, decode, execute and write-back. They contain prediction structures, instruction queues, rename maps, physical register files, schedulers, reorder buffers, load-store queues, replay mechanisms and multiple execution pipelines. The difficulty is not writing the syntax required to instantiate these structures. The difficulty is making them cooperate while preserving correctness.

An out-of-order processor, for example, must allow instructions to execute when their operands become ready rather than strictly in program order. At the same time, it must preserve the architectural illusion that instructions completed in order. That requires mechanisms for register renaming, dependency tracking, speculative state, exception recovery and in-order retirement.

None of those mechanisms make sense as isolated RTL modules. Their meaning comes from the architectural problem they solve. The rename table exists to remove false dependencies between architectural register names. The reorder buffer exists to preserve precise state and ordered retirement. The scheduler exists to select ready work from a larger window of instructions. The physical register file exists because the machine needs more internal versions of values than the ISA exposes.

Once that architecture is understood, the RTL becomes much less mysterious. The student is no longer memorising structures. They are implementing consequences.

Modern synthesis tools make this distinction even more important. Synthesis already performs substantial structural optimisation. It can propagate constants, eliminate dead logic, simplify Boolean expressions, infer multiplexers, map arithmetic onto implementation resources, optimise finite-state machines and restructure logic to meet timing constraints. Physical-design tools continue the process by buffering nets, placing cells, routing wires and optimising paths.

This means the value of an RTL engineer is not primarily the ability to manually produce clever gate-level structures. The important work is deciding what state should exist, which operations should occur in parallel, where pipeline boundaries should be placed, how resources should be shared, what ordering guarantees must be preserved and how the system behaves under pressure.

The true intellectual work of RTL engineering is increasingly architectural rather than syntactic. Describing hardware correctly is only half the problem. Describing the right hardware is where engineering actually begins.

A poor architecture written in perfect SystemVerilog is still a poor design. It may be functionally correct and completely impractical. It may contain an unacceptably long critical path, excessive fanout, unnecessary storage, insufficient buffering or a throughput bottleneck caused by a badly shared resource. No amount of syntactic fluency fixes a design whose information flow was misunderstood from the beginning.

This also explains why beginners often overestimate the complexity of HDL itself. Verilog is not an especially large language. The difficult part is not remembering where to write begin and end. The difficult part is mentally modelling concurrency, state transitions, cycle boundaries, resource contention, propagation delay and physical cost.

These are architecture problems long before they become language problems.

A software language usually lets the programmer express a sequence of operations while leaving many implementation decisions to the machine. HDL is different. The designer is not merely describing computation. The designer is defining storage, parallelism and communication. A line of RTL can imply thousands of gates, a large multiplexer, a bank of registers or a long combinational path. Two functionally equivalent descriptions can synthesise into hardware with radically different timing and area characteristics.

That is why “hardware programming” is such a dangerous phrase. It suggests that learning HDL is similar to learning another programming language. It encourages students to focus on syntax, control flow and code organisation while underestimating the importance of cycles, wires, fanout and state.

HDL does not behave like software because hardware is not software executed somewhere else. Hardware is the machine itself. Every assignment describes a relationship that may become physical logic. Every register becomes actual state. Every dependency may become a path through gates and wires. Every additional port may duplicate structures or expand arbitration logic.

Perhaps the biggest misconception is treating RTL as if it creates computation. It does not. The computation has already been defined by the architecture. RTL constrains how that computation occupies space, time and eventually silicon.

An adder performs addition because the architecture requires an addition operation. A pipeline exists because the system must balance timing and throughput. A scheduler exists because ready operations must be selected from competing candidates. A cache exists because distant memory is too slow to access directly for every operation. RTL expresses these decisions. It does not invent their purpose.

This is why I believe a better digital-design curriculum would begin with information flow. Students should first learn datapaths, instruction execution, memory hierarchies, control flow, hazards, pipelines, resource contention and basic performance analysis. They should understand latency versus throughput, architectural state versus microarchitectural state and functional correctness versus timing correctness.

They should trace instructions through a processor before being asked to implement one. They should understand why state is required before writing state machines. They should understand why buffering exists before building FIFOs. They should understand why arbitration exists before coding arbiters. They should understand why pipelines stall before implementing valid-ready handshakes.

Only then should HDL be introduced as the language used to make those ideas precise.

This does not mean students must master advanced superscalar architecture before writing their first Verilog module. It means the concepts should be taught in the correct causal order. The architectural problem should come first, followed by the microarchitectural solution, followed by the RTL representation.

For example, begin with the need to store a value across cycles. Then introduce the concept of a register. Then show how a clocked RTL block describes it. Begin with the need to choose between multiple data sources. Then introduce multiplexing. Then show its RTL representation. Begin with a multi-cycle operation that requires control state. Then introduce the state machine. Then show how states and transitions are encoded.

This approach makes HDL feel less arbitrary because each construct corresponds to a previously understood hardware requirement. The language becomes a tool rather than the subject itself.

Teaching architecture first also produces better debugging instincts. A syntax-first learner tends to inspect statements and signals locally. An architecture-first learner asks broader questions. Is the instruction allowed to advance this cycle? Who owns the data now? Has the consumer observed the producer’s result? Is a queue full? Was speculative state correctly invalidated? Is an operation waiting for a structural resource or a true dependency?

Those questions lead more directly to the real cause of hardware bugs.

The same is true for optimisation. Without architecture, optimisation becomes a collection of coding tricks. With architecture, it becomes the disciplined process of identifying bottlenecks in information movement. A long critical path may require retiming or pipelining. A low-throughput unit may need replication. A congested resource may need banking or additional ports. A large structure may need hierarchical lookup. A frequently stalled pipeline may need more buffering or better scheduling.

These are not HDL optimisations. They are architectural responses expressed through HDL.

Learning architecture first also helps students understand that correctness exists at several levels. A circuit may produce the correct result but violate its latency contract. It may work in isolation but deadlock when connected to another block. It may pass simple tests but fail under simultaneous requests. It may simulate correctly with idealised delays but miss timing after synthesis.

Architecture defines the assumptions and guarantees that allow separate blocks to work together. It determines ordering, handshakes, visibility, atomicity, exceptions and recovery. RTL implements those guarantees, but it cannot replace the need to reason about them.

Ultimately, the goal is not to produce people who can write Verilog. The goal is to produce engineers who can design digital systems.

That requires understanding where computation lives, how state evolves, how data moves, why operations wait and which physical constraints determine performance. HDL matters because it gives these ideas an executable, simulatable and synthesizable form. But the language is not the foundation. The architecture is.

Learn HDL first, and it is surprisingly easy to mistake syntax for engineering. Learn architecture first, and HDL becomes what it should have been from the beginning: a precise language for expressing decisions you already understand.

contact.me()