Flash interface issue: sim vs. icebreaker

I’ve got a simple SoC + custom view controller working in both a simulator (Verilator + SDL window) and the icebreaker itself (HDMI PMOD) but the one part that’s not working is the loading the CPU program from flash on reset. The CPU, RAM + decoding, video controller and the test program works fine on the icebreaker but only when I $readmemh into the block RAM and ignore the flash. The loader reaches its end state and once the CPU is allowed to start (running code from RAM), it just crashes. iceprog verifies the flash contents OK so I’m wondering what the disconnect between my Verilator environment state and the icebreaker state after it loads the bitstream from flash. Are there any simple gotchas with loading from flash or any config I need to manage that I might miss in a simulation? I’m guessing it’s something silly but subtle. Any ideas?

Some project config stuff below, tried to fish out the most relevant bits:

I’m using the icosoc flash controller to start out with, to load 32bits at a time and writing it to the SPRAM:


And this to simulate it with Verilator (I just removed the “Hello world” bit):

Verilator: Copying the 24kbyte prog to flash at 1MByte+, where buffer contains the contents of prog.bin:

auto flash = tb->top__DOT__sim_flash__DOT__memory;
std::copy(buffer.begin(), buffer.end(), &flash[0x100000]);

to mimic what should be happening on the icebreaker:

iceprog -o 1024k prog.bin

I checked the yosys / nextpnr logs for any warnings about the flash IOs not being referenced but it seems right:

set_io flash_sck 15
set_io flash_csn 16
set_io flash_mosi 14
set_io flash_miso 17

referenced by my top module:

module top(
        input clk_in,
        input reset,    

        output [3:0] vga_r,
        output [3:0] vga_g,
        output [3:0] vga_b,

        output vsync,
        output hsync,
        output vga_hsync,
        output vga_vsync,

        output vga_clk,
        output vga_de,

        output led_r,
        output led_b,

        output flash_sck,
        output flash_csn,
        output flash_mosi,
        input flash_miso
);

// ...

icosoc_flashmem flash(
        .clk(cpu_clk),
        .resetn(!generated_reset),
        .valid(flash_loading),
        .ready(flash_ready),
        .addr(flash_address),
        .rdata(flash_data_out),
        
        .spi_cs(flash_csn),
        .spi_sclk(flash_sck),
        .spi_mosi(flash_mosi),
// TODO: add a top module specific to Verilator so this `ifdef can be removed
`ifdef VERILATOR
        .spi_miso(flash_miso_sim)
`else
        .spi_miso(flash_miso)
`endif
);

Any help greatly appreciated. I saw the icosoc referenced in @lawrie’s NES demo and it looked easy enough to hook up. I did notice the difference with inverted reset but it is inverted in my source.

Cheers

1 Like

The iCE40 puts the FPGA into deep sleep at startup. Adding -s to icepack should disable this.

2 Likes

Right that explains it. That’s all it was, works great now Thanks Dave. :+1:

1 Like