Skip to content
DenseChip
Learn
PracticeCode labInterview

DenseChip

Navigate

Menu

LearnPracticeCode labInterview

Tools

All toolsSV editorCode repoResume
DenseChip

Practice, courses, and chip-design tools in one focused workspace.

LearnPracticeCode labInterviewToolsLearning paths

© 2026 DenseChip

AboutContactPrivacyTerms
Skip to lesson

Course contents

  • Logic Synthesis Glossary
  • Logic Synthesis Cheatsheet
  • Synthesis Foundations14
  • Logic Optimisation and Mapping20
  • Constraints and Timing15
  • Sequential, FSM and Memory19
    • Flops, Resets and Enables
    • FSM Synthesis
    • Memory and Macro Inference
      • Flop Arrays or RAM Macros
      • Read and Write Port Styles
      • Byte Enables and Bypass Wrappers
      • Compilers, Macros and Wrappers
    • Retiming and Pipelining
  • Datapath Synthesis14
  • Area, Power and Test14
  • Verification, ECO and Handoff15
  • Flows and Debugging15
  • Interview and Judgment11
Loading lesson

‹Memory and Macro Inference
  1. Topics
  2. Logic Synthesis
  3. Sequential, FSM and Memory
  4. Memory and Macro Inference
  5. Compilers, Macros and Wrappers

Part 4 · Sequential, FSM and Memory · Intermediate

Compilers, Macros and Wrappers

Memory compilers, black-box macros, wrappers, and port arithmetic.

On this page

    What a memory compiler produces

    A memory compiler builds an SRAM macro for a stated depth, width and port count. It emits the layout block plus a timing and power model for that exact shape.

    There is one macro per shape, not one generic RAM. A new depth or width means a new compiler run and a new model.

    Compiler input

    Compiler output

    Depth and width

    Placed block with fixed pins

    Port count and type

    Timing model per port

    Options (masks, test)

    Power model and test interface, varying by compiler

    Black-box macros in synthesis

    A synthesis tool treats a compiled macro as a black box. It connects the pins and meets the setup and hold requirements from the model; it never maps logic inside.

    That boundary cuts both ways. The inside is free of synthesis effort, but no RTL change can fix a macro whose shape is wrong.

    design.sv
    1
    2
    3
    4
    5
    // not-run: illustrative excerpt, not a complete testbench
    ram_macro u_mem (
      .clk(clk), .addr(addr_q), .wdata(wdata_q), .we(we_q),
      .rdata(mem_q)
    );

    The listing above is an illustrative excerpt, not a runnable testbench.

    Wrapper patterns

    A wrapper registers the macro boundary and absorbs the awkward cases. Address and control register on the way in, and the read data registers on the way out.

    The wrapper also holds the bypass mux and any power or test hooks. Those hooks vary by macro, so the wrapper isolates them from the rest of the RTL.

    Wrapper piece

    Job

    Input registers

    Meet macro setup with a clean pipeline stage

    Bypass mux

    Forward write data to a same-cycle same-address read

    Output register

    Absorb macro clock-to-data delay

    Port and bandwidth arithmetic

    Ports set the accesses per cycle, and width times clock sets the bit rate. The program below checks address width, macro tiling and EXAMPLE port rates.

    example.py
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    import sys
    
    WIDTH = 32
    FREQ_HZ = 500000000
    DEPTH = 1024
    
    bytes_per_access = WIDTH // 8
    bw_single = WIDTH * FREQ_HZ
    bw_dual = 2 * WIDTH * FREQ_HZ
    
    addr = 0
    p = 1
    while p < DEPTH:
        p = p * 2
        addr = addr + 1
    
    macros_for_1mib = 1048576 // 65536
    
    checks = [
        (bytes_per_access, 4),
        (bw_single, 16000000000),
        (bw_dual, 32000000000),
        (addr, 10),
        (macros_for_1mib, 16),
    ]
    
    bad = 0
    for i in range(len(checks)):
        got, want = checks[i]
        if got != want:
            print("FAIL: check " + str(i) + " got " + str(got) + " want " + str(want))
            bad += 1
    
    if bad != 0:
        sys.exit(1)
    print("PASS: port and tiling arithmetic holds on 5 checks")

    The rates above use EXAMPLE parameters, not measurements of any real macro. The tiling count is exact: sixteen 64 KiB macros hold one MiB.

    Diagram
    1 MiB = 1048576 bytes, one macro = 64 KiB = 65536 bytes
    macros = 1048576 / 65536 = 16
    address bits for 1024 rows = 10, since 2^10 = 1024
    EXAMPLE port rate: 32 bits x 500000000 cycles/s = 16000000000 bit/s

    Common pitfall

    Editing the netlist inside a macro boundary throws away the model the flow signs off against. Fix macro problems by rerunning the compiler or changing the wrapper, never by touching the inside.

    Interview

    Likely probes: the black-box boundary and wrapper contents.

    • The compiler emits a block plus timing and power models for one exact shape.

    • Synthesis connects the pins and meets the model; it never maps inside.

    • The wrapper holds pipeline registers, the bypass mux and macro-specific hooks.

    Check yourself The RTL needs 1024 rows of 32 bits from compiled macros. How many address bits, and how many 64 KiB macros hold one MiB?

    Ten address bits, since 2^10 is 1024. Sixteen 64 KiB macros hold one MiB.

    Check yourself A 32-bit port runs at an EXAMPLE 500 MHz with one access per cycle. What is the bit rate?

    16000000000 bit/s: 32 times 500000000. A dual-port shape doing one read and one write per cycle doubles it to 32000000000 bit/s.

    Related topics

    • Previous: Byte Enables and Bypass Wrappers

    • Caches

    • Retiming and Pipelining

    Previous

    65 / 139

    Next: Retiming and Pipelining

    Up next

    Up nextRetiming and Pipelining

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Flop Arrays or RAM Macros
    • Read and Write Port Styles