Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

BRAM Refactoring & Clock Speed Optimization

Motivation

The original HaDes-V BRAM model used a dual-port memory design that supplied data on the falling edge of the clock. This forced the entire read path, BRAM address decode, memory array access, and the Wishbone ACK/data multiplexing, to complete within a single half-cycle. The synthesizer could not pipeline this path, limiting the achievable system frequency to approximately 50 MHz on the Basys 3 target.

For DOOM, every additional MHz directly translates into more instructions per second, which means more rendering throughput and a smoother frame rate. Increasing the clock speed to 75 MHz was therefore a key performance goal.

BRAM Refactoring

The wishbone_ram.sv module was redesigned to remove the falling-edge behavior and instead operate as a conventional single-cycle memory:

always_ff @(posedge clk) begin
    if (rst) begin
        port_a.ack      <= 0;
        port_a.err      <= 0;
        port_a.dat_miso <= 0;
    end else begin
        port_a.ack <= 0;
        port_a.err <= 0;

        if (port_a.cyc && port_a.stb && !port_a.ack) begin
            if (port_a.adr >= ADDRESS && port_a.adr < ADDRESS + SIZE) begin
                port_a.ack <= 1;
                if (!port_a.we) begin
                    port_a.dat_miso <= memory[port_a.adr - ADDRESS];
                end else begin
                    // Byte-level writes via sel mask
                end
            end else begin
                port_a.err <= 1;
            end
        end
    end
end

The key changes were:

  • Synchronous reads: data is now registered and available on the next rising edge, giving the toolchain a full clock cycle for the memory access path
  • Simplified control: ack is asserted unconditionally on the next cycle for valid accesses, rather than using the half-clock scheme
  • Byte-level writes preserved: write masking via sel remains unchanged for correct sub-word writes

Clock Speed Increase

With the BRAM timing path resolved, the MMCM configuration in clk_params.sv was adjusted to raise the CPU clock from 50 MHz to 75 MHz:

localparam real MMCM_MUL   = 6.000;   // VCO: 100 MHz * 6 = 600 MHz
localparam int  MMCM_DIV   = 1;       // Input divider
localparam real MMCM_DIV_75 = 8.000;  // 600 MHz / 8 = 75 MHz (CPU)
localparam int  MMCM_DIV_200 = 3;     // 600 MHz / 3 = 200 MHz (MIG refclk)

The 600 MHz VCO output drives both the 75 MHz system clock (for the CPU, caches, and peripherals) and the 200 MHz reference clock (for the DDR2 MIG controller).

Pipeline Adjustments

The instruction fetch stage (fetch_stage.sv) and memory stage (memory_stage.sv) were updated to account for the single-cycle BRAM access model. The memory stage’s BRAM read path now correctly expects data on the next cycle, and the stall/forwarding logic around memory operations was tightened to prevent mis-speculations.

Result

The CPU core now runs at a stable 75 MHz on the Nexys 4 DDR, a 50% increase over the original 50 MHz Basys 3 design. This clock speed was maintained throughout all subsequent additions (caches, M-Extension, peripherals), and the timing analysis consistently passes with the Explore-directive place-and-route flow used in the synthesis script.