Structured topics to take you from HDL basics to advanced design patterns.
Learn what hardware description really means (parallel circuits, not sequential programs) through binary numbers, logic gates, two's complement, and Karnaugh-map simplification.
Write complete components from the first line to the last: declare interfaces with ports, swap implementations behind them, and use the three port directions correctly.
Declare wires of your own and give them honest types: the logic values and driver resolution, multi-bit vectors with indexing and slices, and the numeric types and constants that put real numbers on wires.
The operator toolbox for vectors: bitwise logic and reductions, concatenation and replication, and arithmetic with explicit width and sign handling.
Master the always-on assignments that wire circuits together outside any procedural block (plain, conditional, and selected) and see why the order of statements in a file changes nothing.
Step inside the process and the always block: the fenced region where statements run in order. Learn the default-first idiom, variables for step-by-step computation, and what wakes a block back up.
Make decisions inside procedural blocks with if and case statements, keep every path latch-free, finish every case, and unroll per-bit work with loops so synthesis builds exactly the logic you meant.
Build the standard combinational components every design leans on (multiplexers, decoders, encoders, display drivers, and tri-state buses) and choose the right coding style for each one.
Learn to read and write the self-checking benches that grade every design on this platform: DUT instantiation, stimulus timing, assertions that echo expected versus actual, and the wait statements that sequence it all.
Look inside the simulator: the event-driven loop that runs your designs, delta cycles, why signal and variable updates follow different rules, and how concurrent processes really communicate.
Master the conversion chains between raw vectors, unsigned and signed readings, and integers (numeric_std in VHDL, casting in SystemVerilog) and make every width and sign change explicit and correct.
Open the box behind the + operator: build adders from gates, chain them into multi-bit slices, shift by a signal amount, subtract with the same adder, flag overflow honestly, and assemble an ALU. Then agree where the binary point lives: fixed-point formats, rounding, and saturation.
Cross from combinational to sequential design. Build the storage elements that remember: the latch you avoid, the flip-flop you trust, and registers with resets, enables, and edge detection.
Compose designs out of designs: instantiate sub-units by name and by position, make them width-configurable with generics and parameters, generate repeated structure with one loop, and manage the binding and the wiring as the tree grows.
Turn the register and the adder into circuits that act over time. Binary, up/down, mod-N, Gray, ring, and Johnson counters: the workhorses behind timers, dividers, pointers, and sequencers.
Move data through time with register chains: serial delay lines, serial-parallel converters, and feedback registers that generate pseudo-random sequences. Close with the CRC circuit, the feedback register that catches transmission errors.
Define your own types: enumerations that name every mode, arrays that turn many slots into one signal, and records that bundle fields that belong together, then assemble and flatten them without losing a bit.
Design Moore and Mealy state machines, compare coding styles, and build FSMs with datapaths for real-world digital systems.
Name your logic: wrap reusable combinational logic in functions, hand multi-result steps to procedures and tasks, and organize shared definitions with packages, includes, and aliases.
Build the workhorse blocks of real sequential design (debouncers, PWM, programmable timers) by composing the counters, registers, and FSMs you already own. Cap it with a complete stopwatch, then close with the arbiter: sharing one resource fairly.
Cut long combinational paths with pipeline registers, keep parallel branches marching in step, and trade latency for throughput: the assembly-line technique behind every fast datapath.
Verify like it's your job: file-driven stimulus, constrained-random testing with reproducible seeds, functional coverage that names what you never tested, and a method for debugging straight from waveforms.
Infer RAM from arrays, then compose it: independent read and write ports, synchronous FIFOs, and multi-port register files. Close by stripping the write port entirely: constant tables read as ROM.
See what the synthesizer actually builds from your code: which constructs survive synthesis, how coding style steers registers, memories, and multipliers, why clock enables beat gated clocks, and how the clock period becomes a per-path budget with slack as the verdict.
Move signals safely between unrelated clocks: understand metastability, build two-flop and pulse synchronizers, and bring an asynchronous reset into every domain without waking half a machine mid-cycle.
Move whole values between unrelated clocks: close the loop with a req/ack handshake, cross counters in Gray code, and assemble the asynchronous FIFO (Gray pointer pair, dual-clock memory, and a reset bridge per domain).
Build real serial protocols (UART, SPI, and I2C) by combining FSMs, counters, shift registers, and edge detection into transmitters, receivers, and bus masters that survive noise, errors, and contention - then bring the handshake on-chip with AXI-Stream and back pressure, and close with the APB register block that lets software reach it all.
Architect benches as cooperating components -- record and struct transactions, driver procedures and tasks, independent monitors, reference-model scoreboards, and a clean end of test -- around the protocol hardware you built.