mirror of https://github.com/YosysHQ/yosys.git
verilog: Use proc memory writes in the frontend.
This commit is contained in:
parent
4e03865d5b
commit
89c74ffd71
|
@ -54,6 +54,8 @@ namespace AST_INTERNAL {
|
|||
AstNode *current_always, *current_top_block, *current_block, *current_block_child;
|
||||
AstModule *current_module;
|
||||
bool current_always_clocked;
|
||||
dict<std::string, int> current_memwr_count;
|
||||
dict<std::string, pool<int>> current_memwr_visible;
|
||||
}
|
||||
|
||||
// convert node types to string
|
||||
|
|
|
@ -381,6 +381,8 @@ namespace AST_INTERNAL
|
|||
extern AST::AstNode *current_always, *current_top_block, *current_block, *current_block_child;
|
||||
extern AST::AstModule *current_module;
|
||||
extern bool current_always_clocked;
|
||||
extern dict<std::string, int> current_memwr_count;
|
||||
extern dict<std::string, pool<int>> current_memwr_visible;
|
||||
struct LookaheadRewriter;
|
||||
struct ProcessGenerator;
|
||||
}
|
||||
|
|
|
@ -399,6 +399,9 @@ struct AST_INTERNAL::ProcessGenerator
|
|||
if (child->type == AST_BLOCK)
|
||||
processAst(child);
|
||||
|
||||
for (auto sync: proc->syncs)
|
||||
processMemWrites(sync);
|
||||
|
||||
if (initSyncSignals.size() > 0)
|
||||
{
|
||||
RTLIL::SyncRule *sync = new RTLIL::SyncRule;
|
||||
|
@ -698,6 +701,34 @@ struct AST_INTERNAL::ProcessGenerator
|
|||
log_abort();
|
||||
}
|
||||
}
|
||||
|
||||
void processMemWrites(RTLIL::SyncRule *sync)
|
||||
{
|
||||
// Maps per-memid AST_MEMWR IDs to indices in the mem_write_actions array.
|
||||
dict<std::pair<std::string, int>, int> port_map;
|
||||
for (auto child : always->children)
|
||||
if (child->type == AST_MEMWR)
|
||||
{
|
||||
std::string memid = child->str;
|
||||
int portid = child->children[3]->asInt(false);
|
||||
int cur_idx = GetSize(sync->mem_write_actions);
|
||||
RTLIL::MemWriteAction action;
|
||||
set_src_attr(&action, child);
|
||||
action.memid = memid;
|
||||
action.address = child->children[0]->genWidthRTLIL(-1, &subst_rvalue_map.stdmap());
|
||||
action.data = child->children[1]->genWidthRTLIL(current_module->memories[memid]->width, &subst_rvalue_map.stdmap());
|
||||
action.enable = child->children[2]->genWidthRTLIL(-1, &subst_rvalue_map.stdmap());
|
||||
RTLIL::Const orig_priority_mask = child->children[4]->bitsAsConst();
|
||||
RTLIL::Const priority_mask = RTLIL::Const(0, cur_idx);
|
||||
for (int i = 0; i < portid; i++) {
|
||||
int new_bit = port_map[std::make_pair(memid, i)];
|
||||
priority_mask.bits[new_bit] = orig_priority_mask.bits[i];
|
||||
}
|
||||
action.priority_mask = priority_mask;
|
||||
sync->mem_write_actions.push_back(action);
|
||||
port_map[std::make_pair(memid, portid)] = cur_idx;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// detect sign and width of an expression
|
||||
|
@ -1644,26 +1675,22 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
|
|||
return RTLIL::SigSpec(wire);
|
||||
}
|
||||
|
||||
// generate $memwr cells for memory write ports
|
||||
case AST_MEMWR:
|
||||
// generate $meminit cells
|
||||
case AST_MEMINIT:
|
||||
{
|
||||
std::stringstream sstr;
|
||||
sstr << (type == AST_MEMWR ? "$memwr$" : "$meminit$") << str << "$" << filename << ":" << location.first_line << "$" << (autoidx++);
|
||||
sstr << "$meminit$" << str << "$" << filename << ":" << location.first_line << "$" << (autoidx++);
|
||||
|
||||
RTLIL::Cell *cell = current_module->addCell(sstr.str(), type == AST_MEMWR ? ID($memwr) : ID($meminit));
|
||||
RTLIL::Cell *cell = current_module->addCell(sstr.str(), ID($meminit));
|
||||
set_src_attr(cell, this);
|
||||
|
||||
int mem_width, mem_size, addr_bits;
|
||||
id2ast->meminfo(mem_width, mem_size, addr_bits);
|
||||
|
||||
int num_words = 1;
|
||||
if (type == AST_MEMINIT) {
|
||||
if (children[2]->type != AST_CONSTANT)
|
||||
log_file_error(filename, location.first_line, "Memory init with non-constant word count!\n");
|
||||
num_words = int(children[2]->asInt(false));
|
||||
cell->parameters[ID::WORDS] = RTLIL::Const(num_words);
|
||||
}
|
||||
if (children[2]->type != AST_CONSTANT)
|
||||
log_file_error(filename, location.first_line, "Memory init with non-constant word count!\n");
|
||||
int num_words = int(children[2]->asInt(false));
|
||||
cell->parameters[ID::WORDS] = RTLIL::Const(num_words);
|
||||
|
||||
SigSpec addr_sig = children[0]->genRTLIL();
|
||||
|
||||
|
@ -1674,13 +1701,6 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
|
|||
cell->parameters[ID::ABITS] = RTLIL::Const(GetSize(addr_sig));
|
||||
cell->parameters[ID::WIDTH] = RTLIL::Const(current_module->memories[str]->width);
|
||||
|
||||
if (type == AST_MEMWR) {
|
||||
cell->setPort(ID::CLK, RTLIL::SigSpec(RTLIL::State::Sx, 1));
|
||||
cell->setPort(ID::EN, children[2]->genRTLIL());
|
||||
cell->parameters[ID::CLK_ENABLE] = RTLIL::Const(0);
|
||||
cell->parameters[ID::CLK_POLARITY] = RTLIL::Const(0);
|
||||
}
|
||||
|
||||
cell->parameters[ID::PRIORITY] = RTLIL::Const(autoidx-1);
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -1217,6 +1217,14 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
|
|||
}
|
||||
}
|
||||
|
||||
dict<std::string, pool<int>> backup_memwr_visible;
|
||||
dict<std::string, pool<int>> final_memwr_visible;
|
||||
|
||||
if (type == AST_CASE && stage == 2) {
|
||||
backup_memwr_visible = current_memwr_visible;
|
||||
final_memwr_visible = current_memwr_visible;
|
||||
}
|
||||
|
||||
// simplify all children first
|
||||
// (iterate by index as e.g. auto wires can add new children in the process)
|
||||
for (size_t i = 0; i < children.size(); i++) {
|
||||
|
@ -1279,11 +1287,25 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
|
|||
}
|
||||
flag_autowire = backup_flag_autowire;
|
||||
unevaluated_tern_branch = backup_unevaluated_tern_branch;
|
||||
if (stage == 2 && type == AST_CASE) {
|
||||
for (auto &x : current_memwr_visible) {
|
||||
for (int y : x.second)
|
||||
final_memwr_visible[x.first].insert(y);
|
||||
}
|
||||
current_memwr_visible = backup_memwr_visible;
|
||||
}
|
||||
}
|
||||
for (auto &attr : attributes) {
|
||||
while (attr.second->simplify(true, false, false, stage, -1, false, true))
|
||||
did_something = true;
|
||||
}
|
||||
if (type == AST_CASE && stage == 2) {
|
||||
current_memwr_visible = final_memwr_visible;
|
||||
}
|
||||
if (type == AST_ALWAYS && stage == 2) {
|
||||
current_memwr_visible.clear();
|
||||
current_memwr_count.clear();
|
||||
}
|
||||
|
||||
if (reset_width_after_children) {
|
||||
width_hint = backup_width_hint;
|
||||
|
@ -2570,12 +2592,12 @@ skip_dynamic_range_lvalue_expansion:;
|
|||
current_scope[wire_addr->str] = wire_addr;
|
||||
while (wire_addr->simplify(true, false, false, 1, -1, false, false)) { }
|
||||
|
||||
AstNode *assign_addr = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(x_bits_addr, false));
|
||||
AstNode *assign_addr = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), mkconst_bits(x_bits_addr, false));
|
||||
assign_addr->children[0]->str = id_addr;
|
||||
assign_addr->children[0]->was_checked = true;
|
||||
defNode->children.push_back(assign_addr);
|
||||
|
||||
assign_addr = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), children[0]->children[0]->children[0]->clone());
|
||||
assign_addr = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), children[0]->children[0]->children[0]->clone());
|
||||
assign_addr->children[0]->str = id_addr;
|
||||
assign_addr->children[0]->was_checked = true;
|
||||
newNode->children.push_back(assign_addr);
|
||||
|
@ -2596,7 +2618,7 @@ skip_dynamic_range_lvalue_expansion:;
|
|||
current_scope[wire_data->str] = wire_data;
|
||||
while (wire_data->simplify(true, false, false, 1, -1, false, false)) { }
|
||||
|
||||
AstNode *assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(x_bits_data, false));
|
||||
AstNode *assign_data = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), mkconst_bits(x_bits_data, false));
|
||||
assign_data->children[0]->str = id_data;
|
||||
assign_data->children[0]->was_checked = true;
|
||||
defNode->children.push_back(assign_data);
|
||||
|
@ -2616,7 +2638,7 @@ skip_dynamic_range_lvalue_expansion:;
|
|||
current_scope[wire_en->str] = wire_en;
|
||||
while (wire_en->simplify(true, false, false, 1, -1, false, false)) { }
|
||||
|
||||
AstNode *assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_int(0, false, mem_width));
|
||||
AstNode *assign_en = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), mkconst_int(0, false, mem_width));
|
||||
assign_en->children[0]->str = id_en;
|
||||
assign_en->children[0]->was_checked = true;
|
||||
defNode->children.push_back(assign_en);
|
||||
|
@ -2642,7 +2664,7 @@ skip_dynamic_range_lvalue_expansion:;
|
|||
|
||||
std::vector<RTLIL::State> padding_x(offset, RTLIL::State::Sx);
|
||||
|
||||
assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER),
|
||||
assign_data = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER),
|
||||
new AstNode(AST_CONCAT, mkconst_bits(padding_x, false), children[1]->clone()));
|
||||
assign_data->children[0]->str = id_data;
|
||||
assign_data->children[0]->was_checked = true;
|
||||
|
@ -2650,7 +2672,7 @@ skip_dynamic_range_lvalue_expansion:;
|
|||
if (current_always->type != AST_INITIAL) {
|
||||
for (int i = 0; i < mem_width; i++)
|
||||
set_bits_en[i] = offset <= i && i < offset+width ? RTLIL::State::S1 : RTLIL::State::S0;
|
||||
assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(set_bits_en, false));
|
||||
assign_en = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), mkconst_bits(set_bits_en, false));
|
||||
assign_en->children[0]->str = id_en;
|
||||
assign_en->children[0]->was_checked = true;
|
||||
}
|
||||
|
@ -2671,7 +2693,7 @@ skip_dynamic_range_lvalue_expansion:;
|
|||
log_file_error(filename, location.first_line, "Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str());
|
||||
int width = abs(int(left_at_zero_ast->integer - right_at_zero_ast->integer)) + 1;
|
||||
|
||||
assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER),
|
||||
assign_data = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER),
|
||||
new AstNode(AST_SHIFT_LEFT, children[1]->clone(), offset_ast->clone()));
|
||||
assign_data->children[0]->str = id_data;
|
||||
assign_data->children[0]->was_checked = true;
|
||||
|
@ -2679,7 +2701,7 @@ skip_dynamic_range_lvalue_expansion:;
|
|||
if (current_always->type != AST_INITIAL) {
|
||||
for (int i = 0; i < mem_width; i++)
|
||||
set_bits_en[i] = i < width ? RTLIL::State::S1 : RTLIL::State::S0;
|
||||
assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER),
|
||||
assign_en = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER),
|
||||
new AstNode(AST_SHIFT_LEFT, mkconst_bits(set_bits_en, false), offset_ast->clone()));
|
||||
assign_en->children[0]->str = id_en;
|
||||
assign_en->children[0]->was_checked = true;
|
||||
|
@ -2693,13 +2715,13 @@ skip_dynamic_range_lvalue_expansion:;
|
|||
else
|
||||
{
|
||||
if (!(children[0]->children.size() == 1 && children[1]->isConst())) {
|
||||
assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), children[1]->clone());
|
||||
assign_data = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), children[1]->clone());
|
||||
assign_data->children[0]->str = id_data;
|
||||
assign_data->children[0]->was_checked = true;
|
||||
}
|
||||
|
||||
if (current_always->type != AST_INITIAL) {
|
||||
assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(set_bits_en, false));
|
||||
assign_en = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), mkconst_bits(set_bits_en, false));
|
||||
assign_en->children[0]->str = id_en;
|
||||
assign_en->children[0]->was_checked = true;
|
||||
}
|
||||
|
@ -2712,7 +2734,21 @@ skip_dynamic_range_lvalue_expansion:;
|
|||
AstNode *wrnode = new AstNode(current_always->type == AST_INITIAL ? AST_MEMINIT : AST_MEMWR, node_addr, node_data, node_en);
|
||||
wrnode->str = children[0]->str;
|
||||
wrnode->id2ast = children[0]->id2ast;
|
||||
current_ast_mod->children.push_back(wrnode);
|
||||
wrnode->location = location;
|
||||
if (wrnode->type == AST_MEMWR) {
|
||||
int portid = current_memwr_count[wrnode->str]++;
|
||||
wrnode->children.push_back(mkconst_int(portid, false));
|
||||
std::vector<RTLIL::State> priority_mask;
|
||||
for (int i = 0; i < portid; i++) {
|
||||
bool has_prio = current_memwr_visible[wrnode->str].count(i);
|
||||
priority_mask.push_back(State(has_prio));
|
||||
}
|
||||
wrnode->children.push_back(mkconst_bits(priority_mask, false));
|
||||
current_memwr_visible[wrnode->str].insert(portid);
|
||||
current_always->children.push_back(wrnode);
|
||||
} else {
|
||||
current_ast_mod->children.push_back(wrnode);
|
||||
}
|
||||
|
||||
if (newNode->children.empty()) {
|
||||
delete newNode;
|
||||
|
|
|
@ -503,6 +503,8 @@ signal to the temporary signal in its \lstinline[language=C++]{RTLIL::CaseRule}/
|
|||
\item Finally a \lstinline[language=C++]{RTLIL::SyncRule} is created for the \lstinline[language=C++]{RTLIL::Process} that
|
||||
assigns the temporary signals for the final values to the actual signals.
|
||||
%
|
||||
\item A process may also contain memory writes. A \lstinline[language=C++]{RTLIL::MemWriteAction} is created for each of them.
|
||||
%
|
||||
\item Calls to \lstinline[language=C++]{AST::AstNode::genRTLIL()} are generated for right hand sides as needed. When blocking
|
||||
assignments are used, \lstinline[language=C++]{AST::AstNode::genRTLIL()} is configured using global variables to use
|
||||
the temporary signals that hold the correct intermediate values whenever one of the previously assigned signals is used
|
||||
|
@ -821,6 +823,9 @@ the \C{RTLIL::SyncRule}s that describe the output registers.
|
|||
This pass replaces the \C{RTLIL::SyncRule}s to d-type flip-flops (with
|
||||
asynchronous resets if necessary).
|
||||
%
|
||||
\item {\tt proc\_dff} \\
|
||||
This pass replaces the \C{RTLIL::MemWriteActions}s with {\tt \$memwr} cells.
|
||||
%
|
||||
\item {\tt proc\_clean} \\
|
||||
A final call to {\tt proc\_clean} removes the now empty \C{RTLIL::Process} objects.
|
||||
\end{itemize}
|
||||
|
|
Loading…
Reference in New Issue