Skip to content

Introduction

Warning

This document explains the future Pyrope, some features are still not implemented. They are documented to guide the designers.

Pyrope is a modern hardware description language, with these focus points:

  • Fast parallel and incremental elaboration
  • Modern and concise language
  • Avoiding hardware specific artifacts
  • Zero cost abstraction
  • Help hardware verification:
    • Powerful type system
    • Hot-Reload support, powerful assertions
    • Allows Pyrope 2 Verilog, edit Verilog, Verilog 2 Pyrope, edit Pyrope...
    • Static checks as long as they not produce false positives

Hello world

Create a directory for the project:

$ mkdir hello
$ cd hello
$ mkdir src

Populate the Pyrope code

src/hello.prp

test "my first test" {
  puts "hello world"
}

Run

$prp test

All the pyrope files reside in the src directory. The prp builder calls LiveHD to elaborate the pyrope files and run all the tests.

Trivial GCD

Populate the Pyrope code

src/gcd.prp:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var gcd = proc (a:uint,b:uint)->(reg x:uint) {
  x = a
  reg y = b

  while y!=0 #> {
    if x > y { 
      x -= y 
    }else{ 
      y -= x 
    }
  }
}

for a in 1..=100 {
  for b in 1..=100 {
    test "check.gcd({},{})",a,b {
      let z =#[..] gcd(a,b)

      waitfor z?

      assert z == __my_cpp_gcd(v1=a, v2=b)
    }
  }
}

src/my_cpp_gcd.cpp

25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
void my_gcd_cpp(const Lbundle &inp, Lbundle &out) {
  assert(inp.has_const("v1") && inp.has_const("v2"));

  auto x = inp.get_const("v1");
  auto y = inp.get_const("v2");

  while (y > 0) {
    if (x > y) {
      x -= y
    }else{
      y -= x
    }
  }

  out.add_const(x);
}

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
import Chisel._
import firrtl_interpreter.InterpretiveTester
import org.scalatest.{Matchers, FlatSpec}

object GCDCalculator {
  def computeGcd(a: Int, b: Int): (Int, Int) = {
    var x = a
    var y = b
    while(y > 0 ) {
      if (x > y) {
        x -= y
      }
      else {
        y -= x
      }
    }
    x
  }
}

class GCD extends Module {
  val io = new Bundle {
    val a  = UInt(INPUT,  16)
    val b  = UInt(INPUT,  16)
    val e  = Bool(INPUT)
    val z  = UInt(OUTPUT, 16)
    val v  = Bool(OUTPUT)
  }
  val x  = Reg(UInt())
  val y  = Reg(UInt())
  when   (x > y) { x := x - y }
  unless (x > y) { y := y - x }
  when (io.e) { x := io.a; y := io.b }
  io.z := x
  io.v := y === UInt(0)
}

class InterpreterUsageSpec extends FlatSpec with Matchers {

  "GCD" should "return correct values for a range of inputs" in {
    val s = Driver.emit(() => new GCD)

    val tester = new InterpretiveTester(s)

    for {
      i <- 1 to 100
      j <- 1 to 100
    } {
      tester.poke("io_a", i)
      tester.poke("io_b", j)
      tester.poke("io_e", 1)
      tester.step()
      tester.poke("io_e", 0)

      while (tester.peek("io_v") != BigInt(1)) {
        tester.step()
      }
      tester.expect("io_z", BigInt(GCDCalculator.computeGcd(i, j)._1))
    }
    tester.report()
  }
}

Run

$prp test check.gcd

The gcd.prp includes the top-level module (gcd) and the unit test.

  • Some Pyrope features not common in other HDLs (CHISEL):

    • Pyrope is not a DSL. Most modern HDLs like CHISEL, pyMTL, pyRTL, CλaSH are DSL cases. In these cases, there is a host language (SCALA, or Python, or Haskell) that must be executed. The result of the execution is the hardware description which can be Verilog or some internal IR like FIRRTL in CHISEL. The advantage of the DSL is that it can leverage the existing language to have a nice hardware generator. The disadvantage is that there are 2 languages at once, the DSL and the host language, and that it is difficult to do incremental because the generated executable from the host language must be executed to generate the design.

    • Global type inference. In the gcd example, the input/outputs are inferred.

    • Synthesizable object system with runtime polymorphism

    • Immutable objects

    • Language support for several hardware constructs

  • Some Pyrope features not common in other languages

    • No object references, only pass by value

    • Pipelining support