kernel/mem: Add functions to emulate read port enable/init/reset signals.
[yosys.git] / kernel / ff.h
index 7f01b8a368d13a900d20610a4bfdfdca3f99cf75..5a629d5dd6c18d7aaebf2ada572f87ad81d15514 100644 (file)
@@ -76,7 +76,10 @@ YOSYS_NAMESPACE_BEGIN
 // - empty set [not a cell — will be emitted as a simple direct connection]
 
 struct FfData {
+       Module *module;
        FfInitVals *initvals;
+       Cell *cell;
+       IdString name;
        // The FF output.
        SigSpec sig_q;
        // The sync data input, present if has_clk or has_gclk.
@@ -142,7 +145,7 @@ struct FfData {
        int width;
        dict<IdString, Const> attributes;
 
-       FfData(FfInitVals *initvals = nullptr, Cell *cell = nullptr) : initvals(initvals) {
+       FfData(Module *module = nullptr, FfInitVals *initvals = nullptr, IdString name = IdString()) : module(module), initvals(initvals), cell(nullptr), name(name) {
                width = 0;
                has_clk = false;
                has_gclk = false;
@@ -160,464 +163,52 @@ struct FfData {
                pol_srst = false;
                pol_clr = false;
                pol_set = false;
+       }
 
-               if (!cell)
-                       return;
+       FfData(FfInitVals *initvals, Cell *cell_);
 
-               sig_q = cell->getPort(ID::Q);
-               width = GetSize(sig_q);
-               attributes = cell->attributes;
+       // Returns a FF identical to this one, but only keeping bit indices from the argument.
+       FfData slice(const std::vector<int> &bits);
 
-               if (initvals)
-                       val_init = (*initvals)(sig_q);
+       void add_dummy_ce();
+       void add_dummy_srst();
+       void add_dummy_arst();
+       void add_dummy_aload();
+       void add_dummy_sr();
+       void add_dummy_clk();
 
-               std::string type_str = cell->type.str();
+       void arst_to_aload();
+       void arst_to_sr();
 
-               if (cell->type.in(ID($ff), ID($dff), ID($dffe), ID($dffsr), ID($dffsre), ID($adff), ID($adffe), ID($aldff), ID($aldffe), ID($sdff), ID($sdffe), ID($sdffce), ID($dlatch), ID($adlatch), ID($dlatchsr), ID($sr))) {
-                       if (cell->type == ID($ff)) {
-                               has_gclk = true;
-                               sig_d = cell->getPort(ID::D);
-                       } else if (cell->type == ID($sr)) {
-                               // No data input at all.
-                       } else if (cell->type.in(ID($dlatch), ID($adlatch), ID($dlatchsr))) {
-                               has_aload = true;
-                               sig_aload = cell->getPort(ID::EN);
-                               pol_aload = cell->getParam(ID::EN_POLARITY).as_bool();
-                               sig_ad = cell->getPort(ID::D);
-                       } else {
-                               has_clk = true;
-                               sig_clk = cell->getPort(ID::CLK);
-                               pol_clk = cell->getParam(ID::CLK_POLARITY).as_bool();
-                               sig_d = cell->getPort(ID::D);
-                       }
-                       if (cell->type.in(ID($dffe), ID($dffsre), ID($adffe), ID($aldffe), ID($sdffe), ID($sdffce))) {
-                               has_ce = true;
-                               sig_ce = cell->getPort(ID::EN);
-                               pol_ce = cell->getParam(ID::EN_POLARITY).as_bool();
-                       }
-                       if (cell->type.in(ID($dffsr), ID($dffsre), ID($dlatchsr), ID($sr))) {
-                               has_sr = true;
-                               sig_clr = cell->getPort(ID::CLR);
-                               sig_set = cell->getPort(ID::SET);
-                               pol_clr = cell->getParam(ID::CLR_POLARITY).as_bool();
-                               pol_set = cell->getParam(ID::SET_POLARITY).as_bool();
-                       }
-                       if (cell->type.in(ID($aldff), ID($aldffe))) {
-                               has_aload = true;
-                               sig_aload = cell->getPort(ID::ALOAD);
-                               pol_aload = cell->getParam(ID::ALOAD_POLARITY).as_bool();
-                               sig_ad = cell->getPort(ID::AD);
-                       }
-                       if (cell->type.in(ID($adff), ID($adffe), ID($adlatch))) {
-                               has_arst = true;
-                               sig_arst = cell->getPort(ID::ARST);
-                               pol_arst = cell->getParam(ID::ARST_POLARITY).as_bool();
-                               val_arst = cell->getParam(ID::ARST_VALUE);
-                       }
-                       if (cell->type.in(ID($sdff), ID($sdffe), ID($sdffce))) {
-                               has_srst = true;
-                               sig_srst = cell->getPort(ID::SRST);
-                               pol_srst = cell->getParam(ID::SRST_POLARITY).as_bool();
-                               val_srst = cell->getParam(ID::SRST_VALUE);
-                               ce_over_srst = cell->type == ID($sdffce);
-                       }
-               } else if (cell->type == ID($_FF_)) {
-                       is_fine = true;
-                       has_gclk = true;
-                       sig_d = cell->getPort(ID::D);
-               } else if (type_str.substr(0, 5) == "$_SR_") {
-                       is_fine = true;
-                       has_sr = true;
-                       pol_set = type_str[5] == 'P';
-                       pol_clr = type_str[6] == 'P';
-                       sig_set = cell->getPort(ID::S);
-                       sig_clr = cell->getPort(ID::R);
-               } else if (type_str.substr(0, 6) == "$_DFF_" && type_str.size() == 8) {
-                       is_fine = true;
-                       sig_d = cell->getPort(ID::D);
-                       has_clk = true;
-                       pol_clk = type_str[6] == 'P';
-                       sig_clk = cell->getPort(ID::C);
-               } else if (type_str.substr(0, 7) == "$_DFFE_" && type_str.size() == 10) {
-                       is_fine = true;
-                       sig_d = cell->getPort(ID::D);
-                       has_clk = true;
-                       pol_clk = type_str[7] == 'P';
-                       sig_clk = cell->getPort(ID::C);
-                       has_ce = true;
-                       pol_ce = type_str[8] == 'P';
-                       sig_ce = cell->getPort(ID::E);
-               } else if (type_str.substr(0, 6) == "$_DFF_" && type_str.size() == 10) {
-                       is_fine = true;
-                       sig_d = cell->getPort(ID::D);
-                       has_clk = true;
-                       pol_clk = type_str[6] == 'P';
-                       sig_clk = cell->getPort(ID::C);
-                       has_arst = true;
-                       pol_arst = type_str[7] == 'P';
-                       sig_arst = cell->getPort(ID::R);
-                       val_arst = type_str[8] == '1' ? State::S1 : State::S0;
-               } else if (type_str.substr(0, 7) == "$_DFFE_" && type_str.size() == 12) {
-                       is_fine = true;
-                       sig_d = cell->getPort(ID::D);
-                       has_clk = true;
-                       pol_clk = type_str[7] == 'P';
-                       sig_clk = cell->getPort(ID::C);
-                       has_arst = true;
-                       pol_arst = type_str[8] == 'P';
-                       sig_arst = cell->getPort(ID::R);
-                       val_arst = type_str[9] == '1' ? State::S1 : State::S0;
-                       has_ce = true;
-                       pol_ce = type_str[10] == 'P';
-                       sig_ce = cell->getPort(ID::E);
-               } else if (type_str.substr(0, 8) == "$_ALDFF_" && type_str.size() == 11) {
-                       is_fine = true;
-                       sig_d = cell->getPort(ID::D);
-                       has_clk = true;
-                       pol_clk = type_str[8] == 'P';
-                       sig_clk = cell->getPort(ID::C);
-                       has_aload = true;
-                       pol_aload = type_str[9] == 'P';
-                       sig_aload = cell->getPort(ID::L);
-                       sig_ad = cell->getPort(ID::AD);
-               } else if (type_str.substr(0, 9) == "$_ALDFFE_" && type_str.size() == 13) {
-                       is_fine = true;
-                       sig_d = cell->getPort(ID::D);
-                       has_clk = true;
-                       pol_clk = type_str[9] == 'P';
-                       sig_clk = cell->getPort(ID::C);
-                       has_aload = true;
-                       pol_aload = type_str[10] == 'P';
-                       sig_aload = cell->getPort(ID::L);
-                       sig_ad = cell->getPort(ID::AD);
-                       has_ce = true;
-                       pol_ce = type_str[11] == 'P';
-                       sig_ce = cell->getPort(ID::E);
-               } else if (type_str.substr(0, 8) == "$_DFFSR_" && type_str.size() == 12) {
-                       is_fine = true;
-                       sig_d = cell->getPort(ID::D);
-                       has_clk = true;
-                       pol_clk = type_str[8] == 'P';
-                       sig_clk = cell->getPort(ID::C);
-                       has_sr = true;
-                       pol_set = type_str[9] == 'P';
-                       pol_clr = type_str[10] == 'P';
-                       sig_set = cell->getPort(ID::S);
-                       sig_clr = cell->getPort(ID::R);
-               } else if (type_str.substr(0, 9) == "$_DFFSRE_" && type_str.size() == 14) {
-                       is_fine = true;
-                       sig_d = cell->getPort(ID::D);
-                       has_clk = true;
-                       pol_clk = type_str[9] == 'P';
-                       sig_clk = cell->getPort(ID::C);
-                       has_sr = true;
-                       pol_set = type_str[10] == 'P';
-                       pol_clr = type_str[11] == 'P';
-                       sig_set = cell->getPort(ID::S);
-                       sig_clr = cell->getPort(ID::R);
-                       has_ce = true;
-                       pol_ce = type_str[12] == 'P';
-                       sig_ce = cell->getPort(ID::E);
-               } else if (type_str.substr(0, 7) == "$_SDFF_" && type_str.size() == 11) {
-                       is_fine = true;
-                       sig_d = cell->getPort(ID::D);
-                       has_clk = true;
-                       pol_clk = type_str[7] == 'P';
-                       sig_clk = cell->getPort(ID::C);
-                       has_srst = true;
-                       pol_srst = type_str[8] == 'P';
-                       sig_srst = cell->getPort(ID::R);
-                       val_srst = type_str[9] == '1' ? State::S1 : State::S0;
-               } else if (type_str.substr(0, 8) == "$_SDFFE_" && type_str.size() == 13) {
-                       is_fine = true;
-                       sig_d = cell->getPort(ID::D);
-                       has_clk = true;
-                       pol_clk = type_str[8] == 'P';
-                       sig_clk = cell->getPort(ID::C);
-                       has_srst = true;
-                       pol_srst = type_str[9] == 'P';
-                       sig_srst = cell->getPort(ID::R);
-                       val_srst = type_str[10] == '1' ? State::S1 : State::S0;
-                       has_ce = true;
-                       pol_ce = type_str[11] == 'P';
-                       sig_ce = cell->getPort(ID::E);
-               } else if (type_str.substr(0, 9) == "$_SDFFCE_" && type_str.size() == 14) {
-                       is_fine = true;
-                       sig_d = cell->getPort(ID::D);
-                       has_clk = true;
-                       pol_clk = type_str[9] == 'P';
-                       sig_clk = cell->getPort(ID::C);
-                       has_srst = true;
-                       pol_srst = type_str[10] == 'P';
-                       sig_srst = cell->getPort(ID::R);
-                       val_srst = type_str[11] == '1' ? State::S1 : State::S0;
-                       has_ce = true;
-                       pol_ce = type_str[12] == 'P';
-                       sig_ce = cell->getPort(ID::E);
-                       ce_over_srst = true;
-               } else if (type_str.substr(0, 9) == "$_DLATCH_" && type_str.size() == 11) {
-                       is_fine = true;
-                       has_aload = true;
-                       sig_ad = cell->getPort(ID::D);
-                       has_aload = true;
-                       pol_aload = type_str[9] == 'P';
-                       sig_aload = cell->getPort(ID::E);
-               } else if (type_str.substr(0, 9) == "$_DLATCH_" && type_str.size() == 13) {
-                       is_fine = true;
-                       has_aload = true;
-                       sig_ad = cell->getPort(ID::D);
-                       has_aload = true;
-                       pol_aload = type_str[9] == 'P';
-                       sig_aload = cell->getPort(ID::E);
-                       has_arst = true;
-                       pol_arst = type_str[10] == 'P';
-                       sig_arst = cell->getPort(ID::R);
-                       val_arst = type_str[11] == '1' ? State::S1 : State::S0;
-               } else if (type_str.substr(0, 11) == "$_DLATCHSR_" && type_str.size() == 15) {
-                       is_fine = true;
-                       has_aload = true;
-                       sig_ad = cell->getPort(ID::D);
-                       has_aload = true;
-                       pol_aload = type_str[11] == 'P';
-                       sig_aload = cell->getPort(ID::E);
-                       has_sr = true;
-                       pol_set = type_str[12] == 'P';
-                       pol_clr = type_str[13] == 'P';
-                       sig_set = cell->getPort(ID::S);
-                       sig_clr = cell->getPort(ID::R);
-               } else {
-                       log_assert(0);
-               }
-               if (has_aload && !has_clk && !has_sr && !has_arst && sig_ad.is_fully_const()) {
-                       // Plain D latches with const D treated specially.
-                       has_aload = false;
-                       has_arst = true;
-                       sig_arst = sig_aload;
-                       pol_arst = pol_aload;
-                       val_arst = sig_ad.as_const();
-               }
-       }
+       void aload_to_sr();
 
-       // Returns a FF identical to this one, but only keeping bit indices from the argument.
-       FfData slice(const std::vector<int> &bits) {
-               FfData res(initvals);
-               res.sig_clk = sig_clk;
-               res.sig_ce = sig_ce;
-               res.sig_aload = sig_aload;
-               res.sig_arst = sig_arst;
-               res.sig_srst = sig_srst;
-               res.has_clk = has_clk;
-               res.has_gclk = has_gclk;
-               res.has_ce = has_ce;
-               res.has_aload = has_aload;
-               res.has_arst = has_arst;
-               res.has_srst = has_srst;
-               res.has_sr = has_sr;
-               res.ce_over_srst = ce_over_srst;
-               res.is_fine = is_fine;
-               res.pol_clk = pol_clk;
-               res.pol_ce = pol_ce;
-               res.pol_aload = pol_aload;
-               res.pol_arst = pol_arst;
-               res.pol_srst = pol_srst;
-               res.pol_clr = pol_clr;
-               res.pol_set = pol_set;
-               res.attributes = attributes;
-               for (int i : bits) {
-                       res.sig_q.append(sig_q[i]);
-                       if (has_clk || has_gclk)
-                               res.sig_d.append(sig_d[i]);
-                       if (has_aload)
-                               res.sig_ad.append(sig_ad[i]);
-                       if (has_sr) {
-                               res.sig_clr.append(sig_clr[i]);
-                               res.sig_set.append(sig_set[i]);
-                       }
-                       if (has_arst)
-                               res.val_arst.bits.push_back(val_arst[i]);
-                       if (has_srst)
-                               res.val_srst.bits.push_back(val_srst[i]);
-                       if (initvals)
-                               res.val_init.bits.push_back(val_init[i]);
-               }
-               res.width = GetSize(res.sig_q);
-               return res;
-       }
+       // Given a FF with both has_ce and has_srst, sets ce_over_srst to the given value and
+       // fixes up control signals appropriately to preserve semantics.
+       void convert_ce_over_srst(bool val);
 
-       void unmap_ce(Module *module) {
-               if (!has_ce)
-                       return;
-               log_assert(has_clk);
-               if (has_srst && ce_over_srst)
-                       unmap_srst(module);
+       void unmap_ce();
+       void unmap_srst();
 
-               if (!is_fine) {
-                       if (pol_ce)
-                               sig_d = module->Mux(NEW_ID, sig_q, sig_d, sig_ce);
-                       else
-                               sig_d = module->Mux(NEW_ID, sig_d, sig_q, sig_ce);
-               } else {
-                       if (pol_ce)
-                               sig_d = module->MuxGate(NEW_ID, sig_q, sig_d, sig_ce);
-                       else
-                               sig_d = module->MuxGate(NEW_ID, sig_d, sig_q, sig_ce);
-               }
-               has_ce = false;
+       void unmap_ce_srst() {
+               unmap_ce();
+               unmap_srst();
        }
 
-       void unmap_srst(Module *module) {
-               if (!has_srst)
-                       return;
-               if (has_ce && !ce_over_srst)
-                       unmap_ce(module);
+       Cell *emit();
 
-               if (!is_fine) {
-                       if (pol_srst)
-                               sig_d = module->Mux(NEW_ID, sig_d, val_srst, sig_srst);
-                       else
-                               sig_d = module->Mux(NEW_ID, val_srst, sig_d, sig_srst);
-               } else {
-                       if (pol_srst)
-                               sig_d = module->MuxGate(NEW_ID, sig_d, val_srst[0], sig_srst);
-                       else
-                               sig_d = module->MuxGate(NEW_ID, val_srst[0], sig_d, sig_srst);
-               }
-               has_srst = false;
+       // Removes init attribute from the Q output, but keeps val_init unchanged.
+       // It will be automatically reattached on emit.  Use this before changing sig_q.
+       void remove_init() {
+               if (initvals)
+                       initvals->remove_init(sig_q);
        }
 
-       void unmap_ce_srst(Module *module) {
-               unmap_ce(module);
-               unmap_srst(module);
-       }
+       void remove();
 
-       Cell *emit(Module *module, IdString name) {
-               if (!width)
-                       return nullptr;
-               if (!has_aload && !has_clk && !has_gclk && !has_sr) {
-                       if (has_arst) {
-                               // Convert this case to a D latch.
-                               has_aload = true;
-                               has_arst = false;
-                               sig_ad = val_arst;
-                               sig_aload = sig_arst;
-                               pol_aload = pol_arst;
-                       } else {
-                               // No control inputs left.  Turn into a const driver.
-                               if (initvals)
-                                       initvals->remove_init(sig_q);
-                               module->connect(sig_q, val_init);
-                               return nullptr;
-                       }
-               }
-               if (initvals)
-                       initvals->set_init(sig_q, val_init);
-               Cell *cell;
-               if (!is_fine) {
-                       if (has_gclk) {
-                               log_assert(!has_clk);
-                               log_assert(!has_ce);
-                               log_assert(!has_aload);
-                               log_assert(!has_arst);
-                               log_assert(!has_srst);
-                               log_assert(!has_sr);
-                               cell = module->addFf(name, sig_d, sig_q);
-                       } else if (!has_aload && !has_clk) {
-                               log_assert(has_sr);
-                               cell = module->addSr(name, sig_set, sig_clr, sig_q, pol_set, pol_clr);
-                       } else if (!has_clk) {
-                               log_assert(!has_srst);
-                               if (has_sr)
-                                       cell = module->addDlatchsr(name, sig_aload, sig_set, sig_clr, sig_ad, sig_q, pol_aload, pol_set, pol_clr);
-                               else if (has_arst)
-                                       cell = module->addAdlatch(name, sig_aload, sig_arst, sig_ad, sig_q, val_arst, pol_aload, pol_arst);
-                               else
-                                       cell = module->addDlatch(name, sig_aload, sig_ad, sig_q, pol_aload);
-                       } else {
-                               if (has_sr) {
-                                       if (has_ce)
-                                               cell = module->addDffsre(name, sig_clk, sig_ce, sig_set, sig_clr, sig_d, sig_q, pol_clk, pol_ce, pol_set, pol_clr);
-                                       else
-                                               cell = module->addDffsr(name, sig_clk, sig_set, sig_clr, sig_d, sig_q, pol_clk, pol_set, pol_clr);
-                               } else if (has_arst) {
-                                       if (has_ce)
-                                               cell = module->addAdffe(name, sig_clk, sig_ce, sig_arst, sig_d, sig_q, val_arst, pol_clk, pol_ce, pol_arst);
-                                       else
-                                               cell = module->addAdff(name, sig_clk, sig_arst, sig_d, sig_q, val_arst, pol_clk, pol_arst);
-                               } else if (has_aload) {
-                                       if (has_ce)
-                                               cell = module->addAldffe(name, sig_clk, sig_ce, sig_aload, sig_d, sig_q, sig_ad, pol_clk, pol_ce, pol_aload);
-                                       else
-                                               cell = module->addAldff(name, sig_clk, sig_aload, sig_d, sig_q, sig_ad, pol_clk, pol_aload);
-                               } else if (has_srst) {
-                                       if (has_ce)
-                                               if (ce_over_srst)
-                                                       cell = module->addSdffce(name, sig_clk, sig_ce, sig_srst, sig_d, sig_q, val_srst, pol_clk, pol_ce, pol_srst);
-                                               else
-                                                       cell = module->addSdffe(name, sig_clk, sig_ce, sig_srst, sig_d, sig_q, val_srst, pol_clk, pol_ce, pol_srst);
-                                       else
-                                               cell = module->addSdff(name, sig_clk, sig_srst, sig_d, sig_q, val_srst, pol_clk, pol_srst);
-                               } else {
-                                       if (has_ce)
-                                               cell = module->addDffe(name, sig_clk, sig_ce, sig_d, sig_q, pol_clk, pol_ce);
-                                       else
-                                               cell = module->addDff(name, sig_clk, sig_d, sig_q, pol_clk);
-                               }
-                       }
-               } else {
-                       if (has_gclk) {
-                               log_assert(!has_clk);
-                               log_assert(!has_ce);
-                               log_assert(!has_aload);
-                               log_assert(!has_arst);
-                               log_assert(!has_srst);
-                               log_assert(!has_sr);
-                               cell = module->addFfGate(name, sig_d, sig_q);
-                       } else if (!has_aload && !has_clk) {
-                               log_assert(has_sr);
-                               cell = module->addSrGate(name, sig_set, sig_clr, sig_q, pol_set, pol_clr);
-                       } else if (!has_clk) {
-                               log_assert(!has_srst);
-                               if (has_sr)
-                                       cell = module->addDlatchsrGate(name, sig_aload, sig_set, sig_clr, sig_ad, sig_q, pol_aload, pol_set, pol_clr);
-                               else if (has_arst)
-                                       cell = module->addAdlatchGate(name, sig_aload, sig_arst, sig_ad, sig_q, val_arst.as_bool(), pol_aload, pol_arst);
-                               else
-                                       cell = module->addDlatchGate(name, sig_aload, sig_ad, sig_q, pol_aload);
-                       } else {
-                               if (has_sr) {
-                                       if (has_ce)
-                                               cell = module->addDffsreGate(name, sig_clk, sig_ce, sig_set, sig_clr, sig_d, sig_q, pol_clk, pol_ce, pol_set, pol_clr);
-                                       else
-                                               cell = module->addDffsrGate(name, sig_clk, sig_set, sig_clr, sig_d, sig_q, pol_clk, pol_set, pol_clr);
-                               } else if (has_arst) {
-                                       if (has_ce)
-                                               cell = module->addAdffeGate(name, sig_clk, sig_ce, sig_arst, sig_d, sig_q, val_arst.as_bool(), pol_clk, pol_ce, pol_arst);
-                                       else
-                                               cell = module->addAdffGate(name, sig_clk, sig_arst, sig_d, sig_q, val_arst.as_bool(), pol_clk, pol_arst);
-                               } else if (has_aload) {
-                                       if (has_ce)
-                                               cell = module->addAldffeGate(name, sig_clk, sig_ce, sig_aload, sig_d, sig_q, sig_ad, pol_clk, pol_ce, pol_aload);
-                                       else
-                                               cell = module->addAldffGate(name, sig_clk, sig_aload, sig_d, sig_q, sig_ad, pol_clk, pol_aload);
-                               } else if (has_srst) {
-                                       if (has_ce)
-                                               if (ce_over_srst)
-                                                       cell = module->addSdffceGate(name, sig_clk, sig_ce, sig_srst, sig_d, sig_q, val_srst.as_bool(), pol_clk, pol_ce, pol_srst);
-                                               else
-                                                       cell = module->addSdffeGate(name, sig_clk, sig_ce, sig_srst, sig_d, sig_q, val_srst.as_bool(), pol_clk, pol_ce, pol_srst);
-                                       else
-                                               cell = module->addSdffGate(name, sig_clk, sig_srst, sig_d, sig_q, val_srst.as_bool(), pol_clk, pol_srst);
-                               } else {
-                                       if (has_ce)
-                                               cell = module->addDffeGate(name, sig_clk, sig_ce, sig_d, sig_q, pol_clk, pol_ce);
-                                       else
-                                               cell = module->addDffGate(name, sig_clk, sig_d, sig_q, pol_clk);
-                               }
-                       }
-               }
-               cell->attributes = attributes;
-               return cell;
-       }
+       // Flip the sense of the given bit slices of the FF: insert inverters on data
+       // inputs and output, flip the corresponding init/reset bits, swap clr/set
+       // inputs with proper priority fix.
+       void flip_bits(const pool<int> &bits);
 };
 
 YOSYS_NAMESPACE_END