intel_alm: direct LUTRAM cell instantiation
authorDan Ravensloft <dan.ravensloft@gmail.com>
Thu, 16 Apr 2020 11:24:04 +0000 (12:24 +0100)
committerMarcelina Koƛcielnicka <mwk@0x04.net>
Thu, 7 May 2020 19:03:13 +0000 (21:03 +0200)
By instantiating the LUTRAM cell directly, we avoid a trip through
altsyncram, which speeds up Quartus synthesis time. This also gives
a little more flexibility, as Yosys can build RAMs out of individual
32x1 LUTRAM cells.

While working on this, I discovered that the mem_init0 parameter of
<family>_mlab_cell gets ignored by Quartus.

techlibs/intel_alm/Makefile.inc
techlibs/intel_alm/common/bram_m10k_map.v
techlibs/intel_alm/common/lutram_mlab.txt
techlibs/intel_alm/common/lutram_mlab_map.v [deleted file]
techlibs/intel_alm/common/megafunction_bb.v
techlibs/intel_alm/common/mem_sim.v [new file with mode: 0644]
techlibs/intel_alm/common/quartus_rename.v
techlibs/intel_alm/synth_intel_alm.cc
tests/arch/intel_alm/lutram.ys [new file with mode: 0644]

index bbf233aebcc9341a6716a06ac8e8edb77166b1b4..ed6c4510b953618dde357fe88fe53e7e648820a2 100644 (file)
@@ -7,13 +7,13 @@ $(eval $(call add_share_file,share/intel_alm/common,techlibs/intel_alm/common/al
 $(eval $(call add_share_file,share/intel_alm/common,techlibs/intel_alm/common/arith_alm_map.v))
 $(eval $(call add_share_file,share/intel_alm/common,techlibs/intel_alm/common/dff_map.v))
 $(eval $(call add_share_file,share/intel_alm/common,techlibs/intel_alm/common/dff_sim.v))
+$(eval $(call add_share_file,share/intel_alm/common,techlibs/intel_alm/common/mem_sim.v))
 
 # RAM
 bramtypes := m10k m20k
 $(foreach bramtype, $(bramtypes), $(eval $(call add_share_file,share/intel_alm/common,techlibs/intel_alm/common/bram_$(bramtype).txt)))
 $(foreach bramtype, $(bramtypes), $(eval $(call add_share_file,share/intel_alm/common,techlibs/intel_alm/common/bram_$(bramtype)_map.v)))
 $(eval $(call add_share_file,share/intel_alm/common,techlibs/intel_alm/common/lutram_mlab.txt))
-$(eval $(call add_share_file,share/intel_alm/common,techlibs/intel_alm/common/lutram_mlab_map.v))
 
 # Miscellaneous
 $(eval $(call add_share_file,share/intel_alm/common,techlibs/intel_alm/common/megafunction_bb.v))
index e5566010d8e3eb9dd8dcf2d17324c47c7efe7092..061463c3e1d77c94fbba2e06d33c5e9bf2eb493e 100644 (file)
@@ -28,4 +28,4 @@ altsyncram #(
     .clock1(CLK1)\r
 );\r
 \r
-endmodule
+endmodule\r
index 1d6174d8550fbf9f56ec2c6ea0499c8c13b17816..3cc69399dbfa31e9e2bb6c7f589be62c8cd87737 100644 (file)
@@ -1,20 +1,18 @@
-bram __MISTRAL_MLAB\r
-    init   0   # TODO: Re-enable when I figure out how LUTRAM init works\r
-    abits  5\r
-    dbits  16  @D32x16\r
-    dbits  18  @D32x18\r
-    dbits  20  @D32x20\r
-    groups 2\r
-    ports  1 1\r
-    wrmode 1 0\r
-    # read enable\r
-    enable 1 0\r
-    transp 1 0\r
-    clocks 1 2\r
-    clkpol 1 1\r
-endbram\r
-\r
-match __MISTRAL_MLAB\r
-    min efficiency 5\r
-    make_outreg\r
-endmatch\r
+bram MISTRAL_MLAB
+    init   0   # TODO: Re-enable when Yosys remembers the original filename.
+    abits  5
+    dbits  1
+    groups 2
+    ports  1 1
+    wrmode 1 0
+    # write enable
+    enable 1 0
+    transp 0 0
+    clocks 1 0
+    clkpol 1 1
+endbram
+
+match MISTRAL_MLAB
+    min efficiency 5
+    make_outreg
+endmatch
\ No newline at end of file
diff --git a/techlibs/intel_alm/common/lutram_mlab_map.v b/techlibs/intel_alm/common/lutram_mlab_map.v
deleted file mode 100644 (file)
index 3a9c859..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-module __MISTRAL_MLAB(CLK1, CLK2, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA);\r
-\r
-parameter CFG_ABITS = 5;\r
-parameter CFG_DBITS = 20;\r
-\r
-input CLK1, CLK2;\r
-input [CFG_ABITS-1:0] A1ADDR, B1ADDR;\r
-input [CFG_DBITS-1:0] A1DATA;\r
-input A1EN;\r
-output [CFG_DBITS-1:0] B1DATA;\r
-\r
-altsyncram #(\r
-    .operation_mode("dual_port"),\r
-    .ram_block_type("mlab"),\r
-    .widthad_a(CFG_ABITS),\r
-    .width_a(CFG_DBITS),\r
-    .widthad_b(CFG_ABITS),\r
-    .width_b(CFG_DBITS),\r
-) _TECHMAP_REPLACE_ (\r
-    .address_a(A1ADDR),\r
-    .data_a(A1DATA),\r
-    .wren_a(A1EN),\r
-    .address_b(B1ADDR),\r
-    .q_b(B1DATA),\r
-    .clock0(CLK1),\r
-    .clock1(CLK1),\r
-);\r
-\r
-endmodule\r
index 21ba73a09160b7d9cfe183dea9660c0da9e61530..c749fa70bd72dd0f3193a6e746674b5ae4db9bf5 100644 (file)
@@ -106,3 +106,26 @@ input aclr1;
 output eccstatus;
 
 endmodule
