aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--backends/edif/edif.cc43
-rw-r--r--techlibs/xilinx/Makefile.inc4
-rw-r--r--techlibs/xilinx/arith_map.v152
-rw-r--r--techlibs/xilinx/synth_xilinx.cc14
-rw-r--r--techlibs/xilinx/xc2v_brams.txt31
-rw-r--r--techlibs/xilinx/xc2v_brams_map.v266
-rw-r--r--techlibs/xilinx/xc3sa_brams.txt51
-rw-r--r--techlibs/xilinx/xc3sda_brams.txt33
-rw-r--r--techlibs/xilinx/xc6s_brams.txt1
-rw-r--r--techlibs/xilinx/xc6s_brams_map.v3
-rw-r--r--techlibs/xilinx/xc7_brams_map.v2
-rw-r--r--techlibs/xilinx/xc7_xcu_brams.txt2
-rw-r--r--techlibs/xilinx/xcu_brams_map.v2
13 files changed, 478 insertions, 126 deletions
diff --git a/backends/edif/edif.cc b/backends/edif/edif.cc
index 616b754ce..199560ad0 100644
--- a/backends/edif/edif.cc
+++ b/backends/edif/edif.cc
@@ -246,19 +246,25 @@ struct EdifBackend : public Backend {
else if (!ct.cell_input(cell_it.first, port_it.first))
dir = "OUTPUT";
}
- if (port_it.second == 1)
+ int width = port_it.second;
+ int start = 0;
+ bool upto = false;
+ auto m = design->module(cell_it.first);
+ if (m) {
+ auto w = m->wire(port_it.first);
+ if (w) {
+ width = GetSize(w);
+ start = w->start_offset;
+ upto = w->upto;
+ }
+ }
+ if (width == 1)
*f << stringf(" (port %s (direction %s))\n", EDIF_DEF(port_it.first), dir);
else {
- int b[2] = {port_it.second-1, 0};
- auto m = design->module(cell_it.first);
- if (m) {
- auto w = m->wire(port_it.first);
- if (w) {
- b[w->upto ? 0 : 1] = w->start_offset;
- b[w->upto ? 1 : 0] = w->start_offset+GetSize(w)-1;
- }
- }
- *f << stringf(" (port (array %s %d) (direction %s))\n", EDIF_DEFR(port_it.first, port_rename, b[0], b[1]), port_it.second, dir);
+ int b[2];
+ b[upto ? 0 : 1] = start;
+ b[upto ? 1 : 0] = start+width-1;
+ *f << stringf(" (port (array %s %d) (direction %s))\n", EDIF_DEFR(port_it.first, port_rename, b[0], b[1]), width, dir);
}
}
*f << stringf(" )\n");
@@ -390,18 +396,23 @@ struct EdifBackend : public Backend {
if (sig[i].wire == NULL && sig[i] != RTLIL::State::S0 && sig[i] != RTLIL::State::S1)
log_warning("Bit %d of cell port %s.%s.%s driven by %s will be left unconnected in EDIF output.\n",
i, log_id(module), log_id(cell), log_id(p.first), log_signal(sig[i]));
- else if (sig.size() == 1)
- net_join_db[sig[i]].insert(make_pair(stringf("(portRef %s (instanceRef %s))", EDIF_REF(p.first), EDIF_REF(cell->name)), cell->output(p.first)));
else {
int member_idx = GetSize(sig)-i-1;
auto m = design->module(cell->type);
+ int width = sig.size();
if (m) {
auto w = m->wire(p.first);
- if (w)
+ if (w) {
member_idx = GetSize(w)-i-1;
+ width = GetSize(w);
+ }
+ }
+ if (width == 1)
+ net_join_db[sig[i]].insert(make_pair(stringf("(portRef %s (instanceRef %s))", EDIF_REF(p.first), EDIF_REF(cell->name)), cell->output(p.first)));
+ else {
+ net_join_db[sig[i]].insert(make_pair(stringf("(portRef (member %s %d) (instanceRef %s))",
+ EDIF_REF(p.first), member_idx, EDIF_REF(cell->name)), cell->output(p.first)));
}
- net_join_db[sig[i]].insert(make_pair(stringf("(portRef (member %s %d) (instanceRef %s))",
- EDIF_REF(p.first), member_idx, EDIF_REF(cell->name)), cell->output(p.first)));
}
}
}
diff --git a/techlibs/xilinx/Makefile.inc b/techlibs/xilinx/Makefile.inc
index 3f2fbcc85..7785bf81c 100644
--- a/techlibs/xilinx/Makefile.inc
+++ b/techlibs/xilinx/Makefile.inc
@@ -27,6 +27,10 @@ techlibs/xilinx/brams_init_8.vh: techlibs/xilinx/brams_init.mk
$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/cells_map.v))
$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/cells_sim.v))
$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/cells_xtra.v))
+$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xc2v_brams.txt))
+$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xc2v_brams_map.v))
+$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xc3sa_brams.txt))
+$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xc3sda_brams.txt))
$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xc6s_brams.txt))
$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xc6s_brams_map.v))
$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xc7_xcu_brams.txt))
diff --git a/techlibs/xilinx/arith_map.v b/techlibs/xilinx/arith_map.v
index 40c378d16..4ae938827 100644
--- a/techlibs/xilinx/arith_map.v
+++ b/techlibs/xilinx/arith_map.v
@@ -53,63 +53,31 @@ module _80_xilinx_lcu (P, G, CI, CO);
localparam MAX_WIDTH = CARRY4_COUNT * 4;
localparam PAD_WIDTH = MAX_WIDTH - WIDTH;
- wire [MAX_WIDTH-1:0] S = {{PAD_WIDTH{1'b0}}, P & ~G};
- wire [MAX_WIDTH-1:0] C = CO;
+ wire [MAX_WIDTH-1:0] S = {{PAD_WIDTH{1'b0}}, P & ~G};
+ wire [MAX_WIDTH-1:0] GG = {{PAD_WIDTH{1'b0}}, G};
+ wire [MAX_WIDTH-1:0] C;
+ assign CO = C;
generate for (i = 0; i < CARRY4_COUNT; i = i + 1) begin:slice
-
- // Partially occupied CARRY4
- if ((i+1)*4 > WIDTH) begin
-
- // First one
- if (i == 0) begin
- CARRY4 carry4_1st_part
- (
- .CYINIT(CI),
- .CI (1'd0),
- .DI (G [(WIDTH - 1):i*4]),
- .S (S [(WIDTH - 1):i*4]),
- .CO (CO[(WIDTH - 1):i*4]),
- );
- // Another one
- end else begin
- CARRY4 carry4_part
- (
- .CYINIT(1'd0),
- .CI (C [i*4 - 1]),
- .DI (G [(WIDTH - 1):i*4]),
- .S (S [(WIDTH - 1):i*4]),
- .CO (CO[(WIDTH - 1):i*4]),
- );
- end
-
- // Fully occupied CARRY4
+ if (i == 0) begin
+ CARRY4 carry4
+ (
+ .CYINIT(CI),
+ .CI (1'd0),
+ .DI (GG[i*4 +: 4]),
+ .S (S [i*4 +: 4]),
+ .CO (C [i*4 +: 4]),
+ );
end else begin
-
- // First one
- if (i == 0) begin
- CARRY4 carry4_1st_full
- (
- .CYINIT(CI),
- .CI (1'd0),
- .DI (G [((i+1)*4 - 1):i*4]),
- .S (S [((i+1)*4 - 1):i*4]),
- .CO (CO[((i+1)*4 - 1):i*4]),
- );
- // Another one
- end else begin
- CARRY4 carry4_full
- (
- .CYINIT(1'd0),
- .CI (C [i*4 - 1]),
- .DI (G [((i+1)*4 - 1):i*4]),
- .S (S [((i+1)*4 - 1):i*4]),
- .CO (CO[((i+1)*4 - 1):i*4]),
- );
- end
-
+ CARRY4 carry4
+ (
+ .CYINIT(1'd0),
+ .CI (C [i*4 - 1]),
+ .DI (GG[i*4 +: 4]),
+ .S (S [i*4 +: 4]),
+ .CO (C [i*4 +: 4]),
+ );
end
-
end endgenerate
`endif
@@ -254,67 +222,33 @@ module _80_xilinx_alu (A, B, CI, BI, X, Y, CO);
wire [MAX_WIDTH-1:0] S = {{PAD_WIDTH{1'b0}}, AA ^ BB};
wire [MAX_WIDTH-1:0] DI = {{PAD_WIDTH{1'b0}}, AA & BB};
- wire [MAX_WIDTH-1:0] C = CO;
+ wire [MAX_WIDTH-1:0] O;
+ wire [MAX_WIDTH-1:0] C;
+ assign Y = O, CO = C;
genvar i;
generate for (i = 0; i < CARRY4_COUNT; i = i + 1) begin:slice
-
- // Partially occupied CARRY4
- if ((i+1)*4 > Y_WIDTH) begin
-
- // First one
- if (i == 0) begin
- CARRY4 carry4_1st_part
- (
- .CYINIT(CI),
- .CI (1'd0),
- .DI (DI[(Y_WIDTH - 1):i*4]),
- .S (S [(Y_WIDTH - 1):i*4]),
- .O (Y [(Y_WIDTH - 1):i*4]),
- .CO (CO[(Y_WIDTH - 1):i*4])
- );
- // Another one
- end else begin
- CARRY4 carry4_part
- (
- .CYINIT(1'd0),
- .CI (C [i*4 - 1]),
- .DI (DI[(Y_WIDTH - 1):i*4]),
- .S (S [(Y_WIDTH - 1):i*4]),
- .O (Y [(Y_WIDTH - 1):i*4]),
- .CO (CO[(Y_WIDTH - 1):i*4])
- );
- end
-
- // Fully occupied CARRY4
+ if (i == 0) begin
+ CARRY4 carry4
+ (
+ .CYINIT(CI),
+ .CI (1'd0),
+ .DI (DI[i*4 +: 4]),
+ .S (S [i*4 +: 4]),
+ .O (O [i*4 +: 4]),
+ .CO (C [i*4 +: 4])
+ );
end else begin
-
- // First one
- if (i == 0) begin
- CARRY4 carry4_1st_full
- (
- .CYINIT(CI),
- .CI (1'd0),
- .DI (DI[((i+1)*4 - 1):i*4]),
- .S (S [((i+1)*4 - 1):i*4]),
- .O (Y [((i+1)*4 - 1):i*4]),
- .CO (CO[((i+1)*4 - 1):i*4])
- );
- // Another one
- end else begin
- CARRY4 carry4_full
- (
- .CYINIT(1'd0),
- .CI (C [i*4 - 1]),
- .DI (DI[((i+1)*4 - 1):i*4]),
- .S (S [((i+1)*4 - 1):i*4]),
- .O (Y [((i+1)*4 - 1):i*4]),
- .CO (CO[((i+1)*4 - 1):i*4])
- );
- end
-
+ CARRY4 carry4
+ (
+ .CYINIT(1'd0),
+ .CI (C [i*4 - 1]),
+ .DI (DI[i*4 +: 4]),
+ .S (S [i*4 +: 4]),
+ .O (O [i*4 +: 4]),
+ .CO (C [i*4 +: 4])
+ );
end
-
end endgenerate
`endif
diff --git a/techlibs/xilinx/synth_xilinx.cc b/techlibs/xilinx/synth_xilinx.cc
index 5a28bb139..a7fa73837 100644
--- a/techlibs/xilinx/synth_xilinx.cc
+++ b/techlibs/xilinx/synth_xilinx.cc
@@ -438,7 +438,19 @@ struct SynthXilinxPass : public ScriptPass
run("memory_bram -rules +/xilinx/{family}_brams.txt");
run("techmap -map +/xilinx/{family}_brams_map.v");
} else if (!nobram) {
- if (family == "xc6s") {
+ if (family == "xc2v" || family == "xc2vp" || family == "xc3s" || family == "xc3se") {
+ run("memory_bram -rules +/xilinx/xc2v_brams.txt");
+ run("techmap -map +/xilinx/xc2v_brams_map.v");
+ } else if (family == "xc3sa") {
+ // Superset of Virtex 2 primitives — uses common map file.
+ run("memory_bram -rules +/xilinx/xc3sa_brams.txt");
+ run("techmap -map +/xilinx/xc2v_brams_map.v");
+ } else if (family == "xc3sda") {
+ // Supported block RAMs for Spartan 3A DSP are
+ // a subset of Spartan 6's ones.
+ run("memory_bram -rules +/xilinx/xc3sda_brams.txt");
+ run("techmap -map +/xilinx/xc6s_brams_map.v");
+ } else if (family == "xc6s") {
run("memory_bram -rules +/xilinx/xc6s_brams.txt");
run("techmap -map +/xilinx/xc6s_brams_map.v");
} else if (family == "xc6v" || family == "xc7") {
diff --git a/techlibs/xilinx/xc2v_brams.txt b/techlibs/xilinx/xc2v_brams.txt
new file mode 100644
index 000000000..ac8cfb552
--- /dev/null
+++ b/techlibs/xilinx/xc2v_brams.txt
@@ -0,0 +1,31 @@
+# Virtex 2, Virtex 2 Pro, Spartan 3, Spartan 3E block RAM rules.
+
+bram $__XILINX_RAMB16
+ init 1
+ abits 9 @a9d36
+ dbits 36 @a9d36
+ abits 10 @a10d18
+ dbits 18 @a10d18
+ abits 11 @a11d9
+ dbits 9 @a11d9
+ abits 12 @a12d4
+ dbits 4 @a12d4
+ abits 13 @a13d2
+ dbits 2 @a13d2
+ abits 14 @a14d1
+ dbits 1 @a14d1
+ groups 2
+ ports 1 1
+ wrmode 0 1
+ enable 1 1
+ transp 0 0
+ clocks 2 3
+ clkpol 2 3
+endbram
+
+match $__XILINX_RAMB16
+ min bits 4096
+ min efficiency 5
+ shuffle_enable B
+ make_transp
+endmatch
diff --git a/techlibs/xilinx/xc2v_brams_map.v b/techlibs/xilinx/xc2v_brams_map.v
new file mode 100644
index 000000000..dc698f956
--- /dev/null
+++ b/techlibs/xilinx/xc2v_brams_map.v
@@ -0,0 +1,266 @@
+// Virtex 2, Virtex 2 Pro, Spartan 3, Spartan 3E, Spartan 3A block RAM
+// mapping (Spartan 3A is a superset of the other four).
+
+// ------------------------------------------------------------------------
+
+module \$__XILINX_RAMB16 (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN);
+ parameter CFG_ABITS = 9;
+ parameter CFG_DBITS = 36;
+ parameter CFG_ENABLE_B = 1;
+
+ parameter CLKPOL2 = 1;
+ parameter CLKPOL3 = 1;
+ parameter [18431:0] INIT = 18432'bx;
+
+ input CLK2;
+ input CLK3;
+
+ input [CFG_ABITS-1:0] A1ADDR;
+ output [CFG_DBITS-1:0] A1DATA;
+ input A1EN;
+
+ input [CFG_ABITS-1:0] B1ADDR;
+ input [CFG_DBITS-1:0] B1DATA;
+ input [CFG_ENABLE_B-1:0] B1EN;
+
+ generate if (CFG_DBITS == 1) begin
+ wire DOB;
+ RAMB16_S1_S1 #(
+ `include "brams_init_16.vh"
+ .WRITE_MODE_A("READ_FIRST"),
+ .WRITE_MODE_B("READ_FIRST"),
+ ) _TECHMAP_REPLACE_ (
+ .DIA(1'd0),
+ .DOA(A1DATA),
+ .ADDRA(A1ADDR),
+ .CLKA(CLK2 ^ !CLKPOL2),
+ .ENA(A1EN),
+ .SSRA(|0),
+ .WEA(1'b0),
+
+ .DIB(B1DATA),
+ .DOB(DOB),
+ .ADDRB(B1ADDR),
+ .CLKB(CLK3 ^ !CLKPOL3),
+ .ENB(|1),
+ .SSRB(|0),
+ .WEB(B1EN)
+ );
+ end else if (CFG_DBITS == 2) begin
+ wire [1:0] DOB;
+ RAMB16_S2_S2 #(
+ `include "brams_init_16.vh"
+ .WRITE_MODE_A("READ_FIRST"),
+ .WRITE_MODE_B("READ_FIRST"),
+ ) _TECHMAP_REPLACE_ (
+ .DIA(2'd0),
+ .DOA(A1DATA),
+ .ADDRA(A1ADDR),
+ .CLKA(CLK2 ^ !CLKPOL2),
+ .ENA(A1EN),
+ .SSRA(|0),
+ .WEA(1'b0),
+
+ .DIB(B1DATA),
+ .DOB(DOB),
+ .ADDRB(B1ADDR),
+ .CLKB(CLK3 ^ !CLKPOL3),
+ .ENB(|1),
+ .SSRB(|0),
+ .WEB(B1EN)
+ );
+ end else if (CFG_DBITS == 4) begin
+ wire [3:0] DOB;
+ RAMB16_S4_S4 #(
+ `include "brams_init_16.vh"
+ .WRITE_MODE_A("READ_FIRST"),
+ .WRITE_MODE_B("READ_FIRST"),
+ ) _TECHMAP_REPLACE_ (
+ .DIA(4'd0),
+ .DOA(A1DATA),
+ .ADDRA(A1ADDR),
+ .CLKA(CLK2 ^ !CLKPOL2),
+ .ENA(A1EN),
+ .SSRA(|0),
+ .WEA(1'b0),
+
+ .DIB(B1DATA),
+ .DOB(DOB),
+ .ADDRB(B1ADDR),
+ .CLKB(CLK3 ^ !CLKPOL3),
+ .ENB(|1),
+ .SSRB(|0),
+ .WEB(B1EN)
+ );
+ end else if (CFG_DBITS == 9) begin
+ wire [7:0] DOB;
+ wire DOPB;
+ RAMB16_S9_S9 #(
+ `include "brams_init_18.vh"
+ .WRITE_MODE_A("READ_FIRST"),
+ .WRITE_MODE_B("READ_FIRST"),
+ ) _TECHMAP_REPLACE_ (
+ .DIA(8'd0),
+ .DIPA(1'd0),
+ .DOA(A1DATA[7:0]),
+ .DOPA(A1DATA[8]),
+ .ADDRA(A1ADDR),
+ .CLKA(CLK2 ^ !CLKPOL2),
+ .ENA(A1EN),
+ .SSRA(|0),
+ .WEA(1'b0),
+
+ .DIB(B1DATA[7:0]),
+ .DIPB(B1DATA[8]),
+ .DOB(DOB),
+ .DOPB(DOPB),
+ .ADDRB(B1ADDR),
+ .CLKB(CLK3 ^ !CLKPOL3),
+ .ENB(|1),
+ .SSRB(|0),
+ .WEB(B1EN)
+ );
+ end else if (CFG_DBITS == 18) begin
+ wire [15:0] DOB;
+ wire [1:0] DOPB;
+ RAMB16_S18_S18 #(
+ `include "brams_init_18.vh"
+ .WRITE_MODE_A("READ_FIRST"),
+ .WRITE_MODE_B("READ_FIRST"),
+ ) _TECHMAP_REPLACE_ (
+ .DIA(16'd0),
+ .DIPA(2'd0),
+ .DOA({A1DATA[16:9], A1DATA[7:0]}),
+ .DOPA({A1DATA[17], A1DATA[8]}),
+ .ADDRA(A1ADDR),
+ .CLKA(CLK2 ^ !CLKPOL2),
+ .ENA(A1EN),
+ .SSRA(|0),
+ .WEA(1'b0),
+
+ .DIB({B1DATA[16:9], B1DATA[7:0]}),
+ .DIPB({B1DATA[17], B1DATA[8]}),
+ .DOB(DOB),
+ .DOPB(DOPB),
+ .ADDRB(B1ADDR),
+ .CLKB(CLK3 ^ !CLKPOL3),
+ .ENB(|1),
+ .SSRB(|0),
+ .WEB(B1EN)
+ );
+ end else if (CFG_DBITS == 36) begin
+ wire [31:0] DOB;
+ wire [3:0] DOPB;
+ RAMB16_S36_S36 #(
+ `include "brams_init_18.vh"
+ .WRITE_MODE_A("READ_FIRST"),
+ .WRITE_MODE_B("READ_FIRST"),
+ ) _TECHMAP_REPLACE_ (
+ .DIA(32'd0),
+ .DIPA(4'd0),
+ .DOA({A1DATA[34:27], A1DATA[25:18], A1DATA[16:9], A1DATA[7:0]}),
+ .DOPA({A1DATA[35], A1DATA[26], A1DATA[17], A1DATA[8]}),
+ .ADDRA(A1ADDR),
+ .CLKA(CLK2 ^ !CLKPOL2),
+ .ENA(A1EN),
+ .SSRA(|0),
+ .WEA(1'b0),
+
+ .DIB({B1DATA[34:27], B1DATA[25:18], B1DATA[16:9], B1DATA[7:0]}),
+ .DIPB({B1DATA[35], B1DATA[26], B1DATA[17], B1DATA[8]}),
+ .DOB(DOB),
+ .DOPB(DOPB),
+ .ADDRB(B1ADDR),
+ .CLKB(CLK3 ^ !CLKPOL3),
+ .ENB(|1),
+ .SSRB(|0),
+ .WEB(B1EN)
+ );
+ end else begin
+ $error("Strange block RAM data width.");
+ end endgenerate
+endmodule
+
+
+// Version with separate byte enables, only available on Spartan 3A.
+
+module \$__XILINX_RAMB16BWE (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN);
+ parameter CFG_ABITS = 9;
+ parameter CFG_DBITS = 36;
+ parameter CFG_ENABLE_B = 4;
+
+ parameter CLKPOL2 = 1;
+ parameter CLKPOL3 = 1;
+ parameter [18431:0] INIT = 18432'bx;
+
+ input CLK2;
+ input CLK3;
+
+ input [CFG_ABITS-1:0] A1ADDR;
+ output [CFG_DBITS-1:0] A1DATA;
+ input A1EN;
+
+ input [CFG_ABITS-1:0] B1ADDR;
+ input [CFG_DBITS-1:0] B1DATA;
+ input [CFG_ENABLE_B-1:0] B1EN;
+
+ generate if (CFG_DBITS == 18) begin
+ wire [15:0] DOB;
+ wire [1:0] DOPB;
+ RAMB16BWE_S18_S18 #(
+ `include "brams_init_18.vh"
+ .WRITE_MODE_A("READ_FIRST"),
+ .WRITE_MODE_B("READ_FIRST"),
+ ) _TECHMAP_REPLACE_ (
+ .DIA(16'd0),
+ .DIPA(2'd0),
+ .DOA({A1DATA[16:9], A1DATA[7:0]}),
+ .DOPA({A1DATA[17], A1DATA[8]}),
+ .ADDRA(A1ADDR),
+ .CLKA(CLK2 ^ !CLKPOL2),
+ .ENA(A1EN),
+ .SSRA(|0),
+ .WEA(2'b00),
+
+ .DIB({B1DATA[16:9], B1DATA[7:0]}),
+ .DIPB({B1DATA[17], B1DATA[8]}),
+ .DOB(DOB),
+ .DOPB(DOPB),
+ .ADDRB(B1ADDR),
+ .CLKB(CLK3 ^ !CLKPOL3),
+ .ENB(|1),
+ .SSRB(|0),
+ .WEB(B1EN)
+ );
+ end else if (CFG_DBITS == 36) begin
+ wire [31:0] DOB;
+ wire [3:0] DOPB;
+ RAMB16BWE_S36_S36 #(
+ `include "brams_init_18.vh"
+ .WRITE_MODE_A("READ_FIRST"),
+ .WRITE_MODE_B("READ_FIRST"),
+ ) _TECHMAP_REPLACE_ (
+ .DIA(32'd0),
+ .DIPA(4'd0),
+ .DOA({A1DATA[34:27], A1DATA[25:18], A1DATA[16:9], A1DATA[7:0]}),
+ .DOPA({A1DATA[35], A1DATA[26], A1DATA[17], A1DATA[8]}),
+ .ADDRA(A1ADDR),
+ .CLKA(CLK2 ^ !CLKPOL2),
+ .ENA(A1EN),
+ .SSRA(|0),
+ .WEA(4'b0000),
+
+ .DIB({B1DATA[34:27], B1DATA[25:18], B1DATA[16:9], B1DATA[7:0]}),
+ .DIPB({B1DATA[35], B1DATA[26], B1DATA[17], B1DATA[8]}),
+ .DOB(DOB),
+ .DOPB(DOPB),
+ .ADDRB(B1ADDR),
+ .CLKB(CLK3 ^ !CLKPOL3),
+ .ENB(|1),
+ .SSRB(|0),
+ .WEB(B1EN)
+ );
+ end else begin
+ $error("Strange block RAM data width.");
+ end endgenerate
+endmodule
diff --git a/techlibs/xilinx/xc3sa_brams.txt b/techlibs/xilinx/xc3sa_brams.txt
new file mode 100644
index 000000000..22a62bd2c
--- /dev/null
+++ b/techlibs/xilinx/xc3sa_brams.txt
@@ -0,0 +1,51 @@
+# Spartan 3A block RAM rules.
+
+bram $__XILINX_RAMB16
+ init 1
+ abits 11 @a11d9
+ dbits 9 @a11d9
+ abits 12 @a12d4
+ dbits 4 @a12d4
+ abits 13 @a13d2
+ dbits 2 @a13d2
+ abits 14 @a14d1
+ dbits 1 @a14d1
+ groups 2
+ ports 1 1
+ wrmode 0 1
+ enable 1 1
+ transp 0 0
+ clocks 2 3
+ clkpol 2 3
+endbram
+
+bram $__XILINX_RAMB16BWE
+ init 1
+ abits 9 @a9d36
+ dbits 36 @a9d36
+ abits 10 @a10d18
+ dbits 18 @a10d18
+ groups 2
+ ports 1 1
+ wrmode 0 1
+ enable 1 4 @a9d36
+ enable 1 2 @a10d18
+ transp 0 0
+ clocks 2 3
+ clkpol 2 3
+endbram
+
+match $__XILINX_RAMB16
+ min bits 4096
+ min efficiency 5
+ shuffle_enable B
+ make_transp
+ or_next_if_better
+endmatch
+
+match $__XILINX_RAMB16BWE
+ min bits 4096
+ min efficiency 5
+ shuffle_enable B
+ make_transp
+endmatch
diff --git a/techlibs/xilinx/xc3sda_brams.txt b/techlibs/xilinx/xc3sda_brams.txt
new file mode 100644
index 000000000..12c68ffd5
--- /dev/null
+++ b/techlibs/xilinx/xc3sda_brams.txt
@@ -0,0 +1,33 @@
+# Spartan 3A DSP block RAM rules.
+
+bram $__XILINX_RAMB16BWER_TDP
+ init 1
+ abits 9 @a9d36
+ dbits 36 @a9d36
+ abits 10 @a10d18
+ dbits 18 @a10d18
+ abits 11 @a11d9
+ dbits 9 @a11d9
+ abits 12 @a12d4
+ dbits 4 @a12d4
+ abits 13 @a13d2
+ dbits 2 @a13d2
+ abits 14 @a14d1
+ dbits 1 @a14d1
+ groups 2
+ ports 1 1
+ wrmode 0 1
+ enable 1 4 @a9d36
+ enable 1 2 @a10d18
+ enable 1 1 @a11d9 @a12d4 @a13d2 @a14d1
+ transp 0 0
+ clocks 2 3
+ clkpol 2 3
+endbram
+
+match $__XILINX_RAMB16BWER_TDP
+ min bits 4096
+ min efficiency 5
+ shuffle_enable B
+ make_transp
+endmatch
diff --git a/techlibs/xilinx/xc6s_brams.txt b/techlibs/xilinx/xc6s_brams.txt
index 17cd8e355..6457097db 100644
--- a/techlibs/xilinx/xc6s_brams.txt
+++ b/techlibs/xilinx/xc6s_brams.txt
@@ -1,3 +1,4 @@
+# Spartan 6 block RAM rules.
bram $__XILINX_RAMB8BWER_SDP
init 1
diff --git a/techlibs/xilinx/xc6s_brams_map.v b/techlibs/xilinx/xc6s_brams_map.v
index 16fd15e74..9577eebe4 100644
--- a/techlibs/xilinx/xc6s_brams_map.v
+++ b/techlibs/xilinx/xc6s_brams_map.v
@@ -1,3 +1,6 @@
+// Spartan 3A DSP and Spartan 6 block RAM mapping (Spartan 6 is a superset of
+// Spartan 3A DSP).
+
module \$__XILINX_RAMB8BWER_SDP (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN);
parameter CLKPOL2 = 1;
parameter CLKPOL3 = 1;
diff --git a/techlibs/xilinx/xc7_brams_map.v b/techlibs/xilinx/xc7_brams_map.v
index 7ea49158d..2b6ad0da6 100644
--- a/techlibs/xilinx/xc7_brams_map.v
+++ b/techlibs/xilinx/xc7_brams_map.v
@@ -1,3 +1,5 @@
+// Virtex 6 and Series 7 block RAM mapping.
+
module \$__XILINX_RAMB36_SDP (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN);
parameter CLKPOL2 = 1;
parameter CLKPOL3 = 1;
diff --git a/techlibs/xilinx/xc7_xcu_brams.txt b/techlibs/xilinx/xc7_xcu_brams.txt
index c63218ae1..650367abf 100644
--- a/techlibs/xilinx/xc7_xcu_brams.txt
+++ b/techlibs/xilinx/xc7_xcu_brams.txt
@@ -1,3 +1,5 @@
+# Virtex 6, Series 7, Ultrascale, Ultrascale Plus block RAM rules.
+
bram $__XILINX_RAMB36_SDP
init 1
abits 9
diff --git a/techlibs/xilinx/xcu_brams_map.v b/techlibs/xilinx/xcu_brams_map.v
index 6e7925b57..b6719b2dd 100644
--- a/techlibs/xilinx/xcu_brams_map.v
+++ b/techlibs/xilinx/xcu_brams_map.v
@@ -1,3 +1,5 @@
+// Ultrascale and Ultrascale Plus block RAM mapping.
+
module \$__XILINX_RAMB36_SDP (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN);
parameter CLKPOL2 = 1;
parameter CLKPOL3 = 1;