| --- | 
 | title: "lowRISC Hardware Primitives" | 
 | --- | 
 |  | 
 | ## Concepts | 
 |  | 
 | This directory contains basic building blocks to create a hardware design, | 
 | called primitives. A primitive is described by its name, and has a well-defined | 
 | list of ports and parameters. | 
 |  | 
 | Under the hood, primitives are slightly special, as they can have multiple | 
 | implementations. In contrast to many other modules in a hardware design, | 
 | primitives must often be implemented in technology-dependent ways. For example, | 
 | a clock multiplexer for a Xilinx FPGA is implemented differently than one for | 
 | a specific ASIC technology. | 
 |  | 
 | Not all primitives need to have multiple implementations. | 
 |  | 
 | * Primitives with a single, generic, implementation are normal SystemVerilog | 
 |   modules inside the `hw/ip/prim/rtl` directory. We call these primitives | 
 |   "technology-independent primitives". | 
 | * Primitives with multiple implementations have only a FuseSoC core file in the | 
 |   `hw/ip/prim` directory. The actual implementations are in "technology | 
 |   libraries". We call these primitives "technology-dependent primitives". | 
 |  | 
 | ### Abstract primitives | 
 |  | 
 | Abstract primitives are wrappers around technology-dependent implementations of | 
 | primitives, with the ability to select a specific implementation if needed. | 
 |  | 
 | In more technical terms, abstract primitives are SystemVerilog modules. The | 
 | example below shows one. | 
 |  | 
 | ```systemverilog | 
 | `ifndef PRIM_DEFAULT_IMPL | 
 |   `define PRIM_DEFAULT_IMPL prim_pkg::ImplGeneric | 
 | `endif | 
 |  | 
 | module prim_pad_wrapper | 
 | #( | 
 |   parameter int unsigned AttrDw = 6 | 
 | ) ( | 
 |   inout wire         inout_io, // bidirectional pad | 
 |   output logic       in_o,     // input data | 
 |   input              out_i,    // output data | 
 |   input              oe_i,     // output enable | 
 |   // additional attributes {drive strength, keeper, pull-up, pull-down, open-drain, invert} | 
 |   input [AttrDw-1:0] attr_i | 
 | ); | 
 |   parameter prim_pkg::impl_e Impl = `PRIM_DEFAULT_IMPL; | 
 |  | 
 |   if (Impl == prim_pkg::ImplGeneric) begin : gen_generic | 
 |     prim_generic_pad_wrapper u_impl_generic ( | 
 |       .* | 
 |     ); | 
 |   end else if (Impl == prim_pkg::ImplXilinx) begin : gen_xilinx | 
 |     prim_xilinx_pad_wrapper u_impl_xilinx ( | 
 |       .* | 
 |     ); | 
 |   end else begin : gen_failure | 
 |     // TODO: Find code that works across tools and causes a compile failure | 
 |   end | 
 |  | 
 | endmodule | 
 | ``` | 
 |  | 
 | As seen from the source code snippet, abstract primitives have the following | 
 | properties: | 
 |  | 
 | - They have an `Impl` parameter which can be set to choose a specific | 
 |   implementation of the primitive. | 
 | - The `Impl` parameter is set to a system-wide default determined by the | 
 |   `PRIM_DEFAULT_IMPL` define. | 
 | - All ports and parameters of the abstract primitive are forwarded to the | 
 |   implementations. | 
 |  | 
 | ### Technology libraries | 
 |  | 
 | Technology libraries collect implementations of primitives. | 
 |  | 
 | At least one technology library must exist: the `generic` technology library, | 
 | which contains a pure-SystemVerilog implementation of the functionality. This | 
 | library is commonly used for simulations and as functional reference. The | 
 | `generic` technology library is contained in the `hw/ip/prim_generic` directory. | 
 |  | 
 | In addition to the implementation in the `generic` library, primitives may be | 
 | implemented by as many other libraries as needed. | 
 |  | 
 | Technology libraries are referenced by their name. | 
 |  | 
 | ### Technology library discovery | 
 |  | 
 | In many cases, technology libraries contain vendor-specific code which cannot be | 
 | shared widely or openly. Therefore, a FuseSoC looks for available technology | 
 | libraries at build time, and makes all libraries it finds available. | 
 |  | 
 | The discovery is performed based on the agreed-on naming scheme for primitives. | 
 |  | 
 | - FuseSoC scans all libraries (e.g. as specified by its `--cores-root` command | 
 |   line argument) for cores. | 
 | - All cores with a name matching `lowrisc:prim_TECHLIBNAME:PRIMNAME` | 
 |   are considered. `TECHLIBNAME` is then added to the list of technology | 
 |   libraries. | 
 |  | 
 | After the discovery process has completed, a script (`primgen`) creates | 
 | - an abstract primitive (see above), and | 
 | - an entry in the `prim_pkg` package in the form of `prim_pkg::ImplTechlibname` | 
 |   to identify the technology library by its name. | 
 |  | 
 | ## User Guide | 
 |  | 
 | ### Use primitives | 
 |  | 
 | Primitives are normal SystemVerilog modules, and can be used as usual: | 
 | * instantiate it like a normal SystemVerilog module, and | 
 | * add a dependency in the FuseSoC core file. | 
 |  | 
 | Technology-dependent primitives have an additional parameter called `Impl`. | 
 | Set this parameter to use a specific implementation of the primitive for this | 
 | specific instance. For example: | 
 |  | 
 | ```systemverilog | 
 | prim_ram_2p #( | 
 |   .Width (TotalWidth), | 
 |   .Depth (Depth), | 
 |   // Force the use of the tsmc40lp technology library for this instance, instead | 
 |   // of using the build-time default. | 
 |   .Impl(prim_pkg::ImplTsmc40lp) | 
 | ) u_mem ( | 
 |   .clk_a_i    (clk_i), | 
 |   ... | 
 | ) | 
 | ``` | 
 |  | 
 |  | 
 | ### Set the default technology library | 
 |  | 
 | If no specific technology library is chosen for an instantiated primitive the | 
 | default library is used. The SystemVerilog define `PRIM_DEFAULT_IMPL` can be | 
 | used to set the default for the whole design. Set this define to one of the enum | 
 | values in `prim_pkg.sv` in the form `prim_pkg::ImplTechlibname`. `Techlibname` | 
 | is the capitalized name of the technology library. | 
 |  | 
 | In the top-level FuseSoC core file the default technology library can be chosen | 
 | like this: | 
 |  | 
 | ```yaml | 
 | # my_toplevel.core | 
 |  | 
 | # Declare filesets and other things (omitted) | 
 |  | 
 | parameters: | 
 |   # Make the parameter known to FuseSoC to enable overrides from the | 
 |   # command line. If not overwritten, use the generic technology library. | 
 |   PRIM_DEFAULT_IMPL: | 
 |     datatype: str | 
 |     paramtype: vlogdefine | 
 |     description: Primitives implementation to use, e.g. "prim_pkg::ImplGeneric". | 
 |     default: prim_pkg::ImplGeneric | 
 |  | 
 | targets: | 
 |   fpga_synthesis: | 
 |     filesets: | 
 |       - my_rtl_files | 
 |     parameters: | 
 |       # Use the xilinx technology library for this target by default. | 
 |       - PRIM_DEFAULT_IMPL=prim_pkg::ImplXilinx | 
 |     toplevel: my_toplevel | 
 | ``` | 
 |  | 
 |  | 
 | ### Create a technology library | 
 |  | 
 | To create a technology library follow these steps: | 
 |  | 
 | - Choose a name for the new technology library. Names are all lower-case. | 
 |   To ease sharing of technology libraries it is encouraged to pick a very | 
 |   specific name, e.g. `tsmc40lp`, and not `asic`. | 
 | - Copy the `prim_generic` folder into an arbitrary location (can be outside | 
 |   of this repository). Name the folder `prim_YOURLIBRARYNAME`. | 
 | - Replace the word `generic` everywhere with the name of your technology | 
 |   library. This includes | 
 |   - file and directory names (e.g. `prim_generic_ram1p.sv` becomes | 
 |     `prim_tsmc40lp_ram1p.sv`), | 
 |   - module names (e.g. `prim_generic_ram1p` becomes `prim_tsmc40lp_ram1p`), and | 
 |   - all other references (grep for it!). | 
 | - Implement all primitives. Replace the module body of the generic | 
 |   implementation with a technology-specific implementation as needed. Do *not* | 
 |   modify the list of ports or parameters in any way! | 
 |  | 
 | ## Implementation details | 
 |  | 
 | Technology-dependent primitives are implemented as a FuseSoC generator. The | 
 | core of the primitive (e.g. `lowrisc:prim:rom` in `prim/prim_rom.core`) calls | 
 | a FuseSoC generator. This generator is the script `util/primgen.py`. As input, | 
 | the script receives a list of all cores found by FuseSoC anywhere in its search | 
 | path. The script then looks through the cores FuseSoC discovered and extracts | 
 | a list of technology libraries out of it. It then goes on to create the | 
 | abstract primitive (copying over the list of parameters and ports from the | 
 | generic implementation), and an associated core file, which depends on all | 
 | technology-dependent libraries that were found. |