aboutsummaryrefslogtreecommitdiffstats
path: root/techlibs/ecp5/brams_map.v
blob: 0353cbadb70493ce367fa83871f9a1f0b425d17a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
module \$__ECP5_DP16KD (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN);
	parameter CFG_ABITS = 10;
	parameter CFG_DBITS = 18;
	parameter CFG_ENABLE_A = 2;

	parameter CLKPOL2 = 1;
	parameter CLKPOL3 = 1;
	parameter [18431:0] INIT = 18432'bx;
	parameter TRANSP2 = 0;

	input CLK2;
	input CLK3;

	input [CFG_ABITS-1:0] A1ADDR;
	input [CFG_DBITS-1:0] A1DATA;
	input [CFG_ENABLE_A-1:0] A1EN;

	input [CFG_ABITS-1:0] B1ADDR;
	output [CFG_DBITS-1:0] B1DATA;
	input B1EN;

	localparam CLKAMUX = CLKPOL2 ? "CLKA" : "INV";
	localparam CLKBMUX = CLKPOL3 ? "CLKB" : "INV";

	localparam WRITEMODE_A = TRANSP2 ? "WRITETHROUGH" : "READBEFOREWRITE";

	generate if (CFG_DBITS == 1) begin
		DP16KD #(
			`include "bram_init_1_2_4.vh"
			.DATA_WIDTH_A(1),
			.DATA_WIDTH_B(1),
			.CLKAMUX(CLKAMUX),
			.CLKBMUX(CLKBMUX),
			.WRITEMODE_A(WRITEMODE_A),
			.WRITEMODE_B("READBEFOREWRITE"),
			.GSR("AUTO")
		) _TECHMAP_REPLACE_ (
			`include "bram_conn_1.vh"
			.CLKA(CLK2), .CLKB(CLK3),
			.WEA(|A1EN), .CEA(1'b1), .OCEA(1'b1),
			.WEB(1'b0), .CEB(B1EN), .OCEB(1'b1),
			.RSTA(1'b0), .RSTB(1'b0)
		);
	end else if (CFG_DBITS == 2) begin
		DP16KD #(
			`include "bram_init_1_2_4.vh"
			.DATA_WIDTH_A(2),
			.DATA_WIDTH_B(2),
			.CLKAMUX(CLKAMUX),
			.CLKBMUX(CLKBMUX),
			.WRITEMODE_A(WRITEMODE_A),
			.WRITEMODE_B("READBEFOREWRITE"),
			.GSR("AUTO")
		) _TECHMAP_REPLACE_ (
			`include "bram_conn_2.vh"
			.CLKA(CLK2), .CLKB(CLK3),
			.WEA(|A1EN), .CEA(1'b1), .OCEA(1'b1),
			.WEB(1'b0), .CEB(B1EN), .OCEB(1'b1),
			.RSTA(1'b0), .RSTB(1'b0)
		);
	end else if (CFG_DBITS <= 4) begin
		DP16KD #(
			`include "bram_init_1_2_4.vh"
			.DATA_WIDTH_A(4),
			.DATA_WIDTH_B(4),
			.CLKAMUX(CLKAMUX),
			.CLKBMUX(CLKBMUX),
			.WRITEMODE_A(WRITEMODE_A),
			.WRITEMODE_B("READBEFOREWRITE"),
			.GSR("AUTO")
		) _TECHMAP_REPLACE_ (
			`include "bram_conn_4.vh"
			.CLKA(CLK2), .CLKB(CLK3),
			.WEA(|A1EN), .CEA(1'b1), .OCEA(1'b1),
			.WEB(1'b0), .CEB(B1EN), .OCEB(1'b1),
			.RSTA(1'b0), .RSTB(1'b0)
		);
	end else if (CFG_DBITS <= 9) begin
		DP16KD #(
			`include "bram_init_9_18_36.vh"
			.DATA_WIDTH_A(9),
			.DATA_WIDTH_B(9),
			.CLKAMUX(CLKAMUX),
			.CLKBMUX(CLKBMUX),
			.WRITEMODE_A(WRITEMODE_A),
			.WRITEMODE_B("READBEFOREWRITE"),
			.GSR("AUTO")
		) _TECHMAP_REPLACE_ (
			`include "bram_conn_9.vh"
			.CLKA(CLK2), .CLKB(CLK3),
			.WEA(|A1EN), .CEA(1'b1), .OCEA(1'b1),
			.WEB(1'b0), .CEB(B1EN), .OCEB(1'b1),
			.RSTA(1'b0), .RSTB(1'b0)
		);
	end else if (CFG_DBITS <= 18) begin
		DP16KD #(
			`include "bram_init_9_18_36.vh"
			.DATA_WIDTH_A(18),
			.DATA_WIDTH_B(18),
			.CLKAMUX(CLKAMUX),
			.CLKBMUX(CLKBMUX),
			.WRITEMODE_A(WRITEMODE_A),
			.WRITEMODE_B("READBEFOREWRITE"),
			.GSR("AUTO")
		) _TECHMAP_REPLACE_ (
			`include "bram_conn_18.vh"
			.CLKA(CLK2), .CLKB(CLK3),
			.WEA(|A1EN), .CEA(1'b1), .OCEA(1'b1),
			.WEB(1'b0), .CEB(B1EN), .OCEB(1'b1),
			.RSTA(1'b0), .RSTB(1'b0)
		);
	end else begin
		wire TECHMAP_FAIL = 1'b1;
	end endgenerate
