@@ -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 (
@@ -225,19 +235,6 @@ void RamGen::makeSlice(const int slice_idx,
225235 {and2_ports_[{PortRoleType::DataOut, 0 }], write_sel}});
226236 }
227237
228- // Write path: this net is row_select AND with word_select so clock gate only
229- // fires for the addressed word within the row. Read path handled by AOI mux.
230- // word_select is nullptr when column_mux_ratio=1 (no mux needed).
231- dbNet* write_sel = selects[0 ];
232- if (word_select) {
233- write_sel = makeNet (prefix, " write_sel" );
234- makeInst (sel_cell.get (),
235- prefix,
236- " word_and" ,
237- and2_cell_,
238- {{" A" , selects[0 ]}, {" B" , word_select}, {" X" , write_sel}});
239- }
240-
241238 // Make clock and
242239 // this AND gate needs to be fed a net created by a decoder
243240 // adding any net will automatically connect with any port
@@ -627,31 +624,33 @@ void RamGen::findMasters()
627624 }
628625 and2_ports_ = buildPortMap (and2_cell_);
629626
630- if (!storage_cell_) {
631- // FIXME
632- // Still needs changes to get right type of flip-flop
633- storage_cell_ = findMaster (
634- [](sta::LibertyPort* port) {
635- if (!port->isRegOutput ()) {
636- return false ;
637- }
638- // looking for DFF specifically
639- auto cell = port->libertyCell ();
640- auto port_iter = cell->portIterator ();
641- while (port_iter->hasNext ()) {
642- auto p = port_iter->next ()->libertyPort ();
643- // check to filter out latches
644- if (p && p->isLatchData ()) {
645- 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 ()) {
646634 return false ;
647635 }
648- }
649- delete port_iter;
650- return true ;
651- },
652- " 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_);
653653 }
654- storage_ports_ = buildPortMap (storage_cell_);
655654
656655 if (!clock_gate_cell_) {
657656 clock_gate_cell_ = findMaster (
@@ -670,6 +669,39 @@ void RamGen::findMasters()
670669 }
671670 buffer_ports_ = buildPortMap (buffer_cell_);
672671
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 p = static_cast <sta::ConcretePort*>(port_iter->next ());
688+ auto lp = p->libertyPort ();
689+ if (lp && lp->isLatchData ()) {
690+ has_latch_data = true ;
691+ }
692+ if (std::string (p->name ()) == " GATE"
693+ || std::string (p->name ()) == " G" ) {
694+ has_gate = true ;
695+ }
696+ }
697+ delete port_iter;
698+ return has_latch_data && has_gate;
699+ },
700+ " latch" );
701+ }
702+
703+ latch_ports_ = buildPortMap (latch_cell_);
704+
673705 // aoi cells used for column mux functionality when column_mux_ratio > 1
674706 // uses truth table simulation to identify AOI22 and discover port names
675707 // to work for all PDKs, avoiding hardcoding PDK-specific expression parsing
@@ -1021,6 +1053,7 @@ void RamGen::generate(const int mask_size,
10211053 const int num_words,
10221054 const int column_mux_ratio,
10231055 const int read_ports,
1056+ const bool use_latch,
10241057 dbMaster* storage_cell,
10251058 dbMaster* tristate_cell,
10261059 dbMaster* inv_cell,
@@ -1078,6 +1111,7 @@ void RamGen::generate(const int mask_size,
10781111
10791112 logger_->info (RAM , 3 , " Generating {}" , ram_name);
10801113
1114+ use_latch_ = use_latch;
10811115 storage_cell_ = storage_cell;
10821116 tristate_cell_ = tristate_cell;
10831117 inv_cell_ = inv_cell;
@@ -1086,6 +1120,7 @@ void RamGen::generate(const int mask_size,
10861120 clock_gate_cell_ = nullptr ;
10871121 buffer_cell_ = nullptr ;
10881122 aoi22_cell_ = nullptr ;
1123+ latch_cell_ = nullptr ;
10891124 findMasters ();
10901125
10911126 auto chip = db_->getChip ();
@@ -1181,19 +1216,6 @@ void RamGen::generate(const int mask_size,
11811216 ram_grid_.addCell (std::move (decoder_and_cell), col_cell_count);
11821217 }
11831218 }
1184- } else {
1185- word_decoder_nets[row] = selectNets (decoder_name, read_ports);
1186- auto decoder_and_cell = makeDecoder (decoder_name,
1187- num_rows,
1188- read_ports,
1189- word_decoder_nets[row],
1190- decoder_input_nets[row]);
1191- ram_grid_.addCell (std::move (decoder_and_cell), col_cell_count);
1192- }
1193- }
1194-
1195- std::unique_ptr<Cell> inv_sel_cell;
1196- std::unique_ptr<Cell> word_sel_cell;
11971219
11981220 std::unique_ptr<Cell> inv_sel_cell;
11991221 std::unique_ptr<Cell> word_sel_cell;
@@ -1301,6 +1323,17 @@ void RamGen::generate(const int mask_size,
13011323 }
13021324 }
13031325
1326+ // For latch-based ram: mid_nets carry data from negative latch down to all
1327+ // pos latches per bit in the same column
1328+ vector<dbNet*> mid_nets (word_size);
1329+ dbNet* clk_b_net = nullptr ;
1330+ if (use_latch_) {
1331+ clk_b_net = makeNet (" global" , " clk_b" );
1332+ for (int bit = 0 ; bit < word_size; ++bit) {
1333+ mid_nets[bit] = makeNet (" mid" , fmt::format (" b{}" , bit));
1334+ }
1335+ }
1336+
13041337 // For column_mux_ratio > 1, create one shared select_b net per row before
13051338 // iterating over word_idx. All words in the same row share this net as the
13061339 // tristate TE_B enable signal. Only word_idx=0 instantiates the select_inv
@@ -1337,7 +1370,7 @@ void RamGen::generate(const int mask_size,
13371370 word_decoder_nets[row],
13381371 shared_select_b_nets,
13391372 (word_idx == 0 || column_mux_ratio == 1 ),
1340- D_nets,
1373+ use_latch_ ? mid_nets : D_nets,
13411374 word_output_nets);
13421375 }
13431376 }
@@ -1437,6 +1470,54 @@ void RamGen::generate(const int mask_size,
14371470 }
14381471 }
14391472
1473+ if (use_latch_) {
1474+ auto clk_inv_cell = std::make_unique<Cell>();
1475+ makeInst (clk_inv_cell.get (),
1476+ " neg_row" ,
1477+ " clk_inv" ,
1478+ inv_cell_,
1479+ {{inv_ports_[{PortRoleType::DataIn, 0 }], clock->getNet ()},
1480+ {inv_ports_[{PortRoleType::DataOut, 0 }], clk_b_net}});
1481+ ram_grid_.addCell (std::move (clk_inv_cell), col_cell_count);
1482+
1483+ const int nl_col_offset = (column_mux_ratio == 4 ) ? 1 : 0 ;
1484+
1485+ for (int slice = 0 ; slice < slices_per_word; ++slice) {
1486+ for (int bit = 0 ; bit < mask_size; ++bit) {
1487+ const int global_bit = slice * mask_size + bit;
1488+ const int group_start
1489+ = slice * (mask_size * column_mux_ratio + column_mux_ratio)
1490+ + bit * column_mux_ratio;
1491+ const int nl_col = group_start + nl_col_offset;
1492+ for (int c = group_start; c < group_start + column_mux_ratio; ++c) {
1493+ if (c == nl_col) {
1494+ auto nl_cell = std::make_unique<Cell>();
1495+ makeInst (
1496+ nl_cell.get (),
1497+ fmt::format (" neg_lat_b{}" , global_bit),
1498+ " nlat" ,
1499+ latch_cell_,
1500+ {{latch_ports_[{PortRoleType::Clock, 0 }], clk_b_net},
1501+ {latch_ports_[{PortRoleType::DataIn, 0 }], D_nets[global_bit]},
1502+ {latch_ports_[{PortRoleType::DataOut, 0 }],
1503+ mid_nets[global_bit]}});
1504+ ram_grid_.addCell (std::move (nl_cell), c);
1505+ } else {
1506+ ram_grid_.addCell (nullptr , c);
1507+ }
1508+ }
1509+ }
1510+ }
1511+ for (int slice = 0 ; slice < slices_per_word; ++slice) {
1512+ for (int w = 0 ; w < column_mux_ratio; ++w) {
1513+ int sel_col = slice * (mask_size * column_mux_ratio + column_mux_ratio)
1514+ + mask_size * column_mux_ratio + w;
1515+ ram_grid_.addCell (nullptr , sel_col);
1516+ }
1517+ }
1518+ ram_grid_.addCell (nullptr , col_cell_count);
1519+ }
1520+
14401521 for (int slice = 0 ; slice < slices_per_word; ++slice) {
14411522 for (int bit = 0 ; bit < mask_size; ++bit) {
14421523 int bit_idx = bit + slice * mask_size;
@@ -1460,6 +1541,10 @@ void RamGen::generate(const int mask_size,
14601541 ram_grid_.addCell (std::move (word_sel_cell), col_cell_count - 1 );
14611542 }
14621543
1544+ // One extra row at the top for placing input buffers, one extra row for
1545+ // negative latches if use latch-based ram
1546+ const int num_rows_grid = num_rows + 1 + (use_latch_ ? 1 : 0 );
1547+
14631548 auto cell_inv_layout = std::make_unique<Layout>(odb::vertical);
14641549 // check for AND gate, specific case for 2 words
14651550 int inv_col_cells = 0 ;
@@ -1500,8 +1585,8 @@ void RamGen::generate(const int mask_size,
15001585 ++inv_col_cells;
15011586 }
15021587 }
1503- // Pad remaining slots so this column matches the grid height (num_rows + 1 )
1504- while (inv_col_cells < num_rows + 1 ) {
1588+ // Pad remaining slots so this column matches the grid height (num_rows_grid )
1589+ while (inv_col_cells < num_rows_grid ) {
15051590 cell_inv_layout->addCell (nullptr );
15061591 ++inv_col_cells;
15071592 }
@@ -1515,7 +1600,7 @@ void RamGen::generate(const int mask_size,
15151600 if (tapcell_) {
15161601 // max tap distance specified is greater than the length of ram
15171602 if (ram_grid_.getRowWidth () <= max_tap_dist) {
1518- auto tapcell_layout = generateTapColumn (num_rows , 0 );
1603+ auto tapcell_layout = generateTapColumn (num_rows_grid - 1 , 0 );
15191604 ram_grid_.insertLayout (std::move (tapcell_layout), 0 );
15201605 } else {
15211606 // needed this calculation so first cells have right distance
@@ -1526,7 +1611,8 @@ void RamGen::generate(const int mask_size,
15261611 for (int col = 0 ; col < ram_grid_.numLayouts (); ++col) {
15271612 if (nearest_tap + ram_grid_.getLayoutWidth (col) >= max_tap_dist) {
15281613 // if the nearest_tap is too far, generate tap column
1529- auto tapcell_layout = generateTapColumn (num_rows, tapcell_count);
1614+ auto tapcell_layout
1615+ = generateTapColumn (num_rows_grid - 1 , tapcell_count);
15301616 ram_grid_.insertLayout (std::move (tapcell_layout), col);
15311617 ++col; // col adjustment after insertion
15321618 nearest_tap = 0 ;
@@ -1536,7 +1622,8 @@ void RamGen::generate(const int mask_size,
15361622 }
15371623 // check for last column in the grid
15381624 if (nearest_tap >= max_tap_dist) {
1539- auto tapcell_layout = generateTapColumn (num_rows, tapcell_count);
1625+ auto tapcell_layout
1626+ = generateTapColumn (num_rows_grid - 1 , tapcell_count);
15401627 ram_grid_.addLayout (std::move (tapcell_layout));
15411628 }
15421629 }
@@ -1550,8 +1637,6 @@ void RamGen::generate(const int mask_size,
15501637
15511638 int num_sites = ram_grid_.getRowWidth () / db_sites->getWidth ();
15521639
1553- // One extra row at the top for placing input buffers
1554- const int num_rows_grid = num_rows + 1 ;
15551640 for (int i = 0 ; i < num_rows_grid; ++i) {
15561641 auto row_name = fmt::format (" RAM_ROW{}" , i);
15571642 auto y_coord = i * ram_grid_.getHeight ();
0 commit comments