+
+(* blackbox *)
+module cyclonev_mlab_cell(portaaddr, portadatain, portbaddr, portbdataout, ena0, clk0, clk1);
+
+parameter logical_ram_name = "";
+parameter logical_ram_depth = 32;
+parameter logical_ram_width = 20;
+parameter mixed_port_feed_through_mode = "new";
+parameter first_bit_number = 0;
+parameter first_address = 0;
+parameter last_address = 31;
+parameter address_width = 5;
+parameter data_width = 1;
+parameter byte_enable_mask_width = 1;
+parameter port_b_data_out_clock = "NONE";
+parameter [639:0] mem_init0 = 640'b0;
+
+input [address_width-1:0] portaaddr, portbaddr;
+input [data_width-1:0] portadatain;
+output [data_width-1:0] portbdataout;
+input ena0, clk0, clk1;
+
+endmodule
diff --git a/techlibs/intel_alm/common/mem_sim.v b/techlibs/intel_alm/common/mem_sim.v
new file mode 100644 (file)
index 0000000..ae79b19
--- /dev/null
@@ -0,0 +1,60 @@
+// The MLAB
+// --------
+// In addition to Logic Array Blocks (LABs) that contain ten Adaptive Logic
+// Modules (ALMs, see alm_sim.v), the Cyclone V/10GX also contain
+// Memory/Logic Array Blocks (MLABs) that can act as either ten ALMs, or utilise
+// the memory the ALM uses to store the look-up table data for general usage, 
+// producing a 32 address by 20-bit block of memory. MLABs are spread out
+// around the chip, so they can be placed near where they are needed, rather than
+// being comparatively limited in placement for a deep but narrow memory such as
+// the M10K memory block.
+//
+// MLABs are used mainly for shallow but wide memories, such as CPU register
+// files (which have perhaps 32 registers that are comparatively wide (16/32-bit))
+// or shift registers (by using the output of the Nth bit as input for the N+1th
+// bit).
+//
+// Oddly, instead of providing a block 32 address by 20-bit cell, Quartus asks
+// synthesis tools to build MLABs out of 32 address by 1-bit cells, and tries
+// to put these cells in the same MLAB during cell placement. Because of this
+// a MISTRAL_MLAB cell represents one of these 32 address by 1-bit cells, and
+// 20 of them represent a physical MLAB.
+//
+// How the MLAB works
+// ------------------
+// MLABs are poorly documented, so the following information is based mainly
+// on the simulation model and my knowledge of how memories like these work.
+// Additionally, note that the ports of MISTRAL_MLAB are the ones auto-generated
+// by the Yosys `memory_bram` pass, and it doesn't make sense to me to use
+// `techmap` just for the sake of renaming the cell ports.
+//
+// The MLAB can be initialised to any value, but unfortunately Quartus only
+// allows memory initialisation from a file. Since Yosys doesn't preserve input
+// file information, or write the contents of an `initial` block to a file,
+// Yosys can't currently initialise the MLAB in a way Quartus will accept.
+//
+// The MLAB takes in data from A1DATA at the rising edge of CLK1, and if A1EN
+// is high, writes it to the address in A1ADDR. A1EN can therefore be used to
+// conditionally write data to the MLAB.
+//
+// Simultaneously, the MLAB reads data from B1ADDR, and outputs it to B1DATA,
+// asynchronous to CLK1 and ignoring A1EN. If a synchronous read is needed
+// then the output can be fed to embedded flops. Presently, Yosys assumes
+// Quartus will pack external flops into the MLAB, but this is an assumption
+// that needs testing.
+
+// The vendor sim model outputs 'x for a very short period (a few 
+// combinational delta cycles) after each write. This has been omitted from
+// the following model because it's very difficult to trigger this in practice
+// as clock cycles will be much longer than any potential blip of 'x, so the
+// model can be treated as always returning a defined result.
+module MISTRAL_MLAB(input [4:0] A1ADDR, input A1DATA, A1EN, CLK1, input [4:0] B1ADDR, output B1DATA);
+
+reg [31:0] mem = 32'b0;
+
+always @(posedge CLK1)
+    if (A1EN) mem[A1ADDR] <= A1DATA;
+
+assign B1DATA = mem[B1ADDR];
+
+endmodule
index ac0fe12aa7114f4e305dc675c0dab168b8d78465..c40a4e02d1e8a3eacdfd758783dcd68017b7d726 100644 (file)
@@ -1,8 +1,10 @@
 `ifdef cyclonev
 `define LCELL cyclonev_lcell_comb
