-
Notifications
You must be signed in to change notification settings - Fork 948
ram: Cross platform testing and support #9991
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 6 commits
cbbbf9c
62711fe
3d95c43
6a8b7a8
582763a
e9d4890
30cc938
be3543f
d495b3b
7c8125a
0dcdb68
132725e
3ca123e
813287e
eb55fa6
1e6c537
4ca740a
57631b6
9d02db1
cab68f4
22fbb07
0cd9418
0b0e823
55f02a6
5b53d5a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,6 +5,7 @@ | |
|
|
||
| #include <array> | ||
| #include <functional> | ||
| #include <map> | ||
| #include <memory> | ||
| #include <string> | ||
| #include <utility> | ||
|
|
@@ -50,6 +51,30 @@ class TritonRoute; | |
|
|
||
| namespace ram { | ||
|
|
||
| enum class PinRoleType | ||
| { | ||
| Clock, | ||
| DataIn, | ||
| DataOut, | ||
| WriteEnable, | ||
| TriEnable, | ||
| Select, // for mux support in future | ||
| Power, | ||
| Ground | ||
| }; | ||
|
|
||
| struct PinRole | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would change |
||
| { | ||
| PinRoleType type; | ||
| int index; | ||
|
|
||
| // for map so that keys are comparable | ||
| bool operator<(const PinRole& other) const | ||
| { | ||
| return std::tie(type, index) < std::tie(other.type, other.index); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. warning: no header providing "std::tie" is directly included [misc-include-cleaner] src/ram/include/ram/ram.h:9: - #include <utility>
+ #include <tuple>
+ #include <utility>
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. warning: no header providing "std::tie" is directly included [misc-include-cleaner] src/ram/include/ram/ram.h:10: - #include <utility>
+ #include <tuple>
+ #include <utility> |
||
| } | ||
| }; | ||
|
|
||
| class RamGen | ||
| { | ||
| public: | ||
|
|
@@ -96,15 +121,10 @@ class RamGen | |
|
|
||
| private: | ||
| void findMasters(); | ||
| std::map<PinRole, std::string> buildPinMap(odb::dbMaster*); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. warning: no header providing "std::map" is directly included [misc-include-cleaner] src/ram/include/ram/ram.h:7: - #include <memory>
+ #include <map>
+ #include <memory> |
||
| odb::dbMaster* findMaster(const std::function<bool(sta::LibertyPort*)>& match, | ||
| const char* name); | ||
| odb::dbNet* makeNet(const std::string& prefix, const std::string& name); | ||
| odb::dbInst* makeInst( | ||
| Layout* layout, | ||
| const std::string& prefix, | ||
| const std::string& name, | ||
| odb::dbMaster* master, | ||
| const std::vector<std::pair<std::string, odb::dbNet*>>& connections); | ||
| odb::dbInst* makeInst( | ||
| Cell* cell, | ||
| const std::string& prefix, | ||
|
|
@@ -168,6 +188,13 @@ class RamGen | |
| odb::dbMaster* buffer_cell_{nullptr}; | ||
| odb::dbMaster* tapcell_{nullptr}; | ||
|
|
||
| std::map<PinRole, std::string> storage_pins_; | ||
| std::map<PinRole, std::string> tristate_pins_; | ||
| std::map<PinRole, std::string> inv_pins_; | ||
| std::map<PinRole, std::string> and2_pins_; | ||
| std::map<PinRole, std::string> clock_gate_pins_; | ||
| std::map<PinRole, std::string> buffer_pins_; | ||
|
|
||
| std::vector<odb::dbBTerm*> addr_inputs_; | ||
| std::vector<odb::dbBTerm*> data_inputs_; | ||
| std::vector<std::vector<odb::dbBTerm*>> q_outputs_; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,8 +8,10 @@ | |
| #include <fstream> | ||
| #include <functional> | ||
| #include <limits> | ||
| #include <map> | ||
| #include <memory> | ||
| #include <string> | ||
| #include <tuple> | ||
| #include <utility> | ||
| #include <vector> | ||
|
|
||
|
|
@@ -120,18 +122,19 @@ std::unique_ptr<Cell> RamGen::makeBit(const std::string& prefix, | |
| prefix, | ||
| "bit", | ||
| storage_cell_, | ||
| {{storage_cell_->findMTerm("CLK") ? "CLK" : "GATE", clock}, | ||
| {"D", data_input}, | ||
| {"Q", storage_net}}); | ||
| {{storage_pins_[{PinRoleType::Clock, 0}], clock}, | ||
| {storage_pins_[{PinRoleType::DataIn, 0}], data_input}, | ||
| {storage_pins_[{PinRoleType::DataOut, 0}], storage_net}}); | ||
|
|
||
| for (int read_port = 0; read_port < read_ports; ++read_port) { | ||
| makeInst(bit_cell.get(), | ||
| prefix, | ||
| fmt::format("obuf{}", read_port), | ||
| tristate_cell_, | ||
| {{"A", storage_net}, | ||
| {"TE_B", select[read_port]}, | ||
| {"Z", data_output[read_port]}}); | ||
| makeInst( | ||
| bit_cell.get(), | ||
| prefix, | ||
| fmt::format("obuf{}", read_port), | ||
| tristate_cell_, | ||
| {{tristate_pins_[{PinRoleType::DataIn, 0}], storage_net}, | ||
| {tristate_pins_[{PinRoleType::TriEnable, 0}], select[read_port]}, | ||
| {tristate_pins_[{PinRoleType::DataOut, 0}], data_output[read_port]}}); | ||
| } | ||
|
|
||
| return bit_cell; | ||
|
|
@@ -178,7 +181,9 @@ void RamGen::makeSlice(const int slice_idx, | |
| prefix, | ||
| "cg", | ||
| clock_gate_cell_, | ||
| {{"CLK", clock}, {"GATE", we0_net}, {"GCLK", gclock_net}}); | ||
| {{clock_gate_pins_[{PinRoleType::Clock, 0}], clock}, | ||
| {clock_gate_pins_[{PinRoleType::DataIn, 0}], we0_net}, | ||
| {clock_gate_pins_[{PinRoleType::DataOut, 0}], gclock_net}}); | ||
|
|
||
| // Make clock and | ||
| // this AND gate needs to be fed a net created by a decoder | ||
|
|
@@ -187,15 +192,18 @@ void RamGen::makeSlice(const int slice_idx, | |
| prefix, | ||
| "gcand", | ||
| and2_cell_, | ||
| {{"A", selects[0]}, {"B", write_enable}, {"X", we0_net}}); | ||
| {{and2_pins_[{PinRoleType::DataIn, 0}], selects[0]}, | ||
| {and2_pins_[{PinRoleType::DataIn, 1}], write_enable}, | ||
| {and2_pins_[{PinRoleType::DataOut, 0}], we0_net}}); | ||
|
|
||
| // Make select inverters | ||
| for (int i = 0; i < selects.size(); ++i) { | ||
| makeInst(sel_cell.get(), | ||
| prefix, | ||
| fmt::format("select_inv_{}", i), | ||
| inv_cell_, | ||
| {{"A", selects[i]}, {"Y", select_b_nets[i]}}); | ||
| {{inv_pins_[{PinRoleType::DataIn, 0}], selects[i]}, | ||
| {inv_pins_[{PinRoleType::DataOut, 0}], select_b_nets[i]}}); | ||
| } | ||
|
|
||
| ram_grid_.addCell(std::move(sel_cell), start_bit_idx + mask_size + slice_idx); | ||
|
|
@@ -278,30 +286,36 @@ std::unique_ptr<Cell> RamGen::makeDecoder( | |
| prefix, | ||
| fmt::format("and_layer{}", i), | ||
| and2_cell_, | ||
| {{"A", addr_nets[i]}, | ||
| {"B", addr_nets[i + 1]}, | ||
| {"X", decoder_out_net}}); | ||
| {{and2_pins_[{PinRoleType::DataIn, 0}], addr_nets[i]}, | ||
| {and2_pins_[{PinRoleType::DataIn, 1}], addr_nets[i + 1]}, | ||
| {and2_pins_[{PinRoleType::DataOut, 0}], decoder_out_net}}); | ||
| prev_net = input_net; | ||
| } else if (i == 0) { | ||
| makeInst(word_cell.get(), | ||
| prefix, | ||
| fmt::format("and_layer{}", i), | ||
| and2_cell_, | ||
| {{"A", addr_nets[i]}, {"B", input_net}, {"X", decoder_out_net}}); | ||
| {{and2_pins_[{PinRoleType::DataIn, 0}], addr_nets[i]}, | ||
| {and2_pins_[{PinRoleType::DataIn, 1}], input_net}, | ||
| {and2_pins_[{PinRoleType::DataOut, 0}], decoder_out_net}}); | ||
| prev_net = input_net; | ||
| } else if (i == layers - 1) { // last AND gate layer | ||
| makeInst(word_cell.get(), | ||
| prefix, | ||
| fmt::format("and_layer{}", i), | ||
| and2_cell_, | ||
| {{"A", addr_nets[i]}, {"B", addr_nets[i + 1]}, {"X", prev_net}}); | ||
| {{and2_pins_[{PinRoleType::DataIn, 0}], addr_nets[i]}, | ||
| {and2_pins_[{PinRoleType::DataIn, 1}], addr_nets[i + 1]}, | ||
| {and2_pins_[{PinRoleType::DataOut, 0}], prev_net}}); | ||
| prev_net = input_net; | ||
| } else { // middle AND gate layers | ||
| makeInst(word_cell.get(), | ||
| prefix, | ||
| fmt::format("and_layer{}", i), | ||
| and2_cell_, | ||
| {{"A", addr_nets[i]}, {"B", input_net}, {"X", prev_net}}); | ||
| {{and2_pins_[{PinRoleType::DataIn, 0}], addr_nets[i]}, | ||
| {and2_pins_[{PinRoleType::DataIn, 1}], input_net}, | ||
| {and2_pins_[{PinRoleType::DataIn, 0}], prev_net}}); | ||
| prev_net = input_net; | ||
| } | ||
| } | ||
|
|
@@ -311,7 +325,8 @@ std::unique_ptr<Cell> RamGen::makeDecoder( | |
| prefix, | ||
| fmt::format("buf_port{}", port), | ||
| buffer_cell_, | ||
| {{"A", decoder_out_net}, {"X", selects[port]}}); | ||
| {{buffer_pins_[{PinRoleType::DataIn, 0}], decoder_out_net}, | ||
| {buffer_pins_[{PinRoleType::DataOut, 0}], selects[port]}}); | ||
| } | ||
|
|
||
| return word_cell; | ||
|
|
@@ -388,6 +403,61 @@ dbMaster* RamGen::findMaster( | |
| return best; | ||
| } | ||
|
|
||
| std::map<PinRole, std::string> RamGen::buildPinMap(dbMaster* master) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. warning: no header providing "std::map" is directly included [misc-include-cleaner] src/ram/src/ram.cpp:10: - #include <memory>
+ #include <map>
+ #include <memory> |
||
| { | ||
| auto sta_cell = network_->dbToSta(master); | ||
| auto liberty = network_->libertyCell(sta_cell); | ||
| std::map<PinRole, std::string> pin_map; | ||
| int in_idx = 0; | ||
|
|
||
| std::string tri_enable_name; | ||
|
|
||
| auto port_iter = liberty->portIterator(); | ||
| while (port_iter->hasNext()) { | ||
| auto concrete = port_iter->next(); | ||
| auto lib_port = concrete->libertyPort(); | ||
| auto dir = concrete->direction(); | ||
|
|
||
| if (lib_port->isPwrGnd()) { | ||
| auto pwr_gnd_type = lib_port->pwrGndType(); | ||
| if (pwr_gnd_type == sta::PwrGndType::primary_power) { | ||
| pin_map[{PinRoleType::Power, 0}] = lib_port->name(); | ||
| } else if (pwr_gnd_type == sta::PwrGndType::primary_ground) { | ||
| pin_map[{PinRoleType::Ground, 0}] = lib_port->name(); | ||
| } | ||
| } else if (lib_port->isClock() || lib_port->isRegClk() | ||
| || lib_port->isClockGateClock()) { | ||
| pin_map[{PinRoleType::Clock, 0}] = lib_port->name(); | ||
| } else if (dir->isTristate()) { | ||
| pin_map[{PinRoleType::DataOut, 0}] = lib_port->name(); | ||
| auto tri_expr = lib_port->tristateEnable(); | ||
| if (tri_expr && tri_expr->op() == sta::FuncExpr::Op::port) { | ||
| tri_enable_name = tri_expr->port()->name(); | ||
| } else if (tri_expr && tri_expr->op() == sta::FuncExpr::Op::not_) { | ||
| tri_enable_name = tri_expr->left()->port()->name(); | ||
| } | ||
| } else if (dir->isAnyOutput()) { // catches isOutput() and isTristate() | ||
| pin_map[{PinRoleType::DataOut, 0}] = lib_port->name(); | ||
| } else if (dir->isInput()) { | ||
| pin_map[{PinRoleType::DataIn, in_idx++}] = lib_port->name(); | ||
| } | ||
| } | ||
|
|
||
| if (!tri_enable_name.empty()) { | ||
| // find and remove it from DataIn | ||
|
Comment on lines
+452
to
+453
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why would a tristate enable appear as a DataIn? That doesn't appear possible in the prior loop.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The tristate enable pin has no distinguishing Liberty flag and is treated as an input port, so the only way to identify it is through the
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Got it. Perhaps a comment would help as it is a bit tricky |
||
| for (auto it = pin_map.begin(); it != pin_map.end(); ++it) { | ||
| if (it->second == tri_enable_name | ||
| && it->first.type == PinRoleType::DataIn) { | ||
| pin_map.erase(it); | ||
| break; | ||
| } | ||
| } | ||
| pin_map[{PinRoleType::TriEnable, 0}] = tri_enable_name; | ||
| } | ||
| delete port_iter; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. avoid manual memory management with a smart pointer and no delete std::unique_ptr port_iter = liberty->portIterator(); |
||
| return pin_map; | ||
| } | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should have some error checking to avoid using badly defined cells. e.g.
Or I guess I suppose you could check those things after the cell map has been made, if easier. It doesn't need to be solved in this PR, but you should mark as a TODO item to check against various kinds of flip-flops to make sure invalid ones are flagged, e.g. scan flops, flip-flops with reset, etc. Anything that doesn't have only clk, D, and Q ports. There are occasionally some flip-flops that have a Qn (negated Q) output and those are ok, Qn is just left disconnected. |
||
|
|
||
| void RamGen::findMasters() | ||
| { | ||
| if (!inv_cell_) { | ||
|
|
@@ -397,6 +467,7 @@ void RamGen::findMasters() | |
| }, | ||
| "inverter"); | ||
| } | ||
| inv_pins_ = buildPinMap(inv_cell_); | ||
|
|
||
| if (!tristate_cell_) { | ||
| tristate_cell_ = findMaster( | ||
|
|
@@ -409,6 +480,7 @@ void RamGen::findMasters() | |
| }, | ||
| "tristate"); | ||
| } | ||
| tristate_pins_ = buildPinMap(tristate_cell_); | ||
|
|
||
| if (!and2_cell_) { | ||
| and2_cell_ = findMaster( | ||
|
|
@@ -423,21 +495,32 @@ void RamGen::findMasters() | |
| }, | ||
| "and2"); | ||
| } | ||
| and2_pins_ = buildPinMap(and2_cell_); | ||
|
|
||
| if (!storage_cell_) { | ||
| // FIXME | ||
| storage_cell_ = findMaster( | ||
| [](sta::LibertyPort* port) { | ||
| if (!port->direction()->isOutput()) { | ||
| if (!port->isRegOutput()) { | ||
| return false; | ||
| } | ||
| auto function = port->function(); | ||
| return function && function->op() == sta::FuncExpr::Op::and_ | ||
| && function->left()->op() == sta::FuncExpr::Op::port | ||
| && function->right()->op() == sta::FuncExpr::Op::port; | ||
| // looking for DFF specifically | ||
| auto cell = port->libertyCell(); | ||
| auto port_iter = cell->portIterator(); | ||
| while (port_iter->hasNext()) { | ||
| auto p = port_iter->next()->libertyPort(); | ||
| // check to filter out latches | ||
| if (p && p->isLatchData()) { | ||
| delete port_iter; | ||
| return false; | ||
| } | ||
| } | ||
| delete port_iter; | ||
| return true; | ||
| }, | ||
| "storage"); | ||
| } | ||
| storage_pins_ = buildPinMap(storage_cell_); | ||
|
|
||
| if (!clock_gate_cell_) { | ||
| clock_gate_cell_ = findMaster( | ||
|
|
@@ -446,12 +529,15 @@ void RamGen::findMasters() | |
| }, | ||
| "clock gate"); | ||
| } | ||
| clock_gate_pins_ = buildPinMap(clock_gate_cell_); | ||
|
|
||
| // for input buffers | ||
| if (!buffer_cell_) { | ||
| buffer_cell_ = findMaster( | ||
| [](sta::LibertyPort* port) { return port->libertyCell()->isBuffer(); }, | ||
| "buffer"); | ||
| } | ||
| buffer_pins_ = buildPinMap(buffer_cell_); | ||
| } | ||
|
|
||
| void RamGen::ramPdngen(const char* power_pin, | ||
|
|
@@ -753,12 +839,13 @@ void RamGen::generate(const int mask_size, | |
| for (int bit = 0; bit < mask_size; ++bit) { | ||
| int bit_idx = bit + slice * mask_size; | ||
| auto buffer_grid_cell = std::make_unique<Cell>(); | ||
| makeInst( | ||
| buffer_grid_cell.get(), | ||
| "buffer", | ||
| fmt::format("in[{}]", bit_idx), | ||
| buffer_cell_, | ||
| {{"A", data_inputs_[bit_idx]->getNet()}, {"X", D_nets[bit_idx]}}); | ||
| makeInst(buffer_grid_cell.get(), | ||
| "buffer", | ||
| fmt::format("in[{}]", bit_idx), | ||
| buffer_cell_, | ||
| {{buffer_pins_[{PinRoleType::DataIn, 0}], | ||
| data_inputs_[bit_idx]->getNet()}, | ||
| {buffer_pins_[{PinRoleType::DataOut, 0}], D_nets[bit_idx]}}); | ||
| ram_grid_.addCell(std::move(buffer_grid_cell), bit_idx + slice); | ||
| } | ||
| } | ||
|
|
@@ -768,11 +855,13 @@ void RamGen::generate(const int mask_size, | |
| if (num_inputs > 1) { | ||
| for (int i = num_inputs - 1; i >= 0; --i) { | ||
| auto inv_grid_cell = std::make_unique<Cell>(); | ||
| makeInst(inv_grid_cell.get(), | ||
| "decoder", | ||
| fmt::format("inv_{}", i), | ||
| inv_cell_, | ||
| {{"A", addr_inputs_[i]->getNet()}, {"Y", inv_addr[i]}}); | ||
| makeInst( | ||
| inv_grid_cell.get(), | ||
| "decoder", | ||
| fmt::format("inv_{}", i), | ||
| inv_cell_, | ||
| {{inv_pins_[{PinRoleType::DataIn, 0}], addr_inputs_[i]->getNet()}, | ||
| {inv_pins_[{PinRoleType::DataOut, 0}], inv_addr[i]}}); | ||
| cell_inv_layout->addCell(std::move(inv_grid_cell)); | ||
| for (int filler_count = 0; filler_count < num_inputs - 1; | ||
| ++filler_count) { | ||
|
|
@@ -785,7 +874,8 @@ void RamGen::generate(const int mask_size, | |
| "decoder", | ||
| fmt::format("inv_{}", 0), | ||
| inv_cell_, | ||
| {{"A", addr_inputs_[0]->getNet()}, {"Y", inv_addr[0]}}); | ||
| {{inv_pins_[{PinRoleType::DataIn, 0}], addr_inputs_[0]->getNet()}, | ||
| {inv_pins_[{PinRoleType::DataOut, 0}], inv_addr[0]}}); | ||
| cell_inv_layout->addCell(std::move(inv_grid_cell)); | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,5 +2,6 @@ or_integration_tests( | |
| "ram" | ||
| TESTS | ||
| make_8x8 | ||
| make_7x7_nangate45 | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you also update |
||
| ) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| ../../../test/Nangate45 |
Uh oh!
There was an error while loading. Please reload this page.