-
Notifications
You must be signed in to change notification settings - Fork 61
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add spinal.lib.misc.plugin doc #229
Merged
Merged
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,3 +6,4 @@ Misc | |
:glob: | ||
|
||
*/* | ||
service_plugin |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,213 @@ | ||
.. role:: raw-html-m2r(raw) | ||
:format: html | ||
|
||
Plugin | ||
========================== | ||
|
||
Introduction | ||
-------------------- | ||
|
||
For some design, instead of implementing your Component's hardware directly in it, | ||
you may instead want to compose its hardware by using some sorts of Plugins. This can provide a few key features : | ||
|
||
- You can extend the features of your component by adding new plugins in its parameters. For instance adding Floating point support in a CPU. | ||
- You can swap various implementations of the same functionality just by using another set of plugins. For instance one implementation of a CPU multiplier may fit well on some FPGA, while others may fit well on ASIC. | ||
- It avoid the very very very large hand written toplevel syndrom where everything has to be connected manualy. Instead plugins can discover their neighborhood by looking/using the software interface of other plugins. | ||
|
||
VexRiscv and NaxRiscv projects are an example of this. Their are CPUs which have a mostly empty toplevel, | ||
and their hardware parts are injected using plugins. For instance : | ||
|
||
- PcPlugin | ||
- FetchPlugin | ||
- DecoderPlugin | ||
- RegFilePlugin | ||
- IntAluPlugin | ||
- ... | ||
|
||
And those plugins will then negociate/propagate/interconnect to each others via their pool of services. | ||
|
||
While VexRiscv use a strict synchronous 2 phase system (setup/build callback), NaxRiscv uses a more flexible approach which uses the spinal.core.fiber API to fork elaboration threads which can interlock each others in order to ensure a workable elaboration ordering. | ||
|
||
The Plugin API provide a NaxRiscv like system to define composable components using plugins. | ||
|
||
Simple example | ||
-------------------- | ||
|
||
Here is a simple dummy example with a SubComponent which will be composed using 2 plugins : | ||
|
||
.. code-block:: scala | ||
|
||
import spinal.core._ | ||
import spinal.lib.misc.plugin._ | ||
|
||
// Let's define a Component with a PluginHost instance | ||
class SubComponent extends Component { | ||
val host = new PluginHost() | ||
} | ||
|
||
// Let's define a plugin which create a register | ||
class StatePlugin extends FiberPlugin { | ||
// during build new Area { body } will run the body of code in the Fiber build phase, in the context of the PluginHost | ||
val logic = during build new Area { | ||
val signal = Reg(UInt(32 bits)) | ||
} | ||
} | ||
|
||
// Let's define a plugin which will make the StatePlugin's register increment | ||
class DriverPlugin extends FiberPlugin { | ||
// We define how to get the instance of StatePlugin.logic from the PluginHost. It is a lazy val, because we can't evaluate it until the plugin is binded to its host. | ||
lazy val sp = host[StatePlugin].logic.get | ||
|
||
val logic = during build new Area { | ||
// Generate the increment hardware | ||
sp.signal := sp.signal + 1 | ||
} | ||
} | ||
|
||
class TopLevel extends Component { | ||
val sub = new SubComponent() | ||
|
||
// Here we create plugins and embed them in sub.host | ||
new DriverPlugin().setHost(sub.host) | ||
new StatePlugin().setHost(sub.host) | ||
} | ||
|
||
Such TopLevel would generate the following Verilog code : | ||
|
||
.. code-block:: verilog | ||
|
||
module TopLevel ( | ||
input wire clk, | ||
input wire reset | ||
); | ||
|
||
|
||
SubComponent sub ( | ||
.clk (clk ), //i | ||
.reset (reset) //i | ||
); | ||
|
||
endmodule | ||
|
||
module SubComponent ( | ||
input wire clk, | ||
input wire reset | ||
); | ||
|
||
reg [31:0] StatePlugin_logic_signal; //Created by StatePlugin | ||
|
||
always @(posedge clk) begin | ||
StatePlugin_logic_signal <= (StatePlugin_logic_signal + 32'h00000001); //incremented by DriverPlugin | ||
end | ||
endmodule | ||
|
||
Note each "during build" fork an elaboration thread, the DriverPlugin.logic thread execution will be blocked on the "sp" evaluation until the StatePlugin.logic execution is done. | ||
|
||
|
||
Interlocking / Ordering | ||
---------------------------------------- | ||
|
||
Plugins can interlock each others using Retainer instances. | ||
Each plugin instance has a built in lock which can be controlled using retain/release functions. | ||
|
||
Here is an example based on the above `Simple example` but that time, the DriverPlugin will increment the StatePlugin.logic.signal | ||
by an amount set by other plugins (SetupPlugin in our case). And to ensure that the DriverPlugin doesn't generate the hardware too early, | ||
the SetupPlugin uses the DriverPlugin.retain/release functions. | ||
|
||
.. code-block:: verilog | ||
|
||
import spinal.core._ | ||
import spinal.lib.misc.plugin._ | ||
import spinal.core.fiber._ | ||
|
||
class SubComponent extends Component { | ||
val host = new PluginHost() | ||
} | ||
|
||
class StatePlugin extends FiberPlugin { | ||
val logic = during build new Area { | ||
val signal = Reg(UInt(32 bits)) | ||
} | ||
} | ||
|
||
class DriverPlugin extends FiberPlugin { | ||
// incrementBy will be set by others plugin at elaboration time | ||
var incrementBy = 0 | ||
// retainer allows other plugins to create locks, on which this plugin will wait before using incrementBy | ||
val retainer = Retainer() | ||
|
||
val logic = during build new Area { | ||
val sp = host[StatePlugin].logic.get | ||
retainer.await() | ||
|
||
// Generate the incrementer hardware | ||
sp.signal := sp.signal + incrementBy | ||
} | ||
} | ||
|
||
// Let's define a plugin which will modify the DriverPlugin.incrementBy variable because letting it elaborate its hardware | ||
class SetupPlugin extends FiberPlugin { | ||
// during setup { body } will spawn the body of code in the Fiber setup phase (it is before the Fiber build phase) | ||
val logic = during setup new Area { | ||
// *** Setup phase code *** | ||
val dp = host[DriverPlugin] | ||
|
||
// Prevent the DriverPlugin from executing its build's body (until release() is called) | ||
val lock = dp.retainer() | ||
// Wait until the fiber phase reached build phase | ||
awaitBuild() | ||
|
||
// *** Build phase code *** | ||
// Let's mutate DriverPlugin.incrementBy | ||
dp.incrementBy += 1 | ||
|
||
// Allows the DriverPlugin to execute its build's body | ||
lock.release() | ||
} | ||
} | ||
|
||
class TopLevel extends Component { | ||
val sub = new SubComponent() | ||
|
||
sub.host.asHostOf( | ||
new DriverPlugin(), | ||
new StatePlugin(), | ||
new SetupPlugin(), | ||
new SetupPlugin() //Let's add a second SetupPlugin, because we can | ||
) | ||
} | ||
|
||
Here is the generated verilog | ||
|
||
.. code-block:: verilog | ||
|
||
module TopLevel ( | ||
input wire clk, | ||
input wire reset | ||
); | ||
|
||
|
||
SubComponent sub ( | ||
.clk (clk ), //i | ||
.reset (reset) //i | ||
); | ||
|
||
endmodule | ||
|
||
module SubComponent ( | ||
input wire clk, | ||
input wire reset | ||
); | ||
|
||
reg [31:0] StatePlugin_logic_signal; | ||
|
||
always @(posedge clk) begin | ||
StatePlugin_logic_signal <= (StatePlugin_logic_signal + 32'h00000002); // + 2 as we have two SetupPlugin | ||
end | ||
endmodule | ||
|
||
Clearly, those examples are overkilled for what they do, the idea in general is more about : | ||
|
||
- Negociate / create interfaces between plugins (ex jump / flush ports) | ||
- Schedule the elaboration (ex decode / dispatch specification) | ||
- Provide a distributed framework which can scale up (minimal hardcoding) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we haven't needed that in a while, you can delete these (I did so in most of the other docs)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok :D
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, the API got some big changes, i need to update the doc