Merge pull request #1574 from YosysHQ/eddie/xilinx_lutram
authorEddie Hung <eddie@fpgeh.com>
Tue, 17 Dec 2019 05:48:21 +0000 (21:48 -0800)
committerGitHub <noreply@github.com>
Tue, 17 Dec 2019 05:48:21 +0000 (21:48 -0800)
xilinx: add LUTRAM rules for RAM32M, RAM64M

frontends/verilog/preproc.cc
frontends/verilog/verilog_lexer.l
manual/CHAPTER_Verilog.tex
manual/manual.tex
passes/memory/memory_bram.cc
passes/opt/opt_expr.cc
techlibs/xilinx/xc7_xcu_brams.txt
tests/arch/common/blockram.v [new file with mode: 0644]
tests/arch/common/memory_attributes/attributes_test.v [new file with mode: 0644]
tests/arch/xilinx/attributes_test.ys [new file with mode: 0644]
tests/arch/xilinx/blockram.ys [new file with mode: 0644]

index 7e107dc267b8cff6b7fbaa5c2cfac02586824243..161253a99fec97c080b2aeb1aea5c48ff9fd2601 100644 (file)
@@ -28,7 +28,7 @@
  *
  *  Ad-hoc implementation of a Verilog preprocessor. The directives `define,
  *  `include, `ifdef, `ifndef, `else and `endif are handled here. All other
- *  directives are handled by the lexer (see lexer.l).
+ *  directives are handled by the lexer (see verilog_lexer.l).
  *
  */
 
index c8984c2c41a404f60d9b6555c842addad7ed0bfa..ca23df3e8c27c68224b2be6f15885bbf1645394d 100644 (file)
@@ -28,7 +28,7 @@
  *
  *  A simple lexer for Verilog code. Non-preprocessor compiler directives are
  *  handled here. The preprocessor stuff is handled in preproc.cc. Everything
- *  else is left to the bison parser (see parser.y).
+ *  else is left to the bison parser (see verilog_parser.y).
  *
  */
 
index e9ca6114e01a98f890f166b7d425c5591a586791..d4cc55647df3e38d55712462bc7d26fc5a0365d6 100644 (file)
@@ -93,7 +93,7 @@ frontends/verilog/preproc.cc} in the Yosys source tree.
 
 \begin{sloppypar}
 The Verilog Lexer is written using the lexer generator {\it flex} \citeweblink{flex}. Its source code
-can be found in {\tt frontends/verilog/lexer.l} in the Yosys source tree.
+can be found in {\tt frontends/verilog/verilog\_lexer.l} in the Yosys source tree.
 The lexer does little more than identifying all keywords and literals
 recognised by the Yosys Verilog frontend.
 \end{sloppypar}
@@ -115,7 +115,7 @@ whenever possible.)
 \subsection{The Verilog Parser}
 
 The Verilog Parser is written using the parser generator {\it bison} \citeweblink{bison}. Its source code
-can be found in {\tt frontends/verilog/parser.y} in the Yosys source tree.
+can be found in {\tt frontends/verilog/verilog\_parser.y} in the Yosys source tree.
 
 It generates an AST using the \lstinline[language=C++]{AST::AstNode} data structure
 defined in {\tt frontends/ast/ast.h}. An \lstinline[language=C++]{AST::AstNode} object has
index 67982cbc8168e7f4b257e026c45453aee29533d8..75f087ecae41e0c2b9ed040e9a5ed4cd5b760d8c 100644 (file)
@@ -146,7 +146,7 @@ with the help of HDL synthesis tools.
 
 In special cases such as synthesis for coarse-grain cell libraries or when
 testing new synthesis algorithms it might be necessary to write a custom HDL
-synthesis tool or add new features to an existing one. It this cases the
+synthesis tool or add new features to an existing one. In these cases the
 availability of a Free and Open Source (FOSS) synthesis tool that can be used
 as basis for custom tools would be helpful.
 
index aa8f9414976ab07bdb727c5ffde5e28fd4a1eaf0..e0970d19214f19234eea3f16782162b554149686 100644 (file)
@@ -134,6 +134,7 @@ struct rules_t
                dict<string, int> min_limits, max_limits;
                bool or_next_if_better, make_transp, make_outreg;
                char shuffle_enable;
+               vector<vector<std::tuple<bool,IdString,Const>>> attributes;
        };
 
        dict<IdString, vector<bram_t>> brams;
