@@ -120,13 +120,23 @@ std::unique_ptr<Cell> RamGen::makeBit(const std::string& prefix,
120120
121121 auto storage_net = makeNet (prefix, " storage" );
122122
123- makeInst (bit_cell.get (),
124- prefix,
125- " bit" ,
126- storage_cell_,
127- {{storage_ports_[{PortRoleType::Clock, 0 }], clock},
128- {storage_ports_[{PortRoleType::DataIn, 0 }], data_input},
129- {storage_ports_[{PortRoleType::DataOut, 0 }], storage_net}});
123+ if (!use_latch_) {
124+ makeInst (bit_cell.get (),
125+ prefix,
126+ " bit" ,
127+ storage_cell_,
128+ {{storage_ports_[{PortRoleType::Clock, 0 }], clock},
129+ {storage_ports_[{PortRoleType::DataIn, 0 }], data_input},
130+ {storage_ports_[{PortRoleType::DataOut, 0 }], storage_net}});
131+ } else {
132+ makeInst (bit_cell.get (),
133+ prefix,
134+ " bit" ,
135+ latch_cell_,
136+ {{latch_ports_[{PortRoleType::Clock, 0 }], clock},
137+ {latch_ports_[{PortRoleType::DataIn, 0 }], data_input},
138+ {latch_ports_[{PortRoleType::DataOut, 0 }], storage_net}});
139+ }
130140
131141 for (int read_port = 0 ; read_port < read_ports; ++read_port) {
132142 makeInst (
@@ -614,31 +624,33 @@ void RamGen::findMasters()
614624 }
615625 and2_ports_ = buildPortMap (and2_cell_);
616626
617- if (!storage_cell_) {
618- // FIXME
619- // Still needs changes to get right type of flip-flop
620- storage_cell_ = findMaster (
621- [](sta::LibertyPort* port) {
622- if (!port->isRegOutput ()) {
623- return false ;
624- }
625- // looking for DFF specifically
626- auto cell = port->libertyCell ();
627- auto port_iter = cell->portIterator ();
628- while (port_iter->hasNext ()) {
629- auto p = port_iter->next ()->libertyPort ();
630- // check to filter out latches
631- if (p && p->isLatchData ()) {
632- delete port_iter;
627+ if (!use_latch_) {
628+ if (!storage_cell_) {
629+ // FIXME
630+ // Still needs changes to get right type of flip-flop
631+ storage_cell_ = findMaster (
632+ [](sta::LibertyPort* port) {
633+ if (!port->isRegOutput ()) {
633634 return false ;
634635 }
635- }
636- delete port_iter;
637- return true ;
638- },
639- " storage" );
636+ // looking for DFF specifically
637+ auto cell = port->libertyCell ();
638+ auto port_iter = cell->portIterator ();
639+ while (port_iter->hasNext ()) {
640+ auto p = port_iter->next ()->libertyPort ();
641+ // check to filter out latches
642+ if (p && p->isLatchData ()) {
643+ delete port_iter;
644+ return false ;
645+ }
646+ }
647+ delete port_iter;
648+ return true ;
649+ },
650+ " storage" );
651+ }
652+ storage_ports_ = buildPortMap (storage_cell_);
640653 }
641- storage_ports_ = buildPortMap (storage_cell_);
642654
643655 if (!clock_gate_cell_) {
644656 clock_gate_cell_ = findMaster (
@@ -657,6 +669,41 @@ void RamGen::findMasters()
657669 }
658670 buffer_ports_ = buildPortMap (buffer_cell_);
659671
672+ // latch cells for latch-based ram: find neg/pos latch cell
673+ if (use_latch_ && !latch_cell_) {
674+ latch_cell_ = findMaster (
675+ [](sta::LibertyPort* port) {
676+ if (!port->direction ()->isOutput ()) {
677+ return false ;
678+ }
679+ auto cell = port->libertyCell ();
680+ if (!cell->hasSequentials ()) {
681+ return false ;
682+ }
683+ bool has_latch_data = false ;
684+ bool has_gate = false ;
685+ auto port_iter = cell->portIterator ();
686+ while (port_iter->hasNext ()) {
687+ auto lp = port_iter->next ()->libertyPort ();
688+ if (lp) {
689+ if (lp->isLatchData ()) {
690+ has_latch_data = true ;
691+ }
692+ if (lp->isClock ()) {
693+ has_gate = true ;
694+ }
695+ }
696+ }
697+ delete port_iter;
698+ return has_latch_data && has_gate;
699+ },
700+ " latch" );
701+ }
702+
703+ if (use_latch_ && latch_cell_) {
704+ latch_ports_ = buildPortMap (latch_cell_);
705+ }
706+
660707 // aoi cells used for column mux functionality when column_mux_ratio > 1
661708 // uses truth table simulation to identify AOI22 and discover port names
662709 // to work for all PDKs, avoiding hardcoding PDK-specific expression parsing
@@ -1008,6 +1055,7 @@ void RamGen::generate(const int mask_size,
10081055 const int num_words,
10091056 const int column_mux_ratio,
10101057 const int read_ports,
1058+ const bool use_latch,
10111059 dbMaster* storage_cell,
10121060 dbMaster* tristate_cell,
10131061 dbMaster* inv_cell,
@@ -1065,6 +1113,7 @@ void RamGen::generate(const int mask_size,
10651113
10661114 logger_->info (RAM , 3 , " Generating {}" , ram_name);
10671115
1116+ use_latch_ = use_latch;
10681117 storage_cell_ = storage_cell;
10691118 tristate_cell_ = tristate_cell;
10701119 inv_cell_ = inv_cell;
@@ -1073,6 +1122,32 @@ void RamGen::generate(const int mask_size,
10731122 clock_gate_cell_ = nullptr ;
10741123 buffer_cell_ = nullptr ;
10751124 aoi22_cell_ = nullptr ;
1125+ latch_cell_ = nullptr ;
1126+
1127+ if (use_latch_ && storage_cell_) {
1128+ auto sta_cell = network_->dbToSta (storage_cell_);
1129+ auto liberty = network_->libertyCell (sta_cell);
1130+ bool is_latch = false ;
1131+ auto port_iter = std::unique_ptr<sta::ConcreteCellPortIterator>(
1132+ liberty->portIterator ());
1133+ while (port_iter->hasNext ()) {
1134+ auto lp = port_iter->next ()->libertyPort ();
1135+ if (lp && lp->isLatchData ()) {
1136+ is_latch = true ;
1137+ break ;
1138+ }
1139+ }
1140+ if (!is_latch) {
1141+ logger_->error (RAM ,
1142+ 37 ,
1143+ " -storage_cell {} is not a latch cell. Please provide a "
1144+ " latch cell when setting -use_latch 1." ,
1145+ storage_cell_->getName ());
1146+ }
1147+ latch_cell_ = storage_cell_;
1148+ storage_cell_ = nullptr ;
1149+ }
1150+
10761151 findMasters ();
10771152
10781153 auto chip = db_->getChip ();
@@ -1275,6 +1350,17 @@ void RamGen::generate(const int mask_size,
12751350 }
12761351 }
12771352
1353+ // For latch-based ram: mid_nets carry data from negative latch down to all
1354+ // pos latches per bit in the same column
1355+ vector<dbNet*> mid_nets (word_size);
1356+ dbNet* clk_b_net = nullptr ;
1357+ if (use_latch_) {
1358+ clk_b_net = makeNet (" global" , " clk_b" );
1359+ for (int bit = 0 ; bit < word_size; ++bit) {
1360+ mid_nets[bit] = makeNet (" mid" , fmt::format (" b{}" , bit));
1361+ }
1362+ }
1363+
12781364 // For column_mux_ratio > 1, create one shared select_b net per row before
12791365 // iterating over word_idx. All words in the same row share this net as the
12801366 // tristate TE_B enable signal. Only word_idx=0 instantiates the select_inv
@@ -1311,7 +1397,7 @@ void RamGen::generate(const int mask_size,
13111397 word_decoder_nets[row],
13121398 shared_select_b_nets,
13131399 (word_idx == 0 || column_mux_ratio == 1 ),
1314- D_nets,
1400+ use_latch_ ? mid_nets : D_nets,
13151401 word_output_nets);
13161402 }
13171403 }
@@ -1411,6 +1497,54 @@ void RamGen::generate(const int mask_size,
14111497 }
14121498 }
14131499
1500+ if (use_latch_) {
1501+ auto clk_inv_cell = std::make_unique<Cell>();
1502+ makeInst (clk_inv_cell.get (),
1503+ " neg_row" ,
1504+ " clk_inv" ,
1505+ inv_cell_,
1506+ {{inv_ports_[{PortRoleType::DataIn, 0 }], clock->getNet ()},
1507+ {inv_ports_[{PortRoleType::DataOut, 0 }], clk_b_net}});
1508+ ram_grid_.addCell (std::move (clk_inv_cell), col_cell_count);
1509+
1510+ const int nl_col_offset = (column_mux_ratio == 4 ) ? 1 : 0 ;
1511+
1512+ for (int slice = 0 ; slice < slices_per_word; ++slice) {
1513+ for (int bit = 0 ; bit < mask_size; ++bit) {
1514+ const int global_bit = slice * mask_size + bit;
1515+ const int group_start
1516+ = slice * (mask_size * column_mux_ratio + column_mux_ratio)
1517+ + bit * column_mux_ratio;
1518+ const int nl_col = group_start + nl_col_offset;
1519+ for (int c = group_start; c < group_start + column_mux_ratio; ++c) {
1520+ if (c == nl_col) {
1521+ auto nl_cell = std::make_unique<Cell>();
1522+ makeInst (
1523+ nl_cell.get (),
1524+ // NOLINTNEXTLINE(misc-include-cleaner)
1525+ fmt::format (" neg_lat_b{}" , global_bit),
1526+ " nlat" ,
1527+ latch_cell_,
1528+ {{latch_ports_[{PortRoleType::Clock, 0 }], clk_b_net},
1529+ {latch_ports_[{PortRoleType::DataIn, 0 }], D_nets[global_bit]},
1530+ {latch_ports_[{PortRoleType::DataOut, 0 }],
1531+ mid_nets[global_bit]}});
1532+ ram_grid_.addCell (std::move (nl_cell), c);
1533+ } else {
1534+ ram_grid_.addCell (nullptr , c);
1535+ }
1536+ }
1537+ }
1538+ }
1539+ for (int slice = 0 ; slice < slices_per_word; ++slice) {
1540+ for (int w = 0 ; w < column_mux_ratio; ++w) {
1541+ int sel_col = slice * (mask_size * column_mux_ratio + column_mux_ratio)
1542+ + mask_size * column_mux_ratio + w;
1543+ ram_grid_.addCell (nullptr , sel_col);
1544+ }
1545+ }
1546+ }
1547+
14141548 for (int slice = 0 ; slice < slices_per_word; ++slice) {
14151549 for (int bit = 0 ; bit < mask_size; ++bit) {
14161550 int bit_idx = bit + slice * mask_size;
@@ -1434,6 +1568,10 @@ void RamGen::generate(const int mask_size,
14341568 ram_grid_.addCell (std::move (word_sel_cell), col_cell_count - 1 );
14351569 }
14361570
1571+ // One extra row at the top for placing input buffers, one extra row for
1572+ // negative latches if use latch-based ram
1573+ const int num_rows_grid = num_rows + 1 + (use_latch_ ? 1 : 0 );
1574+
14371575 auto cell_inv_layout = std::make_unique<Layout>(odb::vertical);
14381576 // check for AND gate, specific case for 2 words
14391577 int inv_col_cells = 0 ;
@@ -1474,8 +1612,8 @@ void RamGen::generate(const int mask_size,
14741612 ++inv_col_cells;
14751613 }
14761614 }
1477- // Pad remaining slots so this column matches the grid height (num_rows + 1 )
1478- while (inv_col_cells < num_rows + 1 ) {
1615+ // Pad remaining slots so this column matches the grid height (num_rows_grid )
1616+ while (inv_col_cells < num_rows_grid ) {
14791617 cell_inv_layout->addCell (nullptr );
14801618 ++inv_col_cells;
14811619 }
@@ -1489,7 +1627,7 @@ void RamGen::generate(const int mask_size,
14891627 if (tapcell_) {
14901628 // max tap distance specified is greater than the length of ram
14911629 if (ram_grid_.getRowWidth () <= max_tap_dist) {
1492- auto tapcell_layout = generateTapColumn (num_rows , 0 );
1630+ auto tapcell_layout = generateTapColumn (num_rows_grid - 1 , 0 );
14931631 ram_grid_.insertLayout (std::move (tapcell_layout), 0 );
14941632 } else {
14951633 // needed this calculation so first cells have right distance
@@ -1500,7 +1638,8 @@ void RamGen::generate(const int mask_size,
15001638 for (int col = 0 ; col < ram_grid_.numLayouts (); ++col) {
15011639 if (nearest_tap + ram_grid_.getLayoutWidth (col) >= max_tap_dist) {
15021640 // if the nearest_tap is too far, generate tap column
1503- auto tapcell_layout = generateTapColumn (num_rows, tapcell_count);
1641+ auto tapcell_layout
1642+ = generateTapColumn (num_rows_grid - 1 , tapcell_count);
15041643 ram_grid_.insertLayout (std::move (tapcell_layout), col);
15051644 ++col; // col adjustment after insertion
15061645 nearest_tap = 0 ;
@@ -1510,7 +1649,8 @@ void RamGen::generate(const int mask_size,
15101649 }
15111650 // check for last column in the grid
15121651 if (nearest_tap >= max_tap_dist) {
1513- auto tapcell_layout = generateTapColumn (num_rows, tapcell_count);
1652+ auto tapcell_layout
1653+ = generateTapColumn (num_rows_grid - 1 , tapcell_count);
15141654 ram_grid_.addLayout (std::move (tapcell_layout));
15151655 }
15161656 }
@@ -1524,8 +1664,6 @@ void RamGen::generate(const int mask_size,
15241664
15251665 int num_sites = ram_grid_.getRowWidth () / db_sites->getWidth ();
15261666
1527- // One extra row at the top for placing input buffers
1528- const int num_rows_grid = num_rows + 1 ;
15291667 for (int i = 0 ; i < num_rows_grid; ++i) {
15301668 auto row_name = fmt::format (" RAM_ROW{}" , i);
15311669 auto y_coord = i * ram_grid_.getHeight ();
0 commit comments