Initial values, memories, and Yosys

Hi all,

First off, thanks for creating and sustaining MyHDL! It is a fantastic tool, and I have thoroughly enjoyed using it.

I have a question, and I suspect that as I am new to MyHDL and FPGA development, the error may lie in my use of the tools as opposed to a bug. The issue is the following:

  1. I believe it is often the case that we want to pre-initialize BRAMs with some predetermined values. This is generally done with the initial syntax in Verilog, and via toVerilog.initial_values = True support in MyHDL.
  2. When I enable initial value support on MyHDL, it wants to initialize the output of the data register for the BRAM.
  3. Yosys then decides that since the output of the RAM has an initial value that it is incompatible with the BRAM model, and pushes the entire memory out of BRAMs.

Here is a sample test case:

@block
def case_for_test(clock, reset, values):
    addr = Signal(intbv(0)[4:])
    data = Signal(intbv(0)[8:])
    ram = [Signal(intbv(x)[8:]) for x in range(16)]
    cnt = Signal(modbv(0, min=0, max=16))
    we = Signal(bool(0))

    @always(clock.posedge)
    def rom():
        if we:
            ram[addr].next = 0
        else:
            data.next = ram[addr]

    @always_seq(clock.posedge, reset=reset)
    def cycle():
        cnt.next = cnt + 1
        addr.next = cnt
        values.next = data

    return instances()

I had to add the unused we line to convince MyHDL that the memory was driven (so that initial values made it to the output). Here is the generated Verilog

// File: ../gen/case_for_test.v
// Generated by MyHDL 0.11
// Date: Mon Oct 14 11:43:58 2019


`timescale 1ns/10ps

module case_for_test (
    clock,
    reset,
    values
);


input clock;
input reset;
output [7:0] values;
reg [7:0] values;

reg [3:0] addr = 0;
reg [3:0] cnt = 0;
reg [7:0] data = 0;
wire we;
reg [7:0] ram [0:16-1];
initial begin
    ram[0] <= 0;
    ram[1] <= 1;
    ram[2] <= 2;
    ram[3] <= 3;
    ram[4] <= 4;
    ram[5] <= 5;
    ram[6] <= 6;
    ram[7] <= 7;
    ram[8] <= 8;
    ram[9] <= 9;
    ram[10] <= 10;
    ram[11] <= 11;
    ram[12] <= 12;
    ram[13] <= 13;
    ram[14] <= 14;
    ram[15] <= 15;
end

assign we = 1'd0;


always @(posedge clock) begin: CASE_FOR_TEST_ROM
    if (we) begin
        ram[addr] <= 0;
    end
    else begin
        data <= ram[addr];
    end
end


always @(posedge clock, negedge reset) begin: CASE_FOR_TEST_CYCLE
    if (reset == 0) begin
        cnt <= 0;
        addr <= 0;
        values <= 0;
    end
    else begin
        cnt <= (cnt + 1);
        addr <= cnt;
        values <= data;
    end
end

endmodule

The heartburn is from this line:

reg [7:0] data = 0;

The Yosys synthesis pass claims:

Checking rule #1 for bram type $__ICE40_RAM4K_M0 (variant 1):
Bram geometry: abits=8 dbits=16 wports=0 rports=0
Estimated number of duplicates for more read ports: dups=1
Metrics for $__ICE40_RAM4K_M0: awaste=240 dwaste=8 bwaste=3968 waste=3968 efficiency=3
Rule #1 for bram type $__ICE40_RAM4K_M0 (variant 1) accepted.
Mapping to bram type $__ICE40_RAM4K_M0 (variant 1):
Read port #0 is in clock domain !~async~.
Bram port A1.1 has incompatible clock type.
Failed to map read port #0.
Mapping to bram type $__ICE40_RAM4K_M0 failed.

And the complaint about the read port being in the async clock domain appears linked to its initial value. If I remove that initial value, I get the following:

Number of cells: 24
SB_CARRY 2
SB_DFFR 16
SB_LUT4 5
SB_RAM40_4K 1

And the BRAM is correctly synthesized.

Any suggestions/advice on how to proceed? I can’t imagine I’m the first one to encounter this issue, but searching has so far been unhelpful.

Samit

Hi,

Synthesis tools require specific coding to detect RAM, ROM, dual RAM…
You have to refer to the synthesis documentation and code accordingly.
The we signal might be required by your tool to instantiate BRAM correctly.

Nicolas

Is it a really small BRAM? It strikes me that initialised values for BRAM is a slightly strange concept. That said, I can see when it might be useful when the RAM is really just a small holding area, in which case a solution is to use a block to load the RAM from a ROM - it can be an initial state which just loads RAM and then permanently exits.

It’s rather application specific as to what the best strategy might be.

Hi, I am a beginner in HDL, and am currently working on a project requiring a functionnal block-RAM with initial values. However whenever I try to convert to VHDL or Verilog, I get the same error: it can’t convert a list of Signals to the HDL.

I see that you are using a list of Signals, how did you manage to convert it to Verilog? Did you find a solution for the initial values?

Is there a way to find out if code is being simulated, rather than converted, from within a definition?

I have almost finished a generic initialized RAM model, but it currently needs a parameter or global to obtain this information.

Jan Coombs

There is __debug__ ; it will skip conversion (only)
http://docs.myhdl.org/en/stable/manual/conversion.html#excluding-code-from-conversion

Thanks, that was quick. I still struggle with the terseness of the manual, and find most useful info from example code. The part you cited was new to me, I have seen all the words five times over, and probably learned something.

In my code, both Simulation, and Synthesis via constructed template, work as expected. This is using a structure that selects the text of two @always… blocks to build a simulator, or text to convert using template construction and the necessary appendages.

This works as expected when a parameter ’ SynType’ is set to ‘Sim’ or ‘Verilog’ The test used is “if (SynType==“Sim”):”

Using “if (debug):” Simulation is inconclusive, and synthesis is using the simulation code, not the template, so I get an uninitialized RAM.

When substituting “if (not debug):” simulation fails with little info, but synthesis is good.

So “debug” seems close, but not close enough. Something that tells me that simulation is in progress would be ideal.

Using “if (now()==0):” works, but probably will not if I change the order of convert and synthesis operations.

I will pull the parameter “SynType” and use a global variable, unless there is anything that can tell me whether or not a simulation is running.

Thanks, Jan Coombs

it is __debug__ (with the ‘dunder’ double underscores) …
But yes, that only applies to insert arbitrary Python code.
Why would you want to have different code for simulation than for conversion?

The pairs of underscores got lost in copying, have just checked to make sure.

The verilog memory code could be the same, but the model also builds the RAM initialization block with a memory content values list. I have not found another way to attach this section to the generated verilog code.

See: http://murray-microft.co.uk/MyHDL/myram_2020-03-31_1745_SimRunsJust_SynthGood.tar.gz

Poor planning, new update, etc, see: http://murray-microft.co.uk/MyHDL/iRam/

Dear,

who can guide me to setup development environment (Windhoos) and implement Index of /MyHDL/iRam on ICE40 ?

The …/MyHDL/IRam models were developed under Project IceStorm (Project IceStorm). They should work with the Lattice toolchain, but have not been tested with it.