@@ -327,6 +328,20 @@ struct rules_t
                                continue;
                        }
 
+                       if (GetSize(tokens) >= 2 && tokens[0] == "attribute") {
+                               data.attributes.emplace_back();
+                               for (int idx = 1; idx <= GetSize(tokens)-1; idx++) {
+                                       size_t c1 = tokens[idx][0] == '!' ? 1 : 0;
+                                       size_t c2 = tokens[idx].find("=");
+                                       bool exists = (c1 == 0);
+                                       IdString key = RTLIL::escape_id(tokens[idx].substr(c1, c2));
+                                       Const val = c2 != std::string::npos ? tokens[idx].substr(c2+1) : RTLIL::Const(1);
+
+                                       data.attributes.back().emplace_back(exists, key, val);
+                               }
+                               continue;
+                       }
+
                        syntax_error();
                }
        }
@@ -724,7 +739,7 @@ grow_read_ports:;
                                        if (match.make_transp && wr_ports <= 1) {
                                                pi.make_transp = true;
                                                if (pi.clocks != 0) {
-                                                       if (wr_ports == 1 && wr_clkdom != clkdom) {                                                             
+                                                       if (wr_ports == 1 && wr_clkdom != clkdom) {
                                                                log("        Bram port %c%d.%d cannot have soft transparency logic added as read and write clock domains differ.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
                                                                goto skip_bram_rport;
                                                        }
@@ -813,6 +828,45 @@ grow_read_ports:;
                        return false;
                }
 
+               for (const auto &sums : match.attributes) {
+                       bool found = false;
+                       for (const auto &term : sums) {
+                               bool exists = std::get<0>(term);
+                               IdString key = std::get<1>(term);
+                               const Const &value = std::get<2>(term);
+                               auto it = cell->attributes.find(key);
+                               if (it == cell->attributes.end()) {
+                                       if (exists)
+                                               continue;
+                                       found = true;
+                                       break;
+                               }
+                               else if (!exists)
+                                       continue;
+                               if (it->second != value)
+                                       continue;
+                               found = true;
+                               break;
+                       }
+                       if (!found) {
+                               std::stringstream ss;
+                               bool exists = std::get<0>(sums.front());
+                               if (!exists)
+                                       ss << "!";
+                               IdString key = std::get<1>(sums.front());
+                               ss << key.str();
+                               const Const &value = std::get<2>(sums.front());
+                               if (exists)
+                                       ss << "=";
+                               if (value != Const(1))
+                                       ss << "\"" << value.decode_string() << "\"";
+
+                               log("    Rule for bram type %s rejected: requirement 'attribute %s ...' not met.\n",
+                                               log_id(match.name), ss.str().c_str());
+                               return false;
+                       }
+               }
+
                if (mode == 1)
                        return true;
        }
@@ -1100,6 +1154,45 @@ void handle_cell(Cell *cell, const rules_t &rules)
                                goto next_match_rule;
                        }
 
+                       for (const auto &sums : match.attributes) {
+                               bool found = false;
+                               for (const auto &term : sums) {
+                                       bool exists = std::get<0>(term);
+                                       IdString key = std::get<1>(term);
+                                       const Const &value = std::get<2>(term);
+                                       auto it = cell->attributes.find(key);
+                                       if (it == cell->attributes.end()) {
+                                               if (exists)
+                                                       continue;
+                                               found = true;
+                                               break;
+                                       }
+                                       else if (!exists)
+                                               continue;
+                                       if (it->second != value)
+                                               continue;
+                                       found = true;
+                                       break;
+                               }
+                               if (!found) {
+                                       std::stringstream ss;
+                                       bool exists = std::get<0>(sums.front());
+                                       if (!exists)
+                                               ss << "!";
+                                       IdString key = std::get<1>(sums.front());
+                                       ss << key.str();
+                                       const Const &value = std::get<2>(sums.front());
+                                       if (exists)
+                                               ss << "=";
+                                       if (value != Const(1))
+                                               ss << "\"" << value.decode_string() << "\"";
+
+                                       log("    Rule for bram type %s (variant %d) rejected: requirement 'attribute %s ...' not met.\n",
+                                                       log_id(bram.name), bram.variant, ss.str().c_str());
+                                       goto next_match_rule;
+                               }
+                       }
+
                        log("    Rule #%d for bram type %s (variant %d) accepted.\n", i+1, log_id(bram.name), bram.variant);
 
                        if (or_next_if_better || !best_rule_cache.empty())
@@ -1225,6 +1318,13 @@ struct MemoryBramPass : public Pass {
                log("    dcells  .......  number of cells in 'data-direction'\n");
                log("    cells  ........  total number of cells (acells*dcells*dups)\n");
                log("\n");
+               log("A match containing the command 'attribute' followed by a list of space\n");
+               log("separated 'name[=string_value]' values requires that the memory contains any\n");
+               log("one of the given attribute name and string values (where specified), or name\n");
+               log("and integer 1 value (if no string_value given, since Verilog will interpret\n");
+               log("'(* attr *)' as '(* attr=1 *)').\n");
+               log("A name prefixed with '!' indicates that the attribute must not exist.\n");
+               log("\n");
                log("The interface for the created bram instances is derived from the bram\n");
                log("description. Use 'techmap' to convert the created bram instances into\n");
                log("instances of the actual bram cells of your target architecture.\n");
index 6cf66fb95aec858f38e17ca1522c169c65d885ad..4a2f170b83401acaa02b2a4944cc1ae8ca556235 100644 (file)
@@ -978,7 +978,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
                {
                        cover_list("opt.opt_expr.eqneq.cmpzero", "$eq", "$ne", cell->type.str());
                        log_debug("Replacing %s cell `%s' in module `%s' with %s.\n", log_id(cell->type), log_id(cell),
-                                       log_id(module), "$eq" ? "$logic_not" : "$reduce_bool");
+                                       log_id(module), cell->type == ID($eq) ? "$logic_not" : "$reduce_bool");
                        cell->type = cell->type == ID($eq) ? ID($logic_not) : ID($reduce_bool);
                        if (assign_map(cell->getPort(ID::A)).is_fully_zero()) {
                                cell->setPort(ID::A, cell->getPort(ID::B));
index f1161114e9bb0cf40cb4d09977a69c9be29b6f2e..c63218ae1b500cc7d3a129d38e2ed9e06aa768d8 100644 (file)
@@ -1,4 +1,3 @@
-
 bram $__XILINX_RAMB36_SDP
   init 1
   abits 9
@@ -72,34 +71,79 @@ bram $__XILINX_RAMB18_TDP
   clkpol 2 3
 endbram
 
+# The "min bits" value were taken from:
+# [[CITE]] 7 Series FPGAs Memory Resources User Guide (UG473),
+# v1.14 ed., p 29-30, July, 2019.
+# https://www.xilinx.com/support/documentation/user_guides/ug473_7Series_Memory_Resources.pdf
+
 match $__XILINX_RAMB36_SDP
-  min bits 4096
+  attribute !ram_style
+  attribute !logic_block
+  min bits 1024
   min efficiency 5
   shuffle_enable B
   make_transp
   or_next_if_better
 endmatch
 
+match $__XILINX_RAMB36_SDP
+  attribute ram_style=block ram_block
+  attribute !logic_block
+  shuffle_enable B
+  make_transp
+  or_next_if_better
+endmatch
+
 match $__XILINX_RAMB18_SDP
-  min bits 4096
+  attribute !ram_style
+  attribute !logic_block
+  min bits 1024
   min efficiency 5
   shuffle_enable B
   make_transp
   or_next_if_better
 endmatch
 
+match $__XILINX_RAMB18_SDP
+  attribute ram_style=block ram_block
+  attribute !logic_block
+  shuffle_enable B
+  make_transp
+  or_next_if_better
+endmatch
+
 match $__XILINX_RAMB36_TDP
-  min bits 4096
+  attribute !ram_style
+  attribute !logic_block
+  min bits 1024
   min efficiency 5
   shuffle_enable B
   make_transp
   or_next_if_better
 endmatch
 
+match $__XILINX_RAMB36_TDP
+  attribute ram_style=block ram_block
+  attribute !logic_block
+  shuffle_enable B
+  make_transp
+  or_next_if_better
+endmatch
+
 match $__XILINX_RAMB18_TDP
-  min bits 4096
+  attribute !ram_style
+  attribute !logic_block
+  min bits 1024
   min efficiency 5
   shuffle_enable B
   make_transp
+  or_next_if_better
+endmatch
+
+match $__XILINX_RAMB18_TDP
+  attribute ram_style=block ram_block
+  attribute !logic_block
+  shuffle_enable B
+  make_transp
 endmatch
 
diff --git a/tests/arch/common/blockram.v b/tests/arch/common/blockram.v
new file mode 100644 (file)
index 0000000..dbc6ca6
--- /dev/null
@@ -0,0 +1,45 @@
+`default_nettype none
+module sync_ram_sp #(parameter DATA_WIDTH=8, ADDRESS_WIDTH=10)
+   (input  wire                      write_enable, clk,
+    input  wire  [DATA_WIDTH-1:0]    data_in,
+    input  wire  [ADDRESS_WIDTH-1:0] address_in,
+    output wire  [DATA_WIDTH-1:0]    data_out);
+
+   localparam WORD  = (DATA_WIDTH-1);
+   localparam DEPTH = (2**ADDRESS_WIDTH-1);
+
+   reg [WORD:0] data_out_r;
+   reg [WORD:0] memory [0:DEPTH];
+
+   always @(posedge clk) begin
+      if (write_enable)
+        memory[address_in] <= data_in;
+      data_out_r <= memory[address_in];
+   end
+
+   assign data_out = data_out_r;
+endmodule // sync_ram_sp
+
+
+`default_nettype none
+module sync_ram_sdp #(parameter DATA_WIDTH=8, ADDRESS_WIDTH=10)
+   (input  wire                      clk, write_enable,
+    input  wire  [DATA_WIDTH-1:0]    data_in,
+    input  wire  [ADDRESS_WIDTH-1:0] address_in_r, address_in_w,
+    output wire  [DATA_WIDTH-1:0]    data_out);
+
+   localparam WORD  = (DATA_WIDTH-1);
+   localparam DEPTH = (2**ADDRESS_WIDTH-1);
+
+   reg [WORD:0] data_out_r;
+   reg [WORD:0] memory [0:DEPTH];
+
+   always @(posedge clk) begin
+      if (write_enable)
+        memory[address_in_w] <= data_in;
+      data_out_r <= memory[address_in_r];
+   end
+
+   assign data_out = data_out_r;
+endmodule // sync_ram_sdp
+
diff --git a/tests/arch/common/memory_attributes/attributes_test.v b/tests/arch/common/memory_attributes/attributes_test.v
new file mode 100644 (file)
index 0000000..275800d
--- /dev/null
@@ -0,0 +1,88 @@
+`default_nettype none
+module block_ram #(parameter DATA_WIDTH=4, ADDRESS_WIDTH=10)
+   (input  wire                      write_enable, clk,
+    input  wire  [DATA_WIDTH-1:0]    data_in,
+    input  wire  [ADDRESS_WIDTH-1:0] address_in,
+    output wire  [DATA_WIDTH-1:0]    data_out);
+
+   localparam WORD  = (DATA_WIDTH-1);
+   localparam DEPTH = (2**ADDRESS_WIDTH-1);
+
+   reg [WORD:0] data_out_r;
+   reg [WORD:0] memory [0:DEPTH];
+
+   always @(posedge clk) begin
+      if (write_enable)
+        memory[address_in] <= data_in;
+      data_out_r <= memory[address_in];
+   end
+
+   assign data_out = data_out_r;
+endmodule // block_ram
+
+`default_nettype none
+module distributed_ram #(parameter DATA_WIDTH=8, ADDRESS_WIDTH=4)
+   (input  wire                      write_enable, clk,
+    input  wire  [DATA_WIDTH-1:0]    data_in,
+    input  wire  [ADDRESS_WIDTH-1:0] address_in,
+    output wire  [DATA_WIDTH-1:0]    data_out);
+
+   localparam WORD  = (DATA_WIDTH-1);
+   localparam DEPTH = (2**ADDRESS_WIDTH-1);
+
+   reg [WORD:0] data_out_r;
+   reg [WORD:0] memory [0:DEPTH];
+
+   always @(posedge clk) begin
+      if (write_enable)
+        memory[address_in] <= data_in;
+      data_out_r <= memory[address_in];
+   end
+
+   assign data_out = data_out_r;
+endmodule // distributed_ram
+
+`default_nettype none
+module distributed_ram_manual #(parameter DATA_WIDTH=8, ADDRESS_WIDTH=4)
+   (input  wire                      write_enable, clk,
+    input  wire  [DATA_WIDTH-1:0]    data_in,
+    input  wire  [ADDRESS_WIDTH-1:0] address_in,
+    output wire  [DATA_WIDTH-1:0]    data_out);
+
+   localparam WORD  = (DATA_WIDTH-1);
+   localparam DEPTH = (2**ADDRESS_WIDTH-1);
+
+   reg [WORD:0] data_out_r;
+   (* ram_style = "block" *) reg [WORD:0] memory [0:DEPTH];
+
+   always @(posedge clk) begin
+      if (write_enable)
+        memory[address_in] <= data_in;
+      data_out_r <= memory[address_in];
+   end
+
+   assign data_out = data_out_r;
+endmodule // distributed_ram
+
+`default_nettype none
+module distributed_ram_manual_syn #(parameter DATA_WIDTH=8, ADDRESS_WIDTH=4)
+   (input  wire                      write_enable, clk,
+    input  wire  [DATA_WIDTH-1:0]    data_in,
+    input  wire  [ADDRESS_WIDTH-1:0] address_in,
+    output wire  [DATA_WIDTH-1:0]    data_out);
+
+   localparam WORD  = (DATA_WIDTH-1);
+   localparam DEPTH = (2**ADDRESS_WIDTH-1);
+
+   reg [WORD:0] data_out_r;
+   (* synthesis, ram_block *) reg [WORD:0] memory [0:DEPTH];
+
+   always @(posedge clk) begin
+      if (write_enable)
+        memory[address_in] <= data_in;
+      data_out_r <= memory[address_in];
+   end
+
+   assign data_out = data_out_r;
+endmodule // distributed_ram
+
diff --git a/tests/arch/xilinx/attributes_test.ys b/tests/arch/xilinx/attributes_test.ys
new file mode 100644 (file)
index 0000000..4c881b2
--- /dev/null
@@ -0,0 +1,47 @@
+# Check that blockram memory without parameters is not modified
+read_verilog ../common/memory_attributes/attributes_test.v
+hierarchy -top block_ram
+synth_xilinx -top block_ram
+cd block_ram # Constrain all select calls below inside the top module
+select -assert-count 1 t:RAMB18E1
+# Check that distributed memory without parameters is not modified
+design -reset
+read_verilog ../common/memory_attributes/attributes_test.v
+hierarchy -top distributed_ram
+synth_xilinx -top distributed_ram
+cd distributed_ram # Constrain all select calls below inside the top module
+select -assert-count 8 t:RAM32X1D
+# Set ram_style distributed to blockram memory; will be implemented as distributed
+design -reset
+read_verilog ../common/memory_attributes/attributes_test.v
+prep
+setattr -mod -set ram_style "distributed" block_ram
+synth_xilinx -top block_ram
+cd block_ram # Constrain all select calls below inside the top module
+select -assert-count 32 t:RAM128X1D
+# Set synthesis, logic_block to blockram memory; will be implemented as distributed
+design -reset
+read_verilog ../common/memory_attributes/attributes_test.v
+prep
+setattr -mod -set logic_block 1 block_ram
+synth_xilinx -top block_ram
+cd block_ram # Constrain all select calls below inside the top module
+select -assert-count 0 t:RAMB18E1
+select -assert-count 32 t:RAM128X1D
+# Set ram_style block to a distributed memory; will be implemented as blockram
+design -reset
+read_verilog ../common/memory_attributes/attributes_test.v
+synth_xilinx -top distributed_ram_manual
+cd distributed_ram_manual # Constrain all select calls below inside the top module
+select -assert-count 1 t:RAMB18E1
+# Set synthesis, ram_block block to a distributed memory; will be implemented as blockram
+design -reset
+read_verilog ../common/memory_attributes/attributes_test.v
+synth_xilinx -top distributed_ram_manual_syn
+cd distributed_ram_manual_syn # Constrain all select calls below inside the top module
+select -assert-count 1 t:RAMB18E1
diff --git a/tests/arch/xilinx/blockram.ys b/tests/arch/xilinx/blockram.ys
new file mode 100644 (file)
index 0000000..bb908cb
--- /dev/null
@@ -0,0 +1,97 @@
+### TODO: Not running equivalence checking because BRAM models does not exists
+###       currently. Checking instance counts instead.
+# Memory bits <= 18K; Data width <= 36; Address width <= 14: -> RAMB18E1
+read_verilog ../common/blockram.v
+chparam -set ADDRESS_WIDTH 10 -set DATA_WIDTH 1 sync_ram_sdp
+synth_xilinx -top sync_ram_sdp
+cd sync_ram_sdp
+select -assert-count 1 t:RAMB18E1
+
+design -reset
+read_verilog ../common/blockram.v
+chparam -set ADDRESS_WIDTH 8 -set DATA_WIDTH 18 sync_ram_sdp
+synth_xilinx -top sync_ram_sdp
+cd sync_ram_sdp
+select -assert-count 1 t:RAMB18E1
+
+design -reset
+read_verilog ../common/blockram.v
+chparam -set ADDRESS_WIDTH 14 -set DATA_WIDTH 1 sync_ram_sdp
+synth_xilinx -top sync_ram_sdp
+cd sync_ram_sdp
+select -assert-count 1 t:RAMB18E1
+
+design -reset
+read_verilog ../common/blockram.v
+chparam -set ADDRESS_WIDTH 9 -set DATA_WIDTH 36 sync_ram_sdp
+synth_xilinx -top sync_ram_sdp
+cd sync_ram_sdp
+select -assert-count 1 t:RAMB18E1
+
+# Anything memory bits < 1024 -> LUTRAM
+design -reset
+read_verilog ../common/blockram.v
+chparam -set ADDRESS_WIDTH 8 -set DATA_WIDTH 2 sync_ram_sdp
+synth_xilinx -top sync_ram_sdp
+cd sync_ram_sdp
+select -assert-count 0 t:RAMB18E1
+select -assert-count 4 t:RAM128X1D
+
+# More than 18K bits, data width <= 36 (TDP), and address width from 10 to 15b (non-cascaded) -> RAMB36E1
+design -reset
+read_verilog ../common/blockram.v
+chparam -set ADDRESS_WIDTH 10 -set DATA_WIDTH 36 sync_ram_sdp
+synth_xilinx -top sync_ram_sdp
+cd sync_ram_sdp
+select -assert-count 1 t:RAMB36E1
+
+
+### With parameters
+
+design -reset
+read_verilog ../common/blockram.v
+hierarchy -top sync_ram_sdp -chparam ADDRESS_WIDTH 10 -chparam DATA_WIDTH 1 
+setattr -set ram_style "block" m:memory
+synth_xilinx -top sync_ram_sdp
+cd sync_ram_sdp
+select -assert-count 1 t:RAMB18E1
+
+design -reset
+read_verilog ../common/blockram.v
+hierarchy -top sync_ram_sdp -chparam ADDRESS_WIDTH 10 -chparam DATA_WIDTH 1 
+setattr -set ram_block 1 m:memory
+synth_xilinx -top sync_ram_sdp
+cd sync_ram_sdp
+select -assert-count 1 t:RAMB18E1
+
+design -reset
+read_verilog ../common/blockram.v
+hierarchy -top sync_ram_sdp -chparam ADDRESS_WIDTH 10 -chparam DATA_WIDTH 1 
+setattr -set ram_style "dont_infer_a_ram_pretty_please" m:memory
+synth_xilinx -top sync_ram_sdp
+cd sync_ram_sdp
+select -assert-count 0 t:RAMB18E1
+
+design -reset
+read_verilog ../common/blockram.v
+hierarchy -top sync_ram_sdp -chparam ADDRESS_WIDTH 10 -chparam DATA_WIDTH 1 
+setattr -set logic_block 1 m:memory
+synth_xilinx -top sync_ram_sdp
+cd sync_ram_sdp
+select -assert-count 0 t:RAMB18E1
+
+design -reset
+read_verilog ../common/blockram.v
+hierarchy -top sync_ram_sdp -chparam ADDRESS_WIDTH 8 -chparam DATA_WIDTH 1
+setattr -set ram_style "block" m:memory
+synth_xilinx -top sync_ram_sdp
+cd sync_ram_sdp
+select -assert-count 1 t:RAMB18E1
+
+design -reset
+read_verilog ../common/blockram.v
+hierarchy -top sync_ram_sdp -chparam ADDRESS_WIDTH 8 -chparam DATA_WIDTH 1
+setattr -set ram_block 1 m:memory
+synth_xilinx -top sync_ram_sdp
+cd sync_ram_sdp
+select -assert-count 1 t:RAMB18E1