From 8a6d9c9391dd30ea01c5dbe0447e8230834080c5 Mon Sep 17 00:00:00 2001 From: alaindargelas Date: Wed, 25 Sep 2024 15:04:48 -0700 Subject: [PATCH] Blocking to non-blocking RAM inference transform --- templates/SynthSubset.cpp | 142 +++++++++++++++++++++++++++++++++++++- templates/SynthSubset.h | 3 + 2 files changed, 144 insertions(+), 1 deletion(-) diff --git a/templates/SynthSubset.cpp b/templates/SynthSubset.cpp index dd5fd2af..79ebfc09 100644 --- a/templates/SynthSubset.cpp +++ b/templates/SynthSubset.cpp @@ -591,9 +591,14 @@ void SynthSubset::leavePort(const port* object, vpiHandle handle) { } } +void SynthSubset::leaveAlways(const always* object, vpiHandle handle) { + sensitivityListRewrite(object, handle); + blockingToNonBlockingRewrite(object, handle); +} + // Transform 3 vars sensitivity list into 2 vars sensitivity list because of a // Yosys limitation -void SynthSubset::leaveAlways(const always* object, vpiHandle handle) { +void SynthSubset::sensitivityListRewrite(const always* object, vpiHandle handle) { // Transform: always @ (posedge clk or posedge rst or posedge start) // if (rst | start) ... // Into: @@ -728,6 +733,141 @@ void SynthSubset::leaveAlways(const always* object, vpiHandle handle) { } } +void collectAssignmentStmt(const any* stmt, std::vector& blocking_assigns, std::vector& nonblocking_assigns) { + if (stmt == nullptr) + return; + UHDM_OBJECT_TYPE stmt_type = stmt->UhdmType(); + switch (stmt_type) { + case uhdmbegin: { + VectorOfany* stmts = any_cast(stmt)->Stmts(); + if (stmts) + for (auto stmt : *stmts) { + collectAssignmentStmt(stmt, blocking_assigns, nonblocking_assigns); + } + break; + } + case uhdmnamed_begin: { + VectorOfany* stmts = any_cast(stmt)->Stmts(); + if (stmts) + for (auto stmt : *stmts) { + collectAssignmentStmt(stmt, blocking_assigns, nonblocking_assigns); + } + break; + } + case uhdmif_else: { + const any* the_stmt = any_cast(stmt)->VpiStmt(); + collectAssignmentStmt(the_stmt, blocking_assigns, nonblocking_assigns); + const any* else_stmt = any_cast(stmt)->VpiElseStmt(); + collectAssignmentStmt(else_stmt, blocking_assigns, nonblocking_assigns); + break; + } + case uhdmif_stmt: { + const any* the_stmt = any_cast(stmt)->VpiStmt(); + collectAssignmentStmt(the_stmt, blocking_assigns, nonblocking_assigns); + break; + } + case uhdmcase_stmt: { + //VectorOfcase_item* items = any_cast(stmt)->Case_items(); + // TODO + break; + } + case uhdmassignment: { + const assignment* as = any_cast(stmt); + if (as->VpiBlocking()) { + blocking_assigns.push_back(as); + } else { + nonblocking_assigns.push_back(as); + } + } + default: + break; + } +} + +// Transforms the following to enable RAM inference: +// if (we) +// RAM[addr] = di; +// read = RAM[addr]; +// Into: +// if (we) +// RAM[addr] <= di; +// read <= RAM[addr]; +void SynthSubset::blockingToNonBlockingRewrite(const always* object, + vpiHandle handle) { + if (const UHDM::any* stmt = object->Stmt()) { + if (const event_control* ec = any_cast(stmt)) { + // Collect all the blocking and non blocking assignments + std::vector blocking_assigns; + std::vector nonblocking_assigns; + collectAssignmentStmt(ec->Stmt(), blocking_assigns, nonblocking_assigns); + // Identify a potential RAM in the LHSs + std::string ram_name; + // 1) It has to be a LHS of a blocking assignment to be a candidate + for (const assignment* stmt : blocking_assigns) { + const expr* lhs = stmt->Lhs(); + // LHS assigns to a bit select + // RAM[addr] = ... + if (lhs->UhdmType() == uhdmbit_select) { + // The actual has to be an array_net with 2 dimensions (packed and unpacked): + const bit_select* bs = any_cast(lhs); + const any* actual = bs->Actual_group(); + if (actual && (actual->UhdmType() == uhdmarray_net)) { + const array_net* arr_net = any_cast(actual); + if (arr_net->Ranges()) { // Unpacked dimension + if (VectorOfnet* nets = arr_net->Nets()) { + if (nets->size()) { + net* n = nets->at(0); + ref_typespec* reft = n->Typespec(); + typespec* tps = reft->Actual_typespec(); + bool has_packed_dimm = false; // Packed dimension + if (tps->UhdmType() == uhdmlogic_typespec) { + logic_typespec* ltps = any_cast(tps); + if (ltps->Ranges()) { + has_packed_dimm = true; + } + } + if (has_packed_dimm) { + ram_name = lhs->VpiName(); + } + } + } + } + } + } + } + // 2) It cannot be a LHS of a non blocking assignment + for (const assignment* stmt : nonblocking_assigns) { + const expr* lhs = stmt->Lhs(); + if (lhs->VpiName() == ram_name) { + // Invalidate the candidate + ram_name = ""; + } + } + // Finally check that it is referenced in RHS of blocking assignments + bool used = false; + if (!ram_name.empty()) { + for (const assignment* stmt : blocking_assigns) { + const any* rhs = stmt->Rhs(); + if (rhs && rhs->VpiName() == ram_name) { + used = true; + break; + } + } + } + // Match all the criteria: Convert all blocking assignments writing or reading the ram to non blocking + if (used) { + for (const assignment* stmt : blocking_assigns) { + const expr* lhs = stmt->Lhs(); + const any* rhs = stmt->Rhs(); + if ((lhs && lhs->VpiName() == ram_name) || (rhs && rhs->VpiName() == ram_name)) { + ((assignment*)stmt)->VpiBlocking(false); + } + } + } + } + } +} + void SynthSubset::leaveArray_var(const array_var* object, vpiHandle handle) { VectorOfvariables* vars = object->Variables(); if (!vars) return; diff --git a/templates/SynthSubset.h b/templates/SynthSubset.h index 172b27b7..0032b543 100644 --- a/templates/SynthSubset.h +++ b/templates/SynthSubset.h @@ -81,6 +81,9 @@ class SynthSubset final : public VpiListener { void mark(const any* object); bool reportedParent(const any* object); + void sensitivityListRewrite(const always* object, vpiHandle handle); + void blockingToNonBlockingRewrite(const always* object, vpiHandle handle); + Serializer* serializer_ = nullptr; std::set& nonSynthesizableObjects_; std::set> nonSynthSysCalls_;