+`define MLAB cyclonev_mlab_cell
 `endif
 `ifdef cyclone10gx
 `define LCELL cyclone10gx_lcell_comb
+`define MLAB cyclone10gx_mlab_cell
 `endif
 
 module __MISTRAL_VCC(output Q);
@@ -80,3 +82,40 @@ parameter LUT1 = 16'h0000;
 `LCELL #(.lut_mask({16'h0, LUT1, 16'h0, LUT0})) _TECHMAP_REPLACE_ (.dataa(A), .datab(B), .datac(C), .datad(D0), .dataf(D1), .cin(CI), .sumout(SO), .cout(CO));
 
 endmodule
+
+
+module MISTRAL_MLAB(input [4:0] A1ADDR, input A1DATA, A1EN, CLK1, input [4:0] B1ADDR, output B1DATA);
+
+// Here we get to an unfortunate situation. The cell has a mem_init0 parameter,
+// which takes in a hexadecimal string that could be used to initialise RAM.
+// In the vendor simulation models, this appears to work fine, but Quartus,
+// either intentionally or not, forgets about this parameter and initialises the
+// RAM to zero.
+//
+// Because of this, RAM initialisation is presently disabled, but the source
+// used to generate mem_init0 is kept (commented out) in case this gets fixed
+// or an undocumented way to get Quartus to initialise from mem_init0 is found.
+
+`MLAB #(
+    .logical_ram_name("MISTRAL_MLAB"),
+    .logical_ram_depth(32),
+    .logical_ram_width(1),
+    .mixed_port_feed_through_mode("Dont Care"),
+    .first_bit_number(0),
+    .first_address(0),
+    .last_address(31),
+    .address_width(5),
+    .data_width(1),
+    .byte_enable_mask_width(1),
+    .port_b_data_out_clock("NONE"),
+    // .mem_init0($sformatf("%08x", INIT))
+) _TECHMAP_REPLACE_ (
+    .portaaddr(A1ADDR),
+    .portadatain(A1DATA),
+    .portbaddr(B1ADDR),
+    .portbdataout(B1DATA),
+    .ena0(A1EN),
+    .clk0(CLK1)
+);
+
+endmodule
index 200b0cdd140ad264223bd619c9fa0ca1a01826d7..bf9e746b878becc63a3be801e901fad6af5c3582 100644 (file)
@@ -164,6 +164,7 @@ struct SynthIntelALMPass : public ScriptPass {
                        run(stringf("read_verilog -sv -lib +/intel/%s/cells_sim.v", family_opt.c_str()));
                        run(stringf("read_verilog -specify -lib -D %s +/intel_alm/common/alm_sim.v", family_opt.c_str()));
                        run(stringf("read_verilog -specify -lib -D %s +/intel_alm/common/dff_sim.v", family_opt.c_str()));
+                       run(stringf("read_verilog -specify -lib -D %s +/intel_alm/common/mem_sim.v", family_opt.c_str()));
 
                        // Misc and common cells
                        run("read_verilog -lib +/intel/common/altpll_bb.v");
@@ -190,7 +191,6 @@ struct SynthIntelALMPass : public ScriptPass {
 
                if (!nolutram && check_label("map_lutram", "(skip if -nolutram)")) {
                        run("memory_bram -rules +/intel_alm/common/lutram_mlab.txt", "(for Cyclone V / Cyclone 10GX)");
-                       run("techmap -map +/intel_alm/common/lutram_mlab_map.v", "(for Cyclone V / Cyclone 10GX)");
                }
 
                if (check_label("map_ffram")) {
diff --git a/tests/arch/intel_alm/lutram.ys b/tests/arch/intel_alm/lutram.ys
new file mode 100644 (file)
index 0000000..6f997b6
--- /dev/null
@@ -0,0 +1,20 @@
+read_verilog ../common/lutram.v
+hierarchy -top lutram_1w1r
+proc
+memory -nomap
+equiv_opt -run :prove -map +/intel_alm/common/alm_sim.v -map +/intel_alm/common/dff_sim.v -map +/intel_alm/common/mem_sim.v synth_intel_alm -family cyclonev -nobram
+memory
+opt -full
+
+miter -equiv -flatten -make_assert -make_outputs gold gate miter
+sat -verify -prove-asserts -seq 5 -set-init-zero -show-inputs -show-outputs miter
+
+design -load postopt
+cd lutram_1w1r
+select -assert-count 16 t:MISTRAL_MLAB
+select -assert-count 1 t:MISTRAL_NOT
+select -assert-count 2 t:MISTRAL_ALUT2
+select -assert-count 8 t:MISTRAL_ALUT3
+select -assert-count 17 t:MISTRAL_FF
+select -assert-none t:MISTRAL_NOT t:MISTRAL_ALUT2 t:MISTRAL_ALUT3 t:MISTRAL_FF t:MISTRAL_MLAB %% t:* %D
+