2pyrtl logo2pyrtl

Start here

Your first design

One design, from an empty workspace to a schematic and generated SystemVerilog. It is a counter with an enable - small enough to read at a glance, big enough to show every step of the loop.

Getting in

An account takes a passkey and no card. Sign up, choose a username, and your browser makes the key - there is no password to remember. On the next screen you set a passphrase: that is what protects your files, and it never leaves your machine.

Why a passphrase as well as a passkey

The passkey proves who you are to us. The passphrase unlocks the key your files are encrypted with, which we never see - so your work is unreadable to us by construction. That is also why nobody, including us, can recover files if both are lost.

Write it

A new workspace has a src/ folder with an example in it. Make a new file - Files pane, the page-with-a-plus icon - call it counter.py, and write:

from rtlpack import *
from ProjectConfig import ProjectConfig


class Counter(Module):
    """Counts up while enable is high; wraps at 2**WIDTH."""

    def __init__(self, global_config, name, parent_module, width=8):
        Module.__init__(self, global_config, name, parent_module=parent_module)
        self.width  = ModuleParam(self, name='WIDTH', value=width, default_value=8)
        self.enable = Wire(my_module=self, name='enable', direction='input', is_control=True)
        self.count  = Wire(my_module=self, name='count', direction='output', width=self.width)
        self.build()

    def _logic(self):
        self.add_flop(self.count, reset_value=0)
        if self.enable:
            self.count = self.count + 1


if __name__ == '__main__':
    cfg = ProjectConfig()
    Counter(global_config=cfg, name='counter', parent_module=None, width=8)

Three things are worth naming. build() is always the last line of the constructor - it is what tells 2pyrtl the module's wires are all declared. add_flop makes count a register with a reset value, so the assignment under the if is what it does on a clock edge. And the block at the bottom is what actually builds the design when the file is run.

Run it

Press Run on the editor's bar, or Ctrl+Enter. Your file runs in a sandbox - a container of its own, with your workspace files and nothing else - and the console below shows what it prints as it goes. A run is capped at 30 seconds.

When it finishes you have new files in 2pyrtl_output/: the SystemVerilog under hdl/systemverilog/, and the design itself under rtl_arch/. Open the SystemVerilog from the Files pane and read it - it is ordinary, synthesisable RTL.

module Counter
   #(
      parameter WIDTH = 8
   )
   (
      input  clk,
      input  resetn,
      input  enable,
      output reg [WIDTH-1:0] count
   );


   always @(posedge clk or negedge resetn)
   begin
      if(resetn == 0) begin
         count <= 0;
      end
      else begin
         if(enable) begin
            count <= count + 1;
         end
      end
   end

endmodule

Names and style are yours to set - the clock's name, whether reset is active low, the indentation, SystemVerilog or VHDL. See project settings.

See it

The schematic appears in the viewer beside your code: the flop, the adder, the multiplexer the if became, and the wires between them. Drag a component and it moves; drag the middle of a wire and it bends; what you arrange is saved and comes back next time you open the design.

If the viewer does not follow a run, check the link icon on its bar - that is the "update the schematic when a run finishes" switch. Full details in the schematic viewer.

Where to go next

  • ·
    Parameterise it: give Counter a different width and run again.
  • ·
    Use something from the library - from rtllibpack import FIFO - and watch a design of real size draw itself. The component library.
  • ·
    Read modules, wires and logic for what the language gives you, and when it goes wrong for the handful of mistakes that catch everyone once.