From b50d5828aa184e368aa9c7aa01adafd2747e5adb Mon Sep 17 00:00:00 2001 From: Sylvain Lefebvre Date: Sun, 12 Mar 2023 21:04:15 +0100 Subject: [PATCH] documenting pipelines, fixed issue with first block of circuitry creating a new pipeline stage instead of adding to previous --- learn-silice/Documentation.md | 55 ++++++++++++++++++++++++++-- projects/vga_demo/vga_msponge.si | 6 +-- src/Algorithm.cpp | 63 +++++++++++++++++++------------- tests/circuits18.si | 28 ++++++++------ 4 files changed, 110 insertions(+), 42 deletions(-) diff --git a/learn-silice/Documentation.md b/learn-silice/Documentation.md index bdc563cc..f63e77c7 100644 --- a/learn-silice/Documentation.md +++ b/learn-silice/Documentation.md @@ -1775,15 +1775,64 @@ come in handy. A powerful construct is to define pipelines in [circuitries](#circuitry), which can then be *concatenated* to a current pipeline. -Here is a simple example: +Here is [an example](../tests/circuits18.si): ```c +circuitry add_two(input i,output o) +{ // stage 1 + uint8 v = i + 1; +-> + // stage 2 + o = v + 1; +-> +} +unit main(output uint8 leds) +{ + uint32 cycle=0; + uint8 a =0; + algorithm { + while (a<3) { + // stage 0 + uint8 v = a; + __display("cycle %d, first stage, v=%d",cycle,v); + a = a + 1; + -> + (v) = add_two(v); // adds two stages + // stage 3 + v = v + 100; + -> + // stage 4 + __display("cycle %d, last stage, v=%d",cycle,v); + } + } + always_after { cycle = cycle + 1; } +} +``` +The output is: ``` +cycle 2, first stage, v= 0 +cycle 3, first stage, v= 1 +cycle 4, first stage, v= 2 +cycle 6, last stage, v=102 +cycle 7, last stage, v=103 +cycle 8, last stage, v=104 +``` +Everything happens as if the content of the `add_two` circuitry had been cut and +pasted where it is instantiated, with two stage being added to the pipeline. +This is very convenient to defined pieces of pipelines to be added, for instance +a pipelined multiplier. + +More advanced examples are in the raymarching pipeline +available in the [demo projects](../projects/vga_demo/vga_msponge.si). -An advanced example is the raymarching pipeline available in the -[demo projects](../projects/vga_demo/vga_msponge.si). +> **Note:** As a good practice suggestion, see how I indented the circuitry +instantiation to align with the pipelining arrows, indicating it does change the +pipeline. +> **Note:** A limitation of the current implementation is that a circuitry +containing a pipeline can only be instantiated within a parent pipeline using +at least one `->`. This will be fixed in the future. ## Pipelines in always blocks diff --git a/projects/vga_demo/vga_msponge.si b/projects/vga_demo/vga_msponge.si index 7e2ac639..1c1c4d3e 100644 --- a/projects/vga_demo/vga_msponge.si +++ b/projects/vga_demo/vga_msponge.si @@ -19,15 +19,15 @@ $$if ECPIX5 then $$ N_steps = 128 -$$ delay = 45751 +$$ delay = 45790 $$ VGA_1024_768 = 1 $$elseif DE10NANO then $$ N_steps = 190 -$$ delay = 93065 +$$ delay = 93104 $$ VGA_1920_1080 = 1 $$elseif VERILATOR then $$ N_steps = 64 -$$ delay = 93191 +$$ delay = 93230 $$ VGA_1920_1080 = 1 $$else $$ error('sorry, this design is currently only for the ECPIX5 and de10-nano') diff --git a/src/Algorithm.cpp b/src/Algorithm.cpp index a4a5f03b..727e71b5 100644 --- a/src/Algorithm.cpp +++ b/src/Algorithm.cpp @@ -2324,37 +2324,49 @@ Algorithm::t_combinational_block *Algorithm::concatenatePipeline(siliceParser::P // go through the pipeline // -> for each stage block t_combinational_block *prev = _current; + bool first = (_current->context.pipeline_stage != nullptr); // if in an existing pipeline, start by adding to the last stage for (auto b : pip->instructionList()) { - // create a fsm for the pipeline stage - t_fsm_nfo *fsm = new t_fsm_nfo; - fsm->name = "fsm_" + nfo->name + "_" + std::to_string(nfo->stages.size()); - m_PipelineFSMs.push_back(fsm); - // stage info - t_pipeline_stage_nfo *snfo = new t_pipeline_stage_nfo(); - snfo->pipeline = nfo; - snfo->fsm = fsm; - snfo->stage_id = (int)nfo->stages.size(); - snfo->node = b; - // block context - t_combinational_block_context ctx = { - fsm, _current->context.subroutine, snfo, - nfo->stages.empty() ? _current : nfo->stages.back()->fsm->lastBlock, - nfo->stages.empty() ? _current->context.vio_rewrites : nfo->stages.back()->fsm->lastBlock->context.vio_rewrites - }; - // add stage - nfo->stages.push_back(snfo); - // gather stage blocks (may recurse and concatenate other pipelines parts) - t_combinational_block *stage_start = addBlock("__stage_" + generateBlockName(), nullptr, &ctx, sourceloc(b)); - fsm->firstBlock = stage_start; - fsm->parentBlock = _current; + t_fsm_nfo* fsm = nullptr; + t_pipeline_stage_nfo* snfo = nullptr; + t_combinational_block* from = nullptr; + if (first) { + // resume from previous + fsm = _current->context.pipeline_stage->fsm; + snfo = _current->context.pipeline_stage; + from = _current; + first = false; + } else { + // create a fsm for the pipeline stage + fsm = new t_fsm_nfo; + fsm->name = "fsm_" + nfo->name + "_" + std::to_string(nfo->stages.size()); + m_PipelineFSMs.push_back(fsm); + // stage info + snfo = new t_pipeline_stage_nfo(); + snfo->pipeline = nfo; + snfo->fsm = fsm; + snfo->stage_id = (int)nfo->stages.size(); + snfo->node = b; + // block context + t_combinational_block_context ctx = { + fsm, _current->context.subroutine, snfo, + nfo->stages.empty() ? _current : nfo->stages.back()->fsm->lastBlock, + nfo->stages.empty() ? _current->context.vio_rewrites : nfo->stages.back()->fsm->lastBlock->context.vio_rewrites + }; + // gather stage blocks (may recurse and concatenate other pipelines parts) + from = addBlock("__stage_" + generateBlockName(), nullptr, &ctx, sourceloc(b)); + fsm->firstBlock = from; + fsm->parentBlock = _current; + // add stage + nfo->stages.push_back(snfo); + } // gather the rest (note: stages may be recursively added in this call) - gather(b, stage_start, _context); + gather(b, from, _context); // stage end is on last block t_combinational_block *stage_end = fsm->lastBlock; // -> check whether pipeline may contain fsms if (_current->context.fsm == nullptr) { // no, report an error if that is the case - if (!isStateLessGraph(stage_start)) { + if (!isStateLessGraph(from)) { reportError(sourceloc(pip), "pipelines in always blocks cannot contain multiple states (they can in algorithms)"); } fsm->firstBlock->is_state = false; @@ -2362,7 +2374,7 @@ Algorithm::t_combinational_block *Algorithm::concatenatePipeline(siliceParser::P fsm->firstBlock->is_state = true; // make them all FSMs } // set next stage - prev->pipeline_next(stage_start, stage_end); + prev->pipeline_next(from, stage_end); // advance prev = nfo->stages.back()->fsm->lastBlock; } @@ -2536,6 +2548,7 @@ Algorithm::t_combinational_block *Algorithm::gatherPipeline(siliceParser::Pipeli // yes: expand the parent pipeline auto nfo = _current->context.pipeline_stage->pipeline; return concatenatePipeline(pip, _current, _context, nfo); + } } diff --git a/tests/circuits18.si b/tests/circuits18.si index 95fac879..0b0a9738 100644 --- a/tests/circuits18.si +++ b/tests/circuits18.si @@ -1,11 +1,11 @@ -circuitry add_three(input i,output o) +circuitry add_two(input i,output o) { + // stage 1 uint8 v = i + 1; -> - v = v + 1; --> + // stage 2 o = v + 1; --> +-> } unit main(output uint8 leds) @@ -13,13 +13,19 @@ unit main(output uint8 leds) uint32 cycle=0; uint8 a =0; algorithm { - while (a<16) { - uint8 v = a; - __display("cycle %d, first stage, v=%d",cycle,v); - a = a + 1; - (v) = add_three(v); - __display("cycle %d, last stage, v=%d",cycle,v); - } + while (a<3) { + // stage 0 + uint8 v = a; + __display("cycle %d, first stage, v=%d",cycle,v); + a = a + 1; + -> + (v) = add_two(v); + // stage 3 + v = v + 100; + -> + // stage 4 + __display("cycle %d, last stage, v=%d",cycle,v); + } } always_after { cycle = cycle + 1; } }