Basic CPU Tutorial won't compile "Nextpnr exited with status: 1"

I was following along the CPU tutorial but I couldn’t get it to actually work and was a bit stumped as to why. As a sanity check and a direct comparison I just copied and pasted the complete code from the tutorial itself to a new file but that code fails to compile and throws the error above.

Here are my files, basicCPU is my attempt and tutorial CPU is the tutorial copy and pasted.

basicCPU.zip (60.1 KB)

tutorial CPU.zip (90.1 KB)

I’m using the Cu V2 and the Yosis toolchain. I would try it with iCEcube2 to test if that’s the issue but no one is responding to my emails asking for a license so Yosis is my only option for the time being.

Update: The terminal keeps saying “65 arcs ripped up due to negative slack” which is I think a timing issue? I tried using a counter to slow down the clock and that did compile, but it just doesn’t do anything at all in the simulator or on the real board. Any advice for how to fix this would be helpful.

The reason you don’t see anything happening in the interactive simulator is that it is VERY slow compared to the real board (~1000Hz vs 100,000,000Hz). If you modify the loop to take less time it works in the simulation.

module instRom (
    input address[8],
    output inst[16]
) {
    
    always {
        inst = c{Inst.NOP, 12b0}
        
        case (address) {
            // begin:
            0: inst = c{Inst.SET, 4d2, 8d0}              // SET R2, 0
                // loop:
            1: inst = c{Inst.SET, 4d1, 8d128}            // SET R1, 128
            2: inst = c{Inst.STORE, 4d2, 4d1, 4d0}       // STORE R2, R1, 0
            3: inst = c{Inst.SET, 4d1, 8d1}              // SET R1, 1
            4: inst = c{Inst.ADD, 4d2, 4d2, 4d1}         // ADD R2, R2, R1
            5: inst = c{Inst.SET, 4d15, 8d1}             // SET R15, loop
            6: inst = c{Inst.SET, 4d0, 8d7}              // SET R0, delay
                // delay:
            7: inst = c{Inst.SET, 4d11, 8d0}             // SET R11, 0
            8: inst = c{Inst.SET, 4d12, 8d0}             // SET R12, 0
            9: inst = c{Inst.SET, 4d13, 8d0}             // SET R13, 0
            10: inst = c{Inst.SET, 4d1, 8d1}             // SET R1, 1
                // delay_loop:
            11: inst = c{Inst.ADD, 4d11, 4d11, 4d1}      // ADD R11, R11, R1
            12: inst = c{Inst.BEQ, 4d11, 8d0}            // BEQ R11, 0
            13: inst = c{Inst.SET, 4d0, 8d11}            // SET R0, delay_loop
            14: inst = c{Inst.ADD, 4d12, 4d12, 4d1}      // ADD R12, R12, R1
            15: inst = c{Inst.BEQ, 4d12, 8d0}            // BEQ R12, 0
            16: inst = c{Inst.SET, 4d0, 8d20}            // SET R0, return via inst 20
            17: inst = c{Inst.ADD, 4d13, 4d13, 4d1}      // ADD R13, R13, R1
            18: inst = c{Inst.BEQ, 4d13, 8d0}            // BEQ R13, 0
            19: inst = c{Inst.SET, 4d0, 8d11}            // SET R0, delay_loop
            20: inst = c{Inst.SET, 4d1, 8d0}             // SET R1, 0
            21: inst = c{Inst.ADD, 4d0, 4d15, 4d1}       // ADD R0, R15, R1
        }
    }
}

That being said, there does seem to be something wrong. It won’t build with the open source tools as they fail to meet timing (my run said a max speed of 65MHz) but even with iCEcube2 (which does meet timing) it wasn’t working on my Cu.

I just tested it on my Au and it works so it seems something weird is going on. I’ll take a look and see if I can’t figure out what.

1 Like

Ok well… It turns out it fails to meet timing with iCEcube2 by even more but Alchitry Labs didn’t check it like I thought it did.

The solution was to use the PLL to reduce the clock speed and then it all works out. I guess either Vivado (for the Au) is that much better as routing or the Artix 7 is inherently that much faster than the ICE40.

I attached my modified project with the PLL code. It works with the open source tools and iCEcube2.

cu_cpu.zip (4.7 KB)

1 Like

Thank you for your response :slight_smile:

I’ll try the slowed down code you wrote in the simulator this weekend (Im away from my computer for the week) but it’s worth noting even in the simulator wouldn’t follow any set of instruction I gave it, even if the instruction ROM was just: set a register to a constant value, output that value, repeat. Same for the physical Cu.

Is there any reason using a counter to generate a slower clock would be causing problems? Until I can get lattice to respond to me that seems to be the only way I can meet timing requirements.

You generally shouldn’t use a counter to make slower clocks as it causes clock skew and other timing problems. The open source tools are happy to use the version I posted with the PLL.

The PLL code is just a wrapper for the primitive.


module clk_pll (
    input  wire clk_in,   // 100 MHz input clock
    output wire clk_out,  // 25 MHz output clock
    output wire lock      // Goes high when the clock is stable
);

    // F_VCO = (clk_in / (DIVR + 1)) * (DIVF + 1)
    // F_OUT = F_VCO / (2^DIVQ)
    // F_VCO must be between 533 MHz and 1066 MHz.
    
    // For 100 MHz -> 25 MHz:
    // F_VCO = (100 / 1) * 8 = 800 MHz
    // F_OUT = 800 / 32 = 25 MHz

    SB_PLL40_CORE #(
        .FEEDBACK_PATH("SIMPLE"),
        .DIVR(4'b0000),         // Reference divider: 1 
        .DIVF(7'b0000111),      // Feedback divider: 8 (7 + 1)
        .DIVQ(3'b101),          // VCO divider: 32 (2^5)
        .FILTER_RANGE(3'b101)   // Required filter range for 100 MHz input
    ) pll_inst (
        .REFERENCECLK(clk_in),
        .PLLOUTCORE(clk_out),
        .LOCK(lock),
        .RESETB(1'b1),          // Active low reset (1 = normal operation)
        .BYPASS(1'b0)           // 0 = use PLL, 1 = bypass PLL
    );

endmodule

If you want to provide your other attempts where the simulator seemed to not work I’m happy to look at them.

1 Like