From bc1d3eb7cd009053f00d16bd33547bf390564b5e Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Sat, 9 Feb 2008 22:19:42 -0800 Subject: [PATCH] Add support for generate case Generate case is a complex generate scheme where the items are sub-schemes of the case generate itself. The parser handles them something like nested generate statements, but storing the case guards as the test expression. Then the elaborator notes the case scheme and reaches into the case item schemes inside to make up tests, select the generate item, and elaborate. --- PGenerate.h | 16 ++++++++-- elab_scope.cc | 86 +++++++++++++++++++++++++++++++++++++++++++++++---- elaborate.cc | 21 +++++++++++++ parse.y | 18 +++++++++++ pform.cc | 53 +++++++++++++++++++++++++++++-- pform.h | 2 ++ pform_dump.cc | 11 ++++++- 7 files changed, 196 insertions(+), 11 deletions(-) diff --git a/PGenerate.h b/PGenerate.h index 39be19f47..b29c60b25 100644 --- a/PGenerate.h +++ b/PGenerate.h @@ -37,7 +37,17 @@ class PGate; class PWire; /* - * This represents a generate scheme. + * This represents a generate scheme. The interpretation of the + * members depends on the scheme_type. + * + * GS_LOOP + * + * GS_CASE + * loop_test is the expression to be compared. + * generates contains only GS_CASE_ITEM schemes. + * GS_CASE_ITEM + * The parent points to the GS_CASE that contains this item. + * the loop_test is compared with the parent->loop_test expression. */ class PGenerate : public LineInfo { @@ -50,7 +60,8 @@ class PGenerate : public LineInfo { const unsigned id_number; perm_string scope_name; - enum scheme_t {GS_NONE, GS_LOOP, GS_CONDIT, GS_ELSE}; + enum scheme_t {GS_NONE, GS_LOOP, GS_CONDIT, GS_ELSE, + GS_CASE, GS_CASE_ITEM}; scheme_t scheme_type; // generate loops have an index variable and three @@ -88,6 +99,7 @@ class PGenerate : public LineInfo { private: bool generate_scope_loop_(Design*des, NetScope*container); bool generate_scope_condit_(Design*des, NetScope*container, bool else_flag); + bool generate_scope_case_(Design*des, NetScope*container); // Elaborate_scope within a generated scope. void elaborate_subscope_(Design*des, NetScope*scope); diff --git a/elab_scope.cc b/elab_scope.cc index 7cc2eeddd..c954da025 100644 --- a/elab_scope.cc +++ b/elab_scope.cc @@ -315,6 +315,15 @@ bool PGenerate::generate_scope(Design*des, NetScope*container) case GS_ELSE: return generate_scope_condit_(des, container, true); + case GS_CASE: + return generate_scope_case_(des, container); + return true; + + case GS_CASE_ITEM: + cerr << get_fileline() << ": internal error: " + << "Case item outside of a case generate scheme?" << endl; + return false; + default: cerr << get_fileline() << ": sorry: Generate of this sort" << " is not supported yet!" << endl; @@ -345,7 +354,7 @@ bool PGenerate::generate_scope_loop_(Design*des, NetScope*container) genvar = init->value().as_long(); delete init_ex; - if (debug_elaborate) + if (debug_scopes) cerr << get_fileline() << ": debug: genvar init = " << genvar << endl; container->genvar_tmp = loop_index; @@ -367,7 +376,7 @@ bool PGenerate::generate_scope_loop_(Design*des, NetScope*container) des->errors += 1; return false; } - if (debug_elaborate) + if (debug_scopes) cerr << get_fileline() << ": debug: " << "Create generated scope " << use_name << endl; @@ -386,7 +395,7 @@ bool PGenerate::generate_scope_loop_(Design*des, NetScope*container) genvar_verinum); scope->set_localparam(loop_index, gp); - if (debug_elaborate) + if (debug_scopes) cerr << get_fileline() << ": debug: " << "Create implicit localparam " << loop_index << " = " << genvar_verinum << endl; @@ -398,7 +407,7 @@ bool PGenerate::generate_scope_loop_(Design*des, NetScope*container) NetExpr*step_ex = elab_and_eval(des, container, loop_step, -1); NetEConst*step = dynamic_cast(step_ex); assert(step); - if (debug_elaborate) + if (debug_scopes) cerr << get_fileline() << ": debug: genvar step from " << genvar << " to " << step->value().as_long() << endl; @@ -428,7 +437,7 @@ bool PGenerate::generate_scope_condit_(Design*des, NetScope*container, bool else // scope. if ( (test->value().as_long() == 0 && !else_flag) || (test->value().as_long() != 0 && else_flag) ) { - if (debug_elaborate) + if (debug_scopes) cerr << get_fileline() << ": debug: Generate condition " << (else_flag? "(else)" : "(if)") << " value=" << test->value() << ": skip generation" @@ -445,7 +454,7 @@ bool PGenerate::generate_scope_condit_(Design*des, NetScope*container, bool else des->errors += 1; return false; } - if (debug_elaborate) + if (debug_scopes) cerr << get_fileline() << ": debug: Generate condition " << (else_flag? "(else)" : "(if)") << " value=" << test->value() << ": Generate scope=" @@ -459,6 +468,71 @@ bool PGenerate::generate_scope_condit_(Design*des, NetScope*container, bool else return true; } +bool PGenerate::generate_scope_case_(Design*des, NetScope*container) +{ + NetExpr*case_value_ex = elab_and_eval(des, container, loop_test, -1); + NetEConst*case_value_co = dynamic_cast(case_value_ex); + assert(case_value_co); + + // The name of the scope to generate, whatever that item is. + hname_t use_name (scope_name); + + if (debug_scopes) + cerr << get_fileline() << ": debug: Generate case " + << "switch value=" << case_value_co->value() << endl; + + PGenerate*default_item = 0; + + typedef list::const_iterator generator_it_t; + generator_it_t cur = generates.begin(); + while (cur != generates.end()) { + PGenerate*item = *cur; + assert( item->scheme_type == PGenerate::GS_CASE_ITEM ); + + // Detect that the item is a default. + if (item->loop_test == 0) { + default_item = item; + cur ++; + continue; + } + + NetExpr*item_value_ex = elab_and_eval(des, container, item->loop_test, -1); + NetEConst*item_value_co = dynamic_cast(item_value_ex); + assert(item_value_co); + + // If we stumble on the item that matches, then break + // out now. + if (case_value_co->value() == item_value_co->value()) { + delete item_value_co; + break; + } + + delete item_value_co; + cur ++; + } + + delete case_value_co; + case_value_co = 0; + + PGenerate*item = (cur == generates.end())? default_item : *cur; + if (item == 0) { + cerr << get_fileline() << ": debug: " + << "No generate items found" << endl; + return true; + } + + if (debug_scopes) + cerr << get_fileline() << ": debug: " + << "Generate case matches item at " + << item->get_fileline() << endl; + + NetScope*scope = new NetScope(container, use_name, + NetScope::GENBLOCK); + item->elaborate_subscope_(des, scope); + + return true; +} + void PGenerate::elaborate_subscope_(Design*des, NetScope*scope) { // Scan the generated scope for nested generate schemes, diff --git a/elaborate.cc b/elaborate.cc index 0845329e5..50481b044 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -3554,6 +3554,27 @@ bool PGenerate::elaborate(Design*des, NetScope*container) const { bool flag = true; + // Handle the special case that this is a CASE scheme. In this + // case the PGenerate itself does not have the generated + // item. Look instead for the case ITEM that has a scope + // generated for it. + if (scheme_type == PGenerate::GS_CASE) { + if (debug_elaborate) + cerr << get_fileline() << ": debug: generate case" + << " elaborating in scope " + << scope_path(container) << "." << endl; + + typedef list::const_iterator generate_it_t; + for (generate_it_t cur = generates.begin() + ; cur != generates.end() ; cur ++) { + PGenerate*item = *cur; + if (! item->scope_list_.empty()) { + flag &= item->elaborate(des, container); + } + } + return flag; + } + typedef list::const_iterator scope_list_it_t; for (scope_list_it_t cur = scope_list_.begin() ; cur != scope_list_.end() ; cur ++ ) { diff --git a/parse.y b/parse.y index a5d0679fc..818372c7c 100644 --- a/parse.y +++ b/parse.y @@ -1920,6 +1920,12 @@ module_item generate_block_opt %prec less_than_K_else { pform_endgenerate(); } + | K_case '(' expression ')' + { pform_start_generate_case(@1, $3); } + generate_case_items + K_endcase + { pform_endgenerate(); } + /* specify blocks are parsed but ignored. */ | K_specify K_endspecify @@ -1984,6 +1990,18 @@ module_item generate_if : K_if '(' expression ')' { pform_start_generate_if(@1, $3); } +generate_case_items + : generate_case_items generate_case_item + | generate_case_item + ; + +generate_case_item + : expression ':' { pform_generate_case_item(@1, $1); } generate_block + { pform_endgenerate(); } + | K_default ':' { pform_generate_case_item(@1, 0); } generate_block + { pform_endgenerate(); } + ; + module_item_list : module_item_list module_item | module_item diff --git a/pform.cc b/pform.cc index 3da43e8e4..425a2162e 100644 --- a/pform.cc +++ b/pform.cc @@ -412,6 +412,51 @@ void pform_start_generate_else(const struct vlltype&li) pform_cur_generate->loop_step = 0; } +/* + * The GS_CASE version of the PGenerate contains only case items. The + * items in turn contain the generated items themselves. + */ +void pform_start_generate_case(const struct vlltype&li, PExpr*expr) +{ + PGenerate*gen = new PGenerate(scope_generate_counter++); + + FILE_NAME(gen, li); + + gen->parent = pform_cur_generate; + pform_cur_generate = gen; + + pform_cur_generate->scheme_type = PGenerate::GS_CASE; + + pform_cur_generate->loop_init = 0; + pform_cur_generate->loop_test = expr; + pform_cur_generate->loop_step = 0; +} + +/* + * The generate case item is a special case schema that takes its id + * from the case schema that it is a part of. The idea is that the + * case schema can only instantiate exactly one item, so the items + * need not have a unique number. + */ +void pform_generate_case_item(const struct vlltype&li, PExpr*expr) +{ + assert(pform_cur_generate); + assert(pform_cur_generate->scheme_type == PGenerate::GS_CASE); + + PGenerate*gen = new PGenerate(pform_cur_generate->id_number); + + FILE_NAME(gen, li); + + gen->parent = pform_cur_generate; + pform_cur_generate = gen; + + pform_cur_generate->scheme_type = PGenerate::GS_CASE_ITEM; + + pform_cur_generate->loop_init = 0; + pform_cur_generate->loop_test = expr; + pform_cur_generate->loop_step = 0; +} + void pform_generate_block_name(char*name) { assert(pform_cur_generate != 0); @@ -439,10 +484,14 @@ void pform_endgenerate() PGenerate*cur = pform_cur_generate; pform_cur_generate = cur->parent; - if (pform_cur_generate != 0) + if (pform_cur_generate != 0) { + assert(cur->scheme_type == PGenerate::GS_CASE_ITEM + || pform_cur_generate->scheme_type != PGenerate::GS_CASE); pform_cur_generate->generates.push_back(cur); - else + } else { + assert(cur->scheme_type != PGenerate::GS_CASE_ITEM); pform_cur_module->generate_schemes.push_back(cur); + } } bool pform_expression_is_constant(const PExpr*ex) diff --git a/pform.h b/pform.h index 2ae8d0c57..5dc556f30 100644 --- a/pform.h +++ b/pform.h @@ -188,6 +188,8 @@ extern void pform_start_generate_for(const struct vlltype&li, PExpr*next); extern void pform_start_generate_if(const struct vlltype&li, PExpr*test); extern void pform_start_generate_else(const struct vlltype&li); +extern void pform_start_generate_case(const struct vlltype&lp, PExpr*test); +extern void pform_generate_case_item(const struct vlltype&lp, PExpr*test); extern void pform_generate_block_name(char*name); extern void pform_endgenerate(); diff --git a/pform_dump.cc b/pform_dump.cc index 6db7ccce1..addc8932c 100644 --- a/pform_dump.cc +++ b/pform_dump.cc @@ -854,6 +854,15 @@ void PGenerate::dump(ostream&out, unsigned indent) const case GS_ELSE: out << " else !(" << *loop_test << ")"; break; + case GS_CASE: + out << " case (" << *loop_test << ")"; + break; + case GS_CASE_ITEM: + if (loop_test) + out << " (" << *loop_test << ") == (" << *parent->loop_test << ")"; + else + out << " default:"; + break; } if (scope_name) @@ -882,7 +891,7 @@ void PGenerate::dump(ostream&out, unsigned indent) const (*idx)->dump(out, indent+2); } - out << " endgenerate" << endl; + out << setw(indent) << "" << "endgenerate" << endl; } void Module::dump(ostream&out) const