endmodule
pan class="o">::vector<RTLIL::Cell*> &new_cells) { // Copy the contents of the flattened cell dict<IdString, IdString> memory_map; for (auto &tpl_memory_it : tpl->memories) { RTLIL::Memory *new_memory = module->addMemory(map_name(cell, tpl_memory_it.second), tpl_memory_it.second); map_attributes(cell, new_memory, tpl_memory_it.second->name); memory_map[tpl_memory_it.first] = new_memory->name; design->select(module, new_memory); } dict<RTLIL::Wire*, RTLIL::Wire*> wire_map; dict<IdString, IdString> positional_ports; for (auto tpl_wire : tpl->wires()) { if (tpl_wire->port_id > 0) positional_ports.emplace(stringf("$%d", tpl_wire->port_id), tpl_wire->name); RTLIL::Wire *new_wire = nullptr; if (tpl_wire->name[0] == '\\') { RTLIL::Wire *hier_wire = module->wire(concat_name(cell, tpl_wire->name)); if (hier_wire != nullptr && hier_wire->get_bool_attribute(ID::hierconn)) { hier_wire->attributes.erase(ID::hierconn); if (GetSize(hier_wire) < GetSize(tpl_wire)) { log_warning("Widening signal %s.%s to match size of %s.%s (via %s.%s).\n", log_id(module), log_id(hier_wire), log_id(tpl), log_id(tpl_wire), log_id(module), log_id(cell)); hier_wire->width = GetSize(tpl_wire); } new_wire = hier_wire; } } if (new_wire == nullptr) { new_wire = module->addWire(map_name(cell, tpl_wire), tpl_wire); new_wire->port_input = new_wire->port_output = false; new_wire->port_id = false; } map_attributes(cell, new_wire, tpl_wire->name); wire_map[tpl_wire] = new_wire; design->select(module, new_wire); } for (auto &tpl_proc_it : tpl->processes) { RTLIL::Process *new_proc = module->addProcess(map_name(cell, tpl_proc_it.second), tpl_proc_it.second); map_attributes(cell, new_proc, tpl_proc_it.second->name); auto rewriter = [&](RTLIL::SigSpec &sig) { map_sigspec(wire_map, sig); }; new_proc->rewrite_sigspecs(rewriter); design->select(module, new_proc); } for (auto tpl_cell : tpl->cells()) { RTLIL::Cell *new_cell = module->addCell(map_name(cell, tpl_cell), tpl_cell); map_attributes(cell, new_cell, tpl_cell->name); if (new_cell->type.in(ID($memrd), ID($memwr), ID($meminit))) { IdString memid = new_cell->getParam(ID::MEMID).decode_string(); new_cell->setParam(ID::MEMID, Const(memory_map.at(memid).str())); } else if (new_cell->type == ID($mem)) { IdString memid = new_cell->getParam(ID::MEMID).decode_string(); new_cell->setParam(ID::MEMID, Const(concat_name(cell, memid).str())); } auto rewriter = [&](RTLIL::SigSpec &sig) { map_sigspec(wire_map, sig); }; new_cell->rewrite_sigspecs(rewriter); design->select(module, new_cell); new_cells.push_back(new_cell); } for (auto &tpl_conn_it : tpl->connections()) { RTLIL::SigSig new_conn = tpl_conn_it; map_sigspec(wire_map, new_conn.first); map_sigspec(wire_map, new_conn.second); module->connect(new_conn); } // Attach port connections of the flattened cell pool<SigBit> tpl_driven; for (auto tpl_cell : tpl->cells()) for (auto &tpl_conn : tpl_cell->connections()) if (tpl_cell->output(tpl_conn.first)) for (auto bit : tpl_conn.second) tpl_driven.insert(bit); for (auto &tpl_conn : tpl->connections()) for (auto bit : tpl_conn.first) tpl_driven.insert(bit); SigMap sigmap(module); for (auto &port_it : cell->connections()) { IdString port_name = port_it.first; if (positional_ports.count(port_name) > 0) port_name = positional_ports.at(port_name); if (tpl->wire(port_name) == nullptr || tpl->wire(port_name)->port_id == 0) { if (port_name.begins_with("$")) log_error("Can't map port `%s' of cell `%s' to template `%s'!\n", port_name.c_str(), cell->name.c_str(), tpl->name.c_str()); continue; } if (GetSize(port_it.second) == 0) continue; RTLIL::Wire *tpl_wire = tpl->wire(port_name); RTLIL::SigSig new_conn; if (tpl_wire->port_output && !tpl_wire->port_input) { new_conn.first = port_it.second; new_conn.second = tpl_wire; } else if (!tpl_wire->port_output && tpl_wire->port_input) { new_conn.first = tpl_wire; new_conn.second = port_it.second; } else { SigSpec sig_tpl = tpl_wire, sig_mod = port_it.second; for (int i = 0; i < GetSize(sig_tpl) && i < GetSize(sig_mod); i++) { if (tpl_driven.count(sig_tpl[i])) { new_conn.first.append(sig_mod[i]); new_conn.second.append(sig_tpl[i]); } else { new_conn.first.append(sig_tpl[i]); new_conn.second.append(sig_mod[i]); } } } map_sigspec(wire_map, new_conn.first, module); map_sigspec(wire_map, new_conn.second, module); if (new_conn.second.size() > new_conn.first.size()) new_conn.second.remove(new_conn.first.size(), new_conn.second.size() - new_conn.first.size()); if (new_conn.second.size() < new_conn.first.size()) new_conn.second.append(RTLIL::SigSpec(RTLIL::State::S0, new_conn.first.size() - new_conn.second.size())); log_assert(new_conn.first.size() == new_conn.second.size()); if (sigmap(new_conn.first).has_const()) log_error("Mismatch in directionality for cell port %s.%s.%s: %s <= %s\n", log_id(module), log_id(cell), log_id(port_it.first), log_signal(new_conn.first), log_signal(new_conn.second)); module->connect(new_conn); } module->remove(cell); } void flatten_module(RTLIL::Design *design, RTLIL::Module *module, pool<RTLIL::Module*> &used_modules) { if (!design->selected(module) || module->get_blackbox_attribute(ignore_wb)) return; std::vector<RTLIL::Cell*> worklist = module->selected_cells(); while (!worklist.empty()) { RTLIL::Cell *cell = worklist.back(); worklist.pop_back(); if (!design->has(cell->type)) continue; RTLIL::Module *tpl = design->module(cell->type); if (tpl->get_blackbox_attribute(ignore_wb)) continue; if (cell->get_bool_attribute(ID::keep_hierarchy) || tpl->get_bool_attribute(ID::keep_hierarchy)) { log("Keeping %s.%s (found keep_hierarchy attribute).\n", log_id(module), log_id(cell)); used_modules.insert(tpl); continue; } log_debug("Flattening %s.%s (%s).\n", log_id(module), log_id(cell), log_id(cell->type)); // If a design is fully selected and has a top module defined, topological sorting ensures that all cells // added during flattening are black boxes, and flattening is finished in one pass. However, when flattening // individual modules, this isn't the case, and the newly added cells might have to be flattened further. flatten_cell(design, module, cell, tpl, worklist); } } }; struct FlattenPass : public Pass { FlattenPass() : Pass("flatten", "flatten design") { } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" flatten [options] [selection]\n"); log("\n"); log("This pass flattens the design by replacing cells by their implementation. This\n"); log("pass is very similar to the 'techmap' pass. The only difference is that this\n"); log("pass is using the current design as mapping library.\n"); log("\n"); log("Cells and/or modules with the 'keep_hierarchy' attribute set will not be\n"); log("flattened by this command.\n"); log("\n"); log(" -wb\n"); log(" Ignore the 'whitebox' attribute on cell implementations.\n"); log("\n"); } void execute(std::vector<std::string> args, RTLIL::Design *design) override { log_header(design, "Executing FLATTEN pass (flatten design).\n"); log_push(); FlattenWorker worker; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { if (args[argidx] == "-wb") { worker.ignore_wb = true; continue; } break; } extra_args(args, argidx, design); RTLIL::Module *top = nullptr; if (design->full_selection()) for (auto module : design->modules()) if (module->get_bool_attribute(ID::top)) top = module; pool<RTLIL::Module*> used_modules; if (top == nullptr) used_modules = design->modules(); else used_modules.insert(top); TopoSort<RTLIL::Module*, IdString::compare_ptr_by_name<RTLIL::Module>> topo_modules; pool<RTLIL::Module*> worklist = used_modules; while (!worklist.empty()) { RTLIL::Module *module = worklist.pop(); for (auto cell : module->selected_cells()) { RTLIL::Module *tpl = design->module(cell->type); if (tpl != nullptr) { if (topo_modules.database.count(tpl) == 0) worklist.insert(tpl); topo_modules.edge(tpl, module); } } } if (!topo_modules.sort()) log_error("Cannot flatten a design containing recursive instantiations.\n"); for (auto module : topo_modules.sorted) worker.flatten_module(design, module, used_modules); if (top != nullptr) for (auto module : design->modules().to_vector()) if (!used_modules[module] && !module->get_blackbox_attribute(worker.ignore_wb)) { log("Deleting now unused module %s.\n", log_id(module)); design->remove(module); } log_pop(); } } FlattenPass; PRIVATE_NAMESPACE_END