How can I use iCE40 4K block RAM in 512x8 read mode with IceStorm?

4.4k views Asked by At

I am trying to figure out how to use the block RAM on my iCE40HX-8K Breakout Board. I would like to access it in a 512x8 configuration, which as far as I can tell from the documentation is supported by project IceStorm, but I haven't been able to get it to work like I expected.

If I understand correctly, initializing an SB_RAM40_4K primitive with the READ_MODE parameter set to 1 should set the block up in 512x8 read mode, which uses a 9 bit read address, and reads 8 bits of data at each address.

Here is the simplest example I could think of. It sets up an SB_RAM40_4K with some pre-initialized memory and reads straight to the pins of the on-board LED's.

hx8kboard.pcf

set_io leds[0] B5
set_io leds[1] B4
set_io leds[2] A2
set_io leds[3] A1
set_io leds[4] C5
set_io leds[5] C4
set_io leds[6] B3
set_io leds[7] C3
set_io clk J3

top.v

module top (
  output [7:0] leds,
  input clk
);

//reg [8:0] raddr = 8'd0;
reg [8:0] raddr = 8'd1;

SB_RAM40_4K #(
  .INIT_0(256'h00000000000000000000000000000000000000000000000000000000_44_33_22_11),
  .WRITE_MODE(1),
  .READ_MODE(1)
) ram40_4k_512x8 (
  .RDATA(leds),
  .RADDR(raddr),
  .RCLK(clk),
  .RCLKE(1'b1),
  .RE(1'b1),
  .WADDR(8'b0),
  .WCLK(1'b0),
  .WCLKE(1'b0),
  .WDATA(8'b0),
  .WE(1'b0)
);

endmodule

LED output when raddr == 0

           \|/             \|/
O   O   O   O   O   O   O   O 

LED output when raddr == 1

       \|/ \|/         \|/ \|/
O   O   O   O   O   O   O   O 

I would think that address 1 in 512x8 mode would be the second 8 bits from RAM, which is 8'h22 or 8'b0010010. Instead I get 8'h33 or 8'b00110011. After a little experimentation, this seems to be the lower 8 bits of a 16 bit read.

I'm not sure where I went wrong. Any help understanding what's going on here would be appreciated. Thanks!

1

There are 1 answers

1
CliffordVienna On BEST ANSWER

This question is not really about Yosys or Project IceStorm. The format used for the SB_RAM40_4K INIT_* parameters is the same for the IceStorm flow and the Lattice iCEcube2 flow. However, Lattice has done a very very bad job at documenting this format. Otherwise I'd just point you to the right Lattice document.. :)

You are interested in the 512x8 mode. First you need to know that in 512x8 mode only the even bits of .RDATA() and .WDATA() are used (not the 8 LSB bits, as your code suggests!).

The data in .INIT_* is stored as 16 16-bit words per parameter. The lowest 16-bit word in .INIT_0() contains the 8-bit word at addr 0 in its even bits and the 8-bit word at addr 256 in its odd bits.

The next 16-bit word in .INIT_0() contains words 1 and 257. The lowest 16-bits in .INIT_1() contain words 16 and 272, and so forth.

The easiest way to investigate this kind of stuff is probably to either read the SB_RAM40_4K simulation model in /usr/local/share/yosys/ice40/cells_sim.v, or simply let Yosys infer the memory and observe what yosys does. For example the following design:

module test(input clk, wen, input [8:0] addr, input [7:0] wdata, output reg [7:0] rdata);
  reg [7:0] mem [0:511];
  initial mem[0] = 255;
  always @(posedge clk) begin
        if (wen) mem[addr] <= wdata;
        rdata <= mem[addr];
  end
endmodule

Will produce the following output when run through yosys -p 'synth_ice40; write_verilog' test.v:

(* top =  1  *)
(* src = "test.v:1" *)
module test(clk, wen, addr, wdata, rdata);
  (* src = "/usr/local/bin/../share/yosys/ice40/brams_map.v:255" *)
  (* unused_bits = "0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15" *)
  wire [15:0] _0_;
  (* src = "test.v:1" *)
  input [8:0] addr;
  (* src = "test.v:1" *)
  input clk;
  (* src = "test.v:1" *)
  output [7:0] rdata;
  (* src = "test.v:1" *)
  input [7:0] wdata;
  (* src = "test.v:1" *)
  input wen;
  (* src = "/usr/local/bin/../share/yosys/ice40/brams_map.v:277|/usr/local/bin/../share/yosys/ice40/brams_map.v:35" *)
  SB_RAM40_4K #(
    .INIT_0(256'bxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx1x1x1x1x1x1x1x1),
    .INIT_1(256'hxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx),
    .INIT_2(256'hxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx),
    .INIT_3(256'hxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx),
    .INIT_4(256'hxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx),
    .INIT_5(256'hxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx),
    .INIT_6(256'hxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx),
    .INIT_7(256'hxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx),
    .INIT_8(256'hxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx),
    .INIT_9(256'hxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx),
    .INIT_A(256'hxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx),
    .INIT_B(256'hxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx),
    .INIT_C(256'hxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx),
    .INIT_D(256'hxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx),
    .INIT_E(256'hxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx),
    .INIT_F(256'hxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx),
    .READ_MODE(32'sd1),
    .WRITE_MODE(32'sd1)
  ) \mem.0.0.0  (
    .MASK(16'hxxxx),
    .RADDR({ 2'h0, addr }),
    .RCLK(clk),
    .RCLKE(1'h1),
    .RDATA({ _0_[15], rdata[7], _0_[13], rdata[6], _0_[11], rdata[5], _0_[9], rdata[4], _0_[7], rdata[3], _0_[5], rdata[2], _0_[3], rdata[1], _0_[1], rdata[0] }),
    .RE(1'h1),
    .WADDR({ 2'h0, addr }),
    .WCLK(clk),
    .WCLKE(wen),
    .WDATA({ 1'hx, wdata[7], 1'hx, wdata[6], 1'hx, wdata[5], 1'hx, wdata[4], 1'hx, wdata[3], 1'hx, wdata[2], 1'hx, wdata[1], 1'hx, wdata[0] }),
    .WE(1'h1)
  );
endmodule

(Scroll all the way to the right to see the initialization pattern generated for the mem[0] = 255 initialization.)